Bug 1099956 - Parse regular expression after yield correctly. r=Waldo
authorTooru Fujisawa <arai_a@mac.com>
Wed, 19 Nov 2014 13:45:07 +0900
changeset 240719 c910d562c7c365cbea1dfa3afba14d027b86e733
parent 240718 c8f1433453748617c9e7ef1f82886aa6e4deb5f6
child 240720 dd689d9e6620a78a21b1603a5c28347a671dec88
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1099956
milestone36.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 1099956 - Parse regular expression after yield correctly. r=Waldo
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/jit-test/tests/generators/yield-regexp.js
js/src/tests/ecma_6/Generators/yield-non-regexp.js
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -5807,17 +5807,19 @@ Parser<ParseHandler>::statement(bool can
                 return null();
             if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL))
                 return null();
         }
         return expressionStatement();
 
       case TOK_YIELD: {
         TokenKind next;
-        if (!tokenStream.peekToken(&next))
+        TokenStream::Modifier modifier = yieldExpressionsSupported() ? TokenStream::Operand
+                                                                     : TokenStream::None;
+        if (!tokenStream.peekToken(&next, modifier))
             return null();
         if (next == TOK_COLON) {
             if (!checkYieldNameValidity())
                 return null();
             return labeledStatement();
         }
         return expressionStatement();
       }
@@ -6166,17 +6168,17 @@ Parser<ParseHandler>::assignExpr()
 
     if (tt == TOK_STRING) {
         if (!tokenStream.nextTokenEndsExpr(&endsExpr))
             return null();
         if (endsExpr)
             return stringLiteral();
     }
 
-    if (tt == TOK_YIELD && (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()))
+    if (tt == TOK_YIELD && yieldExpressionsSupported())
         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);
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -501,16 +501,19 @@ class Parser : private JS::AutoGCRooter,
 
     bool functionArgsAndBodyGeneric(Node pn, HandleFunction fun, FunctionType type,
                                     FunctionSyntaxKind kind);
 
     // Determine whether |yield| is a valid name in the current context, or
     // whether it's prohibited due to strictness, JS version, or occurrence
     // inside a star generator.
     bool checkYieldNameValidity();
+    bool yieldExpressionsSupported() {
+        return versionNumber() >= JSVERSION_1_7 || pc->isGenerator();
+    }
 
     virtual bool strictMode() { return pc->sc->strict; }
 
     const ReadOnlyCompileOptions &options() const {
         return tokenStream.options();
     }
 
   private:
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/generators/yield-regexp.js
@@ -0,0 +1,41 @@
+// Bug 1099956
+
+load(libdir + "asserts.js");
+
+// ES6 treating yield as an identifier except in ES6 generators introduces a
+// syntax conflict with permissible JS >= 1.7 legacy generator syntax.  Is
+// |yield /a/g| inside a function an attempt to convert the function into a
+// legacy generator, yielding a RegExp instance?  Or does it instead read as
+// |(yield / a) / g|?  Similar ambiguities exist for different textual content
+// in place of |a| -- |yield /x+17/g| or |(yield / x) + 17 / g|, and so on.
+// (And, much less importantly, is |yield /a/g| a syntax error in global code
+// as in JS >= 1.7, or is it |(yield / a) / g|.)
+//
+// For now, in JS >= 1.7, we preserve the old behavior.  In all other JS we
+// conform to ES6: |yield /a/g| is a YieldExpression inside an ES6 generator,
+// and it's an IdentifierReference divided twice when not in an ES6 generator.
+// This test will need changes if we change our JS >= 1.7 parsing to be
+// ES6-compatible.
+
+function f1() {
+  yield /abc/g;
+}
+
+var g = f1();
+var v;
+v = g.next();
+assertEq(v instanceof RegExp, true);
+assertEq(v.toString(), "/abc/g");
+assertThrowsValue(() => g.next(), StopIteration);
+
+function* f2() {
+  yield /abc/g;
+}
+
+g = f2();
+v = g.next();
+assertEq(v.done, false);
+assertEq(v.value instanceof RegExp, true);
+assertEq(v.value.toString(), "/abc/g");
+v = g.next();
+assertEq(v.done, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Generators/yield-non-regexp.js
@@ -0,0 +1,16 @@
+var BUGNUMBER = 1099956;
+var summary =
+  "The token next to yield should be tokenized as non-operand if yield is " +
+  "a valid name";
+
+printBugNumber(BUGNUMBER);
+printStatus(summary);
+
+var yield = 12, a = 3, b = 6, g = 2;
+var yieldParsedAsIdentifier = false;
+
+yield /a; yieldParsedAsIdentifier = true; b/g;
+
+assertEq(yieldParsedAsIdentifier, true);
+
+reportCompare(true, true);