Skip to content

Commit 058118b

Browse files
committed
Changing Function.prototype.toString to report original source string
According to https://tc39.github.io/Function-prototype-toString-revision/ the result of toString on a function should be the source text which matches the grammar describing the kind of function. In practice this essentially means that toString should just be whatever the source code defining the function is. To support this, we need to slightly adjust the extent of a function in the case of member list declarations to ensure that it captures the start of the function, including any `async` or computed function name, etc.
1 parent 52562df commit 058118b

12 files changed

Lines changed: 146 additions & 205 deletions

lib/Parser/Parse.cpp

Lines changed: 63 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,7 @@ ParseNodeProg * Parser::CreateProgNode(bool isModuleSource, ULONG lineNumber)
984984
}
985985

986986
pnodeProg->cbMin = this->GetScanner()->IecpMinTok();
987+
pnodeProg->cbStringMin = pnodeProg->cbMin;
987988
pnodeProg->lineNumber = lineNumber;
988989
pnodeProg->homeObjLocation = Js::Constants::NoRegister;
989990
return pnodeProg;
@@ -3346,8 +3347,7 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
33463347
pnode = ParseFncDeclNoCheckScope<buildAST>(flags, pNameHint, false, true, fUnaryOrParen);
33473348
if (isAsyncExpr)
33483349
{
3349-
pnode->AsParseNodeFnc()->cbMin = iecpMin;
3350-
pnode->ichMin = ichMin;
3350+
pnode->AsParseNodeFnc()->cbStringMin = iecpMin;
33513351
}
33523352
fCanAssign = FALSE;
33533353
break;
@@ -4218,7 +4218,7 @@ template<bool buildAST> void Parser::ParseComputedName(ParseNodePtr* ppnodeName,
42184218
{ get foo(){ ... }, set bar(arg) { ... } }
42194219
***************************************************************************/
42204220
template<bool buildAST>
4221-
ParseNodeBin * Parser::ParseMemberGetSet(OpCode nop, LPCOLESTR* ppNameHint)
4221+
ParseNodeBin * Parser::ParseMemberGetSet(OpCode nop, LPCOLESTR* ppNameHint, size_t iecpMin, charcount_t ichMin)
42224222
{
42234223
ParseNodePtr pnodeName = nullptr;
42244224
Assert(nop == knopGetMember || nop == knopSetMember);
@@ -4316,15 +4316,17 @@ ParseNodeBin * Parser::ParseMemberGetSet(OpCode nop, LPCOLESTR* ppNameHint)
43164316
ParseNodeFnc * pnodeFnc = ParseFncDeclNoCheckScope<buildAST>(flags, *ppNameHint,
43174317
/*needsPIDOnRCurlyScan*/ false, /*resetParsingSuperRestrictionState*/ false);
43184318

4319+
pnodeFnc->cbStringMin = iecpMin;
4320+
43194321
if (isComputedName)
43204322
{
43214323
pnodeFnc->SetHasComputedName();
43224324
}
43234325
pnodeFnc->SetHasHomeObj();
4326+
pnodeFnc->SetIsAccessor();
43244327

43254328
if (buildAST)
43264329
{
4327-
pnodeFnc->SetIsAccessor();
43284330
return CreateBinNode(nop, pnodeName, pnodeFnc);
43294331
}
43304332
else
@@ -4372,8 +4374,8 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
43724374
}
43734375
#endif
43744376
bool isAsyncMethod = false;
4375-
charcount_t ichMin = 0;
4376-
size_t iecpMin = 0;
4377+
charcount_t ichMin = this->GetScanner()->IchMinTok();
4378+
size_t iecpMin = this->GetScanner()->IecpMinTok();
43774379
if (m_token.tk == tkID && m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.async && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
43784380
{
43794381
RestorePoint parsedAsync;
@@ -4659,13 +4661,14 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
46594661

46604662
if (isAsyncMethod || isGenerator)
46614663
{
4662-
pnodeFnc->cbMin = iecpMin;
4663-
pnodeFnc->ichMin = ichMin;
4664+
pnodeFnc->cbStringMin = iecpMin;
46644665
}
46654666

46664667
if (isComputedName)
46674668
{
46684669
pnodeFnc->SetHasComputedName();
4670+
pnodeFnc->cbStringMin = iecpMin;
4671+
46694672
}
46704673
pnodeFnc->SetHasHomeObj();
46714674

@@ -4698,7 +4701,7 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
46984701
LPCOLESTR pNameGetOrSet = nullptr;
46994702
OpCode op = pidHint == wellKnownPropertyPids.get ? knopGetMember : knopSetMember;
47004703

4701-
pnodeArg = ParseMemberGetSet<buildAST>(op, &pNameGetOrSet);
4704+
pnodeArg = ParseMemberGetSet<buildAST>(op, &pNameGetOrSet, iecpMin, ichMin);
47024705

47034706
if (CONFIG_FLAG(UseFullName) && buildAST && pnodeArg->pnode2->nop == knopFncDecl)
47044707
{
@@ -4980,6 +4983,7 @@ ParseNodeFnc * Parser::ParseFncDeclInternal(ushort flags, LPCOLESTR pNameHint, c
49804983

49814984
pnodeFnc->nestedFuncEscapes = false;
49824985
pnodeFnc->cbMin = this->GetScanner()->IecpMinTok();
4986+
pnodeFnc->cbStringMin = pnodeFnc->cbMin;
49834987
pnodeFnc->functionId = (*m_nextFunctionId)++;
49844988

49854989

@@ -5869,8 +5873,11 @@ void Parser::ParseTopLevelDeferredFunc(ParseNodeFnc * pnodeFnc, ParseNodeFnc * p
58695873

58705874
Assert(pnodeFnc->ichMin == stub->ichMin
58715875
|| (stub->fncFlags & kFunctionIsAsync) == kFunctionIsAsync
5872-
|| ((stub->fncFlags & kFunctionIsGenerator) == kFunctionIsGenerator && (stub->fncFlags & kFunctionIsMethod) == kFunctionIsMethod));
5873-
5876+
|| ((stub->fncFlags & kFunctionIsMethod) == kFunctionIsMethod && (
5877+
(stub->fncFlags & kFunctionIsAccessor) == kFunctionIsAccessor
5878+
|| (stub->fncFlags & kFunctionIsGenerator) == kFunctionIsGenerator
5879+
|| (stub->fncFlags & kFunctionHasComputedName) == kFunctionHasComputedName
5880+
)));
58745881
if (stub->fncFlags & kFunctionCallsEval)
58755882
{
58765883
this->MarkEvalCaller();
@@ -6777,6 +6784,7 @@ ParseNodeFnc * Parser::GenerateEmptyConstructor(bool extends)
67776784
pnodeFnc->ichMin = this->GetScanner()->IchMinTok();
67786785
pnodeFnc->cbLim = this->GetScanner()->IecpLimTok();
67796786
pnodeFnc->cbMin = this->GetScanner()->IecpMinTok();
6787+
pnodeFnc->cbStringMin = pnodeFnc->cbMin;
67806788
pnodeFnc->lineNumber = this->GetScanner()->LineCur();
67816789

67826790
pnodeFnc->functionId = (*m_nextFunctionId);
@@ -7579,8 +7587,8 @@ ParseNodeClass * Parser::ParseClassDecl(BOOL isDeclaration, LPCOLESTR pNameHint,
75797587
}
75807588

75817589
ushort fncDeclFlags = fFncNoName | fFncMethod | fFncClassMember;
7582-
charcount_t ichMin = 0;
7583-
size_t iecpMin = 0;
7590+
charcount_t ichMin = this->GetScanner()->IchMinTok();
7591+
size_t iecpMin = this->GetScanner()->IecpMinTok();
75847592
ParseNodePtr pnodeMemberName = nullptr;
75857593
IdentPtr pidHint = nullptr;
75867594
IdentPtr memberPid = nullptr;
@@ -7774,6 +7782,11 @@ ParseNodeClass * Parser::ParseClassDecl(BOOL isDeclaration, LPCOLESTR pNameHint,
77747782
pnodeFnc->cbMin = iecpMin;
77757783
pnodeFnc->ichMin = ichMin;
77767784
}
7785+
7786+
if (isAsyncMethod || isGenerator || isComputedName)
7787+
{
7788+
pnodeFnc->cbStringMin = iecpMin;
7789+
}
77777790
}
77787791
pnodeFnc->SetIsStaticMember(isStatic);
77797792
if (isComputedName)
@@ -7843,6 +7856,7 @@ ParseNodeClass * Parser::ParseClassDecl(BOOL isDeclaration, LPCOLESTR pNameHint,
78437856
if (buildAST)
78447857
{
78457858
pnodeConstructor->cbMin = cbMinConstructor;
7859+
pnodeConstructor->cbStringMin = cbMinConstructor;
78467860
pnodeConstructor->cbLim = cbLimConstructor;
78477861
pnodeConstructor->ichMin = pnodeClass->ichMin;
78487862
pnodeConstructor->ichLim = pnodeClass->ichLim;
@@ -8884,8 +8898,7 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
88848898
pnode = ParseFncDeclNoCheckScope<buildAST>(flags, nullptr, /* needsPIDOnRCurlyScan = */false, /* resetParsingSuperRestrictionState = */false, /* fUnaryOrParen = */ false, fAllowIn);
88858899
if (isAsyncMethod)
88868900
{
8887-
pnode->AsParseNodeFnc()->cbMin = iecpMin;
8888-
pnode->ichMin = ichMin;
8901+
pnode->AsParseNodeFnc()->cbStringMin = iecpMin;
88898902
}
88908903

88918904
// ArrowFunction/AsyncArrowFunction is part of AssignmentExpression, which should terminate the expression unless followed by a comma
@@ -9740,8 +9753,7 @@ ParseNodePtr Parser::ParseStatement()
97409753
}
97419754
if (isAsyncMethod)
97429755
{
9743-
pnode->AsParseNodeFnc()->cbMin = iecpMin;
9744-
pnode->ichMin = ichMin;
9756+
pnode->AsParseNodeFnc()->cbStringMin = iecpMin;
97459757
}
97469758
break;
97479759
}
@@ -11440,8 +11452,6 @@ ParseNodeProg * Parser::Parse(LPCUTF8 pszSrc, size_t offset, size_t length, char
1144011452
{
1144111453
// Defer parse for a single function should just parse that one function - there are no other statements.
1144211454
ushort flags = fFncNoFlgs;
11443-
size_t iecpMin = 0;
11444-
charcount_t ichMin = 0;
1144511455
bool isAsync = false;
1144611456
bool isGenerator = false;
1144711457
bool isMethod = false;
@@ -11468,55 +11478,52 @@ ParseNodeProg * Parser::Parse(LPCUTF8 pszSrc, size_t offset, size_t length, char
1146811478
m_grfscr &= ~fscrDeferredFncIsMethod;
1146911479
isMethod = true;
1147011480
flags |= fFncNoName | fFncMethod;
11471-
}
1147211481

11473-
// These are the cases which can confirm async function:
11474-
// async function() {} -> async function
11475-
// async () => {} -> async lambda with parens around the formal parameter
11476-
// async arg => {} -> async lambda with single identifier parameter
11477-
// async name() {} -> async method
11478-
// async [computed_name]() {} -> async method with a computed name
11479-
if (m_token.tk == tkID && m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.async && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
11480-
{
11481-
ichMin = this->GetScanner()->IchMinTok();
11482-
iecpMin = this->GetScanner()->IecpMinTok();
11483-
11484-
// Keep state so we can rewind if it turns out that this isn't an async function:
11485-
// async() {} -> method named async
11486-
// async => {} -> lambda with single parameter named async
11487-
RestorePoint termStart;
11488-
this->GetScanner()->Capture(&termStart);
11489-
11490-
this->GetScanner()->Scan();
11491-
11492-
if (m_token.tk == tkDArrow || (m_token.tk == tkLParen && isMethod) || this->GetScanner()->FHadNewLine())
11482+
if (m_grfscr & fscrDeferredFncIsGenerator)
1149311483
{
11494-
this->GetScanner()->SeekTo(termStart);
11484+
m_grfscr &= ~fscrDeferredFncIsGenerator;
11485+
isGenerator = true;
11486+
flags |= fFncGenerator;
1149511487
}
11496-
else
11488+
11489+
if (m_token.tk == tkStar && m_scriptContext->GetConfig()->IsES6GeneratorsEnabled())
1149711490
{
11498-
flags |= fFncAsync;
11499-
isAsync = true;
11491+
Assert(isGenerator && !isMethod);
11492+
this->GetScanner()->Scan();
1150011493
}
1150111494
}
1150211495

11503-
if (m_token.tk == tkStar && m_scriptContext->GetConfig()->IsES6GeneratorsEnabled())
11496+
if (m_grfscr & fscrDeferredFncIsAsync)
1150411497
{
11505-
ichMin = this->GetScanner()->IchMinTok();
11506-
iecpMin = this->GetScanner()->IecpMinTok();
11507-
11508-
flags |= fFncGenerator;
11509-
isGenerator = true;
11510-
11511-
this->GetScanner()->Scan();
11498+
m_grfscr &= ~fscrDeferredFncIsAsync;
11499+
isAsync = true;
11500+
flags |= fFncAsync;
1151211501
}
1151311502

11514-
// Eat the computed name expression
11515-
if (m_token.tk == tkLBrack && isMethod)
11503+
11504+
#if DBG
11505+
if (isMethod && m_token.tk == tkID)
1151611506
{
11507+
RestorePoint atPid;
11508+
IdentPtr pidHint = m_token.GetIdentifier(this->GetHashTbl());
11509+
this->GetScanner()->Capture(&atPid);
1151711510
this->GetScanner()->Scan();
11518-
ParseExpr<false>();
11511+
if ((pidHint == wellKnownPropertyPids.get || pidHint == wellKnownPropertyPids.set) && NextTokenIsPropertyNameStart())
11512+
{
11513+
// Getter/setter
11514+
// Skip the get/set keyword and continue normally
11515+
AssertMsg(false, "We should not be re-parsing the get/set part of member accessor functions");
11516+
}
11517+
else
11518+
{
11519+
// Not a getter/setter; rewind and treat the token as a name.
11520+
this->GetScanner()->SeekTo(atPid);
11521+
}
1151911522
}
11523+
#endif
11524+
11525+
// Ensure this isn't a computed name
11526+
AssertMsg(!(m_token.tk == tkLBrack && isMethod), "Can't defer parse a computed name expression, we should have started after this");
1152011527

1152111528
if (!isMethod && (m_token.tk == tkID || m_token.tk == tkLParen))
1152211529
{
@@ -11528,12 +11535,7 @@ ParseNodeProg * Parser::Parse(LPCUTF8 pszSrc, size_t offset, size_t length, char
1152811535
pnodeProg->pnodeBody = nullptr;
1152911536
AddToNodeList(&pnodeProg->pnodeBody, &lastNodeRef, pnodeFnc);
1153011537

11531-
// Include the async keyword or star character in the function extents
11532-
if (isAsync || isGenerator)
11533-
{
11534-
pnodeFnc->AsParseNodeFnc()->cbMin = iecpMin;
11535-
pnodeFnc->ichMin = ichMin;
11536-
}
11538+
// No need to update the cbStringMin property since no ParseableFunctionInfo will be created from this defer-parsed pnodeFnc
1153711539
}
1153811540
else
1153911541
{

lib/Parser/Parse.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@ class Parser
804804
static MemberNameToTypeMap* CreateMemberNameMap(ArenaAllocator* pAllocator);
805805

806806
template<bool buildAST> void ParseComputedName(ParseNodePtr* ppnodeName, LPCOLESTR* ppNameHint, LPCOLESTR* ppFullNameHint = nullptr, uint32 *pNameLength = nullptr, uint32 *pShortNameOffset = nullptr);
807-
template<bool buildAST> ParseNodeBin * ParseMemberGetSet(OpCode nop, LPCOLESTR* ppNameHint);
807+
template<bool buildAST> ParseNodeBin * ParseMemberGetSet(OpCode nop, LPCOLESTR* ppNameHint,size_t iecpMin, charcount_t ichMin);
808808
template<bool buildAST> ParseNode * ParseFncDeclCheckScope(ushort flags, bool resetParsingSuperRestrictionState = true, bool fAllowIn = true);
809809
template<bool buildAST> ParseNodeFnc * ParseFncDeclNoCheckScope(ushort flags, LPCOLESTR pNameHint = nullptr, const bool needsPIDOnRCurlyScan = false, bool resetParsingSuperRestrictionState = true, bool fUnaryOrParen = false, bool fAllowIn = true);
810810
template<bool buildAST> ParseNodeFnc * ParseFncDeclInternal(ushort flags, LPCOLESTR pNameHint, const bool needsPIDOnRCurlyScan, bool resetParsingSuperRestrictionState, bool fUnaryOrParen, bool noStmtContext, bool fAllowIn = true);

lib/Parser/ParseFlags.h

Lines changed: 2 additions & 2 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-
// Unused = 1 << 6,
17+
fscrDeferredFncIsGenerator = 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,7 +28,7 @@ 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-
// Unused = 1 << 13,
31+
fscrDeferredFncIsAsync = 1 << 13,
3232
fscrDeferredFncExpression = 1 << 14, // the function decl node we deferred is an expression,
3333
// i.e., not a declaration statement
3434
fscrDeferredFnc = 1 << 15, // the function we are parsing is deferred

lib/Parser/ptree.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ ParseNodeFnc::ParseNodeFnc(OpCode nop, charcount_t ichMin, charcount_t ichLim)
424424

425425
this->astSize = 0;
426426
this->cbMin = 0;
427+
this->cbStringMin = 0;
427428
this->cbLim = 0;
428429
this->lineNumber = 0;
429430
this->columnNumber = 0;

lib/Parser/ptree.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,6 @@ class ParseNodeFnc : public ParseNode
467467
LPCOLESTR hint;
468468
uint32 hintLength;
469469
uint32 hintOffset;
470-
bool isNameIdentifierRef;
471-
bool nestedFuncEscapes;
472470
ParseNodeBlock * pnodeScopes;
473471
ParseNodeBlock * pnodeBodyScope;
474472
ParseNodePtr pnodeParams;
@@ -482,11 +480,10 @@ class ParseNodeFnc : public ParseNode
482480
uint nestedCount; // Nested function count (valid until children have been processed)
483481
uint nestedIndex; // Index within the parent function (Used by ByteCodeGenerator)
484482

485-
uint16 firstDefaultArg; // Position of the first default argument, if any
486-
487483
FncFlags fncFlags;
488484
int32 astSize;
489485
size_t cbMin; // Min an Lim UTF8 offsets.
486+
size_t cbStringMin;
490487
size_t cbLim;
491488
ULONG lineNumber; // Line number relative to the current source buffer of the function declaration.
492489
ULONG columnNumber; // Column number of the declaration.
@@ -497,6 +494,9 @@ class ParseNodeFnc : public ParseNode
497494
RestorePoint *pRestorePoint;
498495
DeferredFunctionStub *deferredStub;
499496
IdentPtrSet *capturedNames;
497+
uint16 firstDefaultArg; // Position of the first default argument, if any
498+
bool isNameIdentifierRef;
499+
bool nestedFuncEscapes;
500500
bool canBeDeferred;
501501
bool isBodyAndParamScopeMerged; // Indicates whether the param scope and the body scope of the function can be merged together or not.
502502
// We cannot merge both scopes together if there is any closure capture or eval is present in the param scope.

0 commit comments

Comments
 (0)