Skip to content

Commit 76620d3

Browse files
committed
[1.7>master] [MERGE chakra-core#4136 @boingoing] Support for defer parse of object literal methods
Merge pull request chakra-core#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 17dbf40 + 83b1218 commit 76620d3

7 files changed

Lines changed: 322 additions & 37 deletions

File tree

lib/Parser/Parse.cpp

Lines changed: 102 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3204,6 +3204,14 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
32043204
pid = ParseSuper<buildAST>(!!fAllowCall);
32053205
isSpecialName = true;
32063206

3207+
// Super reference and super call need to push a pid ref to 'this' even when not building an AST
3208+
ReferenceSpecialName(wellKnownPropertyPids._this, ichMin, ichLim);
3209+
// Super call needs to reference 'new.target'
3210+
if (pid == wellKnownPropertyPids._superConstructor)
3211+
{
3212+
ReferenceSpecialName(wellKnownPropertyPids._newTarget, ichMin, ichLim);
3213+
}
3214+
32073215
goto LIdentifier;
32083216

32093217
case tkTHIS:
@@ -4531,6 +4539,11 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
45314539
{
45324540
Error(ERRsyntax);
45334541
}
4542+
4543+
// Include star character in the function extents
4544+
ichMin = m_pscan->IchMinTok();
4545+
iecpMin = m_pscan->IecpMinTok();
4546+
45344547
m_pscan->ScanForcingPid();
45354548
fncDeclFlags |= fFncGenerator;
45364549
}
@@ -4704,7 +4717,7 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
47044717
ParseNodePtr pnodeFunc = ParseFncDecl<buildAST>(fncDeclFlags | (isAsyncMethod ? fFncAsync : fFncNoFlgs), pFullNameHint,
47054718
/*needsPIDOnRCurlyScan*/ false, /*resetParsingSuperRestrictionState*/ false);
47064719

