2
0
Эх сурвалжийг харах

Support JsDiagGetProperties on functionHandle returned in JsDiagGetStackTrace

Sandeep Agarwal 8 жил өмнө
parent
commit
b9ee9241fc

+ 41 - 0
bin/ch/DbgController.js

@@ -781,6 +781,44 @@ var controllerObj = (function () {
                     'sources': sources
                 });
             },
+            dumpFunctionProperties: function (frameIdOrArrayOfIds = [0], expandLevel = 0) {
+                if (typeof frameIdOrArrayOfIds != "number" && !(frameIdOrArrayOfIds instanceof Array)) {
+                    frameIdOrArrayOfIds = [0];
+                }
+                if (typeof expandLevel != "number" || expandLevel < 0) {
+                    expandLevel = 0;
+                }
+                let stackTrace = callHostFunction(hostDebugObject.JsDiagGetStackTrace);
+                let functionHandles = [];
+                let requestedFrameIndexes = [];
+                if (typeof frameIdOrArrayOfIds === "number") {
+                    requestedFrameIndexes.push(frameIdOrArrayOfIds);
+                } else if (frameIdOrArrayOfIds instanceof Array) {
+                    frameIdOrArrayOfIds.forEach((s) => {
+                        if (typeof s === "number") {
+                            requestedFrameIndexes.push(s);
+                        }
+                    });
+                }
+                if (requestedFrameIndexes.length == 0) {
+                    requestedFrameIndexes.push(0);
+                }
+
+                stackTrace.forEach((stackFrame) => {
+                    let stackFrameIndex = stackFrame.index;
+                    if (requestedFrameIndexes.includes(stackFrameIndex) && !functionHandles.includes(stackFrame.functionHandle)) {
+                        functionHandles.push(stackFrame.functionHandle);
+                    }
+                });
+
+                let functionProperties = [];
+                functionHandles.forEach((handle) => {
+                    functionProperties.push(GetChild({ handle: handle }, expandLevel));
+                });
+                recordEvent({
+                    'functionProperties': functionProperties
+                });
+            },
             trace: function (traceFlag) {
                 _trace |= traceFlag;
             }
