ソースを参照

TTD - Support for DebuggerScope snap/restore.

Mark Marron 9 年 前
コミット
62a19a260e

+ 5 - 4
lib/Common/CommonDefines.h

@@ -326,17 +326,18 @@
 
 //Temp code needed to run VSCode but slows down execution (later will fix + implement high perf. version)
 //The ifndef check allows us to override this in build from Node in TTD version (where we want to have this on by default)
-#ifndef TTD_DEBUGGING_PERFORMANCE_WORK_AROUNDS
+#ifndef TTD_ENABLE_FULL_FUNCTIONALITY_IN_NODE
 #define TTD_DEBUGGING_PERFORMANCE_WORK_AROUNDS 0
+#define TTD_DISABLE_COPYONACCESS_ARRAY_WORK_AROUNDS 0
+#else
+#define TTD_DEBUGGING_PERFORMANCE_WORK_AROUNDS 1
+#define TTD_DISABLE_COPYONACCESS_ARRAY_WORK_AROUNDS 1
 #endif
 
 //A workaround for some unimplemented code parse features (force debug mode)
 //Enable to turn these features off for good performance measurements.
 #define TTD_DYNAMIC_DECOMPILATION_WORK_AROUNDS 1
 
-//A workaround for copy on access native arrays -- disable them for now and we should have support later
-#define TTD_DISABLE_COPYONACCESS_ARRAY_WORK_AROUNDS 1
-
 //Enable various sanity checking features and asserts
 #define ENABLE_TTD_INTERNAL_DIAGNOSTICS 1
 

+ 9 - 0
lib/Runtime/Base/FunctionBody.cpp

@@ -4932,6 +4932,15 @@ namespace Js
         }
     }
 #endif
+
+#if ENABLE_TTD
+    Js::PropertyId DebuggerScope::GetPropertyIdForSlotIndex_TTD(uint32 slotIndex) const
+    {
+        const Js::DebuggerScopeProperty& scopeProperty = this->scopeProperties->Item(slotIndex);
+        return scopeProperty.propId;
+    }
+#endif
+
     // Updates the current offset of where the property is first initialized.  This is used to
     // detect whether or not a property is in a dead zone when broken in the debugger.
     // location                 - The slot array index or register slot location of where the property is stored.

+ 4 - 0
lib/Runtime/Base/FunctionBody.h

@@ -3604,6 +3604,10 @@ namespace Js
         PCWSTR GetDebuggerScopeTypeString(DiagExtraScopesType scopeType);
 #endif
 
+#if ENABLE_TTD
+        Js::PropertyId GetPropertyIdForSlotIndex_TTD(uint32 slotIndex) const;
+#endif
+
     public:
         // The list of scope properties in this scope object.
         // For with scope:  Has 1 property that represents the scoped object.

+ 7 - 2
lib/Runtime/Debug/TTEventLog.cpp

@@ -1963,11 +1963,14 @@ namespace TTD
         }
         AssertMsg(snap != nullptr, "Log should start with a snapshot!!!");
 
+        uint32 dbgScopeCount = snap->GetDbgScopeCountNonTopLevel();
+
         TTDIdentifierDictionary<uint64, NSSnapValues::TopLevelScriptLoadFunctionBodyResolveInfo*> topLevelLoadScriptMap;
         topLevelLoadScriptMap.Initialize(this->m_loadedTopLevelScripts.Count());
         for(auto iter = this->m_loadedTopLevelScripts.GetIterator(); iter.IsValid(); iter.MoveNext())
         {
             topLevelLoadScriptMap.AddItem(iter.Current()->TopLevelBase.TopLevelBodyCtr, iter.Current());
+            dbgScopeCount += iter.Current()->TopLevelBase.ScopeChainInfo.ScopeCount;
         }
 
         TTDIdentifierDictionary<uint64, NSSnapValues::TopLevelNewFunctionBodyResolveInfo*> topLevelNewScriptMap;
@@ -1975,6 +1978,7 @@ namespace TTD
         for(auto iter = this->m_newFunctionTopLevelScripts.GetIterator(); iter.IsValid(); iter.MoveNext())
         {
             topLevelNewScriptMap.AddItem(iter.Current()->TopLevelBase.TopLevelBodyCtr, iter.Current());
+            dbgScopeCount += iter.Current()->TopLevelBase.ScopeChainInfo.ScopeCount;
         }
 
         TTDIdentifierDictionary<uint64, NSSnapValues::TopLevelEvalFunctionBodyResolveInfo*> topLevelEvalScriptMap;
@@ -1982,6 +1986,7 @@ namespace TTD
         for(auto iter = this->m_evalTopLevelScripts.GetIterator(); iter.IsValid(); iter.MoveNext())
         {
             topLevelEvalScriptMap.AddItem(iter.Current()->TopLevelBase.TopLevelBodyCtr, iter.Current());
+            dbgScopeCount += iter.Current()->TopLevelBase.ScopeChainInfo.ScopeCount;
         }
 
         //
@@ -1993,14 +1998,14 @@ namespace TTD
 
         if(this->m_lastInflateMap != nullptr)
         {
-            this->m_lastInflateMap->PrepForReInflate(snap->ContextCount(), snap->HandlerCount(), snap->TypeCount(), snap->PrimitiveCount() + snap->ObjectCount(), snap->BodyCount(), snap->EnvCount(), snap->SlotArrayCount());
+            this->m_lastInflateMap->PrepForReInflate(snap->ContextCount(), snap->HandlerCount(), snap->TypeCount(), snap->PrimitiveCount() + snap->ObjectCount(), snap->BodyCount(), dbgScopeCount, snap->EnvCount(), snap->SlotArrayCount());
 
             NSSnapValues::InflateScriptContext(sCtx, this->m_ttdContext, this->m_lastInflateMap, topLevelLoadScriptMap, topLevelNewScriptMap, topLevelEvalScriptMap);
         }
         else
         {
             this->m_lastInflateMap = TT_HEAP_NEW(InflateMap);
-            this->m_lastInflateMap->PrepForInitialInflate(this->m_threadContext, snap->ContextCount(), snap->HandlerCount(), snap->TypeCount(), snap->PrimitiveCount() + snap->ObjectCount(), snap->BodyCount(), snap->EnvCount(), snap->SlotArrayCount());
+            this->m_lastInflateMap->PrepForInitialInflate(this->m_threadContext, snap->ContextCount(), snap->HandlerCount(), snap->TypeCount(), snap->PrimitiveCount() + snap->ObjectCount(), snap->BodyCount(), dbgScopeCount, snap->EnvCount(), snap->SlotArrayCount());
             this->m_lastInflateSnapshotTime = etime;
 
             NSSnapValues::InflateScriptContext(sCtx, this->m_ttdContext, this->m_lastInflateMap, topLevelLoadScriptMap, topLevelNewScriptMap, topLevelEvalScriptMap);

+ 35 - 2
lib/Runtime/Debug/TTInflateMap.cpp

@@ -12,6 +12,7 @@ namespace TTD
         : m_typeMap(), m_handlerMap(),
         m_tagToGlobalObjectMap(), m_objectMap(),
         m_functionBodyMap(), m_environmentMap(), m_slotArrayMap(), m_promiseDataMap(&HeapAllocator::Instance),
+        m_debuggerScopeHomeBodyMap(), m_debuggerScopeChainIndexMap(),
         m_inflatePinSet(nullptr), m_environmentPinSet(nullptr), m_slotArrayPinSet(nullptr), m_oldInflatePinSet(nullptr),
         m_oldObjectMap(), m_oldFunctionBodyMap(), m_propertyReset(&HeapAllocator::Instance)
     {
@@ -45,13 +46,15 @@ namespace TTD
         }
     }
 
