diff --git a/src/mono/mono/component/debugger-agent.c b/src/mono/mono/component/debugger-agent.c index 2b6842c11fea6..493b088153544 100644 --- a/src/mono/mono/component/debugger-agent.c +++ b/src/mono/mono/component/debugger-agent.c @@ -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); } } diff --git a/src/mono/sample/wasm/browser/Program.cs b/src/mono/sample/wasm/browser/Program.cs index f87cd9103c749..11566f32c77a6 100644 --- a/src/mono/sample/wasm/browser/Program.cs +++ b/src/mono/sample/wasm/browser/Program.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.CompilerServices; using System.Diagnostics; +using System.Collections.Generic; namespace Sample { @@ -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!"); @@ -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 myList = new List{ 1, 2, 3, 4 }; + Console.WriteLine(myList); return 42; } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index e551c16dfe5c3..4a2abbccfde0b 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -192,7 +192,7 @@ private object ConvertJSToCSharpType(JToken variable) case "boolean": return value?.Value(); case "object": - return null; + return variable; case "void": return null; } @@ -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() }; } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs index e08904a99b6eb..e5823ea0c86c2 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs @@ -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) { @@ -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) @@ -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 GetValueFromObject(JToken objRet, CancellationToken token) @@ -51,7 +57,7 @@ public async Task GetValueFromObject(JToken objRet, CancellationToken t { if (DotnetObjectId.TryParse(objRet?["value"]?["objectId"]?.Value(), 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().Equals("_message")); exceptionObjectMessage["value"]["value"] = objRet["value"]?["className"]?.Value() + ": " + exceptionObjectMessage["value"]?["value"]?.Value(); return exceptionObjectMessage["value"]?.Value(); @@ -67,8 +73,8 @@ public async Task 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(), objRet["name"].Value(), token); + commandParamsWriter.WriteObj(objectId, sdbHelper); + var ret = await sdbHelper.InvokeMethod(sessionId, commandParams.ToArray(), objRet["get"]["methodId"].Value(), objRet["name"].Value(), token); return await GetValueFromObject(ret, token); } @@ -88,27 +94,27 @@ public async Task 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); } } @@ -118,8 +124,8 @@ public async Task 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); } } } @@ -204,6 +210,7 @@ public async Task Resolve(string varName, CancellationToken token) public async Task Resolve(InvocationExpressionSyntax method, Dictionary memberAccessValues, CancellationToken token) { var methodName = ""; + int isTryingLinq = 0; try { JObject rootObject = null; @@ -223,33 +230,56 @@ public async Task Resolve(InvocationExpressionSyntax method, Dictionary if (rootObject != null) { DotnetObjectId.TryParse(rootObject?["objectId"]?.Value(), 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 + { + 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) + 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); } } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index 5cc5df7914462..f9710ef8bb7be 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -176,7 +176,8 @@ protected override async Task 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; } } } @@ -617,13 +618,18 @@ private async Task OnSetVariableValue(MessageId id, int scopeId, string va internal async Task 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(); - if (args["ownProperties"] != null) - ownProperties = args["ownProperties"].Value(); + if (args["accessorPropertiesOnly"] != null && args["accessorPropertiesOnly"].Value()) + { + objectValuesOpt |= GetObjectCommandOptions.AccessorPropertiesOnly; + accessorPropertiesOnly = true; + } + if (args["ownProperties"] != null && args["ownProperties"].Value()) + { + objectValuesOpt |= GetObjectCommandOptions.OwnProperties; + } } //Console.WriteLine($"RuntimeGetProperties - {args}"); try { @@ -639,7 +645,7 @@ internal async Task 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": @@ -717,7 +723,7 @@ private async Task 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) { @@ -764,7 +770,7 @@ private async Task 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) { @@ -911,7 +917,7 @@ private async Task 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().Equals("message")); var data = JObject.FromObject(new { diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index b508067902105..1b31923d5c4b7 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -16,6 +16,7 @@ using System.Text.RegularExpressions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp; +using System.Reflection; namespace Microsoft.WebAssembly.Diagnostics { @@ -49,6 +50,18 @@ internal enum TokenType MdtBaseType = 0x72000000, // Leave this on the high end value. This does not correspond to metadata table } + [Flags] + internal enum GetObjectCommandOptions + { + None = 0, + WithSetter = 1, + AccessorPropertiesOnly = 2, + OwnProperties = 4, + ForDebuggerProxyAttribute = 8, + ForDebuggerDisplayAttribute = 16, + WithProperties = 32 + } + internal enum CommandSet { Vm = 1, ObjectRef = 9, @@ -732,6 +745,20 @@ public async Task GetMethodToken(SessionId sessionId, int methodId, Cancell return retDebuggerCmdReader.ReadInt32() & 0xffffff; //token } + public async Task MakeGenericMethod(SessionId sessionId, int methodId, List genericTypes, CancellationToken token) + { + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(methodId); + commandParamsWriter.Write(genericTypes.Count); + foreach (var genericType in genericTypes) + { + commandParamsWriter.Write(genericType); + } + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdMethod.MakeGenericMethod, commandParams, token); + return retDebuggerCmdReader.ReadInt32(); + } + public async Task GetMethodIdByToken(SessionId sessionId, int assembly_id, int method_token, CancellationToken token) { var commandParams = new MemoryStream(); @@ -742,6 +769,53 @@ public async Task GetMethodIdByToken(SessionId sessionId, int assembly_id, return retDebuggerCmdReader.ReadInt32(); } + public async Task GetAssemblyIdFromType(SessionId sessionId, int type_id, CancellationToken token) + { + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(type_id); + commandParamsWriter.Write((int) MonoTypeNameFormat.FormatReflection); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetInfo, commandParams, token); + retDebuggerCmdReader.ReadString(); //namespace + retDebuggerCmdReader.ReadString(); //name + retDebuggerCmdReader.ReadString(); //formatted name + return retDebuggerCmdReader.ReadInt32(); + } + + public async Task> GetTypeParamsOrArgsForGenericType(SessionId sessionId, int type_id, CancellationToken token) + { + var ret = new List(); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(type_id); + commandParamsWriter.Write((int) MonoTypeNameFormat.FormatReflection); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetInfo, commandParams, token); + + retDebuggerCmdReader.ReadString(); //namespace + retDebuggerCmdReader.ReadString(); //name + retDebuggerCmdReader.ReadString(); //name full + retDebuggerCmdReader.ReadInt32(); //assembly_id + retDebuggerCmdReader.ReadInt32(); //module_id + retDebuggerCmdReader.ReadInt32(); //type_id + retDebuggerCmdReader.ReadInt32(); //rank type + retDebuggerCmdReader.ReadInt32(); //type token + retDebuggerCmdReader.ReadByte(); //rank + retDebuggerCmdReader.ReadInt32(); //flags + retDebuggerCmdReader.ReadByte(); + int nested = retDebuggerCmdReader.ReadInt32(); + for (int i = 0 ; i < nested; i++) + { + retDebuggerCmdReader.ReadInt32(); //nested type + } + retDebuggerCmdReader.ReadInt32(); //typeid + int generics = retDebuggerCmdReader.ReadInt32(); + for (int i = 0 ; i < generics; i++) + { + ret.Add(retDebuggerCmdReader.ReadInt32()); //generic type + } + return ret; + } + public async Task GetAssemblyIdFromMethod(SessionId sessionId, int methodId, CancellationToken token) { var commandParams = new MemoryStream(); @@ -783,12 +857,22 @@ public async Task GetAssemblyName(SessionId sessionId, int assembly_id, return retDebuggerCmdReader.ReadString(); } + public async Task GetFullAssemblyName(SessionId sessionId, int assemblyId, CancellationToken token) + { + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(assemblyId); + + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetName, commandParams, token); + var name = retDebuggerCmdReader.ReadString(); + return name; + } - public async Task GetAssemblyNameFull(SessionId sessionId, int assembly_id, CancellationToken token) + public async Task GetAssemblyFileNameFromId(SessionId sessionId, int assemblyId, CancellationToken token) { var commandParams = new MemoryStream(); var commandParamsWriter = new MonoBinaryWriter(commandParams); - commandParamsWriter.Write(assembly_id); + commandParamsWriter.Write(assemblyId); var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdAssembly.GetName, commandParams, token); var name = retDebuggerCmdReader.ReadString(); @@ -966,7 +1050,7 @@ public async Task TypeInitialize(SessionId sessionId, int typeId, Cancellat return retDebuggerCmdReader.ReadInt32(); } - public async Task> GetTypeFields(SessionId sessionId, int type_id, CancellationToken token) + public async Task> GetTypeFields(SessionId sessionId, int type_id, bool onlyPublic, CancellationToken token) { var ret = new List(); var commandParams = new MemoryStream(); @@ -981,8 +1065,10 @@ public async Task> GetTypeFields(SessionId sessionId, int t int fieldId = retDebuggerCmdReader.ReadInt32(); //fieldId string fieldNameStr = retDebuggerCmdReader.ReadString(); int typeId = retDebuggerCmdReader.ReadInt32(); //typeId - retDebuggerCmdReader.ReadInt32(); //attrs + int attrs = retDebuggerCmdReader.ReadInt32(); //attrs int isSpecialStatic = retDebuggerCmdReader.ReadInt32(); //is_special_static + if (onlyPublic && ((attrs & (int)MethodAttributes.Public) == 0)) + continue; if (isSpecialStatic == 1) continue; if (fieldNameStr.Contains("k__BackingField")) @@ -1007,10 +1093,8 @@ public string ReplaceCommonClassNames(string className) className = className.Replace("System.Byte", "byte"); return className; } - - public async Task GetDebuggerDisplayAttribute(SessionId sessionId, int objectId, int typeId, CancellationToken token) + internal async Task GetCAttrsFromType(SessionId sessionId, int objectId, int typeId, string attrName, CancellationToken token) { - string expr = ""; var invokeParams = new MemoryStream(); var invokeParamsWriter = new MonoBinaryWriter(invokeParams); var commandParams = new MemoryStream(); @@ -1024,64 +1108,22 @@ public async Task GetDebuggerDisplayAttribute(SessionId sessionId, int o for (int i = 0 ; i < count; i++) { var methodId = retDebuggerCmdReader.ReadInt32(); - if (methodId == 0) - continue; commandParams = new MemoryStream(); commandParamsWriter = new MonoBinaryWriter(commandParams); commandParamsWriter.Write(methodId); var retDebuggerCmdReader2 = await SendDebuggerAgentCommand(sessionId, CmdMethod.GetDeclaringType, commandParams, token); var customAttributeTypeId = retDebuggerCmdReader2.ReadInt32(); var customAttributeName = await GetTypeName(sessionId, customAttributeTypeId, token); - if (customAttributeName == "System.Diagnostics.DebuggerDisplayAttribute") - { - invokeParamsWriter.Write((byte)ValueTypeId.Null); - invokeParamsWriter.Write((byte)0); //not used - invokeParamsWriter.Write(0); //not used - var parmCount = retDebuggerCmdReader.ReadInt32(); - invokeParamsWriter.Write((int)1); - for (int j = 0; j < parmCount; j++) - { - invokeParamsWriter.Write((byte)retDebuggerCmdReader.ReadByte()); - invokeParamsWriter.Write(retDebuggerCmdReader.ReadInt32()); - } - var retMethod = await InvokeMethod(sessionId, invokeParams.ToArray(), methodId, "methodRet", token); - DotnetObjectId.TryParse(retMethod?["value"]?["objectId"]?.Value(), out DotnetObjectId dotnetObjectId); - var displayAttrs = await GetObjectValues(sessionId, int.Parse(dotnetObjectId.Value), true, false, false, false, token); - var displayAttrValue = displayAttrs.FirstOrDefault(attr => attr["name"].Value().Equals("Value")); - try { - ExecutionContext context = proxy.GetContext(sessionId); - var objectValues = await GetObjectValues(sessionId, objectId, true, false, false, false, token); - - var thisObj = CreateJObject("", "object", "", false, objectId:$"dotnet:object:{objectId}"); - thisObj["name"] = "this"; - objectValues.Add(thisObj); + if (customAttributeName == attrName) + return retDebuggerCmdReader; - var resolver = new MemberReferenceResolver(proxy, context, sessionId, objectValues, logger); - var dispAttrStr = displayAttrValue["value"]?["value"]?.Value(); - //bool noQuotes = false; - if (dispAttrStr.Contains(", nq")) - { - //noQuotes = true; - dispAttrStr = dispAttrStr.Replace(", nq", ""); - } - expr = "$\"" + dispAttrStr + "\""; - JObject retValue = await resolver.Resolve(expr, token); - if (retValue == null) - retValue = await EvaluateExpression.CompileAndRunTheExpression(expr, resolver, token); - return retValue?["value"]?.Value(); - } - catch (Exception) - { - logger.LogDebug($"Could not evaluate DebuggerDisplayAttribute - {expr}"); - return null; - } - } - else + //reading buffer only to advance the reader to the next cattr + for (int k = 0 ; k < 2; k++) { var parmCount = retDebuggerCmdReader.ReadInt32(); for (int j = 0; j < parmCount; j++) { - //to read parameters + //to typed_args await CreateJObjectForVariableValue(sessionId, retDebuggerCmdReader, "varName", false, -1, token); } } @@ -1089,20 +1131,57 @@ public async Task GetDebuggerDisplayAttribute(SessionId sessionId, int o return null; } - public async Task GetTypeName(SessionId sessionId, int type_id, CancellationToken token) + public async Task GetValueFromDebuggerDisplayAttribute(SessionId sessionId, int objectId, int typeId, CancellationToken token) { - var commandParams = new MemoryStream(); - var commandParamsWriter = new MonoBinaryWriter(commandParams); - commandParamsWriter.Write(type_id); - commandParamsWriter.Write((int) MonoTypeNameFormat.FormatReflection); - var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetInfo, commandParams, token); - - retDebuggerCmdReader.ReadString(); - - retDebuggerCmdReader.ReadString(); - - string className = retDebuggerCmdReader.ReadString(); + string expr = ""; + try { + var getCAttrsRetReader = await GetCAttrsFromType(sessionId, objectId, typeId, "System.Diagnostics.DebuggerDisplayAttribute", token); + if (getCAttrsRetReader == null) + return null; + + var invokeParams = new MemoryStream(); + var invokeParamsWriter = new MonoBinaryWriter(invokeParams); + invokeParamsWriter.Write((byte)ValueTypeId.Null); + invokeParamsWriter.Write((byte)0); //not used + invokeParamsWriter.Write(0); //not used + var parmCount = getCAttrsRetReader.ReadInt32(); + var monoType = (ElementType) getCAttrsRetReader.ReadByte(); //MonoTypeEnum -> MONO_TYPE_STRING + if (monoType != ElementType.String) + return null; + + var stringId = getCAttrsRetReader.ReadInt32(); + var dispAttrStr = await GetStringValue(sessionId, stringId, token); + ExecutionContext context = proxy.GetContext(sessionId); + JArray objectValues = await GetObjectValues(sessionId, objectId, GetObjectCommandOptions.WithProperties | GetObjectCommandOptions.ForDebuggerDisplayAttribute, token); + + var thisObj = CreateJObject(value: "", type: "object", description: "", writable: false, objectId: $"dotnet:object:{objectId}"); + thisObj["name"] = "this"; + objectValues.Add(thisObj); + + var resolver = new MemberReferenceResolver(proxy, context, sessionId, objectValues, logger); + if (dispAttrStr.Length == 0) + return null; + + if (dispAttrStr.Contains(", nq")) + { + dispAttrStr = dispAttrStr.Replace(", nq", ""); + } + expr = "$\"" + dispAttrStr + "\""; + JObject retValue = await resolver.Resolve(expr, token); + if (retValue == null) + retValue = await EvaluateExpression.CompileAndRunTheExpression(expr, resolver, token); + return retValue?["value"]?.Value(); + } + catch (Exception) + { + logger.LogDebug($"Could not evaluate DebuggerDisplayAttribute - {expr} - {await GetTypeName(sessionId, typeId, token)}"); + } + return null; + } + public async Task GetTypeName(SessionId sessionId, int typeId, CancellationToken token) + { + string className = await GetTypeNameOriginal(sessionId, typeId, token); className = className.Replace("+", "."); className = Regex.Replace(className, @"`\d+", ""); className = className.Replace("[]", "__SQUARED_BRACKETS__"); @@ -1114,6 +1193,18 @@ public async Task GetTypeName(SessionId sessionId, int type_id, Cancella return className; } + public async Task GetTypeNameOriginal(SessionId sessionId, int typeId, CancellationToken token) + { + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(typeId); + commandParamsWriter.Write((int) MonoTypeNameFormat.FormatReflection); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetInfo, commandParams, token); + retDebuggerCmdReader.ReadString(); //namespace + retDebuggerCmdReader.ReadString(); //class name + return retDebuggerCmdReader.ReadString(); //class name formatted + } + public async Task GetStringValue(SessionId sessionId, int string_id, CancellationToken token) { var commandParams = new MemoryStream(); @@ -1186,7 +1277,7 @@ public async Task GetMethodIdByName(SessionId sessionId, int type_id, strin var commandParamsWriter = new MonoBinaryWriter(commandParams); commandParamsWriter.Write((int)type_id); commandParamsWriter.WriteString(method_name); - commandParamsWriter.Write((int)(0x10 | 4 | 0x20)); //instance methods + commandParamsWriter.Write((int)(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)); commandParamsWriter.Write((int)1); //case sensitive var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdType.GetMethodsByNameFlags, commandParams, token); var nMethods = retDebuggerCmdReader.ReadInt32(); @@ -1466,7 +1557,7 @@ public async Task CreateJObjectForObject(SessionId sessionId, MonoBinar var className = ""; var type_id = await GetTypeIdFromObject(sessionId, objectId, false, token); className = await GetTypeName(sessionId, type_id[0], token); - var debuggerDisplayAttribute = await GetDebuggerDisplayAttribute(sessionId, objectId, type_id[0], token); + var debuggerDisplayAttribute = await GetValueFromDebuggerDisplayAttribute(sessionId, objectId, type_id[0], token); var description = className.ToString(); if (debuggerDisplayAttribute != null) @@ -1497,7 +1588,7 @@ public async Task CreateJObjectForValueType(SessionId sessionId, MonoBi var className = await GetTypeName(sessionId, typeId, token); var description = className; var numFields = retDebuggerCmdReader.ReadInt32(); - var fields = await GetTypeFields(sessionId, typeId, token); + var fields = await GetTypeFields(sessionId, typeId, onlyPublic: false, token); JArray valueTypeFields = new JArray(); if (className.IndexOf("System.Nullable<") == 0) //should we call something on debugger-agent to check??? { @@ -1581,7 +1672,6 @@ public async Task CreateJObjectForVariableValue(SessionId sessionId, Mo case ElementType.I: case ElementType.U: case ElementType.Void: - case (ElementType)ValueTypeId.Type: case (ElementType)ValueTypeId.VType: case (ElementType)ValueTypeId.FixedArray: ret = JObject.FromObject(new { @@ -1694,10 +1784,23 @@ public async Task CreateJObjectForVariableValue(SessionId sessionId, Mo ret = await CreateJObjectForNull(sessionId, retDebuggerCmdReader, token); break; } + case (ElementType)ValueTypeId.Type: + { + retDebuggerCmdReader.ReadInt32(); + break; + } + default: + { + logger.LogDebug($"Could not evaluate CreateJObjectForVariableValue invalid type {etype}"); + break; + } + } + if (ret != null) + { + if (isOwn) + ret["isOwn"] = true; + ret["name"] = name; } - if (isOwn) - ret["isOwn"] = true; - ret["name"] = name; return ret; } @@ -1742,7 +1845,7 @@ public async Task GetHoistedLocalVariables(SessionId sessionId, int obje { if (int.TryParse(dotnetObjectId.Value, out int objectIdToGetInfo) && !objectsAlreadyRead.Contains(objectIdToGetInfo)) { - var asyncLocalsFromObject = await GetObjectValues(sessionId, objectIdToGetInfo, true, false, false, false, token); + var asyncLocalsFromObject = await GetObjectValues(sessionId, objectIdToGetInfo, GetObjectCommandOptions.WithProperties, token); var hoistedLocalVariable = await GetHoistedLocalVariables(sessionId, objectIdToGetInfo, asyncLocalsFromObject, token); asyncLocalsFull = new JArray(asyncLocalsFull.Union(hoistedLocalVariable)); } @@ -1785,8 +1888,15 @@ public async Task StackFrameGetValues(SessionId sessionId, MethodInfo me retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdFrame.GetThis, commandParams, token); retDebuggerCmdReader.ReadByte(); //ignore type var objectId = retDebuggerCmdReader.ReadInt32(); - var asyncLocals = await GetObjectValues(sessionId, objectId, true, false, false, false, token); - asyncLocals = await GetHoistedLocalVariables(sessionId, objectId, asyncLocals, token); + var asyncLocals = await GetObjectValues(sessionId, objectId, GetObjectCommandOptions.WithProperties, token); + asyncLocals = new JArray(asyncLocals.Where( asyncLocal => !asyncLocal["name"].Value().Contains("<>") || asyncLocal["name"].Value().EndsWith("__this"))); + foreach (var asyncLocal in asyncLocals) + { + if (asyncLocal["name"].Value().EndsWith("__this", StringComparison.Ordinal)) + asyncLocal["name"] = "this"; + else if (asyncLocal["name"].Value().Contains('<')) + asyncLocal["name"] = Regex.Match(asyncLocal["name"].Value(), @"\<([^)]*)\>").Groups[1].Value; + } return asyncLocals; } @@ -1880,7 +1990,6 @@ public async Task GetArrayValues(SessionId sessionId, int arrayId, Cance } public async Task EnableExceptions(SessionId sessionId, string state, CancellationToken token) { - var commandParams = new MemoryStream(); var commandParamsWriter = new MonoBinaryWriter(commandParams); commandParamsWriter.Write((byte)EventKind.Exception); @@ -1902,9 +2011,88 @@ public async Task EnableExceptions(SessionId sessionId, string state, Canc var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, commandParams, token); return true; } - public async Task GetObjectValues(SessionId sessionId, int objectId, bool withProperties, bool withSetter, bool accessorPropertiesOnly, bool ownProperties, CancellationToken token) + + public async Task GetTypeByName(SessionId sessionId, string typeToSearch, CancellationToken token) + { + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.WriteString(typeToSearch); + var retDebuggerCmdReader = await SendDebuggerAgentCommand(sessionId, CmdVM.GetTypes, commandParams, token); + var count = retDebuggerCmdReader.ReadInt32(); //count ret + return retDebuggerCmdReader.ReadInt32(); + } + + public async Task GetValuesFromDebuggerProxyAttribute(SessionId sessionId, int objectId, int typeId, CancellationToken token) + { + try { + var getCAttrsRetReader = await GetCAttrsFromType(sessionId, objectId, typeId, "System.Diagnostics.DebuggerTypeProxyAttribute", token); + var methodId = -1; + if (getCAttrsRetReader == null) + return null; + var invokeParams = new MemoryStream(); + var invokeParamsWriter = new MonoBinaryWriter(invokeParams); + invokeParamsWriter.Write((byte)ValueTypeId.Null); + invokeParamsWriter.Write((byte)0); //not used + invokeParamsWriter.Write(0); //not used + var parmCount = getCAttrsRetReader.ReadInt32(); + invokeParamsWriter.Write((int)1); + for (int j = 0; j < parmCount; j++) + { + var monoTypeId = getCAttrsRetReader.ReadByte(); + if ((ValueTypeId)monoTypeId != ValueTypeId.Type) + continue; + var cAttrTypeId = getCAttrsRetReader.ReadInt32(); + var commandParams = new MemoryStream(); + var commandParamsWriter = new MonoBinaryWriter(commandParams); + commandParamsWriter.Write(cAttrTypeId); + var className = await GetTypeNameOriginal(sessionId, cAttrTypeId, token); + if (className.IndexOf('[') > 0) + { + className = className.Remove(className.IndexOf('[')); + var assemblyId = await GetAssemblyIdFromType(sessionId, cAttrTypeId, token); + var assemblyName = await GetFullAssemblyName(sessionId, assemblyId, token); + var typeToSearch = className; + typeToSearch += "[["; //System.Collections.Generic.List`1[[System.Int32,mscorlib,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089]],mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + List genericTypeArgs = await GetTypeParamsOrArgsForGenericType(sessionId, typeId, token); + for (int k = 0; k < genericTypeArgs.Count; k++) + { + var assemblyIdArg = await GetAssemblyIdFromType(sessionId, genericTypeArgs[k], token); + var assemblyNameArg = await GetFullAssemblyName(sessionId, assemblyIdArg, token); + var classNameArg = await GetTypeNameOriginal(sessionId, genericTypeArgs[k], token); + typeToSearch += classNameArg +", " + assemblyNameArg; + } + typeToSearch += "]]"; + typeToSearch += ", " + assemblyName; + var genericTypeId = await GetTypeByName(sessionId, typeToSearch, token); + methodId = await GetMethodIdByName(sessionId, genericTypeId, ".ctor", token); + } + else + methodId = await GetMethodIdByName(sessionId, cAttrTypeId, ".ctor", token); + invokeParamsWriter.Write((byte)ElementType.Object); + invokeParamsWriter.Write(objectId); + + var retMethod = await InvokeMethod(sessionId, invokeParams.ToArray(), methodId, "methodRet", token); + DotnetObjectId.TryParse(retMethod?["value"]?["objectId"]?.Value(), out DotnetObjectId dotnetObjectId); + var displayAttrs = await GetObjectValues(sessionId, int.Parse(dotnetObjectId.Value), GetObjectCommandOptions.WithProperties | GetObjectCommandOptions.ForDebuggerProxyAttribute, token); + return displayAttrs; + } + } + catch (Exception e) + { + logger.LogDebug($"Could not evaluate DebuggerTypeProxyAttribute of type {await GetTypeName(sessionId, typeId, token)} - {e}"); + } + return null; + } + + public async Task GetObjectValues(SessionId sessionId, int objectId, GetObjectCommandOptions getCommandType, CancellationToken token) { var typeId = await GetTypeIdFromObject(sessionId, objectId, true, token); + if (!getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerDisplayAttribute)) + { + var debuggerProxy = await GetValuesFromDebuggerProxyAttribute(sessionId, objectId, typeId[0], token); + if (debuggerProxy != null) + return debuggerProxy; + } var className = await GetTypeName(sessionId, typeId[0], token); JArray ret = new JArray(); if (await IsDelegate(sessionId, objectId, token)) @@ -1925,9 +2113,9 @@ public async Task GetObjectValues(SessionId sessionId, int objectId, boo } for (int i = 0; i < typeId.Count; i++) { - if (!accessorPropertiesOnly) + if (!getCommandType.HasFlag(GetObjectCommandOptions.AccessorPropertiesOnly)) { - var fields = await GetTypeFields(sessionId, typeId[i], token); + var fields = await GetTypeFields(sessionId, typeId[i], onlyPublic: getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute), token); JArray objectFields = new JArray(); var commandParams = new MemoryStream(); @@ -1951,7 +2139,7 @@ public async Task GetObjectValues(SessionId sessionId, int objectId, boo if (ret.Where(attribute => attribute["name"].Value().Equals(fieldValue["name"].Value())).Any()) { continue; } - if (withSetter) + if (getCommandType.HasFlag(GetObjectCommandOptions.WithSetter)) { var command_params_to_set = new MemoryStream(); var command_params_writer_to_set = new MonoBinaryWriter(command_params_to_set); @@ -1971,12 +2159,12 @@ public async Task GetObjectValues(SessionId sessionId, int objectId, boo } ret = new JArray(ret.Union(objectFields)); } - if (!withProperties) + if (!getCommandType.HasFlag(GetObjectCommandOptions.WithProperties)) return ret; var command_params_obj = new MemoryStream(); var commandParamsObjWriter = new MonoBinaryWriter(command_params_obj); commandParamsObjWriter.WriteObj(new DotnetObjectId("object", $"{objectId}"), this); - var props = await CreateJArrayForProperties(sessionId, typeId[i], command_params_obj.ToArray(), ret, false, $"dotnet:object:{objectId}", i == 0, token); + var props = await CreateJArrayForProperties(sessionId, typeId[i], command_params_obj.ToArray(), ret, getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute), $"dotnet:object:{objectId}", i == 0, token); ret = new JArray(ret.Union(props)); // ownProperties @@ -1988,13 +2176,13 @@ public async Task GetObjectValues(SessionId sessionId, int objectId, boo /*if (accessorPropertiesOnly) break;*/ } - if (accessorPropertiesOnly) + if (getCommandType.HasFlag(GetObjectCommandOptions.AccessorPropertiesOnly)) { var retAfterRemove = new JArray(); List> allFields = new List>(); for (int i = 0; i < typeId.Count; i++) { - var fields = await GetTypeFields(sessionId, typeId[i], token); + var fields = await GetTypeFields(sessionId, typeId[i], onlyPublic: false, token); allFields.Add(fields); } foreach (var item in ret) @@ -2024,7 +2212,7 @@ public async Task GetObjectValues(SessionId sessionId, int objectId, boo public async Task GetObjectProxy(SessionId sessionId, int objectId, CancellationToken token) { - var ret = await GetObjectValues(sessionId, objectId, false, true, false, false, token); + var ret = await GetObjectValues(sessionId, objectId, GetObjectCommandOptions.WithSetter, token); var typeIds = await GetTypeIdFromObject(sessionId, objectId, true, token); foreach (var typeId in typeIds) { diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/CustomViewTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/CustomViewTests.cs index c848c03c303b7..09bee325d72c4 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/CustomViewTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/CustomViewTests.cs @@ -15,9 +15,9 @@ namespace DebuggerTests public class CustomViewTests : DebuggerTestBase { [Fact] - public async Task CustomView() + public async Task UsingDebuggerDisplay() { - var bp = await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.DebuggerCustomViewTest", "run", 5); + var bp = await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.DebuggerCustomViewTest", "run", 6); var pause_location = await EvaluateAndCheck( "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.DebuggerCustomViewTest:run'); }, 1);", "dotnet://debugger-test.dll/debugger-custom-view-test.cs", @@ -27,7 +27,35 @@ public async Task CustomView() var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); CheckObject(locals, "a", "DebuggerTests.WithDisplayString", description:"Some one Value 2 End"); - CheckObject(locals, "c", "DebuggerTests.DebuggerDisplayMethodTest", description: "First Int:32 Second Int:43"); + CheckObject(locals, "c", "DebuggerTests.DebuggerDisplayMethodTest", description: "First Int:32 Second Int:43"); + CheckObject(locals, "myList", "System.Collections.Generic.List", description: "Count = 4"); + } + + [Fact] + public async Task UsingDebuggerTypeProxy() + { + var bp = await SetBreakpointInMethod("debugger-test.dll", "DebuggerTests.DebuggerCustomViewTest", "run", 6); + var pause_location = await EvaluateAndCheck( + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.DebuggerCustomViewTest:run'); }, 1);", + "dotnet://debugger-test.dll/debugger-custom-view-test.cs", + bp.Value["locations"][0]["lineNumber"].Value(), + bp.Value["locations"][0]["columnNumber"].Value(), + "run"); + + var frame = pause_location["callFrames"][0]; + var locals = await GetProperties(frame["callFrameId"].Value()); + CheckObject(locals, "myList", "System.Collections.Generic.List", description: "Count = 4"); + var props = await GetObjectOnFrame(frame, "myList"); + Assert.Equal(1, props.Count()); + + CheckArray(props, "Items", "int[]", 4); + + CheckObject(locals, "b", "DebuggerTests.WithProxy", description:"DebuggerTests.WithProxy"); + props = await GetObjectOnFrame(frame, "b"); + CheckString(props, "Val2", "one"); + + await EvaluateOnCallFrameAndCheck(frame["callFrameId"].Value(), + ("listToTestToList.ToList()", TObject("System.Collections.Generic.List", description: "Count = 11"))); } } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs index 081cd28732d44..e64348bc675eb 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs @@ -400,7 +400,7 @@ public async Task InspectValueTypeMethodArgsWhileStepping(bool use_cfo) var ss_local_gs = new { StringField = TString("ss_local#SimpleStruct#string#0#SimpleStruct#gs#StringField"), - List = TObject("System.Collections.Generic.List"), + List = TObject("System.Collections.Generic.List", description: "Count = 1"), Options = TEnum("DebuggerTests.Options", "Option1") }; @@ -447,7 +447,7 @@ public async Task InspectValueTypeMethodArgsWhileStepping(bool use_cfo) await CompareObjectPropertiesFor(ss_arg_props, "gs", new { StringField = TString("ValueTypesTest#MethodWithStructArgs#updated#gs#StringField#3"), - List = TObject("System.Collections.Generic.List"), + List = TObject("System.Collections.Generic.List", description: "Count = 1"), Options = TEnum("DebuggerTests.Options", "Option1") }); } @@ -463,7 +463,7 @@ public async Task InspectValueTypeMethodArgsWhileStepping(bool use_cfo) // Check ss_local.gs var gs_props = await GetObjectOnLocals(ss_arg_props, "gs"); CheckString(gs_props, "StringField", "ss_local#SimpleStruct#string#0#SimpleStruct#gs#StringField"); - CheckObject(gs_props, "List", "System.Collections.Generic.List"); + CheckObject(gs_props, "List", "System.Collections.Generic.List", description: "Count = 1"); } // ----------- Step back to the caller --------- diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs index 038af35dd71f2..91f42ab73e58d 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs @@ -201,14 +201,14 @@ await CheckInspectLocalsAtBreakpointSite( use_cfo: use_cfo, test_fn: (locals) => { - CheckObject(locals, "list", "System.Collections.Generic.Dictionary"); + CheckObject(locals, "list", "System.Collections.Generic.Dictionary", description: "Count = 0"); CheckObject(locals, "list_null", "System.Collections.Generic.Dictionary", is_null: true); CheckArray(locals, "list_arr", "System.Collections.Generic.Dictionary[]", 1); CheckObject(locals, "list_arr_null", "System.Collections.Generic.Dictionary[]", is_null: true); // Unused locals - CheckObject(locals, "list_unused", "System.Collections.Generic.Dictionary"); + CheckObject(locals, "list_unused", "System.Collections.Generic.Dictionary", description: "Count = 0"); CheckObject(locals, "list_null_unused", "System.Collections.Generic.Dictionary", is_null: true); CheckArray(locals, "list_arr_unused", "System.Collections.Generic.Dictionary[]", 1); @@ -296,7 +296,7 @@ public async Task InspectLocalsWithStructs(bool use_cfo) // Check ss_local.gs var gs_props = await GetObjectOnLocals(ss_local_props, "gs"); CheckString(gs_props, "StringField", "set in MethodWithLocalStructs#SimpleStruct#gs#StringField"); - CheckObject(gs_props, "List", "System.Collections.Generic.List"); + CheckObject(gs_props, "List", "System.Collections.Generic.List", description: "Count = 1"); } // Check gs_local's properties @@ -486,7 +486,7 @@ await CompareObjectPropertiesFor(ss_local_props, "gs", new { StringField = TString("set in MethodWithLocalStructsStaticAsync#SimpleStruct#gs#StringField"), - List = TObject("System.Collections.Generic.List"), + List = TObject("System.Collections.Generic.List", description: "Count = 1"), Options = TEnum("DebuggerTests.Options", "Option1") } ); @@ -497,7 +497,7 @@ await CompareObjectPropertiesFor(ss_local_props, "gs", await CheckProps(gs_local_props, new { StringField = TString("gs_local#GenericStruct#StringField"), - List = TObject("System.Collections.Generic.List"), + List = TObject("System.Collections.Generic.List", description: "Count = 2"), Options = TEnum("DebuggerTests.Options", "Option2") }, "gs_local"); diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-custom-view-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-custom-view-test.cs index 2d97a9b64a37e..ecdccf0251f9f 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-custom-view-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-custom-view-test.cs @@ -1,10 +1,10 @@ - // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Threading.Tasks; using System.Diagnostics; +using System.Collections.Generic; namespace DebuggerTests { @@ -65,9 +65,9 @@ public static void run() var a = new WithDisplayString(); var b = new WithProxy(); var c = new DebuggerDisplayMethodTest(); - Console.WriteLine(a); - Console.WriteLine(b); - Console.WriteLine(c); + List myList = new List{ 1, 2, 3, 4 }; + var listToTestToList = System.Linq.Enumerable.Range(1, 11); + Console.WriteLine("break here"); } } }