Skip to content

Commit 1c09d36

Browse files
authored
[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
1 parent 13f938f commit 1c09d36

File tree

7 files changed

+93
-51
lines changed

7 files changed

+93
-51
lines changed

src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,36 +60,59 @@ public MessageId(string sessionId, int id)
6060
internal class DotnetObjectId
6161
{
6262
public string Scheme { get; }
63-
public string Value { get; }
63+
public int Value { get; }
64+
public int SubValue { get; set; }
6465

6566
public static bool TryParse(JToken jToken, out DotnetObjectId objectId) => TryParse(jToken?.Value<string>(), out objectId);
6667

6768
public static bool TryParse(string id, out DotnetObjectId objectId)
6869
{
6970
objectId = null;
70-
if (id == null)
71-
return false;
72-
73-
if (!id.StartsWith("dotnet:"))
74-
return false;
71+
try {
72+
if (id == null)
73+
return false;
7574

76-
string[] parts = id.Split(":", 3);
75+
if (!id.StartsWith("dotnet:"))
76+
return false;
7777

78-
if (parts.Length < 3)
79-
return false;
78+
string[] parts = id.Split(":");
8079

81-
objectId = new DotnetObjectId(parts[1], parts[2]);
80+
if (parts.Length < 3)
81+
return false;
8282

83-
return true;
83+
objectId = new DotnetObjectId(parts[1], int.Parse(parts[2]));
84+
switch (objectId.Scheme)
85+
{
86+
case "methodId":
87+
{
88+
parts = id.Split(":");
89+
objectId.SubValue = int.Parse(parts[3]);
90+
break;
91+
}
92+
}
93+
return true;
94+
}
95+
catch (Exception)
96+
{
97+
return false;
98+
}
8499
}
85100

86-
public DotnetObjectId(string scheme, string value)
101+
public DotnetObjectId(string scheme, int value)
87102
{
88103
Scheme = scheme;
89104
Value = value;
90105
}
91106

92-
public override string ToString() => $"dotnet:{Scheme}:{Value}";
107+
public override string ToString()
108+
{
109+
switch (Scheme)
110+
{
111+
case "methodId":
112+
return $"dotnet:{Scheme}:{Value}:{SubValue}";
113+
}
114+
return $"dotnet:{Scheme}:{Value}";
115+
}
93116
}
94117

95118
public struct Result

src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public async Task<JObject> GetValueFromObject(JToken objRet, CancellationToken t
5353
{
5454
if (DotnetObjectId.TryParse(objRet?["value"]?["objectId"]?.Value<string>(), out DotnetObjectId objectId))
5555
{
56-
var exceptionObject = await context.SdbAgent.GetObjectValues(int.Parse(objectId.Value), GetObjectCommandOptions.WithProperties | GetObjectCommandOptions.OwnProperties, token);
56+
var exceptionObject = await context.SdbAgent.GetObjectValues(objectId.Value, GetObjectCommandOptions.WithProperties | GetObjectCommandOptions.OwnProperties, token);
5757
var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value<string>().Equals("_message"));
5858
exceptionObjectMessage["value"]["value"] = objRet["value"]?["className"]?.Value<string>() + ": " + exceptionObjectMessage["value"]?["value"]?.Value<string>();
5959
return exceptionObjectMessage["value"]?.Value<JObject>();
@@ -342,17 +342,15 @@ public async Task<JObject> Resolve(ElementAccessExpressionSyntax elementAccess,
342342
switch (objectId.Scheme)
343343
{
344344
case "array":
345-
rootObject["value"] = await context.SdbAgent.GetArrayValues(int.Parse(objectId.Value), token);
345+
rootObject["value"] = await context.SdbAgent.GetArrayValues(objectId.Value, token);
346346
return (JObject)rootObject["value"][elementIdx]["value"];
347347
case "object":
348-
var typeIds = await context.SdbAgent.GetTypeIdFromObject(int.Parse(objectId.Value), true, token);
348+
var typeIds = await context.SdbAgent.GetTypeIdFromObject(objectId.Value, true, token);
349349
int methodId = await context.SdbAgent.GetMethodIdByName(typeIds[0], "ToArray", token);
350-
var commandParamsObjWriter = new MonoBinaryWriter();
351-
commandParamsObjWriter.WriteObj(objectId, context.SdbAgent);
352-
var toArrayRetMethod = await context.SdbAgent.InvokeMethod(commandParamsObjWriter.GetParameterBuffer(), methodId, elementAccess.Expression.ToString(), token);
350+
var toArrayRetMethod = await context.SdbAgent.InvokeMethodInObject(objectId.Value, methodId, elementAccess.Expression.ToString(), token);
353351
rootObject = await GetValueFromObject(toArrayRetMethod, token);
354352
DotnetObjectId.TryParse(rootObject?["objectId"]?.Value<string>(), out DotnetObjectId arrayObjectId);
355-
rootObject["value"] = await context.SdbAgent.GetArrayValues(int.Parse(arrayObjectId.Value), token);
353+
rootObject["value"] = await context.SdbAgent.GetArrayValues(arrayObjectId.Value, token);
356354
return (JObject)rootObject["value"][elementIdx]["value"];
357355
default:
358356
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
391389
if (rootObject != null)
392390
{
393391
DotnetObjectId.TryParse(rootObject?["objectId"]?.Value<string>(), out DotnetObjectId objectId);
394-
var typeIds = await context.SdbAgent.GetTypeIdFromObject(int.Parse(objectId.Value), true, token);
392+
var typeIds = await context.SdbAgent.GetTypeIdFromObject(objectId.Value, true, token);
395393
int methodId = await context.SdbAgent.GetMethodIdByName(typeIds[0], methodName, token);
396394
var className = await context.SdbAgent.GetTypeNameOriginal(typeIds[0], token);
397395
if (methodId == 0) //try to search on System.Linq.Enumerable

src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ protected override async Task<bool> AcceptCommand(MessageId id, string method, J
411411
{
412412
case "scope":
413413
return await OnSetVariableValue(id,
414-
int.Parse(objectId.Value),
414+
objectId.Value,
415415
args?["variableName"]?.Value<string>(),
416416
args?["newValue"],
417417
token);
@@ -449,7 +449,7 @@ protected override async Task<bool> AcceptCommand(MessageId id, string method, J
449449
{
450450
case "scope":
451451
return await OnEvaluateOnCallFrame(id,
452-
int.Parse(objectId.Value),
452+
objectId.Value,
453453
args?["expression"]?.Value<string>(), token);
454454
default:
455455
return false;
@@ -586,16 +586,17 @@ private async Task<bool> CallOnFunction(MessageId id, JObject args, Cancellation
586586
switch (objectId.Scheme)
587587
{
588588
case "object":
589-
args["details"] = await context.SdbAgent.GetObjectProxy(int.Parse(objectId.Value), token);
589+
case "methodId":
590+
args["details"] = await context.SdbAgent.GetObjectProxy(objectId.Value, token);
590591
break;
591592
case "valuetype":
592-
args["details"] = await context.SdbAgent.GetValueTypeProxy(int.Parse(objectId.Value), token);
593+
args["details"] = await context.SdbAgent.GetValueTypeProxy(objectId.Value, token);
593594
break;
594595
case "pointer":
595-
args["details"] = await context.SdbAgent.GetPointerContent(int.Parse(objectId.Value), token);
596+
args["details"] = await context.SdbAgent.GetPointerContent(objectId.Value, token);
596597
break;
597598
case "array":
598-
args["details"] = await context.SdbAgent.GetArrayValuesProxy(int.Parse(objectId.Value), token);
599+
args["details"] = await context.SdbAgent.GetArrayValuesProxy(objectId.Value, token);
599600
break;
600601
case "cfo_res":
601602
{
@@ -680,20 +681,25 @@ internal async Task<JToken> RuntimeGetPropertiesInternal(SessionId id, DotnetObj
680681
{
681682
case "scope":
682683
{
683-
var res = await GetScopeProperties(id, int.Parse(objectId.Value), token);
684+
var res = await GetScopeProperties(id, objectId.Value, token);
684685
return res.Value?["result"];
685686
}
686687
case "valuetype":
687-
return await context.SdbAgent.GetValueTypeValues(int.Parse(objectId.Value), accessorPropertiesOnly, token);
688+
return await context.SdbAgent.GetValueTypeValues(objectId.Value, accessorPropertiesOnly, token);
688689
case "array":
689-
return await context.SdbAgent.GetArrayValues(int.Parse(objectId.Value), token);
690+
return await context.SdbAgent.GetArrayValues(objectId.Value, token);
691+
case "methodId":
692+
{
693+
var objRet = await context.SdbAgent.InvokeMethodInObject(objectId.Value, objectId.SubValue, "", token);
694+
return new JArray(objRet);
695+
}
690696
case "object":
691-
return await context.SdbAgent.GetObjectValues(int.Parse(objectId.Value), objectValuesOpt, token);
697+
return await context.SdbAgent.GetObjectValues(objectId.Value, objectValuesOpt, token);
692698
case "pointer":
693-
return new JArray{await context.SdbAgent.GetPointerContent(int.Parse(objectId.Value), token)};
699+
return new JArray{await context.SdbAgent.GetPointerContent(objectId.Value, token)};
694700
case "cfo_res":
695701
{
696-
Result res = await SendMonoCommand(id, MonoCommands.GetDetails(RuntimeId, int.Parse(objectId.Value), args), token);
702+
Result res = await SendMonoCommand(id, MonoCommands.GetDetails(RuntimeId, objectId.Value, args), token);
697703
string value_json_str = res.Value["result"]?["value"]?["__value_as_json_string__"]?.Value<string>();
698704
return value_json_str != null ? JArray.Parse(value_json_str) : null;
699705
}

src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ protected unsafe void WriteBigEndian<T>(T val) where T : struct
502502
base.Write(data);
503503
}
504504

505-
private void Write<T>(ElementType type, T value) where T : struct => Write((byte)type, value);
505+
internal void Write<T>(ElementType type, T value) where T : struct => Write((byte)type, value);
506506

507507
private void Write<T1, T2>(T1 type, T2 value) where T1 : struct where T2 : struct
508508
{
@@ -514,11 +514,11 @@ public void WriteObj(DotnetObjectId objectId, MonoSDBHelper SdbHelper)
514514
{
515515
if (objectId.Scheme == "object")
516516
{
517-
Write(ElementType.Class, int.Parse(objectId.Value));
517+
Write(ElementType.Class, objectId.Value);
518518
}
519519
else if (objectId.Scheme == "valuetype")
520520
{
521-
Write(SdbHelper.valueTypes[int.Parse(objectId.Value)].valueTypeBuffer);
521+
Write(SdbHelper.valueTypes[objectId.Value].valueTypeBuffer);
522522
}
523523
}
524524
public async Task<bool> WriteConst(LiteralExpressionSyntax constValue, MonoSDBHelper SdbHelper, CancellationToken token)
@@ -1537,6 +1537,13 @@ public async Task<JObject> InvokeMethod(ArraySegment<byte> valueTypeBuffer, int
15371537
return await CreateJObjectForVariableValue(retDebuggerCmdReader, varName, false, -1, false, token);
15381538
}
15391539

1540+
public async Task<JObject> InvokeMethodInObject(int objectId, int methodId, string varName, CancellationToken token)
1541+
{
1542+
using var commandParamsObjWriter = new MonoBinaryWriter();
1543+
commandParamsObjWriter.Write(ElementType.Class, objectId);
1544+
return await InvokeMethod(commandParamsObjWriter.GetParameterBuffer(), methodId, varName, token);
1545+
}
1546+
15401547
public async Task<int> GetPropertyMethodIdByName(int typeId, string propertyName, CancellationToken token)
15411548
{
15421549
using var retDebuggerCmdReader = await GetTypePropertiesReader(typeId, token);
@@ -1559,13 +1566,14 @@ public async Task<int> GetPropertyMethodIdByName(int typeId, string propertyName
15591566
return -1;
15601567
}
15611568

1562-
public async Task<JArray> CreateJArrayForProperties(int typeId, ArraySegment<byte> object_buffer, JArray attributes, bool isAutoExpandable, string objectId, bool isOwn, CancellationToken token)
1569+
public async Task<JArray> CreateJArrayForProperties(int typeId, ArraySegment<byte> object_buffer, JArray attributes, bool isAutoExpandable, string objectIdStr, bool isOwn, CancellationToken token)
15631570
{
15641571
JArray ret = new JArray();
15651572
using var retDebuggerCmdReader = await GetTypePropertiesReader(typeId, token);
15661573
if (retDebuggerCmdReader == null)
15671574
return null;
1568-
1575+
if (!DotnetObjectId.TryParse(objectIdStr, out DotnetObjectId objectId))
1576+
return null;
15691577
var nProperties = retDebuggerCmdReader.ReadInt32();
15701578
for (int i = 0 ; i < nProperties; i++)
15711579
{
@@ -1595,11 +1603,11 @@ public async Task<JArray> CreateJArrayForProperties(int typeId, ArraySegment<byt
15951603
get = new
15961604
{
15971605
type = "function",
1598-
objectId = $"{objectId}:methodId:{getMethodId}",
1606+
objectId = $"dotnet:methodId:{objectId.Value}:{getMethodId}",
15991607
className = "Function",
16001608
description = "get " + propertyNameStr + " ()",
16011609
methodId = getMethodId,
1602-
objectIdValue = objectId
1610+
objectIdValue = objectIdStr
16031611
},
16041612
name = propertyNameStr
16051613
});
@@ -2054,10 +2062,10 @@ public async Task<JArray> GetHoistedLocalVariables(int objectId, JArray asyncLoc
20542062
{
20552063
if (DotnetObjectId.TryParse(asyncLocal?["value"]?["objectId"]?.Value<string>(), out DotnetObjectId dotnetObjectId))
20562064
{
2057-
if (int.TryParse(dotnetObjectId.Value, out int objectIdToGetInfo) && !objectsAlreadyRead.Contains(objectIdToGetInfo))
2065+
if (!objectsAlreadyRead.Contains(dotnetObjectId.Value))
20582066
{
2059-
var asyncLocalsFromObject = await GetObjectValues(objectIdToGetInfo, GetObjectCommandOptions.WithProperties, token);
2060-
var hoistedLocalVariable = await GetHoistedLocalVariables(objectIdToGetInfo, asyncLocalsFromObject, token);
2067+
var asyncLocalsFromObject = await GetObjectValues(dotnetObjectId.Value, GetObjectCommandOptions.WithProperties, token);
2068+
var hoistedLocalVariable = await GetHoistedLocalVariables(dotnetObjectId.Value, asyncLocalsFromObject, token);
20612069
asyncLocalsFull = new JArray(asyncLocalsFull.Union(hoistedLocalVariable));
20622070
}
20632071
}
@@ -2295,7 +2303,7 @@ public async Task<JArray> GetValuesFromDebuggerProxyAttribute(int objectId, int
22952303

22962304
var retMethod = await InvokeMethod(invokeParamsWriter.GetParameterBuffer(), methodId, "methodRet", token);
22972305
DotnetObjectId.TryParse(retMethod?["value"]?["objectId"]?.Value<string>(), out DotnetObjectId dotnetObjectId);
2298-
var displayAttrs = await GetObjectValues(int.Parse(dotnetObjectId.Value), GetObjectCommandOptions.WithProperties | GetObjectCommandOptions.ForDebuggerProxyAttribute, token);
2306+
var displayAttrs = await GetObjectValues(dotnetObjectId.Value, GetObjectCommandOptions.WithProperties | GetObjectCommandOptions.ForDebuggerProxyAttribute, token);
22992307
return displayAttrs;
23002308
}
23012309
}
@@ -2385,7 +2393,7 @@ public async Task<JArray> GetObjectValues(int objectId, GetObjectCommandOptions
23852393
if (!getCommandType.HasFlag(GetObjectCommandOptions.WithProperties))
23862394
return ret;
23872395
using var commandParamsObjWriter = new MonoBinaryWriter();
2388-
commandParamsObjWriter.WriteObj(new DotnetObjectId("object", $"{objectId}"), this);
2396+
commandParamsObjWriter.WriteObj(new DotnetObjectId("object", objectId), this);
23892397
var props = await CreateJArrayForProperties(typeId[i], commandParamsObjWriter.GetParameterBuffer(), ret, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute), $"dotnet:object:{objectId}", i == 0, token);
23902398
ret = new JArray(ret.Union(props));
23912399

src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,12 @@ internal async Task CheckCustomType(JToken actual_val, JToken exp_val, string la
623623
AssertEqual("Function", get["className"]?.Value<string>(), $"{label}-className");
624624
AssertStartsWith($"get {exp_val["type_name"]?.Value<string>()} ()", get["description"]?.Value<string>(), $"{label}-description");
625625
AssertEqual("function", get["type"]?.Value<string>(), $"{label}-type");
626-
626+
var expectedValue = exp_val["value"];
627+
if (expectedValue.Type != JTokenType.Null)
628+
{
629+
var valueAfterRunGet = await GetProperties(get["objectId"]?.Value<string>());
630+
await CheckValue(valueAfterRunGet[0]?["value"], expectedValue, exp_val["type_name"]?.Value<string>());
631+
}
627632
break;
628633
}
629634

@@ -1059,7 +1064,7 @@ internal static JObject TDelegate(string className, string target) => JObject.Fr
10591064

10601065
internal static JObject TIgnore() => JObject.FromObject(new { __custom_type = "ignore_me" });
10611066

1062-
internal static JObject TGetter(string type) => JObject.FromObject(new { __custom_type = "getter", type_name = type });
1067+
internal static JObject TGetter(string type, JObject value = null) => JObject.FromObject(new { __custom_type = "getter", type_name = type, value = value});
10631068

10641069
internal static JObject TDateTime(DateTime dt) => JObject.FromObject(new
10651070
{

0 commit comments

Comments
 (0)