Bug 979865 - Part 4: Implement ES6 generator comprehensions r=jorendorff
authorAndy Wingo <wingo@igalia.com>
Fri, 07 Mar 2014 22:01:13 +0100
changeset 189805 1cb9462ced3948eaffc018375a8abdfcf1c2ca8a
parent 189804 f4216dc177e48775dc50e629f7d156592cf3677a
child 189806 653d9da6c5a2b2ff4b73f1d32f9fd99baccf52b5
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs979865
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 979865 - Part 4: Implement ES6 generator comprehensions r=jorendorff
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/tests/js1_8/genexps/regress-634472.js
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2042,16 +2042,17 @@ Parser<FullParseHandler>::finishFunction
         body->pn_head = item;
         if (body->pn_tail == &body->pn_head)
             body->pn_tail = &item->pn_next;
         ++body->pn_count;
         body->pn_xflags |= PNX_DESTRUCT;
     }
 
     JS_ASSERT(pn->pn_funbox == funbox);
+    JS_ASSERT(pn->pn_body->isKind(PNK_ARGSBODY));
     pn->pn_body->append(body);
     pn->pn_body->pn_pos = body->pn_pos;
 
     return true;
 }
 
 template <>
 bool
@@ -2619,17 +2620,17 @@ Parser<ParseHandler>::statements()
     return pn;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::condition()
 {
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
-    Node pn = parenExpr();
+    Node pn = exprInParens();
     if (!pn)
         return null();
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
 
     /* Check for (a = b) and warn about possible (a == b) mistype. */
     if (handler.isOperationWithoutParens(pn, PNK_ASSIGN) &&
         !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
     {
@@ -4530,17 +4531,17 @@ template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::switchStatement()
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_SWITCH));
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
 
-    Node discriminant = parenExpr();
+    Node discriminant = exprInParens();
     if (!discriminant)
         return null();
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
 
     StmtInfoPC stmtInfo(context);
     PushStatementPC(pc, &stmtInfo, STMT_SWITCH);
@@ -4876,17 +4877,17 @@ Parser<FullParseHandler>::withStatement(
     // use reportStrictModeError.  However, 'with' is the sole instance of a
     // construct that is forbidden in strict mode code, but doesn't even merit a
     // warning under JSOPTION_EXTRA_WARNINGS.  See
     // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
     if (pc->sc->strict && !report(ParseStrictError, true, null(), JSMSG_STRICT_CODE_WITH))
         return null();
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
-    Node objectExpr = parenExpr();
+    Node objectExpr = exprInParens();
     if (!objectExpr)
         return null();
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
 
     bool oldParsingWith = pc->parsingWith;
     pc->parsingWith = true;
 
     StmtInfoPC stmtInfo(context);
@@ -6264,16 +6265,28 @@ Parser<FullParseHandler>::legacyComprehe
     PopStatementPC(tokenStream, pc);
 
     handler.setEndPosition(pn, pos().end);
 
     return pn;
 }
 
 template <>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::legacyComprehensionTail(SyntaxParseHandler::Node bodyStmt,
+                                                    unsigned blockid,
+                                                    GeneratorKind comprehensionKind,
+                                                    ParseContext<SyntaxParseHandler> *outerpc,
+                                                    unsigned innerBlockScopeDepth)
+{
+    abortIfSyntaxParser();
+    return null();
+}
+
+template <>
 ParseNode*
 Parser<FullParseHandler>::legacyArrayComprehension(ParseNode *array)
 {
     array->setKind(PNK_ARRAYCOMP);
 
     // Remove the single element from array's linked list, leaving us with an
     // empty array literal and a comprehension expression.
     JS_ASSERT(array->pn_count == 1);
@@ -6302,16 +6315,109 @@ Parser<FullParseHandler>::legacyArrayCom
 template <>
 SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::legacyArrayComprehension(Node array)
 {
     abortIfSyntaxParser();
     return null();
 }
 
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::generatorComprehensionLambda(GeneratorKind comprehensionKind,
+                                                   unsigned begin, Node innerStmt)
+{
+    JS_ASSERT(comprehensionKind == LegacyGenerator || comprehensionKind == StarGenerator);
+    JS_ASSERT(!!innerStmt == (comprehensionKind == LegacyGenerator));
+
+    Node genfn = handler.newFunctionDefinition();
+    if (!genfn)
+        return null();
+    handler.setOp(genfn, JSOP_LAMBDA);
+
+    ParseContext<ParseHandler> *outerpc = pc;
+
+    // If we are off the main thread, the generator meta-objects have
+    // already been created by js::StartOffThreadParseScript, so cx will not
+    // be necessary.
+    RootedObject proto(context);
+    if (comprehensionKind == StarGenerator) {
+        JSContext *cx = context->maybeJSContext();
+        proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
+        if (!proto)
+            return null();
+    }
+
+    RootedFunction fun(context, newFunction(outerpc, /* atom = */ NullPtr(), Expression, proto));
+    if (!fun)
+        return null();
+
+    // Create box for fun->object early to root it.
+    Directives directives(/* strict = */ outerpc->sc->strict);
+    FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives, comprehensionKind);
+    if (!genFunbox)
+        return null();
+
+    ParseContext<ParseHandler> genpc(this, outerpc, genfn, genFunbox,
+                                     /* newDirectives = */ nullptr,
+                                     outerpc->staticLevel + 1, outerpc->blockidGen,
+                                     /* blockScopeDepth = */ 0);
+    if (!genpc.init(tokenStream))
+        return null();
+
+    /*
+     * We assume conservatively that any deoptimization flags in pc->sc
+     * 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;
+
+    JS_ASSERT(genFunbox->generatorKind() == comprehensionKind);
+    genFunbox->inGenexpLambda = true;
+    handler.setBlockId(genfn, genpc.bodyid);
+
+    Node body;
+
+    if (comprehensionKind == StarGenerator) {
+        body = comprehension(StarGenerator);
+        if (!body)
+            return null();
+    } else {
+        JS_ASSERT(comprehensionKind == LegacyGenerator);
+        body = legacyComprehensionTail(innerStmt, outerpc->blockid(), LegacyGenerator,
+                                       outerpc, LegacyComprehensionHeadBlockScopeDepth(outerpc));
+        if (!body)
+            return null();
+    }
+
+    if (comprehensionKind == StarGenerator)
+        MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
+
+    handler.setBeginPosition(body, begin);
+    handler.setEndPosition(body, pos().end);
+
+    handler.setBeginPosition(genfn, begin);
+    handler.setEndPosition(genfn, pos().end);
+
+    // Note that if we ever start syntax-parsing generators, we will also
+    // need to propagate the closed-over variable set to the inner
+    // lazyscript, as in finishFunctionDefinition.
+    handler.setFunctionBody(genfn, body);
+
+    PropagateTransitiveParseFlags(genFunbox, outerpc->sc);
+
+    if (!leaveFunction(genfn, outerpc))
+        return null();
+
+    return genfn;
+}
+
 #if JS_HAS_GENERATOR_EXPRS
 
 /*
  * Starting from a |for| keyword after an expression, parse the comprehension
  * tail completing this generator expression. Wrap the expression at kid in a
  * generator function that is immediately called to evaluate to the generator
  * iterator that is the value of this legacy generator expression.
  *
@@ -6321,87 +6427,35 @@ Parser<SyntaxParseHandler>::legacyArrayC
  * innermost loop body.
  *
  * Note how unlike Python, we do not evaluate the expression to the right of
  * the first |in| in the chain of |for| heads. Instead, a generator expression
  * is merely sugar for a generator function expression and its application.
  */
 template <>
 ParseNode *
-Parser<FullParseHandler>::legacyGeneratorExpr(ParseNode *kid)
+Parser<FullParseHandler>::legacyGeneratorExpr(ParseNode *expr)
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
 
     /* Create a |yield| node for |kid|. */
-    ParseNode *yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, kid->pn_pos.begin, kid);
+    ParseNode *yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, expr->pn_pos.begin, expr);
     if (!yieldExpr)
         return null();
     yieldExpr->setInParens(true);
 
     // A statement to wrap the yield expression.
