Bug 981599 - Update parsing of 'yield' to match latest spec r=jorendorff
authorAndy Wingo <wingo@igalia.com>
Thu, 26 Jun 2014 16:17:02 +0200
changeset 191042 4fb43d3e1db15c37b991f82f11c9780b92c804b3
parent 191041 b513f4a6913ab3c7ed291046d5889f1a20eecc57
child 191043 d2983539a941cca84171ad6c5a2b10758bcf267a
push id8417
push userryanvm@gmail.com
push dateThu, 26 Jun 2014 21:15:47 +0000
treeherderb2g-inbound@3bef42144aab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs981599
milestone33.0a1
Bug 981599 - Update parsing of 'yield' to match latest spec r=jorendorff
js/src/frontend/Parser.cpp
js/src/jit-test/tests/parser/yield-without-operand.js
js/src/tests/ecma_6/Generators/syntax.js
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4823,23 +4823,47 @@ Parser<ParseHandler>::yieldExpression()
 
     switch (pc->generatorKind()) {
       case StarGenerator:
       {
         JS_ASSERT(pc->sc->isFunctionBox());
 
         pc->lastYieldOffset = begin;
 
-        ParseNodeKind kind = tokenStream.matchToken(TOK_MUL) ? PNK_YIELD_STAR : PNK_YIELD;
-
-        // ES6 generators require a value.
-        Node exprNode = assignExpr();
-        if (!exprNode)
+        Node exprNode;
+        ParseNodeKind kind = PNK_YIELD;
+        switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
+          case TOK_ERROR:
             return null();
-
+          // TOK_EOL is special; it implements the [no LineTerminator here]
+          // quirk in the grammar.
+          case TOK_EOL:
+          // The rest of these make up the complete set of tokens that can
+          // appear after any of the places where AssignmentExpression is used
+          // throughout the grammar.  Conveniently, none of them can also be the
+          // start an expression.
+          case TOK_EOF:
+          case TOK_SEMI:
+          case TOK_RC:
+          case TOK_RB:
+          case TOK_RP:
+          case TOK_COLON:
+          case TOK_COMMA:
+            // No value.
+            exprNode = null();
+            break;
+          case TOK_MUL:
+            kind = PNK_YIELD_STAR;
+            tokenStream.consumeKnownToken(TOK_MUL);
+            // Fall through.
+          default:
+            exprNode = assignExpr();
+            if (!exprNode)
+                return null();
+        }
         return handler.newUnary(kind, JSOP_NOP, 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);
@@ -4880,21 +4904,16 @@ Parser<ParseHandler>::yieldExpression()
           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_NOP, begin, exprNode);
--- a/js/src/jit-test/tests/parser/yield-without-operand.js
+++ b/js/src/jit-test/tests/parser/yield-without-operand.js
@@ -1,22 +1,20 @@
-// yield without an operand causes a warning. See bug 885463.
+// yield without an operand is fine and dandy.
 
 load(libdir + "asserts.js");
 
-assertWarning(() => Function("yield"), SyntaxError,
-             "yield followed by EOF should cause a warning");
-assertWarning(() => Function("yield;"), SyntaxError,
-             "yield followed by semicolon should cause a warning");
-assertWarning(() => Function("yield\n  print('ok');"), SyntaxError,
-             "yield followed by newline should cause a warning");
+assertNoWarning(() => Function("yield"), SyntaxError,
+                "yield followed by EOF is fine");
+assertNoWarning(() => Function("yield;"), SyntaxError,
+                "yield followed by semicolon is fine");
+assertNoWarning(() => Function("yield\n  print('ok');"), SyntaxError,
+                "yield followed by newline is fine");
 
-assertWarning(() => eval("(function () { yield; })"), SyntaxError,
-             "yield followed by semicolon in eval code should cause a warning");
-assertWarning(() => eval("(function () { yield })"), SyntaxError,
-             "yield followed by } in eval code should cause a warning");
+assertNoWarning(() => eval("(function () { yield; })"), SyntaxError,
+                "yield followed by semicolon in eval code is fine");
+assertNoWarning(() => eval("(function () { yield })"), SyntaxError,
+                "yield followed by } in eval code is fine");
 
 assertNoWarning(() => Function("yield 0;"),
                 "yield with an operand should be fine");
 assertNoWarning(() => Function("yield 0"),
                 "yield with an operand should be fine, even without a semicolon");
-
-print("\npassed - all those warnings are normal and there's no real way to suppress them");
--- a/js/src/tests/ecma_6/Generators/syntax.js
+++ b/js/src/tests/ecma_6/Generators/syntax.js
@@ -20,16 +20,49 @@ function assertSyntaxError(str) {
 }
 
 // Yield statements.
 function* g() { yield 3; yield 4; }
 
 // Yield expressions.
 function* g() { (yield 3) + (yield 4); }
 
+// Yield without a RHS.
+function* g() { yield; }
+function* g() { yield }
+function* g() {
+    yield
+}
+function* g() { (yield) }
+function* g() { [yield] }
+function* g() { {yield} }
+function* g() { yield, yield }
+function* g() { yield; yield }
+function* g() { (yield) ? yield : yield }
+function* g() {
+    (yield)
+    ? yield
+    : yield
+}
+
+// If yield has a RHS, it needs to start on the same line.  The * in a
+// yield* counts as starting the RHS.
+function* g() {
+    yield *
+    foo
+}
+assertThrows("function* g() { yield\n* foo }", SyntaxError);
+assertIteratorNext(function*(){
+                       yield
+                       3
+                   }(), undefined)
+
+// A YieldExpression is not a LogicalORExpression.
+assertThrows("function* g() { yield ? yield : yield }", SyntaxError);
+
 // 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"; }
@@ -69,19 +102,16 @@ function* g(obj) { yield obj.yield; }
 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