Skip to content

Commit 69d24eb

Browse files
committed
Support deferred stubs for functions declared in parameter scope
We previously skipped creating deferred function stubs for functions declared in the parameter scope. This throws-off the order of nested functions under functions with children defined in the parameter scope. We worked-around this by not using deferred stubs in parents with default arguments. Add support for creating deferred stubs and using them when parsing function parameter lists.
1 parent 9bd7d4b commit 69d24eb

File tree

4 files changed

+110
-57
lines changed

4 files changed

+110
-57
lines changed

lib/Parser/Parse.cpp

Lines changed: 41 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5405,10 +5405,7 @@ void Parser::ParseFncDeclHelper(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, us
54055405
m_reparsingLambdaParams = true;
54065406
}
54075407

5408-
DeferredFunctionStub* savedDeferredStub = m_currDeferredStub;
5409-
m_currDeferredStub = nullptr;
54105408
this->ParseFncFormals<buildAST>(pnodeFnc, pnodeFncParent, flags, isTopLevelDeferredFunc);
5411-
m_currDeferredStub = savedDeferredStub;
54125409

54135410
m_reparsingLambdaParams = fLambdaParamsSave;
54145411
}
@@ -5822,7 +5819,7 @@ void Parser::ParseTopLevelDeferredFunc(ParseNodeFnc * pnodeFnc, ParseNodeFnc * p
58225819
{
58235820
ParseExpressionLambdaBody<false>(pnodeFnc, fAllowIn);
58245821
}
5825-
else if (pnodeFncParent != nullptr && m_currDeferredStub != nullptr && !pnodeFncParent->HasDefaultArguments())
5822+
else if (pnodeFncParent != nullptr && m_currDeferredStub != nullptr)
58265823
{
58275824
// We've already parsed this function body for syntax errors on the initial parse of the script.
58285825
// We have information that allows us to skip it, so do so.
@@ -13953,43 +13950,13 @@ bool Parser::IsCreatingStateCache()
1395313950
&& CONFIG_FLAG(ParserStateCache));
1395413951
}
1395513952

13956-
DeferredFunctionStub * Parser::BuildDeferredStubTree(ParseNodeFnc *pnodeFnc, Recycler *recycler)
13953+
uint Parser::BuildDeferredStubTreeHelper(ParseNodeBlock* pnodeBlock, DeferredFunctionStub* deferredStubs, uint currentStubIndex, uint deferredStubCount, Recycler *recycler)
1395713954
{
13958-
Assert(CONFIG_FLAG(ParserStateCache));
13959-
13960-
uint nestedCount = pnodeFnc->nestedCount;
13961-
if (nestedCount == 0)
13962-
{
13963-
return nullptr;
13964-
}
13955+
Assert(pnodeBlock != nullptr
13956+
&& (pnodeBlock->blockType == PnodeBlockType::Function
13957+
|| pnodeBlock->blockType == PnodeBlockType::Parameter));
1396513958

13966-
if (pnodeFnc->deferredStub)
13967-
{
13968-
return pnodeFnc->deferredStub;
13969-
}
13970-
13971-
DeferredFunctionStub* deferredStubs = RecyclerNewArray(recycler, DeferredFunctionStub, nestedCount);
13972-
uint i = 0;
13973-
ParseNodeBlock* pnodeBlock = pnodeFnc->pnodeBodyScope;
13974-
ParseNodePtr pnodeChild = nullptr;
13975-
13976-
if (pnodeFnc->nop == knopProg)
13977-
{
13978-
Assert(pnodeFnc->pnodeBodyScope == nullptr
13979-
&& pnodeFnc->pnodeScopes != nullptr
13980-
&& pnodeFnc->pnodeScopes->blockType == PnodeBlockType::Global);
13981-
13982-
pnodeBlock = pnodeFnc->pnodeScopes;
13983-
pnodeChild = pnodeFnc->pnodeScopes->pnodeScopes;
13984-
}
13985-
else
13986-
{
13987-
Assert(pnodeBlock != nullptr
13988-
&& (pnodeBlock->blockType == PnodeBlockType::Function
13989-
|| pnodeBlock->blockType == PnodeBlockType::Parameter));
13990-
13991-
pnodeChild = pnodeBlock->pnodeScopes;
13992-
}
13959+
ParseNodePtr pnodeChild = pnodeBlock->pnodeScopes;
1399313960

1399413961
while (pnodeChild != nullptr)
1399513962
{
@@ -14004,37 +13971,56 @@ DeferredFunctionStub * Parser::BuildDeferredStubTree(ParseNodeFnc *pnodeFnc, Rec
1400413971
}
1400513972

1400613973
ParseNodeFnc* pnodeFncChild = pnodeChild->AsParseNodeFnc();
14007-
AnalysisAssertOrFailFast(i < nestedCount);
14008-
14009-
if (pnodeFncChild->pnodeBody != nullptr)
14010-
{
14011-
// Anomalous case of a non-deferred function nested within a deferred one.
14012-
// Work around by discarding the stub tree.
14013-
return nullptr;
14014-
}
13974+
AnalysisAssertOrFailFast(currentStubIndex < deferredStubCount);
13975+
Assert(pnodeFncChild->pnodeBody == nullptr);
1401513976

1401613977
if (pnodeFncChild->IsGeneratedDefault())
1401713978
{
14018-
++i;
13979+
++currentStubIndex;
1401913980
pnodeChild = pnodeFncChild->pnodeNext;
1402013981
continue;
1402113982
}
1402213983

14023-
deferredStubs[i].fncFlags = pnodeFncChild->fncFlags;
14024-
deferredStubs[i].nestedCount = pnodeFncChild->nestedCount;
14025-
deferredStubs[i].restorePoint = *pnodeFncChild->pRestorePoint;
14026-
deferredStubs[i].deferredStubs = BuildDeferredStubTree(pnodeFncChild, recycler);
14027-
deferredStubs[i].ichMin = pnodeChild->ichMin;
13984+
deferredStubs[currentStubIndex].fncFlags = pnodeFncChild->fncFlags;
13985+
deferredStubs[currentStubIndex].nestedCount = pnodeFncChild->nestedCount;
13986+
deferredStubs[currentStubIndex].restorePoint = *pnodeFncChild->pRestorePoint;
13987+
deferredStubs[currentStubIndex].deferredStubs = BuildDeferredStubTree(pnodeFncChild, recycler);
13988+
deferredStubs[currentStubIndex].ichMin = pnodeChild->ichMin;
1402813989

1402913990
// Save the set of captured names onto the deferred stub.
1403013991
// Since this set is allocated in the Parser arena, we'll have to convert these
1403113992
// into indices in a string table which will survive when the parser goes away.
14032-
deferredStubs[i].capturedNamePointers = pnodeFncChild->GetCapturedNames();
13993+
deferredStubs[currentStubIndex].capturedNamePointers = pnodeFncChild->GetCapturedNames();
1403313994

14034-
++i;
13995+
++currentStubIndex;
1403513996
pnodeChild = pnodeFncChild->pnodeNext;
1403613997
}
1403713998

13999+
return currentStubIndex;
14000+
}
14001+
14002+
DeferredFunctionStub * Parser::BuildDeferredStubTree(ParseNodeFnc *pnodeFnc, Recycler *recycler)
14003+
{
14004+
Assert(CONFIG_FLAG(ParserStateCache));
14005+
14006+
uint nestedCount = pnodeFnc->nestedCount;
14007+
if (nestedCount == 0)
14008+
{
14009+
return nullptr;
14010+
}
14011+
14012+
if (pnodeFnc->deferredStub)
14013+
{
14014+
return pnodeFnc->deferredStub;
14015+
}
14016+
14017+
DeferredFunctionStub* deferredStubs = RecyclerNewArray(recycler, DeferredFunctionStub, nestedCount);
14018+
14019+
uint currentStubIndex = BuildDeferredStubTreeHelper(pnodeFnc->pnodeScopes, deferredStubs, 0, nestedCount, recycler);
14020+
currentStubIndex = BuildDeferredStubTreeHelper(pnodeFnc->pnodeBodyScope, deferredStubs, currentStubIndex, nestedCount, recycler);
14021+
14022+
Assert(currentStubIndex == nestedCount);
14023+
1403814024
pnodeFnc->deferredStub = deferredStubs;
1403914025
return deferredStubs;
1404014026
}

