//-------------------------------------------------------------------------------------------------------
// 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

#if defined(_WIN32) && defined(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;
class BGParseManager;
struct IActiveScriptDirect;

namespace Js
{
#ifdef ENABLE_BASIC_TELEMETRY
    class ScriptContextTelemetry;
#endif

    class ScriptContext;
    class ScriptEditQuery;
    class MutationBreakpoint;
    class StringProfiler;
    class DebugContext;
    struct HaltCallback;
    struct DebuggerOptionsCallback;
    class ModuleRecordBase;
    class SimpleDataCacheWrapper;
}

// 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:
    Field(SourceContextInfo *) sourceContextInfo;
    Field(ULONG) dlnHost;             // Line number passed to ParseScriptText
    Field(ULONG) ulColumnHost;        // Column number on the line where the parse script text started
    Field(ULONG) lnMinHost;           // Line offset of first host-supplied line
    Field(ULONG) ichMinHost;          // Range of host supplied characters
    Field(ULONG) ichLimHost;
    Field(ULONG) ulCharOffset;        // Char offset of the source text relative to the document. (Populated using IActiveScriptContext)
    Field(Js::ModuleID) moduleID;
    Field(ULONG) grfsi;

    static SRCINFO* Copy(Recycler* recycler, const SRCINFO* srcInfo)
    {
        SRCINFO* copySrcInfo = RecyclerNew(recycler, SRCINFO, *srcInfo);
        return copySrcInfo;
    }

    SRCINFO()
    {
    }
    SRCINFO(const SRCINFO& other)
        :sourceContextInfo(other.sourceContextInfo),
        dlnHost(other.dlnHost),
        ulColumnHost(other.ulColumnHost),
        lnMinHost(other.lnMinHost),
        ichMinHost(other.ichMinHost),
        ichLimHost(other.ichLimHost),
        ulCharOffset(other.ulCharOffset),
        moduleID(other.moduleID),
        grfsi(other.grfsi)
    {
    }
    SRCINFO(
        SourceContextInfo*sourceContextInfo,
        ULONG dlnHost,
        ULONG ulColumnHost,
        ULONG lnMinHost,
        ULONG ichMinHost,
        ULONG ichLimHost,
        ULONG ulCharOffset,
        Js::ModuleID moduleID,
        ULONG grfsi
    ):sourceContextInfo(sourceContextInfo),
        dlnHost(dlnHost),
        ulColumnHost(ulColumnHost),
        lnMinHost(lnMinHost),
        ichMinHost(ichMinHost),
        ichLimHost(ichLimHost),
        ulCharOffset(ulCharOffset),
        moduleID(moduleID),
        grfsi(grfsi)
    {
    }
};

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'
    LoadScriptFlag_ExternalArrayBuffer = 0x100,         // for ExternalArrayBuffer
    LoadScriptFlag_CreateParserState = 0x200,           // create the parser state cache while parsing.
    LoadScriptFlag_StrictMode = 0x400                   // parse using strict mode semantics
};

enum class ScriptContextPrivilegeLevel
{
    Low,
    Medium,
    High
};

#ifdef INLINE_CACHE_STATS
// Used to store inline cache stats

struct InlineCacheData
{
    uint hits;
    uint misses;
    uint collisions;
    bool isGetCache;
    Js::PropertyId propertyId;

    InlineCacheData() : hits(0), misses(0), collisions(0), isGetCache(false), propertyId(Js::PropertyIds::_none) { }
};

#endif

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 EnqueuePromiseTask(Js::Var varTask) = 0;

    virtual HRESULT FetchImportedModule(Js::ModuleRecordBase* referencingModule, LPCOLESTR specifier, Js::ModuleRecordBase** dependentModuleRecord) = 0;
    virtual HRESULT FetchImportedModuleFromScript(DWORD_PTR dwReferencingSourceContext, LPCOLESTR specifier, Js::ModuleRecordBase** dependentModuleRecord) = 0;
    virtual HRESULT NotifyHostAboutModuleReady(Js::ModuleRecordBase* referencingModule, Js::Var exceptionVar) = 0;
    virtual HRESULT InitializeImportMeta(Js::ModuleRecordBase* referencingModule, Js::Var importMetaObject) = 0;
    virtual bool ReportModuleCompletion(Js::ModuleRecordBase* module, Js::Var exception) = 0;

    virtual HRESULT ThrowIfFailed(HRESULT hr) = 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;
};

class HostStream
{
public:
    virtual byte * ExtendBuffer(byte *oldBuffer, size_t newSize, size_t *allocatedSize) = 0;
    virtual bool WriteHostObject(void* data) = 0;
};

class HostReadStream
{
public:
    virtual Js::Var ReadHostObject() = 0;
};

#if ENABLE_TTD
typedef void (CALLBACK *JsTTDOnScriptLoadCallback)(FinalizableObject* hostContext, Js::FunctionBody* body, Js::Utf8SourceInfo* utf8SourceInfo, CompileScriptException* compileException, bool notify);
typedef uint32 (CALLBACK *JsTTDOnBPRegisterCallback)(void* hostRuntime, int64 bpID, Js::ScriptContext* scriptContext, Js::Utf8SourceInfo* utf8SourceInfo, uint32 line, uint32 column, BOOL* isNewBP);
typedef void (CALLBACK *JsTTDOnBPDeleteCallback)(void* hostRuntime, uint32 bpID);
typedef void (CALLBACK *JsTTDOnBPClearDocumentCallback)(void* hostRuntime);

