Skip to content

Commit 0bc7709

Browse files
committed
[MERGE #4105 @boingoing] Support for defer parsing lambda functions
Merge pull request #4105 from boingoing:DeferParseLambda_3 Fix up the parser to enable the defer parse methods to handle lambda functions. All lambdas are allowed to be deferred, even lambdas with compact parameter lists or function bodies. Should also allow lambda functions to be redeferred, which is really the main reason to do this work. One of the related changes is in `Parser::Parse`. I changed this function to skip calling `ParseStmtList` in the case where we are defer parsing a single function. In these cases we know that we are going to parse a function so we don't need to start parsing a set of statements, then one statement, then one expression, then one terminal, etc until we finally start parsing the function via `ParseFncDecl`. Instead we can just jump directly to `ParseFncDecl` after setting up the correct flags. By doing this, we can avoid reparsing the lambda parameter list (because we never assume it will be an ordinary expression list) which saves us the headache of bookkeeping the block and function ids. One other change of note is in `ScopeInfo::SaveSymbolInfo`. We now need to save symbol info for the `arguments` symbol since a lambda may be deferred in a function and lambdas capture the `arguments` value from their parent. I suppose we could move the `arguments` default binding into a regular special symbol at some point in the future.
2 parents 8dbf8e4 + f47ec32 commit 0bc7709

16 files changed

+11666
-11409
lines changed

lib/Parser/Parse.cpp

Lines changed: 213 additions & 92 deletions
Large diffs are not rendered by default.

lib/Parser/Parse.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -809,8 +809,8 @@ class Parser
809809
template<bool buildAST> void ParseExpressionLambdaBody(ParseNodePtr pnodeFnc);
810810
template<bool buildAST> void UpdateCurrentNodeFunc(ParseNodePtr pnodeFnc, bool fLambda);
811811
bool FncDeclAllowedWithoutContext(ushort flags);
812-
void FinishFncDecl(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, ParseNodePtr *lastNodeRef, bool skipCurlyBraces = false);
813-
void ParseTopLevelDeferredFunc(ParseNodePtr pnodeFnc, ParseNodePtr pnodeFncParent, LPCOLESTR pNameHint);
812+
void FinishFncDecl(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, ParseNodePtr *lastNodeRef, bool fLambda, bool skipCurlyBraces = false);
813+
void ParseTopLevelDeferredFunc(ParseNodePtr pnodeFnc, ParseNodePtr pnodeFncParent, LPCOLESTR pNameHint, bool fLambda, bool *pNeedScanRCurly = nullptr);
814814
void ParseNestedDeferredFunc(ParseNodePtr pnodeFnc, bool fLambda, bool *pNeedScanRCurly, bool *pStrictModeTurnedOn);
815815
void CheckStrictFormalParameters();
816816
ParseNodePtr AddArgumentsNodeToVars(ParseNodePtr pnodeFnc);

lib/Parser/Scan.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ Scanner<EncodingPolicy>::Scanner(Parser* parser, HashTbl *phtbl, Token *ptoken,
9999
m_tempChBufSecondary.m_pscanner = this;
100100

101101
m_iecpLimTokPrevious = (size_t)-1;
102+
m_ichLimTokPrevious = (charcount_t)-1;
102103

103104
this->charClassifier = scriptContext->GetCharClassifier();
104105

@@ -1634,6 +1635,7 @@ tokens Scanner<EncodingPolicy>::ScanCore(bool identifyKwds)
16341635
// store the last token
16351636
m_tkPrevious = m_ptoken->tk;
16361637
m_iecpLimTokPrevious = IecpLimTok(); // Introduced for use by lambda parsing to find correct span of expression lambdas
1638+
m_ichLimTokPrevious = IchLimTok();
16371639

16381640
if (p >= last)
16391641
{

lib/Parser/Scan.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,12 @@ class Scanner : public IScanner, public EncodingPolicy
531531
return m_iecpLimTokPrevious;
532532
}
533533

534+
charcount_t IchLimTokPrevious() const
535+
{
536+
AssertMsg(m_ichLimTokPrevious != (charcount_t)-1, "IchLimTokPrevious() cannot be called before scanning a token");
537+
return m_ichLimTokPrevious;
538+
}
539+
534540
IdentPtr PidAt(size_t iecpMin, size_t iecpLim);
535541

536542
// Returns the character offset within the stream of the first character on the current line.
@@ -549,6 +555,7 @@ class Scanner : public IScanner, public EncodingPolicy
549555
void SetCurrentCharacter(charcount_t offset, ULONG lineNumber = 0)
550556
{
551557
DebugOnly(m_iecpLimTokPrevious = (size_t)-1);
558+
DebugOnly(m_ichLimTokPrevious = (charcount_t)-1);
552559
size_t length = m_pchLast - m_pchBase;
553560
if (offset > length) offset = static_cast< charcount_t >(length);
554561
size_t ibOffset = this->CharacterOffsetToUnitOffset(m_pchBase, m_currentCharacter, m_pchLast, offset);
@@ -713,6 +720,7 @@ class Scanner : public IScanner, public EncodingPolicy
713720

714721
tokens m_tkPrevious;
715722
size_t m_iecpLimTokPrevious;
723+
charcount_t m_ichLimTokPrevious;
716724

717725
Scanner(Parser* parser, HashTbl *phtbl, Token *ptoken, Js::ScriptContext *scriptContext);
718726
~Scanner(void);

lib/Runtime/Base/FunctionBody.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,6 +1416,7 @@ namespace Js
14161416
bool IsConstructor() const;
14171417
bool IsGenerator() const;
14181418
bool IsClassConstructor() const;
1419+
bool IsBaseClassConstructor() const;
14191420
bool IsClassMethod() const;
14201421
bool IsModule() const;
14211422
bool IsWasmFunction() const;
@@ -1722,6 +1723,13 @@ namespace Js
17221723
return GetFunctionInfo()->IsClassConstructor();
17231724
}
17241725

1726+
inline bool FunctionProxy::IsBaseClassConstructor() const
1727+
{
1728+
Assert(GetFunctionInfo());
1729+
Assert(GetFunctionInfo()->GetFunctionProxy() == this);
1730+
return GetFunctionInfo()->GetBaseConstructorKind();
1731+
}
1732+
17251733
inline bool FunctionProxy::IsClassMethod() const
17261734
{
17271735
Assert(GetFunctionInfo());

lib/Runtime/ByteCode/ByteCodeGenerator.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,11 @@ FuncInfo * ByteCodeGenerator::StartBindFunction(const char16 *name, uint nameLen
13081308
if (pnode->sxFnc.IsClassConstructor())
13091309
{
13101310
attributes = (Js::FunctionInfo::Attributes)(attributes | Js::FunctionInfo::Attributes::ClassConstructor);
1311+
1312+
if (pnode->sxFnc.IsBaseClassConstructor())
1313+
{
1314+
attributes = (Js::FunctionInfo::Attributes)(attributes | Js::FunctionInfo::Attributes::BaseConstructorKind);
1315+
}
13111316
}
13121317
else
13131318
{

lib/Runtime/ByteCode/ByteCodeSerializer.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ enum FunctionFlags
217217
ffIsAnonymous = 0x100000,
218218
ffUsesArgumentsObject = 0x200000,
219219
ffDoScopeObjectCreation = 0x400000,
220-
ffIsParamAndBodyScopeMerged = 0x800000
220+
ffIsParamAndBodyScopeMerged = 0x800000,
221221
};
222222

223223
// Kinds of constant
@@ -2043,10 +2043,11 @@ class ByteCodeBufferBuilder
20432043
| FunctionInfo::Attributes::CapturesThis
20442044
| FunctionInfo::Attributes::Generator
20452045
| FunctionInfo::Attributes::ClassConstructor
2046+
| FunctionInfo::Attributes::BaseConstructorKind
20462047
| FunctionInfo::Attributes::ClassMethod
20472048
| FunctionInfo::Attributes::EnclosedByGlobalFunc
20482049
| FunctionInfo::Attributes::AllowDirectSuper)) == 0,
2049-
"Only the ErrorOnNew|SuperReference|Lambda|CapturesThis|Generator|ClassConstructor|Async|ClassMember|EnclosedByGlobalFunc|AllowDirectSuper attributes should be set on a serialized function");
2050+
"Only the ErrorOnNew|SuperReference|Lambda|CapturesThis|Generator|ClassConstructor|BaseConstructorKind|Async|ClassMember|EnclosedByGlobalFunc|AllowDirectSuper attributes should be set on a serialized function");
20502051

