Skip to content

Commit 9bd7d4b

Browse files
committed
[1.10>master] [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
2 parents 4f6b81b + 2017ecf commit 9bd7d4b

File tree

6 files changed

+103
-5
lines changed

6 files changed

+103
-5
lines changed

lib/Parser/Parse.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8326,7 +8326,7 @@ void Parser::DeferOrEmitPotentialSpreadError(ParseNodePtr pnodeT)
83268326
}
83278327
}
83288328

8329-
bool Parser::IsTerminateToken()
8329+
bool Parser::IsTerminateToken(bool fAllowIn)
83308330
{
83318331
return (m_token.tk == tkRCurly ||
83328332
m_token.tk == tkRBrack ||
@@ -8335,6 +8335,7 @@ bool Parser::IsTerminateToken()
83358335
m_token.tk == tkColon ||
83368336
m_token.tk == tkComma ||
83378337
m_token.tk == tkLimKwd ||
8338+
(m_token.tk == tkIN && fAllowIn) ||
83388339
this->GetScanner()->FHadNewLine());
83398340
}
83408341

@@ -8347,7 +8348,7 @@ template<bool buildAST>
83478348
bool Parser::ParseOptionalExpr(ParseNodePtr* pnode, bool fUnaryOrParen, int oplMin, BOOL *pfCanAssign, BOOL fAllowIn, BOOL fAllowEllipsis, _Inout_opt_ IdentToken* pToken)
83488349
{
83498350
*pnode = nullptr;
8350-
if (IsTerminateToken())
8351+
if (IsTerminateToken(!fAllowIn))
83518352
{
83528353
return false;
83538354
}
@@ -8481,7 +8482,7 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
84818482

84828483
if (nop == knopYield)
84838484
{
8484-
if (!ParseOptionalExpr<buildAST>(&pnodeT, false, opl, NULL, TRUE, fAllowEllipsis))
8485+
if (!ParseOptionalExpr<buildAST>(&pnodeT, false, opl, NULL, fAllowIn, fAllowEllipsis))
84858486
{
84868487
nop = knopYieldLeaf;
84878488
if (buildAST)
@@ -8842,7 +8843,7 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
88428843
// ArrowFunction/AsyncArrowFunction is part of AssignmentExpression, which should terminate the expression unless followed by a comma
88438844
if (m_token.tk != tkComma && m_token.tk != tkIN)
88448845
{
8845-
if (!(IsTerminateToken()))
8846+
if (!(IsTerminateToken(false)))
88468847
{
88478848
Error(ERRnoSemic);
88488849
}

lib/Parser/Parse.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,7 @@ class Parser
828828
template<bool buildAST> ParseNodePtr ParseMemberList(LPCOLESTR pNameHint, uint32 *pHintLength, tokens declarationType = tkNone);
829829
template<bool buildAST> IdentPtr ParseSuper(bool fAllowCall);
830830

831-
bool IsTerminateToken();
831+
bool IsTerminateToken(bool fAllowIn);
832832

833833
// Used to determine the type of JavaScript object member.
834834
// The values can be combined using bitwise OR.

test/es6/forInEdgeCases.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
7+
8+
9+
function testGen(func, values, count)
10+
{
11+
const gen = func();
12+
let counter = 0;
13+
for (const value of gen)
14+
{
15+
assert.isTrue(value == values[counter]);
16+
++counter;
17+
}
18+
assert.areEqual(counter, count);
19+
}
20+
21+
22+
const tests = [
23+
{
24+
name : "For - in with Arrow function sloppy",
25+
body : function () {
26+
const arr = [0,1,2,5];
27+
for (var a = () => { return "a"} in {});
28+
assert.areEqual(a(), "a");
29+
for (var a = () => { return "a"} in arr);
30+
assert.isTrue(a == "3");
31+
}
32+
},
33+
{
34+
name : "For - in with Arrow function strict",
35+
body : function () {
36+
"use strict";
37+
assert.throws(()=>{eval("for (var a = () => { return 'a'} in {});")}, SyntaxError);
38+
}
39+
},
40+
{
41+
name : "For - in with yield - sloppy",
42+
body : function () {
43+
function* gen1()
44+
{
45+
for (var a = yield 'a' in {b: 1}) {
46+
assert.isTrue(a == "b");
47+
}
48+
}
49+
testGen(gen1, ["a"], 1);
50+
function* gen2()
51+
{
52+
for (var a = yield in {c: 1}) {
53+
assert.isTrue(a == "c");
54+
}
55+
}
56+
testGen(gen2, [undefined], 1);
57+
function* gen3()
58+
{
59+
for (var a = yield 'd' in {} in {a: 1}) {
60+
assert.isTrue(false, "shouldn't reach here");
61+
}
62+
}
63+
testGen(gen3, ['d'], 1);
64+
}
65+
},
66+
{
67+
name : "For - in with yield - strict",
68+
body : function () {
69+
"use strict";
70+
assert.throws(()=>{eval(`function* gen1()
71+
{
72+
for (var a = yield 'a' in {b: 1}) {
73+
assert.isTrue(a == "b");
74+
}
75+
}`)}, SyntaxError);
76+
}
77+
}
78+
];
79+
80+
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

test/es6/generators-syntax.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,14 @@ var tests = [
212212
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");
213213
}
214214
},
215+
{
216+
name : "yield is allowed before 'in' in a for loop control but not elsewhere bug issue #5203",
217+
body : function () {
218+
assert.doesNotThrow(function () { eval("function* gf() {for(var a = yield in {});}"); }, "Yield is allowed before 'in' in a for loop");
219+
assert.throws(function () { eval("function* gf() {var a = yield in {};}"); }, SyntaxError, "Yield is not allowed before 'in' when not declaring a loop");
220+
assert.throws(function () { eval("function* gf() {yield in {};}"); }, SyntaxError, "Yield is not allowed before 'in' when not declaring a loop");
221+
}
222+
}
215223
];
216224

217225
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

test/es6/lambda-expr.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,6 @@ catch (e)
3333
{
3434
print(e.message);
3535
}
36+
37+
// Legal case, lambda in object inside for()
38+
for (var i = () => {} in {});

test/es6/rlexe.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,12 @@
846846
<tags>exclude_arm</tags>
847847
</default>
848848
</test>
849+
<test>
850+
<default>
851+
<files>forInEdgeCases.js</files>
852+
<compile-flags>-args summary -endargs</compile-flags>
853+
</default>
854+
</test>
849855
<test>
850856
<default>
851857
<files>generators-deferparse.js</files>

0 commit comments

Comments
 (0)