//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:
    FinalizableObject* HostContext;
    void* HostRuntime;

    JsTTDOnScriptLoadCallback pfOnScriptLoadCallback;

    JsTTDOnBPRegisterCallback pfOnBPRegisterCallback;
    JsTTDOnBPDeleteCallback pfOnBPDeleteCallback;
    JsTTDOnBPClearDocumentCallback pfOnBPClearDocumentCallback;

    HostScriptContextCallbackFunctor()
        : HostContext(nullptr), HostRuntime(nullptr), pfOnScriptLoadCallback(nullptr), pfOnBPRegisterCallback(nullptr), pfOnBPDeleteCallback(nullptr), pfOnBPClearDocumentCallback(nullptr)
    {
        ;
    }

    HostScriptContextCallbackFunctor(FinalizableObject* hostContext, void* hostRuntime, JsTTDOnScriptLoadCallback callbackOnScriptLoad, JsTTDOnBPRegisterCallback callbackOnBPRegister, JsTTDOnBPDeleteCallback callbackOnBPDelete, JsTTDOnBPClearDocumentCallback callbackOnBPClearDocument)
        : HostContext(hostContext), HostRuntime(hostRuntime), pfOnScriptLoadCallback(callbackOnScriptLoad), pfOnBPRegisterCallback(callbackOnBPRegister), pfOnBPDeleteCallback(callbackOnBPDelete), pfOnBPClearDocumentCallback(callbackOnBPClearDocument)
    {
        ;
    }
};
#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)

    class ScriptConfiguration
    {
    public:
        ScriptConfiguration(const ThreadConfiguration * const threadConfig, const bool isOptimizedForManyInstances) :
            NoNative(Configuration::Global.flags.NoNative),
            NoDynamicThunks(false),
            isOptimizedForManyInstances(isOptimizedForManyInstances),
            threadConfig(threadConfig)
        {
        }

#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

        void ForceNoDynamicThunks() { this->NoDynamicThunks = true; }
        bool IsNoDynamicThunks() const { return this->NoDynamicThunks; }

        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;
        }

    private:
        // Per script configurations
        bool NoNative;
        bool NoDynamicThunks;
        BOOL fCanOptimizeGlobalLookup;
        const bool isOptimizedForManyInstances;
        const ThreadConfiguration * const threadConfig;
    };

    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 * addrOfReturnAddrOfScriptEntryFunction;
        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
    };

#if ENABLE_NATIVE_CODEGEN
    /*
    * This class caches jitted func address ranges.
    * This is to facilitate WER scenarios to use this cache for checking native addresses.
    */
    class JITPageAddrToFuncRangeCache
    {
    public:
        typedef JsUtil::BaseDictionary<void *, uint, HeapAllocator> RangeMap;
        typedef JsUtil::BaseDictionary<void *, RangeMap*, HeapAllocator, PrimeSizePolicy> JITPageAddrToFuncRangeMap;
        typedef JsUtil::BaseDictionary<void *, uint, HeapAllocator, PrimeSizePolicy> LargeJITFuncAddrToSizeMap;

    private:
        JITPageAddrToFuncRangeMap * jitPageAddrToFuncRangeMap;
        LargeJITFuncAddrToSizeMap * largeJitFuncToSizeMap;

        static CriticalSection cs;

    public:
        JITPageAddrToFuncRangeCache() :jitPageAddrToFuncRangeMap(nullptr), largeJitFuncToSizeMap(nullptr) {}
        ~JITPageAddrToFuncRangeCache()
        {
            ClearCache();
        }
        void ClearCache();
        void AddFuncRange(void * address, uint bytes);
        void RemoveFuncRange(void * address);
        void * GetPageAddr(void * address);
        bool IsNativeAddr(void * address);
        JITPageAddrToFuncRangeMap * GetJITPageAddrToFuncRangeMap();
        LargeJITFuncAddrToSizeMap * GetLargeJITFuncAddrToSizeMap();
        static CriticalSection * GetCriticalSection() { return &cs; }
    };
#endif

    class ScriptContext : public ScriptContextBase, public ScriptContextInfo
    {
        friend class LowererMD;
        friend class RemoteScriptContext;
        friend class GlobalObject; // InitializeCache
        friend class SourceTextModuleRecord; // for module bytecode gen.

#if ENABLE_NATIVE_CODEGEN
    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); }

        JITPageAddrToFuncRangeCache * GetJitFuncRangeCache();
    private:
        JITPageAddrToFuncRangeCache * jitFuncRangeCache;
#endif
    public:
        ScriptContext *next;
        ScriptContext *prev;
        bool IsRegistered() { return next != nullptr || prev != nullptr || threadContext->GetScriptContextList() == this; }

        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)(
            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); }

        void RedeferFunctionBodies(ActiveFunctionSet *pActive, uint inactiveThreshold);
        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;
        }

#if ENABLE_NATIVE_CODEGEN
        void InitializeRemoteScriptContext();
#endif

#ifdef ENABLE_JS_ETW
        void EmitStackTraceEvent(__in UINT64 operationID, __in USHORT maxFrameCount, bool emitV2AsyncStackEvent);
        static ushort ProcessNameAndGetLength(Js::StringBuilder<ArenaAllocator>* nameBuffer, const WCHAR* name);
#endif

        void SetPrivilegeLevel(ScriptContextPrivilegeLevel level) { this->scriptContextPrivilegeLevel = level; }
        ScriptContextPrivilegeLevel GetPrivilegeLevel() { return this->scriptContextPrivilegeLevel; }
        void SetIsDiagnosticsScriptContext(bool);
        bool IsDiagnosticsScriptContext() const { return this->isDiagnosticsScriptContext; }
        bool IsScriptContextInNonDebugMode() const;
        bool IsScriptContextInDebugMode() const;
        bool IsScriptContextInSourceRundownOrDebugMode() const;