-    void InflateMap::PrepForInitialInflate(ThreadContext* threadContext, uint32 ctxCount, uint32 handlerCount, uint32 typeCount, uint32 objectCount, uint32 bodyCount, uint32 envCount, uint32 slotCount)
+    void InflateMap::PrepForInitialInflate(ThreadContext* threadContext, uint32 ctxCount, uint32 handlerCount, uint32 typeCount, uint32 objectCount, uint32 bodyCount, uint32 dbgScopeCount, uint32 envCount, uint32 slotCount)
     {
         this->m_typeMap.Initialize(typeCount);
         this->m_handlerMap.Initialize(handlerCount);
         this->m_tagToGlobalObjectMap.Initialize(ctxCount);
         this->m_objectMap.Initialize(objectCount);
         this->m_functionBodyMap.Initialize(bodyCount);
+        this->m_debuggerScopeHomeBodyMap.Initialize(dbgScopeCount);
+        this->m_debuggerScopeChainIndexMap.Initialize(dbgScopeCount);
         this->m_environmentMap.Initialize(envCount);
         this->m_slotArrayMap.Initialize(slotCount);
         this->m_promiseDataMap.Clear();
@@ -66,11 +69,13 @@ namespace TTD
         threadContext->GetRecycler()->RootAddRef(this->m_slotArrayPinSet);
     }
 
-    void InflateMap::PrepForReInflate(uint32 ctxCount, uint32 handlerCount, uint32 typeCount, uint32 objectCount, uint32 bodyCount, uint32 envCount, uint32 slotCount)
+    void InflateMap::PrepForReInflate(uint32 ctxCount, uint32 handlerCount, uint32 typeCount, uint32 objectCount, uint32 bodyCount, uint32 dbgScopeCount, uint32 envCount, uint32 slotCount)
     {
         this->m_typeMap.Initialize(typeCount);
         this->m_handlerMap.Initialize(handlerCount);
         this->m_tagToGlobalObjectMap.Initialize(ctxCount);
+        this->m_debuggerScopeHomeBodyMap.Initialize(dbgScopeCount);
+        this->m_debuggerScopeChainIndexMap.Initialize(dbgScopeCount);
         this->m_environmentMap.Initialize(envCount);
         this->m_slotArrayMap.Initialize(slotCount);
         this->m_promiseDataMap.Clear();
@@ -104,6 +109,8 @@ namespace TTD
         this->m_handlerMap.Unload();
         this->m_typeMap.Unload();
         this->m_tagToGlobalObjectMap.Unload();
+        this->m_debuggerScopeHomeBodyMap.Unload();
+        this->m_debuggerScopeChainIndexMap.Unload();
         this->m_environmentMap.Unload();
         this->m_slotArrayMap.Unload();
         this->m_promiseDataMap.Clear();
@@ -191,6 +198,12 @@ namespace TTD
         return this->m_slotArrayMap.LookupKnownItem(slotid);
     }
 
+    void InflateMap::LookupInfoForDebugScope(TTD_PTR_ID dbgScopeId, Js::FunctionBody** homeBody, int32* chainIndex) const
+    {
+        *homeBody = this->m_debuggerScopeHomeBodyMap.LookupKnownItem(dbgScopeId);
+        *chainIndex = this->m_debuggerScopeChainIndexMap.LookupKnownItem(dbgScopeId);
+    }
+
     void InflateMap::AddDynamicHandler(TTD_PTR_ID handlerId, Js::DynamicTypeHandler* value)
     {
         this->m_handlerMap.AddItem(handlerId, value);
@@ -233,6 +246,26 @@ namespace TTD
         this->m_slotArrayPinSet->AddNew(value);
     }
 
