Skip to content

Commit 83b1218

Browse files
committed
[MERGE #4136 @boingoing] Support for defer parse of object literal methods
Merge pull request #4136 from boingoing:DeferParseMethods This is mostly a straightforward modification to the defer-parse methods in the parser to enable them to know how to handle the object literal method grammar. Communicate the fact that the deferred function is a method via a parser flag - `fscrDeferredFncIsMethod`.
2 parents b006606 + 7941e72 commit 83b1218

File tree

7 files changed

+321
-36
lines changed

7 files changed

+321
-36
lines changed

Diff for: lib/Parser/Parse.cpp

+102-29
Original file line numberDiff line numberDiff line change
@@ -3215,6 +3215,14 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
32153215
pid = ParseSuper<buildAST>(!!fAllowCall);
32163216
isSpecialName = true;
32173217

3218+
// Super reference and super call need to push a pid ref to 'this' even when not building an AST
3219+
ReferenceSpecialName(wellKnownPropertyPids._this, ichMin, ichLim);
3220+
// Super call needs to reference 'new.target'
3221+
if (pid == wellKnownPropertyPids._superConstructor)
3222+
{
3223+
ReferenceSpecialName(wellKnownPropertyPids._newTarget, ichMin, ichLim);
3224+
}
3225+
32183226
goto LIdentifier;
32193227

32203228
case tkTHIS:
@@ -4529,6 +4537,11 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
45294537
{
45304538
Error(ERRsyntax);
45314539
}
4540+
4541+
// Include star character in the function extents
4542+
ichMin = m_pscan->IchMinTok();
4543+
iecpMin = m_pscan->IecpMinTok();
4544+
45324545
m_pscan->ScanForcingPid();
45334546
fncDeclFlags |= fFncGenerator;
45344547
}
@@ -4702,7 +4715,7 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
47024715
ParseNodePtr pnodeFunc = ParseFncDecl<buildAST>(fncDeclFlags | (isAsyncMethod ? fFncAsync : fFncNoFlgs), pFullNameHint,
47034716
/*needsPIDOnRCurlyScan*/ false, /*resetParsingSuperRestrictionState*/ false);
47044717

