blob: b23f0d824288e4ab4c289852354dc245581d689e [file]
//-------------------------------------------------------------------------------------------------------
// 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
namespace Js
{
class ScriptContext;
struct InlineCache;
class DebugManager;
class CodeGenRecyclableData;
struct ReturnedValue;
typedef JsUtil::List<ReturnedValue*> ReturnedValueList;
}
typedef BVSparse<ArenaAllocator> ActiveFunctionSet;
using namespace PlatformAgnostic;
struct IAuthorFileContext;
class HostScriptContext;
class ScriptSite;
class ThreadServiceWrapper;
struct IActiveScriptProfilerHeapEnum;
class DynamicProfileMutator;
class StackProber;
enum DisableImplicitFlags : BYTE
{
DisableImplicitNoFlag = 0x00,
DisableImplicitCallFlag = 0x01,
DisableImplicitExceptionFlag = 0x02,
DisableImplicitCallAndExceptionFlag = DisableImplicitCallFlag | DisableImplicitExceptionFlag
};
enum ThreadContextFlags
{
ThreadContextFlagNoFlag = 0x00000000,
ThreadContextFlagCanDisableExecution = 0x00000001,
ThreadContextFlagEvalDisabled = 0x00000002,
ThreadContextFlagNoJIT = 0x00000004,
};
const int LS_MAX_STACK_SIZE_KB = 300;
struct IProjectionContext
{
public:
virtual HRESULT Close() = 0;
};
class ThreadContext;
class InterruptPoller _ABSTRACT
{
// Interface with a polling object located in the hosting layer.
public:
InterruptPoller(ThreadContext *tc);
virtual ~InterruptPoller() { }
void CheckInterruptPoll();
void GetStatementCount(ULONG *pluHi, ULONG *pluLo);
void ResetStatementCount() { lastResetTick = lastPollTick; }
void StartScript() { lastResetTick = lastPollTick = ::GetTickCount(); }
void EndScript() { lastResetTick = lastPollTick = 0;}
bool IsDisabled() const { return isDisabled; }
void SetDisabled(bool disable) { isDisabled = disable; }
virtual void TryInterruptPoll(Js::ScriptContext *scriptContext) = 0;
// Default: throw up QC dialog after 5M statements == 2 minutes
static const DWORD TicksToStatements = (5000000 / 120000);
protected:
ThreadContext *threadContext;
DWORD lastPollTick;
DWORD lastResetTick;
bool isDisabled;
};
class AutoDisableInterrupt
{
private:
InterruptPoller* interruptPoller;
bool previousState;
public:
AutoDisableInterrupt(InterruptPoller* interruptPoller, bool disable)
: interruptPoller(interruptPoller)
{
if (interruptPoller != nullptr)
{
previousState = interruptPoller->IsDisabled();
interruptPoller->SetDisabled(disable);
}
}
~AutoDisableInterrupt()
{
if (interruptPoller != nullptr)
{
interruptPoller->SetDisabled(previousState);
}
}
};
// This function is called before we step out of script (currently only for WinRT callout).
// Debugger would put a breakpoint on this function if they want to detect the point at which we step
// over the boundary.
// It is intentionally left blank and the next operation should be the callout.
extern "C" void* MarkerForExternalDebugStep();
#define PROBE_STACK(scriptContext, size) ((scriptContext)->GetThreadContext()->ProbeStack(size, scriptContext))
#define PROBE_STACK_NO_DISPOSE(scriptContext, size) ((scriptContext)->GetThreadContext()->ProbeStackNoDispose(size, scriptContext))
#define PROBE_STACK_PARTIAL_INITIALIZED_INTERPRETER_FRAME(scriptContext, size) ((scriptContext)->GetThreadContext()->ProbeStack(size, scriptContext, _ReturnAddress()))
#define PROBE_STACK_PARTIAL_INITIALIZED_BAILOUT_FRAME(scriptContext, size, returnAddress) ((scriptContext)->GetThreadContext()->ProbeStack(size, scriptContext, returnAddress))
#define PROBE_STACK_CALL(scriptContext, obj, size) ((scriptContext)->GetThreadContext()->ProbeStack(size, obj, scriptContext))
#define AssertInScript() Assert(ThreadContext::GetContextForCurrentThread()->IsScriptActive());
#define AssertNotInScript() Assert(!ThreadContext::GetContextForCurrentThread()->IsScriptActive());
#define LEAVE_SCRIPT_START_EX(scriptContext, stackProbe, leaveForHost, isFPUControlRestoreNeeded) \
{ \
void * __frameAddr = nullptr; \
GET_CURRENT_FRAME_ID(__frameAddr); \
Js::LeaveScriptObject<stackProbe, leaveForHost, isFPUControlRestoreNeeded> __leaveScriptObject(scriptContext, __frameAddr);
#define LEAVE_SCRIPT_END_EX(scriptContext) \
if (scriptContext != nullptr) \
{ \
scriptContext->GetThreadContext()->DisposeOnLeaveScript(); \
}\
}
#define LEAVE_SCRIPT_IF(scriptContext, condition, block) \
if (condition) \
{ \
BEGIN_LEAVE_SCRIPT(scriptContext); \
block \
END_LEAVE_SCRIPT(scriptContext); \
} \
else \
{ \
block \
}
#define ENTER_SCRIPT_IF(scriptContext, doCleanup, isCallRoot, hasCaller, condition, block) \
if (condition) \
{ \
BEGIN_ENTER_SCRIPT(scriptContext, doCleanup, isCallRoot, hasCaller); \
block \
END_ENTER_SCRIPT(scriptContext, doCleanup, isCallRoot, hasCaller); \
} \
else \
{ \
block \
}
#define BEGIN_LEAVE_SCRIPT(scriptContext) \
LEAVE_SCRIPT_START_EX(scriptContext, /* stackProbe */ true, /* leaveForHost */ true, /* isFPUControlRestoreNeeded */ false)
#define BEGIN_LEAVE_SCRIPT_SAVE_FPU_CONTROL(scriptContext) \
LEAVE_SCRIPT_START_EX(scriptContext, /* stackProbe */ true, /* leaveForHost */ true, /* isFPUControlRestoreNeeded */ true)
// BEGIN_LEAVE_SCRIPT_INTERNAL is used when there are no explicit external call after leave script,
// but we might have external call when allocation memory doing QC or GC Dispose, which may enter script again.
// This will record the reentry as an implicit call (ImplicitCall_AsyncHostOperation)
#define BEGIN_LEAVE_SCRIPT_INTERNAL(scriptContext) \
LEAVE_SCRIPT_START_EX(scriptContext, /* stackProbe */ true, /* leaveForHost */ false, /* isFPUControlRestoreNeeded */ false)
#define BEGIN_LEAVE_SCRIPT_NO_STACK_PROBE(scriptContext) \
LEAVE_SCRIPT_START_EX(scriptContext, /* stackProbe */ false, /* leaveForHost */ true, /* isFPUControlRestoreNeeded */ false)
#define END_LEAVE_SCRIPT(scriptContext) \
LEAVE_SCRIPT_END_EX(scriptContext)
#define END_LEAVE_SCRIPT_RESTORE_FPU_CONTROL(scriptContext) \
LEAVE_SCRIPT_END_EX(scriptContext)
#define END_LEAVE_SCRIPT_INTERNAL(scriptContext) \
LEAVE_SCRIPT_END_EX(scriptContext)
#define END_LEAVE_SCRIPT_NO_STACK_PROBE(scriptContext) \
LEAVE_SCRIPT_END_EX(scriptContext)
#define BEGIN_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext) \
BEGIN_LEAVE_SCRIPT(scriptContext)
#define END_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext) \
Assert(!scriptContext->HasRecordedException()); \
END_LEAVE_SCRIPT(scriptContext)
// Keep in sync with CollectGarbageCallBackFlags in scriptdirect.idl
enum RecyclerCollectCallBackFlags
{
Collect_Begin = 0x01,
Collect_Begin_Concurrent = 0x11,
Collect_Begin_Partial = 0x21,
Collect_Begin_Concurrent_Partial = Collect_Begin_Concurrent | Collect_Begin_Partial,
Collect_End = 0x02,
Collect_Wait = 0x04 // callback can be from another thread
};
typedef void (__cdecl *RecyclerCollectCallBackFunction)(void * context, RecyclerCollectCallBackFlags flags);
#ifdef ENABLE_PROJECTION
class ExternalWeakReferenceCache
{
public:
virtual void MarkNow(Recycler *recycler, bool inPartialCollect) = 0;
virtual void ResolveNow(Recycler *recycler) = 0;
};
#if DBG_DUMP
class IProjectionContextMemoryInfo abstract
{
public:
virtual void DumpCurrentStats(LPCWSTR headerMsg, bool forceDetailed) = 0;
virtual void Release() = 0;
};
#endif
#endif
#ifdef NTBUILD
struct ThreadContextWatsonTelemetryBlock
{
FILETIME lastScriptStartTime;
FILETIME lastScriptEndTime;
};
#endif
class NativeLibraryEntryRecord
{
public:
struct Entry
{
Js::RecyclableObject* function;
Js::CallInfo callInfo;
PCWSTR name;
PVOID addr;
Entry* next;
};
private:
Entry* head;
public:
NativeLibraryEntryRecord() : head(nullptr)
{
}
const Entry* Peek() const
{
return head;
}
void Push(_In_ Entry* e)
{
e->next = head;
head = e;
}
void Pop()
{
head = head->next;
}
};
class AutoTagNativeLibraryEntry
{
private:
NativeLibraryEntryRecord::Entry entry;
public:
AutoTagNativeLibraryEntry(Js::RecyclableObject* function, Js::CallInfo callInfo, PCWSTR name, void* addr);
~AutoTagNativeLibraryEntry();
};
#define AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, name) \
AutoTagNativeLibraryEntry __tag(function, callInfo, name, _AddressOfReturnAddress())
class ThreadConfiguration
{
public:
ThreadConfiguration(bool enableExperimentalFeatures)
{
CopyGlobalFlags();
if (enableExperimentalFeatures)
{
EnableExperimentalFeatures();
ResetExperimentalFeaturesFromConfig();
}
}
#define DEFINE_FLAG(threadFlag, globalFlag) \
public: \
inline bool threadFlag() const { return m_##globalFlag##; } \
\
private: \
bool m_##globalFlag##;
#define FLAG(threadFlag, globalFlag) DEFINE_FLAG(threadFlag, globalFlag)
#define FLAG_RELEASE(threadFlag, globalFlag) DEFINE_FLAG(threadFlag, globalFlag)
#include "ThreadConfigFlagsList.h"
#undef FLAG_RELEASE
#undef FLAG
#undef DEFINE_FLAG
private:
void CopyGlobalFlags()
{
AutoCriticalSection autocs(&Js::Configuration::Global.flags.csExperimentalFlags);
#define FLAG(threadFlag, globalFlag) m_##globalFlag## = CONFIG_FLAG(globalFlag);
#define FLAG_RELEASE(threadFlag, globalFlag) m_##globalFlag## = CONFIG_FLAG_RELEASE(globalFlag);
#include "ThreadConfigFlagsList.h"
#undef FLAG_RELEASE
#undef FLAG
}
void EnableExperimentalFeatures()
{
// If a ES6 flag is disabled using compile flag don't enable it
#define FLAG_REGOVR_EXP(type, name, ...) m_##name## = COMPILE_DISABLE_##name## ? false : true;
#include "ConfigFlagsList.h"
#undef FLAG_REGOVR_EXP
}
void ResetExperimentalFeaturesFromConfig()
{
// If a flag was overridden using config/command line it should take precedence
#define FLAG_REGOVR_EXP(type, name, ...) if(CONFIG_ISENABLED(Js::Flag::##name##Flag)) { m_##name## = CONFIG_FLAG_RELEASE(##name##); }
#include "ConfigFlagsList.h"
#undef FLAG_REGOVR_EXP
}
};
class ThreadContext sealed :
public DefaultRecyclerCollectionWrapper,
public JsUtil::DoublyLinkedListElement<ThreadContext>,
public ThreadContextInfo
{
public:
static void GlobalInitialize();
static const DWORD NoThread = 0xFFFFFFFF;
struct CollectCallBack
{
RecyclerCollectCallBackFunction callback;
void * context;
};
struct WorkerThread
{
// Abstract notion to hold onto threadHandle of worker thread
HANDLE threadHandle;
WorkerThread(HANDLE handle = nullptr) :threadHandle(handle){};
};
void SetCurrentThreadId(DWORD threadId) { this->currentThreadId = threadId; }
DWORD GetCurrentThreadId() const { return this->currentThreadId; }
void SetIsThreadBound()
{
if (this->recycler)
{
this->recycler->SetIsThreadBound();
}
this->isThreadBound = true;
}
bool IsJSRT() const { return !this->isThreadBound; }
virtual bool IsThreadBound() const override { return this->isThreadBound; }
void SetStackProber(StackProber * stackProber);
static DWORD GetStackLimitForCurrentThreadOffset() { return offsetof(ThreadContext, stackLimitForCurrentThread); }
template <class Fn>
Js::ImplicitCallFlags TryWithDisabledImplicitCall(Fn fn)
{
DisableImplicitFlags prevDisableImplicitFlags = this->GetDisableImplicitFlags();
Js::ImplicitCallFlags savedImplicitCallFlags = this->GetImplicitCallFlags();
this->DisableImplicitCall();
this->SetImplicitCallFlags(Js::ImplicitCallFlags::ImplicitCall_None);
fn();
Js::ImplicitCallFlags curImplicitCallFlags = this->GetImplicitCallFlags();
this->SetDisableImplicitFlags(prevDisableImplicitFlags);
this->SetImplicitCallFlags(savedImplicitCallFlags);
return curImplicitCallFlags;
}
void * GetAddressOfStackLimitForCurrentThread() const
{
FAULTINJECT_SCRIPT_TERMINATION
return &this->stackLimitForCurrentThread;
}
void InitAvailableCommit();
// This is always on for JSRT APIs.
bool IsRentalThreadingEnabledInJSRT() const { return true; }
IActiveScriptProfilerHeapEnum* GetHeapEnum();
void SetHeapEnum(IActiveScriptProfilerHeapEnum* newHeapEnum);
void ClearHeapEnum();
Js::PropertyRecord const * GetPropertyRecord(Js::PropertyId propertyId);
virtual bool IsNumericProperty(Js::PropertyId propertyId) override;
#ifdef ENABLE_BASIC_TELEMETRY
Js::LanguageStats* GetLanguageStats()
{
return langTel.GetLanguageStats();
}
void ResetLangStats()
{
this->langTel.Reset();
}
#endif
#if ENABLE_NATIVE_CODEGEN && defined(ENABLE_SIMDJS)
// used by inliner. Maps Simd FuncInfo (library func) to equivalent opcode.
typedef JsUtil::BaseDictionary<Js::FunctionInfo *, Js::OpCode, ArenaAllocator> FuncInfoToOpcodeMap;
FuncInfoToOpcodeMap * simdFuncInfoToOpcodeMap;
struct SimdFuncSignature
{
bool valid;
uint argCount; // actual arguments count (excluding this)
ValueType returnType;
ValueType *args; // argument types
};
SimdFuncSignature *simdOpcodeToSignatureMap;
void AddSimdFuncToMaps(Js::OpCode op, ...);
void AddSimdFuncInfo(Js::OpCode op, Js::FunctionInfo *funcInfo);
Js::OpCode GetSimdOpcodeFromFuncInfo(Js::FunctionInfo * funcInfo);
void GetSimdFuncSignatureFromOpcode(Js::OpCode op, SimdFuncSignature &funcSignature);
#if _M_IX86 || _M_AMD64
// auxiliary SIMD values in memory to help JIT'ed code. E.g. used for Int8x16 shuffle.
_x86_SIMDValue X86_TEMP_SIMD[SIMD_TEMP_SIZE];
_x86_SIMDValue * GetSimdTempArea() { return X86_TEMP_SIMD; }
#endif
#endif
private:
Js::JavascriptExceptionObject * pendingFinallyException;
bool noScriptScope;
Js::DebugManager * debugManager;
static uint const MaxTemporaryArenaAllocators = 5;
static CriticalSection s_csThreadContext;
StackProber * GetStackProber() const { return this->stackProber; }
size_t GetStackLimitForCurrentThread() const;
void SetStackLimitForCurrentThread(size_t limit);
// The current heap enumeration object being used during enumeration.
IActiveScriptProfilerHeapEnum* heapEnum;
#ifdef ENABLE_BASIC_TELEMETRY
Js::LanguageTelemetry langTel;
#endif
struct PropertyGuardEntry
{
public:
typedef JsUtil::BaseHashSet<RecyclerWeakReference<Js::PropertyGuard>*, Recycler, PowerOf2SizePolicy> PropertyGuardHashSet;
// we do not have WeaklyReferencedKeyHashSet - hence use BYTE as a dummy value.
typedef JsUtil::WeaklyReferencedKeyDictionary<Js::EntryPointInfo, BYTE> EntryPointDictionary;
// The sharedGuard is strongly referenced and will be kept alive by ThreadContext::propertyGuards until it's invalidated or
// the property record itself is collected. If the code using the guard needs access to it after it's been invalidated, it
// (the code) is responsible for keeping it alive.
// Each unique guard, is weakly referenced, such that it can be reclaimed if not referenced elsewhere even without being
// invalidated. The entry of a unique guard is removed from the table once the corresponding cache is invalidated.
Field(Js::PropertyGuard*) sharedGuard;
Field(PropertyGuardHashSet) uniqueGuards;
Field(EntryPointDictionary*) entryPoints;
PropertyGuardEntry(Recycler* recycler) : sharedGuard(nullptr), uniqueGuards(recycler), entryPoints(nullptr) {}
};
public:
typedef JsUtil::BaseHashSet<const Js::PropertyRecord *, HeapAllocator, PrimeSizePolicy, const Js::PropertyRecord *,
Js::PropertyRecordStringHashComparer, JsUtil::SimpleHashedEntry, JsUtil::AsymetricResizeLock> PropertyMap;
PropertyMap * propertyMap;
typedef JsUtil::BaseHashSet<Js::CaseInvariantPropertyListWithHashCode*, Recycler, PowerOf2SizePolicy, Js::CaseInvariantPropertyListWithHashCode*, JsUtil::NoCaseComparer, JsUtil::SimpleDictionaryEntry>
PropertyNoCaseSetType;
typedef JsUtil::WeaklyReferencedKeyDictionary<Js::Type, bool> TypeHashSet;
typedef JsUtil::BaseDictionary<Js::PropertyId, TypeHashSet *, Recycler, PowerOf2SizePolicy> PropertyIdToTypeHashSetDictionary;
typedef JsUtil::WeaklyReferencedKeyDictionary<const Js::PropertyRecord, PropertyGuardEntry*, Js::PropertyRecordPointerComparer> PropertyGuardDictionary;
private:
PTHREADCONTEXT_HANDLE m_remoteThreadContextInfo;
intptr_t m_prereservedRegionAddr;
intptr_t m_jitThunkStartAddr;
#if ENABLE_NATIVE_CODEGEN
BVSparse<HeapAllocator> * m_jitNumericProperties;
bool m_jitNeedsPropertyUpdate;
public:
intptr_t GetPreReservedRegionAddr()
{
return m_prereservedRegionAddr;
}
intptr_t GetJITThunkStartAddr()
{
return m_jitThunkStartAddr;
}
BVSparse<HeapAllocator> * GetJITNumericProperties() const
{
return m_jitNumericProperties;
}
bool JITNeedsPropUpdate() const
{
return m_jitNeedsPropertyUpdate;
}
void ResetJITNeedsPropUpdate()
{
m_jitNeedsPropertyUpdate = false;
}
static void SetJITConnectionInfo(HANDLE processHandle, void* serverSecurityDescriptor, UUID connectionId);
bool EnsureJITThreadContext(bool allowPrereserveAlloc);
PTHREADCONTEXT_HANDLE GetRemoteThreadContextAddr()
{
Assert(m_remoteThreadContextInfo);
return m_remoteThreadContextInfo;
}
#endif
private:
typedef JsUtil::BaseDictionary<uint, Js::SourceDynamicProfileManager*, Recycler, PowerOf2SizePolicy> SourceDynamicProfileManagerMap;
typedef JsUtil::BaseDictionary<const char16*, const Js::PropertyRecord*, Recycler, PowerOf2SizePolicy> SymbolRegistrationMap;
class SourceDynamicProfileManagerCache
{
public:
SourceDynamicProfileManagerCache() : refCount(0), sourceProfileManagerMap(nullptr) {}
Field(SourceDynamicProfileManagerMap*) sourceProfileManagerMap;
void AddRef() { refCount++; }
uint Release() { Assert(refCount > 0); return --refCount; }
private:
Field(uint) refCount; // For every script context using this cache, there is a ref count added.
};
typedef JsUtil::BaseDictionary<const WCHAR*, SourceDynamicProfileManagerCache*, Recycler, PowerOf2SizePolicy> SourceProfileManagersByUrlMap;
struct RecyclableData
{
RecyclableData(Recycler *const recycler);
Field(Js::TempArenaAllocatorObject *) temporaryArenaAllocators[MaxTemporaryArenaAllocators];
Field(Js::TempGuestArenaAllocatorObject *) temporaryGuestArenaAllocators[MaxTemporaryArenaAllocators];
Field(Js::JavascriptExceptionObject *) exceptionObject;
Field(bool) propagateException;
// We throw a JS catchable SO exception if we detect we might overflow the stack. Allocating this (JS)
// object though might really overflow the stack. So use this thread global to identify them from the throw point
// to where they are caught; where the stack has been unwound and it is safer to allocate the real exception
// object and throw.
Field(Js::JavascriptExceptionObject) soErrorObject;
// We can't allocate an out of memory object... So use this static as a way to identify
// them from the throw point to where they are caught.
Field(Js::JavascriptExceptionObject) oomErrorObject;
// This is for JsRT scenario where a runtime is not usable after a suspend request, before a resume runtime call is made
Field(Js::JavascriptExceptionObject) terminatedErrorObject;
Field(Js::JavascriptExceptionObject*) unhandledExceptionObject;
// Used to temporarily keep throwing exception object alive (thrown but not yet caught)
Field(Js::JavascriptExceptionObject*) tempUncaughtException;
// Contains types that have property caches that need to be tracked, as the caches may need to be cleared. Types that
// contain a property cache for a property that is on a prototype object will be tracked in this map since those caches
// need to be cleared if for instance, the property is deleted from the prototype object.
//
// It is expected that over time, types that are deleted will eventually be removed by the weak reference hash sets when
// they're searching through a bucket while registering a type or enumerating types to invalidate, or when a property ID
// is reclaimed. If none of those happen, then this collection may contain weak reference handles to deleted objects
// that would not get removed, but it would also not get any bigger.
Field(PropertyIdToTypeHashSetDictionary) typesWithProtoPropertyCache;
// The property guard dictionary contains property guards which need to be invalidated in response to properties changing
// from writable to read-only and vice versa, properties being shadowed or unshadowed on prototypes, etc. The dictionary
// holds only weak references to property guards and their lifetimes are controlled by their creators (typically entry points).
// When a guard is no longer needed it is garbage collected, but the weak references and dictionary entries remain, until
// the guards for a given property get invalidated.
// TODO: Create and use a self-cleaning weak reference dictionary, which would periodically remove any unused weak references.
Field(PropertyGuardDictionary) propertyGuards;
Field(PropertyNoCaseSetType *) caseInvariantPropertySet;
Field(JsUtil::List<Js::PropertyRecord const*>*) boundPropertyStrings; // Recycler allocated list of property strings that we need to strongly reference so that they're not reclaimed
Field(SourceProfileManagersByUrlMap*) sourceProfileManagersByUrl;
// Used to register recyclable data that needs to be kept alive while jitting
typedef JsUtil::DoublyLinkedList<Js::CodeGenRecyclableData, Recycler> CodeGenRecyclableDataList;
Field(CodeGenRecyclableDataList) codeGenRecyclableDatas;
// Used to root old entry points so that they're not prematurely collected
Field(Js::FunctionEntryPointInfo*) oldEntryPointInfo;
// Used to store a mapping of string to Symbol for cross-realm Symbol registration
// See ES6 (draft 22) 19.4.2.2
Field(SymbolRegistrationMap*) symbolRegistrationMap;
// Just holding the reference to the returnedValueList of the stepController. This way that list will not get recycled prematurely.
Field(Js::ReturnedValueList *) returnedValueList;
Field(uint) constructorCacheInvalidationCount;
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
// use for autoProxy called from Debug.setAutoProxyName. we need to keep the buffer from GetSz() alive.
Field(LPCWSTR) autoProxyName;
#endif
};
static ThreadContext * globalListLast;
ThreadContextFlags threadContextFlags;
DWORD currentThreadId;
mutable size_t stackLimitForCurrentThread;
StackProber * stackProber;
bool isThreadBound;
bool hasThrownPendingException;
bool callDispose;
#if ENABLE_JS_REENTRANCY_CHECK
bool noJsReentrancy;
#endif
AllocationPolicyManager * allocationPolicyManager;
JsUtil::ThreadService threadService;
PreReservedVirtualAllocWrapper preReservedVirtualAllocator;
uint callRootLevel;
#if ENABLE_BACKGROUND_PAGE_FREEING
// The thread page allocator is used by the recycler and need the background page queue
PageAllocator::BackgroundPageQueue backgroundPageQueue;
#endif
IdleDecommitPageAllocator pageAllocator;
Recycler* recycler;
// Fake RecyclerWeakReference for built-in properties
class StaticPropertyRecordReference : public RecyclerWeakReference<const Js::PropertyRecord>
{
public:
StaticPropertyRecordReference(const Js::PropertyRecord* propertyRecord)
{
strongRef = (char*)propertyRecord;
strongRefHeapBlock = &CollectedRecyclerWeakRefHeapBlock::Instance;
}
};
static const Js::PropertyRecord * const builtInPropertyRecords[];
PropertyNoCaseSetType * caseInvariantPropertySet;
Js::ScriptContext * rootPendingClose;
JsUtil::List<IProjectionContext *, ArenaAllocator>* pendingProjectionContextCloseList;
Js::ScriptEntryExitRecord * entryExitRecord;
Js::InterpreterStackFrame* leafInterpreterFrame;
const Js::PropertyRecord * propertyNamesDirect[128];
ArenaAllocator threadAlloc;
ThreadServiceWrapper* threadServiceWrapper;
uint functionCount;
uint sourceInfoCount;
enum RedeferralState
{
InitialRedeferralState,
StartupRedeferralState,
MainRedeferralState
};
RedeferralState redeferralState;
uint gcSinceLastRedeferral;
uint gcSinceCallCountsCollected;
static const uint InitialRedeferralDelay = 5;
static const uint StartupRedeferralCheckInterval = 10;
static const uint StartupRedeferralInactiveThreshold = 5;
static const uint MainRedeferralCheckInterval = 20;
static const uint MainRedeferralInactiveThreshold = 10;
Js::TypeId nextTypeId;
uint32 polymorphicCacheState;
#ifdef ENABLE_PROJECTION
SListBase<ExternalWeakReferenceCache *, HeapAllocator> externalWeakReferenceCacheList;
#if DBG_DUMP
IProjectionContextMemoryInfo *projectionMemoryInformation;
#endif
#endif
#if ENABLE_NATIVE_CODEGEN
JsUtil::JobProcessor *jobProcessor;
Js::Var * bailOutRegisterSaveSpace;
#if !FLOATVAR
CodeGenNumberThreadAllocator * codeGenNumberThreadAllocator;
XProcNumberPageSegmentManager * xProcNumberPageSegmentManager;
#endif
#if DYNAMIC_INTERPRETER_THUNK || defined(ASMJS_PLAT)
CustomHeap::InProcCodePageAllocators thunkPageAllocators;
#endif
CustomHeap::InProcCodePageAllocators codePageAllocators;
#if defined(_CONTROL_FLOW_GUARD) && (_M_IX86 || _M_X64)
InProcJITThunkEmitter jitThunkEmitter;
#endif
#endif
RecyclerRootPtr<RecyclableData> recyclableData;
uint temporaryArenaAllocatorCount;
uint temporaryGuestArenaAllocatorCount;
#if DBG_DUMP || defined(PROFILE_EXEC)
ScriptSite* topLevelScriptSite;
#endif
Js::ScriptContext *scriptContextList;
bool scriptContextEverRegistered;
static size_t processNativeCodeSize;
size_t nativeCodeSize;
size_t sourceCodeSize;
DateTime::HiResTimer hTimer;
int stackProbeCount;
// Count stack probes and poll for continuation every n probes
static const int StackProbePollThreshold = 1000;
EXCEPTION_POINTERS exceptionInfo;
uint32 exceptionCode;
ArenaAllocator inlineCacheThreadInfoAllocator;
ArenaAllocator isInstInlineCacheThreadInfoAllocator;
ArenaAllocator equivalentTypeCacheInfoAllocator;
DListBase<Js::EntryPointInfo *> equivalentTypeCacheEntryPoints;
typedef SList<Js::InlineCache*> InlineCacheList;
typedef JsUtil::BaseDictionary<Js::PropertyId, InlineCacheList*, ArenaAllocator> InlineCacheListMapByPropertyId;
InlineCacheListMapByPropertyId protoInlineCacheByPropId;
InlineCacheListMapByPropertyId storeFieldInlineCacheByPropId;
uint registeredInlineCacheCount;
uint unregisteredInlineCacheCount;
#if DBG
uint totalUnregisteredCacheCount;
#endif
typedef JsUtil::BaseDictionary<Js::Var, Js::IsInstInlineCache*, ArenaAllocator> IsInstInlineCacheListMapByFunction;
IsInstInlineCacheListMapByFunction isInstInlineCacheByFunction;
Js::IsConcatSpreadableCache isConcatSpreadableCache;
ArenaAllocator prototypeChainEnsuredToHaveOnlyWritableDataPropertiesAllocator;
DListBase<Js::ScriptContext *> prototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext;
DListBase<CollectCallBack> collectCallBackList;
CriticalSection csCollectionCallBack;
bool hasCollectionCallBack;
bool isOptimizedForManyInstances;
bool bgJit;
// We report library code to profiler only if called directly by user code. Not if called by library implementation.
bool isProfilingUserCode;
void* jsrtRuntime;
bool hasUnhandledException;
bool hasCatchHandler;
DisableImplicitFlags disableImplicitFlags;
// Used for identifying that any particular time, the caller chain has try/catch blocks belong to the user code.
// If all try/catch blocks in the current stack marked as non-user code then this member will remain false.
bool hasCatchHandlerToUserCode;
#ifdef ENABLE_GLOBALIZATION
Js::DelayLoadWinRtString delayLoadWinRtString;
#ifdef ENABLE_PROJECTION
Js::DelayLoadWinRtError delayLoadWinRtError;
Js::DelayLoadWinRtTypeResolution delayLoadWinRtTypeResolution;
Js::DelayLoadWinRtRoParameterizedIID delayLoadWinRtRoParameterizedIID;
#endif
#if defined(ENABLE_INTL_OBJECT) || defined(ENABLE_ES6_CHAR_CLASSIFIER)
Js::DelayLoadWindowsGlobalization delayLoadWindowsGlobalizationLibrary;
Js::WindowsGlobalizationAdapter windowsGlobalizationAdapter;
#endif
#ifdef ENABLE_FOUNDATION_OBJECT
Js::DelayLoadWinRtFoundation delayLoadWinRtFoundationLibrary;
Js::WindowsFoundationAdapter windowsFoundationAdapter;
#endif
#endif
// Number of script context attached with probe manager.
// This counter will be used as addref when the script context is created, this way we maintain the life of diagnostic object.
// Once no script context available , diagnostic will go away.
LONG crefSContextForDiag;
Entropy entropy;
JsUtil::Stack<HostScriptContext*>* hostScriptContextStack;
//
// Regex globals
//
UnifiedRegex::StandardChars<uint8>* standardUTF8Chars;
UnifiedRegex::StandardChars<char16>* standardUnicodeChars;
Js::ImplicitCallFlags implicitCallFlags;
THREAD_LOCAL static uint activeScriptSiteCount;
bool isScriptActive;
// When ETW rundown in background thread which needs to walk scriptContext/functionBody/entryPoint lists,
// or when JIT thread is getting auxPtrs from function body, we should not be modifying the list of
// functionBody/entrypoints, or expanding the auxPtrs
CriticalSection csFunctionBody;
#ifdef _M_X64
friend class Js::Amd64StackFrame;
Js::Amd64ContextsManager amd64ContextsManager;
Js::Amd64ContextsManager* GetAmd64ContextsManager() { return &amd64ContextsManager; }
#endif
typedef JsUtil::BaseDictionary<Js::DynamicType const *, void *, HeapAllocator, PowerOf2SizePolicy> DynamicObjectEnumeratorCacheMap;
DynamicObjectEnumeratorCacheMap dynamicObjectEnumeratorCacheMap;
#ifdef NTBUILD
ThreadContextWatsonTelemetryBlock localTelemetryBlock;
ThreadContextWatsonTelemetryBlock * telemetryBlock;
#endif
NativeLibraryEntryRecord nativeLibraryEntry;
// Indicates the current loop depth as observed by the interpreter. The interpreter causes this value to be updated upon
// entering and leaving a loop.
uint8 loopDepth;
const ThreadConfiguration configuration;
public:
static ThreadContext * globalListFirst;
static uint GetScriptSiteHolderCount() { return activeScriptSiteCount; }
static uint IncrementActiveScriptSiteCount() { return ++activeScriptSiteCount; }
static uint DecrementActiveScriptSiteCount() { return --activeScriptSiteCount; }
static ThreadContext * GetThreadContextList() { return globalListFirst; }
void ValidateThreadContext();
bool IsInScript() const { return callRootLevel != 0; }
uint GetCallRootLevel() const { return callRootLevel; }
PageAllocator * GetPageAllocator() { return &pageAllocator; }
AllocationPolicyManager * GetAllocationPolicyManager() { return allocationPolicyManager; }
#if ENABLE_NATIVE_CODEGEN
PreReservedVirtualAllocWrapper * GetPreReservedVirtualAllocator() { return &preReservedVirtualAllocator; }
#if DYNAMIC_INTERPRETER_THUNK || defined(ASMJS_PLAT)
CustomHeap::InProcCodePageAllocators * GetThunkPageAllocators() { return &thunkPageAllocators; }
#endif
CustomHeap::InProcCodePageAllocators * GetCodePageAllocators() { return &codePageAllocators; }
#if defined(_CONTROL_FLOW_GUARD) && (_M_IX86 || _M_X64)
InProcJITThunkEmitter * GetJITThunkEmitter() { return &jitThunkEmitter; }
#endif
#endif // ENABLE_NATIVE_CODEGEN
CriticalSection* GetFunctionBodyLock() { return &csFunctionBody; }
Js::IsConcatSpreadableCache* GetIsConcatSpreadableCache() { return &isConcatSpreadableCache; }
#ifdef ENABLE_GLOBALIZATION
Js::DelayLoadWinRtString *GetWinRTStringLibrary();
#ifdef ENABLE_PROJECTION
Js::DelayLoadWinRtError *GetWinRTErrorLibrary();
Js::DelayLoadWinRtTypeResolution* GetWinRTTypeResolutionLibrary();
Js::DelayLoadWinRtRoParameterizedIID* GetWinRTRoParameterizedIIDLibrary();
#endif
#if defined(ENABLE_INTL_OBJECT) || defined(ENABLE_ES6_CHAR_CLASSIFIER)
Js::DelayLoadWindowsGlobalization *GetWindowsGlobalizationLibrary();
Js::WindowsGlobalizationAdapter *GetWindowsGlobalizationAdapter();
#endif
#ifdef ENABLE_FOUNDATION_OBJECT
Js::DelayLoadWinRtFoundation *GetWinRtFoundationLibrary();
Js::WindowsFoundationAdapter *GetWindowsFoundationAdapter();
#endif
#endif
void SetAbnormalExceptionRecord(EXCEPTION_POINTERS *exceptionInfo) { this->exceptionInfo = *exceptionInfo; }
void SetAbnormalExceptionCode(uint32 exceptionInfo) { this->exceptionCode = exceptionInfo; }
uint32 GetAbnormalExceptionCode() const { return this->exceptionCode; }
#ifdef ENABLE_BASIC_TELEMETRY
GUID activityId;
#endif
void *tridentLoadAddress;
void* GetTridentLoadAddress() const { return tridentLoadAddress; }
void SetTridentLoadAddress(void *loadAddress) { tridentLoadAddress = loadAddress; }
#ifdef ENABLE_DIRECTCALL_TELEMETRY
DirectCallTelemetry directCallTelemetry;
#endif
BOOL HasPreviousHostScriptContext();
HostScriptContext* GetPreviousHostScriptContext() ;
void PushHostScriptContext(HostScriptContext* topProvider);
HostScriptContext* PopHostScriptContext();
void SetInterruptPoller(InterruptPoller *poller) { interruptPoller = poller; }
InterruptPoller *GetInterruptPoller() const { return interruptPoller; }
BOOL HasInterruptPoller() const { return interruptPoller != nullptr; }
void CheckScriptInterrupt();
void CheckInterruptPoll();
bool DoInterruptProbe(Js::FunctionBody *const func) const
{
return
(this->TestThreadContextFlag(ThreadContextFlagCanDisableExecution) &&
!PHASE_OFF(Js::InterruptProbePhase, func)) ||
PHASE_ON(Js::InterruptProbePhase, func);
}
bool DoInterruptProbe() const
{
return
(this->TestThreadContextFlag(ThreadContextFlagCanDisableExecution) &&
!PHASE_OFF1(Js::InterruptProbePhase)) ||
PHASE_ON1(Js::InterruptProbePhase);
}
bool EvalDisabled() const
{
return this->TestThreadContextFlag(ThreadContextFlagEvalDisabled);
}
bool NoJIT() const
{
return this->TestThreadContextFlag(ThreadContextFlagNoJIT);
}
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
Js::Var GetMemoryStat(Js::ScriptContext* scriptContext);
void SetAutoProxyName(LPCWSTR objectName);
LPCWSTR GetAutoProxyName() const { return recyclableData->autoProxyName; }
Js::PropertyId handlerPropertyId = Js::Constants::NoProperty;
#endif
void SetReturnedValueList(Js::ReturnedValueList *returnedValueList)
{
Assert(this->recyclableData != nullptr);
this->recyclableData->returnedValueList = returnedValueList;
}
#if DBG
void EnsureNoReturnedValueList()
{
Assert(this->recyclableData == nullptr || this->recyclableData->returnedValueList == nullptr);
}
#endif
#if DBG || defined(RUNTIME_DATA_COLLECTION)
uint GetScriptContextCount() const { return this->scriptContextCount; }
#endif
Js::ScriptContext* GetScriptContextList() const { return this->scriptContextList; }
bool WasAnyScriptContextEverRegistered() const { return this->scriptContextEverRegistered; }
#if DBG_DUMP || defined(PROFILE_EXEC)
void SetTopLevelScriptSite(ScriptSite* topScriptSite) { this->topLevelScriptSite = topScriptSite; }
ScriptSite* GetTopLevelScriptSite () { return this->topLevelScriptSite; }
#endif
#if DBG || defined(PROFILE_EXEC)
virtual bool AsyncHostOperationStart(void *) override;
virtual void AsyncHostOperationEnd(bool wasInAsync, void *) override;
#endif
#if DBG
bool IsInAsyncHostOperation() const;
#endif
#if ENABLE_TTD
//The class that holds info on the TTD state for the thread context
TTD::ThreadContextTTD* TTDContext;
//The class that holds information on TTD <-> debugger interaction state
TTD::ExecutionInfoManager* TTDExecutionInfo;
//The event log for time-travel (or null if TTD is not turned on)
TTD::EventLog* TTDLog;
//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;
bool IsRuntimeInTTDMode() const
{
return this->TTDLog != nullptr;
}
//Initialize the context for time-travel
void InitTimeTravel(ThreadContext* threadContext, void* runtimeHandle, uint32 snapInterval, uint32 snapHistoryLength);
void InitHostFunctionsAndTTData(bool record, bool replay, bool debug, size_t optTTUriLength, const char* optTTUri,
TTD::TTDOpenResourceStreamCallback openResourceStreamfp, TTD::TTDReadBytesFromStreamCallback readBytesFromStreamfp,
TTD::TTDWriteBytesToStreamCallback writeBytesToStreamfp, TTD::TTDFlushAndCloseStreamCallback flushAndCloseStreamfp,
TTD::TTDCreateExternalObjectCallback createExternalObjectfp,
TTD::TTDCreateJsRTContextCallback createJsRTContextCallbackfp, TTD::TTDReleaseJsRTContextCallback releaseJsRTContextCallbackfp, TTD::TTDSetActiveJsRTContext fpSetActiveJsRTContext);
#endif
BOOL ReserveStaticTypeIds(__in int first, __in int last);
Js::TypeId ReserveTypeIds(int count);
Js::TypeId CreateTypeId();
Js::TypeId GetNextTypeId() { return nextTypeId; }
// Lookup the well known type registered with a Js::TypeId.
// wellKnownType: The well known type which we should register
// typeId: The type id to match
// returns: true if the typeid is the wellKnownType
template<WellKnownHostType wellKnownType>
bool IsWellKnownHostType(Js::TypeId typeId)
{
CompileAssert(wellKnownType <= WellKnownHostType_Last);
return wellKnownHostTypeIds[wellKnownType] == typeId;
}
// Register a well known type to a Js::TypeId.
// wellKnownType: The well known type which we should register
// typeId: The type id which matches to the well known type
void SetWellKnownHostTypeId(WellKnownHostType wellKnownType, Js::TypeId typeId);
uint32 GetNextPolymorphicCacheState()
{
return ++polymorphicCacheState;
};
~ThreadContext();
void CloseForJSRT();
//Call back is called for one or more handles
//It does multiple callbacks (For example: separate call back for GC thread handle & JIT thread handles)
// template<class Fn>
//void ShutdownThreads(Fn callback);
void ShutdownThreads()
{
#if ENABLE_NATIVE_CODEGEN
if (jobProcessor)
{
jobProcessor->Close();
}
if (JITManager::GetJITManager()->IsOOPJITEnabled() && JITManager::GetJITManager()->IsConnected() && m_remoteThreadContextInfo)
{
if (JITManager::GetJITManager()->CleanupThreadContext(&m_remoteThreadContextInfo) == S_OK)
{
Assert(m_remoteThreadContextInfo == nullptr);
}
m_remoteThreadContextInfo = nullptr;
}
#endif
#if ENABLE_CONCURRENT_GC
if (this->recycler != nullptr)
{
this->recycler->ShutdownThread();
}
#endif
}
DateTime::HiResTimer * GetHiResTimer() { return &hTimer; }
ArenaAllocator* GetThreadAlloc() { return &threadAlloc; }
static CriticalSection * GetCriticalSection() { return &s_csThreadContext; }
ThreadContext(AllocationPolicyManager * allocationPolicyManager = nullptr, JsUtil::ThreadService::ThreadServiceCallback threadServiceCallback = nullptr, bool enableExperimentalFeatures = false);
static void Add(ThreadContext *threadContext);
ThreadConfiguration const * GetConfig() const { return &configuration; }
public:
#ifdef NTBUILD
void SetTelemetryBlock(ThreadContextWatsonTelemetryBlock * telemetryBlock) { this->telemetryBlock = telemetryBlock; }
#endif
static ThreadContext* GetContextForCurrentThread();
Recycler* GetRecycler() { return recycler; }
Recycler* EnsureRecycler();
ThreadContext::CollectCallBack * AddRecyclerCollectCallBack(RecyclerCollectCallBackFunction callback, void * context);
void RemoveRecyclerCollectCallBack(ThreadContext::CollectCallBack * collectCallBack);
void AddToPendingProjectionContextCloseList(IProjectionContext *projectionContext);
void RemoveFromPendingClose(IProjectionContext *projectionContext);
void ClosePendingProjectionContexts();
void AddToPendingScriptContextCloseList(Js::ScriptContext * scriptContext);
void RemoveFromPendingClose(Js::ScriptContext * scriptContext);
void ClosePendingScriptContexts();
Js::PropertyRecord const * GetPropertyName(Js::PropertyId propertyId);
Js::PropertyRecord const * GetPropertyNameLocked(Js::PropertyId propertyId);
private:
template <bool locked> Js::PropertyRecord const * GetPropertyNameImpl(Js::PropertyId propertyId);
public:
void FindPropertyRecord(Js::JavascriptString *pstName, Js::PropertyRecord const ** propertyRecord);
void FindPropertyRecord(__in LPCWSTR propertyName, __in int propertyNameLength, Js::PropertyRecord const ** propertyRecord);
const Js::PropertyRecord * FindPropertyRecord(const char16 * propertyName, int propertyNameLength);
JsUtil::List<const RecyclerWeakReference<Js::PropertyRecord const>*>* FindPropertyIdNoCase(Js::ScriptContext * scriptContext, LPCWSTR propertyName, int propertyNameLength);
JsUtil::List<const RecyclerWeakReference<Js::PropertyRecord const>*>* FindPropertyIdNoCase(Js::ScriptContext * scriptContext, JsUtil::CharacterBuffer<WCHAR> const& propertyName);
bool FindExistingPropertyRecord(_In_ JsUtil::CharacterBuffer<WCHAR> const& propertyName, Js::CaseInvariantPropertyListWithHashCode** propertyRecord);
void CleanNoCasePropertyMap();
void ForceCleanPropertyMap();
const Js::PropertyRecord * GetOrAddPropertyRecord(JsUtil::CharacterBuffer<char16> propertyName)
{
return GetOrAddPropertyRecordImpl(propertyName, false);
}
const Js::PropertyRecord * GetOrAddPropertyRecordBind(JsUtil::CharacterBuffer<char16> propertyName)
{
return GetOrAddPropertyRecordImpl(propertyName, true);
}
void AddBuiltInPropertyRecord(const Js::PropertyRecord *propertyRecord);
void GetOrAddPropertyId(__in LPCWSTR propertyName, __in int propertyNameLength, Js::PropertyRecord const** propertyRecord);
void GetOrAddPropertyId(JsUtil::CharacterBuffer<WCHAR> const& propertyName, Js::PropertyRecord const** propertyRecord);
Js::PropertyRecord const * UncheckedAddPropertyId(JsUtil::CharacterBuffer<WCHAR> const& propertyName, bool bind, bool isSymbol = false);
Js::PropertyRecord const * UncheckedAddPropertyId(__in LPCWSTR propertyName, __in int propertyNameLength, bool bind = false, bool isSymbol = false);
#ifdef ENABLE_JS_ETW
void EtwLogPropertyIdList();
#endif
private:
const Js::PropertyRecord * GetOrAddPropertyRecordImpl(JsUtil::CharacterBuffer<char16> propertyName, bool bind);
void AddPropertyRecordInternal(const Js::PropertyRecord * propertyRecord);
void BindPropertyRecord(const Js::PropertyRecord * propertyRecord);
bool IsDirectPropertyName(const char16 * propertyName, int propertyNameLength);
RecyclerWeakReference<const Js::PropertyRecord> * CreatePropertyRecordWeakRef(const Js::PropertyRecord * propertyRecord);
void AddCaseInvariantPropertyRecord(const Js::PropertyRecord * propertyRecord);
#if DBG || defined(RUNTIME_DATA_COLLECTION)
uint scriptContextCount;
#endif
public:
void UncheckedAddBuiltInPropertyId();
BOOL IsNumericPropertyId(Js::PropertyId propertyId, uint32* value);
bool IsActivePropertyId(Js::PropertyId pid);
void InvalidatePropertyRecord(const Js::PropertyRecord * propertyRecord);
Js::PropertyId GetNextPropertyId();
Js::PropertyId GetMaxPropertyId();
uint GetHighestPropertyNameIndex() const;
void SetThreadServiceWrapper(ThreadServiceWrapper*);
ThreadServiceWrapper* GetThreadServiceWrapper();
#ifdef ENABLE_PROJECTION
void AddExternalWeakReferenceCache(ExternalWeakReferenceCache *externalWeakReferenceCache);
void RemoveExternalWeakReferenceCache(ExternalWeakReferenceCache *externalWeakReferenceCache);
virtual void MarkExternalWeakReferencedObjects(bool inPartialCollect) override;
virtual void ResolveExternalWeakReferencedObjects() override;
#if DBG_DUMP
void RegisterProjectionMemoryInformation(IProjectionContextMemoryInfo* projectionContextMemoryInfo);
void DumpProjectionContextMemoryStats(LPCWSTR headerMsg, bool forceDetailed = false);
IProjectionContextMemoryInfo* GetProjectionContextMemoryInformation();
#endif
#endif
uint NewFunctionNumber() { return ++functionCount; }
uint PeekNewFunctionNumber() { return functionCount + 1; }
uint NewSourceInfoNumber() { return ++sourceInfoCount; }
void AddCodeSize(size_t newCode)
{
::InterlockedExchangeAdd(&nativeCodeSize, newCode);
::InterlockedExchangeAdd(&processNativeCodeSize, newCode);
}
void AddSourceSize(size_t newCode) { sourceCodeSize += newCode; }
void SubCodeSize(size_t deadCode)
{
Assert(nativeCodeSize >= deadCode);
Assert(processNativeCodeSize >= deadCode);
::InterlockedExchangeSubtract(&nativeCodeSize, deadCode);
::InterlockedExchangeSubtract(&processNativeCodeSize, deadCode);
}
void SubSourceSize(size_t deadCode) { Assert(sourceCodeSize >= deadCode); sourceCodeSize -= deadCode; }
size_t GetCodeSize() { return nativeCodeSize; }
static size_t GetProcessCodeSize() { return processNativeCodeSize; }
size_t GetSourceSize() { return sourceCodeSize; }
bool DoTryRedeferral() const;
void TryRedeferral();
bool DoRedeferFunctionBodies() const;
void UpdateRedeferralState();
uint GetRedeferralCollectionInterval() const;
uint GetRedeferralInactiveThreshold() const;
void GetActiveFunctions(ActiveFunctionSet * pActive);
#if DBG
uint redeferredFunctions;
uint recoveredBytes;
#endif
Js::ScriptEntryExitRecord * GetScriptEntryExit() const { return entryExitRecord; }
void RegisterCodeGenRecyclableData(Js::CodeGenRecyclableData *const codeGenRecyclableData);
void UnregisterCodeGenRecyclableData(Js::CodeGenRecyclableData *const codeGenRecyclableData);
#if ENABLE_NATIVE_CODEGEN
bool IsNativeAddressHelper(void * pCodeAddr, Js::ScriptContext* currentScriptContext);
BOOL IsNativeAddress(void * pCodeAddr, Js::ScriptContext* currentScriptContext = nullptr);
JsUtil::JobProcessor *GetJobProcessor();
Js::Var * GetBailOutRegisterSaveSpace() const { return bailOutRegisterSaveSpace; }
virtual intptr_t GetBailOutRegisterSaveSpaceAddr() const override { return (intptr_t)bailOutRegisterSaveSpace; }
#if !FLOATVAR
CodeGenNumberThreadAllocator * GetCodeGenNumberThreadAllocator() const
{
return codeGenNumberThreadAllocator;
}
XProcNumberPageSegmentManager * GetXProcNumberPageSegmentManager() const
{
return this->xProcNumberPageSegmentManager;
}
#endif
#endif
void ResetFunctionCount() { Assert(this->GetScriptSiteHolderCount() == 0); this->functionCount = 0; }
void PushEntryExitRecord(Js::ScriptEntryExitRecord *);
void PopEntryExitRecord(Js::ScriptEntryExitRecord *);
uint EnterScriptStart(Js::ScriptEntryExitRecord *, bool doCleanup);
void EnterScriptEnd(Js::ScriptEntryExitRecord *, bool doCleanup);
template <bool leaveForHost>
void LeaveScriptStart(void *);
template <bool leaveForHost>
void LeaveScriptEnd(void *);
void DisposeOnLeaveScript();
void PushInterpreterFrame(Js::InterpreterStackFrame *interpreterFrame);
Js::InterpreterStackFrame *PopInterpreterFrame();
Js::InterpreterStackFrame *GetLeafInterpreterFrame() const { return leafInterpreterFrame; }
Js::TempArenaAllocatorObject * GetTemporaryAllocator(LPCWSTR name);
void ReleaseTemporaryAllocator(Js::TempArenaAllocatorObject * tempAllocator);
Js::TempGuestArenaAllocatorObject * GetTemporaryGuestAllocator(LPCWSTR name);
void ReleaseTemporaryGuestAllocator(Js::TempGuestArenaAllocatorObject * tempAllocator);
// Should be called from script context, at the time when construction for scriptcontext is just done.
void EnsureDebugManager();
// Should be called from script context 's destructor,
void ReleaseDebugManager();
void RegisterScriptContext(Js::ScriptContext *scriptContext);
void UnregisterScriptContext(Js::ScriptContext *scriptContext);
// NoScriptScope
void SetNoScriptScope(bool noScriptScope) { this->noScriptScope = noScriptScope; }
bool IsNoScriptScope() { return this->noScriptScope; }
void SetPendingFinallyException(Js::JavascriptExceptionObject * exceptionObj)
{
pendingFinallyException = exceptionObj;
}
Js::JavascriptExceptionObject * GetPendingFinallyException()
{
return pendingFinallyException;
}
Js::EntryPointInfo ** RegisterEquivalentTypeCacheEntryPoint(Js::EntryPointInfo * entryPoint);
void UnregisterEquivalentTypeCacheEntryPoint(Js::EntryPointInfo ** entryPoint);
void RegisterProtoInlineCache(Js::InlineCache * inlineCache, Js::PropertyId propertyId);
void RegisterStoreFieldInlineCache(Js::InlineCache * inlineCache, Js::PropertyId propertyId);
void NotifyInlineCacheBatchUnregistered(uint count);
#if DBG
bool IsProtoInlineCacheRegistered(const Js::InlineCache * inlineCache, Js::PropertyId propertyId);
bool IsStoreFieldInlineCacheRegistered(const Js::InlineCache * inlineCache, Js::PropertyId propertyId);
#endif
#if ENABLE_NATIVE_CODEGEN
Js::PropertyGuard* RegisterSharedPropertyGuard(Js::PropertyId propertyId);
void RegisterLazyBailout(Js::PropertyId propertyId, Js::EntryPointInfo* entryPoint);
void RegisterUniquePropertyGuard(Js::PropertyId propertyId, Js::PropertyGuard* guard);
void RegisterUniquePropertyGuard(Js::PropertyId propertyId, RecyclerWeakReference<Js::PropertyGuard>* guardWeakRef);
void RegisterConstructorCache(Js::PropertyId propertyId, Js::ConstructorCache* cache);
#endif
virtual size_t GetScriptStackLimit() const override;
virtual HANDLE GetProcessHandle() const override;
virtual intptr_t GetThreadStackLimitAddr() const override;
#if ENABLE_NATIVE_CODEGEN && defined(ENABLE_SIMDJS) && (defined(_M_IX86) || defined(_M_X64))
virtual intptr_t GetSimdTempAreaAddr(uint8 tempIndex) const override;
#endif
virtual intptr_t GetDisableImplicitFlagsAddr() const override;
virtual intptr_t GetImplicitCallFlagsAddr() const override;
ptrdiff_t GetChakraBaseAddressDifference() const;
ptrdiff_t GetCRTBaseAddressDifference() const;
private:
void RegisterInlineCache(InlineCacheListMapByPropertyId& inlineCacheMap, Js::InlineCache* inlineCache, Js::PropertyId propertyId);
static bool IsInlineCacheRegistered(InlineCacheListMapByPropertyId& inlineCacheMap, const Js::InlineCache* inlineCache, Js::PropertyId propertyId);
void InvalidateAndDeleteInlineCacheList(InlineCacheList *inlineCacheList);
void CompactInlineCacheList(InlineCacheList *inlineCacheList);
void CompactInlineCacheInvalidationLists();
void CompactProtoInlineCaches();
void CompactStoreFieldInlineCaches();
#if DBG
static bool IsInlineCacheInList(const Js::InlineCache* inlineCache, const InlineCacheList* inlineCacheChain);
#endif
#if ENABLE_NATIVE_CODEGEN
void InvalidateFixedFieldGuard(Js::PropertyGuard* guard);
PropertyGuardEntry* EnsurePropertyGuardEntry(const Js::PropertyRecord* propertyRecord, bool& foundExistingEntry);
void InvalidatePropertyGuardEntry(const Js::PropertyRecord* propertyRecord, PropertyGuardEntry* entry, bool isAllPropertyGuardsInvalidation);
#endif
public:
class AutoDisableExpiration
{
public:
AutoDisableExpiration(ThreadContext* threadContext):
_threadContext(threadContext),
_oldExpirationDisabled(threadContext->disableExpiration)
{
_threadContext->disableExpiration = true;
}
~AutoDisableExpiration()
{
_threadContext->disableExpiration = _oldExpirationDisabled;
}
private:
ThreadContext* _threadContext;
bool _oldExpirationDisabled;
};
void InvalidateProtoInlineCaches(Js::PropertyId propertyId);
void InvalidateStoreFieldInlineCaches(Js::PropertyId propertyId);
void InvalidateAllProtoInlineCaches();
#if DBG
bool IsObjectRegisteredInProtoInlineCaches(Js::DynamicObject * object);
bool IsObjectRegisteredInStoreFieldInlineCaches(Js::DynamicObject * object);
#endif
bool AreAllProtoInlineCachesInvalidated();
void InvalidateAllStoreFieldInlineCaches();
bool AreAllStoreFieldInlineCachesInvalidated();
void InvalidatePropertyGuards(Js::PropertyId propertyId);
void InvalidateAllPropertyGuards();
void RegisterIsInstInlineCache(Js::IsInstInlineCache * inlineCache, Js::Var function);
void UnregisterIsInstInlineCache(Js::IsInstInlineCache * inlineCache, Js::Var function);
#if DBG
bool IsIsInstInlineCacheRegistered(Js::IsInstInlineCache * inlineCache, Js::Var function);
#endif
private:
void InvalidateIsInstInlineCacheList(Js::IsInstInlineCache* inlineCacheList);
#if DBG
bool IsIsInstInlineCacheInList(const Js::IsInstInlineCache* inlineCache, const Js::IsInstInlineCache* inlineCacheList);
#endif
public:
void InvalidateIsInstInlineCachesForFunction(Js::Var function);
void InvalidateAllIsInstInlineCaches();
bool AreAllIsInstInlineCachesInvalidated() const;
#ifdef PERSISTENT_INLINE_CACHES
void ClearInlineCachesWithDeadWeakRefs();
#endif
void ClearInvalidatedUniqueGuards();
void ClearInlineCaches();
void ClearIsInstInlineCaches();
void ClearForInCaches();
void ClearEquivalentTypeCaches();
void ClearScriptContextCaches();
void RegisterTypeWithProtoPropertyCache(const Js::PropertyId propertyId, Js::Type *const type);
void InvalidateProtoTypePropertyCaches(const Js::PropertyId propertyId);
void InternalInvalidateProtoTypePropertyCaches(const Js::PropertyId propertyId);
void InvalidateAllProtoTypePropertyCaches();
Js::ScriptContext ** RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext(Js::ScriptContext * scriptContext);
void UnregisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext(Js::ScriptContext ** scriptContext);
void ClearPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesCaches();
BOOL HasUnhandledException() const { return hasUnhandledException; }
void SetHasUnhandledException() {hasUnhandledException = TRUE; }
void ResetHasUnhandledException() {hasUnhandledException = FALSE; }
void SetUnhandledExceptionObject(Js::JavascriptExceptionObject* exceptionObject) {recyclableData->unhandledExceptionObject = exceptionObject; }
Js::JavascriptExceptionObject* GetUnhandledExceptionObject() const { return recyclableData->unhandledExceptionObject; };
// To temporarily keep throwing exception object alive (thrown but not yet caught)
void SaveTempUncaughtException(Js::JavascriptExceptionObject* exceptionObject)
{
Js::JavascriptExceptionObject::Insert(&recyclableData->tempUncaughtException, exceptionObject);
}
void ClearTempUncaughtException(Js::JavascriptExceptionObject* exceptionObject)
{
Js::JavascriptExceptionObject::Remove(&recyclableData->tempUncaughtException, exceptionObject);
}
public:
bool HasCatchHandler() const { return hasCatchHandler; }
void SetHasCatchHandler(bool hasCatchHandler) { this->hasCatchHandler = hasCatchHandler; }
bool IsUserCode() const { return this->hasCatchHandlerToUserCode; }
void SetIsUserCode(bool set) { this->hasCatchHandlerToUserCode = set; }
void QueueFreeOldEntryPointInfoIfInScript(Js::FunctionEntryPointInfo* oldEntryPointInfo)
{
if (this->IsInScript())
{
// Add it to the list only if it's not already in it
if (oldEntryPointInfo->nextEntryPoint == nullptr && !oldEntryPointInfo->IsCleanedUp())
{
oldEntryPointInfo->nextEntryPoint = recyclableData->oldEntryPointInfo;
recyclableData->oldEntryPointInfo = oldEntryPointInfo;
}
}
}
static BOOLEAN IsOnStack(void const *ptr);
_NOINLINE bool IsStackAvailable(size_t size);
_NOINLINE bool IsStackAvailableNoThrow(size_t size = Js::Constants::MinStackDefault);
static bool IsCurrentStackAvailable(size_t size);
void ProbeStackNoDispose(size_t size, Js::ScriptContext *scriptContext, PVOID returnAddress = nullptr);
void ProbeStack(size_t size, Js::ScriptContext *scriptContext, PVOID returnAddress = nullptr);
void ProbeStack(size_t size, Js::RecyclableObject * obj, Js::ScriptContext *scriptContext);
void ProbeStack(size_t size);
static void __stdcall ProbeCurrentStackNoDispose(size_t size, Js::ScriptContext *scriptContext);
static void __stdcall ProbeCurrentStack(size_t size, Js::ScriptContext *scriptContext);
static void __stdcall ProbeCurrentStack2(size_t size, Js::ScriptContext *scriptContext, uint32 u1, uint32 u2)
{
ProbeCurrentStack(size, scriptContext);
}
#if ENABLE_PROFILE_INFO
void EnsureSourceProfileManagersByUrlMap();
Js::SourceDynamicProfileManager* GetSourceDynamicProfileManager(_In_z_ const WCHAR* url, _In_ uint hash, _Inout_ bool* addref);
uint ReleaseSourceDynamicProfileManagers(const WCHAR* url);
#endif
void EnsureSymbolRegistrationMap();
const Js::PropertyRecord* GetSymbolFromRegistrationMap(const char16* stringKey);
const Js::PropertyRecord* AddSymbolToRegistrationMap(const char16* stringKey, charcount_t stringLength);
#if ENABLE_TTD
JsUtil::BaseDictionary<const char16*, const Js::PropertyRecord*, Recycler, PowerOf2SizePolicy>* GetSymbolRegistrationMap_TTD();
#endif
inline void ClearPendingSOError()
{
this->GetPendingSOErrorObject()->ClearError();
}
inline void ClearPendingOOMError()
{
this->GetPendingOOMErrorObject()->ClearError();
}
Js::JavascriptExceptionObject *GetPendingSOErrorObject()
{
Assert(recyclableData->soErrorObject.IsPendingExceptionObject());
return &recyclableData->soErrorObject;
}
Js::JavascriptExceptionObject *GetPendingOOMErrorObject()
{
Assert(recyclableData->oomErrorObject.IsPendingExceptionObject());
return &recyclableData->oomErrorObject;
}
Js::JavascriptExceptionObject *GetPendingTerminatedErrorObject()
{
return &recyclableData->terminatedErrorObject;
}
Js::JavascriptExceptionObject* GetRecordedException()
{
return recyclableData->exceptionObject;
}
bool GetPropagateException()
{
return recyclableData->propagateException;
}
void SetHasThrownPendingException()
{
Assert(this->IsInScript());
this->hasThrownPendingException = true;
}
void SetRecordedException(Js::JavascriptExceptionObject* exceptionObject, bool propagateToDebugger = false)
{
this->recyclableData->exceptionObject = exceptionObject;
this->recyclableData->propagateException = propagateToDebugger;
}
#ifdef ENABLE_CUSTOM_ENTROPY
Entropy& GetEntropy()
{
return entropy;
}
#endif
Js::ImplicitCallFlags * GetAddressOfImplicitCallFlags()
{
return &implicitCallFlags;
}
DisableImplicitFlags * GetAddressOfDisableImplicitFlags()
{
return &disableImplicitFlags;
}
Js::ImplicitCallFlags GetImplicitCallFlags()
{
return implicitCallFlags;
}
void SetImplicitCallFlags(Js::ImplicitCallFlags flags)
{
//Note: this action is inlined into JITed code in Lowerer::GenerateCallProfiling.
// if you change this, you might want to add it there too.
implicitCallFlags = flags;
}
void ClearImplicitCallFlags();
void ClearImplicitCallFlags(Js::ImplicitCallFlags flags);
void AddImplicitCallFlags(Js::ImplicitCallFlags flags)
{
SetImplicitCallFlags((Js::ImplicitCallFlags)(implicitCallFlags | flags));
}
void CheckAndResetImplicitCallAccessorFlag();
template <class Fn>
inline Js::Var ExecuteImplicitCall(Js::RecyclableObject * function, Js::ImplicitCallFlags flags, Fn implicitCall)
{
// For now, we will not allow Function that is marked as HasNoSideEffect to be called, and we will just bailout.
// These function may still throw exceptions, so we will need to add checks with RecordImplicitException
// so that we don't throw exception when disableImplicitCall is set before we allow these function to be called
// as an optimization. (These functions are valueOf and toString calls for built-in non primitive types)
Js::FunctionInfo::Attributes attributes = Js::FunctionInfo::GetAttributes(function);
// we can hoist out const method if we know the function doesn't have side effect,
// and the value can be hoisted.
if (this->HasNoSideEffect(function, attributes))
{
// Has no side effect means the function does not change global value or
// will check for implicit call flags
return implicitCall();
}
// Don't call the implicit call if disable implicit call
if (IsDisableImplicitCall())
{
AddImplicitCallFlags(flags);
// Return "undefined" just so we have a valid var, in case subsequent instructions are executed
// before we bail out.
return function->GetScriptContext()->GetLibrary()->GetUndefined();
}
if ((attributes & Js::FunctionInfo::HasNoSideEffect) != 0)
{
// Has no side effect means the function does not change global value or
// will check for implicit call flags
return implicitCall();
}
// Save and restore implicit flags around the implicit call
Js::ImplicitCallFlags saveImplicitCallFlags = this->GetImplicitCallFlags();
Js::Var result = implicitCall();
this->SetImplicitCallFlags((Js::ImplicitCallFlags)(saveImplicitCallFlags | flags));
return result;
}
bool HasNoSideEffect(Js::RecyclableObject * function) const;
bool HasNoSideEffect(Js::RecyclableObject * function, Js::FunctionInfo::Attributes attr) const;
bool RecordImplicitException();
DisableImplicitFlags GetDisableImplicitFlags() const { return disableImplicitFlags; }
void SetDisableImplicitFlags(DisableImplicitFlags flags) { disableImplicitFlags = flags; }
bool IsDisableImplicitCall() const { return (disableImplicitFlags & DisableImplicitCallFlag) != 0; }
bool IsDisableImplicitException() const { return (disableImplicitFlags & DisableImplicitExceptionFlag) != 0; }
void DisableImplicitCall() { disableImplicitFlags = (DisableImplicitFlags)(disableImplicitFlags | DisableImplicitCallFlag); }
void ClearDisableImplicitFlags() { disableImplicitFlags = DisableImplicitNoFlag; }
virtual uint GetRandomNumber() override;
virtual bool DoSpecialMarkOnScanStack() override { return this->DoRedeferFunctionBodies(); }
virtual void PostSweepRedeferralCallBack() override;
// DefaultCollectWrapper
virtual void PreCollectionCallBack(CollectionFlags flags) override;
virtual void PreSweepCallback() override;
virtual void WaitCollectionCallBack() override;
virtual void PostCollectionCallBack() override;
virtual BOOL ExecuteRecyclerCollectionFunction(Recycler * recycler, CollectionFunction function, CollectionFlags flags) override;
#ifdef FAULT_INJECTION
virtual void DisposeScriptContextByFaultInjectionCallBack() override;
#endif
virtual void DisposeObjects(Recycler * recycler) override;
virtual void PreDisposeObjectsCallBack() override;
typedef DList<ExpirableObject*, ArenaAllocator> ExpirableObjectList;
ExpirableObjectList* expirableObjectList;
ExpirableObjectList* expirableObjectDisposeList;
int numExpirableObjects;
int expirableCollectModeGcCount;
bool disableExpiration;
bool InExpirableCollectMode();
void TryEnterExpirableCollectMode();
void TryExitExpirableCollectMode();
void RegisterExpirableObject(ExpirableObject* object);
void UnregisterExpirableObject(ExpirableObject* object);
void * GetDynamicObjectEnumeratorCache(Js::DynamicType const * dynamicType);
void AddDynamicObjectEnumeratorCache(Js::DynamicType const * dynamicType, void * cache);
public:
bool IsScriptActive() const { return isScriptActive; }
void SetIsScriptActive(bool isActive) { isScriptActive = isActive; }
bool IsExecutionDisabled() const
{
return this->GetStackLimitForCurrentThread() == Js::Constants::StackLimitForScriptInterrupt;
}
void DisableExecution();
void EnableExecution();
bool TestThreadContextFlag(ThreadContextFlags threadContextFlag) const;
void SetThreadContextFlag(ThreadContextFlags threadContextFlag);
void ClearThreadContextFlag(ThreadContextFlags threadContextFlag);
void SetForceOneIdleCollection();
bool IsInThreadServiceCallback() const { return threadService.IsInCallback(); }
Js::DebugManager * GetDebugManager() const { return this->debugManager; }
const NativeLibraryEntryRecord::Entry* PeekNativeLibraryEntry() const { return this->nativeLibraryEntry.Peek(); }
void PushNativeLibraryEntry(_In_ NativeLibraryEntryRecord::Entry* entry) { this->nativeLibraryEntry.Push(entry); }
void PopNativeLibraryEntry() { this->nativeLibraryEntry.Pop(); }
bool IsProfilingUserCode() const { return isProfilingUserCode; }
void SetIsProfilingUserCode(bool value) { isProfilingUserCode = value; }
#if DBG_DUMP
uint scriptSiteCount;
#endif
#ifdef BAILOUT_INJECTION
uint bailOutByteCodeLocationCount;
#endif
#ifdef DYNAMIC_PROFILE_MUTATOR
DynamicProfileMutator * dynamicProfileMutator;
#endif
//
// Regex helpers
//
UnifiedRegex::StandardChars<uint8>* GetStandardChars(__inout_opt uint8* dummy);
UnifiedRegex::StandardChars<char16>* GetStandardChars(__inout_opt char16* dummy);
bool IsOptimizedForManyInstances() const { return isOptimizedForManyInstances; }
void OptimizeForManyInstances(const bool optimizeForManyInstances)
{
Assert(!recycler || optimizeForManyInstances == isOptimizedForManyInstances); // mode cannot be changed after recycler is created
isOptimizedForManyInstances = optimizeForManyInstances;
}
#if ENABLE_NATIVE_CODEGEN
bool IsBgJitEnabled() const { return bgJit; }
void EnableBgJit(const bool enableBgJit)
{
Assert(!jobProcessor || enableBgJit == bgJit);
bgJit = enableBgJit;
}
#endif
void* GetJSRTRuntime() const { return jsrtRuntime; }
void SetJSRTRuntime(void* runtime);
private:
BOOL ExecuteRecyclerCollectionFunctionCommon(Recycler * recycler, CollectionFunction function, CollectionFlags flags);
void DoInvalidateProtoTypePropertyCaches(const Js::PropertyId propertyId, TypeHashSet *const typeHashSet);
void InitializePropertyMaps();
void CreateNoCasePropertyMap();
InterruptPoller *interruptPoller;
void CollectionCallBack(RecyclerCollectCallBackFlags flags);
// Cache used by HostDispatch::GetBuiltInOperationFromEntryPoint
private:
JsUtil::BaseDictionary<Js::JavascriptMethod, uint, ArenaAllocator, PowerOf2SizePolicy> entryPointToBuiltInOperationIdCache;
#if ENABLE_JS_REENTRANCY_CHECK
public:
void SetNoJsReentrancy(bool val) { noJsReentrancy = val; }
bool GetNoJsReentrancy() { return noJsReentrancy; }
void AssertJsReentrancy()
{
if (GetNoJsReentrancy())
{
Js::Throw::FatalJsReentrancyError();
}
}
#endif
public:
bool IsEntryPointToBuiltInOperationIdCacheInitialized()
{
return entryPointToBuiltInOperationIdCache.Count() != 0;
}
bool GetBuiltInOperationIdFromEntryPoint(Js::JavascriptMethod entryPoint, uint * id)
{
return entryPointToBuiltInOperationIdCache.TryGetValue(entryPoint, id);
}
void SetBuiltInOperationIdForEntryPoint(Js::JavascriptMethod entryPoint, uint id)
{
entryPointToBuiltInOperationIdCache.Add(entryPoint, id);
}
void ResetEntryPointToBuiltInOperationIdCache()
{
entryPointToBuiltInOperationIdCache.ResetNoDelete();
}
uint8 LoopDepth() const
{
return loopDepth;
}
void SetLoopDepth(const uint8 loopDepth)
{
this->loopDepth = loopDepth;
}
void IncrementLoopDepth()
{
if(loopDepth != UCHAR_MAX)
{
++loopDepth;
}
}
void DecrementLoopDepth()
{
if(loopDepth != 0)
{
--loopDepth;
}
}
#if defined(CHECK_MEMORY_LEAK) || defined(LEAK_REPORT)
static void ReportAndCheckLeaksOnProcessDetach();
#endif
#ifdef LEAK_REPORT
void SetRootTrackerScriptContext(Js::ScriptContext * scriptContext);
void ClearRootTrackerScriptContext(Js::ScriptContext * scriptContext);
private:
Js::ScriptContext * rootTrackerScriptContext;
DWORD threadId;
#endif
};
extern void(*InitializeAdditionalProperties)(ThreadContext *threadContext);
// Temporarily set script profiler isProfilingUserCode state, restore at destructor
class AutoProfilingUserCode
{
private:
ThreadContext* threadContext;
const bool oldIsProfilingUserCode;
public:
AutoProfilingUserCode(ThreadContext* threadContext, bool isProfilingUserCode) :
threadContext(threadContext),
oldIsProfilingUserCode(threadContext->IsProfilingUserCode())
{
threadContext->SetIsProfilingUserCode(isProfilingUserCode);
}
~AutoProfilingUserCode()
{
threadContext->SetIsProfilingUserCode(oldIsProfilingUserCode);
}
};
#if ENABLE_JS_REENTRANCY_CHECK
class JsReentLock
{
ThreadContext *m_threadContext;
bool m_savedNoJsReentrancy;
public:
JsReentLock(ThreadContext *threadContext)
{
m_savedNoJsReentrancy = threadContext->GetNoJsReentrancy();
threadContext->SetNoJsReentrancy(true);
m_threadContext = threadContext;
}
void unlock() { m_threadContext->SetNoJsReentrancy(m_savedNoJsReentrancy); }
void relock() { m_threadContext->SetNoJsReentrancy(true); }
~JsReentLock()
{
m_threadContext->SetNoJsReentrancy(m_savedNoJsReentrancy);
}
};
#endif