+    void InflateMap::UpdateFBScopes(const NSSnapValues::SnapFunctionBodyScopeChain& scopeChainInfo, Js::FunctionBody* fb)
+    {
+        AssertMsg((int32)scopeChainInfo.ScopeCount == (fb->GetScopeObjectChain() != nullptr ? fb->GetScopeObjectChain()->pScopeChain->Count() : 0), "Mismatch in scope counts!!!");
+
+        if(fb->GetScopeObjectChain() != nullptr)
+        {
+            Js::ScopeObjectChain* scChain = fb->GetScopeObjectChain();
+            for(int32 i = 0; i < scChain->pScopeChain->Count(); ++i)
+            {
+                TTD_PTR_ID dbgScopeId = scopeChainInfo.ScopeArray[i];
+
+                if(!this->m_debuggerScopeHomeBodyMap.Contains(dbgScopeId))
+                {
+                    this->m_debuggerScopeHomeBodyMap.AddItem(dbgScopeId, fb);
+                    this->m_debuggerScopeChainIndexMap.AddItem(dbgScopeId, i);
+                }
+            }
+        }
+    }
+
     JsUtil::BaseHashSet<Js::PropertyId, HeapAllocator>& InflateMap::GetPropertyResetSet()
     {
         return this->m_propertyReset;

+ 10 - 2
lib/Runtime/Debug/TTInflateMap.h

@@ -24,6 +24,10 @@ namespace TTD
         TTDIdentifierDictionary<TTD_PTR_ID, Js::FrameDisplay*> m_environmentMap;
         TTDIdentifierDictionary<TTD_PTR_ID, Js::Var*> m_slotArrayMap;
 
+        //The maps for resolving debug scopes
+        TTDIdentifierDictionary<TTD_PTR_ID, Js::FunctionBody*> m_debuggerScopeHomeBodyMap;
+        TTDIdentifierDictionary<TTD_PTR_ID, int32> m_debuggerScopeChainIndexMap;
+
         //A dictionary for the Promise related bits (not typesafe and a bit ugly but I prefer it to creating multiple additional collections)
         JsUtil::BaseDictionary<TTD_PTR_ID, void*, HeapAllocator> m_promiseDataMap;
 
@@ -45,8 +49,8 @@ namespace TTD
         InflateMap();
         ~InflateMap();
 
-        void PrepForInitialInflate(ThreadContext* threadContext, uint32 ctxCount, uint32 handlerCount, uint32 typeCount, uint32 objectCount, uint32 bodyCount, uint32 envCount, uint32 slotCount);
-        void PrepForReInflate(uint32 ctxCount, uint32 handlerCount, uint32 typeCount, uint32 objectCount, uint32 bodyCount, uint32 envCount, uint32 slotCount);
+        void PrepForInitialInflate(ThreadContext* threadContext, uint32 ctxCount, uint32 handlerCount, uint32 typeCount, uint32 objectCount, uint32 bodyCount, uint32 dbgScopeCount, uint32 envCount, uint32 slotCount);
+        void PrepForReInflate(uint32 ctxCount, uint32 handlerCount, uint32 typeCount, uint32 objectCount, uint32 bodyCount, uint32 dbgScopeCount, uint32 envCount, uint32 slotCount);
         void CleanupAfterInflate();
 
         bool IsObjectAlreadyInflated(TTD_PTR_ID objid) const;
@@ -67,6 +71,8 @@ namespace TTD
         Js::FrameDisplay* LookupEnvironment(TTD_PTR_ID envid) const;
         Js::Var* LookupSlotArray(TTD_PTR_ID slotid) const;
 
+        void LookupInfoForDebugScope(TTD_PTR_ID dbgScopeId, Js::FunctionBody** homeBody, int32* chainIndex) const;
+
         ////
 
         void AddDynamicHandler(TTD_PTR_ID handlerId, Js::DynamicTypeHandler* value);
@@ -79,6 +85,8 @@ namespace TTD
         void AddEnvironment(TTD_PTR_ID envId, Js::FrameDisplay* value);
         void AddSlotArray(TTD_PTR_ID slotId, Js::Var* value);
 
+        void UpdateFBScopes(const NSSnapValues::SnapFunctionBodyScopeChain& scopeChainInfo, Js::FunctionBody* fb);
+
         ////
 
         //Get the inflate stack and property reset set to use for depends on processing

+ 9 - 2
lib/Runtime/Debug/TTRuntimeInfoTracker.h

@@ -118,10 +118,12 @@ namespace TTD
         //A dictionary which contains the paths for "core" image objects and function bodies
         JsUtil::BaseDictionary<Js::RecyclableObject*, UtilSupport::TTAutoString*, HeapAllocator> m_coreObjToPathMap;
         JsUtil::BaseDictionary<Js::FunctionBody*, UtilSupport::TTAutoString*, HeapAllocator> m_coreBodyToPathMap;
-        
+        JsUtil::BaseDictionary<Js::DebuggerScope*, UtilSupport::TTAutoString*, HeapAllocator> m_coreDbgScopeToPathMap;
+
         JsUtil::List<Js::RecyclableObject*, HeapAllocator> m_sortedObjectList;
         JsUtil::List<Js::FunctionBody*, HeapAllocator> m_sortedFunctionBodyList;
-        
+        JsUtil::List<Js::DebuggerScope*, HeapAllocator> m_sortedDbgScopeList;
+
         //Build a path string based on a given name
         void BuildPathString(UtilSupport::TTAutoString, const char16* name, const char16* optaccessortag, UtilSupport::TTAutoString& into);
 
@@ -140,10 +142,12 @@ namespace TTD
         //Get the path name for a known path object (or function body)
         TTD_WELLKNOWN_TOKEN ResolvePathForKnownObject(Js::RecyclableObject* obj) const;
         TTD_WELLKNOWN_TOKEN ResolvePathForKnownFunctionBody(Js::FunctionBody* fbody) const;
+        TTD_WELLKNOWN_TOKEN ResolvePathForKnownDbgScopeIfExists(Js::DebuggerScope* dbgScope) const;
 
         //Given a path name string lookup the coresponding object
         Js::RecyclableObject* LookupKnownObjectFromPath(TTD_WELLKNOWN_TOKEN pathIdString) const;
         Js::FunctionBody* LookupKnownFunctionBodyFromPath(TTD_WELLKNOWN_TOKEN pathIdString) const;
+        Js::DebuggerScope* LookupKnownDebuggerScopeFromPath(TTD_WELLKNOWN_TOKEN pathIdString) const;
 
         //Walk the "known names" we use and fill the map with the objects at said names
         void GatherKnownObjectToPathMap(Js::ScriptContext* ctx);
@@ -160,6 +164,9 @@ namespace TTD
         //Enqueue a child object that is stored at a special named location in the parent object
         void EnqueueNewFunctionBodyObject(Js::RecyclableObject* parent, Js::FunctionBody* fbody, const char16* name);
 
+        //Add a well known token for a debugger scope object (in a slot array)
+        void AddWellKnownDebuggerScopePath(Js::RecyclableObject* parent, Js::DebuggerScope* dbgScope, uint32 index);
+
         //Build a path string based on a root path and an array index
         void BuildArrayIndexBuffer(uint32 arrayidx, UtilSupport::TTAutoString& res);
 

+ 45 - 3
lib/Runtime/Debug/TTRuntmeInfoTracker.cpp

@@ -388,8 +388,8 @@ namespace TTD
     
     RuntimeContextInfo::RuntimeContextInfo()
         : m_worklist(&HeapAllocator::Instance), m_nullString(),
-        m_coreObjToPathMap(&HeapAllocator::Instance, TTD_CORE_OBJECT_COUNT), m_coreBodyToPathMap(&HeapAllocator::Instance, TTD_CORE_FUNCTION_BODY_COUNT),
-        m_sortedObjectList(&HeapAllocator::Instance, TTD_CORE_OBJECT_COUNT), m_sortedFunctionBodyList(&HeapAllocator::Instance, TTD_CORE_FUNCTION_BODY_COUNT)
+        m_coreObjToPathMap(&HeapAllocator::Instance, TTD_CORE_OBJECT_COUNT), m_coreBodyToPathMap(&HeapAllocator::Instance, TTD_CORE_FUNCTION_BODY_COUNT), m_coreDbgScopeToPathMap(&HeapAllocator::Instance, TTD_CORE_FUNCTION_BODY_COUNT),
+        m_sortedObjectList(&HeapAllocator::Instance, TTD_CORE_OBJECT_COUNT), m_sortedFunctionBodyList(&HeapAllocator::Instance, TTD_CORE_FUNCTION_BODY_COUNT), m_sortedDbgScopeList(&HeapAllocator::Instance, TTD_CORE_FUNCTION_BODY_COUNT)
     {
         ;
     }
@@ -405,6 +405,11 @@ namespace TTD
         {
             TT_HEAP_DELETE(UtilSupport::TTAutoString, iter.CurrentValue());
         }
+
+        for(auto iter = this->m_coreDbgScopeToPathMap.GetIterator(); iter.IsValid(); iter.MoveNext())
+        {
+            TT_HEAP_DELETE(UtilSupport::TTAutoString, iter.CurrentValue());
+        }
     }
 
     //Mark all the well-known objects/values/types from this script context
@@ -439,6 +444,18 @@ namespace TTD
         return res->GetStrValue();
     }
 
+    TTD_WELLKNOWN_TOKEN RuntimeContextInfo::ResolvePathForKnownDbgScopeIfExists(Js::DebuggerScope* dbgScope) const
+    {
+        const UtilSupport::TTAutoString* res = this->m_coreDbgScopeToPathMap.LookupWithKey(dbgScope, nullptr);
+
+        if(res == nullptr)
+        {
+            return nullptr;
+        }
+
+        return res->GetStrValue();
+    }
+
     Js::RecyclableObject* RuntimeContextInfo::LookupKnownObjectFromPath(TTD_WELLKNOWN_TOKEN pathIdString) const
     {
         int32 pos = LookupPositionInDictNameList<Js::RecyclableObject*, true>(pathIdString, this->m_coreObjToPathMap, this->m_sortedObjectList, this->m_nullString);
@@ -455,6 +472,14 @@ namespace TTD
         return (pos != -1) ? this->m_sortedFunctionBodyList.Item(pos) : nullptr;
     }
 
+    Js::DebuggerScope* RuntimeContextInfo::LookupKnownDebuggerScopeFromPath(TTD_WELLKNOWN_TOKEN pathIdString) const
+    {
+        int32 pos = LookupPositionInDictNameList<Js::DebuggerScope*, true>(pathIdString, this->m_coreDbgScopeToPathMap, this->m_sortedDbgScopeList, this->m_nullString);
+        AssertMsg(pos != -1, "Missing debug scope.");
+
+        return (pos != -1) ? this->m_sortedDbgScopeList.Item(pos) : nullptr;
+    }
+
     void RuntimeContextInfo::GatherKnownObjectToPathMap(Js::ScriptContext* ctx)
     {
         JsUtil::List<const Js::PropertyRecord*, HeapAllocator> propertyRecordList(&HeapAllocator::Instance);
@@ -532,6 +557,7 @@ namespace TTD
 
         SortDictIntoListOnNames<Js::RecyclableObject*>(this->m_coreObjToPathMap, this->m_sortedObjectList, this->m_nullString);
         SortDictIntoListOnNames<Js::FunctionBody*>(this->m_coreBodyToPathMap, this->m_sortedFunctionBodyList, this->m_nullString);
+        SortDictIntoListOnNames<Js::DebuggerScope*>(this->m_coreDbgScopeToPathMap, this->m_sortedDbgScopeList, this->m_nullString);
     }
 
     ////