@@ -925,6 +963,9 @@ function dumpBreak() {
 function dumpSourceList() {
     controllerObj.pushCommand(controllerObj.debuggerCommands.dumpSourceList, arguments);
 }
+function dumpFunctionProperties() {
+    controllerObj.pushCommand(controllerObj.debuggerCommands.dumpFunctionProperties, arguments);
+}
 
 // Start internal tracing. E.g.: /**bp:trace(TRACE_COMMANDS)**/
 function trace() {

+ 1 - 0
bin/ch/stdafx.h

@@ -218,6 +218,7 @@ public:
         if (errorCode == JsNoError)
         {
             *(data + length) = char(0);
+            this->length = length;
         }
         return errorCode;
     }

+ 65 - 17
lib/Jsrt/JsrtDebuggerObject.cpp

@@ -258,7 +258,14 @@ Js::DynamicObject * JsrtDebuggerStackFrame::GetJSONObject(Js::ScriptContext* scr
     JsrtDebugUtils::AddLineColumnToObject(stackTraceObject, functionBody, currentByteCodeOffset);
     JsrtDebugUtils::AddSourceLengthAndTextToObject(stackTraceObject, functionBody, currentByteCodeOffset);
 
-    JsrtDebuggerObjectBase* functionObject = JsrtDebuggerObjectFunction::Make(this->debuggerObjectsManager, functionBody);
+    Js::JavascriptFunction* javascriptFunction = stackFrame->GetJavascriptFunction();
+    JsrtDebuggerObjectBase* functionObject = nullptr;
+
+    if (!this->debuggerObjectsManager->TryGetDataFromDataToDebuggerObjectsDictionary(javascriptFunction, &functionObject))
+    {
+        functionObject = JsrtDebuggerObjectFunction::Make(this->debuggerObjectsManager, javascriptFunction);
+    }
+
     JsrtDebugUtils::AddPropertyToObject(stackTraceObject, JsrtDebugPropertyId::functionHandle, functionObject->GetHandle(), frameScriptContext);
 
     return stackTraceObject;
@@ -661,30 +668,40 @@ Js::DynamicObject * JsrtDebuggerObjectScope::GetChildren(Js::ScriptContext * scr
     return childrens;
 }
 
-JsrtDebuggerObjectFunction::JsrtDebuggerObjectFunction(JsrtDebuggerObjectsManager* debuggerObjectsManager, Js::FunctionBody* functionBody) :
+JsrtDebuggerObjectFunction::JsrtDebuggerObjectFunction(JsrtDebuggerObjectsManager* debuggerObjectsManager, Js::JavascriptFunction* javascriptFunction) :
     JsrtDebuggerObjectBase(JsrtDebuggerObjectType::Function, debuggerObjectsManager),
-    functionBody(functionBody)
+    javascriptFunction(javascriptFunction),
+    objectDisplay(nullptr),
+    walkerRef(nullptr)
 {
 }
 
 JsrtDebuggerObjectFunction::~JsrtDebuggerObjectFunction()
 {
-    this->functionBody = nullptr;
+    if (this->objectDisplay != nullptr)
+    {
+        HeapDelete(this->objectDisplay);
+        this->objectDisplay = nullptr;
+    }
+
+    if (this->walkerRef != nullptr)
+    {
+        HeapDelete(this->walkerRef);
+        this->walkerRef = nullptr;
+    }
+
+    this->javascriptFunction = nullptr;
 }
 
-JsrtDebuggerObjectBase * JsrtDebuggerObjectFunction::Make(JsrtDebuggerObjectsManager * debuggerObjectsManager, Js::FunctionBody * functionBody)
+JsrtDebuggerObjectBase * JsrtDebuggerObjectFunction::Make(JsrtDebuggerObjectsManager * debuggerObjectsManager, Js::JavascriptFunction* javascriptFunction)
 {
     JsrtDebuggerObjectBase* debuggerObject = nullptr;
 
-    if (debuggerObjectsManager->TryGetDataFromDataToDebuggerObjectsDictionary(functionBody, &debuggerObject))
-    {
-        Assert(debuggerObject != nullptr);
-        return debuggerObject;
-    }
+    Assert(!debuggerObjectsManager->TryGetDataFromDataToDebuggerObjectsDictionary(javascriptFunction, &debuggerObject));
+    
+    debuggerObject = Anew(debuggerObjectsManager->GetDebugObjectArena(), JsrtDebuggerObjectFunction, debuggerObjectsManager, javascriptFunction);
 
-    debuggerObject = Anew(debuggerObjectsManager->GetDebugObjectArena(), JsrtDebuggerObjectFunction, debuggerObjectsManager, functionBody);
-
-    debuggerObjectsManager->AddToDataToDebuggerObjectsDictionary(functionBody, debuggerObject);
+    debuggerObjectsManager->AddToDataToDebuggerObjectsDictionary(javascriptFunction, debuggerObject);
 
     return debuggerObject;
 }
@@ -693,16 +710,47 @@ Js::DynamicObject * JsrtDebuggerObjectFunction::GetJSONObject(Js::ScriptContext
 {
     Js::DynamicObject* functionObject = scriptContext->GetLibrary()->CreateObject();
 
-    JsrtDebugUtils::AddScriptIdToObject(functionObject, this->functionBody->GetUtf8SourceInfo());
-    JsrtDebugUtils::AddPropertyToObject(functionObject, JsrtDebugPropertyId::line, (uint32) this->functionBody->GetLineNumber(), scriptContext);
-    JsrtDebugUtils::AddPropertyToObject(functionObject, JsrtDebugPropertyId::column, (uint32) this->functionBody->GetColumnNumber(), scriptContext);
-    JsrtDebugUtils::AddPropertyToObject(functionObject, JsrtDebugPropertyId::name, this->functionBody->GetDisplayName(), this->functionBody->GetDisplayNameLength(), scriptContext);
+    Js::FunctionBody* functionBody  = this->javascriptFunction->GetFunctionBody();
+    JsrtDebugUtils::AddScriptIdToObject(functionObject, functionBody->GetUtf8SourceInfo());
+    JsrtDebugUtils::AddPropertyToObject(functionObject, JsrtDebugPropertyId::line, (uint32)functionBody->GetLineNumber(), scriptContext);
+    JsrtDebugUtils::AddPropertyToObject(functionObject, JsrtDebugPropertyId::column, (uint32)functionBody->GetColumnNumber(), scriptContext);
+    JsrtDebugUtils::AddPropertyToObject(functionObject, JsrtDebugPropertyId::name, functionBody->GetDisplayName(), functionBody->GetDisplayNameLength(), scriptContext);
     JsrtDebugUtils::AddPropertyToObject(functionObject, JsrtDebugPropertyId::type, scriptContext->GetLibrary()->GetFunctionTypeDisplayString(), scriptContext);
     JsrtDebugUtils::AddPropertyToObject(functionObject, JsrtDebugPropertyId::handle, this->GetHandle(), scriptContext);
 
     return functionObject;
 }
 
+Js::DynamicObject * JsrtDebuggerObjectFunction::GetChildren(Js::ScriptContext * scriptContext, uint fromCount, uint totalCount)
+{
+    if (this->objectDisplay == nullptr)
+    {
+        Js::ResolvedObject functionResolvedObject;
+        functionResolvedObject.obj = this->javascriptFunction;
+        functionResolvedObject.scriptContext = scriptContext;
+        functionResolvedObject.name = _u("Function");
+        functionResolvedObject.typeId = Js::JavascriptOperators::GetTypeId(functionResolvedObject.obj);
+        this->objectDisplay = functionResolvedObject.GetObjectDisplay();
+    }
+
+    Js::IDiagObjectModelDisplay* objectDisplayRef = this->objectDisplay->GetStrongReference();
+    if (objectDisplayRef == nullptr)
+    {
+        return nullptr;
+    }
+
+    if (this->walkerRef == nullptr)
+    {
+        this->walkerRef = objectDisplayRef->CreateWalker();
+    }
+
+    Js::DynamicObject* childrens = __super::GetChildren(this->walkerRef, scriptContext, fromCount, totalCount);
+
+    this->objectDisplay->ReleaseStrongReference();
+
+    return childrens;
+}
+
 JsrtDebuggerObjectGlobalsNode::JsrtDebuggerObjectGlobalsNode(JsrtDebuggerObjectsManager* debuggerObjectsManager, WeakArenaReference<Js::IDiagObjectModelDisplay>* objectDisplay) :
     JsrtDebuggerObjectBase(JsrtDebuggerObjectType::Globals, debuggerObjectsManager),
     objectDisplay(objectDisplay),

+ 6 - 3
lib/Jsrt/JsrtDebuggerObject.h

@@ -58,13 +58,16 @@ private:
 class JsrtDebuggerObjectFunction : public JsrtDebuggerObjectBase
 {
 public:
-    static JsrtDebuggerObjectBase* Make(JsrtDebuggerObjectsManager* debuggerObjectsManager, Js::FunctionBody* functionBody);
+    static JsrtDebuggerObjectBase* Make(JsrtDebuggerObjectsManager* debuggerObjectsManager, Js::JavascriptFunction* javascriptFunction);
     Js::DynamicObject* GetJSONObject(Js::ScriptContext* scriptContext, bool forceSetValueProp);
+    Js::DynamicObject* GetChildren(Js::ScriptContext* scriptContext, uint fromCount, uint totalCount);
 
 private:
-    JsrtDebuggerObjectFunction(JsrtDebuggerObjectsManager* debuggerObjectsManager, Js::FunctionBody* functionBody);
+    JsrtDebuggerObjectFunction(JsrtDebuggerObjectsManager* debuggerObjectsManager, Js::JavascriptFunction* javascriptFunction);
     ~JsrtDebuggerObjectFunction();
-    Js::FunctionBody* functionBody;
+    Js::JavascriptFunction* javascriptFunction;
+    WeakArenaReference<Js::IDiagObjectModelDisplay>* objectDisplay;
+    WeakArenaReference<Js::IDiagObjectModelWalkerBase>* walkerRef;
 };
 
 class JsrtDebuggerObjectProperty : public JsrtDebuggerObjectBase

+ 41 - 0
test/Debugger/dumpFunctionProperties.js

@@ -0,0 +1,41 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+function foo() {
+  var x = 1; /**bp:dumpFunctionProperties();**/
+}
+foo();
+
+(function () {
+  var x = 1; /**bp:dumpFunctionProperties(0, 1);**/
+})();
+
+var arr = [0];
+arr.forEach((s) => {
+  var x = 1; /**bp:dumpFunctionProperties([0], '0');**/
+});
+
+function same(shouldBreak) {
+  if (shouldBreak) {
+    // 0 is same(true), 1 is same(false), 2 is global function). same is dumped only once as functionHandle for frame 0 and 1 is same.
+    var x = 1; /**bp:stack();dumpFunctionProperties([0,1,2]);**/
+  } else {
+    same(!shouldBreak);
+  }
+}
+same(false);
+
+function one(arg1) {
+  two();
+}
+function two(arg1, arg2) {
+  three();
+}
+function three(arg1, arg2, arg3) {
+  var x = 1; /**bp:stack();dumpFunctionProperties([0,1,2,3], 0);**/
+}
+one();
+
+WScript.Echo("pass");

+ 159 - 0
test/Debugger/dumpFunctionProperties.js.dbg.baseline

@@ -0,0 +1,159 @@
+[
+  {
+    "functionProperties": [
+      {
+        "#__proto__": "function <large string>",
+        "prototype": "Object {...}",
+        "name": "string foo",
+        "caller": "object null",
+        "arguments": "Object {...}",
+        "length": "number 0"
+      }
+    ]
+  },
+  {
+    "functionProperties": [
+      {
+        "#__proto__": {
+          "#__proto__": "Object {...}",
+          "constructor": "function <large string>",
+          "length": "number 0",
+          "name": "string ",
+          "apply": "function <large string>",
+          "bind": "function <large string>",
+          "call": "function <large string>",
+          "toString": "function <large string>",
+          "Symbol.hasInstance": "function <large string>",
+          "caller": "Error <large string>",
+          "arguments": "Error <large string>"
+        },
+        "prototype": {
+          "#__proto__": "Object {...}",
+          "constructor": "function <large string>"
+        },
+        "caller": "object null",
+        "arguments": {
+          "#__proto__": "Object {...}",
+          "length": "number 0",
+          "callee": "function <large string>",
+          "Symbol.iterator": "function <large string>"
+        },
+        "length": "number 0"
+      }
+    ]
+  },
+  {
+    "functionProperties": [
+      {
+        "#__proto__": "function <large string>",
+        "length": "number 1"
+      }
+    ]
+  },
+  {
+    "callStack": [
+      {
+        "line": 22,
+        "column": 4,
+        "sourceText": "var x = 1",
+        "function": "same"
+      },
+      {
+        "line": 24,
+        "column": 4,
+        "sourceText": "same(!shouldBreak)",
+        "function": "same"
+      },
+      {
+        "line": 27,
+        "column": 0,
+        "sourceText": "same(false)",
+        "function": "Global code"
+      }
+    ]
+  },
+  {
+    "functionProperties": [
+      {
+        "#__proto__": "function <large string>",
+        "prototype": "Object {...}",
+        "name": "string same",
+        "caller": "function <large string>",
+        "arguments": "Object {...}",
+        "length": "number 1"
+      },
+      {
+        "#__proto__": "function <large string>",
+        "prototype": "Object {...}",
+        "name": "string ",
+        "caller": "object null",
+        "arguments": "object null",
+        "length": "number -1"
+      }
+    ]
+  },
+  {
+    "callStack": [
+      {
+        "line": 36,
+        "column": 2,
+        "sourceText": "var x = 1",
+        "function": "three"
+      },
+      {
+        "line": 33,
+        "column": 2,
+        "sourceText": "three()",
+        "function": "two"
+      },
+      {
+        "line": 30,
+        "column": 2,
+        "sourceText": "two()",
+        "function": "one"
+      },
+      {
+        "line": 38,
+        "column": 0,
+        "sourceText": "one()",
+        "function": "Global code"
+      }
+    ]
+  },
+  {
+    "functionProperties": [
+      {
+        "#__proto__": "function <large string>",
+        "prototype": "Object {...}",
+        "name": "string three",
+        "caller": "function <large string>",
+        "arguments": "Object {...}",
+        "length": "number 3"
+      },
+      {
+        "#__proto__": "function <large string>",
+        "prototype": "Object {...}",
+        "name": "string two",
+        "caller": "function <large string>",
+        "arguments": "Object {...}",
+        "length": "number 2"
+      },
+      {
+        "#__proto__": "function <large string>",
+        "prototype": "Object {...}",
+        "name": "string one",
+        "caller": "object null",
+        "arguments": "Object {...}",
+        "length": "number 1"
+      },
+      {
+        "#__proto__": "function <large string>",
+        "prototype": "Object {...}",
+        "name": "string ",
+        "caller": "object null",
+        "arguments": "object null",
+        "length": "number -1"
+      }
+    ]
+  }
+]

+ 6 - 0
test/Debugger/rlexe.xml

@@ -68,4 +68,10 @@
       <files>MultipleContextStack.js</files>
     </default>
   </test>
+  <test>
+    <default>
+      <compile-flags>-debuglaunch -dbgbaseline:dumpFunctionProperties.js.dbg.baseline</compile-flags>
+      <files>dumpFunctionProperties.js</files>
+    </default>
+  </test>
 </regress-exe>