diff --git a/lib/Parser/Hash.h b/lib/Parser/Hash.h index a1f2487a0b1..0bfcc86af0f 100644 --- a/lib/Parser/Hash.h +++ b/lib/Parser/Hash.h @@ -144,6 +144,16 @@ struct Ident return m_pidRefStack; } + PidRefStack *GetTopRef(uint maxBlockId) const + { + PidRefStack *ref; + for (ref = m_pidRefStack; ref && (uint)ref->id > maxBlockId; ref = ref->prev) + { + ; // nothing + } + return ref; + } + void SetTopRef(PidRefStack *ref) { m_pidRefStack = ref; diff --git a/lib/Parser/Parse.cpp b/lib/Parser/Parse.cpp index 6c1134cbe71..6de17576522 100644 --- a/lib/Parser/Parse.cpp +++ b/lib/Parser/Parse.cpp @@ -116,7 +116,6 @@ Parser::Parser(Js::ScriptContext* scriptContext, BOOL strictMode, PageAllocator m_errorCallback = nullptr; m_uncertainStructure = FALSE; m_reparsingLambdaParams = false; - m_inFIB = false; currBackgroundParseItem = nullptr; backgroundParseItems = nullptr; fastScannedRegExpNodes = nullptr; @@ -859,7 +858,6 @@ Symbol* Parser::AddDeclForPid(ParseNodePtr pnode, IdentPtr pid, SymbolType symbo if (sym->GetDecl() == nullptr) { - Assert(symbolType == STFunction); sym->SetDecl(pnode); break; } @@ -1446,12 +1444,11 @@ template ParseNodePtr Parser::StartParseBlockWithCapacity(PnodeBlockType blockType, ScopeType scopeType, int capacity) { Scope *scope = nullptr; - // Block scopes are created lazily when we discover block-scoped content. - if (scopeType != ScopeType_Unknown && scopeType != ScopeType_Block) - { - scope = Anew(&m_nodeAllocator, Scope, &m_nodeAllocator, scopeType, capacity); - PushScope(scope); - } + + // Block scopes are not created lazily in the case where we're repopulating a persisted scope. + + scope = Anew(&m_nodeAllocator, Scope, &m_nodeAllocator, scopeType, capacity); + PushScope(scope); return StartParseBlockHelper(blockType, scope, nullptr, nullptr); } @@ -4501,7 +4498,11 @@ BOOL Parser::DeferredParse(Js::LocalFunctionId functionId) { return false; } - if (PHASE_FORCE_RAW(Js::DeferParsePhase, m_sourceContextInfo->sourceContextId, functionId)) + if (PHASE_FORCE_RAW(Js::DeferParsePhase, m_sourceContextInfo->sourceContextId, functionId) +#ifdef ENABLE_DEBUG_CONFIG_OPTIONS + || Js::Configuration::Global.flags.IsEnabled(Js::ForceUndoDeferFlag) +#endif + ) { return true; } @@ -4925,17 +4926,6 @@ bool Parser::ParseFncDeclHelper(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, usho pstmtSave = m_pstmtCur; SetCurrentStatement(nullptr); - // Function definition is inside the parent function's parameter scope - bool isEnclosedInParamScope = this->m_currentScope->GetScopeType() == ScopeType_Parameter; - - if (this->m_currentScope->GetScopeType() == ScopeType_FuncExpr || this->m_currentScope->GetScopeType() == ScopeType_Block) - { - // Or this is a function expression or class enclosed in a parameter scope - isEnclosedInParamScope = this->m_currentScope->GetEnclosingScope() && this->m_currentScope->GetEnclosingScope()->GetScopeType() == ScopeType_Parameter; - } - - Assert(!isEnclosedInParamScope || pnodeFncSave->sxFnc.HasNonSimpleParameterList()); - RestorePoint beginFormals; m_pscan->Capture(&beginFormals); BOOL fWasAlreadyStrictMode = IsStrictMode(); @@ -4947,23 +4937,15 @@ bool Parser::ParseFncDeclHelper(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, usho } uint uDeferSave = m_grfscr & fscrDeferFncParse; - if ((!fDeclaration && m_ppnodeExprScope) || - isEnclosedInParamScope || - (flags & (fFncNoName | fFncLambda))) - { - // NOTE: Don't defer if this is a function expression inside a construct that induces - // a scope nested within the current function (like a with, or a catch in ES5 mode, or - // any function declared inside a nested lexical block or param scope in ES6 mode). - // We won't be able to reconstruct the scope chain properly when we come back and - // try to compile just the function expression. - // Also shut off deferring on getter/setter or other construct with unusual text bounds + if (flags & (fFncNoName | fFncLambda)) + { + // Disable deferral on getter/setter or other construct with unusual text bounds // (fFncNoName|fFncLambda) as these are usually trivial, and re-parsing is problematic. + // NOTE: It is probably worth supporting these cases for memory and load-time purposes, + // especially as they become more and more common. m_grfscr &= ~fscrDeferFncParse; } - bool saveInFIB = this->m_inFIB; - this->m_inFIB = fFunctionInBlock || this->m_inFIB; - bool isTopLevelDeferredFunc = false; struct AutoFastScanFlag { @@ -4995,21 +4977,8 @@ bool Parser::ParseFncDeclHelper(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, usho if (pnodeFnc) { pnodeFnc->sxFnc.SetCanBeDeferred(isTopLevelDeferredFunc && PnFnc::CanBeRedeferred(pnodeFnc->sxFnc.fncFlags)); - pnodeFnc->sxFnc.SetFIBPreventsDeferral(false); } - if (this->m_inFIB) - { - if (isTopLevelDeferredFunc) - { - // Block-scoping is the only non-heuristic reason for not deferring this function up front. - // So on creating the full FunctionBody at byte code gen time, verify that there is no - // block-scoped content visible to this function so it can remain a redeferral candidate. - pnodeFnc->sxFnc.SetFIBPreventsDeferral(true); - } - isTopLevelDeferredFunc = false; - } - // These are heuristic conditions that prohibit upfront deferral but not redeferral. isTopLevelDeferredFunc = isTopLevelDeferredFunc && !isDeferredFnc && (!isLikelyIIFE || !topLevelStmt || PHASE_FORCE_RAW(Js::DeferParsePhase, m_sourceContextInfo->sourceContextId, pnodeFnc->sxFnc.functionId)); @@ -5287,12 +5256,7 @@ bool Parser::ParseFncDeclHelper(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, usho if (buildAST) { DeferredFunctionStub *saveCurrentStub = m_currDeferredStub; - if (isEnclosedInParamScope) - { - // if the enclosed scope is the param scope we would not have created the deferred stub. - m_currDeferredStub = nullptr; - } - else if (pnodeFncSave && m_currDeferredStub) + if (pnodeFncSave && m_currDeferredStub) { // the Deferred stub will not match for the function which are defined on lambda formals. // Since this is not determined upfront that the current function is a part of outer function or part of lambda formal until we have seen the Arrow token. @@ -5449,7 +5413,6 @@ bool Parser::ParseFncDeclHelper(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, usho { m_grfscr |= uDeferSave; } - m_inFIB = saveInFIB; m_pscan->SetYieldIsKeyword(fPreviousYieldIsKeyword); m_pscan->SetAwaitIsKeyword(fPreviousAwaitIsKeyword); @@ -8690,7 +8653,7 @@ PidRefStack* Parser::PushPidRef(IdentPtr pid) Assert(GetCurrentBlock() != nullptr); AssertMsg(pid != nullptr, "PID should be created"); - PidRefStack *ref = pid->GetTopRef(); + PidRefStack *ref = pid->GetTopRef(m_nextBlockId - 1); int blockId = GetCurrentBlock()->sxBlock.blockId; int funcId = GetCurrentFunctionNode()->sxFnc.functionId; if (!ref || (ref->GetScopeId() < blockId)) @@ -9161,7 +9124,7 @@ ParseNodePtr Parser::ParseCatch() } pidCatch = m_token.GetIdentifier(m_phtbl); - PidRefStack *ref = this->PushPidRef(pidCatch); + PidRefStack *ref = this->FindOrAddPidRef(pidCatch, GetCurrentBlock()->sxBlock.blockId, GetCurrentFunctionNode()->sxFnc.functionId); ParseNodePtr pnodeParam = CreateNameNode(pidCatch); pnodeParam->sxPid.symRef = ref->GetSymRef(); @@ -10558,15 +10521,31 @@ void Parser::ParseStmtList(ParseNodePtr *ppnodeList, ParseNodePtr **pppnodeLast, } template -void Parser::VisitFunctionsInScope(ParseNodePtr pnodeScopeList, Fn fn) +void Parser::FinishFunctionsInScope(ParseNodePtr pnodeScopeList, Fn fn) { + Scope * scope; + Scope * origCurrentScope = this->m_currentScope; ParseNodePtr pnodeScope; + ParseNodePtr pnodeBlock; for (pnodeScope = pnodeScopeList; pnodeScope;) { switch (pnodeScope->nop) { case knopBlock: - VisitFunctionsInScope(pnodeScope->sxBlock.pnodeScopes, fn); + m_nextBlockId = pnodeScope->sxBlock.blockId + 1; + PushBlockInfo(pnodeScope); + scope = pnodeScope->sxBlock.scope; + if (scope && scope != origCurrentScope) + { + PushScope(scope); + } + FinishFunctionsInScope(pnodeScope->sxBlock.pnodeScopes, fn); + if (scope && scope != origCurrentScope) + { + BindPidRefs(GetCurrentBlockInfo(), m_nextBlockId - 1); + PopScope(scope); + } + PopBlockInfo(); pnodeScope = pnodeScope->sxBlock.pnodeNext; break; @@ -10576,12 +10555,29 @@ void Parser::VisitFunctionsInScope(ParseNodePtr pnodeScopeList, Fn fn) break; case knopCatch: - VisitFunctionsInScope(pnodeScope->sxCatch.pnodeScopes, fn); + scope = pnodeScope->sxCatch.scope; + if (scope) + { + PushScope(scope); + } + pnodeBlock = CreateBlockNode(PnodeBlockType::Regular); + pnodeBlock->sxBlock.scope = scope; + PushBlockInfo(pnodeBlock); + FinishFunctionsInScope(pnodeScope->sxCatch.pnodeScopes, fn); + if (scope) + { + BindPidRefs(GetCurrentBlockInfo(), m_nextBlockId - 1); + PopScope(scope); + } + PopBlockInfo(); pnodeScope = pnodeScope->sxCatch.pnodeNext; break; case knopWith: - VisitFunctionsInScope(pnodeScope->sxWith.pnodeScopes, fn); + PushBlockInfo(CreateBlockNode()); + PushDynamicBlock(); + FinishFunctionsInScope(pnodeScope->sxWith.pnodeScopes, fn); + PopBlockInfo(); pnodeScope = pnodeScope->sxWith.pnodeNext; break; @@ -10620,7 +10616,10 @@ ULONG Parser::GetDeferralThreshold(bool isProfileLoaded) void Parser::FinishDeferredFunction(ParseNodePtr pnodeScopeList) { - VisitFunctionsInScope(pnodeScopeList, + uint saveNextBlockId = m_nextBlockId; + m_nextBlockId = pnodeScopeList->sxBlock.blockId + 1; + + FinishFunctionsInScope(pnodeScopeList, [this](ParseNodePtr pnodeFnc) { Assert(pnodeFnc->nop == knopFncDecl); @@ -10637,22 +10636,24 @@ void Parser::FinishDeferredFunction(ParseNodePtr pnodeScopeList) this->m_currentNodeFunc = pnodeFnc; ParseNodePtr pnodeFncExprBlock = nullptr; - if (pnodeFnc->sxFnc.pnodeName && - !pnodeFnc->sxFnc.IsDeclaration()) + ParseNodePtr pnodeName = pnodeFnc->sxFnc.pnodeName; + if (pnodeName) { - // Set up the named function expression symbol so references inside the function can be bound. - ParseNodePtr pnodeName = pnodeFnc->sxFnc.pnodeName; Assert(pnodeName->nop == knopVarDecl); Assert(pnodeName->sxVar.pnodeNext == nullptr); - pnodeFncExprBlock = this->StartParseBlock(PnodeBlockType::Function, ScopeType_FuncExpr); - PidRefStack *ref = this->PushPidRef(pnodeName->sxVar.pid); - pnodeName->sxVar.symRef = ref->GetSymRef(); - ref->SetSym(pnodeName->sxVar.sym); + if (!pnodeFnc->sxFnc.IsDeclaration()) + { + // Set up the named function expression symbol so references inside the function can be bound. + pnodeFncExprBlock = this->StartParseBlock(PnodeBlockType::Function, ScopeType_FuncExpr); + PidRefStack *ref = this->PushPidRef(pnodeName->sxVar.pid); + pnodeName->sxVar.symRef = ref->GetSymRef(); + ref->SetSym(pnodeName->sxVar.sym); - Scope *fncExprScope = pnodeFncExprBlock->sxBlock.scope; - fncExprScope->AddNewSymbol(pnodeName->sxVar.sym); - pnodeFnc->sxFnc.scope = fncExprScope; + Scope *fncExprScope = pnodeFncExprBlock->sxBlock.scope; + fncExprScope->AddNewSymbol(pnodeName->sxVar.sym); + pnodeFnc->sxFnc.scope = fncExprScope; + } } ParseNodePtr pnodeBlock = this->StartParseBlock(PnodeBlockType::Parameter, ScopeType_Parameter); @@ -10662,10 +10663,12 @@ void Parser::FinishDeferredFunction(ParseNodePtr pnodeScopeList) // Add the args to the scope, since we won't re-parse those. Scope *scope = pnodeBlock->sxBlock.scope; + uint blockId = GetCurrentBlock()->sxBlock.blockId; + uint funcId = GetCurrentFunctionNode()->sxFnc.functionId; auto addArgsToScope = [&](ParseNodePtr pnodeArg) { if (pnodeArg->IsVarLetOrConst()) { - PidRefStack *ref = this->PushPidRef(pnodeArg->sxVar.pid); + PidRefStack *ref = this->FindOrAddPidRef(pnodeArg->sxVar.pid, blockId, funcId);//this->PushPidRef(pnodeArg->sxVar.pid); pnodeArg->sxVar.symRef = ref->GetSymRef(); if (ref->GetSym() != nullptr) { @@ -10715,9 +10718,11 @@ void Parser::FinishDeferredFunction(ParseNodePtr pnodeScopeList) { if (scope->GetCanMergeWithBodyScope()) { - scope->ForEachSymbol([this](Symbol* paramSym) + blockId = GetCurrentBlock()->sxBlock.blockId; + funcId = GetCurrentFunctionNode()->sxFnc.functionId; + scope->ForEachSymbol([this, blockId, funcId](Symbol* paramSym) { - PidRefStack* ref = PushPidRef(paramSym->GetPid()); + PidRefStack* ref = this->FindOrAddPidRef(paramSym->GetPid(), blockId, funcId); ref->SetSym(paramSym); }); } @@ -10761,6 +10766,8 @@ void Parser::FinishDeferredFunction(ParseNodePtr pnodeScopeList) this->m_currentNodeFunc = pnodeFncSave; } }); + + m_nextBlockId = saveNextBlockId; } void Parser::InitPids() @@ -10784,14 +10791,8 @@ void Parser::InitPids() wellKnownPropertyPids._star = m_phtbl->PidHashNameLen(_u("*"), sizeof("*") - 1); } -void Parser::RestoreScopeInfo(Js::ParseableFunctionInfo* functionBody) +void Parser::RestoreScopeInfo(Js::ScopeInfo * scopeInfo) { - if (!functionBody) - { - return; - } - - Js::ScopeInfo* scopeInfo = functionBody->GetScopeInfo(); if (!scopeInfo) { return; @@ -10806,53 +10807,47 @@ void Parser::RestoreScopeInfo(Js::ParseableFunctionInfo* functionBody) PROBE_STACK(m_scriptContext, Js::Constants::MinStackByteCodeVisitor); } - RestoreScopeInfo(scopeInfo->GetParent()); // Recursively restore outer func scope info + RestoreScopeInfo(scopeInfo->GetParentScopeInfo()); // Recursively restore outer func scope info - Js::ScopeInfo* funcExprScopeInfo = scopeInfo->GetFuncExprScopeInfo(); - if (funcExprScopeInfo) - { - funcExprScopeInfo->SetScopeId(m_nextBlockId); - ParseNodePtr pnodeFncExprScope = StartParseBlockWithCapacity(PnodeBlockType::Function, ScopeType_FuncExpr, funcExprScopeInfo->GetSymbolCount()); - Scope *scope = pnodeFncExprScope->sxBlock.scope; - funcExprScopeInfo->GetScopeInfo(this, nullptr, nullptr, scope); - } + scopeInfo->SetScopeId(m_nextBlockId); + ParseNodePtr pnodeScope = nullptr; + ScopeType scopeType = scopeInfo->GetScopeType(); + PnodeBlockType blockType; + switch (scopeType) + { + case ScopeType_With: + PushDynamicBlock(); + // fall through + case ScopeType_Block: + case ScopeType_Catch: + case ScopeType_CatchParamPattern: + case ScopeType_GlobalEvalBlock: + blockType = PnodeBlockType::Regular; + break; - Js::ScopeInfo* paramScopeInfo = scopeInfo->GetParamScopeInfo(); - if (paramScopeInfo) - { - paramScopeInfo->SetScopeId(m_nextBlockId); - ParseNodePtr pnodeFncExprScope = StartParseBlockWithCapacity(PnodeBlockType::Parameter, ScopeType_Parameter, paramScopeInfo->GetSymbolCount()); - Scope *scope = pnodeFncExprScope->sxBlock.scope; - paramScopeInfo->GetScopeInfo(this, nullptr, nullptr, scope); - } + case ScopeType_FunctionBody: + case ScopeType_FuncExpr: + blockType = PnodeBlockType::Function; + break; - scopeInfo->SetScopeId(m_nextBlockId); - ParseNodePtr pnodeFncScope = nullptr; - if (scopeInfo->IsGlobalEval()) - { - pnodeFncScope = StartParseBlockWithCapacity(PnodeBlockType::Regular, ScopeType_GlobalEvalBlock, scopeInfo->GetSymbolCount()); - } - else - { - pnodeFncScope = StartParseBlockWithCapacity(PnodeBlockType::Function, ScopeType_FunctionBody, scopeInfo->GetSymbolCount()); - } - Scope *scope = pnodeFncScope->sxBlock.scope; - scopeInfo->GetScopeInfo(this, nullptr, nullptr, scope); -} + case ScopeType_Parameter: + blockType = PnodeBlockType::Parameter; + break; -void Parser::FinishScopeInfo(Js::ParseableFunctionInfo *functionBody) -{ - if (!functionBody) - { - return; - } - Js::ScopeInfo* scopeInfo = functionBody->GetScopeInfo(); - if (!scopeInfo) - { - return; + default: + Assert(0); + return; } + pnodeScope = StartParseBlockWithCapacity(blockType, scopeType, scopeInfo->GetSymbolCount()); + Scope *scope = pnodeScope->sxBlock.scope; + scope->SetScopeInfo(scopeInfo); + scopeInfo->ExtractScopeInfo(this, /*nullptr, nullptr,*/ scope); +} + +void Parser::FinishScopeInfo(Js::ScopeInfo * scopeInfo) +{ if (this->IsBackgroundParser()) { PROBE_STACK_NO_DISPOSE(m_scriptContext, Js::Constants::MinStackByteCodeVisitor); @@ -10862,43 +10857,18 @@ void Parser::FinishScopeInfo(Js::ParseableFunctionInfo *functionBody) PROBE_STACK(m_scriptContext, Js::Constants::MinStackByteCodeVisitor); } - int scopeId = scopeInfo->GetScopeId(); - - scopeInfo->GetScope()->ForEachSymbol([this, scopeId](Symbol *sym) - { - this->BindPidRefsInScope(sym->GetPid(), sym, scopeId); - }); - PopScope(scopeInfo->GetScope()); - PopStmt(&m_currentBlockInfo->pstmt); - PopBlockInfo(); - - Js::ScopeInfo *paramScopeInfo = scopeInfo->GetParamScopeInfo(); - if (paramScopeInfo) + for (;scopeInfo != nullptr; scopeInfo = scopeInfo->GetParentScopeInfo()) { - scopeId = paramScopeInfo->GetScopeId(); - paramScopeInfo->GetScope()->ForEachSymbol([this, scopeId](Symbol *sym) - { - this->BindPidRefsInScope(sym->GetPid(), sym, scopeId); - }); - PopScope(paramScopeInfo->GetScope()); - PopStmt(&m_currentBlockInfo->pstmt); - PopBlockInfo(); - } + int scopeId = scopeInfo->GetScopeId(); - Js::ScopeInfo *funcExprScopeInfo = scopeInfo->GetFuncExprScopeInfo(); - if (funcExprScopeInfo) - { - scopeId = funcExprScopeInfo->GetScopeId(); - funcExprScopeInfo->GetScope()->ForEachSymbol([this, scopeId](Symbol *sym) + scopeInfo->GetScope()->ForEachSymbol([this, scopeId](Symbol *sym) { this->BindPidRefsInScope(sym->GetPid(), sym, scopeId); }); - PopScope(funcExprScopeInfo->GetScope()); + PopScope(scopeInfo->GetScope()); PopStmt(&m_currentBlockInfo->pstmt); PopBlockInfo(); } - - FinishScopeInfo(scopeInfo->GetParent()); } /*************************************************************************** @@ -11054,7 +11024,7 @@ ParseNodePtr Parser::Parse(LPCUTF8 pszSrc, size_t offset, size_t length, charcou m_currentNodeFunc->sxFnc.nestedCount = m_functionBody->GetNestedCount(); m_currentNodeFunc->sxFnc.SetStrictMode(!!this->m_fUseStrictMode); - this->RestoreScopeInfo(scopeInfo->GetParent()); + this->RestoreScopeInfo(scopeInfo); } } @@ -11079,7 +11049,7 @@ ParseNodePtr Parser::Parse(LPCUTF8 pszSrc, size_t offset, size_t length, charcou { if (scopeInfo) { - this->FinishScopeInfo(scopeInfo->GetParent()); + this->FinishScopeInfo(scopeInfo); } } diff --git a/lib/Parser/Parse.h b/lib/Parser/Parse.h index 4eab6d6ddde..be61da6df08 100644 --- a/lib/Parser/Parse.h +++ b/lib/Parser/Parse.h @@ -6,6 +6,11 @@ #include "ParseFlags.h" +namespace Js +{ + class ScopeInfo; +}; + // Operator precedence levels enum { @@ -369,7 +374,6 @@ class Parser bool m_inDeferredNestedFunc; // true if parsing a function in deferred mode, nested within the current node bool m_isInBackground; bool m_reparsingLambdaParams; - bool m_inFIB; // This bool is used for deferring the shorthand initializer error ( {x = 1}) - as it is allowed in the destructuring grammar. bool m_hasDeferredShorthandInitError; @@ -976,8 +980,8 @@ class Parser void RemovePrevPidRef(IdentPtr pid, PidRefStack *lastRef); void SetPidRefsInScopeDynamic(IdentPtr pid, int blockId); - void RestoreScopeInfo(Js::ParseableFunctionInfo* functionBody); - void FinishScopeInfo(Js::ParseableFunctionInfo* functionBody); + void RestoreScopeInfo(Js::ScopeInfo * scopeInfo); + void FinishScopeInfo(Js::ScopeInfo * scopeInfo); BOOL PnodeLabelNoAST(IdentToken* pToken, LabelId* pLabelIdList); LabelId* CreateLabelId(IdentToken* pToken); @@ -1011,7 +1015,7 @@ class Parser } template - void VisitFunctionsInScope(ParseNodePtr pnodeScopeList, Fn fn); + void FinishFunctionsInScope(ParseNodePtr pnodeScopeList, Fn fn); void FinishDeferredFunction(ParseNodePtr pnodeScopeList); /*********************************************************************** diff --git a/lib/Parser/ptree.h b/lib/Parser/ptree.h index b11c73fc428..43aab753949 100644 --- a/lib/Parser/ptree.h +++ b/lib/Parser/ptree.h @@ -247,7 +247,6 @@ struct PnFnc RestorePoint *pRestorePoint; DeferredFunctionStub *deferredStub; bool canBeDeferred; - bool fibPreventsDeferral; static const int32 MaxStackClosureAST = 800000; @@ -286,7 +285,6 @@ struct PnFnc { fncFlags = kFunctionNone; canBeDeferred = false; - fibPreventsDeferral = false; } void SetAsmjsMode(bool set = true) { SetFlags(kFunctionAsmjsMode, set); } @@ -323,7 +321,6 @@ struct PnFnc void SetIsDefaultModuleExport(bool set = true) { SetFlags(kFunctionIsDefaultModuleExport, set); } void SetNestedFuncEscapes(bool set = true) { nestedFuncEscapes = set; } void SetCanBeDeferred(bool set = true) { canBeDeferred = set; } - void SetFIBPreventsDeferral(bool set = true) { fibPreventsDeferral = set; } bool CallsEval() const { return HasFlags(kFunctionCallsEval); } bool ChildCallsEval() const { return HasFlags(kFunctionChildCallsEval); } @@ -362,7 +359,6 @@ struct PnFnc bool IsDefaultModuleExport() const { return HasFlags(kFunctionIsDefaultModuleExport); } bool NestedFuncEscapes() const { return nestedFuncEscapes; } bool CanBeDeferred() const { return canBeDeferred; } - bool FIBPreventsDeferral() const { return fibPreventsDeferral; } size_t LengthInBytes() { diff --git a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp index a1d46680add..d0d824f90cd 100644 --- a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp +++ b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp @@ -2993,6 +2993,11 @@ void ByteCodeGenerator::EmitOneFunction(ParseNode *pnode) deferParseFunction->SetReportedInParamsCount(funcInfo->inArgsCount); } + if (deferParseFunction->IsDeferred() || deferParseFunction->CanBeDeferred()) + { + Js::ScopeInfo::SaveEnclosingScopeInfo(this, funcInfo); + } + if (funcInfo->root->sxFnc.pnodeBody == nullptr) { if (!PHASE_OFF1(Js::SkipNestedDeferredPhase)) @@ -3658,13 +3663,7 @@ void ByteCodeGenerator::EmitScopeList(ParseNode *pnode, ParseNode *breakOnBodySc } this->StartEmitFunction(pnode); - // Persist outer func scope info if nested func is deferred - if (CONFIG_FLAG(DeferNested)) - { - FuncInfo* parentFunc = TopFuncInfo(); - Js::ScopeInfo::SaveScopeInfoForDeferParse(this, parentFunc, funcInfo); - PushFuncInfo(_u("StartEmitFunction"), funcInfo); - } + PushFuncInfo(_u("StartEmitFunction"), funcInfo); if (paramScope && !paramScope->GetCanMergeWithBodyScope()) { @@ -3842,21 +3841,6 @@ void ByteCodeGenerator::StartEmitFunction(ParseNode *pnodeFnc) // Only set the environment depth if it's truly known (i.e., not in eval or event handler). funcInfo->GetParsedFunctionBody()->SetEnvDepth(this->envDepth); } - - if (pnodeFnc->sxFnc.FIBPreventsDeferral()) - { - for (Scope *scope = this->currentScope; scope; scope = scope->GetEnclosingScope()) - { - if (scope->GetScopeType() != ScopeType_FunctionBody && - scope->GetScopeType() != ScopeType_Global && - scope->GetScopeType() != ScopeType_GlobalEvalBlock && - scope->GetMustInstantiate()) - { - funcInfo->byteCodeFunction->SetAttributes((Js::FunctionInfo::Attributes)(funcInfo->byteCodeFunction->GetAttributes() & ~Js::FunctionInfo::Attributes::CanDefer)); - break; - } - } - } } if (funcInfo->GetCallsEval()) diff --git a/lib/Runtime/ByteCode/ByteCodeGenerator.cpp b/lib/Runtime/ByteCode/ByteCodeGenerator.cpp index 25a785514f1..55b9af6173c 100644 --- a/lib/Runtime/ByteCode/ByteCodeGenerator.cpp +++ b/lib/Runtime/ByteCode/ByteCodeGenerator.cpp @@ -968,84 +968,101 @@ Js::RegSlot ByteCodeGenerator::EnregisterStringTemplateCallsiteConstant(ParseNod // // Restore all outer func scope info when reparsing a deferred func. // -void ByteCodeGenerator::RestoreScopeInfo(Js::ParseableFunctionInfo* functionBody) +void ByteCodeGenerator::RestoreScopeInfo(Js::ScopeInfo *scopeInfo, FuncInfo * func) { - if (functionBody && functionBody->GetScopeInfo()) + if (scopeInfo) { PROBE_STACK(scriptContext, Js::Constants::MinStackByteCodeVisitor); - Js::ScopeInfo* scopeInfo = functionBody->GetScopeInfo(); - RestoreScopeInfo(scopeInfo->GetParent()); // Recursively restore outer func scope info + Js::ParseableFunctionInfo * pfi = scopeInfo->GetFunctionInfo()->GetParseableFunctionInfo(); + bool newFunc = (func == nullptr || func->byteCodeFunction != pfi); - Js::ScopeInfo* paramScopeInfo = scopeInfo->GetParamScopeInfo(); - Scope* paramScope = nullptr; - if (paramScopeInfo != nullptr) + if (newFunc) { - paramScope = paramScopeInfo->GetScope(); - Assert(paramScope); - if (!paramScopeInfo->GetCanMergeWithBodyScope()) - { - paramScope->SetCannotMergeWithBodyScope(); - } - // We need the funcInfo before continuing the restoration of the param scope, so wait for the funcInfo to be created. - } - - Scope* bodyScope = scopeInfo->GetScope(); - - Assert(bodyScope); - bodyScope->SetHasOwnLocalInClosure(scopeInfo->GetHasOwnLocalInClosure()); - - FuncInfo* func = Anew(alloc, FuncInfo, functionBody->GetDisplayName(), alloc, paramScope, bodyScope, nullptr, functionBody); - - if (bodyScope->GetScopeType() == ScopeType_GlobalEvalBlock) - { - func->bodyScope = this->currentScope; + func = Anew(alloc, FuncInfo, pfi->GetDisplayName(), alloc, nullptr, nullptr, nullptr, pfi); + newFunc = true; } - PushFuncInfo(_u("RestoreScopeInfo"), func); - if (!functionBody->DoStackNestedFunc()) - { - func->hasEscapedUseNestedFunc = true; - } + // Recursively restore enclosing scope info so outermost scopes/funcs are pushed first. + this->RestoreScopeInfo(scopeInfo->GetParentScopeInfo(), func); + this->RestoreOneScope(scopeInfo, func); - Js::ScopeInfo* funcExprScopeInfo = scopeInfo->GetFuncExprScopeInfo(); - if (funcExprScopeInfo) + if (newFunc) { - Scope* funcExprScope = funcExprScopeInfo->GetScope(); - Assert(funcExprScope); - funcExprScope->SetFunc(func); - func->SetFuncExprScope(funcExprScope); - funcExprScopeInfo->GetScopeInfo(nullptr, this, func, funcExprScope); - } - - // Restore the param scope after the function expression scope - if (paramScope != nullptr) - { - paramScope->SetFunc(func); - paramScopeInfo->GetScopeInfo(nullptr, this, func, paramScope); + PushFuncInfo(_u("RestoreScopeInfo"), func); + if (!pfi->DoStackNestedFunc()) + { + func->hasEscapedUseNestedFunc = true; + } } - scopeInfo->GetScopeInfo(nullptr, this, func, bodyScope); } else { Assert(this->TopFuncInfo() == nullptr); // funcBody is glo + Assert(currentScope == nullptr); currentScope = Anew(alloc, Scope, alloc, ScopeType_Global); globalScope = currentScope; - FuncInfo *func = Anew(alloc, FuncInfo, Js::Constants::GlobalFunction, - alloc, nullptr, currentScope, nullptr, functionBody); - PushFuncInfo(_u("RestoreScopeInfo"), func); + if (func == nullptr || !func->byteCodeFunction->GetIsGlobalFunc()) + { + func = Anew(alloc, FuncInfo, Js::Constants::GlobalFunction, + alloc, nullptr, nullptr/*currentScope*/, nullptr, nullptr/*functionBody*/); + PushFuncInfo(_u("RestoreScopeInfo"), func); + } + func->SetBodyScope(currentScope); } } +void ByteCodeGenerator::RestoreOneScope(Js::ScopeInfo * scopeInfo, FuncInfo * func) +{ + TRACE_BYTECODE(_u("\nRestore ScopeInfo: %s #symbols: %d %s\n"), + func->name, scopeInfo->GetSymbolCount(), scopeInfo->IsObject() ? _u("isObject") : _u("")); + + Scope * scope = scopeInfo->GetScope(); + + scope->SetFunc(func); + + switch (scope->GetScopeType()) + { + case ScopeType_Parameter: + if (!scopeInfo->GetCanMergeWithBodyScope()) + { + scope->SetCannotMergeWithBodyScope(); + } + Assert(func->GetParamScope() == nullptr); + func->SetParamScope(scope); + break; + + case ScopeType_FuncExpr: + Assert(func->GetFuncExprScope() == nullptr); + func->SetFuncExprScope(scope); + break; + + case ScopeType_FunctionBody: + case ScopeType_GlobalEvalBlock: + Assert(func->GetBodyScope() == nullptr || (func->GetBodyScope()->GetScopeType() == ScopeType_Global && scope->GetScopeType() == ScopeType_GlobalEvalBlock)); + func->SetBodyScope(scope); + func->SetHasCachedScope(scopeInfo->IsCached()); + break; + } + + Assert(!scopeInfo->IsCached() || scope == func->GetBodyScope()); + + // scopeInfo->scope was created/saved during parsing. + // We no longer need it by now. + // Clear it to avoid GC false positive (arena memory later used by GC). + scopeInfo->SetScope(nullptr); + this->PushScope(scope); +} + FuncInfo * ByteCodeGenerator::StartBindGlobalStatements(ParseNode *pnode) { - if (parentScopeInfo && parentScopeInfo->GetParent() && (!parentScopeInfo->GetParent()->GetIsGlobalFunc() || parentScopeInfo->GetParent()->IsEval())) + if (parentScopeInfo) { Assert(CONFIG_FLAG(DeferNested)); trackEnvDepth = true; - RestoreScopeInfo(parentScopeInfo->GetParent()); + RestoreScopeInfo(parentScopeInfo, nullptr); trackEnvDepth = false; // "currentScope" is the parentFunc scope. This ensures the deferred func declaration // symbol will bind to the func declaration symbol already available in parentFunc scope. @@ -1211,7 +1228,7 @@ FuncInfo * ByteCodeGenerator::StartBindFunction(const char16 *name, uint nameLen if (parsedFunctionBody->GetScopeInfo()) { // Propagate flags from the (real) parent function. - Js::ParseableFunctionInfo *parent = parsedFunctionBody->GetScopeInfo()->GetParent(); + Js::ParseableFunctionInfo *parent = parsedFunctionBody->GetScopeInfo()->GetParseableFunctionInfo(); if (parent) { if (parent->GetHasOrParentHasArguments()) @@ -1632,7 +1649,8 @@ Symbol * ByteCodeGenerator::FindSymbol(Symbol **symRef, IdentPtr pid, bool forRe bool didTransferToFncVarSym = false; - if (!PHASE_OFF(Js::OptimizeBlockScopePhase, top->byteCodeFunction) && + #pragma prefast(suppress:6237, "The right hand side condition does not have any side effects.") + if (PHASE_ON(Js::OptimizeBlockScopePhase, top->byteCodeFunction) && sym->GetIsBlockVar() && !sym->GetScope()->IsBlockInLoop() && sym->GetSymbolType() == STFunction) @@ -1755,7 +1773,7 @@ Symbol * ByteCodeGenerator::AddSymbolToScope(Scope *scope, const char16 *key, in // on such compiles, so we essentially have to migrate the symbol to the new scope. // We check fscrEvalCode, not fscrEval, because the same thing can happen in indirect eval, // when fscrEval is not set. - Assert(scope->GetScopeType() == ScopeType_Global); + Assert(scope->GetScopeType() == ScopeType_Global || scope->GetScopeType() == ScopeType_GlobalEvalBlock); scope->AddNewSymbol(sym); } @@ -1995,7 +2013,7 @@ void ByteCodeGenerator::Generate(__in ParseNode *pnode, uint32 grfscr, __in Byte void ByteCodeGenerator::CheckDeferParseHasMaybeEscapedNestedFunc() { - if (!this->parentScopeInfo || (this->parentScopeInfo->GetParent() && this->parentScopeInfo->GetParent()->GetIsGlobalFunc())) + if (!this->parentScopeInfo) { return; } @@ -2023,7 +2041,7 @@ void ByteCodeGenerator::CheckDeferParseHasMaybeEscapedNestedFunc() else { // We have to wait until it is parsed before we populate the stack nested func parent. - FuncInfo * parentFunc = top->GetBodyScope()->GetEnclosingFunc(); + FuncInfo * parentFunc = top->GetParamScope() ? top->GetParamScope()->GetEnclosingFunc() : top->GetBodyScope()->GetEnclosingFunc(); if (!parentFunc->IsGlobalFunction()) { Assert(parentFunc->byteCodeFunction != rootFuncBody); @@ -2040,6 +2058,11 @@ void ByteCodeGenerator::CheckDeferParseHasMaybeEscapedNestedFunc() FuncInfo * funcInfo = i.Data(); Assert(funcInfo->IsRestored()); Js::ParseableFunctionInfo * parseableFunctionInfo = funcInfo->byteCodeFunction; + if (parseableFunctionInfo == nullptr) + { + Assert(funcInfo->GetBodyScope() && funcInfo->GetBodyScope()->GetScopeType() == ScopeType_Global); + return; + } bool didStackNestedFunc = parseableFunctionInfo->DoStackNestedFunc(); if (!didStackNestedFunc) { @@ -3247,6 +3270,7 @@ void AddFunctionsToScope(ParseNodePtr scope, ByteCodeGenerator * byteCodeGenerat sym->GetScope() != sym->GetScope()->GetFunc()->GetParamScope()) { sym->SetIsBlockVar(true); + sym->SetHasRealBlockVarRef(true); } } }); @@ -3629,15 +3653,21 @@ void PostVisitBlock(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator) return; } + Scope *scope = pnode->sxBlock.scope; + if (pnode->sxBlock.GetCallsEval() || pnode->sxBlock.GetChildCallsEval() || (byteCodeGenerator->GetFlags() & (fscrEval | fscrImplicitThis | fscrImplicitParents))) { - Scope *scope = pnode->sxBlock.scope; bool scopeIsEmpty = scope->IsEmpty(); scope->SetIsObject(); scope->SetCapturesAll(true); scope->SetMustInstantiate(!scopeIsEmpty); } + if (scope->GetHasOwnLocalInClosure()) + { + byteCodeGenerator->ProcessScopeWithCapturedSym(scope); + } + byteCodeGenerator->PopScope(); byteCodeGenerator->PopBlock(); @@ -3686,6 +3716,11 @@ void PreVisitCatch(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator) void PostVisitCatch(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator) { + Scope *scope = pnode->sxCatch.scope; + if (scope->GetHasOwnLocalInClosure()) + { + byteCodeGenerator->ProcessScopeWithCapturedSym(scope); + } byteCodeGenerator->EndBindCatch(); } diff --git a/lib/Runtime/ByteCode/ByteCodeGenerator.h b/lib/Runtime/ByteCode/ByteCodeGenerator.h index 913f0073222..4a0e6f9fcda 100644 --- a/lib/Runtime/ByteCode/ByteCodeGenerator.h +++ b/lib/Runtime/ByteCode/ByteCodeGenerator.h @@ -207,7 +207,9 @@ class ByteCodeGenerator return protoReg; } - void RestoreScopeInfo(Js::ParseableFunctionInfo* funcInfo); + void RestoreScopeInfo(Js::ScopeInfo *scopeInfo, FuncInfo * func); + void RestoreOneScope(Js::ScopeInfo * scopeInfo, FuncInfo * func); + FuncInfo *StartBindGlobalStatements(ParseNode *pnode); void AssignPropertyId(Symbol *sym, Js::ParseableFunctionInfo* functionInfo); void AssignPropertyId(IdentPtr pid); diff --git a/lib/Runtime/ByteCode/FuncInfo.cpp b/lib/Runtime/ByteCode/FuncInfo.cpp index 2bd8a38e33a..7446204c688 100644 --- a/lib/Runtime/ByteCode/FuncInfo.cpp +++ b/lib/Runtime/ByteCode/FuncInfo.cpp @@ -105,7 +105,10 @@ FuncInfo::FuncInfo( maxForInLoopLevel(0) { this->byteCodeFunction = byteCodeFunction; - bodyScope->SetFunc(this); + if (bodyScope != nullptr) + { + bodyScope->SetFunc(this); + } if (paramScope != nullptr) { paramScope->SetFunc(this); diff --git a/lib/Runtime/ByteCode/FuncInfo.h b/lib/Runtime/ByteCode/FuncInfo.h index c9725acda85..d47e14c8f99 100644 --- a/lib/Runtime/ByteCode/FuncInfo.h +++ b/lib/Runtime/ByteCode/FuncInfo.h @@ -263,24 +263,39 @@ class FuncInfo // 1) new Function code's global code // 2) global code generated from the reparsing deferred parse function - bool IsFakeGlobalFunction(uint32 flags) const { + bool IsFakeGlobalFunction(uint32 flags) const + { return IsGlobalFunction() && !(flags & fscrGlobalCode); } - Scope *GetBodyScope() const { + Scope *GetBodyScope() const + { return bodyScope; } - Scope *GetParamScope() const { + void SetBodyScope(Scope * scope) + { + bodyScope = scope; + } + + Scope *GetParamScope() const + { return paramScope; } - Scope *GetTopLevelScope() const { + void SetParamScope(Scope * scope) + { + paramScope = scope; + } + + Scope *GetTopLevelScope() const + { // Top level scope will be the same for knopProg and knopFncDecl. return paramScope; } - Scope* GetFuncExprScope() const { + Scope* GetFuncExprScope() const + { return funcExprScope; } diff --git a/lib/Runtime/ByteCode/Scope.h b/lib/Runtime/ByteCode/Scope.h index fd22ab74cd1..1897675e973 100644 --- a/lib/Runtime/ByteCode/Scope.h +++ b/lib/Runtime/ByteCode/Scope.h @@ -22,6 +22,7 @@ class Scope { private: Scope *enclosingScope; + Js::ScopeInfo *scopeInfo; Js::RegSlot location; FuncInfo *func; Symbol *m_symList; @@ -48,6 +49,7 @@ class Scope alloc(alloc), func(nullptr), enclosingScope(nullptr), + scopeInfo(nullptr), isDynamic(false), isObject(false), canMerge(true), @@ -177,6 +179,16 @@ class Scope return enclosingScope; } + void SetScopeInfo(Js::ScopeInfo * scopeInfo) + { + this->scopeInfo = scopeInfo; + } + + Js::ScopeInfo * GetScopeInfo() const + { + return this->scopeInfo; + } + ScopeType GetScopeType() const { return this->scopeType; diff --git a/lib/Runtime/ByteCode/ScopeInfo.cpp b/lib/Runtime/ByteCode/ScopeInfo.cpp index a54800c5c8e..20841053aea 100644 --- a/lib/Runtime/ByteCode/ScopeInfo.cpp +++ b/lib/Runtime/ByteCode/ScopeInfo.cpp @@ -42,15 +42,6 @@ namespace Js TRACE_BYTECODE(_u("%12s %d\n"), sym->GetName().GetBuffer(), sym->GetScopeSlot()); } - // - // Create scope info for a deferred child to refer to its parent ParseableFunctionInfo. - // - ScopeInfo* ScopeInfo::FromParent(FunctionBody* parent) - { - return RecyclerNew(parent->GetScriptContext()->GetRecycler(), // Alloc with ParseableFunctionInfo - ScopeInfo, parent->GetFunctionInfo(), 0); - } - inline void AddSlotCount(int& count, int addCount) { if (addCount != 0 && Int32Math::Add(count, addCount, &count)) @@ -60,10 +51,13 @@ namespace Js } // - // Create scope info for an outer scope. + // Create scope info for a single scope. // - ScopeInfo* ScopeInfo::FromScope(ByteCodeGenerator* byteCodeGenerator, ParseableFunctionInfo* parent, Scope* scope, ScriptContext *scriptContext) + ScopeInfo* ScopeInfo::SaveOneScopeInfo(/*ByteCodeGenerator* byteCodeGenerator, ParseableFunctionInfo* parent,*/ Scope* scope, ScriptContext *scriptContext) { + Assert(scope->GetScopeInfo() == nullptr); + Assert(scope->GetScopeType() != ScopeType_Global); + int count = scope->Count(); // Add argsPlaceHolder which includes same name args and destructuring patterns on parameters @@ -74,26 +68,29 @@ namespace Js ScopeInfo* scopeInfo = RecyclerNewPlusZ(scriptContext->GetRecycler(), count * sizeof(SymbolInfo), - ScopeInfo, parent ? parent->GetFunctionInfo() : nullptr, count); + ScopeInfo, scope->GetFunc()->byteCodeFunction->GetFunctionInfo(),/*parent ? parent->GetFunctionInfo() : nullptr,*/ count); + scopeInfo->SetScopeType(scope->GetScopeType()); scopeInfo->isDynamic = scope->GetIsDynamic(); scopeInfo->isObject = scope->GetIsObject(); scopeInfo->mustInstantiate = scope->GetMustInstantiate(); scopeInfo->isCached = (scope->GetFunc()->GetBodyScope() == scope) && scope->GetFunc()->GetHasCachedScope(); - scopeInfo->isGlobalEval = scope->GetScopeType() == ScopeType_GlobalEvalBlock; scopeInfo->canMergeWithBodyScope = scope->GetCanMergeWithBodyScope(); scopeInfo->hasLocalInClosure = scope->GetHasOwnLocalInClosure(); + - TRACE_BYTECODE(_u("\nSave ScopeInfo: %s parent: %s #symbols: %d %s\n"), - scope->GetFunc()->name, parent ? parent->GetDisplayName() : _u(""), count, + TRACE_BYTECODE(_u("\nSave ScopeInfo: %s #symbols: %d %s\n"), + scope->GetFunc()->name, /*parent ? parent->GetDisplayName() : _u(""),*/ count, scopeInfo->isObject ? _u("isObject") : _u("")); - MapSymbolData mapSymbolData = { byteCodeGenerator, scope->GetFunc(), 0 }; + MapSymbolData mapSymbolData = { /*byteCodeGenerator,*/ scope->GetFunc(), 0 }; scope->ForEachSymbol([&mapSymbolData, scopeInfo, scope](Symbol * sym) { Assert(scope == sym->GetScope()); scopeInfo->SaveSymbolInfo(sym, &mapSymbolData); }); + scope->SetScopeInfo(scopeInfo); + return scopeInfo; } @@ -107,75 +104,46 @@ namespace Js auto propertyName = scriptContext->GetPropertyName(symbols[i].propertyId); scriptContext->TrackPid(propertyName); } - if (funcExprScopeInfo) - { - funcExprScopeInfo->EnsurePidTracking(scriptContext); - } - if (paramScopeInfo) - { - paramScopeInfo->EnsurePidTracking(scriptContext); - } - } - - // - // Save needed scope info for a deferred child func. The scope info is empty and only links to parent. - // - void ScopeInfo::SaveParentScopeInfo(FuncInfo* parentFunc, FuncInfo* func) - { - Assert(func->IsDeferred() || func->byteCodeFunction->CanBeDeferred()); - - // Parent must be parsed - FunctionBody* parent = parentFunc->byteCodeFunction->GetFunctionBody(); - ParseableFunctionInfo* funcBody = func->byteCodeFunction; - - TRACE_BYTECODE(_u("\nSave ScopeInfo: %s parent: %s\n\n"), - funcBody->GetDisplayName(), parent->GetDisplayName()); - - ScopeInfo *info = FromParent(parent); - info->parentOnly = true; - funcBody->SetScopeInfo(info); } // - // Save scope info for an outer func which has deferred child. + // Save scope info for an individual scope and link it to its enclosing scope. // - void ScopeInfo::SaveScopeInfo(ByteCodeGenerator* byteCodeGenerator, FuncInfo* parentFunc, FuncInfo* func) + ScopeInfo * ScopeInfo::SaveScopeInfo(Scope * scope/*ByteCodeGenerator* byteCodeGenerator, FuncInfo* parentFunc, FuncInfo* func*/, ScriptContext * scriptContext) { - ParseableFunctionInfo* funcBody = func->byteCodeFunction; - - Assert((!func->IsGlobalFunction() || byteCodeGenerator->GetFlags() & fscrEvalCode) && - (func->HasDeferredChild() || func->HasRedeferrableChild() || funcBody->IsReparsed())); - - // If we are reparsing a deferred function, we already have correct "parent" info in - // funcBody->scopeInfo. parentFunc is the knopProg shell and should not be used in this - // case. We should use existing parent if available. - ParseableFunctionInfo * parent = funcBody->GetScopeInfo() ? - funcBody->GetScopeInfo()->GetParent() : - parentFunc ? parentFunc->byteCodeFunction : nullptr; + // Advance past scopes that will be excluded from the closure environment. (But note that we always want the body scope.) + while (scope && (!scope->GetMustInstantiate() && scope != scope->GetFunc()->GetBodyScope())) + { + scope = scope->GetEnclosingScope(); + } - ScopeInfo* funcExprScopeInfo = nullptr; - Scope* funcExprScope = func->GetFuncExprScope(); - if (funcExprScope && funcExprScope->GetMustInstantiate()) + // If we've exhausted the scope chain, we're done. + if (scope == nullptr || scope->GetScopeType() == ScopeType_Global) { - funcExprScopeInfo = FromScope(byteCodeGenerator, parent, funcExprScope, funcBody->GetScriptContext()); + return nullptr; } - Scope* bodyScope = func->IsGlobalFunction() ? func->GetGlobalEvalBlockScope() : func->GetBodyScope(); - ScopeInfo* paramScopeInfo = nullptr; - Scope* paramScope = func->GetParamScope(); - if (paramScope && !paramScope->GetCanMergeWithBodyScope()) + // If we've already collected info for this scope, we're done. + ScopeInfo * scopeInfo = scope->GetScopeInfo(); + if (scopeInfo != nullptr) { - paramScopeInfo = FromScope(byteCodeGenerator, parent, paramScope, funcBody->GetScriptContext()); + return scopeInfo; } - ScopeInfo* scopeInfo = FromScope(byteCodeGenerator, parent, bodyScope, funcBody->GetScriptContext()); - scopeInfo->SetFuncExprScopeInfo(funcExprScopeInfo); - scopeInfo->SetParamScopeInfo(paramScopeInfo); + // Do the work for this scope. + scopeInfo = ScopeInfo::SaveOneScopeInfo(scope, scriptContext); - funcBody->SetScopeInfo(scopeInfo); + // Link to the parent (if any). + scope = scope->GetEnclosingScope(); + if (scope) + { + scopeInfo->SetParentScopeInfo(ScopeInfo::SaveScopeInfo(scope, scriptContext)); + } + + return scopeInfo; } - void ScopeInfo::SaveScopeInfoForDeferParse(ByteCodeGenerator* byteCodeGenerator, FuncInfo* parentFunc, FuncInfo* funcInfo) + void ScopeInfo::SaveEnclosingScopeInfo(ByteCodeGenerator* byteCodeGenerator, /*FuncInfo* parentFunc,*/ FuncInfo* funcInfo) { // TODO: Not technically necessary, as we always do scope look up on eval if it is // not found in the scope chain, and block scopes are always objects in eval. @@ -185,116 +153,52 @@ namespace Js // enable defer parsing function that are in block scopes. if (funcInfo->byteCodeFunction && - funcInfo->byteCodeFunction->GetScopeInfo() != nullptr && - !funcInfo->byteCodeFunction->GetScopeInfo()->IsParentInfoOnly()) + funcInfo->byteCodeFunction->GetScopeInfo() != nullptr /*&& + !funcInfo->byteCodeFunction->GetScopeInfo()->IsParentInfoOnly()*/) { // No need to regenerate scope info if we re-compile an enclosing function return; } Scope* currentScope = byteCodeGenerator->GetCurrentScope(); - Assert(currentScope == funcInfo->GetBodyScope()); - if (funcInfo->HasDeferredChild() || - funcInfo->HasRedeferrableChild() || - (!funcInfo->IsGlobalFunction() && - funcInfo->byteCodeFunction && - funcInfo->byteCodeFunction->IsReparsed() && - funcInfo->byteCodeFunction->GetFunctionBody()->HasAllNonLocalReferenced())) - { - // When we reparse due to attach, we would need to capture all of them, since they were captured before going to debug mode. + Assert(currentScope->GetFunc() == funcInfo); - Js::ScopeInfo::SaveScopeInfo(byteCodeGenerator, parentFunc, funcInfo); + while (currentScope->GetFunc() == funcInfo) + { + currentScope = currentScope->GetEnclosingScope(); } - else if (funcInfo->IsDeferred() || funcInfo->IsRedeferrable()) + + ScopeInfo * scopeInfo = ScopeInfo::SaveScopeInfo(currentScope, byteCodeGenerator->GetScriptContext()); + if (scopeInfo != nullptr) { - // Don't need to remember the parent function if we have a global function - if (!parentFunc->IsGlobalFunction() || - ((byteCodeGenerator->GetFlags() & fscrEvalCode) && (parentFunc->HasDeferredChild() || parentFunc->HasRedeferrableChild()))) - { - // TODO: currently we only support defer nested function that is in function scope (no block scope, no with scope, etc.) -#if DBG - if (funcInfo->GetFuncExprScope() && funcInfo->GetFuncExprScope()->GetIsObject()) - { - if (funcInfo->paramScope && !funcInfo->paramScope->GetCanMergeWithBodyScope()) - { - Assert(currentScope->GetEnclosingScope()->GetEnclosingScope() == funcInfo->GetFuncExprScope()); - } - else - { - Assert(currentScope->GetEnclosingScope() == funcInfo->GetFuncExprScope() && - currentScope->GetEnclosingScope()->GetEnclosingScope() == - (parentFunc->IsGlobalFunction() && parentFunc->GetGlobalEvalBlockScope()->GetMustInstantiate() ? - parentFunc->GetGlobalEvalBlockScope() : parentFunc->GetBodyScope())); - } - } - else - { - if (currentScope->GetEnclosingScope() == parentFunc->GetParamScope()) - { - Assert(!parentFunc->GetParamScope()->GetCanMergeWithBodyScope()); - Assert(funcInfo->GetParamScope()->GetCanMergeWithBodyScope()); - } - else if (currentScope->GetEnclosingScope() == funcInfo->GetParamScope()) - { - Assert(!funcInfo->GetParamScope()->GetCanMergeWithBodyScope()); - } -#if 0 - else - { - Assert(currentScope->GetEnclosingScope() == - (parentFunc->IsGlobalFunction() && parentFunc->GetGlobalEvalBlockScope() && parentFunc->GetGlobalEvalBlockScope()->GetMustInstantiate() ? parentFunc->GetGlobalEvalBlockScope() : parentFunc->GetBodyScope())); - } -#endif - } -#endif - Js::ScopeInfo::SaveParentScopeInfo(parentFunc, funcInfo); - } + funcInfo->byteCodeFunction->SetScopeInfo(scopeInfo); } } // // Load persisted scope info. // - void ScopeInfo::GetScopeInfo(Parser *parser, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, Scope* scope) + void ScopeInfo::ExtractScopeInfo(Parser *parser, /*ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo,*/ Scope* scope) { ScriptContext* scriptContext; ArenaAllocator* alloc; // Load scope attributes and push onto scope stack. + scope->SetMustInstantiate(this->mustInstantiate); + scope->SetHasOwnLocalInClosure(this->hasLocalInClosure); scope->SetIsDynamic(this->isDynamic); if (this->isObject) { scope->SetIsObject(); } - scope->SetMustInstantiate(this->mustInstantiate); if (!this->GetCanMergeWithBodyScope()) { scope->SetCannotMergeWithBodyScope(); } - scope->SetHasOwnLocalInClosure(this->hasLocalInClosure); - if (parser) - { - scriptContext = parser->GetScriptContext(); - alloc = parser->GetAllocator(); - } - else - { - TRACE_BYTECODE(_u("\nRestore ScopeInfo: %s #symbols: %d %s\n"), - funcInfo->name, symbolCount, isObject ? _u("isObject") : _u("")); - - Assert(!this->isCached || scope == funcInfo->GetBodyScope()); - funcInfo->SetHasCachedScope(this->isCached); - byteCodeGenerator->PushScope(scope); - - // this->scope was created/saved during parsing and used by - // ByteCodeGenerator::RestoreScopeInfo. We no longer need it by now. - // Clear it to avoid GC false positive (arena memory later used by GC). - Assert(this->scope == scope); - this->scope = nullptr; + Assert(parser); - // The scope is already populated, so we're done. - return; - } + scriptContext = parser->GetScriptContext(); + alloc = parser->GetAllocator(); // Load scope symbols // On first access to the scopeinfo, replace the ID's with PropertyRecord*'s to save the dictionary lookup diff --git a/lib/Runtime/ByteCode/ScopeInfo.h b/lib/Runtime/ByteCode/ScopeInfo.h index ec502a99d5b..a3ae0b8f057 100644 --- a/lib/Runtime/ByteCode/ScopeInfo.h +++ b/lib/Runtime/ByteCode/ScopeInfo.h @@ -13,7 +13,6 @@ namespace Js { { struct MapSymbolData { - ByteCodeGenerator* byteCodeGenerator; FuncInfo* func; int nonScopeSymbolCount; }; @@ -34,43 +33,29 @@ namespace Js { }; private: - Field(FunctionInfo * const) parent; // link to parent function - Field(ScopeInfo*) funcExprScopeInfo; // optional func expr scope info - Field(ScopeInfo*) paramScopeInfo; // optional param scope info + Field(ScopeInfo *) parent; // link to parent scope info (if any) + Field(FunctionInfo * const) functionInfo;// link to function owning this scope Field(BYTE) isDynamic : 1; // isDynamic bit affects how deferredChild access global ref Field(BYTE) isObject : 1; // isObject bit affects how deferredChild access closure symbols Field(BYTE) mustInstantiate : 1; // the scope must be instantiated as an object/array Field(BYTE) isCached : 1; // indicates that local vars and functions are cached across invocations - Field(BYTE) isGlobalEval : 1; Field(BYTE) areNamesCached : 1; Field(BYTE) canMergeWithBodyScope : 1; Field(BYTE) hasLocalInClosure : 1; - Field(BYTE) parentOnly : 1; FieldNoBarrier(Scope *) scope; + Field(::ScopeType) scopeType; Field(int) scopeId; Field(int) symbolCount; // symbol count in this scope Field(SymbolInfo) symbols[]; // symbol PropertyIDs, index == sym.scopeSlot private: - ScopeInfo(FunctionInfo * parent, int symbolCount) - : parent(parent), funcExprScopeInfo(nullptr), paramScopeInfo(nullptr), symbolCount(symbolCount), scope(nullptr), areNamesCached(false), canMergeWithBodyScope(true), hasLocalInClosure(false), parentOnly(false) + ScopeInfo(FunctionInfo * function, int symbolCount) + : functionInfo(function), /*funcExprScopeInfo(nullptr), paramScopeInfo(nullptr),*/ symbolCount(symbolCount), parent(nullptr), scope(nullptr), areNamesCached(false), canMergeWithBodyScope(true), hasLocalInClosure(false)/*, parentOnly(false)*/ { } - bool IsParentInfoOnly() const { return parentOnly; } - - void SetFuncExprScopeInfo(ScopeInfo* funcExprScopeInfo) - { - this->funcExprScopeInfo = funcExprScopeInfo; - } - - void SetParamScopeInfo(ScopeInfo* paramScopeInfo) - { - this->paramScopeInfo = paramScopeInfo; - } - void SetSymbolId(int i, PropertyId propertyId) { Assert(!areNamesCached); @@ -179,30 +164,29 @@ namespace Js { void SaveSymbolInfo(Symbol* sym, MapSymbolData* mapSymbolData); - static ScopeInfo* FromParent(FunctionBody* parent); - static ScopeInfo* FromScope(ByteCodeGenerator* byteCodeGenerator, ParseableFunctionInfo* parent, Scope* scope, ScriptContext *scriptContext); - static void SaveParentScopeInfo(FuncInfo* parentFunc, FuncInfo* func); - static void SaveScopeInfo(ByteCodeGenerator* byteCodeGenerator, FuncInfo* parentFunc, FuncInfo* func); + static ScopeInfo* SaveScopeInfo(Scope * scope, ScriptContext * scriptContext); + static ScopeInfo* SaveOneScopeInfo(Scope * scope, ScriptContext * scriptContext); public: - ParseableFunctionInfo * GetParent() const + FunctionInfo * GetFunctionInfo() const { - return parent ? parent->GetParseableFunctionInfo() : nullptr; + return functionInfo; } - ScopeInfo* GetParentScopeInfo() const + ParseableFunctionInfo * GetParseableFunctionInfo() const { - return parent ? parent->GetParseableFunctionInfo()->GetScopeInfo() : nullptr; + return functionInfo ? functionInfo->GetParseableFunctionInfo() : nullptr; } - ScopeInfo* GetFuncExprScopeInfo() const + ScopeInfo* GetParentScopeInfo() const { - return funcExprScopeInfo; + return parent;//? parent->GetParseableFunctionInfo()->GetScopeInfo() : nullptr; } - ScopeInfo* GetParamScopeInfo() const + void SetParentScopeInfo(ScopeInfo * parent) { - return paramScopeInfo; + Assert(this->parent == nullptr); + this->parent = parent; } Scope * GetScope() const @@ -210,6 +194,21 @@ namespace Js { return scope; } + void SetScope(Scope * scope) + { + this->scope = scope; + } + + ::ScopeType GetScopeType() const + { + return scopeType; + } + + void SetScopeType(::ScopeType type) + { + this->scopeType = type; + } + void SetScopeId(int id) { this->scopeId = id; @@ -225,9 +224,14 @@ namespace Js { return symbolCount; } - bool IsGlobalEval() const + bool IsObject() const + { + return isObject; + } + + bool IsCached() const { - return isGlobalEval; + return isCached; } bool GetCanMergeWithBodyScope() const @@ -245,11 +249,11 @@ namespace Js { return hasLocalInClosure; } - static void SaveScopeInfoForDeferParse(ByteCodeGenerator* byteCodeGenerator, FuncInfo* parentFunc, FuncInfo* func); + static void SaveEnclosingScopeInfo(ByteCodeGenerator* byteCodeGenerator, /*FuncInfo* parentFunc,*/ FuncInfo* func); void EnsurePidTracking(ScriptContext* scriptContext); - void GetScopeInfo(Parser *parser, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, Scope* scope); + void ExtractScopeInfo(Parser *parser, /*ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo,*/ Scope* scope); // // Turn on capturesAll for a Scope temporarily. Restore old capturesAll when this object diff --git a/lib/Runtime/ByteCode/Symbol.cpp b/lib/Runtime/ByteCode/Symbol.cpp index db255ae08f4..e4e36aa147e 100644 --- a/lib/Runtime/ByteCode/Symbol.cpp +++ b/lib/Runtime/ByteCode/Symbol.cpp @@ -200,7 +200,7 @@ Symbol * Symbol::GetFuncScopeVarSym() const Scope* paramScope = parentFuncInfo->GetParamScope(); fncScopeSym = paramScope->FindLocalSymbol(this->GetName()); } - Assert(fncScopeSym); + // Parser should have added a fake var decl node for block scoped functions in non-strict mode // IsBlockVar() indicates a user let declared variable at function scope which // shadows the function's var binding, thus only emit the var binding init if diff --git a/test/DebuggerCommon/blockScopeFunctionDeclarationSlotArrayTest.js.dbg.baseline b/test/DebuggerCommon/blockScopeFunctionDeclarationSlotArrayTest.js.dbg.baseline index 6026b71ec25..bbba8616b71 100644 --- a/test/DebuggerCommon/blockScopeFunctionDeclarationSlotArrayTest.js.dbg.baseline +++ b/test/DebuggerCommon/blockScopeFunctionDeclarationSlotArrayTest.js.dbg.baseline @@ -21,6 +21,7 @@ "this": "Object {...}", "arguments": "Object {...}", "locals": { + "f": "function function f() { }", "g": "function ", "a": "number 1" } diff --git a/test/stackfunc/let_blockescape.deferparse.baseline b/test/stackfunc/let_blockescape.deferparse.baseline index 89fa736aacf..c2ee9665ac7 100644 --- a/test/stackfunc/let_blockescape.deferparse.baseline +++ b/test/stackfunc/let_blockescape.deferparse.baseline @@ -1,7 +1,7 @@ HasFuncDecl: test HasFuncDecl: test -HasMaybeEscapedUse: x HasMaybeEscapedNestedFunc (CrossScopeAssignment): test (function (#1.1), #2) HasFuncAssignment: f +HasMaybeEscapedUse: x test1str test2str diff --git a/test/stackfunc/let_stackfunc.deferparse.baseline b/test/stackfunc/let_stackfunc.deferparse.baseline index a6f27af9de5..c69367d32fa 100644 --- a/test/stackfunc/let_stackfunc.deferparse.baseline +++ b/test/stackfunc/let_stackfunc.deferparse.baseline @@ -2,17 +2,18 @@ HasFuncDecl: test HasFuncDecl: nested HasFuncDecl: test HasFuncDecl: nested -HasMaybeEscapedUse: x -HasMaybeEscapedUse: x HasFuncAssignment: f HasMaybeEscapedUse: i HasFuncAssignment: f2 HasFuncAssignment: f3 DoStackNestedFunc: test (function (#1.1), #2) HasFuncDecl: nested +HasMaybeEscapedUse: x test1glo yes test2glo yes +HasMaybeEscapedUse: x 0 +HasMaybeEscapedUse: x f30 undefined f3undefined diff --git a/test/stackfunc/with_namedfunc.deferparse.baseline b/test/stackfunc/with_namedfunc.deferparse.baseline index abb45dca7a8..1dab310b32e 100644 --- a/test/stackfunc/with_namedfunc.deferparse.baseline +++ b/test/stackfunc/with_namedfunc.deferparse.baseline @@ -3,5 +3,6 @@ HasFuncDecl: blah HasFuncDecl: test HasFuncDecl: blah HasMaybeEscapedNestedFunc (UnknownAssignment): test (function (#1.1), #2) +HasFuncDecl: blah test1 test2