@@ -588,6 +614,7 @@ namespace TTD
     {
         if(!this->m_coreBodyToPathMap.ContainsKey(fbody))
         {
+            fbody->EnsureDeserialized();
             const UtilSupport::TTAutoString* ppath = this->m_coreObjToPathMap.LookupWithKey(parent, nullptr);
 
             UtilSupport::TTAutoString* fpath = TT_HEAP_NEW(UtilSupport::TTAutoString, *ppath);
@@ -595,11 +622,26 @@ namespace TTD
             fpath->Append(_u("."));
             fpath->Append(name);
 
-            AssertMsg(!this->m_coreBodyToPathMap.ContainsKey(fbody), "Already in map!!!");
             this->m_coreBodyToPathMap.AddNew(fbody, fpath);
         }
     }
 
+    void RuntimeContextInfo::AddWellKnownDebuggerScopePath(Js::RecyclableObject* parent, Js::DebuggerScope* dbgScope, uint32 index)
+    {
+        if(!this->m_coreDbgScopeToPathMap.ContainsKey(dbgScope))
+        {
+            const UtilSupport::TTAutoString* ppath = this->m_coreObjToPathMap.LookupWithKey(parent, nullptr);
+
+            UtilSupport::TTAutoString* scpath = TT_HEAP_NEW(UtilSupport::TTAutoString, *ppath);
+
+            scpath->Append(_u(".!scope["));
+            scpath->Append(index);
+            scpath->Append(_u("]"));
+
+            this->m_coreDbgScopeToPathMap.AddNew(dbgScope, scpath);
+        }
+    }
+
     void RuntimeContextInfo::BuildArrayIndexBuffer(uint32 arrayidx, UtilSupport::TTAutoString& res)
     {
         res.Append(_u("!arrayContents["));

+ 2 - 0
lib/Runtime/Debug/TTSerializeEnum.h

@@ -57,6 +57,7 @@ ENTRY_SERIALIZE_ENUM(handlerId)
 ENTRY_SERIALIZE_ENUM(typeId)
 ENTRY_SERIALIZE_ENUM(prototypeVar)
 ENTRY_SERIALIZE_ENUM(functionBodyId)
+ENTRY_SERIALIZE_ENUM(debuggerScopeId)
 ENTRY_SERIALIZE_ENUM(parentBodyId)
 ENTRY_SERIALIZE_ENUM(bodyCounterId)
 ENTRY_SERIALIZE_ENUM(scopeId)
@@ -81,6 +82,7 @@ ENTRY_SERIALIZE_ENUM(uri)
 ENTRY_SERIALIZE_ENUM(moduleId)
 ENTRY_SERIALIZE_ENUM(documentId)
 ENTRY_SERIALIZE_ENUM(isGlobalCode)
+ENTRY_SERIALIZE_ENUM(scopeChain)
 
 ENTRY_SERIALIZE_ENUM(boundFunction)
 ENTRY_SERIALIZE_ENUM(boundThis)

+ 9 - 1
lib/Runtime/Debug/TTSnapObjects.cpp

@@ -1297,7 +1297,9 @@ namespace TTD
             Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);
             SnapRegexInfo* regexInfo = SnapObjectGetAddtlInfoAs<SnapRegexInfo*, SnapObjectType::SnapRegexObject>(snpObject);
 
-            return ctx->GetLibrary()->CreateRegex_TTD(regexInfo->RegexStr.Contents, regexInfo->RegexStr.Length, regexInfo->Flags, regexInfo->LastIndexOrFlag);
+            Js::Var lastVar = (regexInfo->LastIndexVar != nullptr) ? inflator->InflateTTDVar(regexInfo->LastIndexVar) : nullptr;
+
+            return ctx->GetLibrary()->CreateRegex_TTD(regexInfo->RegexStr.Contents, regexInfo->RegexStr.Length, regexInfo->Flags, regexInfo->LastIndexOrFlag, lastVar);
         }
 
         void EmitAddtlInfo_SnapRegexInfo(const SnapObject* snpObject, FileWriter* writer)
@@ -1308,6 +1310,9 @@ namespace TTD
 
             writer->WriteTag<UnifiedRegex::RegexFlags>(NSTokens::Key::attributeFlags, regexInfo->Flags, NSTokens::Separator::CommaSeparator);
             writer->WriteUInt32(NSTokens::Key::u32Val, regexInfo->LastIndexOrFlag, NSTokens::Separator::CommaSeparator);
+
+            writer->WriteKey(NSTokens::Key::ttdVarTag, NSTokens::Separator::CommaSeparator);
+            NSSnapValues::EmitTTDVar(regexInfo->LastIndexVar, writer, NSTokens::Separator::NoSeparator);
         }
 
         void ParseAddtlInfo_SnapRegexInfo(SnapObject* snpObject, FileReader* reader, SlabAllocator& alloc)
@@ -1319,6 +1324,9 @@ namespace TTD
             regexInfo->Flags = reader->ReadTag<UnifiedRegex::RegexFlags>(NSTokens::Key::attributeFlags, true);
             regexInfo->LastIndexOrFlag = reader->ReadUInt32(NSTokens::Key::u32Val, true);
 
+            reader->ReadKey(NSTokens::Key::ttdVarTag, true);
+            regexInfo->LastIndexVar = NSSnapValues::ParseTTDVar(false, reader);
+
             SnapObjectSetAddtlInfoAs<SnapRegexInfo*, SnapObjectType::SnapRegexObject>(snpObject, regexInfo);
         }
 

+ 3 - 0
lib/Runtime/Debug/TTSnapObjects.h

@@ -460,6 +460,9 @@ namespace TTD
 
             //The char count or flag value from the regex object
             CharCount LastIndexOrFlag;
+
+            //The last index var from the regex object
+            TTDVar LastIndexVar;
         };
 
         ////

+ 163 - 19
lib/Runtime/Debug/TTSnapValues.cpp

@@ -571,8 +571,25 @@ namespace TTD
             }
             else
             {
-                //TODO: when we do better with debuggerscope we should set this to a real value
-                scopeSlots.SetScopeMetadata(nullptr);
+                Js::DebuggerScope* dbgScope = nullptr;
+                if(slotInfo->OptWellKnownDbgScope != TTD_INVALID_WELLKNOWN_TOKEN)
+                {
+                    dbgScope = ctx->TTDWellKnownInfo->LookupKnownDebuggerScopeFromPath(slotInfo->OptWellKnownDbgScope);
+                }
+                else
+                {
+                    Js::FunctionBody* scopeBody = nullptr;
+                    int32 scopeIndex = -1;
+                    inflator->LookupInfoForDebugScope(slotInfo->OptDebugScopeId, &scopeBody, &scopeIndex);
+
+                    dbgScope = scopeBody->GetScopeObjectChain()->pScopeChain->Item(scopeIndex);
+                }
+
+#if ENABLE_TTD_INTERNAL_DIAGNOSTICS
+                AssertMsg(dbgScope->GetStart() == slotInfo->OptDiagDebugScopeBegin && dbgScope->GetEnd() == slotInfo->OptDiagDebugScopeEnd, "Bytecode positions don't match!!!");
+#endif
+
+                scopeSlots.SetScopeMetadata(dbgScope);
             }
 
             for(uint32 j = 0; j < slotInfo->SlotCount; j++)
