Clarify SyntaxErrors around yield and unskip tests
authorcaitp@igalia.com <caitp@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 4 Aug 2016 02:36:28 +0000 (02:36 +0000)
committercaitp@igalia.com <caitp@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 4 Aug 2016 02:36:28 +0000 (02:36 +0000)
https://bugs.webkit.org/show_bug.cgi?id=158460

Reviewed by Saam Barati.

Fix and unskip tests which erroneously asserted that `yield` is not a
valid BindingIdentifier, and improve error message for YieldExpressions
occuring in Arrow formal parameters.

JSTests:

* stress/generator-syntax.js:
* stress/yield-out-of-generator.js:

Source/JavaScriptCore:

* parser/Parser.cpp:
(JSC::Scope::MaybeParseAsGeneratorForScope::MaybeParseAsGeneratorForScope):
(JSC::Parser<LexerType>::parseFunctionInfo):
(JSC::Parser<LexerType>::parseYieldExpression):
* parser/Parser.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@204111 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JSTests/ChangeLog
JSTests/stress/generator-syntax.js
JSTests/stress/yield-out-of-generator.js
JSTests/test262.yaml
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/Parser.h

index f24fb0b..2c0db85 100644 (file)
@@ -1,3 +1,17 @@
+2016-08-03  Caitlin Potter  <caitp@igalia.com>
+
+        Clarify SyntaxErrors around yield and unskip tests
+        https://bugs.webkit.org/show_bug.cgi?id=158460
+
+        Reviewed by Saam Barati.
+
+        Fix and unskip tests which erroneously asserted that `yield` is not a
+        valid BindingIdentifier, and improve error message for YieldExpressions
+        occuring in Arrow formal parameters.
+
+        * stress/generator-syntax.js:
+        * stress/yield-out-of-generator.js:
+
 2016-08-03  Filip Pizlo  <fpizlo@apple.com>
 
         REGRESSION(r203368): broke some test262 tests
