Skip to content

Commit b975dfa

Browse files
committedJul 27, 2018
[MERGE #5545 @boingoing] Support deferred stubs for functions declared in parameter scope
Merge pull request #5545 from boingoing:deferredstubsforparameterscope 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.
2 parents 9bd7d4b + 2f88043 commit b975dfa

File tree

4 files changed

+138
-57
lines changed

4 files changed

+138
-57
lines changed
 

‎lib/Parser/Parse.cpp

+41-55
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

+2
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

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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+
let fail = 'fail';
8+
9+
function func4(a = 123) {
10+
function v8() {
11+
function v9() {
12+
return v9;
13+
}
14+
return v9();
15+
}
16+
return v8();
17+
}
18+
func4();
19+
20+
21+
var func5 = (a = 123) => (function v6() {
22+
function v7() {
23+
return v7;
24+
}
25+
return v7();
26+
})()
27+
func5();
28+
29+
function func6(a = v => { console.log('pass'); }, b = v => { return a; }) {
30+
function c() {
31+
return b();
32+
}
33+
return c();
34+
}
35+
func6()();
36+
37+
function func7(a, b = function() { return pass; }, c) {
38+
function func8(d, e = function() { return b; }, f) {
39+
return e;
40+
}
41+
return func8();
42+
}
43+
console.log(func7()()());
44+
45+
var func9 = (a, b = () => pass, c) => {
46+
var func10 = (d, e = () => b, f) => {
47+
return e;
48+
}
49+
return func10();
50+
}
51+
console.log(func9()()());
52+
53+
var func11 = (a, b = () => { return pass; }, c) => {
54+
var func12 = (d, e = () => { return b; }, f) => {
55+
return e;
56+
}
57+
return func12();
58+
}
59+
console.log(func11()()());
60+
61+
function func13(a = (b = () => pass, c = () => fail) => b()) {
62+
return a();
63+
}
64+
console.log(func13());
65+
66+
function func14(a = (b = () => { return fail; }, c = () => { return pass; }) => { return c(); }) {
67+
return a();
68+
}
69+
console.log(func14());
70+
71+
function func15(a = class A { meth() { return pass } static meth2() { return fail; } }, b = v => fail, c = (v) => { return fail }, d = fail) {
72+
return new a().meth();
73+
}
74+
console.log(func15());
75+
function func16(a = class A { meth() { return fail } static meth2() { return pass; } }, b = v => fail, c = (v) => { return fail }, d = fail) {
76+
return a.meth2();
77+
}
78+
console.log(func16());
79+
function func17(a = class A { meth() { return fail } static meth2() { return fail; } }, b = v => pass, c = (v) => { return fail }, d = fail) {
80+
return b();
81+
}
82+
console.log(func17());
83+
function func18(a = class A { meth() { return fail } static meth2() { return fail; } }, b = v => fail, c = (v) => { return pass }, d = fail) {
84+
return c();
85+
}
86+
console.log(func18());

‎test/Bugs/rlexe.xml

+9-2
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)
Please sign in to comment.