Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[debugger][wasm] Support DebuggerProxyAttribute #56872

Merged
merged 15 commits into from
Aug 16, 2021
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
2 changes: 1 addition & 1 deletion src/mono/mono/component/debugger-agent.c
Original file line number Diff line number Diff line change
Expand Up @@ -6459,7 +6459,7 @@ get_types (gpointer key, gpointer value, gpointer user_data)
t = mono_reflection_get_type_checked (alc, ass->image, ass->image, ud->info, ud->ignore_case, TRUE, &type_resolve, probe_type_error);
mono_error_cleanup (probe_type_error);
if (t) {
g_ptr_array_add (ud->res_classes, mono_type_get_class_internal (t));
g_ptr_array_add (ud->res_classes, mono_class_from_mono_type_internal (t));
g_ptr_array_add (ud->res_domains, domain);
}
}
Expand Down
29 changes: 25 additions & 4 deletions src/mono/sample/wasm/browser/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Runtime.CompilerServices;
using System.Diagnostics;
using System.Collections.Generic;

namespace Sample
{
Expand Down Expand Up @@ -37,6 +38,28 @@ string GetDebuggerDisplay ()
}
}

[DebuggerTypeProxy(typeof(TheProxy))]
class WithProxy
{
public string Val1 {
get { return "one"; }
}
}

class TheProxy
{
WithProxy wp;

public TheProxy (WithProxy wp)
{
this.wp = wp;
}

public string Val2 {
get { return wp.Val1; }
}
}

public static void Main(string[] args)
{
Console.WriteLine ("Hello, World!");
Expand All @@ -45,10 +68,8 @@ public static void Main(string[] args)
[MethodImpl(MethodImplOptions.NoInlining)]
public static int TestMeaning()
{
var a = new WithDisplayString();
var c = new DebuggerDisplayMethodTest();
Console.WriteLine(a);
Console.WriteLine(c);
List<int> myList = new List<int>{ 1, 2, 3, 4 };
Console.WriteLine(myList);
return 42;
}
}
Expand Down
16 changes: 5 additions & 11 deletions src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ private object ConvertJSToCSharpType(JToken variable)
case "boolean":
return value?.Value<bool>();
case "object":
return null;
return variable;
Copy link
Member

Choose a reason for hiding this comment

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

Tests needed for this

