Skip to content

Commit 6d3f131

Browse files
committed
[MERGE #5380 @boingoing] OS#17895855 - Concise-body lambda function followed by in keyword is not handled correctly
Merge pull request #5380 from boingoing:lambda_concise_body_in_expression At the moment we parse this as a postfix operator, it should be part of the lambda body except for when we're parsing a for..in loop header. Fixes: https://microsoft.visualstudio.com/OS/_workitems/edit/17895855
2 parents efe444d + f3dd30d commit 6d3f131

File tree

4 files changed

+84
-31
lines changed

4 files changed

+84
-31
lines changed

lib/Parser/Parse.cpp

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4852,7 +4852,7 @@ BOOL Parser::IsDeferredFnc()
48524852
}
48534853

48544854
template<bool buildAST>
4855-
ParseNode * Parser::ParseFncDeclCheckScope(ushort flags, bool resetParsingSuperRestrictionState)
4855+
ParseNode * Parser::ParseFncDeclCheckScope(ushort flags, bool resetParsingSuperRestrictionState, bool fAllowIn)
48564856
{
48574857
ParseNodeBlock * pnodeFncBlockScope = nullptr;
48584858
ParseNodePtr *ppnodeScopeSave = nullptr;
@@ -4880,7 +4880,7 @@ ParseNode * Parser::ParseFncDeclCheckScope(ushort flags, bool resetParsingSuperR
48804880
}
48814881
}
48824882

4883-
ParseNodeFnc * pnodeFnc = ParseFncDeclInternal<buildAST>(flags, nullptr, /* needsPIDOnRCurlyScan */ false, resetParsingSuperRestrictionState, /* fUnaryOrParen */ false, noStmtContext);
4883+
ParseNodeFnc * pnodeFnc = ParseFncDeclInternal<buildAST>(flags, nullptr, /* needsPIDOnRCurlyScan */ false, resetParsingSuperRestrictionState, /* fUnaryOrParen */ false, noStmtContext, fAllowIn);
48844884