20512052
PrependInt32(builder, _u("Offset Into Source"), sourceDiff);
20522053
if (function->GetNestedCount() > 0)

lib/Runtime/ByteCode/FuncInfo.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,22 +136,22 @@ BOOL FuncInfo::HasDirectSuper() const
136136

137137
BOOL FuncInfo::IsClassMember() const
138138
{
139-
return root->sxFnc.IsClassMember();
139+
return this->byteCodeFunction->IsClassMethod();
140140
}
141141

142142
BOOL FuncInfo::IsLambda() const
143143
{
144-
return root->sxFnc.IsLambda();
144+
return this->byteCodeFunction->IsLambda();
145145
}
146146

147147
BOOL FuncInfo::IsClassConstructor() const
148148
{
149-
return root->sxFnc.IsClassConstructor();
149+
return this->byteCodeFunction->IsClassConstructor();
150150
}
151151

152152
BOOL FuncInfo::IsBaseClassConstructor() const
153153
{
154-
return root->sxFnc.IsBaseClassConstructor();
154+
return this->byteCodeFunction->IsBaseClassConstructor();
155155
}
156156

157157
BOOL FuncInfo::IsDerivedClassConstructor() const

lib/Runtime/ByteCode/ScopeInfo.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ namespace Js
1111
//
1212
void ScopeInfo::SaveSymbolInfo(Symbol* sym, MapSymbolData* mapSymbolData)
1313
{
14-
// We don't need to create slot for or save "arguments"
15-
bool needScopeSlot = !sym->IsArguments() && sym->GetHasNonLocalReference();
14+
bool needScopeSlot = sym->GetHasNonLocalReference();
1615
Js::PropertyId scopeSlot = Constants::NoSlot;
1716

1817
if (sym->GetIsModuleExportStorage())

0 commit comments

Comments
 (0)