-    ParseNode *yieldStmt = handler.newExprStatement(yieldExpr, kid->pn_pos.end);
+    ParseNode *yieldStmt = handler.newExprStatement(yieldExpr, expr->pn_pos.end);
     if (!yieldStmt)
         return null();
 
     /* Make a new node for the desugared generator function. */
-    ParseNode *genfn = CodeNode::create(PNK_FUNCTION, &handler);
+    ParseNode *genfn = generatorComprehensionLambda(LegacyGenerator, expr->pn_pos.begin, yieldStmt);
     if (!genfn)
         return null();
-    genfn->setOp(JSOP_LAMBDA);
-    JS_ASSERT(!genfn->pn_body);
-    genfn->pn_dflags = 0;
-
-    {
-        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,
-                                                LegacyGenerator);
-        if (!genFunbox)
-            return null();
-
-        ParseContext<FullParseHandler> genpc(this, outerpc, genfn, genFunbox,
-                                             /* newDirectives = */ nullptr,
-                                             outerpc->staticLevel + 1, outerpc->blockidGen,
-                                             /* blockScopeDepth = */ 0);
-        if (!genpc.init(tokenStream))
-            return null();
-
-        /*
-         * We assume conservatively that any deoptimization flags in pc->sc
-         * 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;
-
-        JS_ASSERT(genFunbox->isLegacyGenerator());
-        genFunbox->inGenexpLambda = true;
-        genfn->pn_blockid = genpc.bodyid;
-
-        ParseNode *body = legacyComprehensionTail(yieldStmt, outerpc->blockid(), LegacyGenerator,
-                                                  outerpc,
-                                                  LegacyComprehensionHeadBlockScopeDepth(outerpc));
-        if (!body)
-            return null();
-        JS_ASSERT(!genfn->pn_body);
-        genfn->pn_body = body;
-        genfn->pn_pos.begin = body->pn_pos.begin;
-        genfn->pn_pos.end = body->pn_pos.end;
-
-        if (!leaveFunction(genfn, outerpc))
-            return null();
-    }
 
     /*
      * Our result is a call expression that invokes the anonymous generator
      * function object.
      */
     ParseNode *result = ListNode::create(PNK_GENEXP, &handler);
     if (!result)
         return null();
