Mozilla Home
Privacy
Cookies
Legal
Bugzilla
Browse
Advanced Search
New Bug
Reports
Documentation
Log In
Log In with GitHub
or
Remember me
Browse
Advanced Search
New Bug
Reports
Documentation
Attachment 790202 Details for
Bug 666399
[patch]
new Harmony syntax for generators v5
wip-monday (text/plain), 99.55 KB, created by
Andy Wingo [:wingo]
(
hide
)
Description:
new Harmony syntax for generators v5
Filename:
MIME Type:
Creator:
Andy Wingo [:wingo]
Size:
99.55 KB
patch
obsolete
># HG changeset patch ># User Andy Wingo <wingo@igalia.com> ># Date 1376489076 -7200 ># Wed Aug 14 16:04:36 2013 +0200 ># Node ID 0609a326560daba36bdc8475ab8afa3154d872c8 ># Parent c2050f7348022bf9a4ebc24a36d7ee7c33b1258e >Bug 666399 - new Harmony syntax for generators > >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. > >diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp >--- a/js/src/frontend/BytecodeCompiler.cpp >+++ b/js/src/frontend/BytecodeCompiler.cpp >@@ -64,17 +64,17 @@ CheckArgumentsWithinEval(JSContext *cx, > // |arguments| within an eval. > RootedScript script(cx, fun->nonLazyScript()); > if (script->argumentsHasVarBinding()) { > if (!JSScript::argumentsOptimizationFailed(cx, script)) > return false; > } > > // 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; > } > > return true; > } > > static bool >@@ -288,17 +288,18 @@ frontend::CompileScript(ExclusiveContext > if (evalCaller && evalCaller->functionOrCallerFunction()) { > /* > * An eval script in a caller frame needs to have its enclosing > * function captured in case it refers to an upvar, and someone > * wishes to decompile it while it's running. > */ > 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); > } > } > > bool canHaveDirectives = true; > for (;;) { >@@ -408,17 +409,19 @@ frontend::CompileLazyFunction(JSContext > #endif > > Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length, > /* foldConstants = */ true, NULL, lazy); > > 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; > > if (!NameFunctions(cx, pn)) > return false; > > RootedObject enclosingScope(cx, lazy->enclosingScope()); > JS::RootedScriptSource sourceObject(cx, lazy->sourceObject()); >@@ -512,17 +515,17 @@ frontend::CompileFunctionBody(JSContext > Directives directives(options.strictOption); > > TokenStream::Position start(parser.keepAtoms); > parser.tokenStream.tell(&start); > > 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; > > if (parser.hadAbortedSyntaxParse()) { > // Hit some unrecoverable ambiguity during an inner syntax parse. > // Syntax parsing has now been disabled in the parser, so retry > // the parse. > parser.clearAbortedSyntaxParse(); >diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp >--- a/js/src/frontend/ParseNode.cpp >+++ b/js/src/frontend/ParseNode.cpp >@@ -379,19 +379,19 @@ Parser<FullParseHandler>::cloneParseTree > > switch (pn->getArity()) { > #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO > > case PN_CODE: > 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; > pn->pn_blockid = opn->pn_blockid; > break; > > case PN_LIST: > pn->makeEmpty(); >diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp >--- a/js/src/frontend/Parser.cpp >+++ b/js/src/frontend/Parser.cpp >@@ -51,17 +51,16 @@ using namespace js::gc; > using mozilla::Maybe; > > namespace js { > 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. > * NB: this macro uses cx and ts from its lexical environment. > */ > #define MUST_MATCH_TOKEN(tt, errno) \ > JS_BEGIN_MACRO \ > if (tokenStream.getToken() != tt) { \ >@@ -450,23 +449,24 @@ Parser<ParseHandler>::newObjectBox(JSObj > traceListHead = objbox; > > return objbox; > } > > 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), > useAsm(directives.asmJS()), > insideUseAsm(outerpc && outerpc->useAsmOrInsideUseAsm()), > usesArguments(false), > usesApply(false), > funCxFlags() >@@ -517,30 +517,31 @@ FunctionBox::FunctionBox(ExclusiveContex > if (parent && parent->inWith) > inWith = true; > } > } > > 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)); > > /* > * We use JSContext.tempLifoAlloc to allocate parsed objects and place them > * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc > * arenas containing the entries must be alive until we are done with > * scanning, parsing and code generation for the whole script or top-level > * function. > */ > FunctionBox *funbox = > alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc, >- inheritedDirectives, options().extraWarningsOption); >+ inheritedDirectives, options().extraWarningsOption, >+ generatorKind); > if (!funbox) { > js_ReportOutOfMemory(context); > return NULL; > } > > traceListHead = funbox; > if (fn) > handler.setFunctionBox(fn, funbox); >@@ -847,31 +848,33 @@ Parser<ParseHandler>::checkStrictBinding > } > > return true; > } > > template <> > ParseNode * > Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals, >+ GeneratorKind generatorKind, > Directives inheritedDirectives, > Directives *newDirectives) > { > Node fn = handler.newFunctionDefinition(); > if (!fn) > return null(); > > ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &handler); > if (!argsbody) > return null(); > argsbody->setOp(JSOP_NOP); > 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); > > ParseContext<FullParseHandler> funpc(this, pc, fn, funbox, newDirectives, > /* staticLevel = */ 0, /* bodyid = */ 0); > if (!funpc.init()) > return null(); >@@ -1066,32 +1069,41 @@ Parser<ParseHandler>::functionBody(Funct > if (!kid) > return null(); > > pn = handler.newReturnStatement(kid, handler.getPosition(kid)); > if (!pn) > 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); > return null(); > } > if (type == ExpressionBody) { > reportBadReturn(pn, ParseError, > JSMSG_BAD_GENERATOR_RETURN, > 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. */ > if (options().extraWarningsOption && pc->funHasReturnExpr && !checkFinalReturn(pn)) > return null(); > > /* Define the 'arguments' binding if necessary. */ > if (!checkFunctionArguments()) >@@ -1617,34 +1629,40 @@ Parser<ParseHandler>::functionArguments( > if (!list) > return false; > *listp = list; > } > break; > } > #endif /* JS_HAS_DESTRUCTURING */ > >+ case TOK_YIELD: >+ if (!checkYieldNameValidity(JSMSG_MISSING_FORMAL)) >+ return false; >+ goto TOK_NAME; >+ > case TOK_TRIPLEDOT: > { > hasRest = true; > tt = tokenStream.getToken(); > if (tt != TOK_NAME) { > if (tt != TOK_ERROR) > 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; > > if (tokenStream.matchToken(TOK_ASSIGN)) { > // A default argument without parentheses would look like: > // a = expr => body, but both operators are right-associative, so > // that would have been parsed as a = (expr => body) instead. >@@ -1685,28 +1703,45 @@ Parser<ParseHandler>::functionArguments( > report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL); > return false; > } > } > > 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, > ParseNode **pn_, FunctionSyntaxKind kind, > bool *pbodyProcessed) > { > ParseNode *&pn = *pn_; > *pbodyProcessed = false; > > /* 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 > * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). > */ > if (Definition *dn = pc->decls().lookupFirst(funName)) { > JS_ASSERT(!dn->isUsed()); > JS_ASSERT(dn->isDefn()); >@@ -1810,17 +1845,19 @@ Parser<FullParseHandler>::checkFunctionD > } > > // When a lazily-parsed function is called, we only fully parse (and emit) > // that function, not any of its nested children. The initial syntax-only > // parse recorded the free variables of nested functions and their extents, > // 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; > > if (!addFreeVariablesFromLazyFunction(fun, pc)) > return false; > > // The position passed to tokenStream.advance() is relative to > // userbuf.base() while LazyScript::{begin,end} offsets are relative to >@@ -1886,16 +1923,19 @@ Parser<SyntaxParseHandler>::checkFunctio > Node *pn, FunctionSyntaxKind kind, > bool *pbodyProcessed) > { > *pbodyProcessed = false; > > /* 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 > * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). > */ > if (DefinitionNode dn = pc->decls().lookupFirst(funName)) { > if (dn == Definition::CONST) { > JSAutoByteString name; >@@ -1924,17 +1964,18 @@ Parser<SyntaxParseHandler>::checkFunctio > } > > return true; > } > > 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); > > /* Make a TOK_FUNCTION node. */ > Node pn = handler.newFunctionDefinition(); > if (!pn) > return null(); > >@@ -1952,17 +1993,17 @@ Parser<ParseHandler>::functionDef(Handle > // Speculatively parse using the directives of the parent parsing context. > // If a directive is encountered (e.g., "use strict") that changes how the > // function should have been parsed, we backup and reparse with the new set > // of directives. > Directives directives(pc); > 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(); > > // Assignment must be monotonic to prevent reparsing iloops > JS_ASSERT_IF(directives.strict(), newDirectives.strict()); > JS_ASSERT_IF(directives.asmJS(), newDirectives.asmJS()); > directives = newDirectives; >@@ -2056,35 +2097,37 @@ Parser<SyntaxParseHandler>::finishFuncti > JS_ASSERT(i == numFreeVariables); > > HeapPtrFunction *innerFunctions = lazy->innerFunctions(); > for (size_t i = 0; i < numInnerFunctions; i++) > innerFunctions[i].init(pc->innerFunctions[i]); > > if (pc->sc->strict) > lazy->setStrict(); >+ lazy->setGeneratorKind(funbox->generatorKind()); > if (funbox->usesArguments && funbox->usesApply) > lazy->setUsesArgumentsAndApply(); > PropagateTransitiveParseFlags(funbox, lazy); > > fun->initLazyScript(lazy); > return true; > } > > 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; > > // Try a syntax parse for this inner function. > do { > Parser<SyntaxParseHandler> *parser = handler.syntaxParser; > if (!parser) > break; >@@ -2150,23 +2193,24 @@ Parser<FullParseHandler>::functionArgsAn > PropagateTransitiveParseFlags(funbox, outerpc->sc); > return true; > } > > 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; > > // Initialize early for possible flags mutation via destructuringExpr. > ParseContext<SyntaxParseHandler> funpc(this, pc, handler.null(), funbox, newDirectives, > outerpc->staticLevel + 1, outerpc->blockidGen); > if (!funpc.init()) > return false; >@@ -2182,24 +2226,25 @@ Parser<SyntaxParseHandler>::functionArgs > // not need any further parsing or processing of the inner function. > JS_ASSERT(fun->lazyScript()); > return outerpc->innerFunctions.append(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(); > > Directives newDirectives = directives; > ParseContext<FullParseHandler> funpc(this, /* parent = */ NULL, pn, funbox, > &newDirectives, staticLevel, /* bodyid = */ 0); > if (!funpc.init()) > return null(); >@@ -2263,16 +2308,20 @@ Parser<ParseHandler>::functionArgsAndBod > if (kind == Arrow && !tokenStream.matchToken(TOK_ARROW)) { > report(ParseError, false, null(), JSMSG_BAD_ARROW_ARGS); > return false; > } > > // 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(); > } > > Node body = functionBody(kind, bodyType); > if (!body) > return false; >@@ -2300,17 +2349,17 @@ Parser<ParseHandler>::functionArgsAndBod > > return finishFunctionDefinition(pn, funbox, prelude, body); > } > > 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); > return NULL; > } > > ParseNode *pn = CodeNode::create(PNK_MODULE, &handler); > if (!pn) >@@ -2343,54 +2392,92 @@ template <> > SyntaxParseHandler::Node > Parser<SyntaxParseHandler>::moduleDecl() > { > JS_ALWAYS_FALSE(abortIfSyntaxParser()); > 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); > return null(); > } > > /* We forbid function statements in strict mode code. */ > if (!pc->atBodyLevel() && pc->sc->needStrictChecks() && > !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); > } > > /* > * Return true if this node, known to be an unparenthesized string literal, > * could be the string of a directive in a Directive Prologue. Directive > * strings never contain escape sequences or line continuations. > * isEscapeFreeStringLiteral, below, checks whether the node itself could be > * a directive. >@@ -2514,17 +2601,17 @@ Parser<ParseHandler>::maybeParseDirectiv > if (tokenStream.sawOctalEscape()) { > report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL); > return false; > } > pc->sc->strict = true; > } > } > } 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); > } > } > return true; > } > > /* >@@ -2596,25 +2683,31 @@ Parser<ParseHandler>::condition() > if (handler.isOperationWithoutParens(pn, PNK_ASSIGN) && > !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN)) > { > return null(); > } > return pn; > } > >-static bool >-MatchLabel(TokenStream &ts, MutableHandlePropertyName label) >-{ >- TokenKind tt = ts.peekTokenSameLine(TokenStream::Operand); >+template <typename ParseHandler> >+bool >+Parser<ParseHandler>::matchLabel(MutableHandle<PropertyName*> label) >+{ >+ 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); > } > return true; > } > > template <typename ParseHandler> > bool >@@ -3230,17 +3323,17 @@ Parser<SyntaxParseHandler>::pushLetScope > * Parse a let block statement or let expression (determined by 'letContext'). > * In both cases, bindings are not hoisted to the top of the enclosing block > * and thus must be carefully injected between variables() and the let body. > */ > 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) > return null(); > > uint32_t begin = pos().begin; > > MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET); >@@ -3320,17 +3413,17 @@ PushBlocklikeStatement(StmtInfoPC *stmt, > PushStatementPC(pc, stmt, type); > return GenerateBlockId(pc, stmt->blockid); > } > > 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)) > return null(); > > Node list = statements(); > if (!list) > return null(); >@@ -3362,17 +3455,16 @@ Parser<ParseHandler>::newBindingNode(Pro > pc->lexdeps->remove(p); > handler.setPosition(pn, pos()); > return pn; > } > } > } > > /* Make a new node for this declarator name (or destructuring pattern). */ >- JS_ASSERT(tokenStream.currentToken().type == TOK_NAME); > return newName(name); > } > > /* > * The 'blockObj' parameter is non-null when parsing the 'vars' in a let > * expression, block statement, non-top-level let declaration in statement > * context, and the let-initializer of a for-statement. > */ >@@ -3451,22 +3543,27 @@ Parser<ParseHandler>::variables(ParseNod > if (!pn2) > return null(); > handler.addList(pn, pn2); > continue; > } > #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(); > if (data.op == JSOP_DEFCONST) > handler.setFlag(pn2, PND_CONST); > data.pn = pn2; > if (!data.binder(&data, name, this)) > return null(); >@@ -4113,19 +4210,24 @@ Parser<SyntaxParseHandler>::forStatement > * the types of 'for' statements likely to be seen in web content. > */ > JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); > > StmtInfoPC forStmt(context); > 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); > > /* True if we have 'for (var ...)'. */ > bool isForDecl = false; > bool simpleForDecl = true; > >@@ -4317,17 +4419,17 @@ Parser<ParseHandler>::switchStatement() > template <typename ParseHandler> > typename ParseHandler::Node > Parser<ParseHandler>::continueStatement() > { > JS_ASSERT(tokenStream.isCurrentTokenType(TOK_CONTINUE)); > uint32_t begin = pos().begin; > > RootedPropertyName label(context); >- if (!MatchLabel(tokenStream, &label)) >+ if (!matchLabel(&label)) > return null(); > > StmtInfoPC *stmt = pc->topStmt; > if (label) { > for (StmtInfoPC *stmt2 = NULL; ; stmt = stmt->down) { > if (!stmt) { > report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND); > return null(); >@@ -4364,17 +4466,17 @@ Parser<ParseHandler>::continueStatement( > template <typename ParseHandler> > typename ParseHandler::Node > Parser<ParseHandler>::breakStatement() > { > JS_ASSERT(tokenStream.isCurrentTokenType(TOK_BREAK)); > uint32_t begin = pos().begin; > > RootedPropertyName label(context); >- if (!MatchLabel(tokenStream, &label)) >+ if (!matchLabel(&label)) > return null(); > StmtInfoPC *stmt = pc->topStmt; > if (label) { > for (; ; stmt = stmt->down) { > if (!stmt) { > report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND); > return null(); > } >@@ -4395,118 +4497,164 @@ Parser<ParseHandler>::breakStatement() > if (!MatchOrInsertSemicolon(tokenStream)) > return null(); > > return handler.newBreakStatement(label, TokenPos(begin, pos().end)); > } > > template <typename ParseHandler> > typename ParseHandler::Node >-Parser<ParseHandler>::returnStatementOrYieldExpression() >-{ >- JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN) || >- tokenStream.isCurrentTokenType(TOK_YIELD)); >- bool isYield = tokenStream.isCurrentTokenType(TOK_YIELD); >+Parser<ParseHandler>::returnStatement() >+{ >+ 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(); >- } >- >- Node pn = isYield >- ? handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode) >- : handler.newReturnStatement(exprNode, TokenPos(begin, pos().end)); >+ if (!MatchOrInsertSemicolon(tokenStream)) >+ return null(); >+ >+ 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)) > { > 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() > { > // test262/ch12/12.10/12.10-0-1.js fails if we try to parse with-statements > // in syntax-parse mode. See bug 892583. > if (handler.syntaxParser) { > handler.disableSyntaxParser(); >@@ -4566,17 +4714,17 @@ Parser<SyntaxParseHandler>::withStatemen > return null(); > } > > template <typename ParseHandler> > 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); > return null(); > } > } > > tokenStream.consumeKnownToken(TOK_COLON); >@@ -4707,19 +4855,23 @@ Parser<ParseHandler>::tryStatement() > case TOK_LB: > case TOK_LC: > catchName = destructuringExpr(&data, tt); > if (!catchName) > return null(); > 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(); > data.pn = catchName; > if (!data.binder(&data, label, this)) > return null(); > break; > } >@@ -4845,17 +4997,17 @@ Parser<ParseHandler>::statement(bool can > return forStatement(); > case TOK_SWITCH: > return switchStatement(); > case TOK_CONTINUE: > return continueStatement(); > case TOK_BREAK: > return breakStatement(); > case TOK_RETURN: >- return returnStatementOrYieldExpression(); >+ return returnStatement(); > case TOK_WITH: > return withStatement(); > case TOK_THROW: > return throwStatement(); > case TOK_TRY: > return tryStatement(); > case TOK_FUNCTION: > return functionStmt(); >@@ -4876,20 +5028,28 @@ Parser<ParseHandler>::statement(bool can > > case TOK_STRING: > if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) { > if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL)) > return null(); > } > 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(); > } > return expressionStatement(); > > default: > return expressionStatement(); >@@ -5192,18 +5352,18 @@ Parser<ParseHandler>::assignExpr() > return identifierName(); > > if (tt == TOK_NUMBER && tokenStream.nextTokenEndsExpr()) > return newNumber(tokenStream.currentToken()); > > 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(); > > // Save the tokenizer state in case we find an arrow function and have to > // rewind. > TokenStream::Position start(keepAtoms); > tokenStream.tell(&start); > >@@ -5231,17 +5391,17 @@ Parser<ParseHandler>::assignExpr() > tokenStream.seek(start); > if (!abortIfSyntaxParser()) > return null(); > > if (tokenStream.getToken() == TOK_ERROR) > return null(); > tokenStream.ungetToken(); > >- return functionDef(NullPtr(), start, Normal, Arrow); >+ return functionDef(NullPtr(), start, Normal, Arrow, NotGenerator); > } > > default: > JS_ASSERT(!tokenStream.isCurrentTokenAssignment()); > tokenStream.ungetToken(); > return lhs; > } > >@@ -5652,17 +5812,17 @@ Parser<FullParseHandler>::comprehensionT > } > > unsigned adjust; > ParseNode *pn, *pn2, *pn3, **pnp; > StmtInfoPC stmtInfo(context); > BindData<FullParseHandler> data(context); > TokenKind tt; > >- JS_ASSERT(tokenStream.currentToken().type == TOK_FOR); >+ JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); > > if (kind == PNK_SEMI) { > /* > * Generator expression desugars to an immediately applied lambda that > * yields the next value from a for-in loop (possibly nested, and with > * optional if guard). Make pn be the TOK_LC body node. > */ > pn = pushLexicalScope(&stmtInfo); >@@ -5737,17 +5897,17 @@ Parser<FullParseHandler>::comprehensionT > pn3 = primaryExpr(tt); > pc->inDeclDestructuring = false; > if (!pn3) > return null(); > break; > #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 > * JSOP_GETLOCAL here, because we don't yet know the block's depth > * in the operand stack frame. The code generator computes that, > * and it tries to bind all names to slots, so we must let it do > * the deed. > */ >@@ -5945,17 +6105,18 @@ Parser<FullParseHandler>::generatorExpr( > ParseContext<FullParseHandler> *outerpc = pc; > > RootedFunction fun(context, newFunction(outerpc, /* atom = */ NullPtr(), Expression)); > if (!fun) > return null(); > > /* 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(); > > ParseContext<FullParseHandler> genpc(this, outerpc, genfn, genFunbox, > /* newDirectives = */ NULL, outerpc->staticLevel + 1, > outerpc->blockidGen); > if (!genpc.init()) > return null(); >@@ -5965,17 +6126,17 @@ Parser<FullParseHandler>::generatorExpr( > * come from the kid. So we propagate these flags into genfn. For code > * simplicity we also do not detect if the flags were only set in the > * kid and could be removed from pc->sc. > */ > genFunbox->anyCxFlags = outerpc->sc->anyCxFlags; > if (outerpc->sc->isFunctionBox()) > genFunbox->funCxFlags = outerpc->sc->asFunctionBox()->funCxFlags; > >- genFunbox->setIsLegacyGenerator(); >+ JS_ASSERT(genFunbox->isLegacyGenerator()); > genFunbox->inGenexpLambda = true; > genfn->pn_blockid = genpc.bodyid; > > ParseNode *body = comprehensionTail(pn, outerpc->blockid(), true, outerpc); > if (!body) > return null(); > JS_ASSERT(!genfn->pn_body); > genfn->pn_body = body; >@@ -6106,17 +6267,17 @@ Parser<ParseHandler>::memberExpr(TokenKi > > while ((tt = tokenStream.getToken()) > TOK_EOF) { > Node nextMember; > if (tt == TOK_DOT) { > tt = tokenStream.getToken(TokenStream::KeywordIsName); > 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(); > } else { > report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT); > return null(); > } > } else if (tt == TOK_LB) { >@@ -6181,19 +6342,17 @@ Parser<ParseHandler>::newName(PropertyNa > { > return handler.newName(name, pc->blockid(), pos()); > } > > 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(); > > if (!pc->inDeclDestructuring && !noteNameUse(name, pn)) > return null(); > > return pn; >@@ -6397,31 +6556,31 @@ Parser<ParseHandler>::primaryExpr(TokenK > tmp = DoubleValue(tokenStream.currentToken().number()); > atom = ToAtom<CanGC>(context, HandleValue::fromMarkedLocation(&tmp)); > if (!atom) > return null(); > pn3 = newNumber(tokenStream.currentToken()); > 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) { > op = JSOP_INITPROP_SETTER; > } else { > pn3 = handler.newIdentifier(atom, pos()); > if (!pn3) > return null(); > break; > } > > tt = tokenStream.getToken(TokenStream::KeywordIsName); > if (tt == TOK_NAME) { >- atom = tokenStream.currentToken().name(); >+ atom = tokenStream.currentName(); > pn3 = newName(atom->asPropertyName()); > if (!pn3) > return null(); > } else if (tt == TOK_STRING) { > atom = tokenStream.currentToken().atom(); > > uint32_t index; > if (atom->isIndex(&index)) { >@@ -6458,17 +6617,17 @@ Parser<ParseHandler>::primaryExpr(TokenK > > handler.setListFlag(pn, PNX_NONCONST); > > /* NB: Getter function in { get x(){} } is unnamed. */ > Rooted<PropertyName*> funName(context, NULL); > TokenStream::Position start(keepAtoms); > tokenStream.tell(&start); > pn2 = functionDef(funName, start, op == JSOP_INITPROP_GETTER ? Getter : Setter, >- Expression); >+ Expression, NotGenerator); > if (!pn2) > return null(); > pn2 = handler.newBinary(PNK_COLON, pn3, pn2, op); > goto skip; > } > case TOK_STRING: { > atom = tokenStream.currentToken().atom(); > uint32_t index; >@@ -6614,16 +6773,20 @@ Parser<ParseHandler>::primaryExpr(TokenK > if (!genexp) > MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); > return pn; > } > > case TOK_STRING: > return stringLiteral(); > >+ case TOK_YIELD: >+ if (!checkYieldNameValidity()) >+ return null(); >+ // Fall through. > case TOK_NAME: > return identifierName(); > > case TOK_REGEXP: > return newRegExp(); > > case TOK_NUMBER: > return newNumber(tokenStream.currentToken()); >@@ -6675,17 +6838,17 @@ Parser<ParseHandler>::primaryExpr(TokenK > return null(); > } > } > > 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) > *genexp = false; > > uint32_t startYieldOffset = pc->lastYieldOffset; > > /* >diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h >--- a/js/src/frontend/Parser.h >+++ b/js/src/frontend/Parser.h >@@ -99,28 +99,30 @@ struct ParseContext : public GenericPars > StmtInfoPC *topStmt; /* top of statement info stack */ > StmtInfoPC *topScopeStmt; /* top lexical scope statement */ > Rooted<StaticBlockObject *> blockChain; > /* compile time block scope chain */ > Node maybeFunction; /* sc->isFunctionBox, the pn where pn->pn_funbox == sc */ > > 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) */ > private: > AtomDecls<ParseHandler> decls_; /* function, const, and var declarations */ > DeclVector args_; /* argument definitions */ > DeclVector vars_; /* var/const definitions */ > >@@ -244,17 +246,16 @@ struct ParseContext : public GenericPars > : GenericParseContext(parent, sc), > bodyid(0), // initialized in init() > blockidGen(bodyid), // used to set |bodyid| and subsequently incremented in init() > topStmt(NULL), > topScopeStmt(NULL), > blockChain(prs->context), > maybeFunction(maybeFunction), > staticLevel(staticLevel), >- generatorParseMode(NotGenerator), > lastYieldOffset(NoYieldOffset), > blockNode(ParseHandler::null()), > decls_(prs->context, prs->alloc), > args_(prs->context), > vars_(prs->context), > parserPC(&prs->pc), > oldpc(prs->pc), > lexdeps(prs->context), >@@ -394,17 +395,17 @@ class Parser : private AutoGCRooter, pub > > /* > * Allocate a new parsed object or function container from > * cx->tempLifoAlloc. > */ > 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 > * is optional if this is a function expression). > */ > JSFunction *newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind); > > void trace(JSTracer *trc); >@@ -427,23 +428,26 @@ class Parser : private AutoGCRooter, pub > inline bool abortIfSyntaxParser(); > > public: > > /* Public entry points for parsing. */ > 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 > * statements; pass ExpressionBody if the body is a single expression. > */ > enum FunctionBodyType { StatementListBody, ExpressionBody }; > Node functionBody(FunctionSyntaxKind kind, FunctionBodyType type); > >@@ -481,65 +485,70 @@ class Parser : private AutoGCRooter, pub > Node blockStatement(); > Node ifStatement(); > Node doWhileStatement(); > Node whileStatement(); > Node forStatement(); > Node switchStatement(); > Node continueStatement(); > Node breakStatement(); >- Node returnStatementOrYieldExpression(); >+ Node returnStatement(); > Node withStatement(); > Node labeledStatement(); > Node throwStatement(); > Node tryStatement(); > Node debuggerStatement(); > > #if JS_HAS_BLOCK_SCOPE > Node letStatement(); > #endif > Node expressionStatement(); > Node variables(ParseNodeKind kind, bool *psimple = NULL, > StaticBlockObject *blockObj = NULL, > VarContext varContext = HoistVars); > Node expr(); > Node assignExpr(); > Node assignExprWithoutYield(unsigned err); >+ Node yieldExpression(); > Node condExpr1(); > Node orExpr1(); > Node unaryExpr(); > Node memberExpr(TokenKind tt, bool allowCallSyntax); > Node primaryExpr(TokenKind tt); > Node parenExpr(bool *genexp = NULL); > > /* > * Additional JS parsers. > */ > 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); > > Node condition(); > Node comprehensionTail(Node kid, unsigned blockid, bool isGenexp, > ParseContext<ParseHandler> *outerpc, > ParseNodeKind kind = PNK_SEMI, JSOp op = JSOP_NOP); > bool arrayInitializerComprehensionTail(Node pn); > Node generatorExpr(Node kid); > bool argumentList(Node listNode); > Node letBlock(LetContext letContext); > Node destructuringExpr(BindData<ParseHandler> *data, TokenKind tt); > > Node identifierName(); > >+ bool matchLabel(MutableHandle<PropertyName*> label); >+ bool checkYieldNameValidity(unsigned errorNumber = JSMSG_SYNTAX_ERROR); >+ > bool allowsForEachIn() { > #if !JS_HAS_FOR_EACH_IN > return false; > #else > return versionNumber() >= JSVERSION_1_6; > #endif > } > >@@ -550,16 +559,17 @@ class Parser : private AutoGCRooter, pub > IncDecAssignment > }; > > bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor); > bool matchInOrOf(bool *isForOfp); > > 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); > bool addFreeVariablesFromLazyFunction(JSFunction *fun, ParseContext<ParseHandler> *pc); > > bool isValidForStatementLHS(Node pn1, JSVersion version, > bool forDecl, bool forEach, bool forOf); > bool checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder); >diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h >--- a/js/src/frontend/SharedContext.h >+++ b/js/src/frontend/SharedContext.h >@@ -67,20 +67,16 @@ class AnyContextFlags > { } > }; > > 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; > > // This function does something that can extend the set of bindings in its > // call objects --- it does a direct eval in non-strict code, or includes a > // function statement (as opposed to a function definition). > // >@@ -124,18 +120,17 @@ class FunctionContextFlags > // dataflow analysis only looks at how JSOP_ARGUMENTS is used, so it will > // be unsound in several cases. The frontend filters out such cases by > // setting this flag which eagerly sets script->needsArgsObj to true. > // > bool definitelyNeedsArgsObj:1; > > public: > FunctionContextFlags() >- : isLegacyGenerator(false), >- mightAliasLocals(false), >+ : mightAliasLocals(false), > hasExtensibleScope(false), > needsDeclEnvObject(false), > argumentsHasLocalBinding(false), > definitelyNeedsArgsObj(false) > { } > }; > > class GlobalSharedContext; >@@ -259,46 +254,57 @@ class FunctionBox : public ObjectBox, pu > { > public: > Bindings bindings; /* bindings for this function */ > uint32_t bufStart; > uint32_t bufEnd; > 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 */ > bool useAsm:1; /* function contains "use asm" directive */ > bool insideUseAsm:1; /* nested function of function of "use asm" directive */ > > // Fields for use in heuristics. > bool usesArguments:1; /* contains a free use of 'arguments' */ > bool usesApply:1; /* contains an f.apply() call */ > > FunctionContextFlags funCxFlags; > > 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; } > void setArgumentsHasLocalBinding() { funCxFlags.argumentsHasLocalBinding = true; } > void setDefinitelyNeedsArgsObj() { JS_ASSERT(funCxFlags.argumentsHasLocalBinding); > funCxFlags.definitelyNeedsArgsObj = true; } > > // Return whether this function has either specified "use asm" or is >diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp >--- a/js/src/frontend/TokenStream.cpp >+++ b/js/src/frontend/TokenStream.cpp >@@ -927,20 +927,20 @@ TokenStream::checkForKeyword(const jscha > // Working keyword. > if (ttp) { > *ttp = kw->tokentype; > return true; > } > 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; > } > > // Strict reserved word. > return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars); > } > > enum FirstCharKind { >diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h >--- a/js/src/frontend/TokenStream.h >+++ b/js/src/frontend/TokenStream.h >@@ -402,16 +402,23 @@ class MOZ_STACK_CLASS TokenStream > const CharBuffer &getTokenbuf() const { return tokenbuf; } > const char *getFilename() const { return filename; } > unsigned getLineno() const { return lineno; } > unsigned getColumn() const { return userbuf.addressOfNextRawChar() - linebase - 1; } > JSPrincipals *getOriginPrincipals() const { return originPrincipals; } > 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); > } > > // Flag methods. > bool isEOF() const { return flags.isEOF; } > bool sawOctalEscape() const { return flags.sawOctalEscape; } > bool hadError() const { return flags.hadError; } >diff --git a/js/src/jit-test/tests/generators/es6-syntax.js b/js/src/jit-test/tests/generators/es6-syntax.js >new file mode 100644 >--- /dev/null >+++ b/js/src/jit-test/tests/generators/es6-syntax.js >@@ -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; })()") >diff --git a/js/src/jit/AsmJS.cpp b/js/src/jit/AsmJS.cpp >--- a/js/src/jit/AsmJS.cpp >+++ b/js/src/jit/AsmJS.cpp >@@ -4620,17 +4620,17 @@ ParseFunction(ModuleCompiler &m, ParseNo > m.cx()->global(), name, JSFunction::FinalizeKind, > TenuredObject)); > if (!fun) > return false; > > 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; > > Directives newDirectives = directives; > AsmJSParseContext funpc(&m.parser(), outerpc, fn, funbox, &newDirectives, > outerpc->staticLevel + 1, outerpc->blockidGen); > if (!funpc.init()) > return false; >diff --git a/js/src/js.msg b/js/src/js.msg >--- a/js/src/js.msg >+++ b/js/src/js.msg >@@ -404,8 +404,9 @@ MSG_DEF(JSMSG_DEPRECATED_SOURCE_MAP, 35 > MSG_DEF(JSMSG_BAD_DESTRUCT_ASSIGN, 351, 1, JSEXN_SYNTAXERR, "can't assign to {0} using destructuring assignment") > MSG_DEF(JSMSG_BINARYDATA_ARRAYTYPE_BAD_ARGS, 352, 0, JSEXN_ERR, "Invalid arguments") > MSG_DEF(JSMSG_BINARYDATA_BINARYARRAY_BAD_INDEX, 353, 0, JSEXN_RANGEERR, "invalid or out-of-range index") > MSG_DEF(JSMSG_BINARYDATA_STRUCTTYPE_BAD_ARGS, 354, 0, JSEXN_RANGEERR, "invalid field descriptor") > MSG_DEF(JSMSG_BINARYDATA_NOT_BINARYSTRUCT, 355, 1, JSEXN_TYPEERR, "{0} is not a BinaryStruct") > 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") >diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp >--- a/js/src/jsanalyze.cpp >+++ b/js/src/jsanalyze.cpp >@@ -1824,17 +1824,17 @@ ScriptAnalysis::needsArgsObj(JSContext * > JS_ASSERT(script_->argumentsHasVarBinding()); > > /* > * Always construct arguments objects when in debug mode and for generator > * scripts (generators can be suspended when speculation fails). > * > * FIXME: Don't build arguments for ES6 generator expressions. > */ >- if (cx->compartment()->debugMode() || script_->isGenerator) >+ if (cx->compartment()->debugMode() || script_->isGenerator()) > return true; > > /* > * If the script has dynamic name accesses which could reach 'arguments', > * the parser will already have checked to ensure there are no explicit > * uses of 'arguments' in the function. If there are such uses, the script > * will be marked as definitely needing an arguments object. > * >diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp >--- a/js/src/jsatom.cpp >+++ b/js/src/jsatom.cpp >@@ -99,17 +99,16 @@ const char js_static_str[] = "s > const char js_super_str[] = "super"; > const char js_switch_str[] = "switch"; > const char js_this_str[] = "this"; > const char js_try_str[] = "try"; > 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 > * just 55 double atoms, but over 15000 string atoms. Not to penalize more > * economical embeddings allocating too much memory initially we initialize > * atomized strings with just 1K entries. > */ > #define JS_STRING_HASH_COUNT 1024 >diff --git a/js/src/jsatom.h b/js/src/jsatom.h >--- a/js/src/jsatom.h >+++ b/js/src/jsatom.h >@@ -148,17 +148,16 @@ extern const char js_static_str[]; > extern const char js_super_str[]; > extern const char js_switch_str[]; > extern const char js_this_str[]; > extern const char js_try_str[]; > 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 { > > extern const char * const TypeStrings[]; > > /* > * Initialize atom state. Return true on success, false on failure to allocate > * memory. The caller must zero rt->atomState before calling this function and >diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp >--- a/js/src/jsfun.cpp >+++ b/js/src/jsfun.cpp >@@ -521,16 +521,17 @@ FindBody(JSContext *cx, HandleFunction f > AutoKeepAtoms keepAtoms(cx->perThreadData); > TokenStream ts(cx, options, chars.get(), length, NULL); > int nest = 0; > bool onward = true; > // Skip arguments list. > do { > switch (ts.getToken()) { > case TOK_NAME: >+ case TOK_YIELD: > if (nest == 0) > onward = false; > break; > case TOK_LP: > nest++; > break; > case TOK_RP: > if (--nest == 0) >@@ -1223,24 +1224,17 @@ static bool > fun_isGenerator(JSContext *cx, unsigned argc, Value *vp) > { > JSFunction *fun; > if (!IsFunctionObject(vp[1], &fun)) { > JS_SET_RVAL(cx, vp, BooleanValue(false)); > 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; > } > > /* ES5 15.3.4.5. */ > static bool > fun_bind(JSContext *cx, unsigned argc, Value *vp) > { > CallArgs args = CallArgsFromVp(argc, vp); >@@ -1451,31 +1445,36 @@ js::Function(JSContext *cx, unsigned arg > * Check that it's a name. This also implicitly guards against > * TOK_ERROR, which was already reported. > */ > if (hasRest) { > ts.reportError(JSMSG_PARAMETER_AFTER_REST); > 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); > return false; > } > } else { > return OnBadFormal(cx, tt); > } > } > >- if (!formals.append(ts.currentToken().name())) >+ if (!formals.append(ts.currentName())) > return false; > > /* > * Get the next token. Stop on end of stream. Otherwise > * insist on a comma, get another name, and iterate. > */ > tt = ts.getToken(); > if (tt == TOK_EOF) >diff --git a/js/src/jsfun.h b/js/src/jsfun.h >--- a/js/src/jsfun.h >+++ b/js/src/jsfun.h >@@ -277,16 +277,28 @@ class JSFunction : public JSObject > return u.i.s.lazy_; > } > > js::LazyScript *lazyScriptOrNull() const { > JS_ASSERT(isInterpretedLazy()); > 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) { > JS_ASSERT(isInterpreted()); > flags &= ~INTERPRETED; > flags |= INTERPRETED_LAZY; > u.i.s.lazy_ = lazy; > } >diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp >--- a/js/src/jsiter.cpp >+++ b/js/src/jsiter.cpp >@@ -1470,16 +1470,23 @@ Class GeneratorObject::class_ = { > * if they are non-null. > */ > JSObject * > 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); > { > JSObject *proto = global->getOrCreateGeneratorPrototype(cx); > if (!proto) > return NULL; > obj = NewObjectWithGivenProto(cx, &GeneratorObject::class_, proto, global); > } >diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp >--- a/js/src/jsreflect.cpp >+++ b/js/src/jsreflect.cpp >@@ -2782,16 +2782,17 @@ ASTSerializer::module(ParseNode *pn, Mut > builder.module(&pn->pn_pos, name, body, dst); > } > > bool > 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 = > #if JS_HAS_EXPR_CLOSURES > func->isExprClosure(); > #else > false; > #endif >diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp >--- a/js/src/jsscript.cpp >+++ b/js/src/jsscript.cpp >@@ -402,19 +402,19 @@ js::XDRScript(XDRState<mode> *xdr, Handl > SavedCallerFun, > Strict, > ContainsDynamicNameAccess, > FunHasExtensibleScope, > FunNeedsDeclEnvObject, > FunHasAnyAliasedFormal, > ArgumentsHasVarBinding, > NeedsArgsObj, >- IsGenerator, > IsGeneratorExp, > IsLegacyGenerator, >+ IsStarGenerator, > OwnSource, > ExplicitUseStrict, > SelfHosted > }; > > uint32_t length, lineno, nslots; > uint32_t natoms, nsrcnotes, ntrynotes, nobjects, nregexps, nconsts, i; > uint32_t prologLength, version; >@@ -491,22 +491,22 @@ js::XDRScript(XDRState<mode> *xdr, Handl > if (script->funHasAnyAliasedFormal) > scriptBits |= (1 << FunHasAnyAliasedFormal); > if (script->argumentsHasVarBinding()) > scriptBits |= (1 << ArgumentsHasVarBinding); > if (script->analyzedArgsUsage() && script->needsArgsObj()) > 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); > } > > if (!xdr->codeUint32(&prologLength)) > return false; > if (!xdr->codeUint32(&version)) >@@ -591,22 +591,24 @@ js::XDRScript(XDRState<mode> *xdr, Handl > if (scriptBits & (1 << FunNeedsDeclEnvObject)) > script->funNeedsDeclEnvObject = true; > if (scriptBits & (1 << FunHasAnyAliasedFormal)) > script->funHasAnyAliasedFormal = true; > if (scriptBits & (1 << ArgumentsHasVarBinding)) > 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); > JS_STATIC_ASSERT(sizeof(jssrcnote) == 1); > > if (scriptBits & (1 << OwnSource)) { > if (!script->scriptSource()->performXDR<mode>(xdr)) > return false; >@@ -1957,19 +1959,18 @@ JSScript::fullyInitFromEmitter(Exclusive > } > > script->ndefaults = funbox->ndefaults; > } > > 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()); > } > > for (unsigned i = 0, n = script->bindings.numArgs(); i < n; ++i) { > if (script->formalIsAliased(i)) { > script->funHasAnyAliasedFormal = true; > break; > } >@@ -2468,18 +2469,18 @@ js::CloneScript(JSContext *cx, HandleObj > dst->strict = src->strict; > dst->explicitUseStrict = src->explicitUseStrict; > dst->bindingsAccessedDynamically = src->bindingsAccessedDynamically; > dst->funHasExtensibleScope = src->funHasExtensibleScope; > dst->funNeedsDeclEnvObject = src->funNeedsDeclEnvObject; > 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; > dst->shouldCloneAtCallsite = src->shouldCloneAtCallsite; > dst->isCallsiteClone = src->isCallsiteClone; > > if (nconsts != 0) { > HeapValue *vector = Rebase<HeapValue>(dst, src, src->consts()->vector); >@@ -2910,17 +2911,17 @@ JSScript::argumentsOptimizationFailed(JS > * everything has been fixed up, but there was an outstanding magic value > * on the stack that has just now flowed into an apply. In this case, there > * is nothing to do; GuardFunApplySpeculation will patch in the real > * argsobj. > */ > if (script->needsArgsObj()) > return true; > >- JS_ASSERT(!script->isGenerator); >+ JS_ASSERT(!script->isGenerator()); > > script->needsArgsObj_ = true; > > #ifdef JS_ION > /* > * Since we can't invalidate baseline scripts, set a flag that's checked from > * JIT code to indicate the arguments optimization failed and JSOP_ARGUMENTS > * should create an arguments object next time. >@@ -3002,16 +3003,17 @@ LazyScript::LazyScript(JSFunction *fun, > : script_(NULL), > function_(fun), > enclosingScope_(NULL), > sourceObject_(NULL), > table_(table), > version_(version), > numFreeVariables_(numFreeVariables), > numInnerFunctions_(numInnerFunctions), >+ generatorKindBits_(GeneratorKindAsBits(NotGenerator)), > strict_(false), > bindingsAccessedDynamically_(false), > hasDebuggerStatement_(false), > directlyInsideEval_(false), > usesArgumentsAndApply_(false), > hasBeenCloned_(false), > begin_(begin), > end_(end), >diff --git a/js/src/jsscript.h b/js/src/jsscript.h >--- a/js/src/jsscript.h >+++ b/js/src/jsscript.h >@@ -409,16 +409,29 @@ class ScriptSourceObject : public JSObje > } > > void setSource(ScriptSource *source); > > private: > 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 > { > static const uint32_t stepFlagMask = 0x80000000U; > static const uint32_t stepCountMask = 0x7fffffffU; > > public: >@@ -533,16 +546,19 @@ class JSScript : public js::gc::Cell > > 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; > >+ // The GeneratorKind of the script. >+ uint8_t generatorKindBits_; >+ > // 1-bit fields. > > public: > bool noScriptRval:1; /* no need for result value of last > expression statement */ > bool savedCallerFun:1; /* can call getCallerFunction() */ > bool strict:1; /* code is in strict mode */ > bool explicitUseStrict:1; /* code has "use strict"; explicitly */ >@@ -584,24 +600,19 @@ class JSScript : public js::gc::Cell > bool hadFrequentBailouts:1; > #else > bool failedBoundsCheckPad:1; > bool failedShapeGuardPad:1; > bool hadFrequentBailoutsPad:1; > #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 */ > bool hasDebugScript:1; /* script has an entry in > JSCompartment::debugScriptMap */ > bool hasFreezeConstraints:1; /* freeze constraints for stack > * type sets have been generated */ > >@@ -642,16 +653,29 @@ class JSScript : public js::gc::Cell > > void setVersion(JSVersion v) { version = v; } > > /* See ContextFlags::funArgumentsHasLocalBinding comment. */ > bool argumentsHasVarBinding() const { return argsHasVarBinding_; } > 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 > * needsArgsObj which is set by ScriptAnalysis::analyzeSSA before running > * the script the first time. When !needsArgsObj, the prologue may simply > * write MagicValue(JS_OPTIMIZED_ARGUMENTS) to 'arguments's slot and any > * uses of 'arguments' will be guaranteed to handle this magic value. > * So avoid spurious arguments object creation, we maintain the invariant >@@ -1143,17 +1167,19 @@ class LazyScript : public js::gc::Cell > #if JS_BITS_PER_WORD == 32 > uint32_t padding; > #endif > > // Assorted bits that should really be in ScriptSourceObject. > 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; > uint32_t bindingsAccessedDynamically_ : 1; > uint32_t hasDebuggerStatement_ : 1; > uint32_t directlyInsideEval_:1; > uint32_t usesArgumentsAndApply_:1; > uint32_t hasBeenCloned_:1; >@@ -1209,16 +1235,33 @@ class LazyScript : public js::gc::Cell > > uint32_t numInnerFunctions() const { > return numInnerFunctions_; > } > HeapPtrFunction *innerFunctions() { > 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_; > } > void setStrict() { > strict_ = true; > } > > bool bindingsAccessedDynamically() const { >diff --git a/js/src/tests/ecma_6/Generators/shell.js b/js/src/tests/ecma_6/Generators/shell.js >new file mode 100644 >--- /dev/null >+++ b/js/src/tests/ecma_6/Generators/shell.js >@@ -0,0 +1,1 @@ >+version(0); >diff --git a/js/src/tests/ecma_6/Generators/syntax.js b/js/src/tests/ecma_6/Generators/syntax.js >new file mode 100644 >--- /dev/null >+++ b/js/src/tests/ecma_6/Generators/syntax.js >@@ -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); >diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h >--- a/js/src/vm/CommonPropertyNames.h >+++ b/js/src/vm/CommonPropertyNames.h >@@ -147,16 +147,17 @@ > macro(useAsm, useAsm, "use asm") \ > macro(useStrict, useStrict, "use strict") \ > macro(value, value, "value") \ > macro(valueOf, valueOf, "valueOf") \ > macro(var, var, "var") \ > 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") \ > macro(function, function, "function") \ > macro(string, string, "string") \ > macro(number, number, "number") \ > macro(boolean, boolean, "boolean") \ > macro(null, null, "null") >diff --git a/js/src/vm/Keywords.h b/js/src/vm/Keywords.h >--- a/js/src/vm/Keywords.h >+++ b/js/src/vm/Keywords.h >@@ -67,15 +67,19 @@ > /* ES5 future reserved keywords in strict mode. */ \ > macro(implements, implements, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \ > macro(interface, interface, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \ > macro(package, package, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \ > macro(private, private_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \ > 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) > > #endif /* vm_Keywords_h */
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
Flags:
wingo
: review+
Actions:
View
|
Diff
|
Review
Attachments on
bug 666399
:
601093
|
645655
|
651926
|
651927
|
651928
|
651929
|
651930
|
751550
|
751551
|
786260
|
788086
|
788985
|
789498
|
790202
|
790686
|
792043
|
792706