case "void":
return null;
}
Expand Down Expand Up @@ -396,19 +396,13 @@ private static object ConvertCSharpToJSType(object v, ITypeSymbol type)
{
if (v == null)
return new { type = "object", subtype = "null", className = type.ToString(), description = type.ToString() };

if (v is string s)
{
return new { type = "string", value = s, description = s };
}
else if (NumericTypes.Contains(v.GetType()))
{
if (NumericTypes.Contains(v.GetType()))
return new { type = "number", value = v, description = v.ToString() };
}
else
{
return new { type = "object", value = v, description = v.ToString(), className = type.ToString() };
}
if (v is JObject)
return v;
return new { type = "object", value = v, description = v.ToString(), className = type.ToString() };
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ internal class MemberReferenceResolver
private PerScopeCache scopeCache;
private ILogger logger;
private bool localsFetched;
private int linqTypeId;
private MonoSDBHelper sdbHelper;

public MemberReferenceResolver(MonoProxy proxy, ExecutionContext ctx, SessionId sessionId, int scopeId, ILogger logger)
{
Expand All @@ -32,6 +34,8 @@ public MemberReferenceResolver(MonoProxy proxy, ExecutionContext ctx, SessionId
this.ctx = ctx;
this.logger = logger;
scopeCache = ctx.GetCacheForScope(scopeId);
sdbHelper = proxy.SdbHelper;
linqTypeId = -1;
}

public MemberReferenceResolver(MonoProxy proxy, ExecutionContext ctx, SessionId sessionId, JArray objectValues, ILogger logger)
Expand All @@ -43,6 +47,8 @@ public MemberReferenceResolver(MonoProxy proxy, ExecutionContext ctx, SessionId
this.logger = logger;
scopeCache = new PerScopeCache(objectValues);
localsFetched = true;
sdbHelper = proxy.SdbHelper;
linqTypeId = -1;
}

public async Task<JObject> GetValueFromObject(JToken objRet, CancellationToken token)
Expand All @@ -51,7 +57,7 @@ public async Task<JObject> GetValueFromObject(JToken objRet, CancellationToken t
{
if (DotnetObjectId.TryParse(objRet?["value"]?["objectId"]?.Value<string>(), out DotnetObjectId objectId))
{
var exceptionObject = await proxy.SdbHelper.GetObjectValues(sessionId, int.Parse(objectId.Value), true, false, false, true, token);
var exceptionObject = await sdbHelper.GetObjectValues(sessionId, int.Parse(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 All @@ -67,8 +73,8 @@ public async Task<JObject> GetValueFromObject(JToken objRet, CancellationToken t
{
var commandParams = new MemoryStream();
var commandParamsWriter = new MonoBinaryWriter(commandParams);
commandParamsWriter.WriteObj(objectId, proxy.SdbHelper);
var ret = await proxy.SdbHelper.InvokeMethod(sessionId, commandParams.ToArray(), objRet["get"]["methodId"].Value<int>(), objRet["name"].Value<string>(), token);
commandParamsWriter.WriteObj(objectId, sdbHelper);
var ret = await sdbHelper.InvokeMethod(sessionId, commandParams.ToArray(), objRet["get"]["methodId"].Value<int>(), objRet["name"].Value<string>(), token);
return await GetValueFromObject(ret, token);
}

Expand All @@ -88,27 +94,27 @@ public async Task<JObject> TryToRunOnLoadedClasses(string varName, CancellationT
classNameToFind += part.Trim();
if (typeId != -1)
{
var fields = await proxy.SdbHelper.GetTypeFields(sessionId, typeId, token);
var fields = await sdbHelper.GetTypeFields(sessionId, typeId, onlyPublic: false, token);
foreach (var field in fields)
{
if (field.Name == part.Trim())
{
var isInitialized = await proxy.SdbHelper.TypeIsInitialized(sessionId, typeId, token);
var isInitialized = await sdbHelper.TypeIsInitialized(sessionId, typeId, token);
if (isInitialized == 0)
{
isInitialized = await proxy.SdbHelper.TypeInitialize(sessionId, typeId, token);
isInitialized = await sdbHelper.TypeInitialize(sessionId, typeId, token);
}
var valueRet = await proxy.SdbHelper.GetFieldValue(sessionId, typeId, field.Id, token);
var valueRet = await sdbHelper.GetFieldValue(sessionId, typeId, field.Id, token);
return await GetValueFromObject(valueRet, token);
}
}
var methodId = await proxy.SdbHelper.GetPropertyMethodIdByName(sessionId, typeId, part.Trim(), token);
var methodId = await sdbHelper.GetPropertyMethodIdByName(sessionId, typeId, part.Trim(), token);
if (methodId != -1)
{
var commandParamsObj = new MemoryStream();
var commandParamsObjWriter = new MonoBinaryWriter(commandParamsObj);
commandParamsObjWriter.Write(0); //param count
var retMethod = await proxy.SdbHelper.InvokeMethod(sessionId, commandParamsObj.ToArray(), methodId, "methodRet", token);
var retMethod = await sdbHelper.InvokeMethod(sessionId, commandParamsObj.ToArray(), methodId, "methodRet", token);
return await GetValueFromObject(retMethod, token);
}
}
Expand All @@ -118,8 +124,8 @@ public async Task<JObject> TryToRunOnLoadedClasses(string varName, CancellationT
var type = asm.GetTypeByName(classNameToFind);
if (type != null)
{
var assemblyId = await proxy.SdbHelper.GetAssemblyId(sessionId, type.assembly.Name, token);
typeId = await proxy.SdbHelper.GetTypeIdFromToken(sessionId, assemblyId, type.Token, token);
var assemblyId = await sdbHelper.GetAssemblyId(sessionId, type.assembly.Name, token);
typeId = await sdbHelper.GetTypeIdFromToken(sessionId, assemblyId, type.Token, token);
}
}
}
Expand Down Expand Up @@ -204,6 +210,7 @@ public async Task<JObject> Resolve(string varName, CancellationToken token)
public async Task<JObject> Resolve(InvocationExpressionSyntax method, Dictionary<string, JObject> memberAccessValues, CancellationToken token)
{
var methodName = "";
int isTryingLinq = 0;
try
{
JObject rootObject = null;
Expand All @@ -223,33 +230,56 @@ public async Task<JObject> Resolve(InvocationExpressionSyntax method, Dictionary
if (rootObject != null)
{
DotnetObjectId.TryParse(rootObject?["objectId"]?.Value<string>(), out DotnetObjectId objectId);
var typeId = await proxy.SdbHelper.GetTypeIdFromObject(sessionId, int.Parse(objectId.Value), true, token);
int methodId = await proxy.SdbHelper.GetMethodIdByName(sessionId, typeId[0], methodName, token);
var typeIds = await sdbHelper.GetTypeIdFromObject(sessionId, int.Parse(objectId.Value), true, token);
int methodId = await sdbHelper.GetMethodIdByName(sessionId, typeIds[0], methodName, token);
var className = await sdbHelper.GetTypeNameOriginal(sessionId, typeIds[0], token);
if (methodId == 0) //try to search on System.Linq.Enumerable
radical marked this conversation as resolved.
Show resolved Hide resolved
{
if (linqTypeId == -1)
linqTypeId = await sdbHelper.GetTypeByName(sessionId, "System.Linq.Enumerable", token);
methodId = await sdbHelper.GetMethodIdByName(sessionId, linqTypeId, methodName, token);
if (methodId != 0)
{
foreach (var typeId in typeIds)
{
var genericTypeArgs = await sdbHelper.GetTypeParamsOrArgsForGenericType(sessionId, typeId, token);
if (genericTypeArgs.Count > 0)
{
isTryingLinq = 1;
methodId = await sdbHelper.MakeGenericMethod(sessionId, methodId, genericTypeArgs, token);
break;
}
}
}
}
if (methodId == 0) {
var typeName = await proxy.SdbHelper.GetTypeName(sessionId, typeId[0], token);
var typeName = await sdbHelper.GetTypeName(sessionId, typeIds[0], token);
throw new Exception($"Method '{methodName}' not found in type '{typeName}'");
}
var commandParamsObj = new MemoryStream();
var commandParamsObjWriter = new MonoBinaryWriter(commandParamsObj);
commandParamsObjWriter.WriteObj(objectId, proxy.SdbHelper);
if (isTryingLinq == 0)
Copy link
Member

Choose a reason for hiding this comment

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

What is this writing?

commandParamsObjWriter.WriteObj(objectId, sdbHelper);
if (method.ArgumentList != null)
{
commandParamsObjWriter.Write((int)method.ArgumentList.Arguments.Count);
commandParamsObjWriter.Write((int)method.ArgumentList.Arguments.Count + isTryingLinq);
if (isTryingLinq == 1)
commandParamsObjWriter.WriteObj(objectId, sdbHelper);
foreach (var arg in method.ArgumentList.Arguments)
{
if (arg.Expression is LiteralExpressionSyntax)
{
if (!await commandParamsObjWriter.WriteConst(sessionId, arg.Expression as LiteralExpressionSyntax, proxy.SdbHelper, token))
if (!await commandParamsObjWriter.WriteConst(sessionId, arg.Expression as LiteralExpressionSyntax, sdbHelper, token))
return null;
}
if (arg.Expression is IdentifierNameSyntax)
{
var argParm = arg.Expression as IdentifierNameSyntax;
if (!await commandParamsObjWriter.WriteJsonValue(sessionId, memberAccessValues[argParm.Identifier.Text], proxy.SdbHelper, token))
if (!await commandParamsObjWriter.WriteJsonValue(sessionId, memberAccessValues[argParm.Identifier.Text], sdbHelper, token))
return null;
}
}
var retMethod = await proxy.SdbHelper.InvokeMethod(sessionId, commandParamsObj.ToArray(), methodId, "methodRet", token);
var retMethod = await sdbHelper.InvokeMethod(sessionId, commandParamsObj.ToArray(), methodId, "methodRet", token);
return await GetValueFromObject(retMethod, token);
}
}
Expand Down
26 changes: 16 additions & 10 deletions src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ protected override async Task<bool> AcceptEvent(SessionId sessionId, string meth
}
catch (Exception) //if the page is refreshed maybe it stops here.
{
return false;
await SendCommand(sessionId, "Debugger.resume", new JObject(), token);
return true;
}
}
}
Expand Down Expand Up @@ -617,13 +618,18 @@ private async Task<bool> OnSetVariableValue(MessageId id, int scopeId, string va
internal async Task<JToken> RuntimeGetPropertiesInternal(SessionId id, DotnetObjectId objectId, JToken args, CancellationToken token)
{
var accessorPropertiesOnly = false;
var ownProperties = false;
GetObjectCommandOptions objectValuesOpt = GetObjectCommandOptions.WithProperties;
if (args != null)
{
if (args["accessorPropertiesOnly"] != null)
accessorPropertiesOnly = args["accessorPropertiesOnly"].Value<bool>();
if (args["ownProperties"] != null)
ownProperties = args["ownProperties"].Value<bool>();
if (args["accessorPropertiesOnly"] != null && args["accessorPropertiesOnly"].Value<bool>())
{
objectValuesOpt |= GetObjectCommandOptions.AccessorPropertiesOnly;
accessorPropertiesOnly = true;
}
if (args["ownProperties"] != null && args["ownProperties"].Value<bool>())
{
objectValuesOpt |= GetObjectCommandOptions.OwnProperties;
}
}
//Console.WriteLine($"RuntimeGetProperties - {args}");
try {
Expand All @@ -639,7 +645,7 @@ internal async Task<JToken> RuntimeGetPropertiesInternal(SessionId id, DotnetObj
case "array":
return await SdbHelper.GetArrayValues(id, int.Parse(objectId.Value), token);
case "object":
return await SdbHelper.GetObjectValues(id, int.Parse(objectId.Value), true, false, accessorPropertiesOnly, ownProperties, token);
return await SdbHelper.GetObjectValues(id, int.Parse(objectId.Value), objectValuesOpt, token);
case "pointer":
return new JArray{await SdbHelper.GetPointerContent(id, int.Parse(objectId.Value), token)};
case "cfo_res":
Expand Down Expand Up @@ -717,7 +723,7 @@ private async Task<bool> SendBreakpointsOfMethodUpdated(SessionId sessionId, Exe
AssemblyInfo asm = store.GetAssemblyByName(assembly_name);
if (asm == null)
{
assembly_name = await SdbHelper.GetAssemblyNameFull(sessionId, assembly_id, token);
assembly_name = await SdbHelper.GetAssemblyFileNameFromId(sessionId, assembly_id, token);
asm = store.GetAssemblyByName(assembly_name);
if (asm == null)
{
Expand Down Expand Up @@ -764,7 +770,7 @@ private async Task<bool> SendCallStack(SessionId sessionId, ExecutionContext con
AssemblyInfo asm = store.GetAssemblyByName(assembly_name);
if (asm == null)
{
assembly_name = await SdbHelper.GetAssemblyNameFull(sessionId, assembly_id, token); //maybe is a lazy loaded assembly
assembly_name = await SdbHelper.GetAssemblyFileNameFromId(sessionId, assembly_id, token); //maybe is a lazy loaded assembly
asm = store.GetAssemblyByName(assembly_name);
if (asm == null)
{
Expand Down Expand Up @@ -911,7 +917,7 @@ private async Task<bool> OnReceiveDebuggerAgentEvent(SessionId sessionId, JObjec
string reason = "exception";
int object_id = retDebuggerCmdReader.ReadInt32();
var caught = retDebuggerCmdReader.ReadByte();
var exceptionObject = await SdbHelper.GetObjectValues(sessionId, object_id, true, false, false, true, token);
var exceptionObject = await SdbHelper.GetObjectValues(sessionId, object_id, GetObjectCommandOptions.WithProperties | GetObjectCommandOptions.OwnProperties, token);
var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value<string>().Equals("message"));
var data = JObject.FromObject(new
{
Expand Down
Loading