@@ -599,7 +616,20 @@ namespace TTD
             }
             else
             {
-                //TODO: emit debugger scope metadata
+                writer->WriteBool(NSTokens::Key::isWellKnownToken, slotInfo->OptWellKnownDbgScope != TTD_INVALID_WELLKNOWN_TOKEN, NSTokens::Separator::CommaSeparator);
+                if(slotInfo->OptWellKnownDbgScope != TTD_INVALID_WELLKNOWN_TOKEN)
+                {
+                    writer->WriteWellKnownToken(NSTokens::Key::wellKnownToken, slotInfo->OptWellKnownDbgScope, NSTokens::Separator::CommaSeparator);
+                }
+                else
+                {
+                    writer->WriteAddr(NSTokens::Key::debuggerScopeId, slotInfo->OptDebugScopeId, NSTokens::Separator::CommaSeparator);
+                }
+
+#if ENABLE_TTD_INTERNAL_DIAGNOSTICS
+                writer->WriteInt32(NSTokens::Key::i32Val, slotInfo->OptDiagDebugScopeBegin, NSTokens::Separator::CommaSeparator);
+                writer->WriteInt32(NSTokens::Key::i32Val, slotInfo->OptDiagDebugScopeEnd, NSTokens::Separator::CommaSeparator);
+#endif
             }
 
             writer->WriteLengthValue(slotInfo->SlotCount, NSTokens::Separator::CommaAndBigSpaceSeparator);
@@ -639,13 +669,33 @@ namespace TTD
 
             slotInfo->isFunctionBodyMetaData = reader->ReadBool(NSTokens::Key::isFunctionMetaData, true);
             slotInfo->OptFunctionBodyId = TTD_INVALID_PTR_ID;
+            slotInfo->OptDebugScopeId = TTD_INVALID_PTR_ID;
+            slotInfo->OptWellKnownDbgScope = TTD_INVALID_WELLKNOWN_TOKEN;
+
             if(slotInfo->isFunctionBodyMetaData)
             {
                 slotInfo->OptFunctionBodyId = reader->ReadAddr(NSTokens::Key::functionBodyId, true);
+
+#if ENABLE_TTD_INTERNAL_DIAGNOSTICS
+                slotInfo->OptDiagDebugScopeBegin = -1;
+                slotInfo->OptDiagDebugScopeEnd = -1;
+#endif
             }
             else
             {
-                //TODO: emit debugger scope metadata
+                bool isWellKnown = reader->ReadBool(NSTokens::Key::isWellKnownToken, true);
+                if(isWellKnown)
+                {
+                    slotInfo->OptWellKnownDbgScope = reader->ReadWellKnownToken(NSTokens::Key::wellKnownToken, alloc, true);
+                }
+                else
+                {
+                    slotInfo->OptDebugScopeId = reader->ReadAddr(NSTokens::Key::debuggerScopeId, true);
+                }
+#if ENABLE_TTD_INTERNAL_DIAGNOSTICS
+                slotInfo->OptDiagDebugScopeBegin = reader->ReadInt32(NSTokens::Key::i32Val, true);
+                slotInfo->OptDiagDebugScopeEnd = reader->ReadInt32(NSTokens::Key::i32Val, true);
+#endif
             }
 
             slotInfo->SlotCount = reader->ReadLengthValue(true);
@@ -923,6 +973,68 @@ namespace TTD
 
         //////////////////
 
+        void ExtractSnapFunctionBodyScopeChain(bool isWellKnownFunction, SnapFunctionBodyScopeChain& scopeChain, Js::FunctionBody* fb, SlabAllocator& alloc)
+        {
+            scopeChain.ScopeCount = 0;
+            scopeChain.ScopeArray = nullptr;
+
+            if(!isWellKnownFunction && fb->GetScopeObjectChain() != nullptr)
+            {
+                Js::ScopeObjectChain* scChain = fb->GetScopeObjectChain();
+                scopeChain.ScopeCount = (uint32)scChain->pScopeChain->Count();
+                scopeChain.ScopeArray = (scopeChain.ScopeCount != 0) ? alloc.SlabAllocateArray<TTD_PTR_ID>(scopeChain.ScopeCount) : 0;
+
+                for(int32 i = 0; i < scChain->pScopeChain->Count(); ++i)
+                {
+                    Js::DebuggerScope* dbgScope = scChain->pScopeChain->Item(i);
+                    scopeChain.ScopeArray[i] = TTD_CONVERT_DEBUGSCOPE_TO_PTR_ID(dbgScope);
+                }
+            }
+        }
+
+        void EmitSnapFunctionBodyScopeChain(const SnapFunctionBodyScopeChain& scopeChain, FileWriter* writer)
+        {
+            writer->WriteRecordStart();
+
+            writer->WriteLengthValue(scopeChain.ScopeCount);
+            writer->WriteSequenceStart_DefaultKey(NSTokens::Separator::CommaSeparator);
+            for(uint32 i = 0; i < scopeChain.ScopeCount; ++i)
+            {
+                writer->WriteNakedAddr(scopeChain.ScopeArray[i], (i != 0) ? NSTokens::Separator::CommaSeparator : NSTokens::Separator::NoSeparator);
+            }
+            writer->WriteSequenceEnd();
+
+            writer->WriteRecordEnd();
+        }
+
+        void ParseSnapFunctionBodyScopeChain(SnapFunctionBodyScopeChain& scopeChain, FileReader* reader, SlabAllocator& alloc)
+        {
+            reader->ReadRecordStart();
+
+            scopeChain.ScopeCount = reader->ReadLengthValue();
+            scopeChain.ScopeArray = (scopeChain.ScopeCount != 0) ? alloc.SlabAllocateArray<TTD_PTR_ID>(scopeChain.ScopeCount) : nullptr;
+
+            reader->ReadSequenceStart_WDefaultKey(true);
+            for(uint32 i = 0; i < scopeChain.ScopeCount; ++i)
+            {
+                scopeChain.ScopeArray[i] = reader->ReadNakedAddr(i != 0);
+            }
+            reader->ReadSequenceEnd();
+
+            reader->ReadRecordEnd();
+        }
+
+#if ENABLE_SNAPSHOT_COMPARE 
+        void AssertSnapEquiv(const SnapFunctionBodyScopeChain& chain1, const SnapFunctionBodyScopeChain& chain2, TTDCompareMap& compareMap)
+        {
+            compareMap.DiagnosticAssert(chain1.ScopeCount == chain2.ScopeCount);
+
+            //Not sure if there is a way to compare the pointer ids in the two scopes 
+        }
+#endif
+
+        //////////////////
+
         void ExtractTopLevelCommonBodyResolveInfo(TopLevelCommonBodyResolveInfo* fbInfo, Js::FunctionBody* fb, uint64 topLevelCtr, Js::ModuleID moduleId, DWORD_PTR documentID, bool isUtf8source, const byte* source, uint32 sourceLen, SlabAllocator& alloc)
         {
             fbInfo->ScriptContextLogId = fb->GetScriptContext()->ScriptContextLogTag;
@@ -941,6 +1053,8 @@ namespace TTD
 
             fbInfo->DbgSerializedBytecodeSize = 0;
             fbInfo->DbgSerializedBytecodeBuffer = nullptr;
+
+            ExtractSnapFunctionBodyScopeChain(false, fbInfo->ScopeChainInfo, fb, alloc);
         }
 
         void EmitTopLevelCommonBodyResolveInfo(const TopLevelCommonBodyResolveInfo* fbInfo, bool emitInline, ThreadContext* threadContext, FileWriter* writer, NSTokens::Separator separator)
