| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeByteCodePch.h"
- FuncInfo::FuncInfo(
- const char16 *name,
- ArenaAllocator *alloc,
- Scope *paramScope,
- Scope *bodyScope,
- ParseNode *pnode,
- Js::ParseableFunctionInfo* byteCodeFunction)
- : alloc(alloc),
- varRegsCount(0),
- constRegsCount(InitialConstRegsCount),
- inArgsCount(0),
- innerScopeCount(0),
- currentInnerScopeIndex((uint)-1),
- firstTmpReg(Js::Constants::NoRegister),
- curTmpReg(Js::Constants::NoRegister),
- outArgsMaxDepth(0),
- outArgsCurrentExpr(0),
- #if DBG
- outArgsDepth(0),
- #endif
- name(name),
- nullConstantRegister(Js::Constants::NoRegister),
- undefinedConstantRegister(Js::Constants::NoRegister),
- trueConstantRegister(Js::Constants::NoRegister),
- falseConstantRegister(Js::Constants::NoRegister),
- thisPointerRegister(Js::Constants::NoRegister),
- superRegister(Js::Constants::NoRegister),
- superCtorRegister(Js::Constants::NoRegister),
- newTargetRegister(Js::Constants::NoRegister),
- envRegister(Js::Constants::NoRegister),
- frameObjRegister(Js::Constants::NoRegister),
- frameSlotsRegister(Js::Constants::NoRegister),
- paramSlotsRegister(Js::Constants::NoRegister),
- frameDisplayRegister(Js::Constants::NoRegister),
- funcObjRegister(Js::Constants::NoRegister),
- localClosureReg(Js::Constants::NoRegister),
- yieldRegister(Js::Constants::NoRegister),
- paramScope(paramScope),
- bodyScope(bodyScope),
- funcExprScope(nullptr),
- root(pnode),
- capturedSyms(nullptr),
- capturedSymMap(nullptr),
- currentChildFunction(nullptr),
- currentChildScope(nullptr),
- callsEval(false),
- childCallsEval(false),
- hasArguments(false),
- hasHeapArguments(false),
- isTopLevelEventHandler(false),
- hasLocalInClosure(false),
- hasClosureReference(false),
- hasGlobalReference(false),
- hasCachedScope(false),
- funcExprNameReference(false),
- applyEnclosesArgs(false),
- escapes(false),
- hasDeferredChild(false),
- hasRedeferrableChild(false),
- childHasWith(false),
- hasLoop(false),
- hasEscapedUseNestedFunc(false),
- needEnvRegister(false),
- hasCapturedThis(false),
- isBodyAndParamScopeMerged(true),
- #if DBG
- isReused(false),
- #endif
- staticFuncId(-1),
- inlineCacheMap(nullptr),
- slotProfileIdMap(alloc),
- argsPlaceHolderSlotCount(0),
- thisScopeSlot(Js::Constants::NoProperty),
- superScopeSlot(Js::Constants::NoProperty),
- superCtorScopeSlot(Js::Constants::NoProperty),
- newTargetScopeSlot(Js::Constants::NoProperty),
- isThisLexicallyCaptured(false),
- isSuperLexicallyCaptured(false),
- isSuperCtorLexicallyCaptured(false),
- isNewTargetLexicallyCaptured(false),
- inlineCacheCount(0),
- rootObjectLoadInlineCacheCount(0),
- rootObjectLoadMethodInlineCacheCount(0),
- rootObjectStoreInlineCacheCount(0),
- isInstInlineCacheCount(0),
- referencedPropertyIdCount(0),
- argumentsSymbol(nullptr),
- nonUserNonTempRegistersToInitialize(alloc),
- constantToRegister(alloc, 17),
- stringToRegister(alloc, 17),
- doubleConstantToRegister(alloc, 17),
- stringTemplateCallsiteRegisterMap(alloc, 17),
- targetStatements(alloc),
- nextForInLoopLevel(0),
- maxForInLoopLevel(0)
- {
- this->byteCodeFunction = byteCodeFunction;
- bodyScope->SetFunc(this);
- if (paramScope != nullptr)
- {
- paramScope->SetFunc(this);
- }
- if (pnode && pnode->sxFnc.NestedFuncEscapes())
- {
- this->SetHasMaybeEscapedNestedFunc(DebugOnly(_u("Child")));
- }
- }
- bool FuncInfo::IsGlobalFunction() const
- {
- return root && root->nop == knopProg;
- }
- bool FuncInfo::IsDeferred() const
- {
- return root && root->sxFnc.pnodeBody == nullptr;
- }
- bool FuncInfo::IsRedeferrable() const
- {
- return byteCodeFunction && byteCodeFunction->CanBeDeferred();
- }
- BOOL FuncInfo::HasSuperReference() const
- {
- return root->sxFnc.HasSuperReference();
- }
- BOOL FuncInfo::HasDirectSuper() const
- {
- return root->sxFnc.HasDirectSuper();
- }
- BOOL FuncInfo::IsClassMember() const
- {
- return root->sxFnc.IsClassMember();
- }
- BOOL FuncInfo::IsLambda() const
- {
- return root->sxFnc.IsLambda();
- }
- BOOL FuncInfo::IsClassConstructor() const
- {
- return root->sxFnc.IsClassConstructor();
- }
- BOOL FuncInfo::IsBaseClassConstructor() const
- {
- return root->sxFnc.IsBaseClassConstructor();
- }
- void FuncInfo::EnsureThisScopeSlot()
- {
- if (this->thisScopeSlot == Js::Constants::NoProperty)
- {
- // In case of split scope param and body has separate closures. So we have to use different scope slots for them.
- Scope* scope = this->IsBodyAndParamScopeMerged() ? this->bodyScope : this->paramScope;
- Scope* currentScope = scope->IsGlobalEvalBlockScope() ? this->GetGlobalEvalBlockScope() : scope;
- this->thisScopeSlot = currentScope->AddScopeSlot();
- }
- }
- void FuncInfo::EnsureSuperScopeSlot()
- {
- if (this->superScopeSlot == Js::Constants::NoProperty)
- {
- // In case of split scope param and body has separate closures. So we have to use different scope slots for them.
- Scope* scope = this->IsBodyAndParamScopeMerged() ? this->bodyScope : this->paramScope;
- this->superScopeSlot = scope->AddScopeSlot();
- }
- }
- void FuncInfo::EnsureSuperCtorScopeSlot()
- {
- if (this->superCtorScopeSlot == Js::Constants::NoProperty)
- {
- // In case of split scope param and body has separate closures. So we have to use different scope slots for them.
- Scope* scope = this->IsBodyAndParamScopeMerged() ? this->bodyScope : this->paramScope;
- this->superCtorScopeSlot = scope->AddScopeSlot();
- }
- }
- void FuncInfo::EnsureNewTargetScopeSlot()
- {
- if (this->newTargetScopeSlot == Js::Constants::NoProperty)
- {
- // In case of split scope param and body has separate closures. So we have to use different scope slots for them.
- Scope* scope = this->IsBodyAndParamScopeMerged() ? this->bodyScope : this->paramScope;
- this->newTargetScopeSlot = scope->AddScopeSlot();
- }
- }
- Scope *
- FuncInfo::GetGlobalBlockScope() const
- {
- Assert(this->IsGlobalFunction());
- Scope * scope = this->root->sxFnc.pnodeScopes->sxBlock.scope;
- Assert(scope == nullptr || scope == this->GetBodyScope() || scope->GetEnclosingScope() == this->GetBodyScope());
- return scope;
- }
- Scope * FuncInfo::GetGlobalEvalBlockScope() const
- {
- Scope * globalEvalBlockScope = this->GetGlobalBlockScope();
- Assert(globalEvalBlockScope->GetEnclosingScope() == this->GetBodyScope());
- Assert(globalEvalBlockScope->GetScopeType() == ScopeType_GlobalEvalBlock);
- return globalEvalBlockScope;
- }
- uint FuncInfo::FindOrAddReferencedPropertyId(Js::PropertyId propertyId)
- {
- Assert(propertyId != Js::Constants::NoProperty);
- Assert(referencedPropertyIdToMapIndex != nullptr);
- if (propertyId < TotalNumberOfBuiltInProperties)
- {
- return propertyId;
- }
- uint index;
- if (!referencedPropertyIdToMapIndex->TryGetValue(propertyId, &index))
- {
- index = this->NewReferencedPropertyId();
- referencedPropertyIdToMapIndex->Add(propertyId, index);
- }
- return index + TotalNumberOfBuiltInProperties;
- }
- uint FuncInfo::FindOrAddRootObjectInlineCacheId(Js::PropertyId propertyId, bool isLoadMethod, bool isStore)
- {
- Assert(propertyId != Js::Constants::NoProperty);
- Assert(!isLoadMethod || !isStore);
- uint cacheId;
- RootObjectInlineCacheIdMap * idMap = isStore ? rootObjectStoreInlineCacheMap : isLoadMethod ? rootObjectLoadMethodInlineCacheMap : rootObjectLoadInlineCacheMap;
- if (!idMap->TryGetValue(propertyId, &cacheId))
- {
- cacheId = isStore ? this->NewRootObjectStoreInlineCache() : isLoadMethod ? this->NewRootObjectLoadMethodInlineCache() : this->NewRootObjectLoadInlineCache();
- idMap->Add(propertyId, cacheId);
- }
- return cacheId;
- }
- #if DBG_DUMP
- void FuncInfo::Dump()
- {
- Output::Print(_u("FuncInfo: CallsEval:%s ChildCallsEval:%s HasArguments:%s HasHeapArguments:%s\n"),
- IsTrueOrFalse(this->GetCallsEval()),
- IsTrueOrFalse(this->GetChildCallsEval()),
- IsTrueOrFalse(this->GetHasArguments()),
- IsTrueOrFalse(this->GetHasHeapArguments()));
- }
- #endif
- Js::RegSlot FuncInfo::AcquireLoc(ParseNode *pnode)
- {
- // Assign a new temp pseudo-register to this expression.
- if (pnode->location == Js::Constants::NoRegister)
- {
- pnode->location = this->AcquireTmpRegister();
- }
- return pnode->location;
- }
- Js::RegSlot FuncInfo::AcquireTmpRegister()
- {
- Assert(this->firstTmpReg != Js::Constants::NoRegister);
- // Allocate a new temp pseudo-register, increasing the locals count if necessary.
- Assert(this->curTmpReg <= this->varRegsCount && this->curTmpReg >= this->firstTmpReg);
- Js::RegSlot tmpReg = this->curTmpReg;
- UInt32Math::Inc(this->curTmpReg);
- if (this->curTmpReg > this->varRegsCount)
- {
- this->varRegsCount = this->curTmpReg;
- }
- return tmpReg;
- }
- void FuncInfo::ReleaseLoc(ParseNode *pnode)
- {
- // Release the temp assigned to this expression so it can be re-used.
- if (pnode && pnode->location != Js::Constants::NoRegister)
- {
- this->ReleaseTmpRegister(pnode->location);
- }
- }
- void FuncInfo::ReleaseLoad(ParseNode *pnode)
- {
- // Release any temp register(s) acquired by an EmitLoad.
- switch (pnode->nop)
- {
- case knopDot:
- case knopIndex:
- case knopCall:
- this->ReleaseReference(pnode);
- break;
- }
- this->ReleaseLoc(pnode);
- }
- void FuncInfo::ReleaseReference(ParseNode *pnode)
- {
- // Release any temp(s) assigned to this reference expression so they can be re-used.
- switch (pnode->nop)
- {
- case knopDot:
- this->ReleaseLoc(pnode->sxBin.pnode1);
- break;
- case knopIndex:
- this->ReleaseLoc(pnode->sxBin.pnode2);
- this->ReleaseLoc(pnode->sxBin.pnode1);
- break;
- case knopName:
- // Do nothing (see EmitReference)
- break;
- case knopCall:
- case knopNew:
- // For call/new, we have to release the ArgOut register(s) in reverse order,
- // but we have the args in a singly linked list.
- // Fortunately, we know that the set we have to release is sequential.
- // So find the endpoints of the list and release them in descending order.
- if (pnode->sxCall.pnodeArgs)
- {
- ParseNode *pnodeArg = pnode->sxCall.pnodeArgs;
- Js::RegSlot firstArg = Js::Constants::NoRegister;
- Js::RegSlot lastArg = Js::Constants::NoRegister;
- if (pnodeArg->nop == knopList)
- {
- do
- {
- if (this->IsTmpReg(pnodeArg->sxBin.pnode1->location))
- {
- lastArg = pnodeArg->sxBin.pnode1->location;
- if (firstArg == Js::Constants::NoRegister)
- {
- firstArg = lastArg;
- }
- }
- pnodeArg = pnodeArg->sxBin.pnode2;
- }
- while (pnodeArg->nop == knopList);
- }
- if (this->IsTmpReg(pnodeArg->location))
- {
- lastArg = pnodeArg->location;
- if (firstArg == Js::Constants::NoRegister)
- {
- // Just one: first and last point to the same node.
- firstArg = lastArg;
- }
- }
- if (lastArg != Js::Constants::NoRegister)
- {
- Assert(firstArg != Js::Constants::NoRegister);
- Assert(lastArg >= firstArg);
- do
- {
- // Walk down from last to first.
- this->ReleaseTmpRegister(lastArg);
- } while (lastArg-- > firstArg); // these are unsigned, so (--lastArg >= firstArg) will cause an infinite loop if firstArg is 0 (although that shouldn't happen)
- }
- }
- // Now release the call target.
- switch (pnode->sxCall.pnodeTarget->nop)
- {
- case knopDot:
- case knopIndex:
- this->ReleaseReference(pnode->sxCall.pnodeTarget);
- this->ReleaseLoc(pnode->sxCall.pnodeTarget);
- break;
- default:
- this->ReleaseLoad(pnode->sxCall.pnodeTarget);
- break;
- }
- break;
- default:
- this->ReleaseLoc(pnode);
- break;
- }
- }
- void FuncInfo::ReleaseTmpRegister(Js::RegSlot tmpReg)
- {
- // Put this reg back on top of the temp stack (if it's a temp).
- Assert(tmpReg != Js::Constants::NoRegister);
- if (this->IsTmpReg(tmpReg))
- {
- Assert(tmpReg == this->curTmpReg - 1);
- this->curTmpReg--;
- }
- }
- Js::RegSlot FuncInfo::InnerScopeToRegSlot(Scope *scope) const
- {
- Js::RegSlot reg = FirstInnerScopeReg();
- Assert(reg != Js::Constants::NoRegister);
- uint32 index = scope->GetInnerScopeIndex();
- return reg + index;
- }
- Js::RegSlot FuncInfo::FirstInnerScopeReg() const
- {
- // FunctionBody stores this as a mapped reg. Callers of this function want the pre-mapped value.
- Js::RegSlot reg = this->GetParsedFunctionBody()->GetFirstInnerScopeRegister();
- Assert(reg != Js::Constants::NoRegister);
- return reg - this->constRegsCount;
- }
- void FuncInfo::SetFirstInnerScopeReg(Js::RegSlot reg)
- {
- // Just forward to the FunctionBody.
- this->GetParsedFunctionBody()->MapAndSetFirstInnerScopeRegister(reg);
- }
- void FuncInfo::AddCapturedSym(Symbol *sym)
- {
- if (this->capturedSyms == nullptr)
- {
- this->capturedSyms = Anew(alloc, SymbolTable, alloc);
- }
- this->capturedSyms->AddNew(sym);
- }
- void FuncInfo::OnStartVisitFunction(ParseNode *pnodeFnc)
- {
- Assert(pnodeFnc->nop == knopFncDecl);
- Assert(this->GetCurrentChildFunction() == nullptr);
- this->SetCurrentChildFunction(pnodeFnc->sxFnc.funcInfo);
- }
- void FuncInfo::OnEndVisitFunction(ParseNode *pnodeFnc)
- {
- Assert(pnodeFnc->nop == knopFncDecl);
- Assert(this->GetCurrentChildFunction() == pnodeFnc->sxFnc.funcInfo);
- pnodeFnc->sxFnc.funcInfo->SetCurrentChildScope(nullptr);
- this->SetCurrentChildFunction(nullptr);
- }
- void FuncInfo::OnStartVisitScope(Scope *scope, bool *pisMergedScope)
- {
- *pisMergedScope = false;
- if (scope == nullptr)
- {
- return;
- }
- Scope* childScope = this->GetCurrentChildScope();
- if (childScope)
- {
- if (scope->GetScopeType() == ScopeType_Parameter)
- {
- Assert(childScope->GetEnclosingScope() == scope);
- }
- else if (childScope->GetScopeType() == ScopeType_Parameter
- && childScope->GetFunc()->IsBodyAndParamScopeMerged()
- && scope->GetScopeType() == ScopeType_Block)
- {
- // If param and body are merged then the class declaration in param scope will have body as the parent
- *pisMergedScope = true;
- Assert(childScope == scope->GetEnclosingScope()->GetEnclosingScope());
- }
- else
- {
- Assert(childScope == scope->GetEnclosingScope());
- }
- }
- this->SetCurrentChildScope(scope);
- return;
- }
- void FuncInfo::OnEndVisitScope(Scope *scope, bool isMergedScope)
- {
- if (scope == nullptr)
- {
- return;
- }
- Assert(this->GetCurrentChildScope() == scope || (scope->GetScopeType() == ScopeType_Parameter && this->GetParamScope() == scope));
- this->SetCurrentChildScope(isMergedScope ? scope->GetEnclosingScope()->GetEnclosingScope() : scope->GetEnclosingScope());
- }
- CapturedSymMap *FuncInfo::EnsureCapturedSymMap()
- {
- if (this->capturedSymMap == nullptr)
- {
- this->capturedSymMap = Anew(alloc, CapturedSymMap, alloc);
- }
- return this->capturedSymMap;
- }
- void FuncInfo::SetHasMaybeEscapedNestedFunc(DebugOnly(char16 const * reason))
- {
- if (PHASE_TESTTRACE(Js::StackFuncPhase, this->byteCodeFunction) && !hasEscapedUseNestedFunc)
- {
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- char16 const * r = _u("");
- DebugOnly(r = reason);
- Output::Print(_u("HasMaybeEscapedNestedFunc (%s): %s (function %s)\n"),
- r,
- this->byteCodeFunction->GetDisplayName(),
- this->byteCodeFunction->GetDebugNumberSet(debugStringBuffer));
- Output::Flush();
- }
- hasEscapedUseNestedFunc = true;
- }
- uint FuncInfo::AcquireInnerScopeIndex()
- {
- uint index = this->currentInnerScopeIndex;
- if (index == (uint)-1)
- {
- index = 0;
- }
- else
- {
- index++;
- if (index == (uint)-1)
- {
- Js::Throw::OutOfMemory();
- }
- }
- if (index == this->innerScopeCount)
- {
- this->innerScopeCount = index + 1;
- }
- this->currentInnerScopeIndex = index;
- return index;
- }
- void FuncInfo::ReleaseInnerScopeIndex()
- {
- uint index = this->currentInnerScopeIndex;
- Assert(index != (uint)-1);
- if (index == 0)
- {
- index = (uint)-1;
- }
- else
- {
- index--;
- }
- this->currentInnerScopeIndex = index;
- }
|