瀏覽代碼

[2.0>master] [MERGE #2592 @leirocks] Fix leak in JSRT

Merge pull request #2592 from leirocks:jsrtleak

The leak is because of a circular reference and more than one node on the circle are pinned.

1. Pinned object fakeGlobalFuncForUndefer (as well as profileInfoList in test/debug build) reference to javascriptLibrary -- directly or indirectly, these are rely on ScriptContext::Close() to unpin.
2. javascriptLibrary has a reference to JsrtContext
3. JsrtContext is pinned while setting to current thread, and unpinned when getting out of current thread
4. if user code didn't explicited pin JsrtContext (in following POC), at this stage it should be disposed in next GC, and hence call ScriptContext::Close()
5. the disposal in # 4 didn't because JsrtContext is reachable through fakeGlobalFuncForUndefer->javascriptLibrary->JsrtContext(# 2), so the whole graph is leaked
6. when there's external call to JsDisposeRuntime, it will directly dispose JsrtContext, and then ScriptContext::Close, unpin fakeGlobalFuncForUndefer then everything is collectable

the POC:
```c++
    JsRuntimeHandle runtime;
    unsigned currentSourceContext = 0;
    JsCreateRuntime(JsRuntimeAttributeNone, nullptr, &runtime);
    auto runJob = [&](wstring script, int i)
    {
        {
            JsValueRef result;
            JsContextRef context;
            JsCreateContext(runtime, &context);
            JsSetCurrentContext(context);
            JsRunScript(script.c_str(), currentSourceContext++, L"", &result);
            JsSetCurrentContext(JS_INVALID_REFERENCE);
            context = nullptr;
            result = nullptr;
        }

        if (i % 5 == 0) {
            JsCollectGarbage(runtime); // JsrtContext in above scope should be collectible at this point,
                                       // but the Finalize/Dispose of JsrtContext didn't happen
        }
    };

    for (int i = 0; i < 100; i++)
    {
        runJob(L"(()=>{return \'Hello world!\';})()", i);
    }

    printf("JsDisposeRuntime\n");
    JsDisposeRuntime(runtime); // all JsrtContext will be collected at this point
    printf("After JsDisposeRuntime\n");
```

The fix is, do not pin fakeGlobalFuncForUndefer and profileInfoList. However, there are a lot of code(mostly debugger related code) rely on the leak to do the cleanup. Most of the work is to make sure the cleanup working correctly (without either UAF or leak).
Lei Shi 9 年之前
父節點
當前提交
9168573bd3

+ 6 - 12
lib/Jsrt/Core/JsrtContextCore.cpp

@@ -39,7 +39,6 @@ JsrtContextCore::JsrtContextCore(JsrtRuntime * runtime) :
 {
     EnsureScriptContext();
     Link();
-    PinCurrentJsrtContext();
 }
 
 /* static */
@@ -50,22 +49,17 @@ JsrtContextCore *JsrtContextCore::New(JsrtRuntime * runtime)
 
 void JsrtContextCore::Dispose(bool isShutdown)
 {
-    if (nullptr != this->GetJavascriptLibrary())
+    if (this->GetJavascriptLibrary())
     {
-        Js::ScriptContext* scriptContxt = this->GetJavascriptLibrary()->GetScriptContext();
-        if (this->GetRuntime()->GetJsrtDebugManager() != nullptr)
-        {
-            this->GetRuntime()->GetJsrtDebugManager()->ClearDebugDocument(scriptContxt);
-        }
-        scriptContxt->EnsureClearDebugDocument();
-        scriptContxt->GetDebugContext()->GetProbeContainer()->UninstallInlineBreakpointProbe(NULL);
-        scriptContxt->GetDebugContext()->GetProbeContainer()->UninstallDebuggerScriptOptionCallback();
-        scriptContxt->MarkForClose();
-        this->SetJavascriptLibrary(nullptr);
         Unlink();
+        this->SetJavascriptLibrary(nullptr);
     }
 }
 
+void JsrtContextCore::Finalize(bool isShutdown)
+{
+}
+
 Js::ScriptContext* JsrtContextCore::EnsureScriptContext()
 {
     Assert(this->GetJavascriptLibrary() == nullptr);

+ 1 - 0
lib/Jsrt/Core/JsrtContextCore.h

@@ -11,6 +11,7 @@ class JsrtContextCore sealed : public JsrtContext
 {
 public:
     static JsrtContextCore *New(JsrtRuntime * runtime);
+    virtual void Finalize(bool isShutdown) override;
     virtual void Dispose(bool isShutdown) override;
     ChakraCoreHostScriptContext* GetHostScriptContext() const { return hostContext; }
 

+ 3 - 3
lib/Jsrt/Core/JsrtCore.cpp

@@ -119,7 +119,7 @@ JsModuleEvaluation(
         *result = JS_INVALID_REFERENCE;
     }
     Js::ScriptContext* scriptContext = moduleRecord->GetScriptContext();
-    JsrtContext* jsrtContext = (JsrtContext*)scriptContext->GetLibrary()->GetPinnedJsrtContextObject();
+    JsrtContext* jsrtContext = (JsrtContext*)scriptContext->GetLibrary()->GetJsrtContext();
     JsErrorCode errorCode = SetContextAPIWrapper(jsrtContext, [&](Js::ScriptContext *scriptContext) -> JsErrorCode {
         SmartFPUControl smartFpuControl;
         if (smartFpuControl.HasErr())
@@ -148,7 +148,7 @@ JsSetModuleHostInfo(
     }
     Js::SourceTextModuleRecord* moduleRecord = Js::SourceTextModuleRecord::FromHost(requestModule);
     Js::ScriptContext* scriptContext = moduleRecord->GetScriptContext();
-    JsrtContext* jsrtContext = (JsrtContext*)scriptContext->GetLibrary()->GetPinnedJsrtContextObject();
+    JsrtContext* jsrtContext = (JsrtContext*)scriptContext->GetLibrary()->GetJsrtContext();
     JsErrorCode errorCode = SetContextAPIWrapper(jsrtContext, [&](Js::ScriptContext *scriptContext) -> JsErrorCode {
         JsrtContextCore* currentContext = static_cast<JsrtContextCore*>(JsrtContextCore::GetCurrent());
         switch (moduleHostInfo)
@@ -186,7 +186,7 @@ JsGetModuleHostInfo(
     *hostInfo = nullptr;
     Js::SourceTextModuleRecord* moduleRecord = Js::SourceTextModuleRecord::FromHost(requestModule);
     Js::ScriptContext* scriptContext = moduleRecord->GetScriptContext();
-    JsrtContext* jsrtContext = (JsrtContext*)scriptContext->GetLibrary()->GetPinnedJsrtContextObject();
+    JsrtContext* jsrtContext = (JsrtContext*)scriptContext->GetLibrary()->GetJsrtContext();
     JsErrorCode errorCode = SetContextAPIWrapper(jsrtContext, [&](Js::ScriptContext *scriptContext) -> JsErrorCode {
         JsrtContextCore* currentContext = static_cast<JsrtContextCore*>(JsrtContextCore::GetCurrent());
         switch (moduleHostInfo)

+ 11 - 1
lib/Jsrt/Jsrt.cpp

@@ -460,6 +460,16 @@ CHAKRA_API JsDisposeRuntime(_In_ JsRuntimeHandle runtimeHandle)
             runtime->GetJsrtDebugManager()->ClearDebuggerObjects();
         }
 
+        Js::ScriptContext *scriptContext;
+        for (scriptContext = threadContext->GetScriptContextList(); scriptContext; scriptContext = scriptContext->next)
+        {
+            if (runtime->GetJsrtDebugManager() != nullptr)
+            {
+                runtime->GetJsrtDebugManager()->ClearDebugDocument(scriptContext);
+            }
+            scriptContext->MarkForClose();
+        }
+
         // Close any open Contexts.
         // We need to do this before recycler shutdown, because ScriptEngine->Close won't work then.
         runtime->CloseContexts();
@@ -808,7 +818,7 @@ CHAKRA_API JsGetContextOfObject(_In_ JsValueRef object, _Out_ JsContextRef *cont
             RETURN_NO_EXCEPTION(JsErrorArgumentNotObject);
         }
         Js::RecyclableObject* obj = Js::RecyclableObject::FromVar(object);
-        *context = (JsContextRef)obj->GetScriptContext()->GetLibrary()->GetPinnedJsrtContextObject();
+        *context = (JsContextRef)obj->GetScriptContext()->GetLibrary()->GetJsrtContext();
     }
     END_JSRT_NO_EXCEPTION
 }

+ 4 - 10
lib/Jsrt/JsrtContext.cpp

@@ -16,12 +16,10 @@ JsrtContext::JsrtContext(JsrtRuntime * runtime) :
 void JsrtContext::SetJavascriptLibrary(Js::JavascriptLibrary * library)
 {
     this->javascriptLibrary = library;
-}
-
-void JsrtContext::PinCurrentJsrtContext()
-{
-    Assert(this->javascriptLibrary);
-    this->javascriptLibrary->PinJsrtContextObject(this);
+    if (this->javascriptLibrary)
+    {
+        this->javascriptLibrary->SetJsrtContext(this);
+    }
 }
 
 void JsrtContext::Link()
@@ -109,10 +107,6 @@ bool JsrtContext::TrySetCurrent(JsrtContext * context)
     return true;
 }
 
-void JsrtContext::Finalize(bool isShutdown)
-{
-}
-
 void JsrtContext::Mark(Recycler * recycler)
 {
     AssertMsg(false, "Mark called on object that isn't TrackableObject");

+ 0 - 2
lib/Jsrt/JsrtContext.h

@@ -21,7 +21,6 @@ public:
     static bool TrySetCurrent(JsrtContext * context);
     static bool Is(void * ref);
 
-    virtual void Finalize(bool isShutdown) override sealed;
     virtual void Mark(Recycler * recycler) override sealed;
 
 #if ENABLE_TTD
@@ -35,7 +34,6 @@ protected:
     void Link();
     void Unlink();
     void SetJavascriptLibrary(Js::JavascriptLibrary * library);
-    void PinCurrentJsrtContext();
 private:
     Field(Js::JavascriptLibrary *) javascriptLibrary;
 

+ 139 - 131
lib/Runtime/Base/ScriptContext.cpp

@@ -129,7 +129,6 @@ namespace Js
         hasProtoOrStoreFieldInlineCache(false),
         hasIsInstInlineCache(false),
         registeredPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext(nullptr),
-        cache(nullptr),
         firstInterpreterFrameReturnAddress(nullptr),
         builtInLibraryFunctions(nullptr),
         m_remoteScriptContextAddr(nullptr),
@@ -552,21 +551,20 @@ namespace Js
 
     uint ScriptContext::GetNextSourceContextId()
     {
-        Assert(this->cache);
 
-        Assert(this->cache->sourceContextInfoMap ||
-            this->cache->dynamicSourceContextInfoMap);
+        Assert(this->Cache()->sourceContextInfoMap ||
+            this->Cache()->dynamicSourceContextInfoMap);
 
         uint nextSourceContextId = 0;
 
-        if (this->cache->sourceContextInfoMap)
+        if (this->Cache()->sourceContextInfoMap)
         {
-            nextSourceContextId = this->cache->sourceContextInfoMap->Count();
+            nextSourceContextId = this->Cache()->sourceContextInfoMap->Count();
         }
 
-        if (this->cache->dynamicSourceContextInfoMap)
+        if (this->Cache()->dynamicSourceContextInfoMap)
         {
-            nextSourceContextId += this->cache->dynamicSourceContextInfoMap->Count();
+            nextSourceContextId += this->Cache()->dynamicSourceContextInfoMap->Count();
         }
 
         return nextSourceContextId + 1;
@@ -575,8 +573,6 @@ namespace Js
     // Do most of the Close() work except the final release which could delete the scriptContext.
     void ScriptContext::InternalClose()
     {
-        this->PrintStats();
-
         isScriptContextActuallyClosed = true;
 
         PERF_COUNTER_DEC(Basic, ScriptContextActive);
@@ -604,19 +600,6 @@ namespace Js
         }
 #endif
 
-#if ENABLE_PROFILE_INFO
-        HRESULT hr = S_OK;
-        BEGIN_TRANSLATE_OOM_TO_HRESULT_NESTED
-        {
-            DynamicProfileInfo::Save(this);
-        }
-        END_TRANSLATE_OOM_TO_HRESULT(hr);
-
-#if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION)
-        this->ClearDynamicProfileList();
-#endif
-#endif
-
 #if ENABLE_NATIVE_CODEGEN
         if (nativeCodeGen != nullptr)
         {
@@ -625,12 +608,6 @@ namespace Js
         }
 #endif
 
-        if (this->fakeGlobalFuncForUndefer)
-        {
-            this->fakeGlobalFuncForUndefer->Cleanup(true);
-            this->fakeGlobalFuncForUndefer.Unroot(this->GetRecycler());
-        }
-
         if (this->sourceList)
         {
             bool hasFunctions = false;
@@ -657,8 +634,6 @@ namespace Js
             }
         }
 
-        JS_ETW(EtwTrace::LogSourceUnloadEvents(this));
-
         this->GetThreadContext()->SubSourceSize(this->GetSourceSize());
 
 #if DYNAMIC_INTERPRETER_THUNK
@@ -680,14 +655,12 @@ namespace Js
         DeRegisterProfileProbe(S_OK, nullptr);
 #endif
 
-        if (this->diagnosticArena != nullptr)
-        {
-            HeapDelete(this->diagnosticArena);
-            this->diagnosticArena = nullptr;
-        }
-
+        this->EnsureClearDebugDocument();
         if (this->debugContext != nullptr)
         {
+            this->debugContext->GetProbeContainer()->UninstallInlineBreakpointProbe(NULL);
+            this->debugContext->GetProbeContainer()->UninstallDebuggerScriptOptionCallback();
+
             // Guard the closing and deleting of DebugContext as in meantime PDM might
             // call OnBreakFlagChange
             AutoCriticalSection autoDebugContextCloseCS(&debugContextCloseCS);
@@ -697,6 +670,12 @@ namespace Js
             HeapDelete(tempDebugContext);
         }
 
+        if (this->diagnosticArena != nullptr)
+        {
+            HeapDelete(this->diagnosticArena);
+            this->diagnosticArena = nullptr;
+        }
+
         // Need to print this out before the native code gen is deleted
         // which will delete the codegenProfiler
 
@@ -743,7 +722,6 @@ namespace Js
             ReleaseGuestArena();
             guestArena = nullptr;
         }
-        cache = nullptr;
 
         builtInLibraryFunctions = nullptr;
 
@@ -752,14 +730,6 @@ namespace Js
         isWeakReferenceDictionaryListCleared = true;
         this->weakReferenceDictionaryList.Clear(this->GeneralAllocator());
 
-        // This can be null if the script context initialization threw
-        // and InternalClose gets called in the destructor code path
-        if (javascriptLibrary != nullptr)
-        {
-            javascriptLibrary->CleanupForClose();
-            javascriptLibrary->Uninitialize();
-        }
-
         if (registeredPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext != nullptr)
         {
             // UnregisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext may throw, set up the correct state first
@@ -768,7 +738,16 @@ namespace Js
             Assert(registeredPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext == nullptr);
             threadContext->UnregisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext(registeredScriptContext);
         }
+
         threadContext->ReleaseDebugManager();
+
+        // This can be null if the script context initialization threw
+        // and InternalClose gets called in the destructor code path
+        if (javascriptLibrary != nullptr)
+        {
+            javascriptLibrary->CleanupForClose();
+            javascriptLibrary->Uninitialize();
+        }
     }
 
     bool ScriptContext::Close(bool inDestructor)
@@ -794,6 +773,7 @@ namespace Js
             {
                 GetRecycler()->RootRelease(globalObject);
             }
+            globalObject = nullptr;
         }
 
         // A script context closing is a signal to the thread context that it
@@ -1133,10 +1113,9 @@ namespace Js
     RegexPatternMruMap* ScriptContext::GetDynamicRegexMap() const
     {
         Assert(!isScriptContextActuallyClosed);
-        Assert(cache);
-        Assert(cache->dynamicRegexMap);
+        Assert(Cache()->dynamicRegexMap);
 
-        return cache->dynamicRegexMap;
+        return Cache()->dynamicRegexMap;
     }
 
     void ScriptContext::SetTrigramAlphabet(UnifiedRegex::TrigramAlphabet * trigramAlphabet)
@@ -1209,10 +1188,17 @@ namespace Js
 
     void ScriptContext::InitializeCache()
     {
-        this->cache = RecyclerNewFinalized(recycler, Cache);
-        this->javascriptLibrary->scriptContextCache = this->cache;
 
-        this->cache->dynamicRegexMap =
+#if ENABLE_PROFILE_INFO
+#if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION)
+        if (DynamicProfileInfo::NeedProfileInfoList())
+        {
+            this->Cache()->profileInfoList = RecyclerNew(this->GetRecycler(), DynamicProfileInfoList);
+        }
+#endif
+#endif
+
+        this->Cache()->dynamicRegexMap =
             RegexPatternMruMap::New(
                 recycler,
                 REGEX_CONFIG_FLAG(DynamicRegexMruListSize) <= 0 ? 16 : REGEX_CONFIG_FLAG(DynamicRegexMruListSize));
@@ -1221,25 +1207,17 @@ namespace Js
         sourceContextInfo->dwHostSourceContext = Js::Constants::NoHostSourceContext;
         sourceContextInfo->isHostDynamicDocument = false;
         sourceContextInfo->sourceContextId = Js::Constants::NoSourceContext;
-        this->cache->noContextSourceContextInfo = sourceContextInfo;
+        this->Cache()->noContextSourceContextInfo = sourceContextInfo;
 
         SRCINFO* srcInfo = RecyclerNewStructZ(this->GetRecycler(), SRCINFO);
-        srcInfo->sourceContextInfo = this->cache->noContextSourceContextInfo;
+        srcInfo->sourceContextInfo = this->Cache()->noContextSourceContextInfo;
         srcInfo->moduleID = kmodGlobal;
-        this->cache->noContextGlobalSourceInfo = srcInfo;
+        this->Cache()->noContextGlobalSourceInfo = srcInfo;
     }
 
     void ScriptContext::InitializePreGlobal()
     {
         this->guestArena = this->GetRecycler()->CreateGuestArena(_u("Guest"), Throw::OutOfMemory);
-#if ENABLE_PROFILE_INFO
-#if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION)
-        if (DynamicProfileInfo::NeedProfileInfoList())
-        {
-            this->profileInfoList.Root(RecyclerNew(this->GetRecycler(), DynamicProfileInfoList), recycler);
-        }
-#endif
-#endif
 
 #if ENABLE_BACKGROUND_PARSING
         if (PHASE_ON1(Js::ParallelParsePhase))
@@ -1334,6 +1312,11 @@ namespace Js
 #endif
     void ScriptContext::MarkForClose()
     {
+        if (IsClosed()) 
+        {
+            return;
+        }
+
         SaveStartupProfileAndRelease(true);
         SetIsClosed();
 
@@ -1359,12 +1342,54 @@ namespace Js
         if (!this->isClosed)
         {
             this->isClosed = true;
+
+            if (this->javascriptLibrary)
+            {
+                JS_ETW(EtwTrace::LogSourceUnloadEvents(this));
+
+#if ENABLE_PROFILE_INFO
+#if DBG_DUMP
+                DynamicProfileInfo::DumpScriptContext(this);
+#endif
+#ifdef RUNTIME_DATA_COLLECTION
+                DynamicProfileInfo::DumpScriptContextToFile(this);
+#endif
+#endif
+
+#if ENABLE_PROFILE_INFO
+#ifdef DYNAMIC_PROFILE_STORAGE
+                HRESULT hr = S_OK;
+                BEGIN_TRANSLATE_OOM_TO_HRESULT_NESTED
+                {
+                    DynamicProfileInfo::Save(this);
+                }
+                END_TRANSLATE_OOM_TO_HRESULT(hr);
+
+                if (this->Cache()->sourceContextInfoMap)
+                {
+                    this->Cache()->sourceContextInfoMap->Map([&](DWORD_PTR dwHostSourceContext, SourceContextInfo * sourceContextInfo)
+                    {
+                        if (sourceContextInfo->sourceDynamicProfileManager)
+                        {
+                            sourceContextInfo->sourceDynamicProfileManager->ClearSavingData();
+                        }
+                    });
+                }
+#endif
+
+#if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION)
+                this->ClearDynamicProfileList();
+#endif
+#endif
+            }
+
 #if ENABLE_NATIVE_CODEGEN
             if (m_remoteScriptContextAddr)
             {
                 JITManager::GetJITManager()->CloseScriptContext(m_remoteScriptContextAddr);
             }
 #endif
+            this->PrintStats();
         }
     }
 
@@ -1739,7 +1764,7 @@ namespace Js
     {
         if (pSrcInfo == nullptr)
         {
-            pSrcInfo = this->cache->noContextGlobalSourceInfo;
+            pSrcInfo = this->Cache()->noContextGlobalSourceInfo;
         }
 
         LPUTF8 utf8Script = nullptr;
@@ -2215,7 +2240,7 @@ namespace Js
 
     bool ScriptContext::IsInEvalMap(FastEvalMapString const& key, BOOL isIndirect, ScriptFunction **ppFuncScript)
     {
-        EvalCacheDictionary *dict = isIndirect ? this->cache->indirectEvalCacheDictionary : this->cache->evalCacheDictionary;
+        EvalCacheDictionary *dict = isIndirect ? this->Cache()->indirectEvalCacheDictionary : this->Cache()->evalCacheDictionary;
         if (dict == nullptr)
         {
             return false;
@@ -2253,18 +2278,18 @@ namespace Js
 
     void ScriptContext::AddToEvalMap(FastEvalMapString const& key, BOOL isIndirect, ScriptFunction *pFuncScript)
     {
-        EvalCacheDictionary *dict = isIndirect ? this->cache->indirectEvalCacheDictionary : this->cache->evalCacheDictionary;
+        EvalCacheDictionary *dict = isIndirect ? this->Cache()->indirectEvalCacheDictionary : this->Cache()->evalCacheDictionary;
         if (dict == nullptr)
         {
             EvalCacheTopLevelDictionary* evalTopDictionary = RecyclerNew(this->recycler, EvalCacheTopLevelDictionary, this->recycler);
             dict = RecyclerNew(this->recycler, EvalCacheDictionary, evalTopDictionary, recycler);
             if (isIndirect)
             {
-                this->cache->indirectEvalCacheDictionary = dict;
+                this->Cache()->indirectEvalCacheDictionary = dict;
             }
             else
             {
-                this->cache->evalCacheDictionary = dict;
+                this->Cache()->evalCacheDictionary = dict;
             }
         }
 
@@ -2273,19 +2298,19 @@ namespace Js
 
     bool ScriptContext::IsInNewFunctionMap(EvalMapString const& key, FunctionInfo **ppFuncInfo)
     {
-        if (this->cache->newFunctionCache == nullptr)
+        if (this->Cache()->newFunctionCache == nullptr)
         {
             return false;
         }
 
         // If eval map cleanup is false, to preserve existing behavior, add it to the eval map MRU list
-        bool success = this->cache->newFunctionCache->TryGetValue(key, ppFuncInfo);
+        bool success = this->Cache()->newFunctionCache->TryGetValue(key, ppFuncInfo);
         if (success)
         {
-            this->cache->newFunctionCache->NotifyAdd(key);
+            this->Cache()->newFunctionCache->NotifyAdd(key);
 #ifdef VERBOSE_EVAL_MAP
 #if DBG
-            this->cache->newFunctionCache->DumpKeepAlives();
+            this->Cache()->newFunctionCache->DumpKeepAlives();
 #endif
 #endif
         }
@@ -2295,34 +2320,34 @@ namespace Js
 
     void ScriptContext::AddToNewFunctionMap(EvalMapString const& key, FunctionInfo *pFuncInfo)
     {
-        if (this->cache->newFunctionCache == nullptr)
+        if (this->Cache()->newFunctionCache == nullptr)
         {
-            this->cache->newFunctionCache = RecyclerNew(this->recycler, NewFunctionCache, this->recycler);
+            this->Cache()->newFunctionCache = RecyclerNew(this->recycler, NewFunctionCache, this->recycler);
         }
-        this->cache->newFunctionCache->Add(key, pFuncInfo);
+        this->Cache()->newFunctionCache->Add(key, pFuncInfo);
     }
 
 
     void ScriptContext::EnsureSourceContextInfoMap()
     {
-        if (this->cache->sourceContextInfoMap == nullptr)
+        if (this->Cache()->sourceContextInfoMap == nullptr)
         {
-            this->cache->sourceContextInfoMap = RecyclerNew(this->GetRecycler(), SourceContextInfoMap, this->GetRecycler());
+            this->Cache()->sourceContextInfoMap = RecyclerNew(this->GetRecycler(), SourceContextInfoMap, this->GetRecycler());
         }
     }
 
     void ScriptContext::EnsureDynamicSourceContextInfoMap()
     {
-        if (this->cache->dynamicSourceContextInfoMap == nullptr)
+        if (this->Cache()->dynamicSourceContextInfoMap == nullptr)
         {
-            this->cache->dynamicSourceContextInfoMap = RecyclerNew(this->GetRecycler(), DynamicSourceContextInfoMap, this->GetRecycler());
+            this->Cache()->dynamicSourceContextInfoMap = RecyclerNew(this->GetRecycler(), DynamicSourceContextInfoMap, this->GetRecycler());
         }
     }
 
     SourceContextInfo* ScriptContext::GetSourceContextInfo(uint hash)
     {
         SourceContextInfo * sourceContextInfo;
-        if (this->cache->dynamicSourceContextInfoMap && this->cache->dynamicSourceContextInfoMap->TryGetValue(hash, &sourceContextInfo))
+        if (this->Cache()->dynamicSourceContextInfoMap && this->Cache()->dynamicSourceContextInfoMap->TryGetValue(hash, &sourceContextInfo))
         {
             return sourceContextInfo;
         }
@@ -2334,13 +2359,13 @@ namespace Js
         EnsureDynamicSourceContextInfoMap();
         if (this->GetSourceContextInfo(hash) != nullptr)
         {
-            return this->cache->noContextSourceContextInfo;
+            return this->Cache()->noContextSourceContextInfo;
         }
 
-        if (this->cache->dynamicSourceContextInfoMap->Count() > INMEMORY_CACHE_MAX_PROFILE_MANAGER)
+        if (this->Cache()->dynamicSourceContextInfoMap->Count() > INMEMORY_CACHE_MAX_PROFILE_MANAGER)
         {
             OUTPUT_TRACE(Js::DynamicProfilePhase, _u("Max of dynamic script profile info reached.\n"));
-            return this->cache->noContextSourceContextInfo;
+            return this->Cache()->noContextSourceContextInfo;
         }
 
         // This is capped so we can continue allocating in the arena
@@ -2356,7 +2381,7 @@ namespace Js
         // For the host provided dynamic code (if hostSourceContext is not NoHostSourceContext), do not add to dynamicSourceContextInfoMap
         if (hostSourceContext == Js::Constants::NoHostSourceContext)
         {
-            this->cache->dynamicSourceContextInfoMap->Add(hash, sourceContextInfo);
+            this->Cache()->dynamicSourceContextInfoMap->Add(hash, sourceContextInfo);
         }
         return sourceContextInfo;
     }
@@ -2397,7 +2422,7 @@ namespace Js
             Assert(sourceContextInfo->sourceDynamicProfileManager != NULL);
         }
 
-        this->cache->sourceContextInfoMap->Add(sourceContext, sourceContextInfo);
+        this->Cache()->sourceContextInfoMap->Add(sourceContext, sourceContextInfo);
 #endif
         return sourceContextInfo;
     }
@@ -2416,13 +2441,13 @@ namespace Js
     {
         if (sourceContext == Js::Constants::NoHostSourceContext)
         {
-            return this->cache->noContextSourceContextInfo;
+            return this->Cache()->noContextSourceContextInfo;
         }
 
         // We only init sourceContextInfoMap, don't need to lock.
         EnsureSourceContextInfoMap();
         SourceContextInfo * sourceContextInfo;
-        if (this->cache->sourceContextInfoMap->TryGetValue(sourceContext, &sourceContextInfo))
+        if (this->Cache()->sourceContextInfoMap->TryGetValue(sourceContext, &sourceContextInfo))
         {
 #if ENABLE_PROFILE_INFO
             if (profileDataCache &&
@@ -2450,19 +2475,19 @@ namespace Js
                 uint newCount = moduleID + 4;  // Preallocate 4 more slots, moduleID don't usually grow much
 
                 Field(SRCINFO const *)* newModuleSrcInfo = RecyclerNewArrayZ(this->GetRecycler(), Field(SRCINFO const*), newCount);
-                CopyArray(newModuleSrcInfo, newCount, cache->moduleSrcInfo, moduleSrcInfoCount);
-                cache->moduleSrcInfo = newModuleSrcInfo;
+                CopyArray(newModuleSrcInfo, newCount, Cache()->moduleSrcInfo, moduleSrcInfoCount);
+                Cache()->moduleSrcInfo = newModuleSrcInfo;
                 moduleSrcInfoCount = newCount;
-                cache->moduleSrcInfo[0] = this->cache->noContextGlobalSourceInfo;
+                Cache()->moduleSrcInfo[0] = this->Cache()->noContextGlobalSourceInfo;
             }
 
-            SRCINFO const * si = cache->moduleSrcInfo[moduleID];
+            SRCINFO const * si = Cache()->moduleSrcInfo[moduleID];
             if (si == nullptr)
             {
                 SRCINFO * newSrcInfo = RecyclerNewStructZ(this->GetRecycler(), SRCINFO);
-                newSrcInfo->sourceContextInfo = this->cache->noContextSourceContextInfo;
+                newSrcInfo->sourceContextInfo = this->Cache()->noContextSourceContextInfo;
                 newSrcInfo->moduleID = moduleID;
-                cache->moduleSrcInfo[moduleID] = newSrcInfo;
+                Cache()->moduleSrcInfo[moduleID] = newSrcInfo;
                 si = newSrcInfo;
             }
             return si;
@@ -3177,9 +3202,9 @@ namespace Js
 #if ENABLE_PROFILE_INFO
 #if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION)
         // Reset the dynamic profile list
-        if (this->profileInfoList)
+        if (this->Cache()->profileInfoList)
         {
-            this->profileInfoList->Reset();
+            this->Cache()->profileInfoList->Reset();
         }
 #endif
 #endif
@@ -4357,7 +4382,6 @@ namespace Js
             return;
         }
         Assert(this->guestArena);
-        Assert(this->cache);
 
         if (EnableEvalMapCleanup())
         {
@@ -4365,17 +4389,17 @@ namespace Js
             // Also, don't clean the eval map if the debugger is attached
             if (!this->IsScriptContextInDebugMode())
             {
-                if (this->cache->evalCacheDictionary != nullptr)
+                if (this->Cache()->evalCacheDictionary != nullptr)
                 {
-                    this->CleanDynamicFunctionCache<Js::EvalCacheTopLevelDictionary>(this->cache->evalCacheDictionary->GetDictionary());
+                    this->CleanDynamicFunctionCache<Js::EvalCacheTopLevelDictionary>(this->Cache()->evalCacheDictionary->GetDictionary());
                 }
-                if (this->cache->indirectEvalCacheDictionary != nullptr)
+                if (this->Cache()->indirectEvalCacheDictionary != nullptr)
                 {
-                    this->CleanDynamicFunctionCache<Js::EvalCacheTopLevelDictionary>(this->cache->indirectEvalCacheDictionary->GetDictionary());
+                    this->CleanDynamicFunctionCache<Js::EvalCacheTopLevelDictionary>(this->Cache()->indirectEvalCacheDictionary->GetDictionary());
                 }
-                if (this->cache->newFunctionCache != nullptr)
+                if (this->Cache()->newFunctionCache != nullptr)
                 {
-                    this->CleanDynamicFunctionCache<Js::NewFunctionCache>(this->cache->newFunctionCache);
+                    this->CleanDynamicFunctionCache<Js::NewFunctionCache>(this->Cache()->newFunctionCache);
                 }
                 if (this->hostScriptContext != nullptr)
                 {
@@ -4482,7 +4506,7 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie
     {
         if (value == lastNumberToStringRadix10)
         {
-            return cache->lastNumberToStringRadix10String;
+            return Cache()->lastNumberToStringRadix10String;
         }
         return nullptr;
     }
@@ -4491,16 +4515,16 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie
         ScriptContext::SetLastNumberToStringRadix10(double value, JavascriptString * str)
     {
             lastNumberToStringRadix10 = value;
-            cache->lastNumberToStringRadix10String = str;
+            Cache()->lastNumberToStringRadix10String = str;
     }
 
     bool ScriptContext::GetLastUtcTimeFromStr(JavascriptString * str, double& dbl)
     {
         Assert(str != nullptr);
-        if (str != cache->lastUtcTimeFromStrString)
+        if (str != Cache()->lastUtcTimeFromStrString)
         {
-            if (cache->lastUtcTimeFromStrString == nullptr
-                || !JavascriptString::Equals(str, cache->lastUtcTimeFromStrString))
+            if (Cache()->lastUtcTimeFromStrString == nullptr
+                || !JavascriptString::Equals(str, Cache()->lastUtcTimeFromStrString))
             {
                 return false;
             }
@@ -4513,7 +4537,7 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie
     ScriptContext::SetLastUtcTimeFromStr(JavascriptString * str, double value)
     {
             lastUtcTimeFromStr = value;
-            cache->lastUtcTimeFromStrString = str;
+            Cache()->lastUtcTimeFromStrString = str;
     }
 
 #if ENABLE_NATIVE_CODEGEN
@@ -4588,7 +4612,7 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie
                 Assert(this->recycler);
 
                 builtInLibraryFunctions = RecyclerNew(this->recycler, BuiltInLibraryFunctionMap, this->recycler);
-                cache->builtInLibraryFunctions = builtInLibraryFunctions;
+                Cache()->builtInLibraryFunctions = builtInLibraryFunctions;
             }
 
             builtInLibraryFunctions->Item(entryPoint, function);
@@ -4947,9 +4971,10 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie
             }
             Assert(functionBody->GetInterpretedCount() == 0);
 #if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION)
-            if (profileInfoList)
+
+            if (this->Cache()->profileInfoList)
             {
-                profileInfoList->Prepend(this->GetRecycler(), newDynamicProfileInfo);
+                this->Cache()->profileInfoList->Prepend(this->GetRecycler(), newDynamicProfileInfo);
             }
 #endif
             if (!startupComplete)
@@ -4982,10 +5007,10 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie
         {
             return;
         }
-        if (!startupComplete && this->cache->sourceContextInfoMap)
+        if (!startupComplete && this->Cache()->sourceContextInfoMap)
         {
 #if ENABLE_PROFILE_INFO
-            this->cache->sourceContextInfoMap->Map([&](DWORD_PTR dwHostSourceContext, SourceContextInfo* info)
+            this->Cache()->sourceContextInfoMap->Map([&](DWORD_PTR dwHostSourceContext, SourceContextInfo* info)
             {
                 Assert(info->sourceDynamicProfileManager);
                 uint bytesWritten = info->sourceDynamicProfileManager->SaveToProfileCacheAndRelease(info);
@@ -5000,16 +5025,6 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie
         startupComplete = true;
     }
 
-    void ScriptContextBase::ClearGlobalObject()
-    {
-#if ENABLE_NATIVE_CODEGEN
-        ScriptContext* scriptContext = static_cast<ScriptContext*>(this);
-        Assert(scriptContext->IsClosedNativeCodeGenerator());
-#endif
-        globalObject = nullptr;
-        javascriptLibrary = nullptr;
-    }
-
     void ScriptContext::SetFastDOMenabled()
     {
         fastDOMenabled = true; Assert(globalObject->GetDirectHostObject() != NULL);
@@ -5090,14 +5105,7 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie
 
     void ScriptContext::PrintStats()
     {
-#if ENABLE_PROFILE_INFO
-#if DBG_DUMP
-        DynamicProfileInfo::DumpScriptContext(this);
-#endif
-#ifdef RUNTIME_DATA_COLLECTION
-        DynamicProfileInfo::DumpScriptContextToFile(this);
-#endif
-#endif
+
 #ifdef PROFILE_TYPES
         if (Configuration::Global.flags.ProfileTypes)
         {

+ 10 - 73
lib/Runtime/Base/ScriptContext.h

@@ -355,18 +355,6 @@ namespace Js
 #endif
     };
 
-    static const unsigned int EvalMRUSize = 15;
-    typedef JsUtil::BaseDictionary<DWORD_PTR, SourceContextInfo *, Recycler, PowerOf2SizePolicy> SourceContextInfoMap;
-    typedef JsUtil::BaseDictionary<uint, SourceContextInfo *, Recycler, PowerOf2SizePolicy> DynamicSourceContextInfoMap;
-
-    typedef JsUtil::BaseDictionary<EvalMapString, ScriptFunction*, RecyclerNonLeafAllocator, PrimeSizePolicy> SecondLevelEvalCache;
-    typedef TwoLevelHashRecord<FastEvalMapString, ScriptFunction*, SecondLevelEvalCache, EvalMapString> EvalMapRecord;
-    typedef JsUtil::Cache<FastEvalMapString, EvalMapRecord*, RecyclerNonLeafAllocator, PrimeSizePolicy, JsUtil::MRURetentionPolicy<FastEvalMapString, EvalMRUSize>, FastEvalMapStringComparer> EvalCacheTopLevelDictionary;
-    typedef JsUtil::Cache<EvalMapString, FunctionInfo*, RecyclerNonLeafAllocator, PrimeSizePolicy, JsUtil::MRURetentionPolicy<EvalMapString, EvalMRUSize>> NewFunctionCache;
-    typedef JsUtil::BaseDictionary<ParseableFunctionInfo*, ParseableFunctionInfo*, Recycler, PrimeSizePolicy, RecyclerPointerComparer> ParseableFunctionInfoMap;
-    // This is the dictionary used by script context to cache the eval.
-    typedef TwoLevelHashDictionary<FastEvalMapString, ScriptFunction*, EvalMapRecord, EvalCacheTopLevelDictionary, EvalMapString> EvalCacheDictionary;
-
     struct PropertyStringMap
     {
         PropertyString* strLen2[80];
@@ -378,41 +366,6 @@ namespace Js
         }
     };
 
-    // valid if object!= NULL
-    struct EnumeratedObjectCache {
-        static const int kMaxCachedPropStrings=16;
-        Field(DynamicObject*) object;
-        Field(DynamicType*) type;
-        Field(PropertyString*) propertyStrings[kMaxCachedPropStrings];
-        Field(int) validPropStrings;
-    };
-
-    typedef JsUtil::BaseDictionary<JavascriptMethod, JavascriptFunction*, Recycler, PowerOf2SizePolicy> BuiltInLibraryFunctionMap;
-
-    // this is allocated in GC directly to avoid force pinning the object, it is linked from JavascriptLibrary such that it has
-    // the same lifetime as JavascriptLibrary, and it can be collected without ScriptContext Close.
-    // Allocate it as Finalizable such that it will be still available during JavascriptLibrary Dispose time
-    class Cache : public FinalizableObject
-    {
-    public:
-        virtual void Finalize(bool isShutdown) override {}
-        virtual void Dispose(bool isShutdown) override {}
-        virtual void Mark(Recycler *recycler) override { AssertMsg(false, "Mark called on object that isn't TrackableObject"); }
-
-        Field(JavascriptString *) lastNumberToStringRadix10String;
-        Field(EnumeratedObjectCache) enumObjCache;
-        Field(JavascriptString *) lastUtcTimeFromStrString;
-        Field(EvalCacheDictionary*) evalCacheDictionary;
-        Field(EvalCacheDictionary*) indirectEvalCacheDictionary;
-        Field(NewFunctionCache*) newFunctionCache;
-        Field(RegexPatternMruMap *) dynamicRegexMap;
-        Field(SourceContextInfoMap*) sourceContextInfoMap;   // maps host provided context cookie to the URL of the script buffer passed.
-        Field(DynamicSourceContextInfoMap*) dynamicSourceContextInfoMap;
-        Field(SourceContextInfo*) noContextSourceContextInfo;
-        Field(SRCINFO*) noContextGlobalSourceInfo;
-        Field(Field(SRCINFO const *)*) moduleSrcInfo;
-        Field(BuiltInLibraryFunctionMap*) builtInLibraryFunctions;
-    };
 
     /*
     * This class caches jitted func address ranges.
@@ -575,9 +528,6 @@ namespace Js
         InlineCache * GetValueOfInlineCache() const { return valueOfInlineCache;}
         InlineCache * GetToStringInlineCache() const { return toStringInlineCache; }
 
-        FunctionBody * GetFakeGlobalFuncForUndefer() const { return fakeGlobalFuncForUndefer; }
-        void SetFakeGlobalFuncForUndefer(FunctionBody * func) { fakeGlobalFuncForUndefer.Root(func, GetRecycler()); }
-
     private:
         PropertyStringMap* propertyStrings[80];
 
@@ -647,7 +597,6 @@ namespace Js
         StringProfiler* stringProfiler;
 #endif
 
-        RecyclerRootPtr<FunctionBody> fakeGlobalFuncForUndefer;
 
 public:
 #ifdef PROFILE_TYPES
@@ -914,11 +863,6 @@ private:
         BOOL m_inProfileCallback;
 #endif // ENABLE_SCRIPT_PROFILING
 
-#if ENABLE_PROFILE_INFO
-#if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION)
-        RecyclerRootPtr<DynamicProfileInfoList> profileInfoList;
-#endif
-#endif
         // List of weak reference dictionaries. We'll walk through them
         // and clean them up post-collection
         // IWeakReferenceDictionary objects are added to this list by calling
@@ -1030,9 +974,6 @@ private:
 
         uint callCount;
 
-        // Guest arena allocated cache holding references that need to be pinned.
-        Cache* cache;
-
         // if the current context is for webworker
         DWORD webWorkerId;
 
@@ -1070,6 +1011,7 @@ private:
         void InitializeGlobalObject();
         bool IsIntlEnabled();
         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;}
@@ -1118,14 +1060,9 @@ private:
 #if defined(LEAK_REPORT) || defined(CHECK_MEMORY_LEAK)
         void ClearSourceContextInfoMaps()
         {
-          if (this->cache != nullptr)
-          {
-              this->cache->sourceContextInfoMap = nullptr;
-              this->cache->dynamicSourceContextInfoMap = nullptr;
 #if ENABLE_PROFILE_INFO
               this->referencesSharedDynamicSourceContextInfo = false;
 #endif
-          }
         }
 #endif
 
@@ -1133,25 +1070,25 @@ private:
 #if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION)
         void ClearDynamicProfileList()
         {
-            if (profileInfoList)
+            if (this->Cache()->profileInfoList)
             {
-                profileInfoList->Reset();
-                profileInfoList.Unroot(this->recycler);
+                this->Cache()->profileInfoList->Reset();
+                this->Cache()->profileInfoList = nullptr;
             }
         }
 
-        DynamicProfileInfoList * GetProfileInfoList() { return profileInfoList; }
+        DynamicProfileInfoList * GetProfileInfoList() { return this->Cache()->profileInfoList; }
 #endif
 #endif
 
         SRCINFO const * GetModuleSrcInfo(Js::ModuleID moduleID);
         SourceContextInfoMap* GetSourceContextInfoMap()
         {
-            return (this->cache ? this->cache->sourceContextInfoMap : nullptr);
+            return this->Cache()->sourceContextInfoMap;
         }
         DynamicSourceContextInfoMap* GetDynamicSourceContextInfoMap()
         {
-            return (this->cache ? this->cache->dynamicSourceContextInfoMap : nullptr);
+            return this->Cache()->dynamicSourceContextInfoMap;
         }
 
 #if ENABLE_TTD
@@ -1356,7 +1293,7 @@ private:
         void DisposeScriptContextByFaultInjection();
         void SetDisposeDisposeByFaultInjectionEventHandler(EventHandler eventHandler);
 #endif
-        EnumeratedObjectCache* GetEnumeratedObjectCache() { return &(cache->enumObjCache); }
+        EnumeratedObjectCache* GetEnumeratedObjectCache() { return &(this->Cache()->enumObjCache); }
         PropertyString* TryGetPropertyString(PropertyId propertyId);
         PropertyString* GetPropertyString(PropertyId propertyId);
         void InvalidatePropertyStringCache(PropertyId propertyId, Type* type);
@@ -1504,7 +1441,7 @@ private:
         void SetLastUtcTimeFromStr(JavascriptString * str, double value);
         bool IsNoContextSourceContextInfo(SourceContextInfo *sourceContextInfo) const
         {
-            return sourceContextInfo == cache->noContextSourceContextInfo;
+            return sourceContextInfo == this->Cache()->noContextSourceContextInfo;
         }
 
         BOOL IsProfiling()
@@ -1526,7 +1463,7 @@ private:
         }
 
 #if DBG
-        SourceContextInfo const * GetNoContextSourceContextInfo() const { return cache->noContextSourceContextInfo; }
+        SourceContextInfo const * GetNoContextSourceContextInfo() const { return this->Cache()->noContextSourceContextInfo; }
 
 #ifdef ENABLE_SCRIPT_PROFILING
         int GetProfileSession()

+ 0 - 1
lib/Runtime/Base/ScriptContextBase.h

@@ -26,7 +26,6 @@ namespace Js
             pActiveScriptDirect(nullptr) {}
         JavascriptLibrary* GetLibrary() const { return javascriptLibrary; }
         void SetLibrary(JavascriptLibrary* library) { javascriptLibrary = library;}
-        void ClearGlobalObject();
         void SetGlobalObject(GlobalObject *globalObject);
         GlobalObject* GetGlobalObject() const { return globalObject; }
         IActiveScriptDirect* GetActiveScriptDirect()

+ 2 - 1
lib/Runtime/Base/ThreadContext.cpp

@@ -591,7 +591,8 @@ ThreadContext::~ThreadContext()
 void
 ThreadContext::SetJSRTRuntime(void* runtime)
 {
-    Assert(jsrtRuntime == nullptr); jsrtRuntime = runtime;
+    Assert(jsrtRuntime == nullptr);
+    jsrtRuntime = runtime;
 #ifdef ENABLE_BASIC_TELEMETRY
     Telemetry::EnsureInitializeForJSRT();
 #endif

+ 3 - 3
lib/Runtime/ByteCode/ByteCodeGenerator.cpp

@@ -2119,7 +2119,7 @@ void ByteCodeGenerator::Begin(
         this->propertyRecords = nullptr;
     }
 
-    Js::FunctionBody *fakeGlobalFunc = scriptContext->GetFakeGlobalFuncForUndefer();
+    Js::FunctionBody *fakeGlobalFunc = scriptContext->GetLibrary()->GetFakeGlobalFuncForUndefer();
     if (fakeGlobalFunc)
     {
         fakeGlobalFunc->ClearBoundPropertyRecords();
@@ -5509,11 +5509,11 @@ bool ByteCodeGenerator::NeedScopeObjectForArguments(FuncInfo *funcInfo, ParseNod
 
 Js::FunctionBody *ByteCodeGenerator::EnsureFakeGlobalFuncForUndefer(ParseNode *pnode)
 {
-    Js::FunctionBody *func = scriptContext->GetFakeGlobalFuncForUndefer();
+    Js::FunctionBody *func = scriptContext->GetLibrary()->GetFakeGlobalFuncForUndefer();
     if (!func)
     {
         func = this->MakeGlobalFunctionBody(pnode);
-        scriptContext->SetFakeGlobalFuncForUndefer(func);
+        scriptContext->GetLibrary()->SetFakeGlobalFuncForUndefer(func);
     }
     else
     {

+ 16 - 10
lib/Runtime/Language/SourceDynamicProfileManager.cpp

@@ -45,12 +45,6 @@ namespace Js
         return nullptr;
     }
 
-    void
-    SourceDynamicProfileManager::Reset(uint numberOfFunctions)
-    {
-        dynamicProfileInfoMap.Clear();
-    }
-
     void SourceDynamicProfileManager::UpdateDynamicProfileInfo(LocalFunctionId functionId, DynamicProfileInfo * dynamicProfileInfo)
     {
         Assert(dynamicProfileInfo != nullptr);
@@ -330,12 +324,24 @@ namespace Js
     }
 
 #ifdef DYNAMIC_PROFILE_STORAGE
+    void SourceDynamicProfileManager::ClearSavingData()
+    {
+        dynamicProfileInfoMapSaving.Reset();
+    }
+
+    void SourceDynamicProfileManager::CopySavingData()
+    {
+        dynamicProfileInfoMap.Map([&](LocalFunctionId functionId, DynamicProfileInfo *info)
+        {
+            dynamicProfileInfoMapSaving.Item(functionId, info);
+        });
+    }
 
     void
     SourceDynamicProfileManager::SaveDynamicProfileInfo(LocalFunctionId functionId, DynamicProfileInfo * dynamicProfileInfo)
     {
         Assert(dynamicProfileInfo->GetFunctionBody()->HasExecutionDynamicProfileInfo());
-        dynamicProfileInfoMap.Item(functionId, dynamicProfileInfo);
+        dynamicProfileInfoMapSaving.Item(functionId, dynamicProfileInfo);
     }
 
     template <typename T>
@@ -416,15 +422,15 @@ namespace Js
 
             size_t bvSize = BVFixed::GetAllocSize(this->startupFunctions->Length()) ;
             if (!writer->WriteArray((char *)static_cast<BVFixed*>(this->startupFunctions), bvSize)
-                || !writer->Write(this->dynamicProfileInfoMap.Count()))
+                || !writer->Write(this->dynamicProfileInfoMapSaving.Count()))
             {
                 return false;
             }
         }
 
-        for (int i = 0; i < this->dynamicProfileInfoMap.Count(); i++)
+        for (int i = 0; i < this->dynamicProfileInfoMapSaving.Count(); i++)
         {
-            DynamicProfileInfo * dynamicProfileInfo = this->dynamicProfileInfoMap.GetValueAt(i);
+            DynamicProfileInfo * dynamicProfileInfo = this->dynamicProfileInfoMapSaving.GetValueAt(i);
             if (dynamicProfileInfo == nullptr || !dynamicProfileInfo->HasFunctionBody())
             {
                 continue;

+ 15 - 4
lib/Runtime/Language/SourceDynamicProfileManager.h

@@ -21,7 +21,13 @@ namespace Js
     class SourceDynamicProfileManager
     {
     public:
-        SourceDynamicProfileManager(Recycler* allocator) : isNonCachableScript(false), cachedStartupFunctions(nullptr), recycler(allocator), dynamicProfileInfoMap(allocator), startupFunctions(nullptr), profileDataCache(nullptr) {}
+        SourceDynamicProfileManager(Recycler* allocator) : isNonCachableScript(false), cachedStartupFunctions(nullptr), recycler(allocator),
+#ifdef DYNAMIC_PROFILE_STORAGE
+            dynamicProfileInfoMapSaving(&NoThrowHeapAllocator::Instance),
+#endif
+            dynamicProfileInfoMap(allocator), startupFunctions(nullptr), profileDataCache(nullptr) 
+        {
+        }
 
         ExecutionFlags IsFunctionExecuted(Js::LocalFunctionId functionId);
         DynamicProfileInfo * GetDynamicProfileInfo(FunctionBody * functionBody);
@@ -37,12 +43,19 @@ namespace Js
         bool LoadFromProfileCache(IActiveScriptDataCache* profileDataCache, LPCWSTR url);
         IActiveScriptDataCache* GetProfileCache() { return profileDataCache; }
         uint GetStartupFunctionsLength() { return (this->startupFunctions ? this->startupFunctions->Length() : 0); }
+#ifdef DYNAMIC_PROFILE_STORAGE
+        void ClearSavingData();
+        void CopySavingData();
+#endif
 
     private:
         friend class DynamicProfileInfo;
         FieldNoBarrier(Recycler*) recycler;
 
 #ifdef DYNAMIC_PROFILE_STORAGE
+        typedef JsUtil::BaseDictionary<LocalFunctionId, DynamicProfileInfo *, NoThrowHeapAllocator> DynamicProfileInfoMapSavingType;
+        FieldNoBarrier(DynamicProfileInfoMapSavingType) dynamicProfileInfoMapSaving;
+        
         void SaveDynamicProfileInfo(LocalFunctionId functionId, DynamicProfileInfo * dynamicProfileInfo);
         void SaveToDynamicProfileStorage(char16 const * url);
         template <typename T>
@@ -52,7 +65,6 @@ namespace Js
 #endif
         uint SaveToProfileCache();
         bool ShouldSaveToProfileCache(SourceContextInfo* info) const;
-        void Reset(uint numberOfFunctions);
 
     //------ Private data members -------- /
     private:
@@ -61,8 +73,7 @@ namespace Js
         Field(BVFixed*) startupFunctions;                   // Bit vector representing functions that are executed at startup
         Field(BVFixed const *) cachedStartupFunctions;      // Bit vector representing functions executed at startup that are loaded from a persistent or in-memory cache
                                                             // It's not modified but used as an input for deferred parsing/bytecodegen
-        typedef JsUtil::BaseDictionary<LocalFunctionId, DynamicProfileInfo *, Recycler, PowerOf2SizePolicy>
-            DynamicProfileInfoMapType;
+        typedef JsUtil::BaseDictionary<LocalFunctionId, DynamicProfileInfo *, Recycler, PowerOf2SizePolicy>  DynamicProfileInfoMapType;
         Field(DynamicProfileInfoMapType) dynamicProfileInfoMap;
 
         static const uint MAX_FUNCTION_COUNT = 10000;  // Consider data corrupt if there are more functions than this

+ 1 - 1
lib/Runtime/Language/WebAssemblySource.cpp

@@ -60,7 +60,7 @@ void WebAssemblySource::CreateSourceInfo(bool createNewContext, ScriptContext* s
         /* mod                 */ 0,
         /* grfsi               */ 0
     };
-    SRCINFO const * srcInfo = scriptContext->cache->noContextGlobalSourceInfo;
+    SRCINFO const * srcInfo = scriptContext->Cache()->noContextGlobalSourceInfo;
     if (createNewContext)
     {
         si.sourceContextInfo = scriptContext->CreateSourceContextInfo(scriptContext->GetNextSourceContextId(), nullptr, 0, nullptr);

+ 14 - 15
lib/Runtime/Library/JavascriptLibrary.cpp

@@ -1054,6 +1054,7 @@ namespace Js
             scriptContext->ResetWeakReferenceDictionaryList();
             scriptContext->SetIsFinalized();
             scriptContext->GetThreadContext()->UnregisterScriptContext(scriptContext);
+            scriptContext->MarkForClose();
         }
     }
 
@@ -1061,22 +1062,21 @@ namespace Js
     {
         if (scriptContext)
         {
-            if (isShutdown)
-            {
-                // during shut down the global object might not be closed yet.
-                // Clear the global object from the script context so it doesn't
-                // get unpinned (which may fail because the recycler is shutting down)
-                scriptContext->Close(true);
-                scriptContext->ClearGlobalObject();
-            }
-            else
-            {
-                Assert(scriptContext->IsClosed());
-            }
             HeapDelete(scriptContext);
             scriptContext = nullptr;
         }
     }
+    void JavascriptLibrary::Finalize(bool isShutdown)
+    {
+        __super::Finalize(isShutdown);
+
+        this->SetFakeGlobalFuncForUndefer(nullptr);
+
+        if (this->referencedPropertyRecords != nullptr)
+        {
+            RECYCLER_PERF_COUNTER_SUB(PropertyRecordBindReference, this->referencedPropertyRecords->Count());
+        }
+    }
 
     void JavascriptLibrary::InitializeGlobal(GlobalObject * globalObject)
     {
@@ -4943,7 +4943,7 @@ namespace Js
         this->nativeHostPromiseContinuationFunctionState = state;
     }
 
-    void JavascriptLibrary::PinJsrtContextObject(FinalizableObject* jsrtContext)
+    void JavascriptLibrary::SetJsrtContext(FinalizableObject* jsrtContext)
     {
         // With JsrtContext supporting cross context, ensure that it doesn't get GCed
         // prematurely. So pin the instance to javascriptLibrary so it will stay alive
@@ -4952,7 +4952,7 @@ namespace Js
         this->jsrtContextObject = jsrtContext;
     }
 
-    FinalizableObject* JavascriptLibrary::GetPinnedJsrtContextObject()
+    FinalizableObject* JavascriptLibrary::GetJsrtContext()
     {
         return this->jsrtContextObject;
     }
@@ -7084,7 +7084,6 @@ namespace Js
     {
         bindRefChunkCurrent = nullptr;
         bindRefChunkEnd = nullptr;
-        scriptContextCache = nullptr;
     }
 
     void JavascriptLibrary::BeginDynamicFunctionReferences()

+ 78 - 35
lib/Runtime/Library/JavascriptLibrary.h

@@ -28,6 +28,52 @@ namespace Projection
 
 namespace Js
 {
+    static const unsigned int EvalMRUSize = 15;
+    typedef JsUtil::BaseDictionary<DWORD_PTR, SourceContextInfo *, Recycler, PowerOf2SizePolicy> SourceContextInfoMap;
+    typedef JsUtil::BaseDictionary<uint, SourceContextInfo *, Recycler, PowerOf2SizePolicy> DynamicSourceContextInfoMap;
+
+    typedef JsUtil::BaseDictionary<EvalMapString, ScriptFunction*, RecyclerNonLeafAllocator, PrimeSizePolicy> SecondLevelEvalCache;
+    typedef TwoLevelHashRecord<FastEvalMapString, ScriptFunction*, SecondLevelEvalCache, EvalMapString> EvalMapRecord;
+    typedef JsUtil::Cache<FastEvalMapString, EvalMapRecord*, RecyclerNonLeafAllocator, PrimeSizePolicy, JsUtil::MRURetentionPolicy<FastEvalMapString, EvalMRUSize>, FastEvalMapStringComparer> EvalCacheTopLevelDictionary;
+    typedef JsUtil::Cache<EvalMapString, FunctionInfo*, RecyclerNonLeafAllocator, PrimeSizePolicy, JsUtil::MRURetentionPolicy<EvalMapString, EvalMRUSize>> NewFunctionCache;
+    typedef JsUtil::BaseDictionary<ParseableFunctionInfo*, ParseableFunctionInfo*, Recycler, PrimeSizePolicy, RecyclerPointerComparer> ParseableFunctionInfoMap;
+    // This is the dictionary used by script context to cache the eval.
+    typedef TwoLevelHashDictionary<FastEvalMapString, ScriptFunction*, EvalMapRecord, EvalCacheTopLevelDictionary, EvalMapString> EvalCacheDictionary;
+
+    typedef JsUtil::BaseDictionary<JavascriptMethod, JavascriptFunction*, Recycler, PowerOf2SizePolicy> BuiltInLibraryFunctionMap;
+
+    // valid if object!= NULL
+    struct EnumeratedObjectCache 
+    {
+        static const int kMaxCachedPropStrings = 16;
+        Field(DynamicObject*) object;
+        Field(DynamicType*) type;
+        Field(PropertyString*) propertyStrings[kMaxCachedPropStrings];
+        Field(int) validPropStrings;
+    };
+
+    struct Cache
+    {
+        Field(JavascriptString *) lastNumberToStringRadix10String;
+        Field(EnumeratedObjectCache) enumObjCache;
+        Field(JavascriptString *) lastUtcTimeFromStrString;
+        Field(EvalCacheDictionary*) evalCacheDictionary;
+        Field(EvalCacheDictionary*) indirectEvalCacheDictionary;
+        Field(NewFunctionCache*) newFunctionCache;
+        Field(RegexPatternMruMap *) dynamicRegexMap;
+        Field(SourceContextInfoMap*) sourceContextInfoMap;   // maps host provided context cookie to the URL of the script buffer passed.
+        Field(DynamicSourceContextInfoMap*) dynamicSourceContextInfoMap;
+        Field(SourceContextInfo*) noContextSourceContextInfo;
+        Field(SRCINFO*) noContextGlobalSourceInfo;
+        Field(Field(SRCINFO const *)*) moduleSrcInfo;
+        Field(BuiltInLibraryFunctionMap*) builtInLibraryFunctions;
+#if ENABLE_PROFILE_INFO
+#if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION)
+        Field(DynamicProfileInfoList*) profileInfoList;
+#endif
+#endif
+    };
+
     class MissingPropertyTypeHandler;
     class SourceTextModuleRecord;
     class ArrayBufferBase;
@@ -458,11 +504,12 @@ namespace Js
         Field(Field(void*)*) bindRefChunkCurrent;
         Field(Field(void*)*) bindRefChunkEnd;
         Field(TypePath*) rootPath;         // this should be in library instead of ScriptContext::Cache
-        Field(void*) scriptContextCache;   // forward declaration for point to ScriptContext::Cache such that we don't need to hard pin it.
+        Field(Js::Cache) cache;
         Field(FunctionReferenceList*) dynamicFunctionReference;
         Field(uint) dynamicFunctionReferenceDepth;
         Field(FinalizableObject*) jsrtContextObject;
         Field(JsrtExternalTypesCache*) jsrtExternalTypesCache;
+        Field(FunctionBody*) fakeGlobalFuncForUndefer;
 
         typedef JsUtil::BaseHashSet<RecyclerWeakReference<RecyclableObject>*, Recycler, PowerOf2SizePolicy, RecyclerWeakReference<RecyclableObject>*, StringTemplateCallsiteObjectComparer> StringTemplateCallsiteObjectList;
 
@@ -533,32 +580,32 @@ namespace Js
         static void InitializeProperties(ThreadContext * threadContext);
 
         JavascriptLibrary(GlobalObject* globalObject) :
-                              JavascriptLibraryBase(globalObject),
-                              inProfileMode(false),
-                              inDispatchProfileMode(false),
-                              propertyStringMap(nullptr),
-                              parseIntFunctionObject(nullptr),
-                              evalFunctionObject(nullptr),
-                              parseFloatFunctionObject(nullptr),
-                              arrayPrototypeToLocaleStringFunction(nullptr),
-                              arrayPrototypeToStringFunction(nullptr),
-                              identityFunction(nullptr),
-                              throwerFunction(nullptr),
-                              jsrtContextObject(nullptr),
-                              jsrtExternalTypesCache(nullptr),
-                              scriptContextCache(nullptr),
-                              externalLibraryList(nullptr),
+            JavascriptLibraryBase(globalObject),
+            inProfileMode(false),
+            inDispatchProfileMode(false),
+            propertyStringMap(nullptr),
+            parseIntFunctionObject(nullptr),
+            evalFunctionObject(nullptr),
+            parseFloatFunctionObject(nullptr),
+            arrayPrototypeToLocaleStringFunction(nullptr),
+            arrayPrototypeToStringFunction(nullptr),
+            identityFunction(nullptr),
+            throwerFunction(nullptr),
+            jsrtContextObject(nullptr),
+            jsrtExternalTypesCache(nullptr),
+            fakeGlobalFuncForUndefer(nullptr),
+            externalLibraryList(nullptr),
 #if ENABLE_COPYONACCESS_ARRAY
-                              cacheForCopyOnAccessArraySegments(nullptr),
+            cacheForCopyOnAccessArraySegments(nullptr),
 #endif
-                              referencedPropertyRecords(nullptr),
-                              stringTemplateCallsiteObjectList(nullptr),
-                              moduleRecordList(nullptr),
-                              rootPath(nullptr),
-                              bindRefChunkBegin(nullptr),
-                              bindRefChunkCurrent(nullptr),
-                              bindRefChunkEnd(nullptr),
-                              dynamicFunctionReference(nullptr)
+            referencedPropertyRecords(nullptr),
+            stringTemplateCallsiteObjectList(nullptr),
+            moduleRecordList(nullptr),
+            rootPath(nullptr),
+            bindRefChunkBegin(nullptr),
+            bindRefChunkCurrent(nullptr),
+            bindRefChunkEnd(nullptr),
+            dynamicFunctionReference(nullptr)
         {
             this->globalObject = globalObject;
         }
@@ -893,8 +940,8 @@ namespace Js
 
         void SetNativeHostPromiseContinuationFunction(PromiseContinuationCallback function, void *state);
 
-        void PinJsrtContextObject(FinalizableObject* jsrtContext);
-        FinalizableObject* GetPinnedJsrtContextObject();
+        void SetJsrtContext(FinalizableObject* jsrtContext);
+        FinalizableObject* GetJsrtContext();
         void EnqueueTask(Var taskVar);
 
         HeapArgumentsObject* CreateHeapArguments(Var frameObj, uint formalCount, bool isStrictMode = false);
@@ -1176,6 +1223,9 @@ namespace Js
         bool GetArrayObjectHasUserDefinedSpecies() const { return arrayObjectHasUserDefinedSpecies; }
         void SetArrayObjectHasUserDefinedSpecies(bool val) { arrayObjectHasUserDefinedSpecies = val; }
 
+        FunctionBody* GetFakeGlobalFuncForUndefer()const { return this->fakeGlobalFuncForUndefer; }
+        void SetFakeGlobalFuncForUndefer(FunctionBody* functionBody) { this->fakeGlobalFuncForUndefer = functionBody; }        
+
         ModuleRecordList* EnsureModuleRecordList();
         SourceTextModuleRecord* GetModuleRecord(uint moduleId);
 
@@ -1320,14 +1370,7 @@ namespace Js
 
 
     public:
-        virtual void Finalize(bool isShutdown) override
-        {
-            __super::Finalize(isShutdown);
-            if (this->referencedPropertyRecords != nullptr)
-            {
-                RECYCLER_PERF_COUNTER_SUB(PropertyRecordBindReference, this->referencedPropertyRecords->Count());
-            }
-        }
+        virtual void Finalize(bool isShutdown) override;
 
 #if DBG
         void DumpLibraryByteCode();

+ 3 - 3
lib/Runtime/Runtime.h

@@ -337,7 +337,6 @@ namespace TTD
 }
 
 #include "PlatformAgnostic/ChakraPlatform.h"
-#include "DataStructures/EvalMapString.h"
 
 bool IsMathLibraryId(Js::PropertyId propertyId);
 #include "ByteCode/PropertyIdArray.h"
@@ -446,6 +445,9 @@ enum tagDEBUG_EVENT_INFO_TYPE
 #include "Library/JavascriptLibraryBase.h"
 #include "Library/MathLibrary.h"
 #include "Base/ThreadContextInfo.h"
+#include "DataStructures/EvalMapString.h"
+#include "Language/EvalMapRecord.h"
+#include "Base/RegexPatternMruMap.h"
 #include "Library/JavascriptLibrary.h"
 
 #include "Language/JavascriptExceptionOperators.h"
@@ -480,8 +482,6 @@ enum tagDEBUG_EVENT_INFO_TYPE
 #include "Base/StackProber.h"
 #include "Base/ScriptContextProfiler.h"
 
-#include "Language/EvalMapRecord.h"
-#include "Base/RegexPatternMruMap.h"
 #include "Language/JavascriptConversion.h"
 
 #include "Base/ScriptContextOptimizationOverrideInfo.h"