Kaynağa Gözat

[MERGE #5219 @rhuanjl] Allow 'in' to terminate expressions in for fixes #5203

Merge pull request #5219 from rhuanjl:fixYieldIn

Attempt at fixing issue #5203

Based on #5216

Not 100% sure I've got this right but it passes the mentioned cases and doesn't regress anything I'm aware of.

cc @zenparsing @akroshg
Doug Ilijev 7 yıl önce
ebeveyn
işleme
2017ecfcd2

+ 5 - 4
lib/Parser/Parse.cpp

@@ -8278,7 +8278,7 @@ void Parser::DeferOrEmitPotentialSpreadError(ParseNodePtr pnodeT)
     }
 }
 
-bool Parser::IsTerminateToken()
+bool Parser::IsTerminateToken(bool fAllowIn)
 {
     return (m_token.tk == tkRCurly ||
         m_token.tk == tkRBrack ||
@@ -8287,6 +8287,7 @@ bool Parser::IsTerminateToken()
         m_token.tk == tkColon ||
         m_token.tk == tkComma ||
         m_token.tk == tkLimKwd ||
+        (m_token.tk == tkIN && fAllowIn) ||
         this->GetScanner()->FHadNewLine());
 }
 
@@ -8299,7 +8300,7 @@ template<bool buildAST>
 bool Parser::ParseOptionalExpr(ParseNodePtr* pnode, bool fUnaryOrParen, int oplMin, BOOL *pfCanAssign, BOOL fAllowIn, BOOL fAllowEllipsis, _Inout_opt_ IdentToken* pToken)
 {
     *pnode = nullptr;
-    if (IsTerminateToken())
+    if (IsTerminateToken(!fAllowIn))
     {
         return false;
     }
@@ -8433,7 +8434,7 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
 
         if (nop == knopYield)
         {
-            if (!ParseOptionalExpr<buildAST>(&pnodeT, false, opl, NULL, TRUE, fAllowEllipsis))
+            if (!ParseOptionalExpr<buildAST>(&pnodeT, false, opl, NULL, fAllowIn, fAllowEllipsis))
             {
                 nop = knopYieldLeaf;
                 if (buildAST)
@@ -8795,7 +8796,7 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
             // ArrowFunction/AsyncArrowFunction is part of AssignmentExpression, which should terminate the expression unless followed by a comma
             if (m_token.tk != tkComma && m_token.tk != tkIN)
             {
-                if (!(IsTerminateToken()))
+                if (!(IsTerminateToken(false)))
                 {
                     Error(ERRnoSemic);
                 }

+ 1 - 1
lib/Parser/Parse.h

@@ -828,7 +828,7 @@ private:
     template<bool buildAST> ParseNodePtr ParseMemberList(LPCOLESTR pNameHint, uint32 *pHintLength, tokens declarationType = tkNone);
     template<bool buildAST> IdentPtr ParseSuper(bool fAllowCall);
 
-    bool IsTerminateToken();
+    bool IsTerminateToken(bool fAllowIn);
 
     // Used to determine the type of JavaScript object member.
     // The values can be combined using bitwise OR.

+ 80 - 0
test/es6/forInEdgeCases.js

@@ -0,0 +1,80 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
+
+
+function testGen(func, values, count)
+{
+    const gen = func();
+    let counter = 0;
+    for (const value of gen)
+    {
+        assert.isTrue(value == values[counter]);
+        ++counter;
+    }
+    assert.areEqual(counter, count);
+}
+
+
+const tests = [
+    {
+        name : "For - in with Arrow function sloppy",
+        body : function () {
+            const arr = [0,1,2,5];
+            for (var a = () => { return "a"} in {});
+            assert.areEqual(a(), "a");
+            for (var a = () => { return "a"} in arr);
+            assert.isTrue(a == "3");
+        }
+    },
+    {
+        name : "For - in with Arrow function strict",
+        body : function () {
+            "use strict";
+            assert.throws(()=>{eval("for (var a = () => { return 'a'} in {});")}, SyntaxError);
+        }
+    },
+    {
+        name : "For - in with yield - sloppy",
+        body : function () {
+            function* gen1()
+            {
+                for (var a = yield 'a' in {b: 1}) {
+                    assert.isTrue(a == "b");
+                }
+            }
+            testGen(gen1, ["a"], 1);
+            function* gen2()
+            {
+                for (var a = yield in {c: 1}) {
+                    assert.isTrue(a == "c");
+                }
+            }
+            testGen(gen2, [undefined], 1);
+            function* gen3()
+            {
+                for (var a = yield 'd' in {} in {a: 1}) {
+                    assert.isTrue(false, "shouldn't reach here");
+                }
+            }
+            testGen(gen3, ['d'], 1);
+        }
+    },
+    {
+        name : "For - in with yield - strict",
+        body : function () {
+            "use strict";
+            assert.throws(()=>{eval(`function* gen1()
+            {
+                for (var a = yield 'a' in {b: 1}) {
+                    assert.isTrue(a == "b");
+                }
+            }`)}, SyntaxError);
+        }
+    }
+];
+
+testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

+ 8 - 0
test/es6/generators-syntax.js

@@ -212,6 +212,14 @@ var tests = [
             assert.throws(function () { eval("function *gf() { (a = (yield) => {}) => {}; }"); }, SyntaxError, "yield expression is disallowed within arrow function default parameter expression in nested case too", "The use of a keyword for an identifier is invalid");
         }
     },
+    {
+        name : "yield is allowed before 'in' in a for loop control but not elsewhere bug issue #5203",
+        body : function () {
+            assert.doesNotThrow(function () { eval("function* gf() {for(var a = yield in {});}"); }, "Yield is allowed before 'in' in a for loop");
+            assert.throws(function () { eval("function* gf() {var a = yield in {};}"); }, SyntaxError, "Yield is not allowed before 'in' when not declaring a loop");
+            assert.throws(function () { eval("function* gf() {yield in {};}"); }, SyntaxError, "Yield is not allowed before 'in' when not declaring a loop");
+        }
+    }
 ];
 
 testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

+ 3 - 0
test/es6/lambda-expr.js

@@ -33,3 +33,6 @@ catch (e)
 {
     print(e.message);
 }
+
+// Legal case, lambda in object inside for()
+for (var i = () => {} in {});

+ 6 - 0
test/es6/rlexe.xml

@@ -846,6 +846,12 @@
       <tags>exclude_arm</tags>
     </default>
   </test>
+  <test>
+    <default>
+      <files>forInEdgeCases.js</files>
+      <compile-flags>-args summary -endargs</compile-flags>
+    </default>
+  </test>
   <test>
     <default>
       <files>generators-deferparse.js</files>