#ifdef ENABLE_SCRIPT_DEBUGGING
        bool IsDebuggerRecording() const;
        void SetIsDebuggerRecording(bool isDebuggerRecording);
#endif

        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);
        bool IsEnumerateNonUserFunctionsOnly() const { return m_enumerateNonUserFunctionsOnly; }
#ifdef ENABLE_SCRIPT_PROFILING
        bool IsTraceDomCall() const { return !!m_fTraceDomCall; }
#elif defined(ENABLE_SCRIPT_DEBUGGING)
        bool IsTraceDomCall() const { return false; }
#endif

        InlineCache * GetValueOfInlineCache() const { return valueOfInlineCache;}
        InlineCache * GetToStringInlineCache() const { return toStringInlineCache; }

        NoSpecialPropertyScriptRegistry* GetNoSpecialPropertyRegistry() { return &this->noSpecialPropertyRegistry; }
        OnlyWritablePropertyScriptRegistry* GetOnlyWritablePropertyRegistry() { return &this->onlyWritablePropertyRegistry; }
    private:

        JavascriptFunction* GenerateRootFunction(ParseNodeProg * parseTree, uint sourceIndex, Parser* parser, uint32 grfscr, CompileScriptException * pse, const char16 *rootDisplayName);

        typedef void (*EventHandler)(ScriptContext *);
        NoSpecialPropertyScriptRegistry noSpecialPropertyRegistry;
        OnlyWritablePropertyScriptRegistry onlyWritablePropertyRegistry;

        ArenaAllocator generalAllocator;
#ifdef ENABLE_BASIC_TELEMETRY
        ArenaAllocator telemetryAllocator;
#endif

        ArenaAllocator dynamicProfileInfoAllocator;
        InlineCacheAllocator inlineCacheAllocator;
        CacheAllocator isInstInlineCacheAllocator;
        CacheAllocator enumeratorCacheAllocator;

        ArenaAllocator* interpreterArena;

#ifdef ENABLE_SCRIPT_DEBUGGING
        ArenaAllocator* diagnosticArena;
#endif

#if ENABLE_NATIVE_CODEGEN
        PSCRIPTCONTEXT_HANDLE m_remoteScriptContextAddr;
#endif

        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.
#ifdef ENABLE_SCRIPT_DEBUGGING
        bool isEnumeratingRecyclerObjects; // Indicates this scriptContext is enumerating recycler objects. Used by recycler enumerating callbacks to filter out other unrelated scriptContexts.
#endif
        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


public:
#ifdef PROFILE_TYPES
        int convertNullToSimpleCount;
        int convertNullToSimpleDictionaryCount;
        int convertNullToDictionaryCount;
        int convertDeferredToDictionaryCount;
        int convertDeferredToSimpleDictionaryCount;
        int convertSimpleToDictionaryCount;
        int convertSimpleToSimpleDictionaryCount;
        int convertPathToDictionaryExceededLengthCount;
        int convertPathToDictionaryDeletedCount;
        int convertPathToDictionaryAttributesCount;
        int convertPathToDictionaryItemAttributesCount;
        int convertPathToDictionaryAccessorsCount;
        int convertPathToDictionaryItemAccessorsCount;
        int convertPathToDictionaryExtensionsCount;
        int convertPathToDictionaryProtoCount;
        int convertPathToDictionaryNoRootCount;
        int convertPathToDictionaryResetCount;
        int convertPathToSimpleDictionaryCount;
        int convertSimplePathToPathCount;
        int convertSimpleDictionaryToDictionaryCount;
        int convertSimpleSharedDictionaryToNonSharedCount;
        int convertSimpleSharedToNonSharedCount;
        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
#if DBG
        // Count how many Out of Memory and Stack overflow exceptions happened during the execution
        uint oomExceptionCount = 0;
        uint soExceptionCount = 0;
#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, RejitReason 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;
        BailoutStatsMap *bailoutReasonCountsCap;
        uint *rejitReasonCounts;
        uint *rejitReasonCountsCap;
        void ClearBailoutReasonCountsMap();
        void ClearRejitReasonCountsArray();
#endif
#ifdef ENABLE_BASIC_TELEMETRY

    private:
        Js::ScriptContextTelemetry * telemetry;
    public:
        Js::ScriptContextTelemetry& GetTelemetry();
        bool HasTelemetry();