48854885
if (pnodeFncBlockScope)
48864886
{
@@ -4899,14 +4899,14 @@ ParseNode * Parser::ParseFncDeclCheckScope(ushort flags, bool resetParsingSuperR
48994899
}
49004900

49014901
template<bool buildAST>
4902-
ParseNodeFnc * Parser::ParseFncDeclNoCheckScope(ushort flags, LPCOLESTR pNameHint, const bool needsPIDOnRCurlyScan, bool resetParsingSuperRestrictionState, bool fUnaryOrParen)
4902+
ParseNodeFnc * Parser::ParseFncDeclNoCheckScope(ushort flags, LPCOLESTR pNameHint, const bool needsPIDOnRCurlyScan, bool resetParsingSuperRestrictionState, bool fUnaryOrParen, bool fAllowIn)
49034903
{
49044904
Assert((flags & fFncDeclaration) == 0);
4905-
return ParseFncDeclInternal<buildAST>(flags, pNameHint, needsPIDOnRCurlyScan, resetParsingSuperRestrictionState, fUnaryOrParen, /* noStmtContext */ false);
4905+
return ParseFncDeclInternal<buildAST>(flags, pNameHint, needsPIDOnRCurlyScan, resetParsingSuperRestrictionState, fUnaryOrParen, /* noStmtContext */ false, fAllowIn);
49064906
}
49074907

49084908
template<bool buildAST>
4909-
ParseNodeFnc * Parser::ParseFncDeclInternal(ushort flags, LPCOLESTR pNameHint, const bool needsPIDOnRCurlyScan, bool resetParsingSuperRestrictionState, bool fUnaryOrParen, bool noStmtContext)
4909+
ParseNodeFnc * Parser::ParseFncDeclInternal(ushort flags, LPCOLESTR pNameHint, const bool needsPIDOnRCurlyScan, bool resetParsingSuperRestrictionState, bool fUnaryOrParen, bool noStmtContext, bool fAllowIn)
49104910
{
49114911
AutoParsingSuperRestrictionStateRestorer restorer(this);
49124912
if (resetParsingSuperRestrictionState)
@@ -4987,7 +4987,7 @@ ParseNodeFnc * Parser::ParseFncDeclInternal(ushort flags, LPCOLESTR pNameHint, c
49874987

49884988
IdentPtr pFncNamePid = nullptr;
49894989
bool needScanRCurly = true;
4990-
ParseFncDeclHelper<buildAST>(pnodeFnc, pNameHint, flags, fUnaryOrParen, noStmtContext, &needScanRCurly, fModule, &pFncNamePid);
4990+
ParseFncDeclHelper<buildAST>(pnodeFnc, pNameHint, flags, fUnaryOrParen, noStmtContext, &needScanRCurly, fModule, &pFncNamePid, fAllowIn);
49914991
AddNestedCapturedNames(pnodeFnc);
49924992

49934993
AnalysisAssert(pnodeFnc);
@@ -5159,7 +5159,7 @@ void Parser::AppendFunctionToScopeList(bool fDeclaration, ParseNodeFnc * pnodeFn
51595159
Parse a function definition.
51605160
***************************************************************************/
51615161
template<bool buildAST>
5162-
void Parser::ParseFncDeclHelper(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, ushort flags, bool fUnaryOrParen, bool noStmtContext, bool *pNeedScanRCurly, bool skipFormals, IdentPtr* pFncNamePid)
5162+
void Parser::ParseFncDeclHelper(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, ushort flags, bool fUnaryOrParen, bool noStmtContext, bool *pNeedScanRCurly, bool skipFormals, IdentPtr* pFncNamePid, bool fAllowIn)
51635163
{
51645164
Assert(pnodeFnc);
51655165
ParseNodeFnc * pnodeFncParent = GetCurrentFunctionNode();
@@ -5559,7 +5559,7 @@ void Parser::ParseFncDeclHelper(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, us
55595559
{
55605560
fDeferred = true;
55615561

5562-
this->ParseTopLevelDeferredFunc(pnodeFnc, pnodeFncSave, pNameHint, fLambda, pNeedScanRCurly);
5562+
this->ParseTopLevelDeferredFunc(pnodeFnc, pnodeFncSave, pNameHint, fLambda, pNeedScanRCurly, fAllowIn);
55635563
}
55645564
else
55655565
{
@@ -5594,13 +5594,13 @@ void Parser::ParseFncDeclHelper(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, us
55945594
m_currDeferredStubCount = childStub->nestedCount;
55955595
m_currDeferredStub = childStub->deferredStubs;
55965596
}
5597-
this->FinishFncDecl(pnodeFnc, pNameHint, fLambda, skipFormals);
5597+
this->FinishFncDecl(pnodeFnc, pNameHint, fLambda, skipFormals, fAllowIn);
55985598
m_currDeferredStub = savedStub;
55995599
m_currDeferredStubCount = savedStubCount;
56005600
}
56015601
else
56025602
{
5603-
this->ParseNestedDeferredFunc(pnodeFnc, fLambda, pNeedScanRCurly, &strictModeTurnedOn);
5603+
this->ParseNestedDeferredFunc(pnodeFnc, fLambda, pNeedScanRCurly, &strictModeTurnedOn, fAllowIn);
56045604
}
56055605
}
56065606

@@ -5789,7 +5789,7 @@ void Parser::UpdateCurrentNodeFunc(ParseNodeFnc * pnodeFnc, bool fLambda)
57895789
}
57905790
}
57915791

5792-
void Parser::ParseTopLevelDeferredFunc(ParseNodeFnc * pnodeFnc, ParseNodeFnc * pnodeFncParent, LPCOLESTR pNameHint, bool fLambda, bool *pNeedScanRCurly)
5792+
void Parser::ParseTopLevelDeferredFunc(ParseNodeFnc * pnodeFnc, ParseNodeFnc * pnodeFncParent, LPCOLESTR pNameHint, bool fLambda, bool *pNeedScanRCurly, bool fAllowIn)
57935793
{
57945794
// Parse a function body that is a transition point from building AST to doing fast syntax check.
57955795

@@ -5826,7 +5826,7 @@ void Parser::ParseTopLevelDeferredFunc(ParseNodeFnc * pnodeFnc, ParseNodeFnc * p
58265826
// Their more-complicated text extents won't match the deferred-stub and the single expression should be fast to scan, anyway.
58275827
if (fLambda && !*pNeedScanRCurly)
58285828
{
5829-
ParseExpressionLambdaBody<false>(pnodeFnc);
5829+
ParseExpressionLambdaBody<false>(pnodeFnc, fAllowIn);
58305830
}
58315831
else if (pnodeFncParent != nullptr && m_currDeferredStub != nullptr && !pnodeFncParent->HasDefaultArguments())
58325832
{
@@ -6220,15 +6220,15 @@ ParseNodeFnc * Parser::CreateDummyFuncNode(bool fDeclaration)
62206220
return pnodeFnc;
62216221
}
62226222

6223-
void Parser::ParseNestedDeferredFunc(ParseNodeFnc * pnodeFnc, bool fLambda, bool *pNeedScanRCurly, bool *pStrictModeTurnedOn)
6223+
void Parser::ParseNestedDeferredFunc(ParseNodeFnc * pnodeFnc, bool fLambda, bool *pNeedScanRCurly, bool *pStrictModeTurnedOn, bool fAllowIn)
62246224
{
62256225
// Parse a function nested inside another deferred function.
62266226

62276227
size_t lengthBeforeBody = this->GetSourceLength();
62286228

62296229
if (m_token.tk != tkLCurly && fLambda)
62306230
{
6231-
ParseExpressionLambdaBody<false>(pnodeFnc);
6231+
ParseExpressionLambdaBody<false>(pnodeFnc, fAllowIn);
62326232
*pNeedScanRCurly = false;
62336233
}
62346234
else
@@ -6855,7 +6855,7 @@ ParseNodeFnc * Parser::GenerateEmptyConstructor(bool extends)
68556855
}
68566856

68576857
template<bool buildAST>
6858-
void Parser::ParseExpressionLambdaBody(ParseNodeFnc * pnodeLambda)
6858+
void Parser::ParseExpressionLambdaBody(ParseNodeFnc * pnodeLambda, bool fAllowIn)
68596859
{
68606860
ParseNodePtr *lastNodeRef = nullptr;
68616861

@@ -6876,7 +6876,7 @@ void Parser::ParseExpressionLambdaBody(ParseNodeFnc * pnodeLambda)
68766876
// The scanner needs to create a pid in the case of a string constant token immediately following the lambda body expression.
68776877
// Otherwise, we'll save null for the string constant pid which will AV during ByteCode generation.
68786878
BYTE fScanDeferredFlagsSave = this->GetScanner()->SetDeferredParse(FALSE);
6879-
ParseNodePtr result = ParseExpr<buildAST>(koplAsg, nullptr, TRUE, FALSE, nullptr, nullptr, nullptr, &token, false, nullptr, &lastRParen);
6879+
ParseNodePtr result = ParseExpr<buildAST>(koplAsg, nullptr, fAllowIn, FALSE, nullptr, nullptr, nullptr, &token, false, nullptr, &lastRParen);
68806880
this->GetScanner()->SetDeferredParseFlags(fScanDeferredFlagsSave);
68816881

68826882
this->MarkEscapingRef(result, &token);
@@ -6974,7 +6974,7 @@ void Parser::CheckStrictFormalParameters()
69746974
Assert(m_token.tk == tkRParen);
69756975
}
69766976

6977-
void Parser::FinishFncNode(ParseNodeFnc * pnodeFnc)
6977+
void Parser::FinishFncNode(ParseNodeFnc * pnodeFnc, bool fAllowIn)
69786978
{
69796979
AnalysisAssert(pnodeFnc);
69806980

@@ -7141,7 +7141,7 @@ void Parser::FinishFncNode(ParseNodeFnc * pnodeFnc)
71417141
const charcount_t ichLim = pnodeFnc->ichLim;
71427142
const size_t cbLim = pnodeFnc->cbLim;
71437143

7144-
this->FinishFncDecl(pnodeFnc, NULL, fLambda);
7144+
this->FinishFncDecl(pnodeFnc, NULL, fLambda, /* skipCurlyBraces */ false, fAllowIn);
71457145

71467146
#if DBG
71477147
// The pnode extent may not match the original extent.
@@ -7174,7 +7174,7 @@ void Parser::FinishFncNode(ParseNodeFnc * pnodeFnc)
71747174
this->GetScanner()->SetAwaitIsKeywordRegion(fPreviousAwaitIsKeyword);
71757175
}
71767176

7177-
void Parser::FinishFncDecl(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, bool fLambda, bool skipCurlyBraces)
7177+
void Parser::FinishFncDecl(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, bool fLambda, bool skipCurlyBraces, bool fAllowIn)
71787178
{
71797179
LPCOLESTR name = NULL;
71807180
JS_ETW(int32 startAstSize = *m_pCurrentAstSize);
@@ -7196,7 +7196,7 @@ void Parser::FinishFncDecl(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, bool fL
71967196

71977197
if (fLambda && m_token.tk != tkLCurly)
71987198
{
7199-
ParseExpressionLambdaBody<true>(pnodeFnc);
7199+
ParseExpressionLambdaBody<true>(pnodeFnc, fAllowIn);
72007200
}
72017201
else
72027202
{
@@ -8846,15 +8846,15 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
88468846
this->GetScanner()->SeekTo(termStart);
88478847
}
88488848
}
8849-
pnode = ParseFncDeclNoCheckScope<buildAST>(flags, nullptr, /* needsPIDOnRCurlyScan = */false, /* resetParsingSuperRestrictionState = */false);
8849+
pnode = ParseFncDeclNoCheckScope<buildAST>(flags, nullptr, /* needsPIDOnRCurlyScan = */false, /* resetParsingSuperRestrictionState = */false, /* fUnaryOrParen = */ false, fAllowIn);
88508850
if (isAsyncMethod)
88518851
{
88528852
pnode->AsParseNodeFnc()->cbMin = iecpMin;
88538853
pnode->ichMin = ichMin;
88548854
}
88558855

88568856
// ArrowFunction/AsyncArrowFunction is part of AssignmentExpression, which should terminate the expression unless followed by a comma
8857-
if (m_token.tk != tkComma)
8857+
if (m_token.tk != tkComma && m_token.tk != tkIN)
88588858
{
88598859
if (!(IsTerminateToken()))
88608860
{

lib/Parser/Parse.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -801,18 +801,18 @@ class Parser
801801

802802
template<bool buildAST> void ParseComputedName(ParseNodePtr* ppnodeName, LPCOLESTR* ppNameHint, LPCOLESTR* ppFullNameHint = nullptr, uint32 *pNameLength = nullptr, uint32 *pShortNameOffset = nullptr);
803803
template<bool buildAST> ParseNodeBin * ParseMemberGetSet(OpCode nop, LPCOLESTR* ppNameHint);
804-
template<bool buildAST> ParseNode * ParseFncDeclCheckScope(ushort flags, bool resetParsingSuperRestrictionState = true);
805-
template<bool buildAST> ParseNodeFnc * ParseFncDeclNoCheckScope(ushort flags, LPCOLESTR pNameHint = nullptr, const bool needsPIDOnRCurlyScan = false, bool resetParsingSuperRestrictionState = true, bool fUnaryOrParen = false);
806-
template<bool buildAST> ParseNodeFnc * ParseFncDeclInternal(ushort flags, LPCOLESTR pNameHint, const bool needsPIDOnRCurlyScan, bool resetParsingSuperRestrictionState, bool fUnaryOrParen, bool noStmtContext);
804+
template<bool buildAST> ParseNode * ParseFncDeclCheckScope(ushort flags, bool resetParsingSuperRestrictionState = true, bool fAllowIn = true);
805+
template<bool buildAST> ParseNodeFnc * ParseFncDeclNoCheckScope(ushort flags, LPCOLESTR pNameHint = nullptr, const bool needsPIDOnRCurlyScan = false, bool resetParsingSuperRestrictionState = true, bool fUnaryOrParen = false, bool fAllowIn = true);
806+
template<bool buildAST> ParseNodeFnc * ParseFncDeclInternal(ushort flags, LPCOLESTR pNameHint, const bool needsPIDOnRCurlyScan, bool resetParsingSuperRestrictionState, bool fUnaryOrParen, bool noStmtContext, bool fAllowIn = true);
807807
template<bool buildAST> void ParseFncName(ParseNodeFnc * pnodeFnc, ushort flags, IdentPtr* pFncNamePid = nullptr);
808808
template<bool buildAST> void ParseFncFormals(ParseNodeFnc * pnodeFnc, ParseNodeFnc * pnodeParentFnc, ushort flags, bool isTopLevelDeferredFunc = false);
809-
template<bool buildAST> void ParseFncDeclHelper(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, ushort flags, bool fUnaryOrParen, bool noStmtContext, bool *pNeedScanRCurly, bool skipFormals = false, IdentPtr* pFncNamePid = nullptr);
810-
template<bool buildAST> void ParseExpressionLambdaBody(ParseNodeFnc * pnodeFnc);
809+
template<bool buildAST> void ParseFncDeclHelper(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, ushort flags, bool fUnaryOrParen, bool noStmtContext, bool *pNeedScanRCurly, bool skipFormals = false, IdentPtr* pFncNamePid = nullptr, bool fAllowIn = true);
810+
template<bool buildAST> void ParseExpressionLambdaBody(ParseNodeFnc * pnodeFnc, bool fAllowIn = true);
811811
template<bool buildAST> void UpdateCurrentNodeFunc(ParseNodeFnc * pnodeFnc, bool fLambda);
812812
bool FncDeclAllowedWithoutContext(ushort flags);
813-
void FinishFncDecl(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, bool fLambda, bool skipCurlyBraces = false);
814-
void ParseTopLevelDeferredFunc(ParseNodeFnc * pnodeFnc, ParseNodeFnc * pnodeFncParent, LPCOLESTR pNameHint, bool fLambda, bool *pNeedScanRCurly = nullptr);
815-
void ParseNestedDeferredFunc(ParseNodeFnc * pnodeFnc, bool fLambda, bool *pNeedScanRCurly, bool *pStrictModeTurnedOn);
813+
void FinishFncDecl(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, bool fLambda, bool skipCurlyBraces = false, bool fAllowIn = true);
814+
void ParseTopLevelDeferredFunc(ParseNodeFnc * pnodeFnc, ParseNodeFnc * pnodeFncParent, LPCOLESTR pNameHint, bool fLambda, bool *pNeedScanRCurly = nullptr, bool fAllowIn = true);
815+
void ParseNestedDeferredFunc(ParseNodeFnc * pnodeFnc, bool fLambda, bool *pNeedScanRCurly, bool *pStrictModeTurnedOn, bool fAllowIn = true);
816816
void CheckStrictFormalParameters();
817817
ParseNodeVar * AddArgumentsNodeToVars(ParseNodeFnc * pnodeFnc);
818818
ParseNodeVar * InsertVarAtBeginning(ParseNodeFnc * pnodeFnc, IdentPtr pid);
@@ -845,7 +845,7 @@ class Parser
845845
LPCOLESTR AppendNameHints(LPCOLESTR leftStr, uint32 leftLen, LPCOLESTR rightStr, uint32 rightLen, uint32 *pNameLength, uint32 *pShortNameOffset, bool ignoreAddDotWithSpace = false, bool wrapInBrackets = false);
846846
WCHAR * AllocateStringOfLength(ULONG length);
847847

848-
void FinishFncNode(ParseNodeFnc * pnodeFnc);
848+
void FinishFncNode(ParseNodeFnc * pnodeFnc, bool fAllowIn = true);
849849

850850
template<bool buildAST> bool ParseOptionalExpr(
851851
ParseNodePtr* pnode,

test/es6/bug_OS17895855.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
var tests = [
9+
{
10+
name: "Concise-body lambda function containing in expression",
11+
body: function () {
12+
var l = a => '0' in [123]
13+
assert.areEqual("a => '0' in [123]", l.toString(), "consise-body lambda containing in expression");
14+
assert.isTrue(l(), "in expression can be the concise-body lambda body");
15+
}
16+
},
17+
{
18+
name: "Concise-body lambda function as var decl initializer in a for..in loop",
19+
body: function () {
20+
for (var a = () => 'pass' in []) {
21+
assert.fail("Should not enter for loop since [] has no properties");
22+
}
23+
assert.areEqual('pass', a(), "var decl from for loop should have initialized a");
24+
25+
for (var a2 = () => 'pass' in [123]) {
26+
assert.areEqual('0', a2, "Should enter the for loop with property '0'");
27+
}
28+
assert.areEqual('0', a2, "var decl from for loop should have been assigned to during iteration");
29+
}
30+
},
31+
{
32+
name: "Concise-body lambda function as var decl initializer in a for..in..in loop",
33+
body: function () {
34+
for (var b = () => 'pass' in [] in []) {
35+
assert.fail("Should not enter for loop");
36+
}
37+
assert.areEqual('pass', b(), "var decl from for loop should still have initial value");
38+
39+
for (var b2 = () => 'pass' in '0' in [123]) {
40+
assert.fail("var decl initialization turns into var b2 = () => 'pass' in true which should not enter this loop");
41+
}
42+
assert.areEqual('pass', b2(), "var decl was not overriden inside the for loop");
43+
}
44+
},
45+
];
46+
47+
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

test/es6/rlexe.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,4 +1486,10 @@
14861486
<compile-flags>-args summary -endargs</compile-flags>
14871487
</default>
14881488
</test>
1489+
<test>
1490+
<default>
1491+
<files>bug_OS17895855.js</files>
1492+
<compile-flags>-args summary -endargs</compile-flags>
1493+
</default>
1494+
</test>
14891495
</regress-exe>

0 commit comments

Comments
 (0)