| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108 |
- //-------------------------------------------------------------------------------------------------------
- // 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"
- namespace Js
- {
- ScriptFunctionBase::ScriptFunctionBase(DynamicType * type) :
- JavascriptFunction(type)
- {}
- ScriptFunctionBase::ScriptFunctionBase(DynamicType * type, FunctionInfo * functionInfo) :
- JavascriptFunction(type, functionInfo)
- {}
- bool ScriptFunctionBase::Is(Var func)
- {
- if (JavascriptFunction::Is(func))
- {
- JavascriptFunction *function = JavascriptFunction::UnsafeFromVar(func);
- return ScriptFunction::Test(function) || JavascriptGeneratorFunction::Test(function)
- || JavascriptAsyncFunction::Test(function);
- }
- return false;
- }
- ScriptFunctionBase * ScriptFunctionBase::FromVar(Var func)
- {
- AssertOrFailFast(ScriptFunctionBase::Is(func));
- return reinterpret_cast<ScriptFunctionBase *>(func);
- }
- ScriptFunctionBase * ScriptFunctionBase::UnsafeFromVar(Var func)
- {
- Assert(ScriptFunctionBase::Is(func));
- return reinterpret_cast<ScriptFunctionBase *>(func);
- }
- ScriptFunction::ScriptFunction(DynamicType * type) :
- ScriptFunctionBase(type), environment((FrameDisplay*)&NullFrameDisplay),
- cachedScopeObj(nullptr), hasInlineCaches(false), hasSuperReference(false), homeObj(nullptr),
- computedNameVar(nullptr), isActiveScript(false)
- {}
- ScriptFunction::ScriptFunction(FunctionProxy * proxy, ScriptFunctionType* deferredPrototypeType)
- : ScriptFunctionBase(deferredPrototypeType, proxy->GetFunctionInfo()),
- environment((FrameDisplay*)&NullFrameDisplay), cachedScopeObj(nullptr), homeObj(nullptr),
- hasInlineCaches(false), hasSuperReference(false), isActiveScript(false), computedNameVar(nullptr)
- {
- Assert(proxy->GetFunctionInfo()->GetFunctionProxy() == proxy);
- Assert(proxy->EnsureDeferredPrototypeType() == deferredPrototypeType);
- DebugOnly(VerifyEntryPoint());
- #if ENABLE_NATIVE_CODEGEN
- #ifdef BGJIT_STATS
- if (!proxy->IsDeferred())
- {
- FunctionBody* body = proxy->GetFunctionBody();
- if(!body->GetNativeEntryPointUsed() &&
- body->GetDefaultFunctionEntryPointInfo()->IsCodeGenDone())
- {
- MemoryBarrier();
- type->GetScriptContext()->jitCodeUsed += body->GetByteCodeCount();
- type->GetScriptContext()->funcJitCodeUsed++;
- body->SetNativeEntryPointUsed(true);
- }
- }
- #endif
- #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();
- bool hasSuperReference = functionProxy->HasSuperReference();
- if (functionProxy->IsFunctionBody() && functionProxy->GetFunctionBody()->GetInlineCachesOnFunctionObject())
- {
- Js::FunctionBody * functionBody = functionProxy->GetFunctionBody();
- ScriptFunctionWithInlineCache* pfuncScriptWithInlineCache = scriptContext->GetLibrary()->CreateScriptFunctionWithInlineCache(functionProxy);
- pfuncScriptWithInlineCache->SetEnvironment(environment);
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_FUNCTION(pfuncScriptWithInlineCache, EtwTrace::GetFunctionId(functionProxy)));
- Assert(functionBody->GetInlineCacheCount() + functionBody->GetIsInstInlineCacheCount());
- if (functionBody->GetIsFirstFunctionObject())
- {
- // point the inline caches of the first function object to those on the function body.
- pfuncScriptWithInlineCache->SetInlineCachesFromFunctionBody();
- functionBody->SetIsNotFirstFunctionObject();
- }
- else
- {
- // allocate inline cache for this function object
- pfuncScriptWithInlineCache->CreateInlineCache();
- }
- pfuncScriptWithInlineCache->SetHasSuperReference(hasSuperReference);
- 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();
- }
- return pfuncScriptWithInlineCache;
- }
- else
- {
- ScriptFunction* pfuncScript = scriptContext->GetLibrary()->CreateScriptFunction(functionProxy);
- pfuncScript->SetEnvironment(environment);
- pfuncScript->SetHasSuperReference(hasSuperReference);
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_FUNCTION(pfuncScript, EtwTrace::GetFunctionId(functionProxy)));
- return pfuncScript;
- }
- }
- 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 = RecyclableObject::FromVar(scope);
- scopeObj->InvalidateCachedScope();
- }
- }
- bool ScriptFunction::Is(Var func)
- {
- return JavascriptFunction::Is(func) && JavascriptFunction::UnsafeFromVar(func)->GetFunctionInfo()->HasBody();
- }
- ScriptFunction * ScriptFunction::FromVar(Var func)
- {
- AssertOrFailFast(ScriptFunction::Is(func));
- return reinterpret_cast<ScriptFunction *>(func);
- }
- ScriptFunction * ScriptFunction::UnsafeFromVar(Var func)
- {
- Assert(ScriptFunction::Is(func));
- return reinterpret_cast<ScriptFunction *>(func);
- }
- 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;
- }
- uint32 ScriptFunction::GetFrameHeight(FunctionEntryPointInfo* entryPointInfo) const
- {
- Assert(this->GetFunctionBody() != nullptr);
- return this->GetFunctionBody()->GetFrameHeight(entryPointInfo);
- }
- 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(entryPoint != nullptr);
- Assert(this->GetTypeId() == TypeIds_Function);
- #if ENABLE_NATIVE_CODEGEN
- Assert(!IsCrossSiteObject() || entryPoint != (Js::JavascriptMethod)checkCodeGenThunk);
- #endif
- Assert((entryPointInfo != nullptr && this->GetFunctionProxy() != nullptr));
- if (this->GetEntryPoint() == entryPoint && this->GetScriptFunctionType()->GetEntryPointInfo() == entryPointInfo)
- {
- return;
- }
- bool isAsmJS = false;
- if (HasFunctionBody())
- {
- isAsmJS = this->GetFunctionBody()->GetIsAsmjsMode();
- }
- // ASMJS:- for asmjs we don't need to update the entry point here as it updates the types entry point
- if (!isAsmJS)
- {
- // We can't go from cross-site to non-cross-site. Update only in the non-cross site case
- if (!CrossSite::IsThunk(this->GetEntryPoint()))
- {
- this->SetEntryPoint(entryPoint);
- }
- }
- // instead update the address in the function entrypoint info
- else
- {
- entryPointInfo->jsMethod = entryPoint;
- }
- ProxyEntryPointInfo* oldEntryPointInfo = this->GetScriptFunctionType()->GetEntryPointInfo();
- if (oldEntryPointInfo
- && oldEntryPointInfo != entryPointInfo
- && 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);
- }
- this->GetScriptFunctionType()->SetEntryPointInfo(entryPointInfo);
- }
- 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->GetFunctionBody() == newFunctionInfo);
- Assert(!newFunctionInfo->IsDeferred());
- DynamicType * type = this->GetDynamicType();
- // If the type is shared, it must be the shared one in the old function proxy
- this->functionInfo = newFunctionInfo->GetFunctionInfo();
- if (type->GetIsShared())
- {
- // the type is still shared, we can't modify it, just migrate to the shared one in the function body
- this->ReplaceType(newFunctionInfo->EnsureDeferredPrototypeType());
- }
- // The type has change from the default, it is not share, just use that one.
- 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();
- }
- Var ScriptFunction::FormatToString(JavascriptString* inputString)
- {
- FunctionProxy* proxy = this->GetFunctionProxy();
- ParseableFunctionInfo * pFuncBody = proxy->EnsureDeserialized();
- Var returnStr = nullptr;
- EnterPinnedScope((volatile void**)& inputString);
- const char16 * inputStr = inputString->GetString();
- const char16 * paramStr = wcschr(inputStr, _u('('));
- if (paramStr == nullptr || wcscmp(pFuncBody->GetDisplayName(), Js::Constants::EvalCode) == 0)
- {
- Assert(pFuncBody->IsEval());
- return inputString;
- }
- ScriptContext* scriptContext = this->GetScriptContext();
- JavascriptLibrary* library = scriptContext->GetLibrary();
- bool isClassMethod = this->GetFunctionInfo()->IsClassMethod() || this->GetFunctionInfo()->IsClassConstructor();
- JavascriptString* prefixString = nullptr;
- uint prefixStringLength = 0;
- const char16* name = _u("");
- charcount_t nameLength = 0;
- if (!isClassMethod)
- {
- prefixString = library->GetFunctionPrefixString();
- if (pFuncBody->IsGenerator())
- {
- prefixString = library->GetGeneratorFunctionPrefixString();
- }
- else if (pFuncBody->IsAsync())
- {
- prefixString = library->GetAsyncFunctionPrefixString();
- }
- prefixStringLength = prefixString->GetLength();
- if (pFuncBody->GetIsAccessor())
- {
- name = pFuncBody->GetShortDisplayName(&nameLength);
- }
- else if (pFuncBody->GetIsDeclaration() || pFuncBody->GetIsNamedFunctionExpression())
- {
- name = pFuncBody->GetDisplayName();
- nameLength = pFuncBody->GetDisplayNameLength();
- if (name == Js::Constants::FunctionCode)
- {
- name = Js::Constants::Anonymous;
- nameLength = Js::Constants::AnonymousLength;
- }
- }
- }
- else
- {
- if (this->GetFunctionInfo()->IsClassConstructor())
- {
- name = _u("constructor");
- nameLength = _countof(_u("constructor")) -1; //subtract off \0
- }
- else
- {
- name = pFuncBody->GetShortDisplayName(&nameLength); //strip off prototype.
- }
- }
- ENTER_PINNED_SCOPE(JavascriptString, computedName);
- computedName = this->GetComputedName();
- if (computedName != nullptr)
- {
- prefixString = nullptr;
- prefixStringLength = 0;
- name = computedName->GetString();
- nameLength = computedName->GetLength();
- }
- uint functionBodyLength = inputString->GetLength() - ((uint)(paramStr - inputStr));
- size_t totalLength = prefixStringLength + functionBodyLength + nameLength;
- if (!IsValidCharCount(totalLength))
- {
- // We throw here because computed property names are evaluated at runtime and
- // thus are not a subset string of function body source (parameter inputString).
- // For all other cases totalLength <= inputString->GetLength().
- JavascriptExceptionOperators::ThrowOutOfMemory(this->GetScriptContext());
- }
- char16 * funcBodyStr = RecyclerNewArrayLeaf(this->GetScriptContext()->GetRecycler(), char16, totalLength);
- char16 * funcBodyStrStart = funcBodyStr;
- if (prefixString != nullptr)
- {
- js_wmemcpy_s(funcBodyStr, prefixStringLength, prefixString->GetString(), prefixStringLength);
- funcBodyStrStart += prefixStringLength;
- }
- js_wmemcpy_s(funcBodyStrStart, nameLength, name, nameLength);
- funcBodyStrStart = funcBodyStrStart + nameLength;
- js_wmemcpy_s(funcBodyStrStart, functionBodyLength, paramStr, functionBodyLength);
- returnStr = LiteralString::NewCopyBuffer(funcBodyStr, (charcount_t)totalLength, scriptContext);
- LEAVE_PINNED_SCOPE(); // computedName
- LeavePinnedScope(); // inputString
- return returnStr;
- }
- Var ScriptFunction::EnsureSourceString()
- {
- // The function may be defer serialize, need to be deserialized
- FunctionProxy* proxy = this->GetFunctionProxy();
- ParseableFunctionInfo * pFuncBody = proxy->EnsureDeserialized();
- Var 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->GetSource(_u("ScriptFunction::EnsureSourceString"));
- BufferStringBuilder builder(cch, 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();
- }
- if (pFuncBody->IsLambda() || isActiveScript || this->GetFunctionInfo()->IsClassConstructor()
- #ifdef ENABLE_PROJECTION
- || scriptContext->GetConfig()->IsWinRTEnabled()
- #endif
- )
- {
- cachedSourceString = builder.ToString();
- }
- else
- {
- cachedSourceString = FormatToString(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->homeObj != nullptr)
- {
- extractor->MarkVisitVar(this->homeObj);
- }
- if(this->computedNameVar != nullptr)
- {
- extractor->MarkVisitVar(this->computedNameVar);
- }
- }
- 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->homeObj != nullptr)
- {
- this->GetScriptContext()->TTDWellKnownInfo->EnqueueNewPathVarAsNeeded(this, this->homeObj, _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->homeObj != nullptr)
- {
- ssfi->HomeObjId = TTD_CONVERT_VAR_TO_PTR_ID(this->homeObj);
- }
- ssfi->ComputedNameInfo = TTD_CONVERT_JSVAR_TO_TTDVAR(this->computedNameVar);
- ssfi->HasSuperReference = this->hasSuperReference;
- }
- #endif
- AsmJsScriptFunction::AsmJsScriptFunction(FunctionProxy * proxy, ScriptFunctionType* deferredPrototypeType) :
- ScriptFunction(proxy, deferredPrototypeType), m_moduleEnvironment(nullptr)
- {}
- AsmJsScriptFunction::AsmJsScriptFunction(DynamicType * type) :
- ScriptFunction(type), m_moduleEnvironment(nullptr)
- {}
- bool AsmJsScriptFunction::Is(Var func)
- {
- return ScriptFunction::Is(func) && ScriptFunction::UnsafeFromVar(func)->IsAsmJsFunction();
- }
- AsmJsScriptFunction* AsmJsScriptFunction::FromVar(Var func)
- {
- AssertOrFailFast(AsmJsScriptFunction::Is(func));
- return reinterpret_cast<AsmJsScriptFunction *>(func);
- }
- AsmJsScriptFunction* AsmJsScriptFunction::UnsafeFromVar(Var func)
- {
- Assert(AsmJsScriptFunction::Is(func));
- return reinterpret_cast<AsmJsScriptFunction *>(func);
- }
- 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();
- bool hasSuperReference = functionProxy->HasSuperReference();
- AsmJsScriptFunction* asmJsFunc = scriptContext->GetLibrary()->CreateAsmJsScriptFunction(functionProxy);
- asmJsFunc->SetEnvironment(environment);
- Assert(!hasSuperReference);
- asmJsFunc->SetHasSuperReference(hasSuperReference);
- 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(DynamicType * type) :
- AsmJsScriptFunction(type), m_signature(nullptr)
- {}
- WasmScriptFunction::WasmScriptFunction(FunctionProxy * proxy, ScriptFunctionType* deferredPrototypeType) :
- AsmJsScriptFunction(proxy, deferredPrototypeType), m_signature(nullptr)
- {}
- bool WasmScriptFunction::Is(Var func)
- {
- return ScriptFunction::Is(func) && ScriptFunction::UnsafeFromVar(func)->IsWasmFunction();
- }
- WasmScriptFunction* WasmScriptFunction::FromVar(Var func)
- {
- AssertOrFailFast(WasmScriptFunction::Is(func));
- return reinterpret_cast<WasmScriptFunction *>(func);
- }
- WasmScriptFunction* WasmScriptFunction::UnsafeFromVar(Var func)
- {
- Assert(WasmScriptFunction::Is(func));
- return reinterpret_cast<WasmScriptFunction *>(func);
- }
- WebAssemblyMemory* WasmScriptFunction::GetWebAssemblyMemory() const
- {
- return (WebAssemblyMemory*)PointerValue(
- *(this->GetModuleEnvironment() + AsmJsModuleMemory::MemoryTableBeginOffset));
- }
- #endif
- ScriptFunctionWithInlineCache::ScriptFunctionWithInlineCache(FunctionProxy * proxy, ScriptFunctionType* deferredPrototypeType) :
- ScriptFunction(proxy, deferredPrototypeType), hasOwnInlineCaches(false)
- {}
- ScriptFunctionWithInlineCache::ScriptFunctionWithInlineCache(DynamicType * type) :
- ScriptFunction(type), hasOwnInlineCaches(false)
- {}
- bool ScriptFunctionWithInlineCache::Is(Var func)
- {
- return ScriptFunction::Is(func) && ScriptFunction::UnsafeFromVar(func)->GetHasInlineCaches();
- }
- ScriptFunctionWithInlineCache* ScriptFunctionWithInlineCache::FromVar(Var func)
- {
- AssertOrFailFast(ScriptFunctionWithInlineCache::Is(func));
- return reinterpret_cast<ScriptFunctionWithInlineCache *>(func);
- }
- ScriptFunctionWithInlineCache* ScriptFunctionWithInlineCache::UnsafeFromVar(Var func)
- {
- Assert(ScriptFunctionWithInlineCache::Is(func));
- return reinterpret_cast<ScriptFunctionWithInlineCache *>(func);
- }
- 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]));
- }
- Field(void**) ScriptFunctionWithInlineCache::GetInlineCaches()
- {
- // If script function have inline caches pointing to function body and function body got reparsed we need to reset cache
- if (this->GetHasInlineCaches() && !this->GetHasOwnInlineCaches())
- {
- // Script function have inline caches pointing to function body
- if (!this->HasFunctionBody())
- {
- // Function body got re-deferred and have not been re-parsed yet. Reset cache to null
- this->m_inlineCaches = nullptr;
- this->inlineCacheCount = 0;
- this->SetHasInlineCaches(false);
- }
- else if (this->m_inlineCaches != this->GetFunctionBody()->GetInlineCaches())
- {
- // Function body got reparsed we need to reset cache
- Assert(this->GetFunctionBody()->GetCompileCount() > 1);
- this->SetInlineCachesFromFunctionBody();
- }
- }
- return this->m_inlineCaches;
- }
- void ScriptFunctionWithInlineCache::SetInlineCachesFromFunctionBody()
- {
- SetHasInlineCaches(true);
- Js::FunctionBody* functionBody = this->GetFunctionBody();
- this->m_inlineCaches = functionBody->GetInlineCaches();
- #if DBG
- this->m_inlineCacheTypes = functionBody->GetInlineCacheTypes();
- #endif
- this->rootObjectLoadInlineCacheStart = functionBody->GetRootObjectLoadInlineCacheStart();
- this->rootObjectLoadMethodInlineCacheStart = functionBody->GetRootObjectLoadMethodInlineCacheStart();
- this->rootObjectStoreInlineCacheStart = functionBody->GetRootObjectStoreInlineCacheStart();
- this->inlineCacheCount = functionBody->GetInlineCacheCount();
- this->isInstInlineCacheCount = functionBody->GetIsInstInlineCacheCount();
- }
- 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();
- hasOwnInlineCaches = true;
- }
- 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 && this->hasOwnInlineCaches)
- {
- 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(const char16** symbolName, charcount_t* length) const
- {
- if (nullptr != this->computedNameVar && JavascriptSymbol::Is(this->computedNameVar))
- {
- const PropertyRecord* symbolRecord = JavascriptSymbol::FromVar(this->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);
- if (computedNameVar != nullptr)
- {
- const char16* symbolName = nullptr;
- charcount_t symbolNameLength = 0;
- if (this->GetSymbolName(&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 = this->GetComputedName();
- 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() const
- {
- JavascriptString* computedName = nullptr;
- ScriptContext* scriptContext = this->GetScriptContext();
- if (computedNameVar != nullptr)
- {
- if (TaggedInt::Is(computedNameVar))
- {
- computedName = TaggedInt::ToString(computedNameVar, scriptContext);
- }
- else
- {
- computedName = JavascriptConversion::ToString(computedNameVar, scriptContext);
- }
- return computedName;
- }
- return nullptr;
- }
- 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);
- }
- void ScriptFunctionWithInlineCache::ClearBorrowedInlineCacheOnFunctionObject()
- {
- if (this->hasOwnInlineCaches)
- {
- return;
- }
- ClearInlineCacheOnFunctionObject();
- }
- }
|