#endif
#ifdef INLINE_CACHE_STATS
        // This is a strongly referenced dictionary, since we want to know hit rates for dead caches.
        typedef JsUtil::BaseDictionary<const Js::PolymorphicInlineCache*, InlineCacheData*, 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
        {
            Field(RecyclerWeakReference<FunctionBody>*) functionBodyWeakRef;
            Field(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 *GetStandardName(size_t *nameLength, DateTime::YMD *ymd = NULL)
        {
            return dateTimeUtility.GetStandardName(nameLength, ymd);
        }

        inline const WCHAR *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

#ifndef CC_LOW_MEMORY_TARGET
        uint integerStringMapCacheMissCount;
        uint integerStringMapCacheUseCount;
#endif
        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;
        bool isEvalRestricted;
#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.

        // Privilege levels are a way of enforcing a relationship hierarchy between two script contexts
        // A less privileged script context is not allowed to marshal in objects that live in a more privileged context
        ScriptContextPrivilegeLevel scriptContextPrivilegeLevel;
        size_t sourceSize;

        void CleanSourceListInternal(bool calledDuringMark);
        typedef JsUtil::List<RecyclerWeakReference<Utf8SourceInfo>*, Recycler, false, Js::FreeListedRemovePolicy> SourceList;
        RecyclerRootPtr<SourceList> sourceList;

#ifdef ENABLE_SCRIPT_DEBUGGING
        typedef void(*RaiseMessageToDebuggerFunctionType)(ScriptContext *, DEBUG_EVENT_INFO_TYPE, LPCWSTR, LPCWSTR);
        RaiseMessageToDebuggerFunctionType raiseMessageToDebuggerFunctionType;

        typedef void(*TransitionToDebugModeIfFirstSourceFn)(ScriptContext *, Utf8SourceInfo *);
        TransitionToDebugModeIfFirstSourceFn transitionToDebugModeIfFirstSourceFn;

        DebugContext* debugContext;
        CriticalSection debugContextCloseCS;
#endif

#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

        // 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;

        ScriptContext(ThreadContext* threadContext);
        void InitializeAllocations();
        void InitializePreGlobal();
        void InitializePostGlobal();
        void InitializeCache();

        bool isDebugContextInitialized;

        // Source Info
        void EnsureSourceContextInfoMap();
        void EnsureDynamicSourceContextInfoMap();

        void AddToEvalMapHelper(FastEvalMapString & key, BOOL isIndirect, ScriptFunction *pFuncScript);

        uint moduleSrcInfoCount;
#ifdef RUNTIME_DATA_COLLECTION
        time_t createTime;
#endif
        char16 const * url;

        void PrintStats();

        void InternalClose();

        template <typename TProperty> void InvalidatePropertyCache(PropertyId propertyId, Type* type);
        void InvalidatePropertyRecordUsageCache(PropertyRecordUsageCache* propertyRecordUsageCache, Type *type);
        template <typename TProperty> TProperty* TryGetProperty(PropertyId propertyId);
        template <typename TProperty> TProperty* GetProperty(PropertyId propertyId, const PropertyRecord* propertyRecord);
        template <typename TProperty> TProperty* CreateAndCacheSymbolOrPropertyString(const PropertyRecord* propertyRecord);

    public:

#ifdef LEAK_REPORT
        LeakReport::UrlRecord * urlRecord;
        bool isRootTrackerScriptContext;
#endif

        DateTime::DaylightTimeHelper *GetDaylightTimeHelper() { return &daylightTimeHelper; }
        DateTime::Utility *GetDateUtility() { return &dateTimeUtility; }

        virtual bool IsClosed() const override { return isClosed; }
        void SetIsClosed();

        bool IsFinalized() const { return isFinalized; }
        void SetIsFinalized() { isFinalized = true; }
        bool IsActuallyClosed() const { return isScriptContextActuallyClosed; }
        void SetEvalRestriction(bool set) { this->isEvalRestricted = set; }
        bool IsEvalRestriction() const { return this->isEvalRestricted; }
#if ENABLE_NATIVE_CODEGEN
        bool IsClosedNativeCodeGenerator() const
        {
            return !nativeCodeGen || ::IsClosedNativeCodeGenerator(nativeCodeGen);
        }
#endif

        void SetHasUsedInlineCache(bool value)
        {
            hasUsedInlineCache = value;
#if DBG
            if(hasUsedInlineCache)
            {
                inlineCacheAllocator.Unlock();
            }
#endif
        }

        void SetDirectHostTypeId(TypeId typeId) {directHostTypeId = typeId; }
        TypeId GetDirectHostTypeId() const { return directHostTypeId; }

#if ENABLE_NATIVE_CODEGEN
        PSCRIPTCONTEXT_HANDLE GetRemoteScriptAddr(bool allowInitialize = true)
        {
#if ENABLE_OOP_NATIVE_CODEGEN
            if (!m_remoteScriptContextAddr && allowInitialize)
            {
                InitializeRemoteScriptContext();
            }
#endif
            return m_remoteScriptContextAddr;
        }
#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

#ifdef HEAP_ENUMERATION_VALIDATION
        bool IsInitialized() { return this->isInitialized; }
#endif

#ifdef ENABLE_SCRIPT_DEBUGGING
        bool IsDebugContextInitialized() const { return this->isDebugContextInitialized; }
        DebugContext* GetDebugContext() const;
        CriticalSection* GetDebugContextCloseCS() { return &debugContextCloseCS; }
#endif

        uint callCount;

        // if the current context is for webworker
        DWORD webWorkerId;

        static ScriptContext * New(ThreadContext * threadContext);

        ~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();
        bool IsJsBuiltInEnabled();
        JavascriptLibrary* GetLibrary() const { return javascriptLibrary; }
        Js::Cache* Cache() const{ return &this->javascriptLibrary->cache; }
        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 ForceNoDynamicThunks() { config.ForceNoDynamicThunks(); }
        void ForceNoNative() { config.ForceNoNative(); }
        void ForceNative() { config.ForceNative(); }
        ScriptConfiguration const * GetConfig(void) const { return &config; }
        CharClassifier const * GetCharClassifier(void) const;

        static bool ExceedsStackNestedFuncCount(uint count);

        ThreadContext * GetThreadContext() const { return threadContext; }

        static const int MaxEvalSourceSize = 400;

        bool IsInEvalMap(FastEvalMapString const& key, BOOL isIndirect, ScriptFunction **ppFuncScript);
        void AddToEvalMap(FastEvalMapString & key, BOOL isIndirect, ScriptFunction *pFuncScript);

        template <typename TCacheType>
        void CleanDynamicFunctionCache(TCacheType* 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, FunctionInfo **ppFuncInfo);
        void AddToNewFunctionMap(EvalMapString const& key, FunctionInfo *pFuncInfo);

        SourceContextInfo * GetSourceContextInfo(DWORD_PTR hostSourceContext, SimpleDataCacheWrapper* dataCacheWrapper);
        SourceContextInfo * GetSourceContextInfo(uint hash);
        SourceContextInfo * CreateSourceContextInfo(uint hash, DWORD_PTR hostSourceContext);
        SourceContextInfo * CreateSourceContextInfo(DWORD_PTR hostSourceContext, char16 const * url, size_t len,
            SimpleDataCacheWrapper* dataCacheWrapper, char16 const * sourceMapUrl = nullptr, size_t sourceMapUrlLen = 0);

#if defined(LEAK_REPORT) || defined(CHECK_MEMORY_LEAK)
        void ClearSourceContextInfoMaps()
        {
#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 (this->Cache()->profileInfoList)
            {
                this->Cache()->profileInfoList->Reset();
                this->Cache()->profileInfoList = nullptr;
            }
        }

        DynamicProfileInfoList * GetProfileInfoList() { return this->Cache()->profileInfoList; }
#endif
#endif

        SRCINFO const * GetModuleSrcInfo(Js::ModuleID moduleID);
        SourceContextInfoMap* GetSourceContextInfoMap()
        {
            return this->Cache()->sourceContextInfoMap;
        }
        DynamicSourceContextInfoMap* GetDynamicSourceContextInfoMap()
        {
            return this->Cache()->dynamicSourceContextInfoMap;
        }

#if ENABLE_TTD
        //The host callback functor info
        HostScriptContextCallbackFunctor TTDHostCallbackFunctor;

        //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;

        //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;

        //A flag indicating a TTDSnapshot is in progress
        bool TTDSnapshotOrInflateInProgress;

        //Check if a snapshot is in progress on this context
        bool IsTTDSnapshotOrInflateInProgress() const { return this->TTDSnapshotOrInflateInProgress; }

        //Memoized results for frequently used Record/Replay action checks
        bool TTDRecordOrReplayModeEnabled;
        bool TTDRecordModeEnabled;
        bool TTDReplayModeEnabled;

        bool TTDShouldPerformRecordOrReplayAction;
        bool TTDShouldPerformRecordAction;
        bool TTDShouldPerformReplayAction;

        bool TTDShouldPerformRecordOrReplayDebuggerAction;
        bool TTDShouldPerformRecordDebuggerAction;
        bool TTDShouldPerformReplayDebuggerAction;

        bool TTDShouldSuppressGetterInvocationForDebuggerEvaluation;

        //Check if the TTD system has been activated (and record/replay may or may not be enabled)
        bool IsTTDRecordOrReplayModeEnabled() const { return this->TTDRecordOrReplayModeEnabled; }

        //Check if the TTD Record system has been activated (and  may or may not be enabled)
        bool IsTTDRecordModeEnabled() const { return this->TTDRecordModeEnabled; }

        //Check if the TTD Replay system has been activated (and  may or may not be enabled)
        bool IsTTDReplayModeEnabled() const { return this->TTDReplayModeEnabled; }

        //Check if we are in (record OR replay) AND this code is being run on behalf of the user application
        bool ShouldPerformRecordOrReplayAction() const { return this->TTDShouldPerformRecordOrReplayAction; }

        //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 this->TTDShouldPerformRecordAction; }

        //Use this to check specifically if we are in replay mode AND this code is being run on behalf of the user application
        bool ShouldPerformReplayAction() const { return this->TTDShouldPerformReplayAction; }

        //Use this to check specifically if we are in debugging mode AND this code is being run on behalf of the user application
        bool ShouldPerformRecordOrReplayDebuggerAction() const { return this->TTDShouldPerformRecordOrReplayDebuggerAction; }

        //Use this to check specifically if we are in debugging record mode AND this code is being run on behalf of the user application
        bool ShouldPerformRecordDebuggerAction() const { return this->TTDShouldPerformRecordDebuggerAction; }

        //Use this to check specifically if we are in debugging replay mode AND this code is being run on behalf of the user application
        bool ShouldPerformReplayDebuggerAction() const { return this->TTDShouldPerformReplayDebuggerAction; }

        //A special check to see if we are debugging and want to suppress the execution of getters (which may be triggered by displaying values in the debugger)
        bool ShouldSuppressGetterInvocationForDebuggerEvaluation() const { return this->TTDShouldSuppressGetterInvocationForDebuggerEvaluation; }

        //
        //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();
#endif

        void SetFirstInterpreterFrameReturnAddress(void * returnAddress) { firstInterpreterFrameReturnAddress = returnAddress;}
        void *GetFirstInterpreterFrameReturnAddress() { return firstInterpreterFrameReturnAddress;}

        void CleanupWeakReferenceDictionaries();

        void Initialize();
        bool Close(bool inDestructor);
        void MarkForClose();
        void SetCanOptimizeGlobalLookupFlag(BOOL f){ config.SetCanOptimizeGlobalLookupFlag(f);}
        BOOL CanOptimizeGlobalLookup(){ return config.CanOptimizeGlobalLookup();}

        bool IsFastDOMEnabled() { return fastDOMenabled; }
        void SetFastDOMenabled();
        BOOL VerifyAlive(BOOL isJSFunction = FALSE, ScriptContext* requestScriptContext = nullptr);
        void VerifyAliveWithHostContext(BOOL isJSFunction, HostScriptContext* requestHostScriptContext);

        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);
        }
        void GetOrAddPropertyRecord(_In_ Js::JavascriptString * propertyString, _Out_ PropertyRecord const** 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, _Out_ 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();

        template<WellKnownHostType wellKnownType>
        bool IsWellKnownHostType(Js::TypeId typeId) { return threadContext->IsWellKnownHostType<wellKnownType>(typeId); }
        void SetWellKnownHostTypeId(WellKnownHostType wellKnownType, Js::TypeId typeId) { threadContext->SetWellKnownHostTypeId(wellKnownType, typeId); }

        ParseNodeProg * ParseScript(Parser* parser, const byte* script,
            size_t cb, SRCINFO const * pSrcInfo,
            CompileScriptException * pse, Utf8SourceInfo** ppSourceInfo,
            const char16 *rootDisplayName, LoadScriptFlag loadScriptFlag,
            uint* sourceIndex, Js::Var scriptSource = nullptr);

        JavascriptFunction* LoadScript(const byte* script, size_t cb,
            SRCINFO const * pSrcInfo,
            CompileScriptException * pse, Utf8SourceInfo** ppSourceInfo,
            const char16 *rootDisplayName, LoadScriptFlag loadScriptFlag,
            Js::Var scriptSource = nullptr);

        JavascriptFunction* LoadScriptInternal(Parser* parser,
            const byte* script, size_t cb,
            SRCINFO const * pSrcInfo,
            CompileScriptException * pse, Utf8SourceInfo** ppSourceInfo,
            const char16 *rootDisplayName, LoadScriptFlag loadScriptFlag,
            Js::Var scriptSource = nullptr);

        HRESULT TryDeserializeParserState(
            _In_ ULONG grfscr,
            _In_ uint sourceCRC,
            _In_ charcount_t cchLength,
            _In_ SRCINFO *srcInfo,
            _In_ Js::Utf8SourceInfo* utf8SourceInfo,
            _Inout_ uint& sourceIndex,
            _In_ bool isCesu8,
            _In_opt_ NativeModule* nativeModule,
            _Outptr_ Js::ParseableFunctionInfo ** func,
            _Outptr_result_buffer_(*parserStateCacheByteCount) byte** parserStateCacheBuffer,
            _Out_ DWORD* parserStateCacheByteCount,
            _In_ Js::SimpleDataCacheWrapper* pDataCache);

        HRESULT TrySerializeParserState(
            _In_ uint sourceCRC,
            _In_reads_bytes_(cbLength) LPCUTF8 pszSrc,
            _In_ size_t cbLength,
            _In_ SRCINFO *srcInfo,
            _In_ Js::ParseableFunctionInfo* func,
            _In_reads_bytes_(parserStateCacheByteCount) byte* parserStateCacheBuffer,
            _In_ DWORD parserStateCacheByteCount,
            _In_ Js::SimpleDataCacheWrapper* pDataCache);

        HRESULT CompileUTF8Core(
            __in Parser& ps,
            __in Js::Utf8SourceInfo* utf8SourceInfo,
            __in SRCINFO *srcInfo,
            __in BOOL fOriginalUTF8Code,
            _In_reads_bytes_(cbLength) LPCUTF8 pszSrc,
            __in size_t cbLength,
            __in ULONG grfscr,
            __in CompileScriptException *pse,
            __inout charcount_t& cchLength,
            __out size_t& srcLength,
            __out uint& sourceIndex,
            __deref_out Js::ParseableFunctionInfo ** func,
            __in_opt Js::SimpleDataCacheWrapper* pDataCache);

        HRESULT SerializeParserState(const byte* script, size_t cb,
            SRCINFO const * pSrcInfo,
            CompileScriptException * pse, Utf8SourceInfo** ppSourceInfo,
            const char16 *rootDisplayName, LoadScriptFlag loadScriptFlag,
            byte** buffer, DWORD* bufferSize, ArenaAllocator* alloc,
            JavascriptFunction** function = nullptr,
            Js::Var scriptSource = nullptr);

        void MakeUtf8SourceInfo(const byte* script,
            size_t cb,
            SRCINFO const * pSrcInfo,
            Utf8SourceInfo** ppSourceInfo,
            LoadScriptFlag loadScriptFlag,
            Js::Var scriptSource);

        ULONG GetParseFlags(LoadScriptFlag loadScriptFlag, Utf8SourceInfo* pSourceInfo, SourceContextInfo* sourceContextInfo);

        ArenaAllocator* GeneralAllocator() { return &generalAllocator; }

#ifdef ENABLE_BASIC_TELEMETRY
        ArenaAllocator* TelemetryAllocator() { return &telemetryAllocator; }
#endif

#ifdef SEPARATE_ARENA
        ArenaAllocator* SourceCodeAllocator() { return &sourceCodeAllocator; }
        ArenaAllocator* RegexAllocator() { return &regexAllocator; }
#else
        ArenaAllocator* SourceCodeAllocator() { return &generalAllocator; }
        ArenaAllocator* RegexAllocator() { return &generalAllocator; }
#endif
#ifdef NEED_MISC_ALLOCATOR
        ArenaAllocator* MiscAllocator() { return &miscAllocator; }
#endif
        InlineCacheAllocator* GetInlineCacheAllocator() { return &inlineCacheAllocator; }
        CacheAllocator* GetIsInstInlineCacheAllocator() { return &isInstInlineCacheAllocator; }
        CacheAllocator * GetEnumeratorAllocator() { return &enumeratorCacheAllocator; }
        ArenaAllocator* DynamicProfileInfoAllocator() { return &dynamicProfileInfoAllocator; }

#ifdef ENABLE_SCRIPT_DEBUGGING
        ArenaAllocator* AllocatorForDiagnostics();
#endif

        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();

        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 &(this->Cache()->enumObjCache); }
        PropertyString* GetPropertyString(PropertyId propertyId);
        PropertyString* GetPropertyString(const PropertyRecord* propertyRecord);
        JavascriptSymbol* GetSymbol(PropertyId propertyId);
        JavascriptSymbol* GetSymbol(const PropertyRecord* propertyRecord);
        void InvalidatePropertyStringAndSymbolCaches(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 SaveSourceNoCopy(Utf8SourceInfo* sourceInfo, int cchLength, bool isCesu8);

        Utf8SourceInfo* GetSource(uint sourceIndex);
        void RemoveSource(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; }

#ifdef ENABLE_SCRIPT_DEBUGGING
        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);
            }
        }
