未验证 提交 1c09d36e 编写于 作者: T Thays Grazia 提交者: GitHub

[wasm][debugger] Run getter using Runtime.GetProperties (#62857)

* Implementing support on running getters using Runtime.GetProperties as it's done by chrome.

* Addressing @radical comments.

* testing datetime
上级 13f938fe
......@@ -60,36 +60,59 @@ public MessageId(string sessionId, int id)
internal class DotnetObjectId
{
public string Scheme { get; }
public string Value { get; }
public int Value { get; }
public int SubValue { get; set; }
public static bool TryParse(JToken jToken, out DotnetObjectId objectId) => TryParse(jToken?.Value<string>(), out objectId);
public static bool TryParse(string id, out DotnetObjectId objectId)
{
objectId = null;
if (id == null)
return false;
if (!id.StartsWith("dotnet:"))
return false;
try {
if (id == null)
return false;
string[] parts = id.Split(":", 3);
if (!id.StartsWith("dotnet:"))
return false;
if (parts.Length < 3)
return false;
string[] parts = id.Split(":");
objectId = new DotnetObjectId(parts[1], parts[2]);
if (parts.Length < 3)
return false;
return true;
objectId = new DotnetObjectId(parts[1], int.Parse(parts[2]));
switch (objectId.Scheme)
{
case "methodId":
{
parts = id.Split(":");
objectId.SubValue = int.Parse(parts[3]);
break;
}
}
return true;
}
catch (Exception)
{
return false;
}
}
public DotnetObjectId(string scheme, string value)
public DotnetObjectId(string scheme, int value)
{
Scheme = scheme;
Value = value;
}
public override string ToString() => $"dotnet:{Scheme}:{Value}";
public override string ToString()
{
switch (Scheme)
{
case "methodId":
return $"dotnet:{Scheme}:{Value}:{SubValue}";
}
return $"dotnet:{Scheme}:{Value}";
}
}
public struct Result
......
......@@ -53,7 +53,7 @@ public async Task<JObject> GetValueFromObject(JToken objRet, CancellationToken t
{
if (DotnetObjectId.TryParse(objRet?["value"]?["objectId"]?.Value<string>(), out DotnetObjectId objectId))
{
var exceptionObject = await context.SdbAgent.GetObjectValues(int.Parse(objectId.Value), GetObjectCommandOptions.WithProperties | GetObjectCommandOptions.OwnProperties, token);
var exceptionObject = await context.SdbAgent.GetObjectValues(objectId.Value, GetObjectCommandOptions.WithProperties | GetObjectCommandOptions.OwnProperties, token);
var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value<string>().Equals("_message"));
exceptionObjectMessage["value"]["value"] = objRet["value"]?["className"]?.Value<string>() + ": " + exceptionObjectMessage["value"]?["value"]?.Value<string>();
return exceptionObjectMessage["value"]?.Value<JObject>();
......@@ -342,17 +342,15 @@ public async Task<JObject> Resolve(ElementAccessExpressionSyntax elementAccess,
switch (objectId.Scheme)
{
case "array":
rootObject["value"] = await context.SdbAgent.GetArrayValues(int.Parse(objectId.Value), token);
rootObject["value"] = await context.SdbAgent.GetArrayValues(objectId.Value, token);
return (JObject)rootObject["value"][elementIdx]["value"];
case "object":
var typeIds = await context.SdbAgent.GetTypeIdFromObject(int.Parse(objectId.Value), true, token);
var typeIds = await context.SdbAgent.GetTypeIdFromObject(objectId.Value, true, token);
int methodId = await context.SdbAgent.GetMethodIdByName(typeIds[0], "ToArray", token);
var commandParamsObjWriter = new MonoBinaryWriter();
commandParamsObjWriter.WriteObj(objectId, context.SdbAgent);
var toArrayRetMethod = await context.SdbAgent.InvokeMethod(commandParamsObjWriter.GetParameterBuffer(), methodId, elementAccess.Expression.ToString(), token);
var toArrayRetMethod = await context.SdbAgent.InvokeMethodInObject(objectId.Value, methodId, elementAccess.Expression.ToString(), token);
rootObject = await GetValueFromObject(toArrayRetMethod, token);
DotnetObjectId.TryParse(rootObject?["objectId"]?.Value<string>(), out DotnetObjectId arrayObjectId);
rootObject["value"] = await context.SdbAgent.GetArrayValues(int.Parse(arrayObjectId.Value), token);
rootObject["value"] = await context.SdbAgent.GetArrayValues(arrayObjectId.Value, token);
return (JObject)rootObject["value"][elementIdx]["value"];
default:
throw new InvalidOperationException($"Cannot apply indexing with [] to an expression of type '{objectId.Scheme}'");
......@@ -391,7 +389,7 @@ public async Task<JObject> Resolve(InvocationExpressionSyntax method, Dictionary
if (rootObject != null)
{
DotnetObjectId.TryParse(rootObject?["objectId"]?.Value<string>(), out DotnetObjectId objectId);
var typeIds = await context.SdbAgent.GetTypeIdFromObject(int.Parse(objectId.Value), true, token);
var typeIds = await context.SdbAgent.GetTypeIdFromObject(objectId.Value, true, token);
int methodId = await context.SdbAgent.GetMethodIdByName(typeIds[0], methodName, token);
var className = await context.SdbAgent.GetTypeNameOriginal(typeIds[0], token);
if (methodId == 0) //try to search on System.Linq.Enumerable
......
......@@ -411,7 +411,7 @@ protected override async Task<bool> AcceptCommand(MessageId id, string method, J
{
case "scope":
return await OnSetVariableValue(id,
int.Parse(objectId.Value),
objectId.Value,
args?["variableName"]?.Value<string>(),
args?["newValue"],
token);
......@@ -449,7 +449,7 @@ protected override async Task<bool> AcceptCommand(MessageId id, string method, J
{
case "scope":
return await OnEvaluateOnCallFrame(id,
int.Parse(objectId.Value),
objectId.Value,
args?["expression"]?.Value<string>(), token);
default:
return false;
......@@ -586,16 +586,17 @@ private async Task<bool> CallOnFunction(MessageId id, JObject args, Cancellation
switch (objectId.Scheme)
{
case "object":
args["details"] = await context.SdbAgent.GetObjectProxy(int.Parse(objectId.Value), token);
case "methodId":
args["details"] = await context.SdbAgent.GetObjectProxy(objectId.Value, token);
break;
case "valuetype":
args["details"] = await context.SdbAgent.GetValueTypeProxy(int.Parse(objectId.Value), token);
args["details"] = await context.SdbAgent.GetValueTypeProxy(objectId.Value, token);
break;
case "pointer":
args["details"] = await context.SdbAgent.GetPointerContent(int.Parse(objectId.Value), token);
args["details"] = await context.SdbAgent.GetPointerContent(objectId.Value, token);
break;
case "array":
args["details"] = await context.SdbAgent.GetArrayValuesProxy(int.Parse(objectId.Value), token);
args["details"] = await context.SdbAgent.GetArrayValuesProxy(objectId.Value, token);
break;
case "cfo_res":
{
......@@ -680,20 +681,25 @@ internal async Task<JToken> RuntimeGetPropertiesInternal(SessionId id, DotnetObj
{
case "scope":
{
var res = await GetScopeProperties(id, int.Parse(objectId.Value), token);
var res = await GetScopeProperties(id, objectId.Value, token);
return res.Value?["result"];
}
case "valuetype":
return await context.SdbAgent.GetValueTypeValues(int.Parse(objectId.Value), accessorPropertiesOnly, token);
return await context.SdbAgent.GetValueTypeValues(objectId.Value, accessorPropertiesOnly, token);
case "array":
return await context.SdbAgent.GetArrayValues(int.Parse(objectId.Value), token);
return await context.SdbAgent.GetArrayValues(objectId.Value, token);
case "methodId":
{
var objRet = await context.SdbAgent.InvokeMethodInObject(objectId.Value, objectId.SubValue, "", token);
return new JArray(objRet);
}
case "object":
return await context.SdbAgent.GetObjectValues(int.Parse(objectId.Value), objectValuesOpt, token);
return await context.SdbAgent.GetObjectValues(objectId.Value, objectValuesOpt, token);
case "pointer":
return new JArray{await context.SdbAgent.GetPointerContent(int.Parse(objectId.Value), token)};
return new JArray{await context.SdbAgent.GetPointerContent(objectId.Value, token)};
case "cfo_res":
{
Result res = await SendMonoCommand(id, MonoCommands.GetDetails(RuntimeId, int.Parse(objectId.Value), args), token);
Result res = await SendMonoCommand(id, MonoCommands.GetDetails(RuntimeId, objectId.Value, args), token);
string value_json_str = res.Value["result"]?["value"]?["__value_as_json_string__"]?.Value<string>();
return value_json_str != null ? JArray.Parse(value_json_str) : null;
}
......
......@@ -502,7 +502,7 @@ public override void Write(string val)
base.Write(data);
}
private void Write<T>(ElementType type, T value) where T : struct => Write((byte)type, value);
internal void Write<T>(ElementType type, T value) where T : struct => Write((byte)type, value);
private void Write<T1, T2>(T1 type, T2 value) where T1 : struct where T2 : struct
{
......@@ -514,11 +514,11 @@ public void WriteObj(DotnetObjectId objectId, MonoSDBHelper SdbHelper)
{
if (objectId.Scheme == "object")
{
Write(ElementType.Class, int.Parse(objectId.Value));
Write(ElementType.Class, objectId.Value);
}
else if (objectId.Scheme == "valuetype")
{
Write(SdbHelper.valueTypes[int.Parse(objectId.Value)].valueTypeBuffer);
Write(SdbHelper.valueTypes[objectId.Value].valueTypeBuffer);
}
}
public async Task<bool> WriteConst(LiteralExpressionSyntax constValue, MonoSDBHelper SdbHelper, CancellationToken token)
......@@ -1537,6 +1537,13 @@ public async Task<JObject> InvokeMethod(ArraySegment<byte> valueTypeBuffer, int
return await CreateJObjectForVariableValue(retDebuggerCmdReader, varName, false, -1, false, token);
}
public async Task<JObject> InvokeMethodInObject(int objectId, int methodId, string varName, CancellationToken token)
{
using var commandParamsObjWriter = new MonoBinaryWriter();
commandParamsObjWriter.Write(ElementType.Class, objectId);
return await InvokeMethod(commandParamsObjWriter.GetParameterBuffer(), methodId, varName, token);
}
public async Task<int> GetPropertyMethodIdByName(int typeId, string propertyName, CancellationToken token)
{
using var retDebuggerCmdReader = await GetTypePropertiesReader(typeId, token);
......@@ -1559,13 +1566,14 @@ public async Task<int> GetPropertyMethodIdByName(int typeId, string propertyName
return -1;
}
public async Task<JArray> CreateJArrayForProperties(int typeId, ArraySegment<byte> object_buffer, JArray attributes, bool isAutoExpandable, string objectId, bool isOwn, CancellationToken token)
public async Task<JArray> CreateJArrayForProperties(int typeId, ArraySegment<byte> object_buffer, JArray attributes, bool isAutoExpandable, string objectIdStr, bool isOwn, CancellationToken token)
{
JArray ret = new JArray();
using var retDebuggerCmdReader = await GetTypePropertiesReader(typeId, token);
if (retDebuggerCmdReader == null)
return null;
if (!DotnetObjectId.TryParse(objectIdStr, out DotnetObjectId objectId))
return null;
var nProperties = retDebuggerCmdReader.ReadInt32();
for (int i = 0 ; i < nProperties; i++)
{
......@@ -1595,11 +1603,11 @@ public async Task<JArray> CreateJArrayForProperties(int typeId, ArraySegment<byt
get = new
{
type = "function",
objectId = $"{objectId}:methodId:{getMethodId}",
objectId = $"dotnet:methodId:{objectId.Value}:{getMethodId}",
className = "Function",
description = "get " + propertyNameStr + " ()",
methodId = getMethodId,
objectIdValue = objectId
objectIdValue = objectIdStr
},
name = propertyNameStr
});
......@@ -2054,10 +2062,10 @@ public async Task<JArray> GetHoistedLocalVariables(int objectId, JArray asyncLoc
{
if (DotnetObjectId.TryParse(asyncLocal?["value"]?["objectId"]?.Value<string>(), out DotnetObjectId dotnetObjectId))
{
if (int.TryParse(dotnetObjectId.Value, out int objectIdToGetInfo) && !objectsAlreadyRead.Contains(objectIdToGetInfo))
if (!objectsAlreadyRead.Contains(dotnetObjectId.Value))
{
var asyncLocalsFromObject = await GetObjectValues(objectIdToGetInfo, GetObjectCommandOptions.WithProperties, token);
var hoistedLocalVariable = await GetHoistedLocalVariables(objectIdToGetInfo, asyncLocalsFromObject, token);
var asyncLocalsFromObject = await GetObjectValues(dotnetObjectId.Value, GetObjectCommandOptions.WithProperties, token);
var hoistedLocalVariable = await GetHoistedLocalVariables(dotnetObjectId.Value, asyncLocalsFromObject, token);
asyncLocalsFull = new JArray(asyncLocalsFull.Union(hoistedLocalVariable));
}
}
......@@ -2295,7 +2303,7 @@ public async Task<JArray> GetValuesFromDebuggerProxyAttribute(int objectId, int
var retMethod = await InvokeMethod(invokeParamsWriter.GetParameterBuffer(), methodId, "methodRet", token);
DotnetObjectId.TryParse(retMethod?["value"]?["objectId"]?.Value<string>(), out DotnetObjectId dotnetObjectId);
var displayAttrs = await GetObjectValues(int.Parse(dotnetObjectId.Value), GetObjectCommandOptions.WithProperties | GetObjectCommandOptions.ForDebuggerProxyAttribute, token);
var displayAttrs = await GetObjectValues(dotnetObjectId.Value, GetObjectCommandOptions.WithProperties | GetObjectCommandOptions.ForDebuggerProxyAttribute, token);
return displayAttrs;
}
}
......@@ -2385,7 +2393,7 @@ public async Task<JArray> GetObjectValues(int objectId, GetObjectCommandOptions
if (!getCommandType.HasFlag(GetObjectCommandOptions.WithProperties))
return ret;
using var commandParamsObjWriter = new MonoBinaryWriter();
commandParamsObjWriter.WriteObj(new DotnetObjectId("object", $"{objectId}"), this);
commandParamsObjWriter.WriteObj(new DotnetObjectId("object", objectId), this);
var props = await CreateJArrayForProperties(typeId[i], commandParamsObjWriter.GetParameterBuffer(), ret, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute), $"dotnet:object:{objectId}", i == 0, token);
ret = new JArray(ret.Union(props));
......
......@@ -623,7 +623,12 @@ internal async Task CheckCustomType(JToken actual_val, JToken exp_val, string la
AssertEqual("Function", get["className"]?.Value<string>(), $"{label}-className");
AssertStartsWith($"get {exp_val["type_name"]?.Value<string>()} ()", get["description"]?.Value<string>(), $"{label}-description");
AssertEqual("function", get["type"]?.Value<string>(), $"{label}-type");
var expectedValue = exp_val["value"];
if (expectedValue.Type != JTokenType.Null)
{
var valueAfterRunGet = await GetProperties(get["objectId"]?.Value<string>());
await CheckValue(valueAfterRunGet[0]?["value"], expectedValue, exp_val["type_name"]?.Value<string>());
}
break;
}
......@@ -1059,7 +1064,7 @@ internal void AssertEqual(object expected, object actual, string label)
internal static JObject TIgnore() => JObject.FromObject(new { __custom_type = "ignore_me" });
internal static JObject TGetter(string type) => JObject.FromObject(new { __custom_type = "getter", type_name = type });
internal static JObject TGetter(string type, JObject value = null) => JObject.FromObject(new { __custom_type = "getter", type_name = type, value = value});
internal static JObject TDateTime(DateTime dt) => JObject.FromObject(new
{
......
......@@ -340,7 +340,7 @@ public async Task GetObjectValueWithInheritance()
{
var pause_location = await EvaluateAndCheck(
"window.setTimeout(function() { invoke_static_method('[debugger-test] TestChild:TestWatchWithInheritance'); }, 1);",
"dotnet://debugger-test.dll/debugger-test2.cs", 127, 8,
"dotnet://debugger-test.dll/debugger-test2.cs", 128, 8,
"TestWatchWithInheritance");
var frame_id = pause_location["callFrames"][0]["callFrameId"].Value<string>();
var frame_locals = await GetProperties(frame_id);
......@@ -350,9 +350,10 @@ public async Task GetObjectValueWithInheritance()
j = TNumber(20),
i = TNumber(50),
k = TNumber(30),
GetJ = TGetter("GetJ"),
GetI = TGetter("GetI"),
GetK = TGetter("GetK")
GetJ = TGetter("GetJ", TNumber(20)),
GetI = TGetter("GetI", TNumber(50)),
GetK = TGetter("GetK", TNumber(30)),
GetD = TGetter("GetD", TDateTime(new DateTime(2020, 7, 6, 5, 4, 3)))
}, "test_props");
await EvaluateOnCallFrameAndCheck(frame_id,
($"test.GetJ", TNumber(20)),
......
......@@ -118,6 +118,7 @@ public class TestChild : TestParent
{
public int i = 50;
public int GetI => i;
public DateTime GetD => new DateTime(2020, 7, 6, 5, 4, 3);
public TestChild()
{
Console.WriteLine("Hi");
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册