@@ -958,6 +1072,9 @@ namespace TTD
             writer->WriteBool(NSTokens::Key::boolVal, fbInfo->IsUtf8, NSTokens::Separator::CommaSeparator);
             writer->WriteLengthValue(fbInfo->ByteLength, NSTokens::Separator::CommaSeparator);
 
+            writer->WriteKey(NSTokens::Key::scopeChain, NSTokens::Separator::CommaSeparator);
+            EmitSnapFunctionBodyScopeChain(fbInfo->ScopeChainInfo, writer);
+
             if(emitInline || IsNullPtrTTString(fbInfo->SourceUri))
             {
                 AssertMsg(!fbInfo->IsUtf8, "Should only emit char16 encoded data in inline mode.");
@@ -986,6 +1103,9 @@ namespace TTD
             fbInfo->ByteLength = reader->ReadLengthValue(true);
             fbInfo->SourceBuffer = alloc.SlabAllocateArray<byte>(fbInfo->ByteLength);
 
+            reader->ReadKey(NSTokens::Key::scopeChain, true);
+            ParseSnapFunctionBodyScopeChain(fbInfo->ScopeChainInfo, reader, alloc);
+
             if(parseInline || IsNullPtrTTString(fbInfo->SourceUri))
             {
                 AssertMsg(!fbInfo->IsUtf8, "Should only emit char16 encoded data in inline mode.");
@@ -1024,6 +1144,8 @@ namespace TTD
             {
                 compareMap.DiagnosticAssert(fbInfo1->SourceBuffer[i] == fbInfo2->SourceBuffer[i]);
             }
+
+            AssertSnapEquiv(fbInfo1->ScopeChainInfo, fbInfo2->ScopeChainInfo, compareMap);
         }
 #endif
 
@@ -1289,6 +1411,8 @@ namespace TTD
                 fbInfo->OptLine = fb->GetLineNumber();
                 fbInfo->OptColumn = fb->GetColumnNumber();
             }
+
+            ExtractSnapFunctionBodyScopeChain(fbInfo->OptKnownPath != TTD_INVALID_WELLKNOWN_TOKEN, fbInfo->ScopeChainInfo, fb, alloc);
         }
 
         void InflateFunctionBody(const FunctionBodyResolveInfo* fbInfo, InflateMap* inflator, const TTDIdentifierDictionary<TTD_PTR_ID, FunctionBodyResolveInfo*>& idToFbResolveMap)
@@ -1358,6 +1482,9 @@ namespace TTD
                     AssertMsg(resfb != nullptr && fbInfo->OptLine == resfb->GetLineNumber() && fbInfo->OptColumn == resfb->GetColumnNumber(), "We are missing something");
                     AssertMsg(resfb != nullptr && (wcscmp(fbInfo->FunctionName.Contents, resfb->GetDisplayName()) == 0 || wcscmp(_u("get"), resfb->GetDisplayName()) == 0 || wcscmp(_u("set"), resfb->GetDisplayName()) == 0), "We are missing something");
                 }
+
+                //Make sure to register any scopes the found function body has (but *not* for well known functions)
+                inflator->UpdateFBScopes(fbInfo->ScopeChainInfo, resfb);
             }
 
             bool updateName = false;
@@ -1403,6 +1530,9 @@ namespace TTD
                 writer->WriteInt64(NSTokens::Key::column, fbInfo->OptColumn, NSTokens::Separator::CommaSeparator);
             }
 
+            writer->WriteKey(NSTokens::Key::scopeChain, NSTokens::Separator::CommaSeparator);
+            EmitSnapFunctionBodyScopeChain(fbInfo->ScopeChainInfo, writer);
+
             writer->WriteRecordEnd();
         }
 
@@ -1432,6 +1562,9 @@ namespace TTD
                 fbInfo->OptColumn = reader->ReadInt64(NSTokens::Key::column, true);
             }
 
+            reader->ReadKey(NSTokens::Key::scopeChain, true);
+            ParseSnapFunctionBodyScopeChain(fbInfo->ScopeChainInfo, reader, alloc);
+
             reader->ReadRecordEnd();
         }
 
@@ -1449,6 +1582,8 @@ namespace TTD
                 compareMap.DiagnosticAssert(fbInfo1->OptLine == fbInfo2->OptLine);
                 compareMap.DiagnosticAssert(fbInfo1->OptColumn == fbInfo2->OptColumn);
             }
+
+            AssertSnapEquiv(fbInfo1->ScopeChainInfo, fbInfo1->ScopeChainInfo, compareMap);
         }
 #endif
 
@@ -1573,16 +1708,19 @@ namespace TTD
                 const TopLevelFunctionInContextRelation& cri = snpCtx->m_loadedTopLevelScriptArray[i];
 
                 Js::FunctionBody* fb = inflator->FindReusableFunctionBodyIfExists(cri.ContextSpecificBodyPtrId);
-                if(fb != nullptr)
+                const TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo = topLevelLoadScriptMap.LookupKnownItem(cri.TopLevelBodyCtr);
+
+                if(fb == nullptr)
                 {
-                    intoCtx->TTDContextInfo->ProcessFunctionBodyOnLoad(fb, nullptr);
-                    intoCtx->TTDContextInfo->RegisterLoadedScript(fb, cri.TopLevelBodyCtr);
+                    fb = NSSnapValues::InflateTopLevelLoadedFunctionBodyInfo(fbInfo, intoCtx);
                 }
                 else
                 {
-                    const TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo = topLevelLoadScriptMap.LookupKnownItem(cri.TopLevelBodyCtr);
-                    fb = NSSnapValues::InflateTopLevelLoadedFunctionBodyInfo(fbInfo, intoCtx);
+                    intoCtx->TTDContextInfo->ProcessFunctionBodyOnLoad(fb, nullptr);
+                    intoCtx->TTDContextInfo->RegisterLoadedScript(fb, cri.TopLevelBodyCtr);
                 }
+
+                inflator->UpdateFBScopes(fbInfo->TopLevelBase.ScopeChainInfo, fb);
                 inflator->AddInflationFunctionBody(cri.ContextSpecificBodyPtrId, fb);
             }
 
@@ -1591,16 +1729,19 @@ namespace TTD
                 const TopLevelFunctionInContextRelation& cri = snpCtx->m_newFunctionTopLevelScriptArray[i];
 
                 Js::FunctionBody* fb = inflator->FindReusableFunctionBodyIfExists(cri.ContextSpecificBodyPtrId);
-                if(fb != nullptr)
+                const TopLevelNewFunctionBodyResolveInfo* fbInfo = topLevelNewScriptMap.LookupKnownItem(cri.TopLevelBodyCtr);
+
+                if(fb == nullptr)
                 {
-                    intoCtx->TTDContextInfo->ProcessFunctionBodyOnLoad(fb, nullptr);
-                    intoCtx->TTDContextInfo->RegisterNewScript(fb, cri.TopLevelBodyCtr);
+                    fb = NSSnapValues::InflateTopLevelNewFunctionBodyInfo(fbInfo, intoCtx);
                 }
                 else
                 {
-                    const TopLevelNewFunctionBodyResolveInfo* fbInfo = topLevelNewScriptMap.LookupKnownItem(cri.TopLevelBodyCtr);
-                    fb = NSSnapValues::InflateTopLevelNewFunctionBodyInfo(fbInfo, intoCtx);
+                    intoCtx->TTDContextInfo->ProcessFunctionBodyOnLoad(fb, nullptr);
+                    intoCtx->TTDContextInfo->RegisterNewScript(fb, cri.TopLevelBodyCtr);
                 }
+
+                inflator->UpdateFBScopes(fbInfo->TopLevelBase.ScopeChainInfo, fb);
                 inflator->AddInflationFunctionBody(cri.ContextSpecificBodyPtrId, fb);
             }
 
