Skip to content

Commit

Permalink
[1.10>master] [MERGE #5219 @rhuanjl] Allow 'in' to terminate expressi…
Browse files Browse the repository at this point in the history
…ons 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
  • Loading branch information
dilijev committed Jul 27, 2018
2 parents 4f6b81b + 2017ecf commit 9bd7d4b
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 5 deletions.
9 changes: 5 additions & 4 deletions lib/Parser/Parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8326,7 +8326,7 @@ void Parser::DeferOrEmitPotentialSpreadError(ParseNodePtr pnodeT)
}
}

bool Parser::IsTerminateToken()
bool Parser::IsTerminateToken(bool fAllowIn)
{
return (m_token.tk == tkRCurly ||
m_token.tk == tkRBrack ||
Expand All @@ -8335,6 +8335,7 @@ bool Parser::IsTerminateToken()
m_token.tk == tkColon ||
m_token.tk == tkComma ||
m_token.tk == tkLimKwd ||
(m_token.tk == tkIN && fAllowIn) ||
this->GetScanner()->FHadNewLine());
}

Expand All @@ -8347,7 +8348,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;
}
Expand Down Expand Up @@ -8481,7 +8482,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)
Expand Down Expand Up @@ -8842,7 +8843,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);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/Parser/Parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ class Parser
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.
Expand Down
80 changes: 80 additions & 0 deletions test/es6/forInEdgeCases.js
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions test/es6/generators-syntax.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 3 additions & 0 deletions test/es6/lambda-expr.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ catch (e)
{
print(e.message);
}

// Legal case, lambda in object inside for()
for (var i = () => {} in {});
6 changes: 6 additions & 0 deletions test/es6/rlexe.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down

0 comments on commit 9bd7d4b

Please sign in to comment.