Skip to content

Commit a9aee51

Browse files
boingoingakroshg
authored andcommitted
1 parent 2e33d82 commit a9aee51

File tree

10 files changed

+241
-6
lines changed

10 files changed

+241
-6
lines changed

lib/Parser/Parse.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,6 +1738,20 @@ void Parser::BindPidRefsInScope(IdentPtr pid, Symbol *sym, int blockId, uint max
17381738
}
17391739
}
17401740

1741+
if (m_currentNodeFunc && m_currentNodeFunc->pnodeName && pid == m_currentNodeFunc->pnodeName->pid && !m_currentNodeFunc->IsDeclaration() && m_currentNodeFunc->IsBodyAndParamScopeMerged())
1742+
{
1743+
Scope* funcExprScope = m_currentNodeFunc->scope;
1744+
Assert(funcExprScope->GetScopeType() == ScopeType_FuncExpr);
1745+
1746+
ParseNodeBlock* bodyScope = m_currentNodeFunc->pnodeBodyScope;
1747+
Assert(bodyScope->blockType == PnodeBlockType::Function);
1748+
1749+
if (ref->GetScopeId() < bodyScope->blockId && ref->GetScopeId() > blockId)
1750+
{
1751+
funcExprScope->SetIsObject();
1752+
}
1753+
}
1754+
17411755
if (ref->GetScopeId() == blockId)
17421756
{
17431757
break;
@@ -4938,6 +4952,12 @@ ParseNodeFnc * Parser::ParseFncDeclInternal(ushort flags, LPCOLESTR pNameHint, c
49384952
pnodeFnc->SetIsBaseClassConstructor((flags & fFncBaseClassConstructor) != 0);
49394953
pnodeFnc->SetHomeObjLocation(Js::Constants::NoRegister);
49404954

4955+
if (this->m_currentScope && this->m_currentScope->GetScopeType() == ScopeType_Parameter)
4956+
{
4957+
pnodeFnc->SetIsDeclaredInParamScope();
4958+
this->m_currentScope->SetHasNestedParamFunc();
4959+
}
4960+
49414961
IdentPtr pFncNamePid = nullptr;
49424962
bool needScanRCurly = true;
49434963
ParseFncDeclHelper<buildAST>(pnodeFnc, pNameHint, flags, fUnaryOrParen, noStmtContext, &needScanRCurly, fModule, &pFncNamePid, fAllowIn);

lib/Parser/ptree.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ enum FncFlags : uint
445445
kFunctionIsStaticMember = 1 << 24,
446446
kFunctionIsGenerator = 1 << 25, // Function is an ES6 generator function
447447
kFunctionAsmjsMode = 1 << 26,
448-
// Free = 1 << 27,
448+
kFunctionIsDeclaredInParamScope = 1 << 27, // Function is declared in parameter scope (ex: inside default argument)
449449
kFunctionIsAsync = 1 << 28, // function is async
450450
kFunctionHasDirectSuper = 1 << 29, // super()
451451
kFunctionIsDefaultModuleExport = 1 << 30, // function is the default export of a module
@@ -583,6 +583,7 @@ class ParseNodeFnc : public ParseNode
583583
void SetHasHomeObj(bool set = true) { SetFlags(kFunctionHasHomeObj, set); }
584584
void SetUsesArguments(bool set = true) { SetFlags(kFunctionUsesArguments, set); }
585585
void SetIsDefaultModuleExport(bool set = true) { SetFlags(kFunctionIsDefaultModuleExport, set); }
586+
void SetIsDeclaredInParamScope(bool set = true) { SetFlags(kFunctionIsDeclaredInParamScope, set); }
586587
void SetNestedFuncEscapes(bool set = true) { nestedFuncEscapes = set; }
587588
void SetCanBeDeferred(bool set = true) { canBeDeferred = set; }
588589
void ResetBodyAndParamScopeMerged() { isBodyAndParamScopeMerged = false; }
@@ -623,6 +624,7 @@ class ParseNodeFnc : public ParseNode
623624
bool HasHomeObj() const { return HasFlags(kFunctionHasHomeObj); }
624625
bool UsesArguments() const { return HasFlags(kFunctionUsesArguments); }
625626
bool IsDefaultModuleExport() const { return HasFlags(kFunctionIsDefaultModuleExport); }
627+
bool IsDeclaredInParamScope() const { return HasFlags(kFunctionIsDeclaredInParamScope); }
626628
bool NestedFuncEscapes() const { return nestedFuncEscapes; }
627629
bool CanBeDeferred() const { return canBeDeferred; }
628630
bool IsBodyAndParamScopeMerged() { return isBodyAndParamScopeMerged; }

lib/Runtime/ByteCode/ByteCodeEmitter.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3411,8 +3411,6 @@ void ByteCodeGenerator::EmitScopeList(ParseNode *pnode, ParseNode *breakOnBodySc
34113411
}
34123412
this->StartEmitFunction(pnode->AsParseNodeFnc());
34133413

3414-
PushFuncInfo(_u("StartEmitFunction"), funcInfo);
3415-
34163414
if (!funcInfo->IsBodyAndParamScopeMerged())
34173415
{
34183416
this->EmitScopeList(pnode->AsParseNodeFnc()->pnodeBodyScope->pnodeScopes);
@@ -3789,6 +3787,11 @@ void ByteCodeGenerator::StartEmitFunction(ParseNodeFnc *pnodeFnc)
37893787
else if (pnodeFnc->IsBodyAndParamScopeMerged() || bodyScope->GetScopeSlotCount() != 0)
37903788
{
37913789
bodyScope->SetMustInstantiate(funcInfo->frameSlotsRegister != Js::Constants::NoRegister);
3790+
3791+
if (pnodeFnc->IsBodyAndParamScopeMerged() && paramScope && paramScope->GetHasNestedParamFunc())
3792+
{
3793+
paramScope->SetMustInstantiate(funcInfo->frameSlotsRegister != Js::Constants::NoRegister);
3794+
}
37923795
}
37933796

37943797
if (!pnodeFnc->IsBodyAndParamScopeMerged())
@@ -3816,6 +3819,8 @@ void ByteCodeGenerator::StartEmitFunction(ParseNodeFnc *pnodeFnc)
38163819
}
38173820
}
38183821

3822+
PushFuncInfo(_u("StartEmitFunction"), funcInfo);
3823+
38193824
if (!funcInfo->IsBodyAndParamScopeMerged())
38203825
{
38213826
ParseNodeBlock * paramBlock = pnodeFnc->pnodeScopes;

lib/Runtime/ByteCode/ByteCodeGenerator.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1818,7 +1818,7 @@ FuncInfo *ByteCodeGenerator::FindEnclosingNonLambda()
18181818
return nullptr;
18191819
}
18201820

1821-
FuncInfo* GetParentFuncInfo(FuncInfo* child)
1821+
FuncInfo* ByteCodeGenerator::GetParentFuncInfo(FuncInfo* child)
18221822
{
18231823
for (Scope* scope = child->GetBodyScope(); scope; scope = scope->GetEnclosingScope())
18241824
{
@@ -1831,6 +1831,19 @@ FuncInfo* GetParentFuncInfo(FuncInfo* child)
18311831
return nullptr;
18321832
}
18331833

1834+
FuncInfo* ByteCodeGenerator::GetEnclosingFuncInfo()
1835+
{
1836+
FuncInfo* top = this->funcInfoStack->Pop();
1837+
1838+
Assert(!this->funcInfoStack->Empty());
1839+
1840+
FuncInfo* second = this->funcInfoStack->Top();
1841+
1842+
this->funcInfoStack->Push(top);
1843+
1844+
return second;
1845+
}
1846+
18341847
bool ByteCodeGenerator::CanStackNestedFunc(FuncInfo * funcInfo, bool trace)
18351848
{
18361849
#if ENABLE_DEBUG_CONFIG_OPTIONS
@@ -2605,7 +2618,7 @@ void AssignFuncSymRegister(ParseNodeFnc * pnodeFnc, ByteCodeGenerator * byteCode
26052618
Assert(byteCodeGenerator->GetCurrentScope()->GetFunc() == sym->GetScope()->GetFunc());
26062619
if (byteCodeGenerator->GetCurrentScope()->GetFunc() != sym->GetScope()->GetFunc())
26072620
{
2608-
Assert(GetParentFuncInfo(byteCodeGenerator->GetCurrentScope()->GetFunc()) == sym->GetScope()->GetFunc());
2621+
Assert(ByteCodeGenerator::GetParentFuncInfo(byteCodeGenerator->GetCurrentScope()->GetFunc()) == sym->GetScope()->GetFunc());
26092622
sym->GetScope()->SetMustInstantiate(true);
26102623
byteCodeGenerator->ProcessCapturedSym(sym);
26112624
sym->GetScope()->GetFunc()->SetHasLocalInClosure(true);

lib/Runtime/ByteCode/ByteCodeGenerator.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,8 @@ class ByteCodeGenerator
376376
void PopulateFormalsScope(uint beginOffset, FuncInfo *funcInfo, ParseNodeFnc *pnodeFnc);
377377
void InsertPropertyToDebuggerScope(FuncInfo* funcInfo, Js::DebuggerScope* debuggerScope, Symbol* sym);
378378
FuncInfo *FindEnclosingNonLambda();
379+
static FuncInfo* GetParentFuncInfo(FuncInfo* child);
380+
FuncInfo* GetEnclosingFuncInfo();
379381

380382
bool CanStackNestedFunc(FuncInfo * funcInfo, bool trace = false);
381383
void CheckDeferParseHasMaybeEscapedNestedFunc();

lib/Runtime/ByteCode/Scope.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class Scope
4141
BYTE canMergeWithBodyScope : 1;
4242
BYTE hasLocalInClosure : 1;
4343
BYTE isBlockInLoop : 1;
44+
BYTE hasNestedParamFunc : 1;
4445
public:
4546
#if DBG
4647
BYTE isRestored : 1;
@@ -60,6 +61,7 @@ class Scope
6061
canMergeWithBodyScope(true),
6162
hasLocalInClosure(false),
6263
isBlockInLoop(false),
64+
hasNestedParamFunc(false),
6365
location(Js::Constants::NoRegister),
6466
m_symList(nullptr),
6567
m_count(0),
@@ -261,6 +263,9 @@ class Scope
261263
void SetIsBlockInLoop(bool is = true) { isBlockInLoop = is; }
262264
bool IsBlockInLoop() const { return isBlockInLoop; }
263265

266+
void SetHasNestedParamFunc(bool is = true) { hasNestedParamFunc = is; }
267+
bool GetHasNestedParamFunc() const { return hasNestedParamFunc; }
268+
264269
bool HasInnerScopeIndex() const { return innerScopeIndex != (uint)-1; }
265270
uint GetInnerScopeIndex() const { return innerScopeIndex; }
266271
void SetInnerScopeIndex(uint index) { innerScopeIndex = index; }

lib/Runtime/ByteCode/ScopeInfo.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,17 @@ namespace Js
112112
ScopeInfo * ScopeInfo::SaveScopeInfo(ByteCodeGenerator* byteCodeGenerator, Scope * scope, ScriptContext * scriptContext)
113113
{
114114
// Advance past scopes that will be excluded from the closure environment. (But note that we always want the body scope.)
115-
while (scope && (!scope->GetMustInstantiate() && scope != scope->GetFunc()->GetBodyScope()))
115+
while (scope)
116116
{
117+
FuncInfo* func = scope->GetFunc();
118+
119+
if (scope->GetMustInstantiate() ||
120+
func->GetBodyScope() == scope ||
121+
(func->GetParamScope() == scope && func->IsBodyAndParamScopeMerged() && scope->GetHasNestedParamFunc()))
122+
{
123+
break;
124+
}
125+
117126
scope = scope->GetEnclosingScope();
118127
}
119128

@@ -162,6 +171,21 @@ namespace Js
162171
Scope* currentScope = byteCodeGenerator->GetCurrentScope();
163172
Assert(currentScope->GetFunc() == funcInfo);
164173

174+
if (funcInfo->root->IsDeclaredInParamScope())
175+
{
176+
Assert(currentScope->GetScopeType() == ScopeType_FunctionBody);
177+
Assert(currentScope->GetEnclosingScope());
178+
179+
FuncInfo* func = byteCodeGenerator->GetEnclosingFuncInfo();
180+
Assert(func);
181+
182+
if (func->IsBodyAndParamScopeMerged())
183+
{
184+
currentScope = func->GetParamScope();
185+
Assert(currentScope->GetScopeType() == ScopeType_Parameter);
186+
}
187+
}
188+
165189
while (currentScope->GetFunc() == funcInfo)
166190
{
167191
currentScope = currentScope->GetEnclosingScope();

test/Bugs/bug_OS18926499.js

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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+
let throwingFunctions = [{
7+
msg: "Split-scope parent, param-scope child capturing symbol from parent body scope",
8+
body: function foo(a = (()=>+x)()) {
9+
function bar() { eval(''); }
10+
var x;
11+
}
12+
},
13+
{
14+
msg: "Split-scope parent, param-scope child capturing symbol from parent body scope",
15+
body: function foo(a = (()=>+x)()) {
16+
eval('');
17+
var x;
18+
}
19+
},
20+
{
21+
msg: "Merged-scope parent, param-scope child capturing symbol from parent body scope",
22+
body: function foo(a = () => +x) {
23+
var x = 1;
24+
return a();
25+
}
26+
},
27+
{
28+
msg: "Merged-scope parent, param-scope child capturing symbol from parent body scope",
29+
body: function foo(a = (()=>+x)()) {
30+
var x;
31+
}
32+
},
33+
{
34+
msg: "Func expr parent, param-scope func expr child capturing symbol from parent body scope",
35+
body: foo3 = function foo3a(a = (function foo3b() { return +x; })()) {
36+
var x = 123;
37+
}
38+
},
39+
{
40+
msg: "Func expr parent, param-scope func expr child capturing symbol from parent body scope",
41+
body: foo3 = function foo3a(a = (function foo3b() { return +x; })()) {
42+
eval('');
43+
var x = 123;
44+
}
45+
},
46+
{
47+
msg: "Func expr parent, param-scope func expr child capturing symbol from parent body scope",
48+
body: foo3 = function foo3a(a = (function foo3b() { return +x; })()) {
49+
function bar() { eval(''); }
50+
var x = 123;
51+
}
52+
},
53+
{
54+
msg: "Param-scope func expr child with nested func expr capturing symbol from parent body scope",
55+
body: foo5 = function foo5a(a = (function(){(function(b = 123) { +x; })()})()) {
56+
function bar() { eval(''); }
57+
var x;
58+
}
59+
},
60+
{
61+
msg: "Multiple nested func expr, inner param-scope function capturing outer func expr name",
62+
body: foo3 = function foo3a(a = (function foo3b(b = (function foo3c(c = (function foo3d() { +x; })()){})()){})()){ var x;}
63+
}];
64+
65+
let nonThrowingFunctions = [{
66+
msg: "Func expr parent, param-scope func expr child capturing parent func expr name",
67+
body: foo3 = function foo3a(a = (function foo3b() { +foo3a; })()) {
68+
return +a;
69+
}
70+
},
71+
{
72+
msg: "Func expr parent, param-scope func expr child capturing own func expr name",
73+
body: foo3 = function foo3a(a = (function foo3b() { +foo3b; })()) {
74+
return +a;
75+
}
76+
},
77+
{
78+
msg: "Func expr parent, param-scope func expr child capturing expression name hint",
79+
body: foo3 = function foo3a(a = (function foo3b() { +foo3; })()) {
80+
return +a;
81+
}
82+
},
83+
{
84+
msg: "Func expr parent, param-scope func expr child capturing parent argument name",
85+
body: foo3 = function foo3a(b = 123, a = (function foo3b() { return +b; })()) {
86+
if (123 !== a) throw 123;
87+
}
88+
},
89+
{
90+
msg: "Func expr parent, param-scope func expr child capturing symbol from parent body scope",
91+
body: foo3 = function foo3a(b = 123, a = (function foo3b() { return +b; })()) {
92+
if (123 !== a) throw 123;
93+
}
94+
},
95+
{
96+
msg: "Multiple nested func expr, inner param-scope function capturing outer func expr name",
97+
body: foo3 = function foo3a(a = (function foo3b(b = (function foo3c(c = (function foo3d() { foo3d; })()){})()){})()){}
98+
},
99+
{
100+
msg: "Multiple nested func expr, inner param-scope function capturing outer func expr name",
101+
body: foo3 = function foo3a(a = (function foo3b(b = (function foo3c(c = (function foo3d() { foo3c; })()){})()){})()){}
102+
},
103+
{
104+
msg: "Multiple nested func expr, inner param-scope function capturing outer func expr name",
105+
body: foo3 = function foo3a(a = (function foo3b(b = (function foo3c(c = (function foo3d() { foo3b; })()){})()){})()){}
106+
},
107+
{
108+
msg: "Multiple nested func expr, inner param-scope function capturing outer func expr name",
109+
body: foo3 = function foo3a(a = (function foo3b(b = (function foo3c(c = (function foo3d() { foo3a; })()){})()){})()){}
110+
},
111+
{
112+
msg: "Multiple nested func expr, inner param-scope function capturing outer func expr name",
113+
body: foo3 = function foo3a(a = (function foo3b(b = (function foo3c(c = (function foo3d() { foo3; })()){})()){})()){}
114+
}];
115+
116+
for (let fn of throwingFunctions) {
117+
try {
118+
fn.body();
119+
console.log(`fail: ${fn.msg}`);
120+
} catch (e) {
121+
console.log("pass");
122+
}
123+
}
124+
125+
for (let fn of nonThrowingFunctions) {
126+
try {
127+
fn.body();
128+
console.log("pass");
129+
} catch (e) {
130+
console.log(`fail: ${fn.msg}`);
131+
}
132+
}

test/Bugs/bug_OS23102586.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
// force:deferparse
7+
8+
function test0() {
9+
var k;
10+
11+
function foo(a = function() { +k; }) {
12+
a();
13+
function bar() { a }
14+
};
15+
16+
eval('')
17+
foo();
18+
}
19+
test0();
20+
console.log('pass')

test/Bugs/rlexe.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,4 +536,16 @@
536536
<compile-flags>-esdynamicimport -mutehosterrormsg -args summary -endargs</compile-flags>
537537
</default>
538538
</test>
539+
<test>
540+
<default>
541+
<files>bug_OS18926499.js</files>
542+
<compile-flags>-force:deferparse</compile-flags>
543+
</default>
544+
</test>
545+
<test>
546+
<default>
547+
<files>bug_OS23102586.js</files>
548+
<compile-flags>-force:deferparse</compile-flags>
549+
</default>
550+
</test>
539551
</regress-exe>

0 commit comments

Comments
 (0)