@@ -1609,16 +1750,19 @@ namespace TTD
                 const TopLevelFunctionInContextRelation& cri = snpCtx->m_evalTopLevelScriptArray[i];
 
                 Js::FunctionBody* fb = inflator->FindReusableFunctionBodyIfExists(cri.ContextSpecificBodyPtrId);
-                if(fb != nullptr)
+                const TopLevelEvalFunctionBodyResolveInfo* fbInfo = topLevelEvalScriptMap.LookupKnownItem(cri.TopLevelBodyCtr);
+
+                if(fb == nullptr)
                 {
-                    intoCtx->TTDContextInfo->ProcessFunctionBodyOnLoad(fb, nullptr);
-                    intoCtx->TTDContextInfo->RegisterEvalScript(fb, cri.TopLevelBodyCtr);
+                    fb = NSSnapValues::InflateTopLevelEvalFunctionBodyInfo(fbInfo, intoCtx);
                 }
                 else
                 {
-                    const TopLevelEvalFunctionBodyResolveInfo* fbInfo = topLevelEvalScriptMap.LookupKnownItem(cri.TopLevelBodyCtr);
-                    fb = NSSnapValues::InflateTopLevelEvalFunctionBodyInfo(fbInfo, intoCtx);
+                    intoCtx->TTDContextInfo->ProcessFunctionBodyOnLoad(fb, nullptr);
+                    intoCtx->TTDContextInfo->RegisterEvalScript(fb, cri.TopLevelBodyCtr);
                 }
+
+                inflator->UpdateFBScopes(fbInfo->TopLevelBase.ScopeChainInfo, fb);
                 inflator->AddInflationFunctionBody(cri.ContextSpecificBodyPtrId, fb);
             }
         }

+ 32 - 16
lib/Runtime/Debug/TTSnapValues.h

@@ -8,7 +8,6 @@
 
 namespace TTD
 {
-#ifdef WIN32
     class TTDTimer
     {
     private:
@@ -30,21 +29,7 @@ namespace TTD
         {
             return this->m_timer.Now();
         }
-
-    };
-#else
-    //
-    //TODO: x-plat workaround need to link in with timer when it is done.
-    //
-    class TTDTimer
-    {
-    public:
-        double Now()
-        {
-            return 0.0;
-        }
     };
-#endif
 
     namespace JsSupport
     {
@@ -157,13 +142,17 @@ namespace TTD
 
 #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
             Js::PropertyId* DebugPIDArray;
+
+            int32 OptDiagDebugScopeBegin;
+            int32 OptDiagDebugScopeEnd;
 #endif
 
             //The meta-data for the slot array
             bool isFunctionBodyMetaData;
 
             TTD_PTR_ID OptFunctionBodyId;
-            //TODO: add debugger scope meta-data info
+            TTD_PTR_ID OptDebugScopeId;
+            TTD_WELLKNOWN_TOKEN OptWellKnownDbgScope;
         };
 
         Js::Var* InflateSlotArrayInfo(const SlotArrayInfo* slotInfo, InflateMap* inflator);
@@ -254,6 +243,27 @@ namespace TTD
 
         //////////////////
 
+        //Information on the scopechain for a function body
+        struct SnapFunctionBodyScopeChain
+        {
+            //The number of scopes associated with this function body
+            uint32 ScopeCount;
+
+            //The Ids of the scopes
+            TTD_PTR_ID* ScopeArray;
+        };
+
+        void ExtractSnapFunctionBodyScopeChain(bool isWellKnownFunction, SnapFunctionBodyScopeChain& scopeChain, Js::FunctionBody* fb, SlabAllocator& alloc);
+
+        void EmitSnapFunctionBodyScopeChain(const SnapFunctionBodyScopeChain& scopeChain, FileWriter* writer);
+        void ParseSnapFunctionBodyScopeChain(SnapFunctionBodyScopeChain& scopeChain, FileReader* reader, SlabAllocator& alloc);
+
+#if ENABLE_SNAPSHOT_COMPARE 
+        void AssertSnapEquiv(const SnapFunctionBodyScopeChain& chain1, const SnapFunctionBodyScopeChain& chain2, TTDCompareMap& compareMap);
+#endif
+
+        //////////////////
+
         //Information that is common to all top-level bodies
         struct TopLevelCommonBodyResolveInfo
         {
@@ -278,6 +288,9 @@ namespace TTD
             uint32 ByteLength;
             byte* SourceBuffer;
 
+            //The (possibly empty) scope chain info
+            SnapFunctionBodyScopeChain ScopeChainInfo;
+
             //The number of bytes (or -1 if not set) and the buffer for the serialized bytecode
             mutable DWORD DbgSerializedBytecodeSize;
             mutable byte* DbgSerializedBytecodeBuffer;
@@ -375,6 +388,9 @@ namespace TTD
 
             //The column number the function is def starts on
             int64 OptColumn;
+
+            //The (possibly empty) scope chain info
+            SnapFunctionBodyScopeChain ScopeChainInfo;
         };
 
         void ExtractFunctionBodyInfo(FunctionBodyResolveInfo* fbInfo, Js::FunctionBody* fb, bool isWellKnown, SlabAllocator& alloc);

+ 11 - 0
lib/Runtime/Debug/TTSnapshot.cpp

@@ -309,6 +309,17 @@ namespace TTD
         return this->m_slotArrayEntries.Count();
     }
 
+    uint32 SnapShot::GetDbgScopeCountNonTopLevel() const
+    {
+        uint32 dbgScopeCount = 0;
+        for(auto iter = this->m_functionBodyList.GetIterator(); iter.IsValid(); iter.MoveNext()) 
+        {
+            dbgScopeCount += iter.Current()->ScopeChainInfo.ScopeCount;
+        }
+
+        return dbgScopeCount;
+    }
+
     UnorderedArrayList<NSSnapValues::SnapContext, TTD_ARRAY_LIST_SIZE_XSMALL>& SnapShot::GetContextList()
     {
         return this->m_ctxList;

+ 1 - 0
lib/Runtime/Debug/TTSnapshot.h

@@ -122,6 +122,7 @@ namespace TTD
         uint32 ObjectCount() const;
         uint32 EnvCount() const;
         uint32 SlotArrayCount() const;
+        uint32 GetDbgScopeCountNonTopLevel() const;
 
         //Get the context list for this snapshot
         UnorderedArrayList<NSSnapValues::SnapContext, TTD_ARRAY_LIST_SIZE_XSMALL>& GetContextList();

+ 22 - 1
lib/Runtime/Debug/TTSnapshotExtractor.cpp

@@ -114,6 +114,8 @@ namespace TTD
 
                 slotInfo->isFunctionBodyMetaData = true;
                 slotInfo->OptFunctionBodyId = TTD_CONVERT_FUNCTIONBODY_TO_PTR_ID(fb);
+                slotInfo->OptDebugScopeId = TTD_INVALID_PTR_ID;
+                slotInfo->OptWellKnownDbgScope = TTD_INVALID_WELLKNOWN_TOKEN;
 
 #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
                 Js::PropertyId* propertyIds = fb->GetPropertyIdsForScopeSlotArray();
@@ -123,20 +125,39 @@ namespace TTD
                 {
                     slotInfo->DebugPIDArray[j] = propertyIds[j];
                 }
+
+                slotInfo->OptDiagDebugScopeBegin = -1;
+                slotInfo->OptDiagDebugScopeEnd = -1;
 #endif
             }
             else
             {
+                Js::DebuggerScope* dbgScope = slots.GetDebuggerScope();
                 slotInfo->isFunctionBodyMetaData = false;
                 slotInfo->OptFunctionBodyId = TTD_INVALID_PTR_ID;
 
+                TTD_WELLKNOWN_TOKEN wellKnownToken = ctx->TTDWellKnownInfo->ResolvePathForKnownDbgScopeIfExists(dbgScope);
+                if(wellKnownToken == TTD_INVALID_WELLKNOWN_TOKEN)
+                {
+                    slotInfo->OptDebugScopeId = TTD_CONVERT_DEBUGSCOPE_TO_PTR_ID(dbgScope);
+                    slotInfo->OptWellKnownDbgScope = TTD_INVALID_WELLKNOWN_TOKEN;
+                }
+                else
+                {
+                    slotInfo->OptDebugScopeId = TTD_INVALID_PTR_ID;
+                    slotInfo->OptWellKnownDbgScope = wellKnownToken;
+                }
+
 #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
                 slotInfo->DebugPIDArray = this->m_pendingSnap->GetSnapshotSlabAllocator().SlabAllocateArray<Js::PropertyId>(slotInfo->SlotCount);
 
                 for(uint32 j = 0; j < slotInfo->SlotCount; ++j)
                 {
-                    slotInfo->DebugPIDArray[j] = (Js::PropertyId)0;
+                    slotInfo->DebugPIDArray[j] = dbgScope->GetPropertyIdForSlotIndex_TTD(j);
                 }
+
+                slotInfo->OptDiagDebugScopeBegin = dbgScope->GetStart();
+                slotInfo->OptDiagDebugScopeEnd = dbgScope->GetEnd();
 #endif
             }
 

