| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLibraryPch.h"
- using namespace Js;
- ScriptFunctionBase::ScriptFunctionBase(DynamicType * type) :
- JavascriptFunction(type)
- {}
- ScriptFunctionBase::ScriptFunctionBase(DynamicType * type, FunctionInfo * functionInfo) :
- JavascriptFunction(type, functionInfo)
- {}
- template <> bool Js::VarIsImpl<ScriptFunctionBase>(RecyclableObject* obj)
- {
- if (VarIs<JavascriptFunction>(obj))
- {
- JavascriptFunction *function = UnsafeVarTo<JavascriptFunction>(obj);
- return ScriptFunction::Test(function) || JavascriptGeneratorFunction::Test(function)
- || JavascriptAsyncFunction::Test(function) || JavascriptAsyncGeneratorFunction::Test(function);
- }
- return false;
- }
- ScriptFunction::ScriptFunction(FunctionProxy * proxy, ScriptFunctionType* type)
- : ScriptFunctionBase(type, proxy->GetFunctionInfo()),
- environment((FrameDisplay*)&NullFrameDisplay), cachedScopeObj(nullptr),
- hasInlineCaches(false)
- {
- Assert(proxy->GetFunctionInfo()->GetFunctionProxy() == proxy);
- Assert(proxy->GetDeferredPrototypeType() == type || proxy->GetUndeferredFunctionType() == type);
- DebugOnly(VerifyEntryPoint());
- #if ENABLE_NATIVE_CODEGEN
- if (!proxy->IsDeferred())
- {
- FunctionBody* body = proxy->GetFunctionBody();
- if(!body->GetNativeEntryPointUsed() &&
- body->GetDefaultFunctionEntryPointInfo()->IsCodeGenDone())
- {
- MemoryBarrier();
- #ifdef BGJIT_STATS
- type->GetScriptContext()->jitCodeUsed += body->GetByteCodeCount();
- type->GetScriptContext()->funcJitCodeUsed++;
- #endif
- body->SetNativeEntryPointUsed(true);
- }
- }
- #endif
- }
- ScriptFunction * ScriptFunction::OP_NewScFunc(FrameDisplay *environment, FunctionInfoPtrPtr infoRef)
- {
- AssertMsg(infoRef!= nullptr, "BYTE-CODE VERIFY: Must specify a valid function to create");
- FunctionProxy* functionProxy = (*infoRef)->GetFunctionProxy();
- AssertMsg(functionProxy!= nullptr, "BYTE-CODE VERIFY: Must specify a valid function to create");
- ScriptContext* scriptContext = functionProxy->GetScriptContext();
- JIT_HELPER_NOT_REENTRANT_HEADER(ScrFunc_OP_NewScFunc, reentrancylock, scriptContext->GetThreadContext());
- ScriptFunction * pfuncScript = nullptr;
- if (functionProxy->IsFunctionBody() && functionProxy->GetFunctionBody()->GetInlineCachesOnFunctionObject())
- {
- FunctionBody * functionBody = functionProxy->GetFunctionBody();
- if (functionBody->GetIsFirstFunctionObject())
- {
- functionBody->SetIsNotFirstFunctionObject();
- }
- else
- {
- ScriptFunctionWithInlineCache* pfuncScriptWithInlineCache = scriptContext->GetLibrary()->CreateScriptFunctionWithInlineCache(functionProxy);
- // allocate inline cache for this function object
- pfuncScriptWithInlineCache->CreateInlineCache();
- Assert(functionBody->GetInlineCacheCount() + functionBody->GetIsInstInlineCacheCount());
- if (PHASE_TRACE1(Js::ScriptFunctionWithInlineCachePhase))
- {
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("Function object with inline cache: function number: (%s)\tfunction name: %s\n"),
- functionBody->GetDebugNumberSet(debugStringBuffer), functionBody->GetDisplayName());
- Output::Flush();
- }
- pfuncScript = pfuncScriptWithInlineCache;
- }
- }
- if (pfuncScript == nullptr)
- {
- pfuncScript = scriptContext->GetLibrary()->CreateScriptFunction(functionProxy);
- }
- pfuncScript->SetEnvironment(environment);
- ScriptFunctionType *scFuncType = functionProxy->GetUndeferredFunctionType();
- if (scFuncType && pfuncScript->GetType() == functionProxy->GetDeferredPrototypeType())
- {
- pfuncScript->GetTypeHandler()->EnsureObjectReady(pfuncScript);
- }
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_FUNCTION(pfuncScript, EtwTrace::GetFunctionId(functionProxy)));
- return pfuncScript;
- JIT_HELPER_END(ScrFunc_OP_NewScFunc);
- }
- ScriptFunction * ScriptFunction::OP_NewScFuncHomeObj(FrameDisplay *environment, FunctionInfoPtrPtr infoRef, Var homeObj)
- {
- Assert(homeObj != nullptr);
- Assert((*infoRef)->GetFunctionProxy()->GetFunctionInfo()->HasHomeObj());
- JIT_HELPER_NOT_REENTRANT_HEADER(ScrFunc_OP_NewScFuncHomeObj, reentrancylock, (*infoRef)->GetFunctionProxy()->GetScriptContext()->GetThreadContext());
- ScriptFunction* scriptFunc = ScriptFunction::OP_NewScFunc(environment, infoRef);
- scriptFunc->SetHomeObj(homeObj);
- // After setting homeobject we need to set the name if the object is ready.
- if ((*infoRef)->GetFunctionProxy()->GetUndeferredFunctionType())
- {
- if (!scriptFunc->IsAnonymousFunction() &&
- !scriptFunc->GetFunctionInfo()->IsClassConstructor() &&
- !scriptFunc->GetFunctionProxy()->EnsureDeserialized()->GetIsStaticNameFunction())
- {
- JavascriptString * functionName = scriptFunc->GetDisplayNameImpl();
- scriptFunc->SetPropertyWithAttributes(PropertyIds::name, functionName, PropertyConfigurable, nullptr);
- }
- }
- return scriptFunc;
- JIT_HELPER_END(ScrFunc_OP_NewScFuncHomeObj);
- }
- ScriptFunction * ScriptFunction::OP_NewClassConstructor(FrameDisplay *environment, FunctionInfoPtrPtr infoRef, Var homeObj, RecyclableObject *constructorParent)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(ScrFunc_OP_NewClassConstructor, reentrancylock, (*infoRef)->GetFunctionProxy()->GetScriptContext()->GetThreadContext());
- FunctionProxy * proxy = (*infoRef)->GetFunctionProxy();
- if (proxy->GetUndeferredFunctionType() == nullptr)
- {
- DynamicTypeHandler * typeHandler = proxy->GetIsAnonymousFunction()
- ? proxy->GetScriptContext()->GetLibrary()->AnonymousClassConstructorTypeHandler()
- : proxy->GetScriptContext()->GetLibrary()->ClassConstructorTypeHandler();
- ScriptFunctionType * newType = ScriptFunctionType::New(proxy, typeHandler, constructorParent, true);
- proxy->SetUndeferredFunctionType(newType);
- }
- ScriptFunction * scriptFunction = OP_NewScFuncHomeObj(environment, infoRef, homeObj);
- if (scriptFunction->GetPrototype() != constructorParent)
- {
- scriptFunction->SetPrototype(constructorParent);
- }
- Var length = TaggedInt::ToVarUnchecked((*infoRef)->GetFunctionProxy()->EnsureDeserialized()->GetReportedInParamsCount() - 1);
- scriptFunction->SetSlot(SetSlotArguments(Constants::NoProperty, 1, length));
- if (!scriptFunction->IsAnonymousFunction() && !scriptFunction->GetFunctionInfo()->HasComputedName())
- {
- JavascriptString * functionName = nullptr;
- bool result = scriptFunction->GetFunctionName(&functionName);
- Assert(result);
- scriptFunction->SetSlot(SetSlotArguments(Constants::NoProperty, 2, functionName));
- }
- return scriptFunction;
- JIT_HELPER_END(ScrFunc_OP_NewClassConstructor);
- }
- void ScriptFunction::SetEnvironment(FrameDisplay * environment)
- {
- //Assert(ThreadContext::IsOnStack(this) || !ThreadContext::IsOnStack(environment));
- this->environment = environment;
- }
- void ScriptFunction::InvalidateCachedScopeChain()
- {
- // Note: Currently this helper assumes that we're in an eval-class case
- // where all the contents of the closure environment are dynamic objects.
- // Invalidating scopes that are raw slot arrays, etc., will have to be done
- // directly in byte code.
- // A function nested within this one has escaped.
- // Invalidate our own cached scope object, and walk the closure environment
- // doing this same.
- if (this->cachedScopeObj)
- {
- this->cachedScopeObj->InvalidateCachedScope();
- }
- FrameDisplay *pDisplay = this->environment;
- uint length = (uint)pDisplay->GetLength();
- for (uint i = 0; i < length; i++)
- {
- Var scope = pDisplay->GetItem(i);
- RecyclableObject *scopeObj = VarTo<RecyclableObject>(scope);
- scopeObj->InvalidateCachedScope();
- }
- }
- ProxyEntryPointInfo * ScriptFunction::GetEntryPointInfo() const
- {
- return this->GetScriptFunctionType()->GetEntryPointInfo();
- }
- ScriptFunctionType * ScriptFunction::GetScriptFunctionType() const
- {
- return (ScriptFunctionType *)GetDynamicType();
- }
- ScriptFunctionType * ScriptFunction::DuplicateType()
- {
- ScriptFunctionType* type = RecyclerNew(this->GetScriptContext()->GetRecycler(),
- ScriptFunctionType, this->GetScriptFunctionType());
- this->GetFunctionProxy()->RegisterFunctionObjectType(type);
- return type;
- }
- void ScriptFunction::PrepareForConversionToNonPathType()
- {
- // We have a path type handler that is currently responsible for holding some number of entry point infos alive.
- // The last one will be copied on to the new dictionary type handler, but if any previous instances in the path
- // are holding different entry point infos, those need to be copied to somewhere safe.
- // The number of entry points is likely low compared to length of path, so iterate those instead.
- ProxyEntryPointInfo* entryPointInfo = this->GetScriptFunctionType()->GetEntryPointInfo();
- this->GetFunctionProxy()->MapFunctionObjectTypes([&](ScriptFunctionType* functionType)
- {
- CopyEntryPointInfoToThreadContextIfNecessary(functionType->GetEntryPointInfo(), entryPointInfo);
- });
- }
- void ScriptFunction::ReplaceTypeWithPredecessorType(DynamicType * previousType)
- {
- ProxyEntryPointInfo* oldEntryPointInfo = this->GetScriptFunctionType()->GetEntryPointInfo();
- __super::ReplaceTypeWithPredecessorType(previousType);
- ProxyEntryPointInfo* newEntryPointInfo = this->GetScriptFunctionType()->GetEntryPointInfo();
- CopyEntryPointInfoToThreadContextIfNecessary(oldEntryPointInfo, newEntryPointInfo);
- }
- bool ScriptFunction::HasFunctionBody()
- {
- // for asmjs we want to first check if the FunctionObject has a function body. Check that the function is not deferred
- return !this->GetFunctionInfo()->IsDeferredParseFunction() && !this->GetFunctionInfo()->IsDeferredDeserializeFunction() && GetParseableFunctionInfo()->IsFunctionParsed();
- }
- void ScriptFunction::ChangeEntryPoint(ProxyEntryPointInfo* entryPointInfo, JavascriptMethod entryPoint)
- {
- Assert(this->GetTypeId() == TypeIds_Function);
- #if ENABLE_NATIVE_CODEGEN
- Assert(!IsCrossSiteObject() || entryPoint != (Js::JavascriptMethod)checkCodeGenThunk);
- #endif
- Assert((entryPointInfo != nullptr && this->GetFunctionProxy() != nullptr));
- bool isAsmJS = HasFunctionBody() && this->GetFunctionBody()->GetIsAsmjsMode();
- this->GetScriptFunctionType()->ChangeEntryPoint(entryPointInfo, entryPoint, isAsmJS);
- }
- void ScriptFunction::CopyEntryPointInfoToThreadContextIfNecessary(ProxyEntryPointInfo* oldEntryPointInfo, ProxyEntryPointInfo* newEntryPointInfo)
- {
- if (oldEntryPointInfo
- && oldEntryPointInfo != newEntryPointInfo
- && oldEntryPointInfo->SupportsExpiration())
- {
- // The old entry point could be executing so we need root it to make sure
- // it isn't prematurely collected. The rooting is done by queuing it up on the threadContext
- ThreadContext* threadContext = ThreadContext::GetContextForCurrentThread();
- threadContext->QueueFreeOldEntryPointInfoIfInScript((FunctionEntryPointInfo*)oldEntryPointInfo);
- }
- }
- FunctionProxy * ScriptFunction::GetFunctionProxy() const
- {
- Assert(this->functionInfo->HasBody());
- return this->functionInfo->GetFunctionProxy();
- }
- JavascriptMethod ScriptFunction::UpdateUndeferredBody(FunctionBody* newFunctionInfo)
- {
- // Update deferred parsed/serialized function to the real function body
- Assert(this->functionInfo->HasBody());
- Assert(this->functionInfo == newFunctionInfo->GetFunctionInfo());
- Assert(this->functionInfo->GetFunctionBody() == newFunctionInfo);
- Assert(!newFunctionInfo->IsDeferred());
- JavascriptMethod directEntryPoint = newFunctionInfo->GetDirectEntryPoint(newFunctionInfo->GetDefaultEntryPointInfo());
- #if defined(ENABLE_SCRIPT_PROFILING) || defined(ENABLE_SCRIPT_DEBUGGING)
- Assert(directEntryPoint != DefaultDeferredParsingThunk
- && directEntryPoint != ProfileDeferredParsingThunk);
- #else
- Assert(directEntryPoint != DefaultDeferredParsingThunk);
- #endif
- Js::FunctionEntryPointInfo* defaultEntryPointInfo = newFunctionInfo->GetDefaultFunctionEntryPointInfo();
- JavascriptMethod thunkEntryPoint = this->UpdateThunkEntryPoint(defaultEntryPointInfo, directEntryPoint);
- this->GetScriptFunctionType()->SetEntryPointInfo(defaultEntryPointInfo);
- return thunkEntryPoint;
- }
- JavascriptMethod ScriptFunction::UpdateThunkEntryPoint(FunctionEntryPointInfo* entryPointInfo, JavascriptMethod entryPoint)
- {
- this->ChangeEntryPoint(entryPointInfo, entryPoint);
- if (!CrossSite::IsThunk(this->GetEntryPoint()))
- {
- return entryPoint;
- }
- // We already pass through the cross site thunk, which would have called the profile thunk already if necessary
- // So just call the original entry point if our direct entry is the profile entry thunk
- // Otherwise, call the directEntryPoint which may have additional processing to do (e.g. ensure dynamic profile)
- Assert(this->IsCrossSiteObject());
- if (entryPoint != ProfileEntryThunk)
- {
- return entryPoint;
- }
- // Based on the comment below, this shouldn't be a defer deserialization function as it would have a deferred thunk
- FunctionBody * functionBody = this->GetFunctionBody();
- // The original entry point should be an interpreter thunk or the native entry point;
- Assert(functionBody->IsInterpreterThunk() || functionBody->IsNativeOriginalEntryPoint());
- return functionBody->GetOriginalEntryPoint();
- }
- bool ScriptFunction::IsNewEntryPointAvailable()
- {
- Js::FunctionEntryPointInfo *const defaultEntryPointInfo = this->GetFunctionBody()->GetDefaultFunctionEntryPointInfo();
- JavascriptMethod defaultEntryPoint = this->GetFunctionBody()->GetDirectEntryPoint(defaultEntryPointInfo);
- return this->GetEntryPoint() != defaultEntryPoint;
- }
- Var ScriptFunction::GetSourceString() const
- {
- return this->GetFunctionProxy()->EnsureDeserialized()->GetCachedSourceString();
- }
- JavascriptString * ScriptFunction::EnsureSourceString()
- {
- // The function may be defer serialize, need to be deserialized
- FunctionProxy* proxy = this->GetFunctionProxy();
- ParseableFunctionInfo * pFuncBody = proxy->EnsureDeserialized();
- JavascriptString * cachedSourceString = pFuncBody->GetCachedSourceString();
- if (cachedSourceString != nullptr)
- {
- return cachedSourceString;
- }
- ScriptContext * scriptContext = this->GetScriptContext();
- //Library code should behave the same way as RuntimeFunctions
- Utf8SourceInfo* source = pFuncBody->GetUtf8SourceInfo();
- if ((source != nullptr && source->GetIsLibraryCode())
- #ifdef ENABLE_WASM
- || (pFuncBody->IsWasmFunction())
- #endif
- )
- {
- //Don't display if it is anonymous function
- charcount_t displayNameLength = 0;
- PCWSTR displayName = pFuncBody->GetShortDisplayName(&displayNameLength);
- cachedSourceString = JavascriptFunction::GetLibraryCodeDisplayString(scriptContext, displayName);
- }
- else if (!pFuncBody->GetUtf8SourceInfo()->GetIsXDomain()
- // To avoid backward compat issue, we will not give out sourceString for function if it is called from
- // window.onerror trying to retrieve arguments.callee.caller.
- && !(pFuncBody->GetUtf8SourceInfo()->GetIsXDomainString() && scriptContext->GetThreadContext()->HasUnhandledException())
- )
- {
- // Decode UTF8 into Unicode
- // Consider: Should we have a JavascriptUtf8Substring class which defers decoding
- // until it's needed?
- charcount_t cch = pFuncBody->LengthInChars();
- size_t cbLength = pFuncBody->LengthInBytes();
- LPCUTF8 pbStart = pFuncBody->GetToStringSource(_u("ScriptFunction::EnsureSourceString"));
- size_t cbPreludeLength = 0;
- // cch and cbLength refer to the length of the parse, which may be smaller than the length of the to-string function
- PrintOffsets* printOffsets = pFuncBody->GetPrintOffsets();
- if (printOffsets != nullptr)
- {
- Assert((printOffsets->cbEndPrintOffset - printOffsets->cbStartPrintOffset) >= cbLength);
- cbPreludeLength = (printOffsets->cbEndPrintOffset - printOffsets->cbStartPrintOffset) - cbLength;
- Assert(pFuncBody->StartOffset() >= printOffsets->cbStartPrintOffset);
- cbLength = printOffsets->cbEndPrintOffset - printOffsets->cbStartPrintOffset;
- }
- Assert(cbPreludeLength < MaxCharCount);
- // the toString of a function may include some prelude, e.g. the computed name expression.
- // We do not store the char-index of the start, but if there are cbPreludeLength bytes difference,
- // then that is an upper bound on the number of characters difference.
- // We also assume that function.toString is relatively infrequent, and non-ascii characters in
- // a prelude are relatively infrequent, so the inaccuracy here should in general be insignificant
- BufferStringBuilder builder(cch + static_cast<charcount_t>(cbPreludeLength), scriptContext);
- utf8::DecodeOptions options = pFuncBody->GetUtf8SourceInfo()->IsCesu8() ? utf8::doAllowThreeByteSurrogates : utf8::doDefault;
- size_t decodedCount = utf8::DecodeUnitsInto(builder.DangerousGetWritableBuffer(), pbStart, pbStart + cbLength, options);
- if (decodedCount < cch)
- {
- AssertMsg(false, "Decoded incorrect number of characters for function body");
- Js::Throw::FatalInternalError();
- }
- else if (decodedCount < cch + static_cast<charcount_t>(cbPreludeLength))
- {
- Recycler* recycler = scriptContext->GetRecycler();
- char16* buffer = RecyclerNewArrayLeaf(recycler, char16, decodedCount + 1);
- wmemcpy_s(buffer, decodedCount, builder.DangerousGetWritableBuffer(), decodedCount);
- buffer[decodedCount] = 0;
- cachedSourceString = JavascriptString::NewWithBuffer(buffer, static_cast<charcount_t>(decodedCount), scriptContext);
- }
- else
- {
- cachedSourceString = builder.ToString();
- }
- }
- else
- {
- cachedSourceString = scriptContext->GetLibrary()->GetXDomainFunctionDisplayString();
- }
- Assert(cachedSourceString != nullptr);
- pFuncBody->SetCachedSourceString(cachedSourceString);
- return cachedSourceString;
- }
- #if ENABLE_TTD
- void ScriptFunction::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
- {
- Js::FunctionBody* fb = TTD::JsSupport::ForceAndGetFunctionBody(this->GetParseableFunctionInfo());
- extractor->MarkFunctionBody(fb);
- Js::FrameDisplay* environment = this->GetEnvironment();
- if(environment->GetLength() != 0)
- {
- extractor->MarkScriptFunctionScopeInfo(environment);
- }
- if(this->cachedScopeObj != nullptr)
- {
- extractor->MarkVisitVar(this->cachedScopeObj);
- }
- if (this->GetComputedNameVar() != nullptr)
- {
- extractor->MarkVisitVar(this->GetComputedNameVar());
- }
- if (this->GetHomeObj() != nullptr)
- {
- extractor->MarkVisitVar(this->GetHomeObj());
- }
- }
- void ScriptFunction::ProcessCorePaths()
- {
- TTD::RuntimeContextInfo* rctxInfo = this->GetScriptContext()->TTDWellKnownInfo;
- //do the body path mark
- Js::FunctionBody* fb = TTD::JsSupport::ForceAndGetFunctionBody(this->GetParseableFunctionInfo());
- rctxInfo->EnqueueNewFunctionBodyObject(this, fb, _u("!fbody"));
- Js::FrameDisplay* environment = this->GetEnvironment();
- uint32 scopeCount = environment->GetLength();
- for(uint32 i = 0; i < scopeCount; ++i)
- {
- TTD::UtilSupport::TTAutoString scopePathString;
- rctxInfo->BuildEnvironmentIndexBuffer(i, scopePathString);
- void* scope = environment->GetItem(i);
- switch(environment->GetScopeType(scope))
- {
- case Js::ScopeType::ScopeType_ActivationObject:
- case Js::ScopeType::ScopeType_WithScope:
- {
- rctxInfo->EnqueueNewPathVarAsNeeded(this, (Js::Var)scope, scopePathString.GetStrValue());
- break;
- }
- case Js::ScopeType::ScopeType_SlotArray:
- {
- Js::ScopeSlots slotArray = (Field(Js::Var)*)scope;
- uint slotArrayCount = static_cast<uint>(slotArray.GetCount());
- //get the function body associated with the scope
- if (slotArray.IsDebuggerScopeSlotArray())
- {
- rctxInfo->AddWellKnownDebuggerScopePath(this, slotArray.GetDebuggerScope(), i);
- }
- else
- {
- rctxInfo->EnqueueNewFunctionBodyObject(this, slotArray.GetFunctionInfo()->GetFunctionBody(), scopePathString.GetStrValue());
- }
- for(uint j = 0; j < slotArrayCount; j++)
- {
- Js::Var sval = slotArray.Get(j);
- TTD::UtilSupport::TTAutoString slotPathString;
- rctxInfo->BuildEnvironmentIndexAndSlotBuffer(i, j, slotPathString);
- rctxInfo->EnqueueNewPathVarAsNeeded(this, sval, slotPathString.GetStrValue());
- }
- break;
- }
- default:
- TTDAssert(false, "Unknown scope kind");
- }
- }
- if(this->cachedScopeObj != nullptr)
- {
- this->GetScriptContext()->TTDWellKnownInfo->EnqueueNewPathVarAsNeeded(this, this->cachedScopeObj, _u("_cachedScopeObj"));
- }
- if (this->GetComputedNameVar() != nullptr)
- {
- this->GetScriptContext()->TTDWellKnownInfo->EnqueueNewPathVarAsNeeded(this, this->GetComputedNameVar(), _u("_computedName"));
- }
- if (this->GetHomeObj() != nullptr)
- {
- this->GetScriptContext()->TTDWellKnownInfo->EnqueueNewPathVarAsNeeded(this, this->GetHomeObj(), _u("_homeObj"));
- }
- }
- TTD::NSSnapObjects::SnapObjectType ScriptFunction::GetSnapTag_TTD() const
- {
- return TTD::NSSnapObjects::SnapObjectType::SnapScriptFunctionObject;
- }
- void ScriptFunction::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
- {
- TTDAssert(this->GetFunctionInfo() != nullptr, "We are only doing this for functions with ParseableFunctionInfo.");
- TTD::NSSnapObjects::SnapScriptFunctionInfo* ssfi = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapScriptFunctionInfo>();
- this->ExtractSnapObjectDataIntoSnapScriptFunctionInfo(ssfi, alloc);
- TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapScriptFunctionInfo*, TTD::NSSnapObjects::SnapObjectType::SnapScriptFunctionObject>(objData, ssfi);
- }
- // TODO: Fixup function definition - something funky w/ header file includes - cycles?
- void ScriptFunction::ExtractSnapObjectDataIntoSnapScriptFunctionInfo(/*TTD::NSSnapObjects::SnapScriptFunctionInfo* */ void* snapScriptFunctionInfo, TTD::SlabAllocator& alloc)
- {
- TTD::NSSnapObjects::SnapScriptFunctionInfo* ssfi = reinterpret_cast<TTD::NSSnapObjects::SnapScriptFunctionInfo*>(snapScriptFunctionInfo);
- Js::FunctionBody* fb = TTD::JsSupport::ForceAndGetFunctionBody(this->GetParseableFunctionInfo());
- alloc.CopyNullTermStringInto(fb->GetDisplayName(), ssfi->DebugFunctionName);
- ssfi->BodyRefId = TTD_CONVERT_FUNCTIONBODY_TO_PTR_ID(fb);
- Js::FrameDisplay* environment = this->GetEnvironment();
- ssfi->ScopeId = TTD_INVALID_PTR_ID;
- if (environment->GetLength() != 0)
- {
- ssfi->ScopeId = TTD_CONVERT_SCOPE_TO_PTR_ID(environment);
- }
- ssfi->CachedScopeObjId = TTD_INVALID_PTR_ID;
- if (this->cachedScopeObj != nullptr)
- {
- ssfi->CachedScopeObjId = TTD_CONVERT_VAR_TO_PTR_ID(this->cachedScopeObj);
- }
- ssfi->HomeObjId = TTD_INVALID_PTR_ID;
- if (this->GetHomeObj() != nullptr)
- {
- ssfi->HomeObjId = TTD_CONVERT_VAR_TO_PTR_ID(this->GetHomeObj());
- }
- ssfi->ComputedNameInfo = TTD_CONVERT_JSVAR_TO_TTDVAR(this->GetComputedNameVar());
- }
- #endif
- AsmJsScriptFunction::AsmJsScriptFunction(FunctionProxy * proxy, ScriptFunctionType* deferredPrototypeType) :
- ScriptFunction(proxy, deferredPrototypeType), m_moduleEnvironment(nullptr)
- {}
- AsmJsScriptFunction * AsmJsScriptFunction::OP_NewAsmJsFunc(FrameDisplay *environment, FunctionInfoPtrPtr infoRef)
- {
- AssertMsg(infoRef != nullptr, "BYTE-CODE VERIFY: Must specify a valid function to create");
- FunctionProxy* functionProxy = (*infoRef)->GetFunctionProxy();
- AssertMsg(functionProxy != nullptr, "BYTE-CODE VERIFY: Must specify a valid function to create");
- ScriptContext* scriptContext = functionProxy->GetScriptContext();
- Assert(!functionProxy->HasSuperReference());
- AsmJsScriptFunction* asmJsFunc = scriptContext->GetLibrary()->CreateAsmJsScriptFunction(functionProxy);
- asmJsFunc->SetEnvironment(environment);
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_FUNCTION(asmJsFunc, EtwTrace::GetFunctionId(functionProxy)));
- return asmJsFunc;
- }
- JavascriptArrayBuffer* AsmJsScriptFunction::GetAsmJsArrayBuffer() const
- {
- #ifdef ASMJS_PLAT
- return (JavascriptArrayBuffer*)PointerValue(
- *(this->GetModuleEnvironment() + AsmJsModuleMemory::MemoryTableBeginOffset));
- #else
- Assert(UNREACHED);
- return nullptr;
- #endif
- }
- #ifdef ENABLE_WASM
- WasmScriptFunction::WasmScriptFunction(FunctionProxy * proxy, ScriptFunctionType* deferredPrototypeType) :
- AsmJsScriptFunction(proxy, deferredPrototypeType), m_signature(nullptr)
- {
- Assert(!proxy->GetFunctionInfo()->HasComputedName());
- }
- WebAssemblyMemory* WasmScriptFunction::GetWebAssemblyMemory() const
- {
- return (WebAssemblyMemory*)PointerValue(
- *(this->GetModuleEnvironment() + AsmJsModuleMemory::MemoryTableBeginOffset));
- }
- #endif
- ScriptFunctionWithInlineCache::ScriptFunctionWithInlineCache(FunctionProxy * proxy, ScriptFunctionType* deferredPrototypeType) :
- ScriptFunction(proxy, deferredPrototypeType)
- {}
- InlineCache * ScriptFunctionWithInlineCache::GetInlineCache(uint index)
- {
- void** inlineCaches = this->GetInlineCaches();
- AssertOrFailFast(inlineCaches != nullptr);
- AssertOrFailFast(index < this->GetInlineCacheCount());
- #if DBG
- Assert(this->m_inlineCacheTypes[index] == InlineCacheTypeNone ||
- this->m_inlineCacheTypes[index] == InlineCacheTypeInlineCache);
- this->m_inlineCacheTypes[index] = InlineCacheTypeInlineCache;
- #endif
- return reinterpret_cast<InlineCache *>(PointerValue(inlineCaches[index]));
- }
- void ScriptFunctionWithInlineCache::CreateInlineCache()
- {
- Js::FunctionBody *functionBody = this->GetFunctionBody();
- this->rootObjectLoadInlineCacheStart = functionBody->GetRootObjectLoadInlineCacheStart();
- this->rootObjectStoreInlineCacheStart = functionBody->GetRootObjectStoreInlineCacheStart();
- this->inlineCacheCount = functionBody->GetInlineCacheCount();
- this->isInstInlineCacheCount = functionBody->GetIsInstInlineCacheCount();
- SetHasInlineCaches(true);
- AllocateInlineCache();
- }
- void ScriptFunctionWithInlineCache::Finalize(bool isShutdown)
- {
- if (isShutdown)
- {
- FreeOwnInlineCaches<true>();
- }
- else
- {
- FreeOwnInlineCaches<false>();
- }
- }
- template<bool isShutdown>
- void ScriptFunctionWithInlineCache::FreeOwnInlineCaches()
- {
- uint isInstInlineCacheStart = this->GetInlineCacheCount();
- uint totalCacheCount = isInstInlineCacheStart + isInstInlineCacheCount;
- if (this->GetHasInlineCaches() && this->m_inlineCaches)
- {
- Js::ScriptContext* scriptContext = this->GetParseableFunctionInfo()->GetScriptContext();
- uint i = 0;
- uint unregisteredInlineCacheCount = 0;
- uint plainInlineCacheEnd = rootObjectLoadInlineCacheStart;
- __analysis_assume(plainInlineCacheEnd < totalCacheCount);
- for (; i < plainInlineCacheEnd; i++)
- {
- if (this->m_inlineCaches[i])
- {
- InlineCache* inlineCache = (InlineCache*)(void*)this->m_inlineCaches[i];
- if (isShutdown)
- {
- inlineCache->Clear();
- }
- else if(!scriptContext->IsClosed())
- {
- if (inlineCache->RemoveFromInvalidationList())
- {
- unregisteredInlineCacheCount++;
- }
- AllocatorDelete(InlineCacheAllocator, scriptContext->GetInlineCacheAllocator(), inlineCache);
- }
- this->m_inlineCaches[i] = nullptr;
- }
- }
- i = isInstInlineCacheStart;
- for (; i < totalCacheCount; i++)
- {
- if (this->m_inlineCaches[i])
- {
- if (isShutdown)
- {
- ((IsInstInlineCache*)this->m_inlineCaches[i])->Clear();
- }
- else if (!scriptContext->IsClosed())
- {
- AllocatorDelete(CacheAllocator, scriptContext->GetIsInstInlineCacheAllocator(), (IsInstInlineCache*)(void*)this->m_inlineCaches[i]);
- }
- this->m_inlineCaches[i] = nullptr;
- }
- }
- if (unregisteredInlineCacheCount > 0)
- {
- AssertMsg(!isShutdown && !scriptContext->IsClosed(), "Unregistration of inlineCache should only be done if this is not shutdown or scriptContext closing.");
- scriptContext->GetThreadContext()->NotifyInlineCacheBatchUnregistered(unregisteredInlineCacheCount);
- }
- }
- }
- void ScriptFunctionWithInlineCache::AllocateInlineCache()
- {
- Assert(this->m_inlineCaches == nullptr);
- uint isInstInlineCacheStart = this->GetInlineCacheCount();
- uint totalCacheCount = isInstInlineCacheStart + isInstInlineCacheCount;
- Js::FunctionBody* functionBody = this->GetFunctionBody();
- if (totalCacheCount != 0)
- {
- // Root object inline cache are not leaf
- Js::ScriptContext* scriptContext = this->GetFunctionBody()->GetScriptContext();
- void ** inlineCaches = RecyclerNewArrayZ(scriptContext->GetRecycler() ,
- void*, totalCacheCount);
- #if DBG
- this->m_inlineCacheTypes = RecyclerNewArrayLeafZ(scriptContext->GetRecycler(),
- byte, totalCacheCount);
- #endif
- uint i = 0;
- uint plainInlineCacheEnd = rootObjectLoadInlineCacheStart;
- __analysis_assume(plainInlineCacheEnd <= totalCacheCount);
- for (; i < plainInlineCacheEnd; i++)
- {
- inlineCaches[i] = AllocatorNewZ(InlineCacheAllocator,
- scriptContext->GetInlineCacheAllocator(), InlineCache);
- }
- Js::RootObjectBase * rootObject = functionBody->GetRootObject();
- ThreadContext * threadContext = scriptContext->GetThreadContext();
- uint rootObjectLoadInlineCacheEnd = rootObjectLoadMethodInlineCacheStart;
- __analysis_assume(rootObjectLoadInlineCacheEnd <= totalCacheCount);
- for (; i < rootObjectLoadInlineCacheEnd; i++)
- {
- inlineCaches[i] = rootObject->GetInlineCache(
- threadContext->GetPropertyName(functionBody->GetPropertyIdFromCacheId(i)), false, false);
- }
- uint rootObjectLoadMethodInlineCacheEnd = rootObjectStoreInlineCacheStart;
- __analysis_assume(rootObjectLoadMethodInlineCacheEnd <= totalCacheCount);
- for (; i < rootObjectLoadMethodInlineCacheEnd; i++)
- {
- inlineCaches[i] = rootObject->GetInlineCache(
- threadContext->GetPropertyName(functionBody->GetPropertyIdFromCacheId(i)), true, false);
- }
- uint rootObjectStoreInlineCacheEnd = isInstInlineCacheStart;
- __analysis_assume(rootObjectStoreInlineCacheEnd <= totalCacheCount);
- for (; i < rootObjectStoreInlineCacheEnd; i++)
- {
- #pragma prefast(suppress:6386, "The analysis assume didn't help prefast figure out this is in range")
- inlineCaches[i] = rootObject->GetInlineCache(
- threadContext->GetPropertyName(functionBody->GetPropertyIdFromCacheId(i)), false, true);
- }
- for (; i < totalCacheCount; i++)
- {
- inlineCaches[i] = AllocatorNewStructZ(CacheAllocator,
- functionBody->GetScriptContext()->GetIsInstInlineCacheAllocator(), IsInstInlineCache);
- }
- #if DBG
- this->m_inlineCacheTypes = RecyclerNewArrayLeafZ(functionBody->GetScriptContext()->GetRecycler(),
- byte, totalCacheCount);
- #endif
- this->m_inlineCaches = inlineCaches;
- }
- }
- bool ScriptFunction::GetSymbolName(Var computedNameVar, const char16** symbolName, charcount_t* length)
- {
- if (nullptr != computedNameVar && VarIs<JavascriptSymbol>(computedNameVar))
- {
- const PropertyRecord* symbolRecord = VarTo<JavascriptSymbol>(computedNameVar)->GetValue();
- *symbolName = symbolRecord->GetBuffer();
- *length = symbolRecord->GetLength();
- return true;
- }
- *symbolName = nullptr;
- *length = 0;
- return false;
- }
- JavascriptString* ScriptFunction::GetDisplayNameImpl() const
- {
- Assert(this->GetFunctionProxy() != nullptr); // The caller should guarantee a proxy exists
- ParseableFunctionInfo * func = this->GetFunctionProxy()->EnsureDeserialized();
- const char16* name = nullptr;
- charcount_t length = 0;
- JavascriptString* returnStr = nullptr;
- ENTER_PINNED_SCOPE(JavascriptString, computedName);
- Var computedNameVar = this->GetComputedNameVar();
- if (computedNameVar != nullptr)
- {
- const char16* symbolName = nullptr;
- charcount_t symbolNameLength = 0;
- if (ScriptFunction::GetSymbolName(computedNameVar, &symbolName, &symbolNameLength))
- {
- if (symbolNameLength == 0)
- {
- name = symbolName;
- }
- else
- {
- name = FunctionProxy::WrapWithBrackets(symbolName, symbolNameLength, this->GetScriptContext());
- length = symbolNameLength + 2; //adding 2 to length for brackets
- }
- }
- else
- {
- computedName = ScriptFunction::GetComputedName(computedNameVar, this->GetScriptContext());
- if (!func->GetIsAccessor())
- {
- return computedName;
- }
- name = computedName->GetString();
- length = computedName->GetLength();
- }
- }
- else
- {
- name = Constants::Empty;
- if (func->GetIsNamedFunctionExpression()) // GetIsNamedFunctionExpression -> ex. var a = function foo() {} where name is foo
- {
- name = func->GetShortDisplayName(&length);
- }
- else if (func->GetIsNameIdentifierRef()) // GetIsNameIdentifierRef -> confirms a name is not attached like o.x = function() {}
- {
- if (this->GetScriptContext()->GetConfig()->IsES6FunctionNameFullEnabled())
- {
- name = func->GetShortDisplayName(&length);
- }
- else if (func->GetIsDeclaration() || // GetIsDeclaration -> ex. function foo () {}
- func->GetIsAccessor() || // GetIsAccessor -> ex. var a = { get f() {}} new enough syntax that we do not have to disable by default
- func->IsLambda() || // IsLambda -> ex. var y = { o : () => {}}
- GetHomeObj()) // GetHomeObj -> ex. var o = class {}, confirms this is a constructor or method on a class
- {
- name = func->GetShortDisplayName(&length);
- }
- }
- }
- AssertMsg(IsValidCharCount(length), "JavascriptString can't be larger than charcount_t");
- returnStr = DisplayNameHelper(name, static_cast<charcount_t>(length));
- LEAVE_PINNED_SCOPE();
- return returnStr;
- }
- bool ScriptFunction::IsAnonymousFunction() const
- {
- return this->GetFunctionProxy()->GetIsAnonymousFunction();
- }
- JavascriptString* ScriptFunction::GetComputedName(Var computedNameVar, ScriptContext * scriptContext)
- {
- JavascriptString* computedName = nullptr;
- if (computedNameVar != nullptr)
- {
- if (TaggedInt::Is(computedNameVar))
- {
- computedName = TaggedInt::ToString(computedNameVar, scriptContext);
- }
- else
- {
- computedName = JavascriptConversion::ToString(computedNameVar, scriptContext);
- }
- return computedName;
- }
- return nullptr;
- }
- bool ScriptFunction::HasSuperReference()
- {
- return this->GetFunctionProxy()->HasSuperReference();
- }
- void ScriptFunctionWithInlineCache::ClearInlineCacheOnFunctionObject()
- {
- if (NULL != this->m_inlineCaches)
- {
- FreeOwnInlineCaches<false>();
- this->m_inlineCaches = nullptr;
- this->inlineCacheCount = 0;
- this->rootObjectLoadInlineCacheStart = 0;
- this->rootObjectLoadMethodInlineCacheStart = 0;
- this->rootObjectStoreInlineCacheStart = 0;
- this->isInstInlineCacheCount = 0;
- }
- SetHasInlineCaches(false);
- }
- template <>
- void FunctionWithComputedName<ScriptFunction>::SetComputedNameVar(Var computedNameVar)
- {
- this->computedNameVar = computedNameVar;
- }
- template <>
- void FunctionWithComputedName<AsmJsScriptFunction>::SetComputedNameVar(Var computedNameVar)
- {
- this->computedNameVar = computedNameVar;
- }
- template <>
- void FunctionWithComputedName<ScriptFunctionWithInlineCache>::SetComputedNameVar(Var computedNameVar)
- {
- this->computedNameVar = computedNameVar;
- }
- template <>
- void FunctionWithComputedName<GeneratorVirtualScriptFunction>::SetComputedNameVar(Var computedNameVar)
- {
- this->computedNameVar = computedNameVar;
- }
- template <>
- void FunctionWithComputedName<FunctionWithHomeObj<GeneratorVirtualScriptFunction>>::SetComputedNameVar(Var computedNameVar)
- {
- this->computedNameVar = computedNameVar;
- }
- template <>
- void FunctionWithComputedName<FunctionWithHomeObj<ScriptFunction>>::SetComputedNameVar(Var computedNameVar)
- {
- this->computedNameVar = computedNameVar;
- if (GetFunctionInfo()->IsClassConstructor())
- {
- // For class with computed name, we wait until now to set the name property.
- JavascriptString * functionName = nullptr;
- bool result = GetFunctionName(&functionName);
- Assert(result);
- SetSlot(SetSlotArguments(Constants::NoProperty, 2, functionName));
- }
- }
- template <>
- void FunctionWithComputedName<FunctionWithHomeObj<ScriptFunctionWithInlineCache>>::SetComputedNameVar(Var computedNameVar)
- {
- this->computedNameVar = computedNameVar;
- if (GetFunctionInfo()->IsClassConstructor())
- {
- // For class with computed name, we wait until now to set the name property.
- JavascriptString * functionName = nullptr;
- bool result = GetFunctionName(&functionName);
- Assert(result);
- SetSlot(SetSlotArguments(Constants::NoProperty, 2, functionName));
- }
- }
- template <> VTableValue Js::FunctionWithComputedName<Js::AsmJsScriptFunction>::DummyVirtualFunctionToHinderLinkerICF() const
- {
- return VTableValue::VtableAsmJsScriptFunctionWithComputedName;
- }
- template <> VTableValue Js::FunctionWithHomeObj<Js::ScriptFunction>::DummyVirtualFunctionToHinderLinkerICF() const
- {
- return VTableValue::VtableScriptFunctionWithHomeObj;
- }
- template <> VTableValue Js::FunctionWithComputedName<Js::ScriptFunction>::DummyVirtualFunctionToHinderLinkerICF() const
- {
- return VTableValue::VtableScriptFunctionWithComputedName;
- }
- template <> VTableValue Js::FunctionWithComputedName<Js::FunctionWithHomeObj<Js::ScriptFunction>>::DummyVirtualFunctionToHinderLinkerICF() const
- {
- return VTableValue::VtableScriptFunctionWithHomeObjAndComputedName;
- }
- template <> VTableValue Js::FunctionWithHomeObj<Js::ScriptFunctionWithInlineCache>::DummyVirtualFunctionToHinderLinkerICF() const
- {
- return VTableValue::VtableScriptFunctionWithInlineCacheAndHomeObj;
- }
- template <> VTableValue Js::FunctionWithComputedName<Js::ScriptFunctionWithInlineCache>::DummyVirtualFunctionToHinderLinkerICF() const
- {
- return VTableValue::VtableScriptFunctionWithInlineCacheAndComputedName;
- }
- template <> VTableValue Js::FunctionWithComputedName<Js::FunctionWithHomeObj<Js::ScriptFunctionWithInlineCache>>::DummyVirtualFunctionToHinderLinkerICF() const
- {
- return VTableValue::VtableScriptFunctionWithInlineCacheHomeObjAndComputedName;
- }
- template <> VTableValue Js::FunctionWithHomeObj<Js::GeneratorVirtualScriptFunction>::DummyVirtualFunctionToHinderLinkerICF() const
- {
- return VTableValue::VtableVirtualJavascriptGeneratorFunctionWithHomeObj;
- }
- template <> VTableValue Js::FunctionWithComputedName<Js::GeneratorVirtualScriptFunction>::DummyVirtualFunctionToHinderLinkerICF() const
- {
- return VTableValue::VtableVirtualJavascriptGeneratorFunctionWithComputedName;
- }
- template <> VTableValue Js::FunctionWithComputedName<Js::FunctionWithHomeObj<Js::GeneratorVirtualScriptFunction>>::DummyVirtualFunctionToHinderLinkerICF() const
- {
- return VTableValue::VtableVirtualJavascriptGeneratorFunctionWithHomeObjAndComputedName;
- }
|