#endif

        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;
        }

    private:
        Js::PropertyId emptyStringPropertyId;
    public:
        Js::PropertyId GetEmptyStringPropertyId()
        {
            return threadContext->GetEmptyStringPropertyId();
        }

        void FreeFunctionEntryPoint(Js::JavascriptMethod codeAddress, Js::JavascriptMethod thunkAddress);

    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();
        void ClearEnumeratorCaches();
#ifdef PERSISTENT_INLINE_CACHES
        void ClearInlineCachesWithDeadWeakRefs();
#endif
        void ClearScriptContextCaches();
#if ENABLE_NATIVE_CODEGEN
        void RegisterConstructorCache(Js::PropertyId propertyId, Js::ConstructorCache* cache);
#endif

    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 == this->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
        }

        SRCINFO *AddHostSrcInfo(SRCINFO const *pSrcInfo);

#if DBG
        SourceContextInfo const * GetNoContextSourceContextInfo() const { return this->Cache()->noContextSourceContextInfo; }
        bool hadProfiled;
        bool HadProfiled() const { return hadProfiled; }

#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
#endif

#ifdef ENABLE_SCRIPT_PROFILING
        void CoreSetProfileEventMask(DWORD dwEventMask);
        typedef HRESULT(*RegisterExternalLibraryType)(Js::ScriptContext *pScriptContext);
        HRESULT RegisterProfileProbe(IActiveScriptProfilerCallback *pProfileCallback, DWORD dwEventMask, DWORD dwContext, RegisterExternalLibraryType RegisterExternalLibrary, JavascriptMethod dispatchInvoke);
        HRESULT DeRegisterProfileProbe(HRESULT hrReason, JavascriptMethod dispatchInvoke);
        HRESULT RegisterLibraryFunction(const char16 *pwszObjectName, const char16 *pwszFunctionName, Js::PropertyId functionPropertyId, JavascriptMethod entryPoint);
        HRESULT RegisterBuiltinFunctions(RegisterExternalLibraryType RegisterExternalLibrary);
        HRESULT SetProfileEventMask(DWORD dwEventMask);

        HRESULT RegisterScript(Js::FunctionProxy *pFunctionBody, BOOL fRegisterScript = TRUE);

        // Register static and dynamic scripts
        HRESULT RegisterAllScripts();
