Skip to content

Commit b2c0518

Browse files
committed
[MERGE #6253 @boingoing] Enable defer parse for class members including constructors
Merge pull request #6253 from boingoing:defer_parse_class_members Enable defer parse for class members including constructors We have the ability to defer parse any function except for class members (which includes explicit class constructors). Class members are basically parsed in the same way as object literal methods so it isn't very hard to support defer parsing for them since we already support defer parsing for object literal methods. The main complication here is supporting the unusual text extents for class constructors. The class constructor function itself is what we will eventually bind to the name of the class. However, calling toString on the class name should print the text of the entire class. We also need to know the exact text extents for the constructor method in order to defer parse it. There's already a mechanism we use to adjust the beginning of the text extents for async methods so I've extended this to support adjusting the length of the extents as well. We used to keep track of an extra uint tacked-on to FunctionBody but I moved this into a struct of two uints stored in the AuxPtr array instead. This struct is only allocated for objects with unusual text extents. Besides the text extents work, the remainder is mostly just bookkeeping various flags to let us know we're parsing a class constructor, derived constructor, class member, etc.
2 parents 8fcb0f1 + fda2217 commit b2c0518

20 files changed

+23678
-23405
lines changed

lib/Parser/Parse.cpp

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,6 +1134,7 @@ ParseNodeProg * Parser::CreateProgNode(bool isModuleSource, ULONG lineNumber)
11341134

11351135
pnodeProg->cbMin = this->GetScanner()->IecpMinTok();
11361136
pnodeProg->cbStringMin = pnodeProg->cbMin;
1137+
pnodeProg->cbStringLim = pnodeProg->cbLim;
11371138
pnodeProg->lineNumber = lineNumber;
11381139
pnodeProg->homeObjLocation = Js::Constants::NoRegister;
11391140
pnodeProg->superRestrictionState = SuperRestrictionState::Disallowed;
@@ -3678,6 +3679,7 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
36783679
if (isAsyncExpr)
36793680
{
36803681
pnode->AsParseNodeFnc()->cbStringMin = iecpMin;
3682+
pnode->AsParseNodeFnc()->cbStringLim = pnode->AsParseNodeFnc()->cbLim;
36813683
}
36823684
fCanAssign = FALSE;
36833685
break;
@@ -4687,6 +4689,7 @@ ParseNodeBin * Parser::ParseMemberGetSet(OpCode nop, LPCOLESTR* ppNameHint, size
46874689
/*needsPIDOnRCurlyScan*/ false);
46884690

46894691
pnodeFnc->cbStringMin = iecpMin;
4692+
pnodeFnc->cbStringLim = pnodeFnc->cbLim;
46904693

46914694
if (isComputedName)
46924695
{
@@ -5031,13 +5034,14 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
50315034
if (isAsyncMethod || isGenerator)
50325035
{
50335036
pnodeFnc->cbStringMin = iecpMin;
5037+
pnodeFnc->cbStringLim = pnodeFnc->cbLim;
50345038
}
50355039

50365040
if (isComputedName)
50375041
{
50385042
pnodeFnc->SetHasComputedName();
50395043
pnodeFnc->cbStringMin = iecpMin;
5040-
5044+
pnodeFnc->cbStringLim = pnodeFnc->cbLim;
50415045
}
50425046
pnodeFnc->SetHasHomeObj();
50435047

@@ -5354,6 +5358,7 @@ ParseNodeFnc * Parser::ParseFncDeclInternal(ushort flags, LPCOLESTR pNameHint, c
53545358
pnodeFnc->nestedFuncEscapes = false;
53555359
pnodeFnc->cbMin = this->GetScanner()->IecpMinTok();
53565360
pnodeFnc->cbStringMin = pnodeFnc->cbMin;
5361+
pnodeFnc->cbStringLim = pnodeFnc->cbLim;
53575362
pnodeFnc->functionId = (*m_nextFunctionId)++;
53585363
pnodeFnc->superRestrictionState = superRestrictionState;
53595364

@@ -5390,6 +5395,7 @@ ParseNodeFnc * Parser::ParseFncDeclInternal(ushort flags, LPCOLESTR pNameHint, c
53905395
pnodeFnc->SetIsClassConstructor((flags & fFncClassConstructor) != 0);
53915396
pnodeFnc->SetIsBaseClassConstructor((flags & fFncBaseClassConstructor) != 0);
53925397
pnodeFnc->SetHomeObjLocation(Js::Constants::NoRegister);
5398+
pnodeFnc->SetHasNonThisStmt(pnodeFnc->IsClassConstructor());
53935399

53945400
if (this->m_currentScope && this->m_currentScope->GetScopeType() == ScopeType_Parameter)
53955401
{
@@ -5677,15 +5683,6 @@ void Parser::ParseFncDeclHelper(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, us
56775683

56785684
uint uCanDeferSave = m_grfscr & fscrCanDeferFncParse;
56795685
uint uDeferSave = m_grfscr & fscrWillDeferFncParse;
5680-
if (flags & fFncClassMember)
5681-
{
5682-
// Disable deferral on class members or other construct with unusual text bounds
5683-
// as these are usually trivial, and re-parsing is problematic.
5684-
// NOTE: It is probably worth supporting these cases for memory and load-time purposes,
5685-
// especially as they become more and more common.
5686-
m_grfscr &= ~(fscrCanDeferFncParse | fscrWillDeferFncParse);
5687-
}
5688-
56895686
bool isTopLevelDeferredFunc = false;
56905687

56915688
#if ENABLE_BACKGROUND_PARSING
@@ -7176,6 +7173,7 @@ ParseNodeFnc * Parser::GenerateEmptyConstructor(bool extends)
71767173
pnodeFnc->cbLim = this->GetScanner()->IecpLimTok();
71777174
pnodeFnc->cbMin = this->GetScanner()->IecpMinTok();
71787175
pnodeFnc->cbStringMin = pnodeFnc->cbMin;
7176+
pnodeFnc->cbStringLim = pnodeFnc->cbLim;
71797177
pnodeFnc->lineNumber = this->GetScanner()->LineCur();
71807178

71817179
pnodeFnc->functionId = (*m_nextFunctionId);
@@ -8143,15 +8141,11 @@ ParseNodeClass * Parser::ParseClassDecl(BOOL isDeclaration, LPCOLESTR pNameHint,
81438141
fncDeclFlags |= fFncAsync;
81448142
}
81458143
pnodeFnc = ParseFncDeclNoCheckScope<buildAST>(fncDeclFlags, SuperRestrictionState::PropertyAllowed, pidHint ? pidHint->Psz() : nullptr, /* needsPIDOnRCurlyScan */ true);
8146-
if (isAsyncMethod)
8147-
{
8148-
pnodeFnc->cbMin = iecpMin;
8149-
pnodeFnc->ichMin = ichMin;
8150-
}
81518144

81528145
if (isAsyncMethod || isGenerator || isComputedName)
81538146
{
81548147
pnodeFnc->cbStringMin = iecpMin;
8148+
pnodeFnc->cbStringLim = pnodeFnc->cbLim;
81558149
}
81568150
}
81578151
pnodeFnc->SetIsStaticMember(isStatic);
@@ -8221,11 +8215,8 @@ ParseNodeClass * Parser::ParseClassDecl(BOOL isDeclaration, LPCOLESTR pNameHint,
82218215

82228216
if (buildAST)
82238217
{
8224-
pnodeConstructor->cbMin = cbMinConstructor;
82258218
pnodeConstructor->cbStringMin = cbMinConstructor;
8226-
pnodeConstructor->cbLim = cbLimConstructor;
8227-
pnodeConstructor->ichMin = pnodeClass->ichMin;
8228-
pnodeConstructor->ichLim = pnodeClass->ichLim;
8219+
pnodeConstructor->cbStringLim = cbLimConstructor;
82298220

82308221
PopFuncBlockScope(ppnodeScopeSave, ppnodeExprScopeSave);
82318222

@@ -9275,6 +9266,7 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
92759266
if (isAsyncMethod)
92769267
{
92779268
pnode->AsParseNodeFnc()->cbStringMin = iecpMin;
9269+
pnode->AsParseNodeFnc()->cbStringLim = pnode->AsParseNodeFnc()->cbLim;
92789270
}
92799271

92809272
// ArrowFunction/AsyncArrowFunction is part of AssignmentExpression, which should terminate the expression unless followed by a comma
@@ -10204,6 +10196,7 @@ ParseNodePtr Parser::ParseStatement()
1020410196
if (isAsyncMethod)
1020510197
{
1020610198
pnode->AsParseNodeFnc()->cbStringMin = iecpMin;
10199+
pnode->AsParseNodeFnc()->cbStringLim = pnode->AsParseNodeFnc()->cbLim;
1020710200
}
1020810201
break;
1020910202
}
@@ -11957,6 +11950,7 @@ ParseNodeProg * Parser::Parse(LPCUTF8 pszSrc, size_t offset, size_t length, char
1195711950

1195811951
m_currentNodeFunc->SetIsGenerator(scopeInfo->IsGeneratorFunctionBody());
1195911952
m_currentNodeFunc->SetIsAsync(scopeInfo->IsAsyncFunctionBody());
11953+
m_currentNodeFunc->SetIsClassConstructor(scopeInfo->IsClassConstructor());
1196011954
}
1196111955
}
1196211956

@@ -12021,6 +12015,23 @@ ParseNodeProg * Parser::Parse(LPCUTF8 pszSrc, size_t offset, size_t length, char
1202112015
flags |= fFncAsync;
1202212016
}
1202312017

12018+
if (m_grfscr & fscrDeferredFncIsClassConstructor)
12019+
{
12020+
m_grfscr &= ~fscrDeferredFncIsClassConstructor;
12021+
flags |= fFncClassConstructor | fFncClassMember;
12022+
}
12023+
12024+
if (m_grfscr & fscrDeferredFncIsBaseClassConstructor)
12025+
{
12026+
m_grfscr &= ~fscrDeferredFncIsBaseClassConstructor;
12027+
flags |= fFncBaseClassConstructor;
12028+
}
12029+
12030+
if (m_grfscr & fscrDeferredFncIsClassMember)
12031+
{
12032+
m_grfscr &= ~fscrDeferredFncIsClassMember;
12033+
flags |= fFncClassMember;
12034+
}
1202412035

1202512036
#if DBG
1202612037
if (isMethod && m_token.tk == tkID)

lib/Parser/ParseFlags.h

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ enum
1414
fscrWillDeferFncParse = 1 << 3, // Heuristically choosing to defer parsing of functions
1515
fscrCanDeferFncParse = 1 << 4, // Functionally able to defer parsing of functions
1616
fscrDynamicCode = 1 << 5, // The code is being generated dynamically (eval, new Function, etc.)
17-
fscrDeferredFncIsGenerator = 1 << 6,
17+
fscrUseStrictMode = 1 << 6,
1818
fscrNoImplicitHandlers = 1 << 7, // same as Opt NoConnect at start of block
1919
fscrCreateParserState = 1 << 8, // The parser should expose parser state information on the parse nodes.
2020
// This parser state includes the set of names which are captured by each function
@@ -28,29 +28,31 @@ enum
2828
fscrEval = 1 << 10, // this expression has eval semantics (i.e., run in caller's context
2929
fscrEvalCode = 1 << 11, // this is an eval expression
3030
fscrGlobalCode = 1 << 12, // this is a global script
31-
fscrDeferredFncIsAsync = 1 << 13,
32-
fscrDeferredFncExpression = 1 << 14, // the function decl node we deferred is an expression,
33-
// i.e., not a declaration statement
34-
fscrDeferredFnc = 1 << 15, // the function we are parsing is deferred
35-
fscrNoPreJit = 1 << 16, // ignore prejit global flag
36-
fscrAllowFunctionProxy = 1 << 17, // Allow creation of function proxies instead of function bodies
37-
fscrIsLibraryCode = 1 << 18, // Current code is engine library code written in Javascript
38-
fscrNoDeferParse = 1 << 19, // Do not defer parsing
39-
fscrJsBuiltIn = 1 << 20, // Current code is a JS built in code written in JavaScript
31+
fscrIsModuleCode = 1 << 13, // Current code should be parsed as a module body
32+
fscrNoAsmJs = 1 << 14, // Disable generation of asm.js code
33+
fscrNoPreJit = 1 << 15, // ignore prejit global flag
34+
fscrAllowFunctionProxy = 1 << 16, // Allow creation of function proxies instead of function bodies
35+
fscrIsLibraryCode = 1 << 17, // Current code is engine library code written in Javascript
36+
fscrNoDeferParse = 1 << 18, // Do not defer parsing
37+
fscrJsBuiltIn = 1 << 19, // Current code is a JS built in code written in JavaScript
4038
#ifdef IR_VIEWER
41-
fscrIrDumpEnable = 1 << 21, // Allow parseIR to generate an IR dump
39+
fscrIrDumpEnable = 1 << 20, // Allow parseIR to generate an IR dump
4240
#endif /* IRVIEWER */
4341

4442
// Throw a ReferenceError when the global 'this' is used (possibly in a lambda),
4543
// for debugger when broken in a lambda that doesn't capture 'this'
46-
fscrDebuggerErrorOnGlobalThis = 1 << 22,
47-
fscrDeferredClassMemberFnc = 1 << 23,
48-
fscrConsoleScopeEval = 1 << 24, // The eval string is console eval or debugEval, used to have top level
44+
fscrDebuggerErrorOnGlobalThis = 1 << 21,
45+
fscrConsoleScopeEval = 1 << 22, // The eval string is console eval or debugEval, used to have top level
4946
// let/const in global scope instead of eval scope so that they can be preserved across console inputs
50-
fscrNoAsmJs = 1 << 25, // Disable generation of asm.js code
51-
fscrIsModuleCode = 1 << 26, // Current code should be parsed as a module body
5247

53-
fscrDeferredFncIsMethod = 1 << 27,
54-
fscrUseStrictMode = 1 << 28,
48+
fscrDeferredFnc = 1 << 23, // the function we are parsing is deferred
49+
fscrDeferredFncExpression = 1 << 24, // the function decl node we deferred is an expression,
50+
// i.e., not a declaration statement
51+
fscrDeferredFncIsAsync = 1 << 25,
52+
fscrDeferredFncIsMethod = 1 << 26,
53+
fscrDeferredFncIsGenerator = 1 << 27,
54+
fscrDeferredFncIsClassMember = 1 << 28,
55+
fscrDeferredFncIsClassConstructor = 1 << 29,
56+
fscrDeferredFncIsBaseClassConstructor = 1 << 30,
5557
fscrAll = (1 << 29) - 1
5658
};

lib/Parser/ptree.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ class ParseNodeFnc : public ParseNode
521521
int32 astSize;
522522
size_t cbMin; // Min an Lim UTF8 offsets.
523523
size_t cbStringMin;
524+
size_t cbStringLim;
524525
size_t cbLim;
525526
ULONG lineNumber; // Line number relative to the current source buffer of the function declaration.
526527
ULONG columnNumber; // Column number of the declaration.

lib/Runtime/Base/FunctionBody.cpp

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,12 @@ namespace Js
218218
uint
219219
ParseableFunctionInfo::PrintableStartOffset() const
220220
{
221-
return this->m_cbStartPrintOffset;
221+
PrintOffsets* printOffsets = this->GetPrintOffsets();
222+
if (printOffsets != nullptr)
223+
{
224+
return printOffsets->cbStartPrintOffset;
225+
}
226+
return this->m_cbStartOffset;
222227
}
223228

224229
void ParseableFunctionInfo::RegisterFuncToDiag(ScriptContext * scriptContext, char16 const * pszTitle)
@@ -1526,6 +1531,7 @@ namespace Js
15261531
CopyDeferParseField(m_grfscr);
15271532
other->SetScopeInfo(this->GetScopeInfo());
15281533
other->SetDeferredStubs(this->GetDeferredStubs());
1534+
other->SetPrintOffsets(this->GetPrintOffsets());
15291535
CopyDeferParseField(m_utf8SourceHasBeenSet);
15301536
#if DBG
15311537
CopyDeferParseField(deferredParseNextFunctionId);
@@ -1549,7 +1555,6 @@ namespace Js
15491555
CopyDeferParseField(m_lineNumber);
15501556
CopyDeferParseField(m_columnNumber);
15511557
CopyDeferParseField(m_cbStartOffset);
1552-
CopyDeferParseField(m_cbStartPrintOffset);
15531558
CopyDeferParseField(m_cbLength);
15541559

15551560
this->CopyNestedArray(other);
@@ -1653,7 +1658,6 @@ namespace Js
16531658
m_cbLength(0),
16541659
m_cchStartOffset(0),
16551660
m_cbStartOffset(0),
1656-
m_cbStartPrintOffset(0),
16571661
m_lineNumber(0),
16581662
m_columnNumber(0),
16591663
m_isEval(false),
@@ -2460,6 +2464,33 @@ namespace Js
24602464
grfscr &= ~fscrDeferredFncIsGenerator;
24612465
}
24622466

2467+
if (funcBody->IsClassConstructor())
2468+
{
2469+
grfscr |= fscrDeferredFncIsClassConstructor;
2470+
}
2471+
else
2472+
{
2473+
grfscr &= ~fscrDeferredFncIsClassConstructor;
2474+
}
2475+
2476+
if (funcBody->IsBaseClassConstructor())
2477+
{
2478+
grfscr |= fscrDeferredFncIsBaseClassConstructor;
2479+
}
2480+
else
2481+
{
2482+
grfscr &= ~fscrDeferredFncIsBaseClassConstructor;
2483+
}
2484+
2485+
if (funcBody->IsClassMethod())
2486+
{
2487+
grfscr |= fscrDeferredFncIsClassMember;
2488+
}
2489+
else
2490+
{
2491+
grfscr &= ~fscrDeferredFncIsClassMember;
2492+
}
2493+
24632494
if (isDebugOrAsmJsReparse)
24642495
{
24652496
// Disable deferred parsing if not DeferNested, or doing a debug/asm.js re-parse
@@ -2894,9 +2925,16 @@ namespace Js
28942925
}
28952926
Assert(node->cbStringMin <= node->cbMin);
28962927
this->m_cbStartOffset = (uint)cbMin;
2897-
this->m_cbStartPrintOffset = (uint)node->cbStringMin;
28982928
this->m_cbLength = (uint)lengthInBytes;
28992929

2930+
if (node->cbStringMin != node->cbMin)
2931+
{
2932+
PrintOffsets* printOffsets = RecyclerNewLeaf(this->m_scriptContext->GetRecycler(), PrintOffsets);
2933+
printOffsets->cbStartPrintOffset = (uint)node->cbStringMin;
2934+
printOffsets->cbEndPrintOffset = (uint)node->cbStringLim;
2935+
this->SetPrintOffsets(printOffsets);
2936+
}
2937+
29002938
Assert(this->m_utf8SourceInfo != nullptr);
29012939
this->m_utf8SourceHasBeenSet = true;
29022940

@@ -2952,7 +2990,6 @@ namespace Js
29522990
this->m_columnNumber = 0;
29532991

29542992
this->m_cbStartOffset = 0;
2955-
this->m_cbStartPrintOffset = 0;
29562993
this->m_cbLength = 0;
29572994

29582995
this->m_utf8SourceHasBeenSet = true;
@@ -4598,7 +4635,6 @@ namespace Js
45984635
Output::Print(_u("\n\n Line %3d: "), line + 1);
45994636
// Need to match up cchStartOffset to appropriate cbStartOffset given function's cbStartOffset and cchStartOffset
46004637
size_t utf8SrcStartIdx = utf8::CharacterIndexToByteIndex(source, sourceInfo->GetCbLength(), cchStartOffset, this->m_cbStartOffset, this->m_cchStartOffset);
4601-
46024638
size_t utf8SrcEndIdx = StartOffset() + LengthInBytes();
46034639
char16* utf16Buf = HeapNewArray(char16, utf8SrcEndIdx - utf8SrcStartIdx + 2);
46044640
size_t utf16BufSz = utf8::DecodeUnitsIntoAndNullTerminateNoAdvance(utf16Buf, source + utf8SrcStartIdx, source + utf8SrcEndIdx, utf8::DecodeOptions::doDefault);

lib/Runtime/Base/FunctionBody.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,12 @@ namespace Js
854854
typedef Field(FunctionInfo*)* FunctionInfoArray;
855855
typedef Field(FunctionInfo*)* FunctionInfoPtrPtr;
856856

857+
struct PrintOffsets
858+
{
859+
uint cbStartPrintOffset;
860+
uint cbEndPrintOffset;
861+
};
862+
857863
//
858864
// FunctionProxy represents a user defined function
859865
// This could be either from a source file or the byte code cache
@@ -905,7 +911,7 @@ namespace Js
905911
CodeGenCallApplyTargetRuntimeData = 26,
906912
CallSiteToCallApplyCallSiteArray = 27,
907913
#endif
908-
914+
PrintOffsets = 28,
909915
Max,
910916
Invalid = 0xff
911917
};
@@ -954,6 +960,7 @@ namespace Js
954960
AuxPointerTypeEntry(AuxPointerType::CodeGenCallApplyTargetRuntimeData, Field(FunctionCodeGenRuntimeData*)*);
955961
AuxPointerTypeEntry(AuxPointerType::CallSiteToCallApplyCallSiteArray, ProfileId*);
956962
#endif
963+
AuxPointerTypeEntry(AuxPointerType::PrintOffsets, PrintOffsets*);
957964
#undef AuxPointerTypeEntry
958965

959966
typedef AuxPtrs<FunctionProxy, AuxPointerType> AuxPtrsT;
@@ -1772,6 +1779,8 @@ namespace Js
17721779
void BuildDeferredStubs(ParseNodeFnc* pnodeFnc);
17731780
DeferredFunctionStub *GetDeferredStubs() const { return this->GetAuxPtr<AuxPointerType::DeferredStubs>(); }
17741781
void SetDeferredStubs(DeferredFunctionStub *stub) { this->SetAuxPtr<AuxPointerType::DeferredStubs>(stub); }
1782+
PrintOffsets* GetPrintOffsets() const { return this->GetAuxPtr<AuxPointerType::PrintOffsets>(); }
1783+
void SetPrintOffsets(PrintOffsets* offsets) { this->SetAuxPtr<AuxPointerType::PrintOffsets>(offsets); }
17751784
void RegisterFuncToDiag(ScriptContext * scriptContext, char16 const * pszTitle);
17761785
bool IsES6ModuleCode() const;
17771786
private:
@@ -1842,7 +1851,6 @@ namespace Js
18421851

18431852
FieldWithBarrier(uint) m_cbStartOffset; // pUtf8Source is this many bytes from the start of the scriptContext's source buffer.
18441853
// This is generally the same as m_cchStartOffset unless the buffer has a BOM or other non-ascii characters
1845-
FieldWithBarrier(uint) m_cbStartPrintOffset; // pUtf8Source is this many bytes from the start of the toString-relevant part of the scriptContext's source buffer.
18461854

18471855
FieldWithBarrier(ULONG) m_lineNumber;
18481856
FieldWithBarrier(ULONG) m_columnNumber;

0 commit comments

Comments
 (0)