index f5db9bc..4eb3540 100644 (file)
@@ -1,6 +1,3 @@
-// <https://webkit.org/b/158460> Clarify SyntaxErrors around yield and unskip tests
-//@ skip
-
 function testSyntax(script) {
     try {
         eval(script);
@@ -41,40 +38,75 @@ class Hello {
 
 testSyntaxError(`
 function ** gen() { }
-`, `SyntaxError: Unexpected token '*'`);
+`, `SyntaxError: Unexpected token '**'`);
 
-// http://ecma-international.org/ecma-262/6.0/#sec-arrow-function-definitions-static-semantics-early-errors
-testSyntaxError(`
+// "yield" is never a YieldExpression in a ConciseBody (per http://ecma-international.org/ecma-262/6.0/#sec-arrow-function-definitions
+// and https://tc39.github.io/ecma262/#prod-ConciseBody)
+testSyntax(`
 var value = () => {
     yield
 }
-`, `SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.`);
+`);
 
-testSyntaxError(`
+testSyntax(`
 var value = (val = yield) => {
 }
-`, `SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.`);
+`);
 
-testSyntaxError(`
+// Confusingly, FormalParameters[~Yield] does not product a SyntaxError for "yield", even
+// inside of a generator (https://tc39.github.io/ecma262/#prod-FunctionDeclaration),
+// but instead resolves "yield" as a BindingIdentifier
+testSyntax(`
 function *gen() {
     function ng(val = yield) {
     }
 }
-`, `SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.`);
+`);
 
+// Arrow formal parameters within Generators are parameterized with [+Yield], but are
+// still forbidden from including YieldExpressions in ArrowFormalParameters.
+// (https://tc39.github.io/ecma262/#prod-ArrowFormalParameters)
 testSyntaxError(`
+var yield;
 function *gen() {
     var ng = (val = yield) => {
     }
 }
-`, `SyntaxError: Unexpected token '=>'. Expected ';' after variable declaration.`);
+`, `SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.`);
+
+(function testYieldBindingIdentifier() {
+    var yield = "hello!";
+    function* gen() {
+        yield (function(x = yield) { return x; })();
+    }
+    if (gen().next() !== "hello!")
+        throw new Error("Expected BindingIdentifier but found YieldExpression");
+})();
+
+testSyntax(`
+function* gen() {
+    var ng = (it = function*() { yield 1; }) => {
+        return it().next();
+    }
+    yield ng();
+}
+`);
 
-// http://ecma-international.org/ecma-262/6.0/#sec-generator-function-definitions-static-semantics-early-errors
 testSyntaxError(`
-function gen(val = yield) {
+function* gen() {
+    var ng = (it = function() { yield 1; }) => {
+        return it().next();
+    }
+    yield ng();
 }
 `, `SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.`);
 
+testSyntax(`
+function gen(val = yield) {
+}
+`);
+
+// http://ecma-international.org/ecma-262/6.0/#sec-generator-function-definitions-static-semantics-early-errors
 testSyntaxError(`
 function *gen(val = yield) {
 }
index 3347704..64200c7 100644 (file)
@@ -1,6 +1,3 @@
-// <https://webkit.org/b/158460> Clarify SyntaxErrors around yield and unskip tests
-//@ skip
-
 function testSyntax(script) {
     try {
         eval(script);
@@ -24,53 +21,53 @@ function testSyntaxError(script, message) {
         throw new Error("Bad error: " + String(error));
 }
 
-testSyntaxError(`
+testSyntax(`
 yield;
-`, "SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.");
+`);
 
 testSyntaxError(`
 yield*;
-`, "SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.");
+`, "SyntaxError: Unexpected token ';'");
 
 testSyntaxError(`
 yield 0;
-`, "SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.");
+`, "SyntaxError: Unexpected number '0'");
 
-testSyntaxError(`
+testSyntax(`
 yield* 0;
-`, "SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.");
+`);
 
-testSyntaxError(`
+testSyntax(`
 function hello() {
     yield;
 }
-`, "SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.");
+`);
 
 testSyntaxError(`
 function hello() {
     yield*;
 }
-`, "SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.");
+`, "SyntaxError: Unexpected token ';'");
 
 testSyntaxError(`
 function hello() {
     yield 0;
 }
-`, "SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.");
+`, "SyntaxError: Unexpected number '0'");
 
-testSyntaxError(`
+testSyntax(`
 function hello() {
     yield* 0;
 }
-`, "SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.");
+`);
 
-testSyntaxError(`
+testSyntax(`
 function *gen() {
     function hello() {
         yield;
     }
 }
-`, "SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.");
+`);
 
 testSyntaxError(`
 function *gen() {
@@ -78,7 +75,7 @@ function *gen() {
         yield*;
     }
 }
-`, "SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.");
+`, "SyntaxError: Unexpected token ';'");
 
 testSyntaxError(`
 function *gen() {
@@ -86,15 +83,15 @@ function *gen() {
         yield 0;
     }
 }
-`, "SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.");
+`, "SyntaxError: Unexpected number '0'");
 
-testSyntaxError(`
+testSyntax(`
 function *gen() {
     function hello() {
         yield* 0;
     }
 }
-`, "SyntaxError: Unexpected keyword 'yield'. Cannot use yield expression out of generator.");
+`);
 
 testSyntax(`
 function *gen() {
index 4714c33..25c377d 100644 (file)
 - path: test262/test/language/expressions/arrow-function/object-literal-return-requires-body-parens.js
   cmd: runTest262 :normal, "NoException", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/arrow-function/param-dflt-yield-expr.js
-  cmd: runTest262 :fail, "SyntaxError", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "SyntaxError", ["../../../../harness/assert.js", "../../../../harness/sta.js"], []
 - path: test262/test/language/expressions/arrow-function/param-dflt-yield-expr.js
   cmd: runTest262 :normal, "SyntaxError", ["../../../../harness/assert.js", "../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/expressions/arrow-function/param-dflt-yield-id-non-strict.js
index 4886bea..07aae1d 100644 (file)
@@ -1,3 +1,20 @@
+2016-08-03  Caitlin Potter  <caitp@igalia.com>
+
+        Clarify SyntaxErrors around yield and unskip tests
+        https://bugs.webkit.org/show_bug.cgi?id=158460
+
+        Reviewed by Saam Barati.
+
+        Fix and unskip tests which erroneously asserted that `yield` is not a
+        valid BindingIdentifier, and improve error message for YieldExpressions
+        occuring in Arrow formal parameters.
+
+        * parser/Parser.cpp:
+        (JSC::Scope::MaybeParseAsGeneratorForScope::MaybeParseAsGeneratorForScope):
+        (JSC::Parser<LexerType>::parseFunctionInfo):
+        (JSC::Parser<LexerType>::parseYieldExpression):
+        * parser/Parser.h:
+
 2016-08-03  Filip Pizlo  <fpizlo@apple.com>
 
         REGRESSION(r203368): broke some test262 tests
index 0c955e2..509653a 100644 (file)
@@ -240,6 +240,12 @@ Parser<LexerType>::Parser(VM* vm, const SourceCode& source, JSParserBuiltinMode
     next();
 }
 
+class Scope::MaybeParseAsGeneratorForScope : public SetForScope<bool> {
+public:
+    MaybeParseAsGeneratorForScope(ScopeRef& scope, bool shouldParseAsGenerator)
+        : SetForScope<bool>(scope->m_isGenerator, shouldParseAsGenerator) { }
+};
+
 template <typename LexerType>
 Parser<LexerType>::~Parser()
 {
@@ -2026,8 +2032,14 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
 
         if (loadCachedFunction())
             return true;
-        parseFunctionParameters(syntaxChecker, mode, functionInfo);
-        propagateError();
+
+        {
+            // Parse formal parameters with [+Yield] parameterization, in order to ban YieldExpressions
+            // in ArrowFormalParameters, per ES6 #sec-arrow-function-definitions-static-semantics-early-errors.
+            Scope::MaybeParseAsGeneratorForScope parseAsGenerator(functionScope, upperScopeIsGenerator);
+            parseFunctionParameters(syntaxChecker, mode, functionInfo);
+            propagateError();
+        }
 
         matchOrFail(ARROWFUNCTION, "Expected a '=>' after arrow function parameter declaration");
 
@@ -3173,7 +3185,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseYieldExpress
     //     yield [no LineTerminator here] * AssignmentExpression[?In, Yield]
 
     // http://ecma-international.org/ecma-262/6.0/#sec-generator-function-definitions
-    failIfFalse(currentScope()->isGenerator(), "Cannot use yield expression out of generator");
+    failIfFalse(currentScope()->isGenerator() && !currentScope()->isArrowFunctionBoundary(), "Cannot use yield expression out of generator");
 
     // http://ecma-international.org/ecma-262/6.0/#sec-generator-function-definitions-static-semantics-early-errors
     failIfTrue(m_parserState.functionParsePhase == FunctionParsePhase::Parameters, "Cannot use yield expression within parameters");
index a00b804..7697a26 100644 (file)
@@ -667,6 +667,8 @@ public:
             destSet.add(info->usedVariables()[i]);
     }
 
+    class MaybeParseAsGeneratorForScope;
+
 private:
     void setIsFunction()
     {