@@ -6592,16 +6646,42 @@ Parser<ParseHandler>::arrayComprehension
     handler.setBeginPosition(comp, begin);
     handler.setEndPosition(comp, pos().end);
 
     return comp;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
+Parser<ParseHandler>::generatorComprehension(uint32_t begin)
+{
+    JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
+
+    // We have no problem parsing generator comprehensions inside lazy
+    // functions, but the bytecode emitter currently can't handle them that way,
+    // because when it goes to emit the code for the inner generator function,
+    // it expects outer functions to have non-lazy scripts.
+    if (!abortIfSyntaxParser())
+        return null();
+
+    Node genfn = generatorComprehensionLambda(StarGenerator, begin, null());
+    if (!genfn)
+        return null();
+
+    Node result = handler.newList(PNK_GENEXP, genfn, JSOP_CALL);
+    if (!result)
+        return null();
+    handler.setBeginPosition(result, begin);
+    handler.setEndPosition(result, pos().end);
+
+    return result;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
 Parser<ParseHandler>::assignExprWithoutYield(unsigned msg)
 {
     uint32_t startYieldOffset = pc->lastYieldOffset;
     Node res = assignExpr();
     if (res && pc->lastYieldOffset != startYieldOffset) {
         reportWithOffset(ParseError, false, pc->lastYieldOffset,
                          msg, js_yield_str);
         return null();
@@ -7217,27 +7297,17 @@ Parser<ParseHandler>::primaryExpr(TokenK
 
       case TOK_LC:
         return objectLiteral();
 
       case TOK_LET:
         return letBlock(LetExpresion);
 
       case TOK_LP:
-      {
-        bool genexp;
-        Node pn = parenExpr(&genexp);
-        if (!pn)
-            return null();
-        pn = handler.setInParens(pn);
-
-        if (!genexp)
-            MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
-        return pn;
-      }
+        return parenExprOrGeneratorComprehension();
 
       case TOK_STRING:
         return stringLiteral();
 
       case TOK_YIELD:
         if (!checkYieldNameValidity())
             return null();
         // Fall through.
@@ -7295,24 +7365,96 @@ Parser<ParseHandler>::primaryExpr(TokenK
       default:
         report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
         return null();
     }
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::parenExpr(bool *genexp)
+Parser<ParseHandler>::parenExprOrGeneratorComprehension()
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
     uint32_t begin = pos().begin;
-
-    if (genexp)
-        *genexp = false;
-
+    uint32_t startYieldOffset = pc->lastYieldOffset;
+
+    if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand))
+        return generatorComprehension(begin);
+
+    /*
+     * Always accept the 'in' operator in a parenthesized expression,
+     * where it's unambiguous, even if we might be parsing the init of a
+     * for statement.
+     */
+    bool oldParsingForInit = pc->parsingForInit;
+    pc->parsingForInit = false;
+    Node pn = expr();
+    pc->parsingForInit = oldParsingForInit;
+
+    if (!pn)
+        return null();
+
+#if JS_HAS_GENERATOR_EXPRS
+    if (tokenStream.matchToken(TOK_FOR)) {
+        if (pc->lastYieldOffset != startYieldOffset) {
+            reportWithOffset(ParseError, false, pc->lastYieldOffset,
+                             JSMSG_BAD_GENEXP_BODY, js_yield_str);
+            return null();
+        }
+        if (handler.isOperationWithoutParens(pn, PNK_COMMA)) {
+            report(ParseError, false, null(),
+                   JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
+            return null();
+        }
+        pn = legacyGeneratorExpr(pn);
+        if (!pn)
+            return null();
+        handler.setBeginPosition(pn, begin);
+        if (tokenStream.getToken() != TOK_RP) {
+            report(ParseError, false, null(),
+                   JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
+            return null();
+        }
+        handler.setEndPosition(pn, pos().end);
+        handler.setInParens(pn);
+        return pn;
+    }
+#endif /* JS_HAS_GENERATOR_EXPRS */
+
+    pn = handler.setInParens(pn);
+
+    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
+
+    return pn;
+}
+
+// Legacy generator comprehensions can sometimes appear without parentheses.
+// For example:
+//
+//   foo(x for (x in bar))
+//
+// In this case the parens are part of the call, and not part of the generator
+// comprehension.  This can happen in these contexts:
+//
+//   if (_)
+//   while (_) {}
+//   do {} while (_)
+//   switch (_) {}
+//   with (_) {}
+//   foo(_) // must be first and only argument
+//
+// This is not the case for ES6 generator comprehensions; they must always be in
+// parentheses.
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exprInParens()
+{
+    JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
+    uint32_t begin = pos().begin;
     uint32_t startYieldOffset = pc->lastYieldOffset;
 
     /*
      * Always accept the 'in' operator in a parenthesized expression,
      * where it's unambiguous, even if we might be parsing the init of a
      * for statement.
      */
     bool oldParsingForInit = pc->parsingForInit;
@@ -7334,25 +7476,16 @@ Parser<ParseHandler>::parenExpr(bool *ge
             report(ParseError, false, null(),
                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
             return null();
         }
         pn = legacyGeneratorExpr(pn);
         if (!pn)
             return null();
         handler.setBeginPosition(pn, begin);
-        if (genexp) {
-            if (tokenStream.getToken() != TOK_RP) {
-                report(ParseError, false, null(),
-                       JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
-                return null();
-            }
-            handler.setEndPosition(pn, pos().end);
-            *genexp = true;
-        }
     }
 #endif /* JS_HAS_GENERATOR_EXPRS */
 
     return pn;
 }
 
 template class Parser<FullParseHandler>;
 template class Parser<SyntaxParseHandler>;
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -518,17 +518,18 @@ class Parser : private AutoGCRooter, pub
     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 = nullptr);
+    Node parenExprOrGeneratorComprehension();
+    Node exprInParens();
 
     /*
      * 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, GeneratorKind generatorKind);
@@ -536,27 +537,31 @@ class Parser : private AutoGCRooter, pub
                              FunctionType type, FunctionSyntaxKind kind,
                              GeneratorKind generatorKind,
                              Directives inheritedDirectives, Directives *newDirectives);
 
     Node unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin);
 
     Node condition();
 
+    Node generatorComprehensionLambda(GeneratorKind comprehensionKind, unsigned begin,
+                                      Node innerStmt);
+
     Node legacyComprehensionTail(Node kid, unsigned blockid, GeneratorKind comprehensionKind,
                                  ParseContext<ParseHandler> *outerpc,
                                  unsigned innerBlockScopeDepth);
     Node legacyArrayComprehension(Node array);
     Node legacyGeneratorExpr(Node kid);
 
     Node comprehensionTail(GeneratorKind comprehensionKind);
     Node comprehensionIf(GeneratorKind comprehensionKind);
     Node comprehensionFor(GeneratorKind comprehensionKind);
     Node comprehension(GeneratorKind comprehensionKind);
     Node arrayComprehension(uint32_t begin);
+    Node generatorComprehension(uint32_t begin);
 
     bool argumentList(Node listNode, bool *isSpread);
     Node letBlock(LetContext letContext);
     Node destructuringExpr(BindData<ParseHandler> *data, TokenKind tt);
 
     Node identifierName();
 
     bool matchLabel(MutableHandle<PropertyName*> label);
--- a/js/src/tests/js1_8/genexps/regress-634472.js
+++ b/js/src/tests/js1_8/genexps/regress-634472.js
@@ -21,17 +21,17 @@ function error(str) {
     e.lineNumber = e.lineNumber - base + 1;
     return e;
   }
 }
 
 const JSMSG_GENEXP_YIELD         = error("(function(){((yield) for (x in []))})").message;
 const JSMSG_TOP_YIELD            = error("yield").message;
 const JSMSG_YIELD_PAREN          = error("(function(){yield, 1})").message;
-const JSMSG_GENERIC              = error("(for)").message;
+const JSMSG_YIELD_FOR            = error("(function(){yield for})").message;
 const JSMSG_BAD_GENERATOR_SYNTAX = error("(1, x for (x in []))").message;
 
 const cases = [
   // yield expressions
   { expr: "yield",        top: JSMSG_TOP_YIELD, fun: null,              gen: JSMSG_GENEXP_YIELD, desc: "simple yield" },
   { expr: "yield 1",      top: JSMSG_TOP_YIELD, fun: null,              gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg" },
   { expr: "1, yield",     top: JSMSG_TOP_YIELD, fun: null,              gen: JSMSG_GENEXP_YIELD, desc: "simple yield at end of list" },
   { expr: "1, yield 2",   top: JSMSG_TOP_YIELD, fun: null,              gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg at end of list" },
@@ -48,21 +48,21 @@ const cases = [
   { expr: "((((yield))))",   top: JSMSG_TOP_YIELD, fun: null, gen: JSMSG_GENEXP_YIELD, desc: "deeply nested yield" },
   { expr: "((((yield 1))))", top: JSMSG_TOP_YIELD, fun: null, gen: JSMSG_GENEXP_YIELD, desc: "deeply nested yield w/ arg" },
 
   // arguments
   { expr: "arguments",    top: null, fun: null, gen: null, desc: "arguments in list" },
   { expr: "1, arguments", top: null, fun: null, gen: null, desc: "arguments in list" },
 
   // yield in generator expressions
-  { expr: "(yield for (x in []))",           top: JSMSG_TOP_YIELD, fun: JSMSG_GENERIC,      gen: JSMSG_GENERIC,      desc: "simple yield in genexp" },
+  { expr: "(yield for (x in []))",           top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_FOR,    gen: JSMSG_YIELD_FOR,      desc: "simple yield in genexp" },
   { expr: "(yield 1 for (x in []))",         top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg in genexp" },
   { expr: "(yield, 1 for (x in []))",        top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN,  gen: JSMSG_YIELD_PAREN,  desc: "simple yield in list in genexp" },
   { expr: "(yield 1, 2 for (x in []))",      top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN,  gen: JSMSG_YIELD_PAREN,  desc: "yield w/ arg in list in genexp" },
-  { expr: "(1, yield for (x in []))",        top: JSMSG_TOP_YIELD, fun: JSMSG_GENERIC,      gen: JSMSG_GENERIC,      desc: "simple yield at end of list in genexp" },
+  { expr: "(1, yield for (x in []))",        top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_FOR,    gen: JSMSG_YIELD_FOR,      desc: "simple yield at end of list in genexp" },
   { expr: "(1, yield 2 for (x in []))",      top: JSMSG_TOP_YIELD, fun: { simple: JSMSG_GENEXP_YIELD, call: JSMSG_GENEXP_YIELD },
                                                                                             gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg at end of list in genexp" },
   { expr: "((yield) for (x in []))",         top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "simple yield, parenthesized in genexp" },
   { expr: "((yield 1) for (x in []))",       top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg, parenthesized in genexp" },
   { expr: "(1, (yield) for (x in []))",      top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "simple yield, parenthesized in list in genexp" },
   { expr: "(1, (yield 2) for (x in []))",    top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg, parenthesized in list in genexp" },
   { expr: "((1, yield) for (x in []))",      top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "simple yield at end of list, parenthesized in genexp" },
   { expr: "((1, yield 2) for (x in []))",    top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg at end of list, parenthesized in genexp" },