disallow yield and arguments in generator expressions (bug 634472, r=cdleary)
authorDave Herman <dherman@mozilla.com>
Thu, 16 Jun 2011 08:20:18 -0700
changeset 71363 81c343a150a4bc7be1bfa665b41f877a4074bfbd
parent 71362 b65724d6c32633b356471a31f7ff01ff74a14312
child 71364 8e030595916317d42a7982be2ded49a88f489d0f
push id20538
push usercleary@mozilla.com
push dateMon, 20 Jun 2011 23:59:42 +0000
treeherdermozilla-central@a285146675dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscdleary
bugs634472
milestone7.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
disallow yield and arguments in generator expressions (bug 634472, r=cdleary)
js/src/js.msg
js/src/jsemit.cpp
js/src/jsemit.h
js/src/jsparse.cpp
js/src/jsparse.h
js/src/tests/js1_7/lexical/regress-351515.js
js/src/tests/js1_8/genexps/jstests.list
js/src/tests/js1_8/genexps/regress-380237-03.js
js/src/tests/js1_8/genexps/regress-634472.js
js/src/tests/js1_8_1/decompilation/regress-380237-03.js
js/src/tests/js1_8_1/strict/generator-eval-arguments.js
js/src/tests/js1_8_5/extensions/reflect-parse.js
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -345,8 +345,9 @@ MSG_DEF(JSMSG_SC_RECURSION,           26
 MSG_DEF(JSMSG_CANT_WRAP_XML_OBJECT,   263, 0, JSEXN_TYPEERR, "can't wrap XML objects")
 MSG_DEF(JSMSG_BAD_CLONE_VERSION,      264, 0, JSEXN_ERR, "unsupported structured clone version")
 MSG_DEF(JSMSG_CANT_CLONE_OBJECT,      265, 0, JSEXN_TYPEERR, "can't clone object")
 MSG_DEF(JSMSG_NON_NATIVE_SCOPE,       266, 0, JSEXN_TYPEERR, "non-native scope object")
 MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 267, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function")
 MSG_DEF(JSMSG_INVALID_FOR_IN_INIT,    268, 0, JSEXN_SYNTAXERR, "for-in loop let declaration may not have an initializer")
 MSG_DEF(JSMSG_CLEARED_SCOPE,          269, 0, JSEXN_TYPEERR, "attempt to run compile-and-go script on a cleared scope")
 MSG_DEF(JSMSG_MALFORMED_ESCAPE,       270, 1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence")
+MSG_DEF(JSMSG_BAD_GENEXP_BODY,        271, 1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression")
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -5804,22 +5804,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
                 return JS_FALSE;
             if (EmitBlockChain(cx, cg) < 0)
                 return JS_FALSE;
         }
         break;
 
 #if JS_HAS_GENERATORS
       case TOK_YIELD:
-        if (!cg->inFunction()) {
-            ReportCompileErrorNumber(cx, CG_TS(cg), pn, JSREPORT_ERROR,
-                                     JSMSG_BAD_RETURN_OR_YIELD,
-                                     js_yield_str);
-            return JS_FALSE;
-        }
+        JS_ASSERT(cg->inFunction());
         if (pn->pn_kid) {
             if (!js_EmitTree(cx, cg, pn->pn_kid))
                 return JS_FALSE;
         } else {
             if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
                 return JS_FALSE;
         }
         if (pn->pn_hidden && js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
--- a/js/src/jsemit.h
+++ b/js/src/jsemit.h
@@ -296,25 +296,36 @@ struct JSStmtInfo {
                                  TCF_FUN_MUTATES_PARAMETER |                  \
                                  TCF_STRICT_MODE_CODE    |                    \
                                  TCF_FUN_EXTENSIBLE_SCOPE)
 
 struct JSTreeContext {              /* tree context for semantic checks */
     uint32          flags;          /* statement state flags, see above */
     uint32          bodyid;         /* block number of program/function body */
     uint32          blockidGen;     /* preincremented block number generator */
+    uint32          parenDepth;     /* paren-nesting depth */
+    uint32          yieldCount;     /* number of |yield| tokens encountered at
+                                       non-zero depth in current paren tree */
+    uint32          argumentsCount; /* number of |arguments| references encountered
+                                       at non-zero depth in current paren tree */
     JSStmtInfo      *topStmt;       /* top of statement info stack */
     JSStmtInfo      *topScopeStmt;  /* top lexical scope statement */
     JSObjectBox     *blockChainBox; /* compile time block scope chain (NB: one
                                        deeper than the topScopeStmt/downScope
                                        chain when in head of let block/expr) */
     JSParseNode     *blockNode;     /* parse node for a block with let declarations
                                        (block with its own lexical scope)  */
     JSAtomList      decls;          /* function, const, and var declarations */
     js::Parser      *parser;        /* ptr to common parsing and lexing data */
+    JSParseNode     *yieldNode;     /* parse node for a yield expression that might
+                                       be an error if we turn out to be inside a
+                                       generator expression */
+    JSParseNode     *argumentsNode; /* parse node for an arguments variable that
+                                       might be an error if we turn out to be
+                                       inside a generator expression */
 
   private:
     union {
         JSFunction  *fun_;          /* function to store argument and variable
                                        names when flags & TCF_IN_FUNCTION */
         JSObject    *scopeChain_;   /* scope chain object for the script */
     };
 
@@ -353,19 +364,21 @@ struct JSTreeContext {              /* t
 #ifdef JS_SCOPE_DEPTH_METER
     uint16          scopeDepth;     /* current lexical scope chain depth */
     uint16          maxScopeDepth;  /* maximum lexical scope chain depth */
 #endif
 
     void trace(JSTracer *trc);
 
     JSTreeContext(js::Parser *prs)
-      : flags(0), bodyid(0), blockidGen(0), topStmt(NULL), topScopeStmt(NULL),
-        blockChainBox(NULL), blockNode(NULL), parser(prs), scopeChain_(NULL),
-        parent(prs->tc), staticLevel(0), funbox(NULL), functionList(NULL),
+      : flags(0), bodyid(0), blockidGen(0), parenDepth(0), yieldCount(0), argumentsCount(0),
+        topStmt(NULL), topScopeStmt(NULL),
+        blockChainBox(NULL), blockNode(NULL), parser(prs),
+        yieldNode(NULL), argumentsNode(NULL),
+        scopeChain_(NULL), parent(prs->tc), staticLevel(0), funbox(NULL), functionList(NULL),
         innermostWith(NULL), bindings(prs->context, prs->emptyCallShape),
         sharpSlotBase(-1)
     {
         prs->tc = this;
         JS_SCOPE_DEPTH_METERING(scopeDepth = maxScopeDepth = 0);
     }
 
     /*
@@ -453,23 +466,30 @@ struct JSTreeContext {              /* t
         flags |= TCF_FUN_MUTATES_PARAMETER;
     }
 
     bool mutatesParameter() const {
         JS_ASSERT(inFunction());
         return flags & TCF_FUN_MUTATES_PARAMETER;
     }
 
-    void noteArgumentsUse() {
+    void noteArgumentsUse(JSParseNode *pn) {
         JS_ASSERT(inFunction());
+        countArgumentsUse(pn);
         flags |= TCF_FUN_USES_ARGUMENTS;
         if (funbox)
             funbox->node->pn_dflags |= PND_FUNARG;
     }
 
+    void countArgumentsUse(JSParseNode *pn) {
+        JS_ASSERT(pn->pn_atom == parser->context->runtime->atomState.argumentsAtom);
+        argumentsCount++;
+        argumentsNode = pn;
+    }
+
     bool needsEagerArguments() const {
         return inStrictMode() && ((usesArguments() && mutatesParameter()) || callsEval());
     }
 
     void noteHasExtensibleScope() {
         flags |= TCF_FUN_EXTENSIBLE_SCOPE;
     }
 
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -4150,18 +4150,20 @@ NoteLValue(JSContext *cx, JSParseNode *p
      * in ES5, so assignments to them must do nothing or throw a TypeError
      * depending on code strictness.  Assignment to arguments is a syntax error
      * in strict mode and thus cannot happen.  Outside strict mode, we optimize
      * away assignment to the function name.  For assignment to function name
      * to fail in strict mode, we must have a binding for it in the scope
      * chain; we ensure this happens by making such functions heavyweight.
      */
     JSAtom *lname = pn->pn_atom;
-    if (lname == cx->runtime->atomState.argumentsAtom ||
-        (tc->inFunction() && lname == tc->fun()->atom)) {
+    if (lname == cx->runtime->atomState.argumentsAtom) {
+        tc->flags |= TCF_FUN_HEAVYWEIGHT;
+        tc->countArgumentsUse(pn);
+    } else if (tc->inFunction() && lname == tc->fun()->atom) {
         tc->flags |= TCF_FUN_HEAVYWEIGHT;
     }
 }
 
 #if JS_HAS_DESTRUCTURING
 
 static JSBool
 BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
@@ -4612,28 +4614,39 @@ ContainsStmt(JSParseNode *pn, TokenKind 
 
 JSParseNode *
 Parser::returnOrYield(bool useAssignExpr)
 {
     TokenKind tt, tt2;
     JSParseNode *pn, *pn2;
 
     tt = tokenStream.currentToken().type;
-    if (tt == TOK_RETURN && !tc->inFunction()) {
-        reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
+    if (!tc->inFunction()) {
+        reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD,
+                          (tt == TOK_RETURN) ? js_return_str : js_yield_str);
         return NULL;
     }
 
     pn = UnaryNode::create(tc);
     if (!pn)
         return NULL;
 
 #if JS_HAS_GENERATORS
-    if (tt == TOK_YIELD)
-        tc->flags |= TCF_FUN_IS_GENERATOR;
+    if (tt == TOK_YIELD) {
+        /*
+         * If we're within parens, we won't know if this is a generator expression until we see
+         * a |for| token, so we have to delay flagging the current function.
+         */
+        if (tc->parenDepth == 0) {
+            tc->flags |= TCF_FUN_IS_GENERATOR;
+        } else {
+            tc->yieldCount++;
+            tc->yieldNode = pn;
+        }
+    }
 #endif
 
     /* This is ugly, but we don't want to require a semicolon. */
     tt2 = tokenStream.peekTokenSameLine(TSF_OPERAND);
     if (tt2 == TOK_ERROR)
         return NULL;
 
     if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
@@ -4836,16 +4849,20 @@ NewBindingNode(JSAtom *atom, JSTreeConte
             return pn;
         }
     }
 
     /* Make a new node for this declarator name (or destructuring pattern). */
     pn = NameNode::create(atom, tc);
     if (!pn)
         return NULL;
+
+    if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
+        tc->countArgumentsUse(pn);
+
     return pn;
 }
 
 JSParseNode *
 Parser::switchStatement()
 {
     JSParseNode *pn5, *saveBlock;
     JSBool seenDefault = JS_FALSE;
@@ -6208,17 +6225,17 @@ Parser::variables(bool inLetHead)
 
             NoteLValue(context, pn2, tc, data.fresh ? PND_INITIALIZED : PND_ASSIGNED);
 
             /* The declarator's position must include the initializer. */
             pn2->pn_pos.end = init->pn_pos.end;
 
             if (tc->inFunction() &&
                 atom == context->runtime->atomState.argumentsAtom) {
-                tc->noteArgumentsUse();
+                tc->noteArgumentsUse(pn2);
                 if (!let)
                     tc->flags |= TCF_FUN_HEAVYWEIGHT;
             }
         }
     } while (tokenStream.matchToken(TOK_COMMA));
 
     pn->pn_pos.end = pn->last()->pn_pos.end;
     return pn;
@@ -6660,18 +6677,20 @@ Parser::unaryExpr()
             }
             break;
           case TOK_NAME:
             if (!ReportStrictModeError(context, &tokenStream, tc, pn,
                                        JSMSG_DEPRECATED_DELETE_OPERAND)) {
                 return NULL;
             }
             pn2->pn_op = JSOP_DELNAME;
-            if (pn2->pn_atom == context->runtime->atomState.argumentsAtom)
+            if (pn2->pn_atom == context->runtime->atomState.argumentsAtom) {
                 tc->flags |= TCF_FUN_HEAVYWEIGHT;
+                tc->countArgumentsUse(pn2);
+            }
             break;
           default:;
         }
         pn->pn_kid = pn2;
         break;
       }
       case TOK_ERROR:
         return NULL;
@@ -6736,16 +6755,75 @@ class CompExprTransplanter {
       : root(pn), tc(tc), genexp(ge), adjust(adj), funcLevel(0)
     {
     }
 
     bool transplant(JSParseNode *pn);
 };
 
 /*
+ * A helper for lazily checking for the presence of illegal |yield| or |arguments|
+ * tokens inside of generator expressions.
+ *
+ * Use when entering a parenthesized context. If the nested expression is followed
+ * by a |for| token (indicating that the parenthesized expression is a generator
+ * expression), use the checkValidBody() method to see if any illegal tokens were
+ * found.
+ */
+class GenexpGuard {
+    JSTreeContext   *tc;
+    uint32          startYieldCount;
+    uint32          startArgumentsCount;
+
+  public:
+    explicit GenexpGuard(JSTreeContext *tc)
+      : tc(tc)
+    {
+        if (tc->parenDepth == 0) {
+            tc->yieldCount = tc->argumentsCount = 0;
+            tc->yieldNode = tc->argumentsNode = NULL;
+        }
+        startYieldCount = tc->yieldCount;
+        startArgumentsCount = tc->argumentsCount;
+        tc->parenDepth++;
+    }
+
+    void endBody();
+    bool checkValidBody(JSParseNode *pn);
+};
+
+void
+GenexpGuard::endBody()
+{
+    tc->parenDepth--;
+}
+
+bool
+GenexpGuard::checkValidBody(JSParseNode *pn)
+{
+    if (tc->yieldCount > startYieldCount) {
+        JSParseNode *errorNode = tc->yieldNode;
+        if (!errorNode)
+            errorNode = pn;
+        tc->parser->reportErrorNumber(errorNode, JSREPORT_ERROR, JSMSG_BAD_GENEXP_BODY, js_yield_str);
+        return false;
+    }
+
+    if (tc->argumentsCount > startArgumentsCount) {
+        JSParseNode *errorNode = tc->argumentsNode;
+        if (!errorNode)
+            errorNode = pn;
+        tc->parser->reportErrorNumber(errorNode, JSREPORT_ERROR, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
+        return false;
+    }
+
+    return true;
+}
+
+/*
  * Any definitions nested within the comprehension expression of a generator
  * expression must move "down" one static level, which of course increases the
  * upvar-frame-skip count.
  */
 static bool
 BumpStaticLevel(JSParseNode *pn, JSTreeContext *tc)
 {
     if (!pn->pn_cookie.isFree()) {
@@ -6935,17 +7013,17 @@ CompExprTransplanter::transplant(JSParse
  * an expression in an open parenthesis, parse the tail of the comprehension
  * or generator expression signified by this |for| keyword in context.
  *
  * Return null on failure, else return the top-most parse node for the array
  * comprehension or generator expression, with a unary node as the body of the
  * (possibly nested) for-loop, initialized by |type, op, kid|.
  */
 JSParseNode *
-Parser::comprehensionTail(JSParseNode *kid, uintN blockid,
+Parser::comprehensionTail(JSParseNode *kid, uintN blockid, bool isGenexp,
                           TokenKind type, JSOp op)
 {
     uintN adjust;
     JSParseNode *pn, *pn2, *pn3, **pnp;
     JSStmtInfo stmtInfo;
     BindData data;
     TokenKind tt;
     JSAtom *atom;
@@ -7015,16 +7093,18 @@ Parser::comprehensionTail(JSParseNode *k
         if (tokenStream.matchToken(TOK_NAME)) {
             if (tokenStream.currentToken().t_atom == context->runtime->atomState.eachAtom)
                 pn2->pn_iflags |= JSITER_FOREACH;
             else
                 tokenStream.ungetToken();
         }
         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
+        GenexpGuard guard(tc);
+
         atom = NULL;
         tt = tokenStream.getToken();
         switch (tt) {
 #if JS_HAS_DESTRUCTURING
           case TOK_LB:
           case TOK_LC:
             tc->flags |= TCF_DECL_DESTRUCTURING;
             pn3 = primaryExpr(tt, JS_FALSE);
@@ -7057,16 +7137,21 @@ Parser::comprehensionTail(JSParseNode *k
         }
 
         MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
         JSParseNode *pn4 = expr();
         if (!pn4)
             return NULL;
         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
 
+        guard.endBody();
+
+        if (isGenexp && !guard.checkValidBody(pn2))
+            return NULL;
+
         switch (tt) {
 #if JS_HAS_DESTRUCTURING
           case TOK_LB:
           case TOK_LC:
             if (!CheckDestructuring(context, &data, pn3, tc))
                 return NULL;
 
             if (versionNumber() == JSVERSION_1_7) {
@@ -7192,17 +7277,17 @@ Parser::generatorExpr(JSParseNode *kid)
          * removed from tc->flags.
          */
         gentc.flags |= TCF_FUN_IS_GENERATOR | TCF_GENEXP_LAMBDA |
                        (tc->flags & (TCF_FUN_FLAGS & ~TCF_FUN_PARAM_ARGUMENTS));
         funbox->tcflags |= gentc.flags;
         genfn->pn_funbox = funbox;
         genfn->pn_blockid = gentc.bodyid;
 
-        JSParseNode *body = comprehensionTail(pn, outertc->blockid());
+        JSParseNode *body = comprehensionTail(pn, outertc->blockid(), true);
         if (!body)
             return NULL;
         JS_ASSERT(!genfn->pn_body);
         genfn->pn_body = body;
         genfn->pn_pos.begin = body->pn_pos.begin = kid->pn_pos.begin;
         genfn->pn_pos.end = body->pn_pos.end = tokenStream.currentToken().pos.end;
 
         if (!LeaveFunction(genfn, &gentc))
@@ -7229,30 +7314,34 @@ static const char js_generator_str[] = "
 #endif /* JS_HAS_GENERATORS */
 
 JSBool
 Parser::argumentList(JSParseNode *listNode)
 {
     if (tokenStream.matchToken(TOK_RP, TSF_OPERAND))
         return JS_TRUE;
 
+    GenexpGuard guard(tc);
+
     do {
         JSParseNode *argNode = assignExpr();
         if (!argNode)
             return JS_FALSE;
 #if JS_HAS_GENERATORS
         if (argNode->pn_type == TOK_YIELD &&
             !argNode->pn_parens &&
             tokenStream.peekToken() == TOK_COMMA) {
             reportErrorNumber(argNode, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
             return JS_FALSE;
         }
 #endif
 #if JS_HAS_GENERATOR_EXPRS
         if (tokenStream.matchToken(TOK_FOR)) {
+            if (!guard.checkValidBody(argNode))
+                return JS_FALSE;
             argNode = generatorExpr(argNode);
             if (!argNode)
                 return JS_FALSE;
             if (listNode->pn_count > 1 ||
                 tokenStream.peekToken() == TOK_COMMA) {
                 reportErrorNumber(argNode, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
                                   js_generator_str);
                 return JS_FALSE;
@@ -7261,16 +7350,17 @@ Parser::argumentList(JSParseNode *listNo
 #endif
         listNode->append(argNode);
     } while (tokenStream.matchToken(TOK_COMMA));
 
     if (tokenStream.getToken() != TOK_RP) {
         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_AFTER_ARGS);
         return JS_FALSE;
     }
+    guard.endBody();
     return JS_TRUE;
 }
 
 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
 static JSParseNode *
 CheckForImmediatelyAppliedLambda(JSParseNode *pn)
 {
     if (pn->pn_type == TOK_FUNCTION) {
@@ -8327,17 +8417,17 @@ Parser::primaryExpr(TokenKind tt, JSBool
                  * ARRAYPUSH node after we parse the rest of the comprehension.
                  */
                 pnexp = pn->last();
                 JS_ASSERT(pn->pn_count == 1);
                 pn->pn_count = 0;
                 pn->pn_tail = &pn->pn_head;
                 *pn->pn_tail = NULL;
 
-                pntop = comprehensionTail(pnexp, pn->pn_blockid,
+                pntop = comprehensionTail(pnexp, pn->pn_blockid, false,
                                           TOK_ARRAYPUSH, JSOP_ARRAYPUSH);
                 if (!pntop)
                     return NULL;
                 pn->append(pntop);
             }
 #endif /* JS_HAS_GENERATORS */
 
             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
@@ -8635,31 +8725,37 @@ Parser::primaryExpr(TokenKind tt, JSBool
             pn->pn_atom == context->runtime->atomState.argumentsAtom) {
             /*
              * Flag arguments usage so we can avoid unsafe optimizations such
              * as formal parameter assignment analysis (because of the hated
              * feature whereby arguments alias formals). We do this even for
              * a reference of the form foo.arguments, which ancient code may
              * still use instead of arguments (more hate).
              */
-            tc->noteArgumentsUse();
+            tc->noteArgumentsUse(pn);
 
             /*
              * Bind early to JSOP_ARGUMENTS to relieve later code from having
              * to do this work (new rule for the emitter to count on).
              */
             if (!afterDot && !(tc->flags & TCF_DECL_DESTRUCTURING) && !tc->inStatement(STMT_WITH)) {
                 pn->pn_op = JSOP_ARGUMENTS;
                 pn->pn_dflags |= PND_BOUND;
             }
         } else if ((!afterDot
 #if JS_HAS_XML_SUPPORT
                     || tokenStream.peekToken() == TOK_DBLCOLON
 #endif
                    ) && !(tc->flags & TCF_DECL_DESTRUCTURING)) {
+            /* In case this is a generator expression outside of any function. */
+            if (!tc->inFunction() &&
+                pn->pn_atom == context->runtime->atomState.argumentsAtom) {
+                tc->countArgumentsUse(pn);
+            }
+
             JSStmtInfo *stmt = js_LexicalLookup(tc, pn->pn_atom, NULL);
 
             JSDefinition *dn;
 
             JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
             if (ale) {
                 dn = ALE_DEFN(ale);
 #if JS_HAS_BLOCK_SCOPE
@@ -8821,26 +8917,29 @@ Parser::parenExpr(JSBool *genexp)
     TokenPtr begin;
     JSParseNode *pn;
 
     JS_ASSERT(tokenStream.currentToken().type == TOK_LP);
     begin = tokenStream.currentToken().pos.begin;
 
     if (genexp)
         *genexp = JS_FALSE;
+
+    GenexpGuard guard(tc);
+
     pn = bracketedExpr();
     if (!pn)
         return NULL;
+    guard.endBody();
 
 #if JS_HAS_GENERATOR_EXPRS
     if (tokenStream.matchToken(TOK_FOR)) {
-        if (pn->pn_type == TOK_YIELD && !pn->pn_parens) {
-            reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
-            return NULL;
-        }
+        if (!guard.checkValidBody(pn))
+            return NULL;
+        JS_ASSERT(pn->pn_type != TOK_YIELD);
         if (pn->pn_type == TOK_COMMA && !pn->pn_parens) {
             reportErrorNumber(pn->last(), JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
                               js_generator_str);
             return NULL;
         }
         pn = generatorExpr(pn);
         if (!pn)
             return NULL;
@@ -8852,16 +8951,24 @@ Parser::parenExpr(JSBool *genexp)
                 return NULL;
             }
             pn->pn_pos.end = tokenStream.currentToken().pos.end;
             *genexp = JS_TRUE;
         }
     }
 #endif /* JS_HAS_GENERATOR_EXPRS */
 
+    if (tc->yieldCount > 0) {
+        tc->flags |= TCF_FUN_IS_GENERATOR;
+        if (!tc->inFunction()) {
+            reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
+            return NULL;
+        }
+    }
+
     return pn;
 }
 
 /*
  * Fold from one constant type to another.
  * XXX handles only strings and numbers for now
  */
 static JSBool
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -1192,17 +1192,17 @@ private:
     bool recognizeDirectivePrologue(JSParseNode *pn, bool *isDirectivePrologueMember);
 
     enum FunctionType { GETTER, SETTER, GENERAL };
     bool functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSParseNode **list);
     JSParseNode *functionBody();
     JSParseNode *functionDef(JSAtom *name, FunctionType type, uintN lambda);
 
     JSParseNode *condition();
-    JSParseNode *comprehensionTail(JSParseNode *kid, uintN blockid,
+    JSParseNode *comprehensionTail(JSParseNode *kid, uintN blockid, bool isGenexp,
                                    js::TokenKind type = js::TOK_SEMI, JSOp op = JSOP_NOP);
     JSParseNode *generatorExpr(JSParseNode *kid);
     JSBool argumentList(JSParseNode *listNode);
     JSParseNode *bracketedExpr();
     JSParseNode *letBlock(JSBool statement);
     JSParseNode *returnOrYield(bool useAssignExpr);
     JSParseNode *destructuringExpr(BindData *data, js::TokenKind tt);
 
--- a/js/src/tests/js1_7/lexical/regress-351515.js
+++ b/js/src/tests/js1_7/lexical/regress-351515.js
@@ -43,28 +43,40 @@ var expect = '';
 
 
 //-----------------------------------------------------------------------------
 test();
 //-----------------------------------------------------------------------------
 
 try
 {
-  expect = 'SyntaxError: syntax error';
+  expect = 'SyntaxError: yield not in function';
   eval('yield = 1;');
   actual = 'No Error';
 }
 catch(ex)
 {
   actual = ex + '';
 }
 reportCompare(expect, actual, summary + ': global: yield = 1');
 
 try
 {
+  expect = 'SyntaxError: syntax error';
+  eval('(function(){yield = 1;})');
+  actual = 'No Error';
+}
+catch(ex)
+{
+  actual = ex + '';
+}
+reportCompare(expect, actual, summary + ': local: yield = 1');
+
+try
+{
   expect = 'SyntaxError: missing variable name';
   eval('let = 1;');
   actual = 'No Error';
 }
 catch(ex)
 {
   actual = ex + '';
 }
--- a/js/src/tests/js1_8/genexps/jstests.list
+++ b/js/src/tests/js1_8/genexps/jstests.list
@@ -3,8 +3,9 @@ script regress-347739.js
 script regress-349012-01.js
 script regress-349326.js
 script regress-349331.js
 script regress-380237-01.js
 script regress-380237-02.js
 script regress-380237-03.js
 skip script regress-380237-04.js # obsolete test, need to remove minor failures to reenable.
 script regress-384991.js
+script regress-634472.js
--- a/js/src/tests/js1_8/genexps/regress-380237-03.js
+++ b/js/src/tests/js1_8/genexps/regress-380237-03.js
@@ -42,22 +42,21 @@ var summary = 'Decompilation of generato
 var actual = '';
 var expect = '';
 
 
 //-----------------------------------------------------------------------------
 test();
 //-----------------------------------------------------------------------------
 
-expect = 'No Error';
+expect = 'SyntaxError: yield not in function';
 actual = '';
 try
 {
-  var g = ((yield i) for (i in [1,2,3]));
-  actual = 'No Error';
+  eval('((yield i) for (i in [1,2,3]))');
 }
 catch(ex)
 {
   actual = ex + '';
 }
 reportCompare(expect, actual, summary + ': top level');
 
 
@@ -77,18 +76,18 @@ function test()
   actual = f + '';
   compareSource(expect, actual, summary);
 
   f = function() { with((x for (x in []))) { } };
   expect = 'function() { with(x for (x in [])) { } }';
   actual = f + '';
   compareSource(expect, actual, summary);
 
-  f = (function() { (1 for (w in []) if (0)) });
-  expect = 'function() { (1 for (w in []) if (0)); }';
+  f = (function() { (1 for (w in []) if (false)) });
+  expect = 'function() { (1 for (w in []) if (false)); }';
   actual = f + '';
   compareSource(expect, actual, summary);
 
   f = (function() { (1 for (w in []) if (x)) });
   expect = 'function() { (1 for (w in []) if (x)); }';
   actual = f + '';
   compareSource(expect, actual, summary);
 
@@ -97,78 +96,91 @@ function test()
   actual = f + '';
   compareSource(expect, actual, summary);
 
   f = (function() { (x for ([{}, {}] in [])); });
   expect = 'function() { (x for ([[], []] in [])); }';
   actual = f + '';
   compareSource(expect, actual, summary);
 
-  expect = 'SyntaxError: invalid assignment left-hand side';
+  expect = 'ReferenceError: invalid assignment left-hand side';
   actual = '';
   try
   {
     eval('(function() { (x for each (x in [])) = 3; })');
   }
   catch(ex)
   {
     actual = ex + '';
   }
   reportCompare(expect, actual, summary + ': Do not Assert: *pc == JSOP_CALL');
 
   f = (function() { (x*x for (x in a)); });
   expect = 'function() { (x*x for (x in a)); }';
   actual = f + '';
   compareSource(expect, actual, summary);
 
-  f = (function () { (1 for (y in (yield 3))); });
-  expect = 'function () { (1 for (y in yield 3)); }';
-  actual = f + '';
-  compareSource(expect, actual, summary);
+  expect = 'SyntaxError: illegal use of yield in generator expression';
+  actual = '';
+  try
+  {
+    eval('(function () { (1 for (y in (yield 3))); })');
+  }
+  catch(ex)
+  {
+    actual = ex + '';
+  }
+  reportCompare(expect, actual, summary);
    
-  expect = 'SyntaxError: invalid delete operand';
+  expect = 'ReferenceError: invalid delete operand';
   try
   {
     eval('(function () { delete (x for (x in [])); })');
   }
   catch(ex)
   {
     actual = ex + '';
   }
   reportCompare(expect, actual, summary + ': Do not Assert: *pc == JSOP_CALL');
 
-  f = (function() { ([yield] for (x in [])); });
-  expect = 'function() { ([yield] for (x in [])); }';
-  actual = f + '';
-  compareSource(expect, actual, summary);
+  expect = 'SyntaxError: illegal use of yield in generator expression';
+  actual = '';
+  try
+  {
+    eval('(function() { ([yield] for (x in [])); })');
+  }
+  catch(ex)
+  {
+    actual = ex + '';
+  }
+  reportCompare(expect, actual, summary);
 
   f = function() { if(1, (x for (x in []))) { } };
   expect = 'function() { if(1, (x for (x in []))) { } }';
   actual = f + '';
   compareSource(expect, actual, summary);
 
   f = function () {return(i*j for each (i in [1,2,3,4]) 
                           for each (j in [5,6,7,8]))};
   expect = 'function () {return(i*j for each (i in [1,2,3,4]) ' + 
     'for each (j in [5,6,7,8]));}';
   actual = f + '';
   compareSource(expect, actual, summary);
 
-  expect = 'No Error';
+  expect = 'SyntaxError: yield not in function';
   actual = '';
   try
   {
-    var g = ((yield i) for (i in [1,2,3]));
-    actual = 'No Error';
+    eval('((yield i) for (i in [1,2,3]))');
   }
   catch(ex)
   {
     actual = ex + '';
   }
-  reportCompare(expect, actual, summary + ': nested');
+  reportCompare(expect, actual, summary);
 
   f = function() { try { } catch(x if (1 for (x in []))) { } finally { } };
   expect = 'function() { try {} catch(x if (1 for (x in []))) {} finally {} }';
   actual = f + '';
   compareSource(expect, actual, summary);
 
   f = eval(uneval(f));
   expect = 'function() { try {} catch(x if (1 for (x in []))) {} finally {} }';
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8/genexps/regress-634472.js
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is JavaScript Engine testing utilities.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Dave Herman
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 634472;
+var summary = 'contextual restrictions for yield and arguments';
+var actual = '';
+var expect = '';
+
+
+function error(str) {
+  let base;
+  try {
+    // the following line must not be broken up into multiple lines
+    base = (function(){try{eval('throw new Error()')}catch(e){return e.lineNumber}})(); eval(str);
+    return null;
+  } catch (e) {
+    e.lineNumber = e.lineNumber - base + 1;
+    return e;
+  }
+}
+
+const JSMSG_GENEXP_YIELD     = error("(function(){((yield) for (x in []))})").message;
+const JSMSG_GENEXP_ARGUMENTS = error("(function(){(arguments 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 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" },
+  { expr: "yield, 1",     top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN, gen: JSMSG_YIELD_PAREN,  desc: "simple yield in list" },
+  { expr: "yield 1, 2",   top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN, gen: JSMSG_YIELD_PAREN,  desc: "yield w/ arg in list" },
+  { expr: "(yield)",      top: JSMSG_TOP_YIELD, fun: null,              gen: JSMSG_GENEXP_YIELD, desc: "simple yield, parenthesized" },
+  { expr: "(yield 1)",    top: JSMSG_TOP_YIELD, fun: null,              gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg, parenthesized" },
+  { expr: "(1, yield)",   top: JSMSG_TOP_YIELD, fun: null,              gen: JSMSG_GENEXP_YIELD, desc: "simple yield at end of list, parenthesized" },
+  { expr: "(1, yield 2)", top: JSMSG_TOP_YIELD, fun: null,              gen: JSMSG_GENEXP_YIELD, desc: "yield w/ arg at end of list, parenthesized" },
+  { expr: "(yield, 1)",   top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN, gen: JSMSG_YIELD_PAREN,  desc: "simple yield in list, parenthesized" },
+  { expr: "(yield 1, 2)", top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN, gen: JSMSG_YIELD_PAREN,  desc: "yield w/ arg in list, parenthesized" },
+
+  // deeply nested yield expressions
+  { 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: JSMSG_GENEXP_ARGUMENTS, desc: "simple arguments" },
+  { expr: "1, arguments",      top: null, fun: null, gen: JSMSG_GENEXP_ARGUMENTS, desc: "arguments in list" },
+  { expr: "(arguments)",       top: null, fun: null, gen: JSMSG_GENEXP_ARGUMENTS, desc: "simple arguments, parenthesized" },
+  { expr: "(1, arguments)",    top: null, fun: null, gen: JSMSG_GENEXP_ARGUMENTS, desc: "arguments in list, parenthesized" },
+  { expr: "((((arguments))))", top: null, fun: null, gen: JSMSG_GENEXP_ARGUMENTS, desc: "deeply nested arguments" },
+
+  // 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 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 2 for (x in []))",      top: JSMSG_TOP_YIELD, fun: 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" },
+  { expr: "(1, (2, 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 list in genexp" },
+  { expr: "(1, (2, yield 3) 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 list 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, parenthesized 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, parenthesized in genexp" },
+  { expr: "(1, (yield, 2) for (x in []))",   top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN,  gen: JSMSG_YIELD_PAREN,  desc: "simple yield in list, parenthesized in list in genexp" },
+  { expr: "(1, (yield 2, 3) for (x in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_YIELD_PAREN,  gen: JSMSG_YIELD_PAREN,  desc: "yield w/ arg in list, parenthesized in list in genexp" },
+
+  // deeply nested yield in generator expressions
+  { expr: "((((1, yield 2))) for (x in []))",               top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "deeply nested yield in genexp" },
+  { expr: "((((1, yield 2)) for (x in [])) for (y in []))", top: JSMSG_TOP_YIELD, fun: JSMSG_GENEXP_YIELD, gen: JSMSG_GENEXP_YIELD, desc: "deeply nested yield in multiple genexps" },
+
+  // arguments in generator expressions
+  { expr: "(arguments for (x in []))",         top: JSMSG_GENEXP_ARGUMENTS, fun: JSMSG_GENEXP_ARGUMENTS, gen: JSMSG_GENEXP_ARGUMENTS, desc: "simple arguments in genexp" },
+  { expr: "(1, arguments for (x in []))",      top: JSMSG_GENEXP_ARGUMENTS, fun: JSMSG_GENEXP_ARGUMENTS, gen: JSMSG_GENEXP_ARGUMENTS, desc: "arguments in list in genexp" },
+  { expr: "((arguments) for (x in []))",       top: JSMSG_GENEXP_ARGUMENTS, fun: JSMSG_GENEXP_ARGUMENTS, gen: JSMSG_GENEXP_ARGUMENTS, desc: "arguments, parenthesized in genexp" },
+  { expr: "(1, (arguments) for (x in []))",    top: JSMSG_GENEXP_ARGUMENTS, fun: JSMSG_GENEXP_ARGUMENTS, gen: JSMSG_GENEXP_ARGUMENTS, desc: "arguments, parenthesized in list in genexp" },
+  { expr: "((1, arguments) for (x in []))",    top: JSMSG_GENEXP_ARGUMENTS, fun: JSMSG_GENEXP_ARGUMENTS, gen: JSMSG_GENEXP_ARGUMENTS, desc: "arguments in list, parenthesized in genexp" },
+  { expr: "(1, (2, arguments) for (x in []))", top: JSMSG_GENEXP_ARGUMENTS, fun: JSMSG_GENEXP_ARGUMENTS, gen: JSMSG_GENEXP_ARGUMENTS, desc: "arguments in list, parenthesized in list in genexp" },
+
+  // deeply nested arguments in generator expressions
+  { expr: "((((1, arguments))) for (x in []))",               top: JSMSG_GENEXP_ARGUMENTS, fun: JSMSG_GENEXP_ARGUMENTS, gen: JSMSG_GENEXP_ARGUMENTS, desc: "deeply nested arguments in genexp" },
+  { expr: "((((1, arguments)) for (x in [])) for (y in []))", top: JSMSG_GENEXP_ARGUMENTS, fun: JSMSG_GENEXP_ARGUMENTS, gen: JSMSG_GENEXP_ARGUMENTS, desc: "deeply nested arguments in multiple genexps" },
+
+  // legal yield/arguments in nested function
+  { expr: "((function() { yield }) for (x in []))",           top: null, fun: null, gen: null, desc: "legal yield in nested function" },
+  { expr: "((function() { arguments }) for (x in []))",       top: null, fun: null, gen: null, desc: "legal arguments in nested function" },
+  { expr: "((function() arguments) for (x in []))",           top: null, fun: null, gen: null, desc: "legal arguments in nested expression-closure" }
+];
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function splitKeyword(str) {
+  return str.
+         replace(/yield for/, '\nyield for\n').
+         replace(/yield ([0-9])/, '\nyield $1\n').
+         replace(/yield([^ ]|$)/, '\nyield\n$1').
+         replace(/arguments/, '\narguments\n');
+}
+
+function expectError1(err, ctx, msg) {
+  reportCompare('object', typeof err,     'exn for: ' + msg);
+  reportCompare(ctx,      err.message,    'exn message for: ' + msg);
+  reportCompare(2,        err.lineNumber, 'exn token for: ' + msg);
+}
+
+function expectError(expr, call, wrapCtx, ctx, msg) {
+  expectError1(error(wrapCtx(expr)), ctx, msg);
+  if (call)
+    expectError1(error(wrapCtx(call)), ctx, 'call argument in ' + msg);
+}
+
+function expectSuccess(err, msg) {
+  reportCompare(null,     err,            'parse: ' + msg);
+}
+
+function atTop(str) { return str }
+function inFun(str) { return '(function(){' + str + '})' }
+function inGen(str) { return '(y for (y in ' + str + '))' }
+
+function test()
+{
+  enterFunc ('test');
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+
+  for (let i = 0, len = cases.length; i < len; i++) {
+    let {expr, top, fun, gen, desc} = cases[i];
+
+    let call = (expr[0] === "(") ? ("print" + expr) : null;
+
+    expr = splitKeyword(expr);
+    if (call)
+      call = splitKeyword(call);
+
+    if (top)
+      expectError(expr, call, atTop, top, 'top-level context, ' + desc);
+    else
+      expectSuccess(error(expr), 'top-level context, ' + desc);
+
+    if (fun)
+      expectError(expr, call, inFun, fun, 'function context, ' + desc);
+    else
+      expectSuccess(error(inFun(expr)), 'function context, ' + desc);
+
+    if (gen)
+      expectError(expr, call, inGen, gen, 'genexp context, ' + desc);
+    else
+      expectSuccess(error(inGen(expr)), 'genexp context, ' + desc);
+  }
+
+  exitFunc ('test');
+}
--- a/js/src/tests/js1_8_1/decompilation/regress-380237-03.js
+++ b/js/src/tests/js1_8_1/decompilation/regress-380237-03.js
@@ -42,22 +42,21 @@ var summary = 'Decompilation of generato
 var actual = '';
 var expect = '';
 
 
 //-----------------------------------------------------------------------------
 test();
 //-----------------------------------------------------------------------------
 
-expect = 'No Error';
+expect = 'SyntaxError: yield not in function';
 actual = '';
 try
 {
-  var g = ((yield i) for (i in [1,2,3]));
-  actual = 'No Error';
+  eval('((yield i) for (i in [1,2,3]))');
 }
 catch(ex)
 {
   actual = ex + '';
 }
 reportCompare(expect, actual, summary + ': top level');
 
 
@@ -97,72 +96,82 @@ function test()
   actual = f + '';
   compareSource(expect, actual, summary);
 
   f = (function() { (x for ([{}, {}] in [])); });
   expect = 'function() { (x for ([[], []] in [])); }';
   actual = f + '';
   compareSource(expect, actual, summary);
 
-  expect = 'SyntaxError: invalid assignment left-hand side';
+  expect = 'ReferenceError: invalid assignment left-hand side';
   actual = '';
   try
   {
     eval('(function() { (x for each (x in [])) = 3; })');
   }
   catch(ex)
   {
     actual = ex + '';
   }
   reportCompare(expect, actual, summary + ': Do not Assert: *pc == JSOP_CALL');
 
   f = (function() { (x*x for (x in a)); });
   expect = 'function() { (x*x for (x in a)); }';
   actual = f + '';
   compareSource(expect, actual, summary);
 
-  f = (function () { (1 for (y in (yield 3))); });
-  expect = 'function () { (1 for (y in yield 3)); }';
-  actual = f + '';
-  compareSource(expect, actual, summary);
+  expect = 'SyntaxError: illegal use of yield in generator expression';
+  try
+  {
+    eval('(function () { (1 for (y in (yield 3))); })');
+  }
+  catch(ex)
+  {
+    actual = ex + '';
+  }
+  reportCompare(expect, actual, summary);
    
-  expect = 'SyntaxError: invalid delete operand';
+  expect = 'ReferenceError: invalid delete operand';
   try
   {
     eval('(function () { delete (x for (x in [])); })');
   }
   catch(ex)
   {
     actual = ex + '';
   }
   reportCompare(expect, actual, summary + ': Do not Assert: *pc == JSOP_CALL');
 
-  f = (function() { ([yield] for (x in [])); });
-  expect = 'function() { ([(yield)] for (x in [])); }';
-  actual = f + '';
-  compareSource(expect, actual, summary);
+  expect = 'SyntaxError: illegal use of yield in generator expression';
+  try
+  {
+    eval('(function() { ([yield] for (x in [])); })');
+  }
+  catch(ex)
+  {
+    actual = ex + '';
+  }
+  reportCompare(expect, actual, summary);
 
   f = function() { if(1, (x for (x in []))) { } };
   expect = 'function() { if(1, (x for (x in []))) { } }';
   actual = f + '';
   compareSource(expect, actual, summary);
 
   f = function () {return(i*j for each (i in [1,2,3,4]) 
                           for each (j in [5,6,7,8]))};
   expect = 'function () {return(i*j for each (i in [1,2,3,4]) ' + 
     'for each (j in [5,6,7,8]));}';
   actual = f + '';
   compareSource(expect, actual, summary);
 
-  expect = 'No Error';
-  actual = '';
+  expect = 'SyntaxError: yield not in function';
   try
   {
-    var g = ((yield i) for (i in [1,2,3]));
-    actual = 'No Error';
+    eval('((yield i) for (i in [1,2,3]))');
   }
   catch(ex)
   {
     actual = ex + '';
   }
   reportCompare(expect, actual, summary + ': nested');
 
   f = function() { try { } catch(x if (1 for (x in []))) { } finally { } };
--- a/js/src/tests/js1_8_1/strict/generator-eval-arguments.js
+++ b/js/src/tests/js1_8_1/strict/generator-eval-arguments.js
@@ -17,21 +17,21 @@ assertEq(testLenientAndStrict('(1 for ([
                               parsesSuccessfully,
                               parseRaisesException(SyntaxError)),
          true);
 assertEq(testLenientAndStrict('(1 for ({x:eval} in []))',
                               parsesSuccessfully,
                               parseRaisesException(SyntaxError)),
          true);
 assertEq(testLenientAndStrict('(1 for (arguments in []))',
-                              parsesSuccessfully,
+                              parseRaisesException(SyntaxError),
                               parseRaisesException(SyntaxError)),
          true);
 assertEq(testLenientAndStrict('(1 for ([arguments] in []))',
-                              parsesSuccessfully,
+                              parseRaisesException(SyntaxError),
                               parseRaisesException(SyntaxError)),
          true);
 assertEq(testLenientAndStrict('(1 for ({x:arguments} in []))',
-                              parsesSuccessfully,
+                              parseRaisesException(SyntaxError),
                               parseRaisesException(SyntaxError)),
          true);
 
 reportCompare(true, true);
--- a/js/src/tests/js1_8_5/extensions/reflect-parse.js
+++ b/js/src/tests/js1_8_5/extensions/reflect-parse.js
@@ -395,16 +395,27 @@ assertStmt("try { } catch (e if foo) { }
                    blockStmt([])));
 assertStmt("try { } catch (e if foo) { } catch (e if bar) { } catch (e) { } finally { }",
            tryStmt(blockStmt([]),
                    [ catchClause(ident("e"), ident("foo"), blockStmt([])),
                      catchClause(ident("e"), ident("bar"), blockStmt([])),
                      catchClause(ident("e"), null, blockStmt([])) ],
                    blockStmt([])));
 
+// Bug 632028: yield outside of a function should throw
+(function() {
+    var threw = false;
+    try {
+        Reflect.parse("yield 0");
+    } catch (expected) {
+        threw = true;
+    }
+    assertEq(threw, true);
+})();
+
 // redeclarations (TOK_NAME nodes with lexdef)
 
 assertStmt("function f() { function g() { } function g() { } }",
            funDecl(ident("f"), [], blockStmt([funDecl(ident("g"), [], blockStmt([])),
                                               funDecl(ident("g"), [], blockStmt([]))])));
 
 assertStmt("function f() { function g() { } function g() { return 42 } }",
            funDecl(ident("f"), [], blockStmt([funDecl(ident("g"), [], blockStmt([])),