4707-
if (isAsyncMethod)
4720+
if (isAsyncMethod || isGenerator)
47084721
{
47094722
pnodeFunc->sxFnc.cbMin = iecpMin;
47104723
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;
@@ -7170,6 +7182,7 @@ void Parser::FinishFncNode(ParseNodePtr pnodeFnc)
71707182
m_pnestedCount = &pnodeFnc->sxFnc.nestedCount;
71717183

71727184
bool fLambda = pnodeFnc->sxFnc.IsLambda();
7185+
bool fMethod = pnodeFnc->sxFnc.IsMethod();
71737186

71747187
// Cue up the parser to the start of the function body.
71757188
if (pnodeFnc->sxFnc.pnodeName)
@@ -7180,7 +7193,30 @@ void Parser::FinishFncNode(ParseNodePtr pnodeFnc)
71807193
else
71817194
{
71827195
m_pscan->SetCurrentCharacter(pnodeFnc->ichMin, pnodeFnc->sxFnc.lineNumber);
7183-
if (pnodeFnc->sxFnc.IsAccessor())
7196+
7197+
if (fMethod)
7198+
{
7199+
// Method. Skip identifier name, computed property name, "async", "get", "set", and '*' or '(' characters.
7200+
for (;;)
7201+
{
7202+
m_pscan->Scan();
7203+
// '[' character indicates a computed property name for this method. We should consume it.
7204+
if (m_token.tk == tkLBrack)
7205+
{
7206+
// We don't care what the name expr is.
7207+
m_pscan->Scan();
7208+
ParseExpr<false>();
7209+
Assert(m_token.tk == tkRBrack);
7210+
continue;
7211+
}
7212+
// Quit scanning ahead when we reach a '(' character which opens the arg list.
7213+
if (m_token.tk == tkLParen)
7214+
{
7215+
break;
7216+
}
7217+
}
7218+
}
7219+
else if (pnodeFnc->sxFnc.IsAccessor())
71847220
{
71857221
// Getter/setter. The node text starts with the name, so eat that.
71867222
m_pscan->ScanNoKeywords();
@@ -7212,7 +7248,11 @@ void Parser::FinishFncNode(ParseNodePtr pnodeFnc)
72127248
bool fPreviousAwaitIsKeyword = m_pscan->SetAwaitIsKeywordRegion(pnodeFnc && pnodeFnc->sxFnc.IsAsync());
72137249

72147250
// Skip the arg list.
7215-
m_pscan->Scan();
7251+
if (!fMethod)
7252+
{
7253+
// If this is a method, we've already advanced to the '(' token.
7254+
m_pscan->Scan();
7255+
}
72167256
if (m_token.tk == tkStar)
72177257
{
72187258
Assert(pnodeFnc->sxFnc.IsGenerator());
@@ -11576,7 +11616,9 @@ ParseNodePtr Parser::Parse(LPCUTF8 pszSrc, size_t offset, size_t length, charcou
1157611616
ushort flags = fFncNoFlgs;
1157711617
size_t iecpMin = 0;
1157811618
charcount_t ichMin = 0;
11579-
bool isAsyncMethod = false;
11619+
bool isAsync = false;
11620+
bool isGenerator = false;
11621+
bool isMethod = false;
1158011622

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

11598-
// There are three cases which can confirm async function:
11599-
// async function... -> async function
11600-
// async (... -> async lambda with parens around the formal parameter
11601-
// async identifier... -> async lambda with single identifier parameter
11640+
if (m_grfscr & fscrDeferredFncIsMethod)
11641+
{
11642+
m_grfscr &= ~fscrDeferredFncIsMethod;
11643+
isMethod = true;
11644+
flags |= fFncNoName | fFncMethod;
11645+
}
11646+
11647+
// These are the cases which can confirm async function:
11648+
// async function() {} -> async function
11649+
// async () => {} -> async lambda with parens around the formal parameter
11650+
// async arg => {} -> async lambda with single identifier parameter
11651+
// async name() {} -> async method
11652+
// async [computed_name]() {} -> async method with a computed name
1160211653
if (m_token.tk == tkID && m_token.GetIdentifier(m_phtbl) == wellKnownPropertyPids.async && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
1160311654
{
1160411655
ichMin = m_pscan->IchMinTok();
1160511656
iecpMin = m_pscan->IecpMinTok();
1160611657

11607-
// Keep state so we can rewind if it turns out that this isn't an async function.
11608-
// The only way this can happen is if we have a lambda with a single formal parameter named 'async' not enclosed by parens.
11658+
// Keep state so we can rewind if it turns out that this isn't an async function:
11659+
// async() {} -> method named async
11660+
// async => {} -> lambda with single parameter named async
1160911661
RestorePoint termStart;
1161011662
m_pscan->Capture(&termStart);
1161111663

1161211664
m_pscan->Scan();
11613-
if ((m_token.tk == tkID || m_token.tk == tkLParen || m_token.tk == tkFUNCTION) && !m_pscan->FHadNewLine())
11665+
11666+
if (m_token.tk == tkDArrow || (m_token.tk == tkLParen && isMethod) || m_pscan->FHadNewLine())
1161411667
{
11615-
flags |= fFncAsync;
11616-
isAsyncMethod = true;
11668+
m_pscan->SeekTo(termStart);
1161711669
}
1161811670
else
1161911671
{
11620-
m_pscan->SeekTo(termStart);
11672+
flags |= fFncAsync;
11673+
isAsync = true;
1162111674
}
1162211675
}
1162311676

11624-
// If first token of the function is tkID or tkLParen, this is a lambda.
11625-
if (m_token.tk == tkID || m_token.tk == tkLParen)
11677+
if (m_token.tk == tkStar && m_scriptContext->GetConfig()->IsES6GeneratorsEnabled())
1162611678
{
11627-
flags |= fFncLambda;
11679+
ichMin = m_pscan->IchMinTok();
11680+
iecpMin = m_pscan->IecpMinTok();
11681+
11682+
flags |= fFncGenerator;
11683+
isGenerator = true;
11684+
11685+
m_pscan->Scan();
1162811686
}
11629-
else
11687+
11688+
// Eat the computed name expression
11689+
if (m_token.tk == tkLBrack && isMethod)
11690+
{
11691+
m_pscan->Scan();
11692+
ParseExpr<false>();
11693+
}
11694+
11695+
if (!isMethod && (m_token.tk == tkID || m_token.tk == tkLParen))
1163011696
{
11631-
// Must be ordinary function keyword - do not eat the token
11632-
ChkCurTokNoScan(tkFUNCTION, ERRsyntax);
11697+
// If first token of the function is tkID or tkLParen, this is a lambda.
11698+
flags |= fFncLambda;
1163311699
}
1163411700

1163511701
ParseNodePtr pnodeFnc = ParseFncDecl<true>(flags, nullptr, false, false);
1163611702
pnodeProg->sxFnc.pnodeBody = nullptr;
1163711703
AddToNodeList(&pnodeProg->sxFnc.pnodeBody, &lastNodeRef, pnodeFnc);
1163811704

11639-
// Include the async keyword in the function extents
11640-
if (isAsyncMethod)
11705+
// Include the async keyword or star character in the function extents
11706+
if (isAsync || isGenerator)
1164111707
{
1164211708
pnodeFnc->sxFnc.cbMin = iecpMin;
1164311709
pnodeFnc->ichMin = ichMin;
@@ -12578,6 +12644,16 @@ IdentPtr Parser::ParseSuper(bool fAllowCall)
1257812644
break;
1257912645
}
1258012646

12647+
currentNodeFunc->sxFnc.SetHasSuperReference(TRUE);
12648+
CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(Super, m_scriptContext);
12649+
12650+
// If we are defer parsing, we can skip verifying that the super reference is valid.
12651+
// If it wasn't the parser would have thrown during upfront parsing and we wouldn't be defer parsing the function.
12652+
if (m_parseType == ParseType_Deferred)
12653+
{
12654+
return superPid;
12655+
}
12656+
1258112657
if (!fAllowCall && (m_token.tk == tkLParen))
1258212658
{
1258312659
Error(ERRInvalidSuper); // new super() is not allowed
@@ -12616,9 +12692,6 @@ IdentPtr Parser::ParseSuper(bool fAllowCall)
1261612692
Error(ERRInvalidSuper);
1261712693
}
1261812694

12619-
currentNodeFunc->sxFnc.SetHasSuperReference(TRUE);
12620-
CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(Super, m_scriptContext);
12621-
1262212695
return superPid;
1262312696
}
1262412697

lib/Parser/ParseFlags.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ enum
4747
// let/const in global scope instead of eval scope so that they can be preserved across console inputs
4848
fscrNoAsmJs = 1 << 25, // Disable generation of asm.js code
4949
fscrIsModuleCode = 1 << 26, // Current code should be parsed as a module body
50-
fscrAll = (1 << 27) - 1
51-
};
5250

51+
fscrDeferredFncIsMethod = 1 << 27,
52+
fscrAll = (1 << 28) - 1
53+
};

lib/Runtime/Base/FunctionBody.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1583,7 +1583,8 @@ namespace Js
15831583
paramScopeSlotArraySize(0),
15841584
m_reparsed(false),
15851585
m_isAsmJsFunction(false),
1586-
m_tag21(true)
1586+
m_tag21(true),
1587+
m_isMethod(false)
15871588
#if DBG
15881589
,m_wasEverAsmjsMode(false)
15891590
,scopeObjectSize(0)
@@ -1633,6 +1634,7 @@ namespace Js
16331634
m_isStaticNameFunction(proxy->GetIsStaticNameFunction()),
16341635
m_reportedInParamCount(proxy->GetReportedInParamsCount()),
16351636
m_reparsed(proxy->IsReparsed()),
1637+
m_isMethod(proxy->IsMethod()),
16361638
m_tag21(true)
16371639
#if DBG
16381640
, m_wasEverAsmjsMode(proxy->m_wasEverAsmjsMode)
@@ -2319,6 +2321,16 @@ namespace Js
23192321
// (not a function declaration statement).
23202322
grfscr |= fscrDeferredFncExpression;
23212323
}
2324+
2325+
if (funcBody->IsMethod())
2326+
{
2327+
grfscr |= fscrDeferredFncIsMethod;
2328+
}
2329+
else
2330+
{
2331+
grfscr &= ~fscrDeferredFncIsMethod;
2332+
}
2333+
23222334
if (!CONFIG_FLAG(DeferNested) || isDebugOrAsmJsReparse)
23232335
{
23242336
grfscr &= ~fscrDeferFncParse; // Disable deferred parsing if not DeferNested, or doing a debug/asm.js re-parse

lib/Runtime/Base/FunctionBody.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1843,6 +1843,8 @@ namespace Js
18431843
LPCUTF8 GetStartOfDocument(const char16* reason = nullptr) const;
18441844
bool IsReparsed() const { return m_reparsed; }
18451845
void SetReparsed(bool set) { m_reparsed = set; }
1846+
bool IsMethod() const { return m_isMethod; }
1847+
void SetIsMethod(bool set) { m_isMethod = set; }
18461848
#ifdef NTBUILD
18471849
bool GetExternalDisplaySourceName(BSTR* sourceName);
18481850
#endif
@@ -1979,12 +1981,11 @@ namespace Js
19791981
FieldWithBarrier(bool) m_isEval : 1; // Source code is in 'eval'
19801982
FieldWithBarrier(bool) m_isDynamicFunction : 1; // Source code is in 'Function'
19811983
FieldWithBarrier(bool) m_hasImplicitArgIns : 1;
1982-
FieldWithBarrier(bool) m_dontInline : 1; // Used by the JIT's inliner
1983-
1984-
// Indicates if the function has been reparsed for debug attach/detach scenario.
1985-
FieldWithBarrier(bool) m_reparsed : 1;
1984+
FieldWithBarrier(bool) m_dontInline : 1; // Used by the JIT's inliner
1985+
FieldWithBarrier(bool) m_reparsed : 1; // Indicates if the function has been reparsed for debug attach/detach scenario.
1986+
FieldWithBarrier(bool) m_isMethod : 1; // Function is an object literal method
19861987
#if DBG
1987-
FieldWithBarrier(bool) m_wasEverAsmjsMode : 1; // has m_isAsmjsMode ever been true
1988+
FieldWithBarrier(bool) m_wasEverAsmjsMode : 1; // has m_isAsmjsMode ever been true
19881989
#endif
19891990

19901991
// This field is not required for deferred parsing but because our thunks can't handle offsets > 128 bytes

lib/Runtime/ByteCode/ByteCodeGenerator.cpp

Lines changed: 1 addition & 0 deletions
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)