#endif

#ifdef ENABLE_SCRIPT_DEBUGGING
        // Iterate through utf8sourceinfo and clear debug document if they are there.
        void EnsureClearDebugDocument();
        void UpdateRecyclerFunctionEntryPointsForDebugger();
        static void RecyclerFunctionCallbackForDebugger(void *address, size_t size);
        void SetFunctionInRecyclerToProfileMode(bool enumerateNonUserFunctionsOnly = false);
#ifdef ASMJS_PLAT
        void TransitionEnvironmentForDebugger(ScriptFunction * scriptFunction);
#endif
#endif

        // To be called directly only when the thread context is shutting down
        void ShutdownClearSourceLists();

#if defined(ENABLE_SCRIPT_PROFILING) || defined(ENABLE_SCRIPT_DEBUGGING)
        void RegisterDebugThunk(bool calledDuringAttach = true);
        void UnRegisterDebugThunk();
        static void SetEntryPointToProfileThunk(JavascriptFunction* function);
        static void RestoreEntryPointFromProfileThunk(JavascriptFunction* function);
        static void RecyclerEnumClassEnumeratorCallback(void *address, size_t size);
#endif

#if ENABLE_NATIVE_CODEGEN
        HRESULT RecreateNativeCodeGenerator(NativeCodeGenerator ** previousCodeGen = nullptr);
        void DeletePreviousNativeCodeGenerator(NativeCodeGenerator * codeGen);
        HRESULT OnDebuggerAttachedDetached(bool attach, NativeCodeGenerator ** previousCodeGenHolder = nullptr);
