Bug 666399 - New Harmony syntax for generators. r=Waldo

Add a GeneratorKind enumeration, and use it in the parser and runtime to
indicate whether a function is a non-generator, a legacy generator, or a
star generator.

Always parse "yield" as TOK_YIELD, regardless of the version.  Add
TokenStream::currentName() to retrieve the current token's name, which
works for TOK_NAME or TOK_YIELD.  The parser should check the validity
of "yield" as a name, if needed, using checkYieldNameValidity().

Parse "function*" as the StarGenerator GeneratorKind, and allow star
generators to be lazily parsed.

Separate parsing of return statements from yield expressions.
This commit is contained in:
Andy Wingo 2013-08-20 11:03:24 +02:00
Родитель 085d65c97f
Коммит cb60a21f87
23 изменённых файлов: 605 добавлений и 212 удалений

Просмотреть файл

@ -69,7 +69,7 @@ CheckArgumentsWithinEval(JSContext *cx, Parser<FullParseHandler> &parser, Handle
}
// It's an error to use |arguments| in a legacy generator expression.
if (script->isGeneratorExp && script->isLegacyGenerator) {
if (script->isGeneratorExp && script->isLegacyGenerator()) {
parser.report(ParseError, false, NULL, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
return false;
}
@ -294,7 +294,8 @@ frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject sco
*/
JSFunction *fun = evalCaller->functionOrCallerFunction();
Directives directives(/* strict = */ fun->strict());
ObjectBox *funbox = parser.newFunctionBox(/* fn = */ NULL, fun, pc.addr(), directives);
ObjectBox *funbox = parser.newFunctionBox(/* fn = */ NULL, fun, pc.addr(),
directives, fun->generatorKind());
if (!funbox)
return NULL;
bce.objectList.add(funbox);
@ -414,7 +415,9 @@ frontend::CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *cha
uint32_t staticLevel = lazy->staticLevel(cx);
Rooted<JSFunction*> fun(cx, lazy->function());
ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict());
JS_ASSERT(!lazy->isLegacyGenerator());
ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict(),
lazy->generatorKind());
if (!pn)
return false;
@ -518,7 +521,7 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
ParseNode *fn;
while (true) {
Directives newDirectives = directives;
fn = parser.standaloneFunctionBody(fun, formals, directives, &newDirectives);
fn = parser.standaloneFunctionBody(fun, formals, NotGenerator, directives, &newDirectives);
if (fn)
break;

Просмотреть файл

@ -384,9 +384,9 @@ Parser<FullParseHandler>::cloneParseTree(ParseNode *opn)
if (pn->getKind() == PNK_MODULE) {
MOZ_ASSUME_UNREACHABLE("module nodes cannot be cloned");
}
NULLCHECK(pn->pn_funbox =
newFunctionBox(pn, opn->pn_funbox->function(), pc,
Directives(/* strict = */ opn->pn_funbox->strict)));
NULLCHECK(pn->pn_funbox = newFunctionBox(pn, opn->pn_funbox->function(), pc,
Directives(/* strict = */ opn->pn_funbox->strict),
opn->pn_funbox->generatorKind()));
NULLCHECK(pn->pn_body = cloneParseTree(opn->pn_body));
pn->pn_cookie = opn->pn_cookie;
pn->pn_dflags = opn->pn_dflags;

Просмотреть файл

@ -56,7 +56,6 @@ namespace frontend {
typedef Rooted<StaticBlockObject*> RootedStaticBlockObject;
typedef Handle<StaticBlockObject*> HandleStaticBlockObject;
typedef MutableHandle<PropertyName*> MutableHandlePropertyName;
/*
* Insist that the next token be of type tt, or report errno and return null.
@ -466,13 +465,14 @@ Parser<ParseHandler>::newObjectBox(JSObject *obj)
template <typename ParseHandler>
FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
ParseContext<ParseHandler> *outerpc, Directives directives,
bool extraWarnings)
bool extraWarnings, GeneratorKind generatorKind)
: ObjectBox(fun, traceListHead),
SharedContext(cx, directives, extraWarnings),
bindings(),
bufStart(0),
bufEnd(0),
ndefaults(0),
generatorKindBits_(GeneratorKindAsBits(generatorKind)),
inWith(false), // initialized below
inGenexpLambda(false),
hasDestructuringArgs(false),
@ -533,7 +533,7 @@ FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunct
template <typename ParseHandler>
FunctionBox *
Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *outerpc,
Directives inheritedDirectives)
Directives inheritedDirectives, GeneratorKind generatorKind)
{
JS_ASSERT(fun && !IsPoisonedPtr(fun));
@ -546,7 +546,8 @@ Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction *fun, ParseContext<Pars
*/
FunctionBox *funbox =
alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc,
inheritedDirectives, options().extraWarningsOption);
inheritedDirectives, options().extraWarningsOption,
generatorKind);
if (!funbox) {
js_ReportOutOfMemory(context);
return NULL;
@ -863,6 +864,7 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName *name, Node pn)
template <>
ParseNode *
Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals,
GeneratorKind generatorKind,
Directives inheritedDirectives,
Directives *newDirectives)
{
@ -877,7 +879,8 @@ Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoN
argsbody->makeEmpty();
fn->pn_body = argsbody;
FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ NULL, inheritedDirectives);
FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ NULL, inheritedDirectives,
generatorKind);
if (!funbox)
return null();
handler.setFunctionBox(fn, funbox);
@ -1062,7 +1065,9 @@ Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ
JS_ASSERT(pc->sc->isFunctionBox());
JS_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid);
#ifdef DEBUG
uint32_t startYieldOffset = pc->lastYieldOffset;
#endif
Node pn;
if (type == StatementListBody) {
@ -1082,8 +1087,14 @@ Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ
return null();
}
if (pc->lastYieldOffset != startYieldOffset) {
JS_ASSERT(pc->isLegacyGenerator());
switch (pc->generatorKind()) {
case NotGenerator:
JS_ASSERT(pc->lastYieldOffset == startYieldOffset);
break;
case LegacyGenerator:
// FIXME: Catch these errors eagerly, in yieldExpression().
JS_ASSERT(pc->lastYieldOffset != startYieldOffset);
if (kind == Arrow) {
reportWithOffset(ParseError, false, pc->lastYieldOffset,
JSMSG_YIELD_IN_ARROW, js_yield_str);
@ -1095,9 +1106,12 @@ Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ
JSMSG_BAD_ANON_GENERATOR_RETURN);
return null();
}
pc->sc->asFunctionBox()->setIsLegacyGenerator();
} else {
JS_ASSERT(!pc->isLegacyGenerator());
break;
case StarGenerator:
JS_ASSERT(kind != Arrow);
JS_ASSERT(type == StatementListBody);
break;
}
/* Check for falling off the end of a function that returns a value. */
@ -1627,6 +1641,11 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
}
#endif /* JS_HAS_DESTRUCTURING */
case TOK_YIELD:
if (!checkYieldNameValidity(JSMSG_MISSING_FORMAL))
return false;
goto TOK_NAME;
case TOK_TRIPLEDOT:
{
hasRest = true;
@ -1636,15 +1655,16 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
report(ParseError, false, null(), JSMSG_NO_REST_NAME);
return false;
}
/* Fall through */
goto TOK_NAME;
}
TOK_NAME:
case TOK_NAME:
{
if (parenFreeArrow)
funbox->setStart(tokenStream);
RootedPropertyName name(context, tokenStream.currentToken().name());
RootedPropertyName name(context, tokenStream.currentName());
bool disallowDuplicateArgs = funbox->hasDestructuringArgs || hasDefaults;
if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg))
return false;
@ -1695,6 +1715,20 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
return true;
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::checkFunctionName(HandlePropertyName funName)
{
if (pc->isStarGenerator() && funName == context->names().yield) {
// The name of a named function expression is specified to be bound in
// the outer context as if via "let". In an ES6 generator, "yield" is
// not a valid name for a let-bound variable.
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
return false;
}
return true;
}
template <>
bool
Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
@ -1707,6 +1741,9 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
/* Function statements add a binding to the enclosing scope. */
bool bodyLevel = pc->atBodyLevel();
if (!checkFunctionName(funName))
return false;
if (kind == Statement) {
/*
* Handle redeclaration and optimize cases where we can statically bind the
@ -1820,7 +1857,9 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
// so we can skip over them after accounting for their free variables.
if (LazyScript *lazyOuter = handler.lazyOuterFunction()) {
JSFunction *fun = handler.nextLazyInnerFunction();
FunctionBox *funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false));
JS_ASSERT(!fun->isLegacyGenerator());
FunctionBox *funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false),
fun->generatorKind());
if (!funbox)
return false;
@ -1896,6 +1935,9 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
/* Function statements add a binding to the enclosing scope. */
bool bodyLevel = pc->atBodyLevel();
if (!checkFunctionName(funName))
return false;
if (kind == Statement) {
/*
* Handle redeclaration and optimize cases where we can statically bind the
@ -1934,7 +1976,8 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream::Position &start,
FunctionType type, FunctionSyntaxKind kind)
FunctionType type, FunctionSyntaxKind kind,
GeneratorKind generatorKind)
{
JS_ASSERT_IF(kind == Statement, funName);
@ -1962,7 +2005,7 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
Directives newDirectives = directives;
while (true) {
if (functionArgsAndBody(pn, fun, type, kind, directives, &newDirectives))
if (functionArgsAndBody(pn, fun, type, kind, generatorKind, directives, &newDirectives))
break;
if (tokenStream.hadError() || directives == newDirectives)
return null();
@ -2066,6 +2109,7 @@ Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbo
if (pc->sc->strict)
lazy->setStrict();
lazy->setGeneratorKind(funbox->generatorKind());
if (funbox->usesArguments && funbox->usesApply)
lazy->setUsesArgumentsAndApply();
PropagateTransitiveParseFlags(funbox, lazy);
@ -2078,13 +2122,14 @@ template <>
bool
Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
FunctionType type, FunctionSyntaxKind kind,
GeneratorKind generatorKind,
Directives inheritedDirectives,
Directives *newDirectives)
{
ParseContext<FullParseHandler> *outerpc = pc;
// Create box for fun->object early to protect against last-ditch GC.
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives);
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
if (!funbox)
return false;
@ -2160,13 +2205,14 @@ template <>
bool
Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
FunctionType type, FunctionSyntaxKind kind,
GeneratorKind generatorKind,
Directives inheritedDirectives,
Directives *newDirectives)
{
ParseContext<SyntaxParseHandler> *outerpc = pc;
// Create box for fun->object early to protect against last-ditch GC.
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives);
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
if (!funbox)
return false;
@ -2192,14 +2238,15 @@ Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
template <>
ParseNode *
Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel,
bool strict)
bool strict, GeneratorKind generatorKind)
{
Node pn = handler.newFunctionDefinition();
if (!pn)
return null();
Directives directives(/* strict = */ strict);
FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ NULL, directives);
FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ NULL, directives,
generatorKind);
if (!funbox)
return null();
@ -2273,6 +2320,10 @@ Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, Fu
// Parse the function body.
FunctionBodyType bodyType = StatementListBody;
if (tokenStream.getToken(TokenStream::Operand) != TOK_LC) {
if (funbox->isStarGenerator()) {
report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
return false;
}
tokenStream.ungetToken();
bodyType = ExpressionBody;
fun->setIsExprClosure();
@ -2310,7 +2361,7 @@ template <>
ParseNode *
Parser<FullParseHandler>::moduleDecl()
{
JS_ASSERT(tokenStream.currentToken().name() == context->names().module);
JS_ASSERT(tokenStream.currentName() == context->names().module);
if (!((pc->sc->isGlobalSharedContext() || pc->sc->isModuleBox()) && pc->atBodyLevel()))
{
report(ParseError, false, NULL, JSMSG_MODULE_STATEMENT);
@ -2352,18 +2403,44 @@ Parser<SyntaxParseHandler>::moduleDecl()
return SyntaxParseHandler::NodeFailure;
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::checkYieldNameValidity(unsigned errorNumber)
{
// In star generators and in JS >= 1.7, yield is a keyword.
if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7) {
report(ParseError, false, null(), errorNumber);
return false;
}
// Otherwise in strict mode, yield is a future reserved word.
if (pc->sc->strict) {
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
return false;
}
return true;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::functionStmt()
{
JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION);
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
TokenStream::Position start(keepAtoms);
tokenStream.tell(&start);
RootedPropertyName name(context);
if (tokenStream.getToken(TokenStream::KeywordIsName) == TOK_NAME) {
name = tokenStream.currentToken().name();
GeneratorKind generatorKind = NotGenerator;
TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
if (tt == TOK_MUL) {
tokenStream.tell(&start);
tt = tokenStream.getToken(TokenStream::KeywordIsName);
generatorKind = StarGenerator;
}
if (tt == TOK_NAME) {
name = tokenStream.currentName();
} else {
/* Unnamed function expressions are forbidden in statement context. */
report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
@ -2375,22 +2452,34 @@ Parser<ParseHandler>::functionStmt()
!report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT))
return null();
return functionDef(name, start, Normal, Statement);
return functionDef(name, start, Normal, Statement, generatorKind);
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::functionExpr()
{
RootedPropertyName name(context);
JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION);
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
TokenStream::Position start(keepAtoms);
tokenStream.tell(&start);
if (tokenStream.getToken(TokenStream::KeywordIsName) == TOK_NAME)
name = tokenStream.currentToken().name();
RootedPropertyName name(context);
GeneratorKind generatorKind = NotGenerator;
TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
if (tt == TOK_MUL) {
tokenStream.tell(&start);
tt = tokenStream.getToken(TokenStream::KeywordIsName);
generatorKind = StarGenerator;
}
if (tt == TOK_NAME)
name = tokenStream.currentName();
else
tokenStream.ungetToken();
return functionDef(name, start, Normal, Expression);
return functionDef(name, start, Normal, Expression, generatorKind);
}
/*
@ -2524,7 +2613,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool *cont)
}
}
} else if (directive == context->names().useAsm) {
if (pc->sc->isFunctionBox())
if (pc->sc->isFunctionBox() && !pc->isGenerator())
return asmJS(list);
return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL);
}
@ -2606,15 +2695,21 @@ Parser<ParseHandler>::condition()
return pn;
}
static bool
MatchLabel(TokenStream &ts, MutableHandlePropertyName label)
template <typename ParseHandler>
bool
Parser<ParseHandler>::matchLabel(MutableHandle<PropertyName*> label)
{
TokenKind tt = ts.peekTokenSameLine(TokenStream::Operand);
TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand);
if (tt == TOK_ERROR)
return false;
if (tt == TOK_NAME) {
(void) ts.getToken();
label.set(ts.currentToken().name());
tokenStream.consumeKnownToken(TOK_NAME);
label.set(tokenStream.currentName());
} else if (tt == TOK_YIELD) {
tokenStream.consumeKnownToken(TOK_YIELD);
if (!checkYieldNameValidity())
return false;
label.set(tokenStream.currentName());
} else {
label.set(NULL);
}
@ -3240,7 +3335,7 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::letBlock(LetContext letContext)
{
JS_ASSERT(tokenStream.currentToken().type == TOK_LET);
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
if (!blockObj)
@ -3330,7 +3425,7 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::blockStatement()
{
JS_ASSERT(tokenStream.currentToken().type == TOK_LC);
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
StmtInfoPC stmtInfo(context);
if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, pc))
@ -3372,7 +3467,6 @@ Parser<ParseHandler>::newBindingNode(PropertyName *name, bool functionScope, Var
}
/* Make a new node for this declarator name (or destructuring pattern). */
JS_ASSERT(tokenStream.currentToken().type == TOK_NAME);
return newName(name);
}
@ -3461,12 +3555,17 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
#endif /* JS_HAS_DESTRUCTURING */
if (tt != TOK_NAME) {
if (tt != TOK_ERROR)
report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
return null();
if (tt == TOK_YIELD) {
if (!checkYieldNameValidity(JSMSG_NO_VARIABLE_NAME))
return null();
} else {
if (tt != TOK_ERROR)
report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
return null();
}
}
RootedPropertyName name(context, tokenStream.currentToken().name());
RootedPropertyName name(context, tokenStream.currentName());
pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext);
if (!pn2)
return null();
@ -4123,9 +4222,14 @@ Parser<SyntaxParseHandler>::forStatement()
PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
/* Don't parse 'for each' loops. */
if (allowsForEachIn() && tokenStream.peekToken() == TOK_NAME) {
JS_ALWAYS_FALSE(abortIfSyntaxParser());
return null();
if (allowsForEachIn()) {
TokenKind tt = tokenStream.peekToken();
// Not all "yield" tokens are names, but the ones that aren't names are
// invalid in this context anyway.
if (tt == TOK_NAME || tt == TOK_YIELD) {
JS_ALWAYS_FALSE(abortIfSyntaxParser());
return null();
}
}
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
@ -4327,7 +4431,7 @@ Parser<ParseHandler>::continueStatement()
uint32_t begin = pos().begin;
RootedPropertyName label(context);
if (!MatchLabel(tokenStream, &label))
if (!matchLabel(&label))
return null();
StmtInfoPC *stmt = pc->topStmt;
@ -4374,7 +4478,7 @@ Parser<ParseHandler>::breakStatement()
uint32_t begin = pos().begin;
RootedPropertyName label(context);
if (!MatchLabel(tokenStream, &label))
if (!matchLabel(&label))
return null();
StmtInfoPC *stmt = pc->topStmt;
if (label) {
@ -4405,98 +4509,45 @@ Parser<ParseHandler>::breakStatement()
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::returnStatementOrYieldExpression()
Parser<ParseHandler>::returnStatement()
{
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN) ||
tokenStream.isCurrentTokenType(TOK_YIELD));
bool isYield = tokenStream.isCurrentTokenType(TOK_YIELD);
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN));
uint32_t begin = pos().begin;
if (!pc->sc->isFunctionBox()) {
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD,
isYield ? js_yield_str : js_return_str);
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
return null();
}
// Legacy generators are identified by the presence of "yield" in their
// bodies. We only see "yield" as TOK_YIELD in JS 1.7+.
if (isYield) {
JS_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7);
if (!abortIfSyntaxParser())
return null();
if (pc->isLegacyGenerator()) {
// We are in a legacy generator: a function that has already seen a
// yield.
JS_ASSERT(pc->sc->isFunctionBox());
JS_ASSERT(pc->lastYieldOffset != ParseContext<ParseHandler>::NoYieldOffset);
} else {
// We are in a code that has not seen a yield, and in JS 1.8 so
// "yield" parsed as TOK_YIELD. Try to transition to being a legacy
// generator.
JS_ASSERT(tokenStream.currentToken().type == TOK_YIELD);
JS_ASSERT(pc->lastYieldOffset == ParseContext<ParseHandler>::NoYieldOffset);
if (!pc->sc->isFunctionBox()) {
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
return null();
}
pc->generatorParseMode = ParseContext<ParseHandler>::LegacyGenerator;
}
pc->lastYieldOffset = begin;
}
// Parse an optional operand.
//
// Checking whether yield has an operand is especially wonky since
// there is not a mandatory semicolon.
//
// ES6 does not permit yield without an operand. We will have to sunset
// this extension in order to conform to the ES6 syntax, which treats
// "yield \n expr;" as a single ExpressionStatement.
// This is ugly, but we don't want to require a semicolon.
Node exprNode;
TokenKind next = tokenStream.peekTokenSameLine(TokenStream::Operand);
if (next == TOK_ERROR)
switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
case TOK_ERROR:
return null();
if (next == TOK_EOF || next == TOK_EOL || next == TOK_SEMI || next == TOK_RC ||
(isYield && (next == TOK_RB || next == TOK_RP || next == TOK_COLON || next == TOK_COMMA)))
{
if (isYield) {
if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND))
return null();
}
case TOK_EOF:
case TOK_EOL:
case TOK_SEMI:
case TOK_RC:
exprNode = null();
if (!isYield)
pc->funHasReturnVoid = true;
} else {
exprNode = isYield ? assignExpr() : expr();
pc->funHasReturnVoid = true;
break;
default: {
exprNode = expr();
if (!exprNode)
return null();
if (!isYield)
pc->funHasReturnExpr = true;
pc->funHasReturnExpr = true;
}
}
if (!isYield) {
if (!MatchOrInsertSemicolon(tokenStream))
return null();
}
if (!MatchOrInsertSemicolon(tokenStream))
return null();
Node pn = isYield
? handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode)
: handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
Node pn = handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
if (!pn)
return null();
if (pc->funHasReturnExpr && pc->isLegacyGenerator()) {
/* As in Python (see PEP-255), disallow return v; in generators. */
reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
JSMSG_BAD_ANON_GENERATOR_RETURN);
return null();
}
if (options().extraWarningsOption && pc->funHasReturnExpr && pc->funHasReturnVoid &&
!reportBadReturn(pn, ParseExtraWarning,
JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE))
@ -4504,9 +4555,108 @@ Parser<ParseHandler>::returnStatementOrYieldExpression()
return null();
}
if (pc->isLegacyGenerator() && exprNode) {
/* Disallow "return v;" in legacy generators. */
reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
JSMSG_BAD_ANON_GENERATOR_RETURN);
return null();
}
return pn;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::yieldExpression()
{
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
uint32_t begin = pos().begin;
switch (pc->generatorKind()) {
case StarGenerator:
{
JS_ASSERT(pc->sc->isFunctionBox());
pc->lastYieldOffset = begin;
bool isDelegatingYield = tokenStream.matchToken(TOK_MUL);
// ES6 generators require a value.
Node exprNode = assignExpr();
if (!exprNode)
return null();
// FIXME: Plumb isDelegatingYield appropriately.
(void) isDelegatingYield;
return handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode);
}
case NotGenerator:
// We are in code that has not seen a yield, but we are in JS 1.7 or
// later. Try to transition to being a legacy generator.
JS_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7);
JS_ASSERT(pc->lastYieldOffset == ParseContext<ParseHandler>::NoYieldOffset);
if (!abortIfSyntaxParser())
return null();
if (!pc->sc->isFunctionBox()) {
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
return null();
}
pc->sc->asFunctionBox()->setGeneratorKind(LegacyGenerator);
if (pc->funHasReturnExpr) {
/* As in Python (see PEP-255), disallow return v; in generators. */
reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN,
JSMSG_BAD_ANON_GENERATOR_RETURN);
return null();
}
// Fall through.
case LegacyGenerator:
{
// We are in a legacy generator: a function that has already seen a
// yield, or in a legacy generator comprehension.
JS_ASSERT(pc->sc->isFunctionBox());
pc->lastYieldOffset = begin;
// Legacy generators do not require a value.
Node exprNode;
switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
case TOK_ERROR:
return null();
case TOK_EOF:
case TOK_EOL:
case TOK_SEMI:
case TOK_RC:
case TOK_RB:
case TOK_RP:
case TOK_COLON:
case TOK_COMMA:
// No value.
exprNode = null();
// ES6 does not permit yield without an operand. We should
// encourage users of yield expressions of this kind to pass an
// operand, to bring users closer to standard syntax.
if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND))
return null();
break;
default:
exprNode = assignExpr();
if (!exprNode)
return null();
}
return handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode);
}
}
MOZ_ASSUME_UNREACHABLE("yieldExpr");
}
template <>
ParseNode *
Parser<FullParseHandler>::withStatement()
@ -4576,7 +4726,7 @@ typename ParseHandler::Node
Parser<ParseHandler>::labeledStatement()
{
uint32_t begin = pos().begin;
RootedPropertyName label(context, tokenStream.currentToken().name());
RootedPropertyName label(context, tokenStream.currentName());
for (StmtInfoPC *stmt = pc->topStmt; stmt; stmt = stmt->down) {
if (stmt->type == STMT_LABEL && stmt->label == label) {
report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL);
@ -4717,9 +4867,13 @@ Parser<ParseHandler>::tryStatement()
break;
#endif
case TOK_YIELD:
if (!checkYieldNameValidity(JSMSG_CATCH_IDENTIFIER))
return null();
// Fall through.
case TOK_NAME:
{
RootedPropertyName label(context, tokenStream.currentToken().name());
RootedPropertyName label(context, tokenStream.currentName());
catchName = newBindingNode(label, false);
if (!catchName)
return null();
@ -4855,7 +5009,7 @@ Parser<ParseHandler>::statement(bool canHaveDirectives)
case TOK_BREAK:
return breakStatement();
case TOK_RETURN:
return returnStatementOrYieldExpression();
return returnStatement();
case TOK_WITH:
return withStatement();
case TOK_THROW:
@ -4886,10 +5040,18 @@ Parser<ParseHandler>::statement(bool canHaveDirectives)
}
return expressionStatement();
case TOK_YIELD:
if (tokenStream.peekToken() == TOK_COLON) {
if (!checkYieldNameValidity())
return null();
return labeledStatement();
}
return expressionStatement();
case TOK_NAME:
if (tokenStream.peekToken() == TOK_COLON)
return labeledStatement();
if (tokenStream.currentToken().name() == context->names().module
if (tokenStream.currentName() == context->names().module
&& tokenStream.peekTokenSameLine() == TOK_STRING)
{
return moduleDecl();
@ -5202,8 +5364,8 @@ Parser<ParseHandler>::assignExpr()
if (tt == TOK_STRING && tokenStream.nextTokenEndsExpr())
return stringLiteral();
if (tt == TOK_YIELD)
return returnStatementOrYieldExpression();
if (tt == TOK_YIELD && (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()))
return yieldExpression();
tokenStream.ungetToken();
@ -5241,7 +5403,7 @@ Parser<ParseHandler>::assignExpr()
return null();
tokenStream.ungetToken();
return functionDef(NullPtr(), start, Normal, Arrow);
return functionDef(NullPtr(), start, Normal, Arrow, NotGenerator);
}
default:
@ -5662,7 +5824,7 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
BindData<FullParseHandler> data(context);
TokenKind tt;
JS_ASSERT(tokenStream.currentToken().type == TOK_FOR);
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
if (kind == PNK_SEMI) {
/*
@ -5747,7 +5909,7 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
#endif
case TOK_NAME:
name = tokenStream.currentToken().name();
name = tokenStream.currentName();
/*
* Create a name node with pn_op JSOP_NAME. We can't set pn_op to
@ -5955,7 +6117,8 @@ Parser<FullParseHandler>::generatorExpr(ParseNode *kid)
/* Create box for fun->object early to protect against last-ditch GC. */
Directives directives(/* strict = */ outerpc->sc->strict);
FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives);
FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives,
LegacyGenerator);
if (!genFunbox)
return null();
@ -5975,7 +6138,7 @@ Parser<FullParseHandler>::generatorExpr(ParseNode *kid)
if (outerpc->sc->isFunctionBox())
genFunbox->funCxFlags = outerpc->sc->asFunctionBox()->funCxFlags;
genFunbox->setIsLegacyGenerator();
JS_ASSERT(genFunbox->isLegacyGenerator());
genFunbox->inGenexpLambda = true;
genfn->pn_blockid = genpc.bodyid;
@ -6116,7 +6279,7 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
if (tt == TOK_ERROR)
return null();
if (tt == TOK_NAME) {
PropertyName *field = tokenStream.currentToken().name();
PropertyName *field = tokenStream.currentName();
nextMember = handler.newPropertyAccess(lhs, field, pos().end);
if (!nextMember)
return null();
@ -6191,9 +6354,7 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::identifierName()
{
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
RootedPropertyName name(context, tokenStream.currentToken().name());
RootedPropertyName name(context, tokenStream.currentName());
Node pn = newName(name);
if (!pn)
return null();
@ -6395,7 +6556,7 @@ Parser<ParseHandler>::objectLiteral()
break;
case TOK_NAME: {
atom = tokenStream.currentToken().name();
atom = tokenStream.currentName();
if (atom == context->names().get) {
op = JSOP_INITPROP_GETTER;
} else if (atom == context->names().set) {
@ -6411,7 +6572,7 @@ Parser<ParseHandler>::objectLiteral()
// name next.
TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
if (tt == TOK_NAME) {
atom = tokenStream.currentToken().name();
atom = tokenStream.currentName();
propname = newName(atom->asPropertyName());
if (!propname)
return null();
@ -6527,7 +6688,7 @@ Parser<ParseHandler>::objectLiteral()
TokenStream::Position start(keepAtoms);
tokenStream.tell(&start);
Node accessor = functionDef(funName, start, op == JSOP_INITPROP_GETTER ? Getter : Setter,
Expression);
Expression, NotGenerator);
if (!accessor)
return null();
if (!handler.addAccessorPropertyDefinition(literal, propname, accessor, op))
@ -6628,6 +6789,10 @@ Parser<ParseHandler>::primaryExpr(TokenKind tt)
case TOK_STRING:
return stringLiteral();
case TOK_YIELD:
if (!checkYieldNameValidity())
return null();
// Fall through.
case TOK_NAME:
return identifierName();
@ -6689,7 +6854,7 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::parenExpr(bool *genexp)
{
JS_ASSERT(tokenStream.currentToken().type == TOK_LP);
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
uint32_t begin = pos().begin;
if (genexp)

Просмотреть файл

@ -104,18 +104,20 @@ struct ParseContext : public GenericParseContext
const unsigned staticLevel; /* static compilation unit nesting level */
// Functions start off being parsed as NotGenerator.
// NotGenerator transitions to LegacyGenerator on parsing "yield" in JS 1.7.
enum GeneratorParseMode { NotGenerator, LegacyGenerator };
GeneratorParseMode generatorParseMode;
// lastYieldOffset stores the offset of the last yield that was parsed.
// NoYieldOffset is its initial value.
static const uint32_t NoYieldOffset = UINT32_MAX;
uint32_t lastYieldOffset;
bool isGenerator() const { return generatorParseMode != NotGenerator; }
bool isLegacyGenerator() const { return generatorParseMode == LegacyGenerator; }
// Most functions start off being parsed as non-generators.
// Non-generators transition to LegacyGenerator on parsing "yield" in JS 1.7.
// An ES6 generator is marked as a "star generator" before its body is parsed.
GeneratorKind generatorKind() const {
return sc->isFunctionBox() ? sc->asFunctionBox()->generatorKind() : NotGenerator;
}
bool isGenerator() const { return generatorKind() != NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
Node blockNode; /* parse node for a block with let declarations
(block with its own lexical scope) */
@ -249,7 +251,6 @@ struct ParseContext : public GenericParseContext
blockChain(prs->context),
maybeFunction(maybeFunction),
staticLevel(staticLevel),
generatorParseMode(NotGenerator),
lastYieldOffset(NoYieldOffset),
blockNode(ParseHandler::null()),
decls_(prs->context, prs->alloc),
@ -400,7 +401,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
ObjectBox *newObjectBox(JSObject *obj);
ModuleBox *newModuleBox(Module *module, ParseContext<ParseHandler> *pc);
FunctionBox *newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *pc,
Directives directives);
Directives directives, GeneratorKind generatorKind);
/*
* Create a new function object given parse context (pc) and a name (which
@ -433,13 +434,16 @@ class Parser : private AutoGCRooter, public StrictModeGetter
Node statement(bool canHaveDirectives = false);
bool maybeParseDirective(Node list, Node pn, bool *cont);
// Parse a function, given only its body. Used for the Function constructor.
// Parse a function, given only its body. Used for the Function and
// Generator constructors.
Node standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals,
GeneratorKind generatorKind,
Directives inheritedDirectives, Directives *newDirectives);
// Parse a function, given only its arguments and body. Used for lazily
// parsed functions.
Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict);
Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict,
GeneratorKind generatorKind);
/*
* Parse a function body. Pass StatementListBody if the body is a list of
@ -487,7 +491,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
Node switchStatement();
Node continueStatement();
Node breakStatement();
Node returnStatementOrYieldExpression();
Node returnStatement();
Node withStatement();
Node labeledStatement();
Node throwStatement();
@ -504,6 +508,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
Node expr();
Node assignExpr();
Node assignExprWithoutYield(unsigned err);
Node yieldExpression();
Node condExpr1();
Node orExpr1();
Node unaryExpr();
@ -517,9 +522,10 @@ class Parser : private AutoGCRooter, public StrictModeGetter
bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest);
Node functionDef(HandlePropertyName name, const TokenStream::Position &start,
FunctionType type, FunctionSyntaxKind kind);
FunctionType type, FunctionSyntaxKind kind, GeneratorKind generatorKind);
bool functionArgsAndBody(Node pn, HandleFunction fun,
FunctionType type, FunctionSyntaxKind kind,
GeneratorKind generatorKind,
Directives inheritedDirectives, Directives *newDirectives);
Node unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin);
@ -536,6 +542,9 @@ class Parser : private AutoGCRooter, public StrictModeGetter
Node identifierName();
bool matchLabel(MutableHandle<PropertyName*> label);
bool checkYieldNameValidity(unsigned errorNumber = JSMSG_SYNTAX_ERROR);
bool allowsForEachIn() {
#if !JS_HAS_FOR_EACH_IN
return false;
@ -556,6 +565,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
bool checkFunctionArguments();
bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom);
bool checkFunctionName(HandlePropertyName funName);
bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind,
bool *pbodyProcessed);
bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body);

Просмотреть файл

@ -72,10 +72,6 @@ class FunctionContextFlags
// This class's data is all private and so only visible to these friends.
friend class FunctionBox;
// We parsed a yield statement in the function, which can happen in JS1.7+
// mode.
bool isLegacyGenerator:1;
// The function or a function that encloses it may define new local names
// at runtime through means other than calling eval.
bool mightAliasLocals:1;
@ -129,8 +125,7 @@ class FunctionContextFlags
public:
FunctionContextFlags()
: isLegacyGenerator(false),
mightAliasLocals(false),
: mightAliasLocals(false),
hasExtensibleScope(false),
needsDeclEnvObject(false),
argumentsHasLocalBinding(false),
@ -264,6 +259,8 @@ class FunctionBox : public ObjectBox, public SharedContext
uint32_t startLine;
uint32_t startColumn;
uint16_t ndefaults;
uint8_t generatorKindBits_; /* The GeneratorKind of this function. */
bool inWith:1; /* some enclosing scope is a with-statement */
bool inGenexpLambda:1; /* lambda from generator expression */
bool hasDestructuringArgs:1; /* arguments list contains destructuring expression */
@ -279,21 +276,30 @@ class FunctionBox : public ObjectBox, public SharedContext
template <typename ParseHandler>
FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
ParseContext<ParseHandler> *pc, Directives directives,
bool extraWarnings);
bool extraWarnings, GeneratorKind generatorKind);
ObjectBox *toObjectBox() { return this; }
JSFunction *function() const { return &object->as<JSFunction>(); }
// In the future, isGenerator will also return true for ES6 generators.
bool isGenerator() const { return isLegacyGenerator(); }
bool isLegacyGenerator() const { return funCxFlags.isLegacyGenerator; }
GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
bool isGenerator() const { return generatorKind() != NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
void setGeneratorKind(GeneratorKind kind) {
// A generator kind can be set at initialization, or when "yield" is
// first seen. In both cases the transition can only happen from
// NotGenerator.
JS_ASSERT(!isGenerator());
generatorKindBits_ = GeneratorKindAsBits(kind);
}
bool mightAliasLocals() const { return funCxFlags.mightAliasLocals; }
bool hasExtensibleScope() const { return funCxFlags.hasExtensibleScope; }
bool needsDeclEnvObject() const { return funCxFlags.needsDeclEnvObject; }
bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; }
bool definitelyNeedsArgsObj() const { return funCxFlags.definitelyNeedsArgsObj; }
void setIsLegacyGenerator() { funCxFlags.isLegacyGenerator = true; }
void setMightAliasLocals() { funCxFlags.mightAliasLocals = true; }
void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; }
void setNeedsDeclEnvObject() { funCxFlags.needsDeclEnvObject = true; }

Просмотреть файл

@ -937,10 +937,10 @@ TokenStream::checkForKeyword(const jschar *s, size_t length, TokenKind *ttp)
return reportError(JSMSG_RESERVED_ID, kw->chars);
}
// The keyword is not in this version. Treat it as an identifier,
// unless it is let or yield which we treat as TOK_STRICT_RESERVED by
// falling through to the code below (ES5 forbids them in strict mode).
if (kw->tokentype != TOK_LET && kw->tokentype != TOK_YIELD)
// The keyword is not in this version. Treat it as an identifier, unless
// it is let which we treat as TOK_STRICT_RESERVED by falling through to
// the code below (ES5 forbids it in strict mode).
if (kw->tokentype != TOK_LET)
return true;
}

Просмотреть файл

@ -407,6 +407,13 @@ class MOZ_STACK_CLASS TokenStream
JSVersion versionNumber() const { return VersionNumber(options().version); }
JSVersion versionWithFlags() const { return options().version; }
PropertyName *currentName() const {
if (isCurrentTokenType(TOK_YIELD))
return cx->names().yield;
JS_ASSERT(isCurrentTokenType(TOK_NAME));
return currentToken().name();
}
bool isCurrentTokenAssignment() const {
return TokenKindIsAssignment(currentToken().type);
}

Просмотреть файл

@ -0,0 +1,38 @@
// Test interactions between ES6 generators and not-yet-standard
// features.
function assertSyntaxError(str) {
var msg;
var evil = eval;
try {
// Non-direct eval.
evil(str);
} catch (exc) {
if (exc instanceof SyntaxError)
return;
msg = "Assertion failed: expected SyntaxError, got " + exc;
}
if (msg === undefined)
msg = "Assertion failed: expected SyntaxError, but no exception thrown";
throw new Error(msg + " - " + str);
}
// Destructuring binding.
assertSyntaxError("function* f(x = yield) {}");
assertSyntaxError("function* f(x = yield 17) {}");
assertSyntaxError("function* f([yield]) {}");
assertSyntaxError("function* f({ yield }) {}");
assertSyntaxError("function* f(...yield) {}");
// For each.
assertSyntaxError("for yield");
assertSyntaxError("for yield (;;) {}");
assertSyntaxError("for yield (x of y) {}");
assertSyntaxError("for yield (var i in o) {}");
// Expression bodies.
assertSyntaxError("function* f() yield 7");
// Asm.js.
load(libdir + "asm.js");
assertAsmDirectiveFail("function* f() { 'use asm'; function g() { return 0; } return g; })()")

Просмотреть файл

@ -4607,7 +4607,7 @@ ParseFunction(ModuleCompiler &m, ParseNode **fnOut)
AsmJSParseContext *outerpc = m.parser().pc;
Directives directives(outerpc);
FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives);
FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives, NotGenerator);
if (!funbox)
return false;

