diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 5bc2aaf564451f..f98932d7372ed9 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -701,7 +701,9 @@ internal sealed class TypeInfo internal int Token { get; } internal string Namespace { get; } internal bool IsCompilerGenerated { get; } + private bool NonUserCode { get; } public string FullName { get; } + internal bool IsNonUserCode => assembly.pdbMetadataReader == null || NonUserCode; public List Methods { get; } = new(); public Dictionary DebuggerBrowsableFields = new(); public Dictionary DebuggerBrowsableProperties = new(); @@ -769,8 +771,15 @@ internal TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDe continue; var container = metadataReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent; var attributeName = assembly.EnCGetString(metadataReader.GetTypeReference((TypeReferenceHandle)container).Name); - if (attributeName == nameof(CompilerGeneratedAttribute)) - IsCompilerGenerated = true; + switch (attributeName) + { + case nameof(CompilerGeneratedAttribute): + IsCompilerGenerated = true; + break; + case nameof(DebuggerNonUserCodeAttribute): + NonUserCode = true; + break; + } } void AppendToBrowsable(Dictionary dict, CustomAttributeHandleCollection customAttrs, string fieldName) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs index 6e8049e8666bcf..24f4d8522cb845 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs @@ -71,6 +71,7 @@ private static async Task ReadFieldValue( FieldAttributes.Public => "result", _ => "internal" }; + if (field.IsBackingField) { fieldValue["__isBackingField"] = true; @@ -567,13 +568,20 @@ public static async Task GetObjectMemberValues( for (int i = 0; i < typeIdsCnt; i++) { int typeId = typeIdsIncludingParents[i]; + var typeInfo = await sdbHelper.GetTypeInfo(typeId, token); + + if (typeInfo.Info.IsNonUserCode && getCommandType.HasFlag(GetObjectCommandOptions.JustMyCode)) + continue; + int parentTypeId = i + 1 < typeIdsCnt ? typeIdsIncludingParents[i + 1] : -1; string typeName = await sdbHelper.GetTypeName(typeId, token); // 0th id is for the object itself, and then its ancestors bool isOwn = i == 0; + IReadOnlyList thisTypeFields = await sdbHelper.GetTypeFields(typeId, token); if (!includeStatic) thisTypeFields = thisTypeFields.Where(f => !f.Attributes.HasFlag(FieldAttributes.Static)).ToList(); + if (thisTypeFields.Count > 0) { var allFields = await ExpandFieldValues( diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index 1626bb2f9e9d4e..7d2245da67784a 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -745,6 +745,8 @@ internal async Task> RuntimeGetObjectMembers(Sess if (args["forDebuggerDisplayAttribute"]?.Value() == true) getObjectOptions |= GetObjectCommandOptions.ForDebuggerDisplayAttribute; } + if (JustMyCode) + getObjectOptions |= GetObjectCommandOptions.JustMyCode; try { switch (objectId.Scheme) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 3b3de88f56a9c7..3a25a7fac07929 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -59,7 +59,8 @@ internal enum GetObjectCommandOptions OwnProperties = 4, ForDebuggerProxyAttribute = 8, ForDebuggerDisplayAttribute = 16, - WithProperties = 32 + WithProperties = 32, + JustMyCode = 64 } internal enum CommandSet { diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs index 5e0f0d6802c5a9..50ebc7704388ce 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs @@ -1039,5 +1039,52 @@ await EvaluateAndCheck( } ); } + + [Theory] + [InlineData("ClassInheritsFromClassWithoutDebugSymbols", 1287, true)] + [InlineData("ClassInheritsFromClassWithoutDebugSymbols", 1287, false)] + [InlineData("ClassInheritsFromNonUserCodeClass", 1335, true)] + [InlineData("ClassInheritsFromNonUserCodeClass", 1335, false)] + [InlineData("ClassInheritsFromNonUserCodeClassThatInheritsFromNormalClass", 1352, true)] + [InlineData("ClassInheritsFromNonUserCodeClassThatInheritsFromNormalClass", 1352, false)] + public async Task InspectThisThatInheritsFromClassNonUserCode(string class_name, int line, bool jmc) + { + await SetJustMyCode(jmc); + var expression = "{{ invoke_static_method('[debugger-test] " + class_name + ":Run'); }}"; + + await EvaluateAndCheck( + "window.setTimeout(function() {" + expression + "; }, 1);", + "dotnet://debugger-test.dll/debugger-test.cs", line, 8, + $"{class_name}.CallMethod", + locals_fn: async (locals) => + { + var this_props = await GetObjectOnLocals(locals, "this"); + if (jmc) + { + await CheckProps(this_props, new + { + myField = TNumber(0), + myField2 = TNumber(0), + }, "this_props", num_fields: 2); + } + else + { + await CheckProps(this_props, new + { + propA = TNumber(10), + propB = TNumber(20), + propC = TNumber(30), + d = TNumber(40), + e = TNumber(50), + f = TNumber(60), + G = TGetter("G"), + H = TGetter("H"), + myField = TNumber(0), + myField2 = TNumber(0), + }, "this_props", num_fields: 10); + } + } + ); + } } } diff --git a/src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/debugger-test-with-non-user-code-class.csproj b/src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/debugger-test-with-non-user-code-class.csproj new file mode 100644 index 00000000000000..c0d42d7f25cde5 --- /dev/null +++ b/src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/debugger-test-with-non-user-code-class.csproj @@ -0,0 +1,4 @@ + + + + diff --git a/src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/test.cs b/src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/test.cs new file mode 100644 index 00000000000000..af11a6329d0d26 --- /dev/null +++ b/src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/test.cs @@ -0,0 +1,41 @@ +using System; + +namespace DebuggerTests +{ + public class NormalClass + { + public int myField2; + } + + [System.Diagnostics.DebuggerNonUserCode] + public class ClassNonUserCodeToInheritThatInheritsFromNormalClass : NormalClass + { + private int propA {get;} + public int propB {get;} + protected int propC {get;} + private int d; + public int e; + protected int f; + public int G + { + get {return f + 1;} + } + public int H => f; + + public ClassNonUserCodeToInheritThatInheritsFromNormalClass() + { + propA = 10; + propB = 20; + propC = 30; + d = 40; + e = 50; + f = 60; + Console.WriteLine(propA); + Console.WriteLine(propB); + Console.WriteLine(propC); + Console.WriteLine(d); + Console.WriteLine(e); + Console.WriteLine(f); + } + } +} diff --git a/src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/debugger-test-without-debug-symbols-to-load.csproj b/src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/debugger-test-without-debug-symbols-to-load.csproj new file mode 100644 index 00000000000000..34367db0bff2da --- /dev/null +++ b/src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/debugger-test-without-debug-symbols-to-load.csproj @@ -0,0 +1,6 @@ + + + none + false + + diff --git a/src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/test.cs b/src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/test.cs new file mode 100644 index 00000000000000..5dcdc29f93f2d0 --- /dev/null +++ b/src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/test.cs @@ -0,0 +1,35 @@ +using System; + +namespace DebuggerTests +{ + public class ClassWithoutDebugSymbolsToInherit + { + private int propA {get;} + public int propB {get;} + protected int propC {get;} + private int d; + public int e; + protected int f; + public int G + { + get {return f + 1;} + } + public int H => f; + + public ClassWithoutDebugSymbolsToInherit() + { + propA = 10; + propB = 20; + propC = 30; + d = 40; + e = 50; + f = 60; + Console.WriteLine(propA); + Console.WriteLine(propB); + Console.WriteLine(propC); + Console.WriteLine(d); + Console.WriteLine(e); + Console.WriteLine(f); + } + } +} diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs index 6424ca3d5cdd93..6b5f84561beb9e 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs @@ -1275,3 +1275,83 @@ public static void MethodWithHiddenLinesAtTheEnd3() #line default } +public class ClassInheritsFromClassWithoutDebugSymbols : DebuggerTests.ClassWithoutDebugSymbolsToInherit +{ + public static void Run() + { + var myVar = new ClassInheritsFromClassWithoutDebugSymbols(); + myVar.CallMethod(); + } + + public void CallMethod() + { + System.Diagnostics.Debugger.Break(); + } + public int myField2; + public int myField; +} + +[System.Diagnostics.DebuggerNonUserCode] +public class ClassNonUserCodeToInherit +{ + private int propA {get;} + public int propB {get;} + protected int propC {get;} + private int d; + public int e; + protected int f; + public int G + { + get {return f + 1;} + } + public int H => f; + + public ClassNonUserCodeToInherit() + { + propA = 10; + propB = 20; + propC = 30; + d = 40; + e = 50; + f = 60; + Console.WriteLine(propA); + Console.WriteLine(propB); + Console.WriteLine(propC); + Console.WriteLine(d); + Console.WriteLine(e); + Console.WriteLine(f); + } +} + +public class ClassInheritsFromNonUserCodeClass : ClassNonUserCodeToInherit +{ + public static void Run() + { + var myVar = new ClassInheritsFromNonUserCodeClass(); + myVar.CallMethod(); + } + + public void CallMethod() + { + System.Diagnostics.Debugger.Break(); + } + + public int myField2; + public int myField; +} + +public class ClassInheritsFromNonUserCodeClassThatInheritsFromNormalClass : DebuggerTests.ClassNonUserCodeToInheritThatInheritsFromNormalClass +{ + public static void Run() + { + var myVar = new ClassInheritsFromNonUserCodeClassThatInheritsFromNormalClass(); + myVar.CallMethod(); + } + + public void CallMethod() + { + System.Diagnostics.Debugger.Break(); + } + + public int myField; +} \ No newline at end of file diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj index 42edcb663f906a..0ea4fcc719f4b8 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj @@ -21,6 +21,8 @@ + + @@ -44,7 +46,6 @@ debugger-main.js -1 - true @@ -52,6 +53,8 @@ + + @@ -63,6 +66,7 @@ +