Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 36 additions & 13 deletions src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>();
Expand Down Expand Up @@ -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}'");
Expand Down Expand Up @@ -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
Expand Down
30 changes: 18 additions & 12 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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":
{
Expand Down Expand Up @@ -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;
}
Expand Down
32 changes: 20 additions & 12 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ protected unsafe void WriteBigEndian<T>(T val) where T : struct
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
{
Expand All @@ -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)
Expand Down Expand Up @@ -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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this work for structs too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No.

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);
Expand All @@ -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++)
{
Expand Down Expand Up @@ -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
});
Expand Down Expand Up @@ -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));
}
}
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

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

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
{
Expand Down
Loading