| //------------------------------------------------------------------------------------------------------- |
| // Copyright (C) Microsoft. All rights reserved. |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. |
| //------------------------------------------------------------------------------------------------------- |
| #pragma once |
| |
| #ifdef ENABLE_SCRIPT_PROFILING |
| #include "activprof.h" |
| #endif |
| |
| #if DBG || ENABLE_REGEX_CONFIG_OPTIONS || defined(PROFILE_STRINGS) |
| #define NEED_MISC_ALLOCATOR |
| #endif |
| |
| #define BuiltInFunctionsScriptId 0 |
| |
| using namespace PlatformAgnostic; |
| |
| class NativeCodeGenerator; |
| class BackgroundParser; |
| struct IActiveScriptDirect; |
| #ifdef ENABLE_BASIC_TELEMETRY |
| class ScriptContextTelemetry; |
| #endif |
| namespace Js |
| { |
| class ScriptContext; |
| class ScriptEditQuery; |
| class MutationBreakpoint; |
| class StringProfiler; |
| class DebugContext; |
| struct HaltCallback; |
| struct DebuggerOptionsCallback; |
| class ModuleRecordBase; |
| } |
| |
| // Created for every source buffer passed by host. |
| // This structure has all the details. |
| class SRCINFO |
| { |
| // We currently don't free SRCINFO object so we don't want to add extra variables here. |
| // In future, when we do make it freeable and will be able to allocate more than one per Module, |
| // we can move variables m_isGlobalFunc and m_isEval from FunctionBody.cpp here. |
| public: |
| SourceContextInfo * sourceContextInfo; |
| ULONG dlnHost; // Line number passed to ParseScriptText |
| ULONG ulColumnHost; // Column number on the line where the parse script text started |
| ULONG lnMinHost; // Line offset of first host-supplied line |
| ULONG ichMinHost; // Range of host supplied characters |
| ULONG ichLimHost; |
| ULONG ulCharOffset; // Char offset of the source text relative to the document. (Populated using IActiveScriptContext) |
| Js::ModuleID moduleID; |
| ULONG grfsi; |
| |
| static SRCINFO* Copy(Recycler* recycler, const SRCINFO* srcInfo) |
| { |
| SRCINFO* copySrcInfo = RecyclerNew(recycler, SRCINFO, *srcInfo); |
| return copySrcInfo; |
| } |
| }; |
| |
| struct CustomExternalObjectOperations |
| { |
| size_t offsetOfOperationsUsage; |
| DWORD operationFlagEquals; |
| DWORD operationFlagStrictEquals; |
| }; |
| |
| enum ExternalJitData |
| { |
| ExternalJitData_CustomExternalObjectOperations |
| }; |
| |
| enum LoadScriptFlag |
| { |
| LoadScriptFlag_None = 0x0, |
| LoadScriptFlag_Expression = 0x1, // the script returns a value. |
| LoadScriptFlag_disableDeferredParse = 0x2, // do not defer-parse the code. |
| LoadScriptFlag_isByteCodeBufferForLibrary = 0x4, // for bytecode buffer |
| LoadScriptFlag_disableAsmJs = 0x8, // disable parse as asmjs. The code is not conform to asmjs requirement. |
| LoadScriptFlag_Module = 0x10, // input script is module code. |
| LoadScriptFlag_isFunction = 0x20, // input script is in a function scope, not global code. |
| LoadScriptFlag_Utf8Source = 0x40, // input buffer is utf8 encoded. |
| LoadScriptFlag_LibraryCode = 0x80 // for debugger, indicating 'not my code' |
| }; |
| |
| class HostScriptContext |
| { |
| public: |
| HostScriptContext(Js::ScriptContext* inScriptContext) { this->scriptContext = inScriptContext; } |
| virtual void Delete() = 0; |
| virtual HRESULT GetPreviousHostScriptContext(__deref_out HostScriptContext** ppUnkCaller) = 0; |
| virtual HRESULT PushHostScriptContext() = 0; |
| virtual void PopHostScriptContext() = 0; |
| |
| virtual HRESULT SetCaller(IUnknown *punkNew, IUnknown **ppunkPrev) = 0; |
| virtual HRESULT GetDispatchExCaller(__deref_out void** dispatchExCaller) = 0; |
| virtual void ReleaseDispatchExCaller(__in void* dispatchExCaler) = 0; |
| virtual Js::ModuleRoot * GetModuleRoot(int moduleID) = 0; |
| virtual HRESULT CheckCrossDomainScriptContext(__in Js::ScriptContext* scriptContext) = 0; |
| |
| virtual HRESULT GetHostContextUrl(__in DWORD_PTR hostSourceContext, __out BSTR& pUrl) = 0; |
| virtual BOOL HasCaller() = 0; |
| virtual void CleanDynamicCodeCache() = 0; |
| virtual HRESULT VerifyDOMSecurity(Js::ScriptContext* targetContext, Js::Var obj) = 0; |
| |
| virtual HRESULT CheckEvalRestriction() = 0; |
| virtual HRESULT HostExceptionFromHRESULT(HRESULT hr, Js::Var* outError) = 0; |
| |
| virtual HRESULT GetExternalJitData(ExternalJitData id, void *data) = 0; |
| virtual HRESULT SetDispatchInvoke(Js::JavascriptMethod dispatchInvoke) = 0; |
| virtual HRESULT ArrayBufferFromExternalObject(__in Js::RecyclableObject *obj, |
| __out Js::ArrayBuffer **ppArrayBuffer) = 0; |
| virtual Js::JavascriptError* CreateWinRTError(IErrorInfo* perrinfo, Js::RestrictedErrorStrings * proerrstr) = 0; |
| virtual HRESULT EnqueuePromiseTask(Js::Var varTask) = 0; |
| |
| virtual HRESULT FetchImportedModule(Js::ModuleRecordBase* referencingModule, LPCOLESTR specifier, Js::ModuleRecordBase** dependentModuleRecord) = 0; |
| virtual HRESULT NotifyHostAboutModuleReady(Js::ModuleRecordBase* referencingModule, Js::Var exceptionVar) = 0; |
| |
| Js::ScriptContext* GetScriptContext() { return scriptContext; } |
| |
| virtual bool SetCrossSiteForFunctionType(Js::JavascriptFunction * function) = 0; |
| #if DBG_DUMP || defined(PROFILE_EXEC) || defined(PROFILE_MEM) |
| virtual void EnsureParentInfo(Js::ScriptContext* scriptContext = NULL) = 0; |
| #endif |
| #if DBG |
| virtual bool IsHostCrossSiteThunk(Js::JavascriptMethod address) = 0; |
| #endif |
| private: |
| Js::ScriptContext* scriptContext; |
| }; |
| |
| #if ENABLE_TTD |
| //A class that we use to pass in a functor from the host when we need to inform it about something we are doing |
| class HostScriptContextCallbackFunctor |
| { |
| public: |
| void* HostData; |
| void(*pfOnScriptLoadCallback)(void* hostData, Js::JavascriptFunction* scriptFunction, Js::Utf8SourceInfo* utf8SourceInfo, CompileScriptException* compileException); |
| |
| HostScriptContextCallbackFunctor() |
| : HostData(nullptr), pfOnScriptLoadCallback(nullptr) |
| { |
| ; |
| } |
| |
| HostScriptContextCallbackFunctor(void* callbackData, void(*pfcallbackOnScriptLoad)(void* hostData, Js::JavascriptFunction* scriptFunction, Js::Utf8SourceInfo* utf8SourceInfo, CompileScriptException* compileException)) |
| : HostData(callbackData), pfOnScriptLoadCallback(pfcallbackOnScriptLoad) |
| { |
| ; |
| } |
| }; |
| #endif |
| |
| namespace Js |
| { |
| |
| #pragma pack(push, 1) |
| struct StackFrameInfo |
| { |
| StackFrameInfo() { } |
| StackFrameInfo(DWORD_PTR _scriptContextID |
| , UINT32 _sourceLocationLineNumber |
| , UINT32 _sourceLocationColumnNumber |
| , UINT32 _methodIDOrNameIndex |
| , UINT8 _isFrameIndex) |
| : scriptContextID(_scriptContextID) |
| , sourceLocationLineNumber(_sourceLocationLineNumber) |
| , sourceLocationColumnNumber(_sourceLocationColumnNumber) |
| , methodIDOrNameIndex(_methodIDOrNameIndex) |
| , isFrameIndex(_isFrameIndex) |
| { } |
| |
| DWORD_PTR scriptContextID; |
| UINT32 sourceLocationLineNumber; |
| UINT32 sourceLocationColumnNumber; |
| UINT32 methodIDOrNameIndex; |
| UINT8 isFrameIndex; |
| }; |
| #pragma pack(pop) |
| |
| #ifdef ENABLE_PROJECTION |
| class ProjectionConfiguration |
| { |
| public: |
| ProjectionConfiguration() : targetVersion(0) |
| { |
| } |
| |
| DWORD GetTargetVersion() const { return this->targetVersion; } |
| void SetTargetVersion(DWORD version) { this->targetVersion = version; } |
| |
| bool IsTargetWindows8() const { return this->targetVersion == NTDDI_WIN8; } |
| bool IsTargetWindowsBlueOrLater() const { return this->targetVersion >= NTDDI_WINBLUE; } |
| |
| private: |
| DWORD targetVersion; |
| }; |
| #endif // ENABLE_PROJECTION |
| |
| class ScriptConfiguration |
| { |
| public: |
| ScriptConfiguration(const ThreadConfiguration * const threadConfig, const bool isOptimizedForManyInstances) : |
| #ifdef ENABLE_PROJECTION |
| HostType(Configuration::Global.flags.HostType), |
| WinRTConstructorAllowed(Configuration::Global.flags.WinRTConstructorAllowed), |
| #endif |
| NoNative(Configuration::Global.flags.NoNative), |
| isOptimizedForManyInstances(isOptimizedForManyInstances), |
| threadConfig(threadConfig) |
| { |
| } |
| |
| // Version |
| bool SupportsES3() const { return true; } |
| bool SupportsES3Extensions() const { |
| #ifdef ENABLE_PROJECTION |
| return HostType != HostTypeApplication; |
| #else |
| return true; |
| #endif |
| } |
| |
| #define FORWARD_THREAD_CONFIG(flag) inline bool flag() const { return threadConfig->flag(); } |
| #define FLAG(threadFlag, globalFlag) FORWARD_THREAD_CONFIG(threadFlag) |
| #define FLAG_RELEASE(threadFlag, globalFlag) FORWARD_THREAD_CONFIG(threadFlag) |
| #include "../Base/ThreadConfigFlagsList.h" |
| #undef FLAG_RELEASE |
| #undef FLAG |
| #undef FORWARD_THREAD_CONFIG |
| |
| bool SupportsCollectGarbage() const { return true; } |
| |
| void ForceNoNative() { this->NoNative = true; } |
| void ForceNative() { this->NoNative = false; } |
| bool IsNoNative() const { return this->NoNative; } |
| |
| void SetCanOptimizeGlobalLookupFlag(BOOL f){ this->fCanOptimizeGlobalLookup = f;} |
| BOOL CanOptimizeGlobalLookup() const { return this->fCanOptimizeGlobalLookup;} |
| bool IsOptimizedForManyInstances() const { return isOptimizedForManyInstances; } |
| void CopyFrom(ScriptConfiguration& other) |
| { |
| this->NoNative = other.NoNative; |
| this->fCanOptimizeGlobalLookup = other.fCanOptimizeGlobalLookup; |
| #ifdef ENABLE_PROJECTION |
| this->HostType = other.HostType; |
| this->WinRTConstructorAllowed = other.WinRTConstructorAllowed; |
| this->projectionConfiguration = other.projectionConfiguration; |
| #endif |
| } |
| |
| #ifdef ENABLE_PROJECTION |
| Number GetHostType() const // Returns one of enum HostType values (see ConfigFlagsTable.h). |
| { |
| AssertMsg(this->HostType >= HostTypeMin && this->HostType <= HostTypeMax, "HostType value is out of valid range."); |
| return this->HostType; |
| } |
| |
| ProjectionConfiguration const * GetProjectionConfig() const |
| { |
| return &projectionConfiguration; |
| } |
| void SetHostType(int32 hostType) { this->HostType = hostType; } |
| void SetWinRTConstructorAllowed(bool allowed) { this->WinRTConstructorAllowed = allowed; } |
| void SetProjectionTargetVersion(DWORD version) |
| { |
| projectionConfiguration.SetTargetVersion(version); |
| } |
| bool IsWinRTEnabled() const { return (GetHostType() == Js::HostTypeApplication) || (GetHostType() == Js::HostTypeWebview); } |
| |
| bool IsWinRTConstructorAllowed() const { return (GetHostType() != Js::HostTypeWebview) || this->WinRTConstructorAllowed; } |
| #endif |
| private: |
| |
| // Per script configurations |
| bool NoNative; |
| BOOL fCanOptimizeGlobalLookup; |
| const bool isOptimizedForManyInstances; |
| const ThreadConfiguration * const threadConfig; |
| |
| #ifdef ENABLE_PROJECTION |
| Number HostType; // One of enum HostType values (see ConfigFlagsTable.h). |
| bool WinRTConstructorAllowed; // whether allow constructor in webview host type. Also note that this is not a security feature. |
| ProjectionConfiguration projectionConfiguration; |
| #endif |
| }; |
| |
| struct ScriptEntryExitRecord |
| { |
| bool hasCaller : 1; |
| bool hasReentered : 1; |
| #if DBG_DUMP |
| bool isCallRoot : 1; |
| #endif |
| #if DBG || defined(PROFILE_EXEC) |
| bool leaveForHost : 1; |
| #endif |
| #if DBG |
| bool leaveForAsyncHostOperation : 1; |
| #endif |
| #ifdef CHECK_STACKWALK_EXCEPTION |
| bool ignoreStackWalkException: 1; |
| #endif |
| Js::ImplicitCallFlags savedImplicitCallFlags; |
| |
| void * returnAddrOfScriptEntryFunction; |
| void * frameIdOfScriptExitFunction; // the frameAddres in x86, the return address in amd64/arm_soc |
| ScriptContext * scriptContext; |
| struct ScriptEntryExitRecord * next; |
| |
| #if defined(_M_IX86) && defined(DBG) |
| void * scriptEntryFS0; |
| #endif |
| #ifdef EXCEPTION_CHECK |
| ExceptionType handledExceptionType; |
| #endif |
| }; |
| |
| static const unsigned int EvalMRUSize = 15; |
| typedef JsUtil::BaseDictionary<DWORD_PTR, SourceContextInfo *, Recycler, PowerOf2SizePolicy> SourceContextInfoMap; |
| typedef JsUtil::BaseDictionary<uint, SourceContextInfo *, Recycler, PowerOf2SizePolicy> DynamicSourceContextInfoMap; |
| |
| typedef JsUtil::BaseDictionary<EvalMapString, ScriptFunction*, RecyclerNonLeafAllocator, PrimeSizePolicy> SecondLevelEvalCache; |
| typedef TwoLevelHashRecord<FastEvalMapString, ScriptFunction*, SecondLevelEvalCache, EvalMapString> EvalMapRecord; |
| typedef JsUtil::Cache<FastEvalMapString, EvalMapRecord*, RecyclerNonLeafAllocator, PrimeSizePolicy, JsUtil::MRURetentionPolicy<FastEvalMapString, EvalMRUSize>, FastEvalMapStringComparer> EvalCacheTopLevelDictionary; |
| typedef JsUtil::Cache<EvalMapString, ParseableFunctionInfo*, RecyclerNonLeafAllocator, PrimeSizePolicy, JsUtil::MRURetentionPolicy<EvalMapString, EvalMRUSize>> NewFunctionCache; |
| typedef JsUtil::BaseDictionary<ParseableFunctionInfo*, ParseableFunctionInfo*, Recycler, PrimeSizePolicy, RecyclerPointerComparer> ParseableFunctionInfoMap; |
| // This is the dictionary used by script context to cache the eval. |
| typedef TwoLevelHashDictionary<FastEvalMapString, ScriptFunction*, EvalMapRecord, EvalCacheTopLevelDictionary, EvalMapString> EvalCacheDictionary; |
| |
| struct PropertyStringMap |
| { |
| PropertyString* strLen2[80]; |
| |
| inline static uint PStrMapIndex(char16 ch) |
| { |
| Assert(ch >= '0' && ch <= 'z'); |
| return ch - '0'; |
| } |
| }; |
| |
| #ifdef ENABLE_DOM_FAST_PATH |
| typedef JsUtil::BaseDictionary<Js::FunctionInfo*, IR::JnHelperMethod, ArenaAllocator, PowerOf2SizePolicy> DOMFastPathIRHelperMap; |
| #endif |
| |
| // valid if object!= NULL |
| struct EnumeratedObjectCache { |
| static const int kMaxCachedPropStrings=16; |
| DynamicObject* object; |
| DynamicType* type; |
| PropertyString* propertyStrings[kMaxCachedPropStrings]; |
| int validPropStrings; |
| }; |
| |
| typedef JsUtil::BaseDictionary<JavascriptMethod, JavascriptFunction*, Recycler, PowerOf2SizePolicy> BuiltInLibraryFunctionMap; |
| |
| // this is allocated in GC directly to avoid force pinning the object, it is linked from JavascriptLibrary such that it has |
| // the same lifetime as JavascriptLibrary, and it can be collected without ScriptContext Close. |
| // Allocate it as Finalizable such that it will be still available during JavascriptLibrary Dispose time |
| class Cache : public FinalizableObject |
| { |
| public: |
| virtual void Finalize(bool isShutdown) override {} |
| virtual void Dispose(bool isShutdown) override {} |
| virtual void Mark(Recycler *recycler) override { AssertMsg(false, "Mark called on object that isn't TrackableObject"); } |
| |
| JavascriptString * lastNumberToStringRadix10String; |
| EnumeratedObjectCache enumObjCache; |
| JavascriptString * lastUtcTimeFromStrString; |
| EvalCacheDictionary* evalCacheDictionary; |
| EvalCacheDictionary* indirectEvalCacheDictionary; |
| NewFunctionCache* newFunctionCache; |
| RegexPatternMruMap *dynamicRegexMap; |
| SourceContextInfoMap* sourceContextInfoMap; // maps host provided context cookie to the URL of the script buffer passed. |
| DynamicSourceContextInfoMap* dynamicSourceContextInfoMap; |
| SourceContextInfo* noContextSourceContextInfo; |
| SRCINFO* noContextGlobalSourceInfo; |
| SRCINFO const ** moduleSrcInfo; |
| BuiltInLibraryFunctionMap* builtInLibraryFunctions; |
| }; |
| |
| class ScriptContext : public ScriptContextBase |
| { |
| friend class LowererMD; |
| friend class RemoteScriptContext; |
| friend class GlobalObject; // InitializeCache |
| friend class SourceTextModuleRecord; // for module bytecode gen. |
| |
| public: |
| static DWORD GetThreadContextOffset() { return offsetof(ScriptContext, threadContext); } |
| static DWORD GetOptimizationOverridesOffset() { return offsetof(ScriptContext, optimizationOverrides); } |
| static DWORD GetRecyclerOffset() { return offsetof(ScriptContext, recycler); } |
| static DWORD GetNumberAllocatorOffset() { return offsetof(ScriptContext, numberAllocator); } |
| static DWORD GetAsmIntDbValOffset() { return offsetof(ScriptContext, retAsmIntDbVal); } |
| |
| ScriptContext *next; |
| ScriptContext *prev; |
| double retAsmIntDbVal; // stores the double & float result for Asm interpreter |
| |
| AsmJsSIMDValue retAsmSimdVal; // stores raw simd result for Asm interpreter |
| static DWORD GetAsmSimdValOffset() { return offsetof(ScriptContext, retAsmSimdVal); } |
| |
| ScriptContextOptimizationOverrideInfo optimizationOverrides; |
| |
| Js::JavascriptMethod CurrentThunk; |
| Js::JavascriptMethod CurrentCrossSiteThunk; |
| Js::JavascriptMethod DeferredParsingThunk; |
| Js::JavascriptMethod DeferredDeserializationThunk; |
| Js::JavascriptMethod DispatchDefaultInvoke; |
| Js::JavascriptMethod DispatchProfileInvoke; |
| |
| #ifdef ENABLE_SCRIPT_DEBUGGING |
| typedef HRESULT (*GetDocumentContextFunction)( |
| ScriptContext *pContext, |
| Js::FunctionBody *pFunctionBody, |
| IDebugDocumentContext **ppDebugDocumentContext); |
| GetDocumentContextFunction GetDocumentContext; |
| #endif // ENABLE_SCRIPT_DEBUGGING |
| |
| #ifdef ENABLE_SCRIPT_PROFILING |
| typedef HRESULT (*CleanupDocumentContextFunction)(ScriptContext *pContext); |
| CleanupDocumentContextFunction CleanupDocumentContext; |
| #endif |
| |
| const ScriptContextBase* GetScriptContextBase() const { return static_cast<const ScriptContextBase*>(this); } |
| |
| bool DoUndeferGlobalFunctions() const; |
| |
| bool IsUndeclBlockVar(Var var) const { return this->javascriptLibrary->IsUndeclBlockVar(var); } |
| |
| void TrackPid(const PropertyRecord* propertyRecord); |
| void TrackPid(PropertyId propertyId); |
| |
| bool IsTrackedPropertyId(Js::PropertyId propertyId); |
| void InvalidateHostObjects() |
| { |
| AssertMsg(!isClosed, "Host Object invalidation should occur before the engine is fully closed. Figure our how isClosed got set beforehand."); |
| isInvalidatedForHostObjects = true; |
| } |
| bool IsInvalidatedForHostObjects() |
| { |
| return isInvalidatedForHostObjects; |
| } |
| |
| #ifdef ENABLE_JS_ETW |
| void EmitStackTraceEvent(__in UINT64 operationID, __in USHORT maxFrameCount, bool emitV2AsyncStackEvent); |
| #endif |
| |
| void SetIsDiagnosticsScriptContext(bool set) { this->isDiagnosticsScriptContext = set; } |
| bool IsDiagnosticsScriptContext() const { return this->isDiagnosticsScriptContext; } |
| |
| bool IsScriptContextInNonDebugMode() const; |
| bool IsScriptContextInDebugMode() const; |
| bool IsScriptContextInSourceRundownOrDebugMode() const; |
| bool IsRunningScript() const { return this->threadContext->GetScriptEntryExit() != nullptr; } |
| |
| typedef JsUtil::List<RecyclerWeakReference<Utf8SourceInfo>*, Recycler, false, Js::WeakRefFreeListedRemovePolicy> CalleeSourceList; |
| RecyclerRootPtr<CalleeSourceList> calleeUtf8SourceInfoList; |
| void AddCalleeSourceInfoToList(Utf8SourceInfo* sourceInfo); |
| bool HaveCalleeSources() { return calleeUtf8SourceInfoList && !calleeUtf8SourceInfoList->Empty(); } |
| |
| template<class TMapFunction> |
| void MapCalleeSources(TMapFunction map) |
| { |
| if (this->HaveCalleeSources()) |
| { |
| calleeUtf8SourceInfoList->Map([&](uint i, RecyclerWeakReference<Js::Utf8SourceInfo>* sourceInfoWeakRef) |
| { |
| if (calleeUtf8SourceInfoList->IsItemValid(i)) |
| { |
| Js::Utf8SourceInfo* sourceInfo = sourceInfoWeakRef->Get(); |
| map(sourceInfo); |
| } |
| }); |
| } |
| if (calleeUtf8SourceInfoList) |
| { |
| calleeUtf8SourceInfoList.Unroot(this->GetRecycler()); |
| } |
| } |
| |
| #ifdef ASMJS_PLAT |
| inline AsmJsCodeGenerator* GetAsmJsCodeGenerator() const{return asmJsCodeGenerator;} |
| AsmJsCodeGenerator* InitAsmJsCodeGenerator(); |
| #endif |
| |
| bool IsExceptionWrapperForBuiltInsEnabled(); |
| static bool IsExceptionWrapperForBuiltInsEnabled(ScriptContext* scriptContext); |
| static bool IsExceptionWrapperForHelpersEnabled(ScriptContext* scriptContext); |
| #ifdef ENABLE_SCRIPT_PROFILING |
| bool IsEnumerateNonUserFunctionsOnly() const { return m_enumerateNonUserFunctionsOnly; } |
| bool IsTraceDomCall() const { return !!m_fTraceDomCall; } |
| #endif |
| |
| InlineCache * GetValueOfInlineCache() const { return valueOfInlineCache;} |
| InlineCache * GetToStringInlineCache() const { return toStringInlineCache; } |
| |
| FunctionBody * GetFakeGlobalFuncForUndefer() const { return fakeGlobalFuncForUndefer; } |
| void SetFakeGlobalFuncForUndefer(FunctionBody * func) { fakeGlobalFuncForUndefer.Root(func, GetRecycler()); } |
| |
| private: |
| PropertyStringMap* propertyStrings[80]; |
| |
| JavascriptFunction* GenerateRootFunction(ParseNodePtr parseTree, uint sourceIndex, Parser* parser, uint32 grfscr, CompileScriptException * pse, const char16 *rootDisplayName); |
| |
| typedef void (*EventHandler)(ScriptContext *); |
| ScriptContext ** registeredPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext; |
| |
| ArenaAllocator generalAllocator; |
| #ifdef ENABLE_BASIC_TELEMETRY |
| ArenaAllocator telemetryAllocator; |
| #endif |
| |
| ArenaAllocator dynamicProfileInfoAllocator; |
| InlineCacheAllocator inlineCacheAllocator; |
| IsInstInlineCacheAllocator isInstInlineCacheAllocator; |
| |
| ArenaAllocator* interpreterArena; |
| ArenaAllocator* guestArena; |
| |
| ArenaAllocator* diagnosticArena; |
| |
| bool startupComplete; // Indicates if the heuristic startup phase for this script context is complete |
| bool isInvalidatedForHostObjects; // Indicates that we've invalidate all objects in the host so stop calling them. |
| bool isEnumeratingRecyclerObjects; // Indicates this scriptContext is enumerating recycler objects. Used by recycler enumerating callbacks to filter out other unrelated scriptContexts. |
| bool m_enumerateNonUserFunctionsOnly; // Indicates that recycler enumeration callback will consider only non-user functions (which are built-ins, external, winrt etc). |
| |
| ThreadContext* threadContext; |
| TypeId directHostTypeId; |
| |
| InlineCache * valueOfInlineCache; |
| InlineCache * toStringInlineCache; |
| |
| typedef JsUtil::BaseHashSet<Js::PropertyId, ArenaAllocator> PropIdSetForConstProp; |
| PropIdSetForConstProp * intConstPropsOnGlobalObject; |
| PropIdSetForConstProp * intConstPropsOnGlobalUserObject; |
| |
| void * firstInterpreterFrameReturnAddress; |
| #ifdef SEPARATE_ARENA |
| ArenaAllocator sourceCodeAllocator; |
| ArenaAllocator regexAllocator; |
| #endif |
| #ifdef NEED_MISC_ALLOCATOR |
| ArenaAllocator miscAllocator; |
| #endif |
| |
| #if DBG |
| JsUtil::BaseHashSet<void *, ArenaAllocator> bindRef; |
| int m_iProfileSession; |
| #endif |
| |
| #ifdef PROFILE_EXEC |
| ScriptContextProfiler * profiler; |
| bool isProfilerCreated; |
| bool disableProfiler; |
| bool ensureParentInfo; |
| |
| Profiler * CreateProfiler(); |
| #endif |
| #ifdef PROFILE_MEM |
| bool profileMemoryDump; |
| #endif |
| #ifdef PROFILE_STRINGS |
| StringProfiler* stringProfiler; |
| #endif |
| |
| RecyclerRootPtr<FunctionBody> fakeGlobalFuncForUndefer; |
| |
| public: |
| #ifdef PROFILE_TYPES |
| int convertNullToSimpleCount; |
| int convertNullToSimpleDictionaryCount; |
| int convertNullToDictionaryCount; |
| int convertDeferredToDictionaryCount; |
| int convertDeferredToSimpleDictionaryCount; |
| int convertSimpleToDictionaryCount; |
| int convertSimpleToSimpleDictionaryCount; |
| int convertPathToDictionaryCount1; |
| int convertPathToDictionaryCount2; |
| int convertPathToDictionaryCount3; |
| int convertPathToDictionaryCount4; |
| int convertPathToSimpleDictionaryCount; |
| int convertSimplePathToPathCount; |
| int convertSimpleDictionaryToDictionaryCount; |
| int convertSimpleSharedDictionaryToNonSharedCount; |
| int convertSimpleSharedToNonSharedCount; |
| int simplePathTypeHandlerCount; |
| int pathTypeHandlerCount; |
| int promoteCount; |
| int cacheCount; |
| int branchCount; |
| int maxPathLength; |
| int typeCount[TypeIds_Limit]; |
| int instanceCount[TypeIds_Limit]; |
| #endif |
| |
| |
| #ifdef PROFILE_BAILOUT_RECORD_MEMORY |
| __int64 bailOutRecordBytes; |
| __int64 bailOutOffsetBytes; |
| __int64 codeSize; |
| #endif |
| |
| #ifdef PROFILE_OBJECT_LITERALS |
| int objectLiteralInstanceCount; |
| int objectLiteralPathCount; |
| int objectLiteralCount[TypePath::MaxPathTypeHandlerLength]; |
| int objectLiteralSimpleDictionaryCount; |
| uint32 objectLiteralMaxLength; |
| int objectLiteralPromoteCount; |
| int objectLiteralCacheCount; |
| int objectLiteralBranchCount; |
| #endif |
| #if DBG_DUMP |
| uint byteCodeDataSize; |
| uint byteCodeAuxiliaryDataSize; |
| uint byteCodeAuxiliaryContextDataSize; |
| uint byteCodeHistogram[static_cast<uint>(OpCode::ByteCodeLast)]; |
| uint32 forinCache; |
| uint32 forinNoCache; |
| #endif |
| #ifdef BGJIT_STATS |
| uint interpretedCount; |
| uint funcJITCount; |
| uint loopJITCount; |
| uint bytecodeJITCount; |
| uint interpretedCallsHighPri; |
| uint maxFuncInterpret; |
| uint jitCodeUsed; |
| uint funcJitCodeUsed; |
| uint speculativeJitCount; |
| #endif |
| |
| #ifdef REJIT_STATS |
| // Used to store bailout stats |
| typedef JsUtil::BaseDictionary<uint, uint, ArenaAllocator> BailoutStatsMap; |
| |
| struct RejitStats |
| { |
| uint *m_rejitReasonCounts; |
| BailoutStatsMap* m_bailoutReasonCounts; |
| |
| uint m_totalRejits; |
| uint m_totalBailouts; |
| |
| RejitStats(ScriptContext *scriptContext) : m_totalRejits(0), m_totalBailouts(0) |
| { |
| m_rejitReasonCounts = AnewArrayZ(scriptContext->GeneralAllocator(), uint, NumRejitReasons); |
| m_bailoutReasonCounts = Anew(scriptContext->GeneralAllocator(), BailoutStatsMap, scriptContext->GeneralAllocator()); |
| } |
| }; |
| |
| void LogDataForFunctionBody(Js::FunctionBody *body, uint idx, bool isRejit); |
| |
| void LogRejit(Js::FunctionBody *body, uint reason); |
| void LogBailout(Js::FunctionBody *body, uint kind); |
| |
| // Used to centrally collect stats for all function bodies. |
| typedef JsUtil::WeaklyReferencedKeyDictionary<const Js::FunctionBody, RejitStats*> RejitStatsMap; |
| RejitStatsMap* rejitStatsMap; |
| |
| BailoutStatsMap *bailoutReasonCounts; |
| uint *rejitReasonCounts; |
| #endif |
| #ifdef ENABLE_BASIC_TELEMETRY |
| |
| private: |
| ScriptContextTelemetry* telemetry; |
| public: |
| ScriptContextTelemetry& GetTelemetry(); |
| bool HasTelemetry(); |
| |
| #endif |
| #ifdef INLINE_CACHE_STATS |
| // Used to store inline cache stats |
| |
| struct CacheData |
| { |
| uint hits; |
| uint misses; |
| uint collisions; |
| bool isGetCache; |
| Js::PropertyId propertyId; |
| |
| CacheData() : hits(0), misses(0), collisions(0), isGetCache(false), propertyId(Js::PropertyIds::_none) { } |
| }; |
| |
| // This is a strongly referenced dictionary, since we want to know hit rates for dead caches. |
| typedef JsUtil::BaseDictionary<const Js::PolymorphicInlineCache*, CacheData*, Recycler> CacheDataMap; |
| CacheDataMap *cacheDataMap; |
| |
| void LogCacheUsage(Js::PolymorphicInlineCache *cache, bool isGet, Js::PropertyId propertyId, bool hit, bool collision); |
| #endif |
| |
| #ifdef FIELD_ACCESS_STATS |
| typedef SList<FieldAccessStatsPtr, Recycler> FieldAccessStatsList; |
| |
| struct FieldAccessStatsEntry |
| { |
| RecyclerWeakReference<FunctionBody>* functionBodyWeakRef; |
| FieldAccessStatsList stats; |
| |
| FieldAccessStatsEntry(RecyclerWeakReference<FunctionBody>* functionBodyWeakRef, Recycler* recycler) |
| : functionBodyWeakRef(functionBodyWeakRef), stats(recycler) {} |
| }; |
| |
| typedef JsUtil::BaseDictionary<uint, FieldAccessStatsEntry*, Recycler> FieldAccessStatsByFunctionNumberMap; |
| |
| FieldAccessStatsByFunctionNumberMap* fieldAccessStatsByFunctionNumber; |
| |
| void RecordFieldAccessStats(FunctionBody* functionBody, FieldAccessStatsPtr fieldAccessStats); |
| #endif |
| |
| #ifdef MISSING_PROPERTY_STATS |
| int missingPropertyMisses; |
| int missingPropertyHits; |
| int missingPropertyCacheAttempts; |
| |
| void RecordMissingPropertyMiss(); |
| void RecordMissingPropertyHit(); |
| void RecordMissingPropertyCacheAttempt(); |
| #endif |
| |
| bool IsIntConstPropertyOnGlobalObject(Js::PropertyId propId); |
| void TrackIntConstPropertyOnGlobalObject(Js::PropertyId propId); |
| bool IsIntConstPropertyOnGlobalUserObject(Js::PropertyId propertyId); |
| void TrackIntConstPropertyOnGlobalUserObject(Js::PropertyId propertyId); |
| |
| private: |
| // |
| // Regex globals |
| // |
| #if ENABLE_REGEX_CONFIG_OPTIONS |
| UnifiedRegex::DebugWriter* regexDebugWriter; |
| UnifiedRegex::RegexStatsDatabase* regexStatsDatabase; |
| #endif |
| UnifiedRegex::TrigramAlphabet* trigramAlphabet; |
| UnifiedRegex::RegexStacks *regexStacks; |
| |
| JsUtil::Stack<Var>* operationStack; |
| Recycler* recycler; |
| RecyclerJavascriptNumberAllocator numberAllocator; |
| |
| ScriptConfiguration config; |
| CharClassifier *charClassifier; |
| |
| // DisableJIT-TODO: Switch this to Dynamic thunk ifdef instead |
| #if ENABLE_NATIVE_CODEGEN |
| #if DYNAMIC_INTERPRETER_THUNK |
| InterpreterThunkEmitter* interpreterThunkEmitter; |
| #endif |
| BackgroundParser *backgroundParser; |
| #ifdef ASMJS_PLAT |
| InterpreterThunkEmitter* asmJsInterpreterThunkEmitter; |
| AsmJsCodeGenerator* asmJsCodeGenerator; |
| typedef JsUtil::BaseDictionary<void *, SList<AsmJsScriptFunction *>*, ArenaAllocator> AsmFunctionMap; |
| AsmFunctionMap* asmJsEnvironmentMap; |
| ArenaAllocator* debugTransitionAlloc; |
| #endif |
| NativeCodeGenerator* nativeCodeGen; |
| #endif |
| |
| DateTime::DaylightTimeHelper daylightTimeHelper; |
| DateTime::Utility dateTimeUtility; |
| |
| public: |
| inline const WCHAR *const GetStandardName(size_t *nameLength, DateTime::YMD *ymd = NULL) |
| { |
| return dateTimeUtility.GetStandardName(nameLength, ymd); |
| } |
| |
| inline const WCHAR *const GetDaylightName(size_t *nameLength, DateTime::YMD *ymd = NULL) |
| { |
| return dateTimeUtility.GetDaylightName(nameLength, ymd); |
| } |
| |
| private: |
| HostScriptContext * hostScriptContext; |
| HaltCallback* scriptEngineHaltCallback; |
| EventHandler scriptStartEventHandler; |
| EventHandler scriptEndEventHandler; |
| #ifdef FAULT_INJECTION |
| EventHandler disposeScriptByFaultInjectionEventHandler; |
| #endif |
| |
| JsUtil::BaseDictionary<uint, JavascriptString *, ArenaAllocator> integerStringMap; |
| |
| double lastNumberToStringRadix10; |
| double lastUtcTimeFromStr; |
| |
| #if ENABLE_PROFILE_INFO |
| bool referencesSharedDynamicSourceContextInfo; |
| #endif |
| |
| // We could delay the actual close after callRootLevel is 0. |
| // this is to indicate the actual close is called once only. |
| bool isScriptContextActuallyClosed; |
| bool isFinalized; |
| #if DBG |
| bool isInitialized; |
| bool isCloningGlobal; |
| #endif |
| bool fastDOMenabled; |
| bool hasUsedInlineCache; |
| bool hasProtoOrStoreFieldInlineCache; |
| bool hasIsInstInlineCache; |
| bool deferredBody; |
| bool isPerformingNonreentrantWork; |
| bool isDiagnosticsScriptContext; // mentions that current script context belongs to the diagnostics OM. |
| |
| size_t sourceSize; |
| |
| void CleanSourceListInternal(bool calledDuringMark); |
| typedef JsUtil::List<RecyclerWeakReference<Utf8SourceInfo>*, Recycler, false, Js::FreeListedRemovePolicy> SourceList; |
| RecyclerRootPtr<SourceList> sourceList; |
| |
| #ifdef ENABLE_SCRIPT_PROFILING |
| IActiveScriptProfilerHeapEnum* heapEnum; |
| |
| // Profiler Probes |
| // In future these can be list of callbacks ? |
| // Profiler Callback information |
| IActiveScriptProfilerCallback *m_pProfileCallback; |
| BOOL m_fTraceFunctionCall; |
| BOOL m_fTraceNativeFunctionCall; |
| DWORD m_dwEventMask; |
| |
| IActiveScriptProfilerCallback2 *m_pProfileCallback2; |
| BOOL m_fTraceDomCall; |
| BOOL m_inProfileCallback; |
| #endif // ENABLE_SCRIPT_PROFILING |
| |
| #if ENABLE_PROFILE_INFO |
| #if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION) |
| RecyclerRootPtr<SListBase<DynamicProfileInfo *>> profileInfoList; |
| #endif |
| #endif |
| #if DEBUG |
| static int scriptContextCount; |
| #endif |
| // List of weak reference dictionaries. We'll walk through them |
| // and clean them up post-collection |
| // IWeakReferenceDictionary objects are added to this list by calling |
| // RegisterWeakReferenceDictionary. If you use JsUtil::WeakReferenceDictionary, |
| // which also exposes the IWeakReferenceDictionary interface, it'll |
| // automatically register the dictionary on the script context |
| SListBase<JsUtil::IWeakReferenceDictionary*> weakReferenceDictionaryList; |
| bool isWeakReferenceDictionaryListCleared; |
| |
| typedef void(*RaiseMessageToDebuggerFunctionType)(ScriptContext *, DEBUG_EVENT_INFO_TYPE, LPCWSTR, LPCWSTR); |
| RaiseMessageToDebuggerFunctionType raiseMessageToDebuggerFunctionType; |
| |
| typedef void(*TransitionToDebugModeIfFirstSourceFn)(ScriptContext *, Utf8SourceInfo *); |
| TransitionToDebugModeIfFirstSourceFn transitionToDebugModeIfFirstSourceFn; |
| |
| #ifdef ENABLE_DOM_FAST_PATH |
| // Theoretically we can put this in ThreadContext; don't want to keep the dictionary forever, and preserve the possibility of |
| // using JavascriptFunction as key. |
| DOMFastPathIRHelperMap* domFastPathIRHelperMap; |
| #endif |
| |
| |
| ScriptContext(ThreadContext* threadContext); |
| void InitializeAllocations(); |
| void InitializePreGlobal(); |
| void InitializePostGlobal(); |
| void InitializeCache(); |
| |
| // Source Info |
| void EnsureSourceContextInfoMap(); |
| void EnsureDynamicSourceContextInfoMap(); |
| |
| uint moduleSrcInfoCount; |
| #ifdef RUNTIME_DATA_COLLECTION |
| time_t createTime; |
| #endif |
| char16 const * url; |
| |
| void PrintStats(); |
| BOOL LeaveScriptStartCore(void * frameAddress, bool leaveForHost); |
| |
| void InternalClose(); |
| |
| DebugContext* debugContext; |
| CriticalSection debugContextCloseCS; |
| |
| public: |
| static const int kArrayMatchCh=72; |
| static const int kMaxArrayMatchIndex=8192; |
| short arrayMatchItems[kArrayMatchCh]; |
| bool arrayMatchInit; |
| |
| #ifdef LEAK_REPORT |
| LeakReport::UrlRecord * urlRecord; |
| bool isRootTrackerScriptContext; |
| #endif |
| |
| DateTime::DaylightTimeHelper *GetDaylightTimeHelper() { return &daylightTimeHelper; } |
| DateTime::Utility *GetDateUtility() { return &dateTimeUtility; } |
| |
| bool IsClosed() const { return isClosed; } |
| bool IsFinalized() const { return isFinalized; } |
| void SetIsFinalized() { isFinalized = true; } |
| bool IsActuallyClosed() const { return isScriptContextActuallyClosed; } |
| #if ENABLE_NATIVE_CODEGEN |
| bool IsClosedNativeCodeGenerator() const |
| { |
| return !nativeCodeGen || ::IsClosedNativeCodeGenerator(nativeCodeGen); |
| } |
| #endif |
| |
| void SetHasUsedInlineCache(bool value) { hasUsedInlineCache = value; } |
| |
| void SetDirectHostTypeId(TypeId typeId) {directHostTypeId = typeId; } |
| TypeId GetDirectHostTypeId() const { return directHostTypeId; } |
| |
| #ifdef ENABLE_DOM_FAST_PATH |
| DOMFastPathIRHelperMap* EnsureDOMFastPathIRHelperMap(); |
| #endif |
| char16 const * GetUrl() const { return url; } |
| void SetUrl(BSTR bstr); |
| #ifdef RUNTIME_DATA_COLLECTION |
| time_t GetCreateTime() const { return createTime; } |
| uint GetAllocId() const { return allocId; } |
| #endif |
| void InitializeArrayMatch() |
| { |
| if (!arrayMatchInit) |
| { |
| for (int i=0;i<kArrayMatchCh;i++) |
| { |
| arrayMatchItems[i]= -1; |
| } |
| arrayMatchInit=true; |
| } |
| } |
| |
| #ifdef HEAP_ENUMERATION_VALIDATION |
| bool IsInitialized() { return this->isInitialized; } |
| #endif |
| |
| DebugContext* GetDebugContext() const { return this->debugContext; } |
| CriticalSection* GetDebugContextCloseCS() { return &debugContextCloseCS; } |
| |
| uint callCount; |
| |
| // Guest arena allocated cache holding references that need to be pinned. |
| Cache* cache; |
| |
| // if the current context is for webworker |
| DWORD webWorkerId; |
| |
| static ScriptContext * New(ThreadContext * threadContext); |
| static void Delete(ScriptContext* scriptContext); |
| |
| ~ScriptContext(); |
| |
| #ifdef PROFILE_TYPES |
| void ProfileTypes(); |
| #endif |
| |
| #ifdef PROFILE_OBJECT_LITERALS |
| void ProfileObjectLiteral(); |
| #endif |
| |
| // |
| // Regex helpers |
| // |
| #if ENABLE_REGEX_CONFIG_OPTIONS |
| UnifiedRegex::RegexStatsDatabase* GetRegexStatsDatabase(); |
| UnifiedRegex::DebugWriter* GetRegexDebugWriter(); |
| #endif |
| RegexPatternMruMap* GetDynamicRegexMap() const; |
| |
| UnifiedRegex::TrigramAlphabet* GetTrigramAlphabet() { return trigramAlphabet; } |
| |
| void SetTrigramAlphabet(UnifiedRegex::TrigramAlphabet * trigramAlphabet); |
| |
| UnifiedRegex::RegexStacks *RegexStacks(); |
| UnifiedRegex::RegexStacks *AllocRegexStacks(); |
| UnifiedRegex::RegexStacks *SaveRegexStacks(); |
| void RestoreRegexStacks(UnifiedRegex::RegexStacks *const contStack); |
| |
| void InitializeGlobalObject(); |
| bool IsIntlEnabled(); |
| JavascriptLibrary* GetLibrary() const { return javascriptLibrary; } |
| const JavascriptLibraryBase* GetLibraryBase() const { return javascriptLibrary->GetLibraryBase(); } |
| #if DBG |
| BOOL IsCloningGlobal() const { return isCloningGlobal;} |
| #endif |
| void PushObject(Var object); |
| Var PopObject(); |
| BOOL CheckObject(Var object); |
| |
| inline bool IsHeapEnumInProgress() { return GetRecycler()->IsHeapEnumInProgress(); } |
| |
| bool IsInterpreted() { return config.IsNoNative(); } |
| void ForceNoNative() { config.ForceNoNative(); } |
| void ForceNative() { config.ForceNative(); } |
| ScriptConfiguration const * GetConfig(void) const { return &config; } |
| CharClassifier const * GetCharClassifier(void) const; |
| |
| ThreadContext * GetThreadContext() const { return threadContext; } |
| |
| static const int MaxEvalSourceSize = 400; |
| |
| bool IsInEvalMap(FastEvalMapString const& key, BOOL isIndirect, ScriptFunction **ppFuncScript); |
| void AddToEvalMap(FastEvalMapString const& key, BOOL isIndirect, ScriptFunction *pFuncScript); |
| |
| template <typename TCacheType> |
| void CleanDynamicFunctionCache(TCacheType* cacheType); |
| void CleanEvalMapCache(Js::EvalCacheTopLevelDictionary * cacheType); |
| |
| template <class TDelegate> |
| void MapFunction(TDelegate mapper); |
| |
| template <class TDelegate> |
| FunctionBody* FindFunction(TDelegate predicate); |
| |
| inline bool EnableEvalMapCleanup() { return CONFIG_FLAG(EnableEvalMapCleanup); }; |
| uint GetNextSourceContextId(); |
| |
| bool IsInNewFunctionMap(EvalMapString const& key, ParseableFunctionInfo **ppFuncBody); |
| void AddToNewFunctionMap(EvalMapString const& key, ParseableFunctionInfo *pFuncBody); |
| |
| SourceContextInfo * GetSourceContextInfo(DWORD_PTR hostSourceContext, IActiveScriptDataCache* profileDataCache); |
| SourceContextInfo * GetSourceContextInfo(uint hash); |
| SourceContextInfo * CreateSourceContextInfo(uint hash, DWORD_PTR hostSourceContext); |
| SourceContextInfo * CreateSourceContextInfo(DWORD_PTR hostSourceContext, char16 const * url, size_t len, |
| IActiveScriptDataCache* profileDataCache, char16 const * sourceMapUrl = nullptr, size_t sourceMapUrlLen = 0); |
| |
| #if defined(LEAK_REPORT) || defined(CHECK_MEMORY_LEAK) |
| void ClearSourceContextInfoMaps() |
| { |
| if (this->cache != nullptr) |
| { |
| this->cache->sourceContextInfoMap = nullptr; |
| this->cache->dynamicSourceContextInfoMap = nullptr; |
| #if ENABLE_PROFILE_INFO |
| this->referencesSharedDynamicSourceContextInfo = false; |
| #endif |
| } |
| } |
| #endif |
| |
| #if ENABLE_PROFILE_INFO |
| #if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION) |
| void ClearDynamicProfileList() |
| { |
| if (profileInfoList) |
| { |
| profileInfoList->Reset(); |
| profileInfoList.Unroot(this->recycler); |
| } |
| } |
| |
| SListBase<DynamicProfileInfo *> * GetProfileInfoList() { return profileInfoList; } |
| #endif |
| #endif |
| |
| SRCINFO const * GetModuleSrcInfo(Js::ModuleID moduleID); |
| SourceContextInfoMap* GetSourceContextInfoMap() |
| { |
| return (this->cache ? this->cache->sourceContextInfoMap : nullptr); |
| } |
| DynamicSourceContextInfoMap* GetDynamicSourceContextInfoMap() |
| { |
| return (this->cache ? this->cache->dynamicSourceContextInfoMap : nullptr); |
| } |
| |
| #if ENABLE_TTD |
| //The host callback functor info |
| HostScriptContextCallbackFunctor TTDHostCallbackFunctor; |
| |
| //The TTDMode for this script context (the same as the Mode for the thread context but we put it here for fast lookup when identity tagging) |
| TTD::TTDMode TTDMode; |
| |
| //The LogTag for this script context (the same as the tag for the global object but we put it here for fast lookup) |
| TTD_LOG_PTR_ID ScriptContextLogTag; |
| |
| //Keep track of the number of re-entrant calls currently pending (i.e., if we make an external call it may call back into Chakra) |
| int32 TTDRootNestingCount; |
| |
| //Info about the core image for the context |
| TTD::RuntimeContextInfo* TTDWellKnownInfo; |
| |
| //Additional runtime context that we only care about if TTD is running (or will be running) |
| TTD::ScriptContextTTD* TTDContextInfo; |
| |
| //// |
| |
| //Use this to check specifically if we are in record AND this code is being run on behalf of the user application |
| bool ShouldPerformRecordAction() const |
| { |
| //return true if RecordEnabled and ~ExcludedExecution |
| return (this->TTDMode & TTD::TTDMode::TTDShouldRecordActionMask) == TTD::TTDMode::RecordEnabled; |
| } |
| |
| //Use this to check specifically if we are in debugging mode AND this code is being run on behalf of the user application |
| bool ShouldPerformDebugAction() const |
| { |
| #if ENABLE_TTD_DEBUGGING |
| //return true if DebuggingEnabled and ~ExcludedExecution |
| return (this->TTDMode & TTD::TTDMode::TTDShouldDebugActionMask) == TTD::TTDMode::DebuggingEnabled; |
| |
| #else |
| return false; |
| #endif |
| } |
| |
| //Use this to check if the TTD has been set into record/replay mode (although we still need to check if we should do any record ro replay) |
| bool IsTTDActive() const |
| { |
| return (this->TTDMode & TTD::TTDMode::TTDActive) != TTD::TTDMode::Invalid; |
| } |
| |
| //Use this to check if the TTD has been detached (e.g., has traced a context execution and has now been detached) |
| bool IsTTDDetached() const |
| { |
| return (this->TTDMode & TTD::TTDMode::Detached) != TTD::TTDMode::Invalid; |
| } |
| |
| //A special record check because we want to record code load even if we aren't actively logging (but are planning to do so in the future) |
| bool ShouldPerformRecordTopLevelFunction() const |
| { |
| bool modeIsPending = (this->TTDMode & TTD::TTDMode::Pending) == TTD::TTDMode::Pending; |
| bool modeIsRecord = (this->TTDMode & TTD::TTDMode::RecordEnabled) == TTD::TTDMode::RecordEnabled; |
| bool inDebugableCode = (this->TTDMode & TTD::TTDMode::ExcludedExecution) == TTD::TTDMode::Invalid; |
| |
| return ((modeIsPending | modeIsRecord) & inDebugableCode); |
| } |
| |
| //A special record check because we want to record root add/remove ref even if we aren't actively logging (but are planning to do so in the future) |
| bool ShouldPerformRootAddOrRemoveRefAction() const |
| { |
| bool modeIsPending = (this->TTDMode & TTD::TTDMode::Pending) == TTD::TTDMode::Pending; |
| bool modeIsRecord = (this->TTDMode & TTD::TTDMode::RecordEnabled) == TTD::TTDMode::RecordEnabled; |
| bool inDebugableCode = (this->TTDMode & TTD::TTDMode::ExcludedExecution) == TTD::TTDMode::Invalid; |
| |
| return ((modeIsPending | modeIsRecord) & inDebugableCode); |
| } |
| |
| //A special record check because we want to take action on async buffer registration and completion even if we have not started actively logging (but are planning to do so in the future) |
| bool ShouldPerformAsyncBufferModAction() const |
| { |
| bool modeIsPending = (this->TTDMode & TTD::TTDMode::Pending) == TTD::TTDMode::Pending; |
| bool modeIsRecord = (this->TTDMode & TTD::TTDMode::RecordEnabled) == TTD::TTDMode::RecordEnabled; |
| bool inDebugableCode = (this->TTDMode & TTD::TTDMode::ExcludedExecution) == TTD::TTDMode::Invalid; |
| |
| return ((modeIsPending | modeIsRecord) & inDebugableCode); |
| } |
| |
| //A special check for to see if we want to push the supression flag for getter exection |
| bool ShouldDoGetterInvocationSupression() const |
| { |
| #if !ENABLE_TTD_DEBUGGING |
| return false; |
| #else |
| return (this->TTDMode & TTD::TTDMode::DebuggingEnabled) == TTD::TTDMode::DebuggingEnabled; |
| #endif |
| } |
| |
| //A special check to see if we are debugging and want to suppress the execution of getters when displaying values in the debugger |
| bool ShouldSuppressGetterInvocationForDebuggerEvaluation() const |
| { |
| #if !ENABLE_TTD_DEBUGGING |
| return false; |
| #else |
| return (this->TTDMode & TTD::TTDMode::TTDShouldSupressGetterActionMask) == TTD::TTDMode::TTDShouldSupressGetterActionMask; |
| #endif |
| } |
| |
| //A special check to see if we are in the process of a time-travel move and do not want to stop at any breakpoints |
| bool ShouldSuppressBreakpointsForTimeTravelMove() const |
| { |
| #if !ENABLE_TTD_DEBUGGING |
| return false; |
| #else |
| return (this->TTDMode & TTD::TTDMode::DebuggerSuppressBreakpoints) == TTD::TTDMode::DebuggerSuppressBreakpoints; |
| #endif |
| } |
| |
| //A special check to see if we are in the process of a time-travel move and do not want to stop at any breakpoints |
| bool ShouldRecordBreakpointsDuringTimeTravelScan() const |
| { |
| #if !ENABLE_TTD_DEBUGGING |
| return false; |
| #else |
| return (this->TTDMode & TTD::TTDMode::DebuggerLogBreakpoints) == TTD::TTDMode::DebuggerLogBreakpoints; |
| #endif |
| } |
| |
| // |
| //TODO: this is currently called explicitly -- we need to fix up the core image computation and this will be eliminated then |
| // |
| //Initialize the core object image for TTD |
| void InitializeCoreImage_TTD(); |
| |
| //Initialize debug script generation and no-native as needed for replay/debug at script context initialization |
| void InitializeRecordingActionsAsNeeded_TTD(); |
| void InitializeDebuggingActionsAsNeeded_TTD(); |
| #endif |
| |
| void SetFirstInterpreterFrameReturnAddress(void * returnAddress) { firstInterpreterFrameReturnAddress = returnAddress;} |
| void *GetFirstInterpreterFrameReturnAddress() { return firstInterpreterFrameReturnAddress;} |
| |
| void CleanupWeakReferenceDictionaries(); |
| |
| void Initialize(); |
| bool Close(bool inDestructor); |
| void MarkForClose(); |
| #ifdef ENABLE_PROJECTION |
| void SetHostType(int32 hostType) { config.SetHostType(hostType); } |
| void SetWinRTConstructorAllowed(bool allowed) { config.SetWinRTConstructorAllowed(allowed); } |
| void SetProjectionTargetVersion(DWORD version) { config.SetProjectionTargetVersion(version); } |
| #endif |
| void SetCanOptimizeGlobalLookupFlag(BOOL f){ config.SetCanOptimizeGlobalLookupFlag(f);} |
| BOOL CanOptimizeGlobalLookup(){ return config.CanOptimizeGlobalLookup();} |
| |
| |
| bool IsClosed() { return isClosed; } |
| bool IsFastDOMEnabled() { return fastDOMenabled; } |
| void SetFastDOMenabled(); |
| BOOL VerifyAlive(BOOL isJSFunction = FALSE, ScriptContext* requestScriptContext = nullptr); |
| void VerifyAliveWithHostContext(BOOL isJSFunction, HostScriptContext* requestHostScriptContext); |
| void AddFunctionBodyToPropIdMap(FunctionBody* body); |
| |
| void BindReference(void* addr); |
| |
| void InitPropertyStringMap(int i); |
| PropertyString* AddPropertyString2(const Js::PropertyRecord* propertyRecord); |
| PropertyString* CachePropertyString2(const Js::PropertyRecord* propertyRecord); |
| PropertyString* GetPropertyString2(char16 ch1, char16 ch2); |
| void FindPropertyRecord(__in LPCWSTR pszPropertyName, __in int propertyNameLength, PropertyRecord const** propertyRecord); |
| JsUtil::List<const RecyclerWeakReference<Js::PropertyRecord const>*>* FindPropertyIdNoCase(__in LPCWSTR pszPropertyName, __in int propertyNameLength); |
| |
| void FindPropertyRecord(JavascriptString* pstName, PropertyRecord const** propertyRecord); |
| PropertyRecord const * GetPropertyName(PropertyId propertyId); |
| PropertyRecord const * GetPropertyNameLocked(PropertyId propertyId); |
| void GetOrAddPropertyRecord(JsUtil::CharacterBuffer<WCHAR> const& propName, PropertyRecord const** propertyRecord); |
| template <size_t N> void GetOrAddPropertyRecord(const char16(&propertyName)[N], PropertyRecord const** propertyRecord) |
| { |
| GetOrAddPropertyRecord(propertyName, N - 1, propertyRecord); |
| } |
| PropertyId GetOrAddPropertyIdTracked(JsUtil::CharacterBuffer<WCHAR> const& propName); |
| template <size_t N> PropertyId GetOrAddPropertyIdTracked(const char16(&propertyName)[N]) |
| { |
| return GetOrAddPropertyIdTracked(propertyName, N - 1); |
| } |
| PropertyId GetOrAddPropertyIdTracked(__in_ecount(propertyNameLength) LPCWSTR pszPropertyName, __in int propertyNameLength); |
| void GetOrAddPropertyRecord(__in_ecount(propertyNameLength) LPCWSTR pszPropertyName, __in int propertyNameLength, PropertyRecord const** propertyRecord); |
| BOOL IsNumericPropertyId(PropertyId propertyId, uint32* value); |
| |
| void RegisterWeakReferenceDictionary(JsUtil::IWeakReferenceDictionary* weakReferenceDictionary); |
| void ResetWeakReferenceDictionaryList() { weakReferenceDictionaryList.Reset(); } |
| |
| BOOL ReserveStaticTypeIds(__in int first, __in int last); |
| TypeId ReserveTypeIds(int count); |
| TypeId CreateTypeId(); |
| |
| WellKnownHostType GetWellKnownHostType(Js::TypeId typeId) { return threadContext->GetWellKnownHostType(typeId); } |
| void SetWellKnownHostTypeId(WellKnownHostType wellKnownType, Js::TypeId typeId) { threadContext->SetWellKnownHostTypeId(wellKnownType, typeId); } |
| |
| ParseNodePtr ParseScript(Parser* parser, const byte* script, size_t cb, SRCINFO const * pSrcInfo, |
| CompileScriptException * pse, Utf8SourceInfo** ppSourceInfo, const char16 *rootDisplayName, LoadScriptFlag loadScriptFlag, uint* sourceIndex); |
| JavascriptFunction* LoadScript(const byte* script, size_t cb, SRCINFO const * pSrcInfo, |
| CompileScriptException * pse, Utf8SourceInfo** ppSourceInfo, const char16 *rootDisplayName, LoadScriptFlag loadScriptFlag); |
| |
| ArenaAllocator* GeneralAllocator() { return &generalAllocator; } |
| |
| #ifdef ENABLE_BASIC_TELEMETRY |
| ArenaAllocator* TelemetryAllocator() { return &telemetryAllocator; } |
| #endif |
| |
| #ifdef SEPARATE_ARENA |
| ArenaAllocator* SourceCodeAllocator() { return &sourceCodeAllocator; } |
| ArenaAllocator* RegexAllocator() { return ®exAllocator; } |
| #else |
| ArenaAllocator* SourceCodeAllocator() { return &generalAllocator; } |
| ArenaAllocator* RegexAllocator() { return &generalAllocator; } |
| #endif |
| #ifdef NEED_MISC_ALLOCATOR |
| ArenaAllocator* MiscAllocator() { return &miscAllocator; } |
| #endif |
| InlineCacheAllocator* GetInlineCacheAllocator() { return &inlineCacheAllocator; } |
| IsInstInlineCacheAllocator* GetIsInstInlineCacheAllocator() { return &isInstInlineCacheAllocator; } |
| ArenaAllocator* DynamicProfileInfoAllocator() { return &dynamicProfileInfoAllocator; } |
| |
| ArenaAllocator* AllocatorForDiagnostics(); |
| |
| Js::TempArenaAllocatorObject* GetTemporaryAllocator(LPCWSTR name); |
| void ReleaseTemporaryAllocator(Js::TempArenaAllocatorObject* tempAllocator); |
| Js::TempGuestArenaAllocatorObject* GetTemporaryGuestAllocator(LPCWSTR name); |
| void ReleaseTemporaryGuestAllocator(Js::TempGuestArenaAllocatorObject* tempAllocator); |
| |
| bool EnsureInterpreterArena(ArenaAllocator **); |
| void ReleaseInterpreterArena(); |
| |
| ArenaAllocator* GetGuestArena() const |
| { |
| return guestArena; |
| } |
| |
| void ReleaseGuestArena(); |
| |
| Recycler* GetRecycler() const { return recycler; } |
| RecyclerJavascriptNumberAllocator * GetNumberAllocator() { return &numberAllocator; } |
| #if ENABLE_NATIVE_CODEGEN |
| NativeCodeGenerator * GetNativeCodeGenerator() const { return nativeCodeGen; } |
| #endif |
| #if ENABLE_BACKGROUND_PARSING |
| BackgroundParser * GetBackgroundParser() const { return backgroundParser; } |
| #endif |
| |
| void OnScriptStart(bool isRoot, bool isScript); |
| void OnScriptEnd(bool isRoot, bool isForcedEnd); |
| |
| template <bool stackProbe, bool leaveForHost> |
| bool LeaveScriptStart(void * frameAddress); |
| template <bool leaveForHost> |
| void LeaveScriptEnd(void * frameAddress); |
| |
| HostScriptContext * GetHostScriptContext() const { return hostScriptContext; } |
| void SetHostScriptContext(HostScriptContext * hostScriptContext); |
| void SetScriptEngineHaltCallback(HaltCallback* scriptEngine); |
| void ClearHostScriptContext(); |
| |
| IActiveScriptProfilerHeapEnum* GetHeapEnum(); |
| void SetHeapEnum(IActiveScriptProfilerHeapEnum* newHeapEnum); |
| void ClearHeapEnum(); |
| |
| void SetScriptStartEventHandler(EventHandler eventHandler); |
| void SetScriptEndEventHandler(EventHandler eventHandler); |
| #ifdef FAULT_INJECTION |
| void DisposeScriptContextByFaultInjection(); |
| void SetDisposeDisposeByFaultInjectionEventHandler(EventHandler eventHandler); |
| #endif |
| EnumeratedObjectCache* GetEnumeratedObjectCache() { return &(cache->enumObjCache); } |
| PropertyString* TryGetPropertyString(PropertyId propertyId); |
| PropertyString* GetPropertyString(PropertyId propertyId); |
| void InvalidatePropertyStringCache(PropertyId propertyId, Type* type); |
| JavascriptString* GetIntegerString(Var aValue); |
| JavascriptString* GetIntegerString(int value); |
| JavascriptString* GetIntegerString(uint value); |
| |
| void CheckEvalRestriction(); |
| |
| RecyclableObject* GetMissingPropertyResult(); |
| RecyclableObject* GetMissingItemResult(); |
| |
| bool HasRecordedException() const { return threadContext->GetRecordedException() != nullptr; } |
| Js::JavascriptExceptionObject * GetAndClearRecordedException(bool *considerPassingToDebugger = nullptr); |
| void RecordException(Js::JavascriptExceptionObject * exceptionObject, bool propagateToDebugger = false); |
| __declspec(noreturn) void RethrowRecordedException(JavascriptExceptionObject::HostWrapperCreateFuncType hostWrapperCreateFunc); |
| |
| #if ENABLE_NATIVE_CODEGEN |
| BOOL IsNativeAddress(void * codeAddr); |
| #endif |
| |
| uint SaveSourceCopy(Utf8SourceInfo* sourceInfo, int cchLength, bool isCesu8); |
| bool SaveSourceCopy(Utf8SourceInfo* const sourceInfo, int cchLength, bool isCesu8, uint * index); |
| |
| uint SaveSourceNoCopy(Utf8SourceInfo* sourceInfo, int cchLength, bool isCesu8); |
| |
| void CloneSources(ScriptContext* sourceContext); |
| Utf8SourceInfo* GetSource(uint sourceIndex); |
| |
| uint SourceCount() const { return (uint)sourceList->Count(); } |
| void CleanSourceList() { CleanSourceListInternal(false); } |
| SourceList* GetSourceList() const { return sourceList; } |
| bool IsItemValidInSourceList(int index); |
| |
| template <typename TFunction> |
| void MapScript(TFunction mapper) |
| { |
| this->sourceList->Map([mapper] (int, RecyclerWeakReference<Utf8SourceInfo>* sourceInfoWeakReference) |
| { |
| Utf8SourceInfo* strongRef = sourceInfoWeakReference->Get(); |
| |
| if (strongRef) |
| { |
| mapper(strongRef); |
| } |
| }); |
| } |
| |
| #ifdef CHECK_STACKWALK_EXCEPTION |
| void SetIgnoreStackWalkException() {threadContext->GetScriptEntryExit()->ignoreStackWalkException = true; } |
| #endif |
| |
| // For debugging scenarios where execution will go to debugging manager and come back to engine again, enforce the current EER to have |
| // 'hasCaller' property set, which will enable the stack walking across frames. |
| // Do not call this directly, look for ENFORCE_ENTRYEXITRECORD_HASCALLER macro. |
| void EnforceEERHasCaller() { threadContext->GetScriptEntryExit()->hasCaller = true; } |
| |
| void SetRaiseMessageToDebuggerFunction(RaiseMessageToDebuggerFunctionType function) |
| { |
| raiseMessageToDebuggerFunctionType = function; |
| } |
| |
| void RaiseMessageToDebugger(DEBUG_EVENT_INFO_TYPE messageType, LPCWSTR message, LPCWSTR url) |
| { |
| if (raiseMessageToDebuggerFunctionType != nullptr) |
| { |
| raiseMessageToDebuggerFunctionType(this, messageType, message, url); |
| } |
| } |
| |
| void SetTransitionToDebugModeIfFirstSourceFn(TransitionToDebugModeIfFirstSourceFn function) |
| { |
| transitionToDebugModeIfFirstSourceFn = function; |
| } |
| |
| void TransitionToDebugModeIfFirstSource(Utf8SourceInfo *sourceInfo) |
| { |
| if (transitionToDebugModeIfFirstSourceFn != nullptr) |
| { |
| transitionToDebugModeIfFirstSourceFn(this, sourceInfo); |
| } |
| } |
| |
| void AddSourceSize(size_t sourceSize) |
| { |
| this->sourceSize += sourceSize; |
| this->threadContext->AddSourceSize(sourceSize); |
| } |
| |
| size_t GetSourceSize() |
| { |
| return this->sourceSize; |
| } |
| |
| BOOL SetDeferredBody(BOOL set) |
| { |
| bool old = this->deferredBody; |
| this->deferredBody = !!set; |
| return old; |
| } |
| |
| BOOL GetDeferredBody(void) const |
| { |
| return this->deferredBody; |
| } |
| |
| public: |
| void FreeFunctionEntryPoint(Js::JavascriptMethod method); |
| |
| private: |
| uint CloneSource(Utf8SourceInfo* info); |
| public: |
| void RegisterProtoInlineCache(InlineCache *pCache, PropertyId propId); |
| void InvalidateProtoCaches(const PropertyId propertyId); |
| void InvalidateAllProtoCaches(); |
| void RegisterStoreFieldInlineCache(InlineCache *pCache, PropertyId propId); |
| void InvalidateStoreFieldCaches(const PropertyId propertyId); |
| void InvalidateAllStoreFieldCaches(); |
| void RegisterIsInstInlineCache(Js::IsInstInlineCache * cache, Js::Var function); |
| #if DBG |
| bool IsIsInstInlineCacheRegistered(Js::IsInstInlineCache * cache, Js::Var function); |
| #endif |
| void ClearInlineCaches(); |
| void ClearIsInstInlineCaches(); |
| #ifdef PERSISTENT_INLINE_CACHES |
| void ClearInlineCachesWithDeadWeakRefs(); |
| #endif |
| void ClearScriptContextCaches(); |
| #if ENABLE_NATIVE_CODEGEN |
| void RegisterConstructorCache(Js::PropertyId propertyId, Js::ConstructorCache* cache); |
| #endif |
| |
| public: |
| void RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext(); |
| private: |
| void DoRegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext(); |
| public: |
| void ClearPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesCaches(); |
| |
| public: |
| JavascriptString * GetLastNumberToStringRadix10(double value); |
| void SetLastNumberToStringRadix10(double value, JavascriptString * str); |
| bool GetLastUtcTimeFromStr(JavascriptString * str, double& dbl); |
| void SetLastUtcTimeFromStr(JavascriptString * str, double value); |
| bool IsNoContextSourceContextInfo(SourceContextInfo *sourceContextInfo) const |
| { |
| return sourceContextInfo == cache->noContextSourceContextInfo; |
| } |
| |
| BOOL IsProfiling() |
| { |
| #ifdef ENABLE_SCRIPT_PROFILING |
| return (m_pProfileCallback != nullptr); |
| #else |
| return FALSE; |
| #endif |
| } |
| |
| BOOL IsInProfileCallback() |
| { |
| #ifdef ENABLE_SCRIPT_PROFILING |
| return m_inProfileCallback; |
| #else |
| return FALSE; |
| #endif |
| } |
| |
| #if DBG |
| SourceContextInfo const * GetNoContextSourceContextInfo() const { return cache->noContextSourceContextInfo; } |
| |
| #ifdef ENABLE_SCRIPT_PROFILING |
| int GetProfileSession() |
| { |
| AssertMsg(m_pProfileCallback != nullptr, "Asking for profile session when we aren't in one."); |
| return m_iProfileSession; |
| } |
| |
| void StartNewProfileSession() |
| { |
| AssertMsg(m_pProfileCallback != nullptr, "New Session when the profiler isn't set to any callback."); |
| m_iProfileSession++; |
| } |
| |
| void StopProfileSession() |
| { |
| AssertMsg(m_pProfileCallback == nullptr, "How to stop when there is still the callback out there"); |
| } |
| #endif // ENABLE_SCRIPT_PROFILING |
| |
| bool hadProfiled; |
| bool HadProfiled() const { return hadProfiled; } |
| #endif |
| |
| SRCINFO *AddHostSrcInfo(SRCINFO const *pSrcInfo); |
| |
| inline void CoreSetProfileEventMask(DWORD dwEventMask); |
| typedef HRESULT (*RegisterExternalLibraryType)(Js::ScriptContext *pScriptContext); |
| #ifdef ENABLE_SCRIPT_PROFILING |
| HRESULT RegisterProfileProbe(IActiveScriptProfilerCallback *pProfileCallback, DWORD dwEventMask, DWORD dwContext, RegisterExternalLibraryType RegisterExternalLibrary, JavascriptMethod dispatchInvoke); |
| #endif |
| HRESULT SetProfileEventMask(DWORD dwEventMask); |
| HRESULT DeRegisterProfileProbe(HRESULT hrReason, JavascriptMethod dispatchInvoke); |
| |
| HRESULT RegisterScript(Js::FunctionProxy *pFunctionBody, BOOL fRegisterScript = TRUE); |
| |
| // Register static and dynamic scripts |
| HRESULT RegisterAllScripts(); |
| |
| // Iterate through utf8sourceinfo and clear debug document if they are there. |
| void EnsureClearDebugDocument(); |
| |
| // To be called directly only when the thread context is shutting down |
| void ShutdownClearSourceLists(); |
| |
| HRESULT RegisterLibraryFunction(const char16 *pwszObjectName, const char16 *pwszFunctionName, Js::PropertyId functionPropertyId, JavascriptMethod entryPoint); |
| |
| HRESULT RegisterBuiltinFunctions(RegisterExternalLibraryType RegisterExternalLibrary); |
| void RegisterDebugThunk(bool calledDuringAttach = true); |
| void UnRegisterDebugThunk(); |
| |
| void UpdateRecyclerFunctionEntryPointsForDebugger(); |
| void SetFunctionInRecyclerToProfileMode(bool enumerateNonUserFunctionsOnly = false); |
| #ifdef ENABLE_SCRIPT_PROFILING |
| static void SetEntryPointToProfileThunk(JavascriptFunction* function); |
| static void RestoreEntryPointFromProfileThunk(JavascriptFunction* function); |
| #endif |
| |
| static void RecyclerEnumClassEnumeratorCallback(void *address, size_t size); |
| static void RecyclerFunctionCallbackForDebugger(void *address, size_t size); |
| |
| static ushort ProcessNameAndGetLength(Js::StringBuilder<ArenaAllocator>* nameBuffer, const WCHAR* name); |
| |
| #ifdef ASMJS_PLAT |
| void TransitionEnvironmentForDebugger(ScriptFunction * scriptFunction); |
| #endif |
| |
| #if ENABLE_NATIVE_CODEGEN |
| HRESULT RecreateNativeCodeGenerator(); |
| #endif |
| |
| HRESULT OnDebuggerAttached(); |
| HRESULT OnDebuggerDetached(); |
| HRESULT OnDebuggerAttachedDetached(bool attach); |
| void InitializeDebugging(); |
| bool IsForceNoNative(); |
| bool IsEnumeratingRecyclerObjects() const { return isEnumeratingRecyclerObjects; } |
| |
| private: |
| class AutoEnumeratingRecyclerObjects |
| { |
| public: |
| AutoEnumeratingRecyclerObjects(ScriptContext* scriptContext): |
| m_scriptContext(scriptContext) |
| { |
| Assert(!m_scriptContext->IsEnumeratingRecyclerObjects()); |
| m_scriptContext->isEnumeratingRecyclerObjects = true; |
| } |
| |
| ~AutoEnumeratingRecyclerObjects() |
| { |
| Assert(m_scriptContext->IsEnumeratingRecyclerObjects()); |
| m_scriptContext->isEnumeratingRecyclerObjects = false; |
| } |
| |
| private: |
| ScriptContext* m_scriptContext; |
| }; |
| |
| #ifdef EDIT_AND_CONTINUE |
| private: |
| ScriptEditQuery* activeScriptEditQuery; |
| |
| void BeginScriptEditEnumFunctions(ScriptEditQuery* scriptEditQuery) { Assert(!activeScriptEditQuery); activeScriptEditQuery = scriptEditQuery; } |
| void EndScriptEditEnumFunctions() { Assert(activeScriptEditQuery); activeScriptEditQuery = nullptr; } |
| public: |
| ScriptEditQuery* GetActiveScriptEditQuery() const { return activeScriptEditQuery; } |
| |
| class AutoScriptEditEnumFunctions |
| { |
| public: |
| AutoScriptEditEnumFunctions(ScriptContext* scriptContext, ScriptEditQuery* scriptEditQuery) : m_scriptContext(scriptContext) |
| { |
| scriptContext->BeginScriptEditEnumFunctions(scriptEditQuery); |
| } |
| ~AutoScriptEditEnumFunctions() { m_scriptContext->EndScriptEditEnumFunctions(); } |
| private: |
| ScriptContext* m_scriptContext; |
| }; |
| #endif |
| |
| private: |
| typedef JsUtil::BaseDictionary<JavascriptMethod, Js::PropertyId, ArenaAllocator, PrimeSizePolicy> BuiltinFunctionIdDictionary; |
| BuiltinFunctionIdDictionary *m_pBuiltinFunctionIdMap; |
| Js::PropertyId GetFunctionNumber(JavascriptMethod entryPoint); |
| |
| static const char16* CopyString(const char16* str, size_t charCount, ArenaAllocator* alloc); |
| static charcount_t AppendWithEscapeCharacters(Js::StringBuilder<ArenaAllocator>* stringBuilder, const WCHAR* sourceString, charcount_t sourceStringLen, WCHAR escapeChar, WCHAR charToEscape); |
| |
| public: |
| #if DYNAMIC_INTERPRETER_THUNK |
| JavascriptMethod GetNextDynamicAsmJsInterpreterThunk(PVOID* ppDynamicInterpreterThunk); |
| JavascriptMethod GetNextDynamicInterpreterThunk(PVOID* ppDynamicInterpreterThunk); |
| BOOL IsDynamicInterpreterThunk(void* address); |
| void ReleaseDynamicInterpreterThunk(BYTE* address, bool addtoFreeList); |
| void ReleaseDynamicAsmJsInterpreterThunk(BYTE* address, bool addtoFreeList); |
| #endif |
| |
| static Var DebugProfileProbeThunk(RecyclableObject* function, CallInfo callInfo, ...); |
| static JavascriptMethod ProfileModeDeferredParse(ScriptFunction **function); |
| static Var ProfileModeDeferredParsingThunk(RecyclableObject* function, CallInfo callInfo, ...); |
| |
| // Thunks for deferred deserialization of function bodies from the byte code cache |
| static JavascriptMethod ProfileModeDeferredDeserialize(ScriptFunction* function); |
| static Var ProfileModeDeferredDeserializeThunk(RecyclableObject* function, CallInfo callInfo, ...); |
| |
| #ifdef ENABLE_SCRIPT_PROFILING |
| void SetProfileMode(BOOL fSet); |
| static JavascriptMethod GetProfileModeThunk(JavascriptMethod entryPoint); |
| static Var ProfileModeThunk_DebugModeWrapper(JavascriptFunction* function, ScriptContext* scriptContext, JavascriptMethod entryPoint, Arguments& args); |
| BOOL GetProfileInfo( |
| JavascriptFunction* function, |
| PROFILER_TOKEN &scriptId, |
| PROFILER_TOKEN &functionId); |
| |
| HRESULT OnScriptCompiled(PROFILER_TOKEN scriptId, PROFILER_SCRIPT_TYPE type, IUnknown *pIDebugDocumentContext); |
| HRESULT OnFunctionCompiled( |
| PROFILER_TOKEN functionId, |
| PROFILER_TOKEN scriptId, |
| const WCHAR *pwszFunctionName, |
| const WCHAR *pwszFunctionNameHint, |
| IUnknown *pIDebugDocumentContext); |
| HRESULT OnFunctionEnter(PROFILER_TOKEN scriptId, PROFILER_TOKEN functionId); |
| HRESULT OnFunctionExit(PROFILER_TOKEN scriptId, PROFILER_TOKEN functionId); |
| |
| static HRESULT FunctionExitSenderThunk(PROFILER_TOKEN functionId, PROFILER_TOKEN scriptId, ScriptContext *pScriptContext); |
| static HRESULT FunctionExitByNameSenderThunk(const char16 *pwszFunctionName, ScriptContext *pScriptContext); |
| #endif // ENABLE_SCRIPT_PROFILING |
| |
| bool SetDispatchProfile(bool fSet, JavascriptMethod dispatchInvoke); |
| HRESULT OnDispatchFunctionEnter(const WCHAR *pwszFunctionName); |
| HRESULT OnDispatchFunctionExit(const WCHAR *pwszFunctionName); |
| |
| void OnStartupComplete(); |
| void SaveStartupProfileAndRelease(bool isSaveOnClose = false); |
| |
| #if ENABLE_PROFILE_INFO |
| void AddDynamicProfileInfo(FunctionBody * functionBody, WriteBarrierPtr<DynamicProfileInfo>* dynamicProfileInfo); |
| #endif |
| #if DBG || defined(RUNTIME_DATA_COLLECTION) |
| uint allocId; |
| #endif |
| |
| #ifdef PROFILE_EXEC |
| void DisableProfiler(); |
| void SetRecyclerProfiler(); |
| void SetProfilerFromScriptContext(ScriptContext * scriptContext); |
| void ProfileBegin(Js::Phase); |
| void ProfileEnd(Js::Phase); |
| void ProfileSuspend(Js::Phase, Js::Profiler::SuspendRecord * suspendRecord); |
| void ProfileResume(Js::Profiler::SuspendRecord * suspendRecord); |
| void ProfilePrint(); |
| bool IsProfilerCreated() const { return isProfilerCreated; } |
| #endif |
| |
| #ifdef PROFILE_MEM |
| void DisableProfileMemoryDumpOnDelete() { profileMemoryDump = false; } |
| #endif |
| |
| #ifdef PROFILE_STRINGS |
| StringProfiler * GetStringProfiler(); // May be null if string profiling not enabled |
| #endif |
| |
| public: |
| void SetBuiltInLibraryFunction(JavascriptMethod entryPoint, JavascriptFunction* function); |
| JavascriptFunction* GetBuiltInLibraryFunction(JavascriptMethod entryPoint); |
| |
| private: |
| BuiltInLibraryFunctionMap* builtInLibraryFunctions; |
| |
| #ifdef RECYCLER_PERF_COUNTERS |
| size_t bindReferenceCount; |
| #endif |
| |
| ScriptContext * nextPendingClose; |
| public: |
| void SetNextPendingClose(ScriptContext * nextPendingClose); |
| inline ScriptContext * GetNextPendingClose() const { return nextPendingClose; } |
| |
| #ifdef ENABLE_MUTATION_BREAKPOINT |
| // Keep track of all breakpoints in order to properly clean up on debugger detach |
| bool HasMutationBreakpoints(); |
| void InsertMutationBreakpoint(Js::MutationBreakpoint *mutationBreakpoint); |
| #endif |
| }; |
| |
| class AutoDynamicCodeReference |
| { |
| public: |
| AutoDynamicCodeReference(ScriptContext* scriptContext): |
| m_scriptContext(scriptContext) |
| { |
| scriptContext->GetLibrary()->BeginDynamicFunctionReferences(); |
| } |
| |
| ~AutoDynamicCodeReference() |
| { |
| m_scriptContext->GetLibrary()->EndDynamicFunctionReferences(); |
| } |
| |
| private: |
| ScriptContext* m_scriptContext; |
| }; |
| |
| template <typename TCacheType> |
| void ScriptContext::CleanDynamicFunctionCache(TCacheType* cacheType) |
| { |
| // Remove eval map functions that haven't been recently used |
| // TODO: Metric based on allocation size too? So don't clean if there hasn't been much allocated? |
| |
| cacheType->Clean([this](const typename TCacheType::KeyType& key, typename TCacheType::ValueType value) { |
| #ifdef ENABLE_DEBUG_CONFIG_OPTIONS |
| if (CONFIG_FLAG(DumpEvalStringOnRemoval)) |
| { |
| Output::Print(_u("EvalMap: Removing Dynamic Function String from dynamic function cache: %s\n"), key.str.GetBuffer()); Output::Flush(); |
| } |
| #endif |
| }); |
| } |
| |
| template <class TDelegate> |
| void ScriptContext::MapFunction(TDelegate mapper) |
| { |
| if (this->sourceList) |
| { |
| this->sourceList->Map([&mapper](int, RecyclerWeakReference<Js::Utf8SourceInfo>* sourceInfo) |
| { |
| Utf8SourceInfo* sourceInfoStrongRef = sourceInfo->Get(); |
| if (sourceInfoStrongRef) |
| { |
| sourceInfoStrongRef->MapFunction(mapper); |
| } |
| }); |
| } |
| } |
| |
| template <class TDelegate> |
| FunctionBody* ScriptContext::FindFunction(TDelegate predicate) |
| { |
| FunctionBody* functionBody = nullptr; |
| |
| this->sourceList->MapUntil([&functionBody, &predicate](int, RecyclerWeakReference<Js::Utf8SourceInfo>* sourceInfo) -> bool |
| { |
| Utf8SourceInfo* sourceInfoStrongRef = sourceInfo->Get(); |
| if (sourceInfoStrongRef) |
| { |
| functionBody = sourceInfoStrongRef->FindFunction(predicate); |
| if (functionBody) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| }); |
| |
| return functionBody; |
| } |
| } |
| |
| #define BEGIN_TEMP_ALLOCATOR(allocator, scriptContext, name) \ |
| Js::TempArenaAllocatorObject *temp##allocator = scriptContext->GetTemporaryAllocator(name); \ |
| ArenaAllocator * allocator = temp##allocator->GetAllocator(); |
| |
| #define END_TEMP_ALLOCATOR(allocator, scriptContext) \ |
| scriptContext->ReleaseTemporaryAllocator(temp##allocator); |
| |
| #define DECLARE_TEMP_ALLOCATOR(allocator) \ |
| Js::TempArenaAllocatorObject *temp##allocator = nullptr; \ |
| ArenaAllocator * allocator = nullptr; |
| |
| #define ACQUIRE_TEMP_ALLOCATOR(allocator, scriptContext, name) \ |
| temp##allocator = scriptContext->GetTemporaryAllocator(name); \ |
| allocator = temp##allocator->GetAllocator(); |
| |
| #define RELEASE_TEMP_ALLOCATOR(allocator, scriptContext) \ |
| if (temp##allocator) \ |
| scriptContext->ReleaseTemporaryAllocator(temp##allocator); |
| |
| #define DECLARE_TEMP_GUEST_ALLOCATOR(allocator) \ |
| Js::TempGuestArenaAllocatorObject *tempGuest##allocator = nullptr; \ |
| ArenaAllocator * allocator = nullptr; |
| |
| #define ACQUIRE_TEMP_GUEST_ALLOCATOR(allocator, scriptContext, name) \ |
| tempGuest##allocator = scriptContext->GetTemporaryGuestAllocator(name); \ |
| allocator = tempGuest##allocator->GetAllocator(); |
| |
| #define RELEASE_TEMP_GUEST_ALLOCATOR(allocator, scriptContext) \ |
| if (tempGuest##allocator) \ |
| scriptContext->ReleaseTemporaryGuestAllocator(tempGuest##allocator); |