+ 2 - 0
lib/Runtime/Debug/TTSupport.h

@@ -29,6 +29,7 @@ namespace TTD
         struct SnapPrimitiveValue;
         struct SlotArrayInfo;
         struct ScriptFunctionScopeInfo;
+        struct SnapFunctionBodyScopeChain;
         struct TopLevelScriptLoadFunctionBodyResolveInfo;
         struct TopLevelNewFunctionBodyResolveInfo;
         struct TopLevelEvalFunctionBodyResolveInfo;
@@ -79,6 +80,7 @@ typedef uint64 TTD_PTR_ID;
 #define TTD_CONVERT_ENV_TO_PTR_ID(X) reinterpret_cast<TTD_PTR_ID>(X)
 #define TTD_CONVERT_SLOTARRAY_TO_PTR_ID(X) reinterpret_cast<TTD_PTR_ID>(X)
 #define TTD_CONVERT_SCOPE_TO_PTR_ID(X) reinterpret_cast<TTD_PTR_ID>(X)
+#define TTD_CONVERT_DEBUGSCOPE_TO_PTR_ID(X) reinterpret_cast<TTD_PTR_ID>(X)
 
 //Promises have a wide range of heap allocated bits -- we define He-Man casts for all of them -- ugly but so is having a bunch of specific functions
 #define TTD_CONVERT_PROMISE_INFO_TO_PTR_ID(X) reinterpret_cast<TTD_PTR_ID>(X)

+ 2 - 2
lib/Runtime/Library/JavascriptLibrary.cpp

@@ -4771,10 +4771,10 @@ namespace Js
         return this->CreateDate(value);
     }
 
-    Js::RecyclableObject* JavascriptLibrary::CreateRegex_TTD(const char16* patternSource, uint32 patternLength, UnifiedRegex::RegexFlags flags, CharCount lastIndex)
+    Js::RecyclableObject* JavascriptLibrary::CreateRegex_TTD(const char16* patternSource, uint32 patternLength, UnifiedRegex::RegexFlags flags, CharCount lastIndex, Js::Var lastVar)
     {
         Js::JavascriptRegExp* re = Js::JavascriptRegExp::CreateRegEx(patternSource, patternLength, flags, this->scriptContext);
-        re->SetLastIndex(lastIndex);
+        re->SetLastIndexInfo_TTD(lastIndex, lastVar);
 
         return re;
     }

+ 1 - 1
lib/Runtime/Library/JavascriptLibrary.h

@@ -644,7 +644,7 @@ namespace Js
         void SetBoxedObjectValue_TTD(Js::RecyclableObject* obj, Js::Var value);
 
         Js::RecyclableObject* CreateDate_TTD(double value);
-        Js::RecyclableObject* CreateRegex_TTD(const char16* patternSource, uint32 patternLength, UnifiedRegex::RegexFlags flags, CharCount lastIndex);
+        Js::RecyclableObject* CreateRegex_TTD(const char16* patternSource, uint32 patternLength, UnifiedRegex::RegexFlags flags, CharCount lastIndex, Js::Var lastVar);
         Js::RecyclableObject* CreateError_TTD();
 
         Js::RecyclableObject* CreateES5Array_TTD();

+ 8 - 1
lib/Runtime/Library/JavascriptRegularExpression.cpp

@@ -1526,9 +1526,16 @@ namespace Js
         //split regex should be automatically generated from regex string and flags so no need to exttract it as well
 
         sri->Flags = this->GetFlags();
-        sri->LastIndexOrFlag = this->GetLastIndex();
+        sri->LastIndexVar = TTD_CONVERT_JSVAR_TO_TTDVAR(this->lastIndexVar);
+        sri->LastIndexOrFlag = this->lastIndexOrFlag;
 
         TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapRegexInfo*, TTD::NSSnapObjects::SnapObjectType::SnapRegexObject>(objData, sri);
     }
+
+    void JavascriptRegExp::SetLastIndexInfo_TTD(CharCount lastIndex, Js::Var lastVar)
+    {
+        this->lastIndexOrFlag = lastIndex;
+        this->lastIndexVar = lastVar;
+    }
 #endif
 } // namespace Js

+ 2 - 0
lib/Runtime/Library/JavascriptRegularExpression.h

@@ -206,6 +206,8 @@ namespace Js
     public:
         virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
         virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
+
+        void SetLastIndexInfo_TTD(CharCount lastIndex, Js::Var lastVar);
 #endif
     };
 

+ 4 - 0
lib/Runtime/Library/ScriptFunction.cpp

@@ -558,6 +558,10 @@ namespace Js
                 {
                     rctxInfo->EnqueueNewFunctionBodyObject(this, slotArray.GetFunctionBody(), scopePathString.GetStrValue());
                 }
+                else
+                {
+                    rctxInfo->AddWellKnownDebuggerScopePath(this, slotArray.GetDebuggerScope(), i);
+                }
 
                 for(uint j = 0; j < slotArrayCount; j++)
                 {

+ 3 - 0
test/TTBasic/regex.js

@@ -5,8 +5,10 @@
 
 var x = /World/;
 var y = new RegExp("l", "g");
+var z = new RegExp("l", "g");
 
 y.exec("Hello World");
+z.lastIndex = -1;
 
 WScript.SetTimeout(testFunction, 50);
 
@@ -15,6 +17,7 @@ WScript.SetTimeout(testFunction, 50);
 function testFunction()
 {
     telemetryLog(`y.lastIndex: ${y.lastIndex}`, true); //3
+    telemetryLog(`z.lastIndex: ${z.lastIndex}`, true); //3
 
     ////
     var m = "Hello World".match(x);

+ 1 - 0
test/TTBasic/regexRecord.baseline

@@ -1,3 +1,4 @@
 y.lastIndex: 3
+z.lastIndex: -1
 m.index: 6
 post update -- y.lastIndex: 4

+ 1 - 0
test/TTBasic/regexReplay.baseline

@@ -1,4 +1,5 @@
 y.lastIndex: 3
+z.lastIndex: -1
 m.index: 6
 post update -- y.lastIndex: 4