lib/Parser/Parse.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ class Parser
272272
SourceContextInfo * sourceContextInfo, Js::ParseableFunctionInfo* functionInfo);
273273

274274
protected:
275+
static uint BuildDeferredStubTreeHelper(ParseNodeBlock* pnodeBlock, DeferredFunctionStub* deferredStubs, uint currentStubIndex, uint deferredStubCount, Recycler *recycler);
276+
275277
HRESULT ParseSourceInternal(
276278
__out ParseNodeProg ** parseTree, LPCUTF8 pszSrc, size_t offsetInBytes,
277279
size_t lengthInCodePoints, charcount_t offsetInChars, bool isUtf8,

test/Bugs/deferredStubBugs.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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 pass = 'pass';
7+
8+
function func4(a = 123) {
9+
function v8() {
10+
function v9() {
11+
return v9;
12+
}
13+
return v9();
14+
}
15+
return v8();
16+
}
17+
func4();
18+
19+
20+
var func5 = (a = 123) => (function v6() {
21+
function v7() {
22+
return v7;
23+
}
24+
return v7();
25+
})()
26+
func5();
27+
28+
function func6(a = v => { console.log('pass'); }, b = v => { return a; }) {
29+
function c() {
30+
return b();
31+
}
32+
return c();
33+
}
34+
func6()();
35+
36+
function func7(a, b = function() { return pass; }, c) {
37+
function func8(d, e = function() { return b; }, f) {
38+
return e;
39+
}
40+
return func8();
41+
}
42+
console.log(func7()()());
43+
44+
var func9 = (a, b = () => pass, c) => {
45+
var func10 = (d, e = () => b, f) => {
46+
return e;
47+
}
48+
return func10();
49+
}
50+
console.log(func9()()());
51+
52+
var func11 = (a, b = () => { return pass; }, c) => {
53+
var func12 = (d, e = () => { return b; }, f) => {
54+
return e;
55+
}
56+
return func12();
57+
}
58+
console.log(func11()()());

test/Bugs/rlexe.xml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@
488488
<default>
489489
<files>bug17785360.js</files>
490490
</default>
491-
</test>
491+
</test>
492492
<test>
493493
<default>
494494
<files>bug_OS17530048.js</files>
@@ -510,7 +510,7 @@
510510
<default>
511511
<files>OS_17745531.js</files>
512512
</default>
513-
</test>
513+
</test>
514514
<test>
515515
<default>
516516
<files>SuperUndoDeferIssue.js</files>
@@ -523,4 +523,11 @@
523523
<compile-flags>-force:cachedScope</compile-flags>
524524
</default>
525525
</test>
526+
<test>
527+
<default>
528+
<files>deferredStubBugs.js</files>
529+
<tags>exclude_jshost</tags>
530+
<compile-flags>-force:deferparse -parserstatecache -useparserstatecache</compile-flags>
531+
</default>
532+
</test>
526533
</regress-exe>

0 commit comments

Comments
 (0)