4705-
if (isAsyncMethod)
4718+
if (isAsyncMethod || isGenerator)
47064719
{
47074720
pnodeFunc->sxFnc.cbMin = iecpMin;
47084721
pnodeFunc->ichMin = ichMin;
@@ -5277,7 +5290,6 @@ bool Parser::ParseFncDeclHelper(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, usho
52775290
// switch scanner to treat 'yield' as keyword in generator functions
52785291
// or as an identifier in non-generator functions
52795292
bool fPreviousYieldIsKeyword = m_pscan->SetYieldIsKeywordRegion(pnodeFnc && pnodeFnc->sxFnc.IsGenerator());
5280-
52815293
bool fPreviousAwaitIsKeyword = m_pscan->SetAwaitIsKeywordRegion(fAsync);
52825294

52835295
if (pnodeFnc && pnodeFnc->sxFnc.IsGenerator())
@@ -5318,10 +5330,10 @@ bool Parser::ParseFncDeclHelper(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, usho
53185330
}
53195331

53205332
uint uDeferSave = m_grfscr & fscrDeferFncParse;
5321-
if (flags & fFncNoName)
5333+
if (flags & fFncClassMember)
53225334
{
5323-
// Disable deferral on getter/setter or other construct with unusual text bounds
5324-
// (fFncNoName) as these are usually trivial, and re-parsing is problematic.
5335+
// Disable deferral on class members or other construct with unusual text bounds
5336+
// as these are usually trivial, and re-parsing is problematic.
53255337
// NOTE: It is probably worth supporting these cases for memory and load-time purposes,
53265338
// especially as they become more and more common.
53275339
m_grfscr &= ~fscrDeferFncParse;
@@ -7129,6 +7141,7 @@ void Parser::FinishFncNode(ParseNodePtr pnodeFnc)
71297141
m_pnestedCount = &pnodeFnc->sxFnc.nestedCount;
71307142

71317143
bool fLambda = pnodeFnc->sxFnc.IsLambda();
7144+
bool fMethod = pnodeFnc->sxFnc.IsMethod();
71327145

71337146
// Cue up the parser to the start of the function body.
71347147
if (pnodeFnc->sxFnc.pnodeName)
@@ -7139,7 +7152,30 @@ void Parser::FinishFncNode(ParseNodePtr pnodeFnc)
71397152
else
71407153
{
71417154
m_pscan->SetCurrentCharacter(pnodeFnc->ichMin, pnodeFnc->sxFnc.lineNumber);
7142-
if (pnodeFnc->sxFnc.IsAccessor())
7155+
7156+
if (fMethod)
7157+
{
7158+
// Method. Skip identifier name, computed property name, "async", "get", "set", and '*' or '(' characters.
7159+
for (;;)
7160+
{
7161+
m_pscan->Scan();
7162+
// '[' character indicates a computed property name for this method. We should consume it.
7163+
if (m_token.tk == tkLBrack)
7164+
{
7165+
// We don't care what the name expr is.
7166+
m_pscan->Scan();
7167+
ParseExpr<false>();
7168+
Assert(m_token.tk == tkRBrack);
7169+
continue;
7170+
}
7171+
// Quit scanning ahead when we reach a '(' character which opens the arg list.
7172+
if (m_token.tk == tkLParen)
7173+
{
7174+
break;
7175+
}
7176+
}
7177+
}
7178+
else if (pnodeFnc->sxFnc.IsAccessor())
71437179
{
71447180
// Getter/setter. The node text starts with the name, so eat that.
71457181
m_pscan->ScanNoKeywords();
@@ -7171,7 +7207,11 @@ void Parser::FinishFncNode(ParseNodePtr pnodeFnc)
71717207
bool fPreviousAwaitIsKeyword = m_pscan->SetAwaitIsKeywordRegion(pnodeFnc && pnodeFnc->sxFnc.IsAsync());
71727208

71737209
// Skip the arg list.
7174-
m_pscan->Scan();
7210+
if (!fMethod)
7211+
{
7212+
// If this is a method, we've already advanced to the '(' token.
7213+
m_pscan->Scan();
7214+
}
71757215
if (m_token.tk == tkStar)
71767216
{
71777217
Assert(pnodeFnc->sxFnc.IsGenerator());
@@ -11517,7 +11557,9 @@ ParseNodePtr Parser::Parse(LPCUTF8 pszSrc, size_t offset, size_t length, charcou
1151711557
ushort flags = fFncNoFlgs;
1151811558
size_t iecpMin = 0;
1151911559
charcount_t ichMin = 0;
11520-
bool isAsyncMethod = false;
11560+
bool isAsync = false;
11561+
bool isGenerator = false;
11562+
bool isMethod = false;
1152111563

1152211564
// The top-level deferred function body was defined by a function expression whose parsing was deferred. We are now
1152311565
// parsing it, so unset the flag so that any nested functions are parsed normally. This flag is only applicable the
@@ -11536,49 +11578,73 @@ ParseNodePtr Parser::Parse(LPCUTF8 pszSrc, size_t offset, size_t length, charcou
1153611578
flags |= fFncDeclaration;
1153711579
}
1153811580

11539-
// There are three cases which can confirm async function:
11540-
// async function... -> async function
11541-
// async (... -> async lambda with parens around the formal parameter
11542-
// async identifier... -> async lambda with single identifier parameter
11581+
if (m_grfscr & fscrDeferredFncIsMethod)
11582+
{
11583+
m_grfscr &= ~fscrDeferredFncIsMethod;
11584+
isMethod = true;
11585+
flags |= fFncNoName | fFncMethod;
11586+
}
11587+
11588+
// These are the cases which can confirm async function:
11589+
// async function() {} -> async function
11590+
// async () => {} -> async lambda with parens around the formal parameter
11591+
// async arg => {} -> async lambda with single identifier parameter
11592+
// async name() {} -> async method
11593+
// async [computed_name]() {} -> async method with a computed name
1154311594
if (m_token.tk == tkID && m_token.GetIdentifier(m_phtbl) == wellKnownPropertyPids.async && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
1154411595
{
1154511596
ichMin = m_pscan->IchMinTok();
1154611597
iecpMin = m_pscan->IecpMinTok();
1154711598

11548-
// Keep state so we can rewind if it turns out that this isn't an async function.
11549-
// The only way this can happen is if we have a lambda with a single formal parameter named 'async' not enclosed by parens.
11599+
// Keep state so we can rewind if it turns out that this isn't an async function:
11600+
// async() {} -> method named async
11601+
// async => {} -> lambda with single parameter named async
1155011602
RestorePoint termStart;
1155111603
m_pscan->Capture(&termStart);
1155211604

1155311605
m_pscan->Scan();
11554-
if ((m_token.tk == tkID || m_token.tk == tkLParen || m_token.tk == tkFUNCTION) && !m_pscan->FHadNewLine())
11606+
11607+
if (m_token.tk == tkDArrow || (m_token.tk == tkLParen && isMethod) || m_pscan->FHadNewLine())
1155511608
{
11556-
flags |= fFncAsync;
11557-
isAsyncMethod = true;
11609+
m_pscan->SeekTo(termStart);
1155811610
}
1155911611
else
1156011612
{
11561-
m_pscan->SeekTo(termStart);
11613+
flags |= fFncAsync;
11614+
isAsync = true;
1156211615
}
1156311616
}
1156411617

11565-
// If first token of the function is tkID or tkLParen, this is a lambda.
11566-
if (m_token.tk == tkID || m_token.tk == tkLParen)
11618+
if (m_token.tk == tkStar && m_scriptContext->GetConfig()->IsES6GeneratorsEnabled())
1156711619
{
11568-
flags |= fFncLambda;
11620+
ichMin = m_pscan->IchMinTok();
11621+
iecpMin = m_pscan->IecpMinTok();
11622+
11623+
flags |= fFncGenerator;
11624+
isGenerator = true;
11625+
11626+
m_pscan->Scan();
1156911627
}
11570-
else
11628+
11629+
// Eat the computed name expression
11630+
if (m_token.tk == tkLBrack && isMethod)
1157111631
{
11572-
// Must be ordinary function keyword - do not eat the token
11573-
ChkCurTokNoScan(tkFUNCTION, ERRsyntax);
11632+
m_pscan->Scan();
11633+
ParseExpr<false>();
11634+
}
11635+
11636+
if (!isMethod && (m_token.tk == tkID || m_token.tk == tkLParen))
11637+
{
11638+
// If first token of the function is tkID or tkLParen, this is a lambda.
11639+
flags |= fFncLambda;
1157411640
}
1157511641

1157611642
ParseNodePtr pnodeFnc = ParseFncDecl<true>(flags, nullptr, false, false);
1157711643
pnodeProg->sxFnc.pnodeBody = nullptr;
1157811644
AddToNodeList(&pnodeProg->sxFnc.pnodeBody, &lastNodeRef, pnodeFnc);
1157911645

11580-
// Include the async keyword in the function extents
11581-
if (isAsyncMethod)
11646+
// Include the async keyword or star character in the function extents
11647+
if (isAsync || isGenerator)
1158211648
{
1158311649
pnodeFnc->sxFnc.cbMin = iecpMin;
1158411650
pnodeFnc->ichMin = ichMin;
@@ -12519,6 +12585,16 @@ IdentPtr Parser::ParseSuper(bool fAllowCall)
1251912585
break;
1252012586
}
1252112587

12588+
currentNodeFunc->sxFnc.SetHasSuperReference(TRUE);
12589+
CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(Super, m_scriptContext);
12590+
12591+
// If we are defer parsing, we can skip verifying that the super reference is valid.
12592+
// If it wasn't the parser would have thrown during upfront parsing and we wouldn't be defer parsing the function.
12593+
if (m_parseType == ParseType_Deferred)
12594+
{
12595+
return superPid;
12596+
}
12597+
1252212598
if (!fAllowCall && (m_token.tk == tkLParen))
1252312599
{
1252412600
Error(ERRInvalidSuper); // new super() is not allowed
@@ -12556,9 +12632,6 @@ IdentPtr Parser::ParseSuper(bool fAllowCall)
1255612632
// Anything else is an error
1255712633
Error(ERRInvalidSuper);
1255812634
}
12559-
12560-
currentNodeFunc->sxFnc.SetHasSuperReference(TRUE);
12561-
CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(Super, m_scriptContext);
1256212635

1256312636
return superPid;
1256412637
}

Diff for: lib/Parser/ParseFlags.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ enum
5151
// let/const in global scope instead of eval scope so that they can be preserved across console inputs
5252
fscrNoAsmJs = 1 << 25, // Disable generation of asm.js code
5353
fscrIsModuleCode = 1 << 26, // Current code should be parsed as a module body
54-
fscrAll = (1 << 27) - 1
55-
};
5654

55+
fscrDeferredFncIsMethod = 1 << 27,
56+
fscrAll = (1 << 28) - 1
57+
};

Diff for: lib/Runtime/Base/FunctionBody.cpp

+13-1
Original file line numberDiff line numberDiff line change
@@ -1605,7 +1605,8 @@ namespace Js
16051605
paramScopeSlotArraySize(0),
16061606
m_reparsed(false),
16071607
m_isAsmJsFunction(false),
1608-
m_tag21(true)
1608+
m_tag21(true),
1609+
m_isMethod(false)
16091610
#if DBG
16101611
,m_wasEverAsmjsMode(false)
16111612
,scopeObjectSize(0)
@@ -1655,6 +1656,7 @@ namespace Js
16551656
m_isStaticNameFunction(proxy->GetIsStaticNameFunction()),
16561657
m_reportedInParamCount(proxy->GetReportedInParamsCount()),
16571658
m_reparsed(proxy->IsReparsed()),
1659+
m_isMethod(proxy->IsMethod()),
16581660
m_tag21(true)
16591661
#if DBG
16601662
,m_wasEverAsmjsMode(proxy->m_wasEverAsmjsMode)
@@ -2336,6 +2338,16 @@ namespace Js
23362338
// (not a function declaration statement).
23372339
grfscr |= fscrDeferredFncExpression;
23382340
}
2341+
2342+
if (funcBody->IsMethod())
2343+
{
2344+
grfscr |= fscrDeferredFncIsMethod;
2345+
}
2346+
else
2347+
{
2348+
grfscr &= ~fscrDeferredFncIsMethod;
2349+
}
2350+
23392351
if (!CONFIG_FLAG(DeferNested) || isDebugOrAsmJsReparse)
23402352
{
23412353
grfscr &= ~fscrDeferFncParse; // Disable deferred parsing if not DeferNested, or doing a debug/asm.js re-parse

Diff for: lib/Runtime/Base/FunctionBody.h

+5-4
Original file line numberDiff line numberDiff line change
@@ -2025,6 +2025,8 @@ namespace Js
20252025
LPCUTF8 GetStartOfDocument(const char16* reason = nullptr) const;
20262026
bool IsReparsed() const { return m_reparsed; }
20272027
void SetReparsed(bool set) { m_reparsed = set; }
2028+
bool IsMethod() const { return m_isMethod; }
2029+
void SetIsMethod(bool set) { m_isMethod = set; }
20282030
bool GetExternalDisplaySourceName(BSTR* sourceName);
20292031

20302032
void CleanupToReparse();
@@ -2159,10 +2161,9 @@ namespace Js
21592161
FieldWithBarrier(bool) m_isEval : 1; // Source code is in 'eval'
21602162
FieldWithBarrier(bool) m_isDynamicFunction : 1; // Source code is in 'Function'
21612163
FieldWithBarrier(bool) m_hasImplicitArgIns : 1;
2162-
FieldWithBarrier(bool) m_dontInline : 1; // Used by the JIT's inliner
2163-
2164-
// Indicates if the function has been reparsed for debug attach/detach scenario.
2165-
FieldWithBarrier(bool) m_reparsed : 1;
2164+
FieldWithBarrier(bool) m_dontInline : 1; // Used by the JIT's inliner
2165+
FieldWithBarrier(bool) m_reparsed : 1; // Indicates if the function has been reparsed for debug attach/detach scenario.
2166+
FieldWithBarrier(bool) m_isMethod : 1; // Function is an object literal method
21662167

21672168
// This field is not required for deferred parsing but because our thunks can't handle offsets > 128 bytes
21682169
// yet, leaving this here for now. We can look at optimizing the function info and function proxy structures some

Diff for: lib/Runtime/ByteCode/ByteCodeGenerator.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,7 @@ FuncInfo * ByteCodeGenerator::StartBindFunction(const char16 *name, uint nameLen
14031403
parseableFunctionInfo->deferredParseNextFunctionId = pnode->sxFnc.deferredParseNextFunctionId;
14041404
#endif
14051405
parseableFunctionInfo->SetIsDeclaration(pnode->sxFnc.IsDeclaration() != 0);
1406+
parseableFunctionInfo->SetIsMethod(pnode->sxFnc.IsMethod() != 0);
14061407
parseableFunctionInfo->SetIsAccessor(pnode->sxFnc.IsAccessor() != 0);
14071408
if (pnode->sxFnc.IsAccessor())
14081409
{

0 commit comments

Comments
 (0)