#else
        HRESULT OnDebuggerAttachedDetached(bool attach);
#endif
        bool IsForceNoNative();

#ifdef ENABLE_SCRIPT_DEBUGGING
        HRESULT OnDebuggerAttached();
        HRESULT OnDebuggerDetached();
        void InitializeDebugging();
        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;
        };
#endif

#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);

#ifdef ENABLE_JS_ETW
        static charcount_t AppendWithEscapeCharacters(Js::StringBuilder<ArenaAllocator>* stringBuilder, const WCHAR* sourceString, charcount_t sourceStringLen, WCHAR escapeChar, WCHAR charToEscape);
#endif

    public:
#if DYNAMIC_INTERPRETER_THUNK
        JavascriptMethod GetNextDynamicAsmJsInterpreterThunk(PVOID* ppDynamicInterpreterThunk);
        JavascriptMethod GetNextDynamicInterpreterThunk(PVOID* ppDynamicInterpreterThunk);
#if DBG
        BOOL IsDynamicInterpreterThunk(JavascriptMethod address);
#endif
        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, ...);

#if defined(ENABLE_SCRIPT_DEBUGGING) || defined(ENABLE_SCRIPT_PROFILING)
        static Var ProfileModeThunk_DebugModeWrapper(JavascriptFunction* function, ScriptContext* scriptContext, JavascriptMethod entryPoint, Arguments& args);
        static JavascriptMethod GetProfileModeThunk(JavascriptMethod entryPoint);
#endif

