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 190967 4fb43d3e1db15c37b991f82f11c9780b92c804b3
parent 190966 b513f4a6913ab3c7ed291046d5889f1a20eecc57
child 190968 d2983539a941cca84171ad6c5a2b10758bcf267a
push id45444
push userwingo@igalia.com
push dateThu, 26 Jun 2014 14:17:42 +0000
treeherdermozilla-inbound@4fb43d3e1db1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs981599
milestone33.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 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