Просмотреть файл

@ -409,3 +409,4 @@ MSG_DEF(JSMSG_BINARYDATA_NOT_BINARYSTRUCT, 355, 1, JSEXN_TYPEERR, "{0} is not
MSG_DEF(JSMSG_BINARYDATA_SUBARRAY_INTEGER_ARG, 356, 1, JSEXN_ERR, "argument {0} must be an integer")
MSG_DEF(JSMSG_BINARYDATA_STRUCTTYPE_EMPTY_DESCRIPTOR, 357, 0, JSEXN_ERR, "field descriptor cannot be empty")
MSG_DEF(JSMSG_BINARYDATA_STRUCTTYPE_BAD_FIELD, 358, 1, JSEXN_ERR, "field {0} is not a valid BinaryData Type descriptor")
MSG_DEF(JSMSG_ES6_UNIMPLEMENTED, 359, 0, JSEXN_ERR, "ES6 functionality not yet implemented")

Просмотреть файл

@ -1749,7 +1749,7 @@ ScriptAnalysis::needsArgsObj(JSContext *cx)
*
* FIXME: Don't build arguments for ES6 generator expressions.
*/
if (cx->compartment()->debugMode() || script_->isGenerator)
if (cx->compartment()->debugMode() || script_->isGenerator())
return true;
/*

Просмотреть файл

@ -104,7 +104,6 @@ const char js_typeof_str[] = "typeof";
const char js_void_str[] = "void";
const char js_while_str[] = "while";
const char js_with_str[] = "with";
const char js_yield_str[] = "yield";
/*
* For a browser build from 2007-08-09 after the browser starts up there are

Просмотреть файл

@ -153,7 +153,6 @@ extern const char js_typeof_str[];
extern const char js_void_str[];
extern const char js_while_str[];
extern const char js_with_str[];
extern const char js_yield_str[];
namespace js {

Просмотреть файл

@ -526,6 +526,7 @@ FindBody(JSContext *cx, HandleFunction fun, StableCharPtr chars, size_t length,
do {
switch (ts.getToken()) {
case TOK_NAME:
case TOK_YIELD:
if (nest == 0)
onward = false;
break;
@ -1228,14 +1229,7 @@ fun_isGenerator(JSContext *cx, unsigned argc, Value *vp)
return true;
}
bool result = false;
if (fun->hasScript()) {
JSScript *script = fun->nonLazyScript();
JS_ASSERT(script->length != 0);
result = script->isGenerator;
}
JS_SET_RVAL(cx, vp, BooleanValue(result));
JS_SET_RVAL(cx, vp, BooleanValue(fun->isGenerator()));
return true;
}
@ -1456,10 +1450,15 @@ js::Function(JSContext *cx, unsigned argc, Value *vp)
return false;
}
if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7)
tt = TOK_NAME;
if (tt != TOK_NAME) {
if (tt == TOK_TRIPLEDOT) {
hasRest = true;
tt = ts.getToken();
if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7)
tt = TOK_NAME;
if (tt != TOK_NAME) {
if (tt != TOK_ERROR)
ts.reportError(JSMSG_NO_REST_NAME);
@ -1470,7 +1469,7 @@ js::Function(JSContext *cx, unsigned argc, Value *vp)
}
}
if (!formals.append(ts.currentToken().name()))
if (!formals.append(ts.currentName()))
return false;
/*

Просмотреть файл

@ -284,6 +284,18 @@ class JSFunction : public JSObject
return u.i.s.lazy_;
}
js::GeneratorKind generatorKind() const {
if (!isInterpreted())
return js::NotGenerator;
return hasScript() ? nonLazyScript()->generatorKind() : lazyScript()->generatorKind();
}
bool isGenerator() const { return generatorKind() != js::NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
inline void setScript(JSScript *script_);
inline void initScript(JSScript *script_);
void initLazyScript(js::LazyScript *lazy) {

Просмотреть файл

@ -1475,6 +1475,13 @@ js_NewGenerator(JSContext *cx, const FrameRegs &stackRegs)
JS_ASSERT(stackRegs.stackDepth() == 0);
StackFrame *stackfp = stackRegs.fp();
JS_ASSERT(stackfp->script()->isGenerator());
if (stackfp->script()->isStarGenerator()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ES6_UNIMPLEMENTED);
return NULL;
}
Rooted<GlobalObject*> global(cx, &stackfp->global());
RootedObject obj(cx);
{

Просмотреть файл

@ -2787,6 +2787,7 @@ ASTSerializer::function(ParseNode *pn, ASTType type, MutableHandleValue dst)
{
RootedFunction func(cx, pn->pn_funbox->function());
// FIXME: Provide more information (legacy generator vs star generator).
bool isGenerator = pn->pn_funbox->isGenerator();
bool isExpression =

Просмотреть файл

@ -408,9 +408,9 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
FunHasAnyAliasedFormal,
ArgumentsHasVarBinding,
NeedsArgsObj,
IsGenerator,
IsGeneratorExp,
IsLegacyGenerator,
IsStarGenerator,
OwnSource,
ExplicitUseStrict,
SelfHosted
@ -497,12 +497,12 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
scriptBits |= (1 << NeedsArgsObj);
if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
scriptBits |= (1 << OwnSource);
if (script->isGenerator)
scriptBits |= (1 << IsGenerator);
if (script->isGeneratorExp)
scriptBits |= (1 << IsGeneratorExp);
if (script->isLegacyGenerator)
if (script->isLegacyGenerator())
scriptBits |= (1 << IsLegacyGenerator);
if (script->isStarGenerator())
scriptBits |= (1 << IsStarGenerator);
JS_ASSERT(!script->compileAndGo);
JS_ASSERT(!script->hasSingletons);
@ -597,12 +597,14 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
script->setArgumentsHasVarBinding();
if (scriptBits & (1 << NeedsArgsObj))
script->setNeedsArgsObj(true);
if (scriptBits & (1 << IsGenerator))
script->isGenerator = true;
if (scriptBits & (1 << IsGeneratorExp))
script->isGeneratorExp = true;
if (scriptBits & (1 << IsLegacyGenerator))
script->isLegacyGenerator = true;
if (scriptBits & (1 << IsLegacyGenerator)) {
JS_ASSERT(!(scriptBits & (1 << IsStarGenerator)));
script->setGeneratorKind(LegacyGenerator);
} else if (scriptBits & (1 << IsStarGenerator))
script->setGeneratorKind(StarGenerator);
}
JS_STATIC_ASSERT(sizeof(jsbytecode) == 1);
@ -1974,9 +1976,8 @@ JSScript::fullyInitFromEmitter(ExclusiveContext *cx, HandleScript script, Byteco
RootedFunction fun(cx, NULL);
if (funbox) {
JS_ASSERT(!bce->script->noScriptRval);
script->isGenerator = funbox->isGenerator();
script->isGeneratorExp = funbox->inGenexpLambda;
script->isLegacyGenerator = funbox->isLegacyGenerator();
script->setGeneratorKind(funbox->generatorKind());
script->setFunction(funbox->function());
}
@ -2477,8 +2478,8 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
dst->funHasAnyAliasedFormal = src->funHasAnyAliasedFormal;
dst->hasSingletons = src->hasSingletons;
dst->treatAsRunOnce = src->treatAsRunOnce;
dst->isGenerator = src->isGenerator;
dst->isGeneratorExp = src->isGeneratorExp;
dst->setGeneratorKind(src->generatorKind());
/* Copy over hints. */
dst->shouldInline = src->shouldInline;
@ -2919,7 +2920,7 @@ JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script)
if (script->needsArgsObj())
return true;
JS_ASSERT(!script->isGenerator);
JS_ASSERT(!script->isGenerator());
script->needsArgsObj_ = true;
@ -3011,6 +3012,7 @@ LazyScript::LazyScript(JSFunction *fun, void *table, uint32_t numFreeVariables,
version_(version),
numFreeVariables_(numFreeVariables),
numInnerFunctions_(numInnerFunctions),
generatorKindBits_(GeneratorKindAsBits(NotGenerator)),
strict_(false),
bindingsAccessedDynamically_(false),
hasDebuggerStatement_(false),

Просмотреть файл

@ -414,6 +414,19 @@ class ScriptSourceObject : public JSObject
static const uint32_t SOURCE_SLOT = 0;
};
enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
static inline unsigned
GeneratorKindAsBits(GeneratorKind generatorKind) {
return static_cast<unsigned>(generatorKind);
}
static inline GeneratorKind
GeneratorKindFromBits(unsigned val) {
JS_ASSERT(val <= StarGenerator);
return static_cast<GeneratorKind>(val);
}
} /* namespace js */
class JSScript : public js::gc::Cell
@ -519,7 +532,7 @@ class JSScript : public js::gc::Cell
uint16_t nslots; /* vars plus maximum stack depth */
uint16_t staticLevel;/* static level for display maintenance */
// 8-bit fields.
// 4-bit fields.
public:
// The kinds of the optional arrays.
@ -528,15 +541,16 @@ class JSScript : public js::gc::Cell
OBJECTS,
REGEXPS,
TRYNOTES,
LIMIT
ARRAY_KIND_BITS
};
typedef uint8_t ArrayBitsT;
private:
// The bits in this field indicate the presence/non-presence of several
// optional arrays in |data|. See the comments above Create() for details.
ArrayBitsT hasArrayBits;
uint8_t hasArrayBits:4;
// The GeneratorKind of the script.
uint8_t generatorKindBits_:4;
// 1-bit fields.
@ -589,14 +603,9 @@ class JSScript : public js::gc::Cell
#endif
bool invalidatedIdempotentCache:1; /* idempotent cache has triggered invalidation */
// All generators have isGenerator set to true.
bool isGenerator:1;
// If the generator was created implicitly via a generator expression,
// isGeneratorExp will be true.
bool isGeneratorExp:1;
// Generators are either legacy-style (JS 1.7+ starless generators with
// StopIteration), or ES6-style (function* with boxed return values).
bool isLegacyGenerator:1;
bool hasScriptCounts:1;/* script has an entry in
JSCompartment::scriptCountsMap */
@ -647,6 +656,19 @@ class JSScript : public js::gc::Cell
jsbytecode *argumentsBytecode() const { JS_ASSERT(code[0] == JSOP_ARGUMENTS); return code; }
void setArgumentsHasVarBinding();
js::GeneratorKind generatorKind() const {
return js::GeneratorKindFromBits(generatorKindBits_);
}
bool isGenerator() const { return generatorKind() != js::NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
void setGeneratorKind(js::GeneratorKind kind) {
// A script only gets its generator kind set as part of initialization,
// so it can only transition from not being a generator.
JS_ASSERT(!isGenerator());
generatorKindBits_ = GeneratorKindAsBits(kind);
}
/*
* As an optimization, even when argsHasLocalBinding, the function prologue
* may not need to create an arguments object. This is determined by
@ -1038,7 +1060,8 @@ class JSScript : public js::gc::Cell
void markChildren(JSTracer *trc);
};
JS_STATIC_ASSERT(sizeof(JSScript::ArrayBitsT) * 8 >= JSScript::LIMIT);
/* The array kind flags are stored in a 4-bit field; make sure they fit. */
JS_STATIC_ASSERT(JSScript::ARRAY_KIND_BITS <= 4);
/* If this fails, add/remove padding within JSScript. */
JS_STATIC_ASSERT(sizeof(JSScript) % js::gc::CellSize == 0);
@ -1145,7 +1168,9 @@ class LazyScript : public js::gc::Cell
uint32_t version_ : 8;
uint32_t numFreeVariables_ : 24;
uint32_t numInnerFunctions_ : 26;
uint32_t numInnerFunctions_ : 24;
uint32_t generatorKindBits_:2;
// N.B. These are booleans but need to be uint32_t to pack correctly on MSVC.
uint32_t strict_ : 1;
@ -1211,6 +1236,23 @@ class LazyScript : public js::gc::Cell
return (HeapPtrFunction *)&freeVariables()[numFreeVariables()];
}
GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
bool isGenerator() const { return generatorKind() != NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
void setGeneratorKind(GeneratorKind kind) {
// A script only gets its generator kind set as part of initialization,
// so it can only transition from NotGenerator.
JS_ASSERT(!isGenerator());
// Legacy generators cannot currently be lazy.
JS_ASSERT(kind != LegacyGenerator);
generatorKindBits_ = GeneratorKindAsBits(kind);
}
bool strict() const {
return strict_;
}

Просмотреть файл

Просмотреть файл

@ -0,0 +1,97 @@
// This file was written by Andy Wingo <wingo@igalia.com> and originally
// contributed to V8 as generators-parsing.js, available here:
//
// http://code.google.com/p/v8/source/browse/branches/bleeding_edge/test/mjsunit/harmony/generators-parsing.js
function assertSyntaxError(str) {
var msg;
var evil = eval;
try {
// Non-direct eval.
evil(str);
} catch (exc) {
if (exc instanceof SyntaxError)
return;
msg = "Assertion failed: expected SyntaxError, got " + exc;
}
if (msg === undefined)
msg = "Assertion failed: expected SyntaxError, but no exception thrown";
throw new Error(msg + " - " + str);
}
// Yield statements.
function* g() { yield 3; yield 4; }
// Yield expressions.
function* g() { (yield 3) + (yield 4); }
// You can have a generator in strict mode.
function* g() { "use strict"; yield 3; yield 4; }
// Generators can have return statements also, which internally parse to a kind
// of yield expression.
function* g() { yield 1; return; }
function* g() { yield 1; return 2; }
function* g() { yield 1; return 2; yield "dead"; }
// Generator expression.
(function* () { yield 3; });
// Named generator expression.
(function* g() { yield 3; });
// Generators do not have to contain yield expressions.
function* g() { }
// YieldExpressions can occur in the RHS of a YieldExpression.
function* g() { yield yield 1; }
function* g() { yield 3 + (yield 4); }
// Generator definitions with a name of "yield" are not specifically ruled out
// by the spec, as the `yield' name is outside the generator itself. However,
// in strict-mode, "yield" is an invalid identifier.
function* yield() { (yield 3) + (yield 4); }
assertSyntaxError("function* yield() { 'use strict'; (yield 3) + (yield 4); }");
// In classic mode, yield is a normal identifier, outside of generators.
function yield(yield) { yield: yield (yield + yield (0)); }
// Yield is always valid as a key in an object literal.
({ yield: 1 });
function* g() { yield ({ yield: 1 }) }
function* g() { yield ({ get yield() { return 1; }}) }
// Yield is a valid property name.
function* g(obj) { yield obj.yield; }
// Checks that yield is a valid label in classic mode, but not valid in a strict
// mode or in generators.
function f() { yield: 1 }
assertSyntaxError("function f() { 'use strict'; yield: 1 }")
assertSyntaxError("function* g() { yield: 1 }")
// Yield is only a keyword in the body of the generator, not in nested
// functions.
function* g() { function f(yield) { yield (yield + yield (0)); } }
// Yield needs a RHS.
assertSyntaxError("function* g() { yield; }");
// Yield in a generator is not an identifier.
assertSyntaxError("function* g() { yield = 10; }");
// Yield binds very loosely, so this parses as "yield (3 + yield 4)", which is
// invalid.
assertSyntaxError("function* g() { yield 3 + yield 4; }");
// Yield is still a future-reserved-word in strict mode
assertSyntaxError("function f() { 'use strict'; var yield = 13; }");
// The name of the NFE is let-bound in G, so is invalid.
assertSyntaxError("function* g() { yield (function yield() {}); }");
// In generators, yield is invalid as a formal argument name.
assertSyntaxError("function* g(yield) { yield (10); }");
if (typeof reportCompare == "function")
reportCompare(true, true);

Просмотреть файл

@ -152,6 +152,7 @@
macro(void0, void0, "(void 0)") \
macro(watch, watch, "watch") \
macro(writable, writable, "writable") \
macro(yield, yield, "yield") \
/* Type names must be contiguous and ordered; see js::TypeName. */ \
macro(undefined, undefined, "undefined") \
macro(object, object, "object") \

Просмотреть файл

@ -72,8 +72,12 @@
macro(protected, protected_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
macro(public, public_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
macro(static, static_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
/* ES5 future reserved keyword in strict mode, keyword in JS1.7 even when not strict. */ \
macro(yield, yield, TOK_YIELD, JSVERSION_1_7) \
/* \
* ES5 future reserved keyword in strict mode, keyword in JS1.7 even when \
* not strict, keyword inside function* in all versions. Punt logic to \
* parser. \
*/ \
macro(yield, yield, TOK_YIELD, JSVERSION_DEFAULT) \
/* Various conditional keywords. */ \
FOR_CONST_KEYWORD(macro) \
FOR_LET_KEYWORD(macro)