#ifdef ENABLE_SCRIPT_PROFILING
        void SetProfileMode(BOOL fSet);
        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);

        bool SetDispatchProfile(bool fSet, JavascriptMethod dispatchInvoke);
        HRESULT OnDispatchFunctionEnter(const WCHAR *pwszFunctionName);
        HRESULT OnDispatchFunctionExit(const WCHAR *pwszFunctionName);

#endif // ENABLE_SCRIPT_PROFILING

        void OnStartupComplete();
        void SaveStartupProfileAndRelease(bool isSaveOnClose = false);

#if ENABLE_PROFILE_INFO
        template<template<typename> class BarrierT>
        void AddDynamicProfileInfo(FunctionBody * functionBody, BarrierT<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:
        virtual intptr_t GetNullAddr() const override;
        virtual intptr_t GetUndefinedAddr() const override;
        virtual intptr_t GetTrueAddr() const override;
        virtual intptr_t GetFalseAddr() const override;
        virtual intptr_t GetUndeclBlockVarAddr() const override;
        virtual intptr_t GetEmptyStringAddr() const override;
        virtual intptr_t GetNegativeZeroAddr() const override;
        virtual intptr_t GetNumberTypeStaticAddr() const override;
        virtual intptr_t GetStringTypeStaticAddr() const override;
        virtual intptr_t GetSymbolTypeStaticAddr() const override;
        virtual intptr_t GetObjectTypeAddr() const override;
        virtual intptr_t GetObjectHeaderInlinedTypeAddr() const override;
        virtual intptr_t GetRegexTypeAddr() const override;
        virtual intptr_t GetArrayTypeAddr() const override;
        virtual intptr_t GetNativeIntArrayTypeAddr() const override;
        virtual intptr_t GetNativeFloatArrayTypeAddr() const override;
        virtual intptr_t GetArrayConstructorAddr() const override;
        virtual intptr_t GetCharStringCacheAddr() const override;
        virtual intptr_t GetSideEffectsAddr() const override;
        virtual intptr_t GetArraySetElementFastPathVtableAddr() const override;
        virtual intptr_t GetIntArraySetElementFastPathVtableAddr() const override;
        virtual intptr_t GetFloatArraySetElementFastPathVtableAddr() const override;
        virtual intptr_t GetLibraryAddr() const override;
        virtual intptr_t GetGlobalObjectAddr() const override;
        virtual intptr_t GetGlobalObjectThisAddr() const override;
        virtual intptr_t GetObjectPrototypeAddr() const;
        virtual intptr_t GetFunctionPrototypeAddr() const;
        virtual intptr_t GetNumberAllocatorAddr() const override;
        virtual intptr_t GetRecyclerAddr() const override;
        virtual bool GetRecyclerAllowNativeCodeBumpAllocation() const override;
        virtual bool IsPRNGSeeded() const override;
        virtual intptr_t GetBuiltinFunctionsBaseAddr() const override;

#ifdef ENABLE_SCRIPT_DEBUGGING
        virtual intptr_t GetDebuggingFlagsAddr() const override;
        virtual intptr_t GetDebugStepTypeAddr() const override;
        virtual intptr_t GetDebugFrameAddressAddr() const override;
        virtual intptr_t GetDebugScriptIdWhenSetAddr() const override;
#endif

        virtual intptr_t GetChakraLibAddr() const override;
        virtual intptr_t GetAddr() const override;

        virtual intptr_t GetVTableAddress(VTableValue vtableType) const override;

        virtual bool IsRecyclerVerifyEnabled() const override;
        virtual uint GetRecyclerVerifyPad() const override;

        virtual Field(Js::Var)* GetModuleExportSlotArrayAddress(uint moduleIndex, uint slotIndex) override;

        Js::SourceTextModuleRecord* GetModuleRecord(uint moduleId) const
        {
            return javascriptLibrary->GetModuleRecord(moduleId);
        }

        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;
    }

    class AutoProfilingPhase
    {
    public:
        AutoProfilingPhase(ScriptContext* scriptcontext, Js::Phase phase) : scriptcontext(scriptcontext), phase(phase), isPhaseComplete(false)
        {
    #ifdef PROFILE_EXEC
            scriptcontext->ProfileBegin(phase);
    #endif
        }

        ~AutoProfilingPhase()
        {
            if(!this->isPhaseComplete)
            {
                EndProfile();
            }
        }

        void EndProfile()
        {
            this->isPhaseComplete = true;
#ifdef PROFILE_EXEC
            scriptcontext->ProfileEnd(phase);
#endif
        }
    private:
        ScriptContext* scriptcontext;
        Js::Phase phase;
        bool isPhaseComplete;
    };

#ifdef ENABLE_SCRIPT_DEBUGGING
    // Set up a scope in which we will initialize library JS code (like Intl.js),
    // which should not be treated as user-level JS code.
    // We should not profile and should not log debugger information in such a scope.
    class AutoInitLibraryCodeScope
    {
    private:
        ScriptContext * const scriptContext;
        const bool oldIsProfilingUserCode;
        const bool oldIsDebuggerRecording;

    public:
        AutoInitLibraryCodeScope(ScriptContext *scriptContext) :
            scriptContext(scriptContext),
            oldIsProfilingUserCode(scriptContext->GetThreadContext()->IsProfilingUserCode()),
            oldIsDebuggerRecording(scriptContext->IsDebuggerRecording())
        {
            this->scriptContext->GetThreadContext()->SetIsProfilingUserCode(false);
            this->scriptContext->SetIsDebuggerRecording(false);
        }

        ~AutoInitLibraryCodeScope()
        {
            this->scriptContext->GetThreadContext()->SetIsProfilingUserCode(this->oldIsProfilingUserCode);
            this->scriptContext->SetIsDebuggerRecording(this->oldIsDebuggerRecording);
        }
    };
#endif
}

#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);

