diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 9fe5205394e24..6ca97d9c4785f 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -331,6 +331,8 @@ internal class MethodInfo public bool IsStatic() => (methodDef.Attributes & MethodAttributes.Static) != 0; public int IsAsync { get; set; } public bool IsHiddenFromDebugger { get; } + public TypeInfo TypeInfo { get; } + public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, int token, SourceFile source, TypeInfo type, MetadataReader asmMetadataReader, MetadataReader pdbMetadataReader) { this.IsAsync = -1; @@ -343,6 +345,7 @@ public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, this.Name = asmMetadataReader.GetString(methodDef.Name); this.pdbMetadataReader = pdbMetadataReader; this.IsEnCMethod = false; + this.TypeInfo = type; if (!DebugInformation.SequencePointsBlob.IsNil) { var sps = DebugInformation.GetSequencePoints(); @@ -475,6 +478,7 @@ internal class TypeInfo private TypeDefinition type; private List methods; internal int Token { get; } + internal string Namespace { get; } public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefinition type) { @@ -484,21 +488,20 @@ public TypeInfo(AssemblyInfo assembly, TypeDefinitionHandle typeHandle, TypeDefi this.type = type; methods = new List(); Name = metadataReader.GetString(type.Name); - var namespaceName = ""; if (type.IsNested) { var declaringType = metadataReader.GetTypeDefinition(type.GetDeclaringType()); Name = metadataReader.GetString(declaringType.Name) + "/" + Name; - namespaceName = metadataReader.GetString(declaringType.Namespace); + Namespace = metadataReader.GetString(declaringType.Namespace); } else { - namespaceName = metadataReader.GetString(type.Namespace); + Namespace = metadataReader.GetString(type.Namespace); } - - if (namespaceName.Length > 0) - namespaceName += "."; - FullName = namespaceName + Name; + if (Namespace.Length > 0) + FullName = Namespace + "." + Name; + else + FullName = Name; } public TypeInfo(AssemblyInfo assembly, string name) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs index 5c7b4d3d1405f..97f8767f6e235 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs @@ -119,13 +119,35 @@ public async Task TryToRunOnLoadedClasses(string varName, CancellationT } } var store = await proxy.LoadStore(sessionId, token); - foreach (var asm in store.assemblies) + var methodInfo = ctx.CallStack.FirstOrDefault(s => s.Id == scopeId)?.Method?.Info; + var classNameToFindWithNamespace = + string.IsNullOrEmpty(methodInfo?.TypeInfo?.Namespace) ? + classNameToFind : + methodInfo.TypeInfo.Namespace + "." + classNameToFind; + + var searchResult = await TryFindNameInAssembly(store.assemblies, classNameToFindWithNamespace); + if (searchResult == null) + searchResult = await TryFindNameInAssembly(store.assemblies, classNameToFind); + if (searchResult != null) + typeId = (int)searchResult; + + async Task TryGetTypeIdFromName(string typeName, AssemblyInfo assembly) { - var type = asm.GetTypeByName(classNameToFind); - if (type != null) + var type = assembly.GetTypeByName(typeName); + if (type == null) + return null; + return await sdbHelper.GetTypeIdFromToken(sessionId, assembly.DebugId, type.Token, token); + } + + async Task TryFindNameInAssembly(List assemblies, string name) + { + foreach (var asm in assemblies) { - typeId = await sdbHelper.GetTypeIdFromToken(sessionId, asm.DebugId, type.Token, token); + var typeId = await TryGetTypeIdFromName(name, asm); + if (typeId != null) + return typeId; } + return null; } } return null; @@ -196,12 +218,13 @@ public async Task Resolve(string varName, CancellationToken token) } else { - rootObject = await TryToRunOnLoadedClasses(varName, token); - return rootObject; + break; } } } } + if (rootObject == null) + rootObject = await TryToRunOnLoadedClasses(varName, token); scopeCache.MemberReferences[varName] = rootObject; return rootObject; } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 524ff68fb9e2d..d07912f8b589e 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -36,6 +36,12 @@ public static IEnumerable InstanceMethodForTypeMembersTestData(string } } + public static IEnumerable EvaluateStaticClassFromStaticMethodTestData(string type_name) + { + yield return new object[] { type_name, "EvaluateAsyncMethods", "EvaluateAsyncMethods", true }; + yield return new object[] { type_name, "EvaluateMethods", "EvaluateMethods", false }; + } + [Theory] [MemberData(nameof(InstanceMethodForTypeMembersTestData), parameters: "DebuggerTests.EvaluateTestsStructWithProperties")] [MemberData(nameof(InstanceMethodForTypeMembersTestData), parameters: "DebuggerTests.EvaluateTestsClassWithProperties")] @@ -695,6 +701,69 @@ await EvaluateOnCallFrameAndCheck(id, ("DebuggerTests.EvaluateStaticClass.StaticPropertyWithError", TString("System.Exception: not implemented"))); }); + [Theory] + [MemberData(nameof(EvaluateStaticClassFromStaticMethodTestData), parameters: "DebuggerTests.EvaluateMethodTestsClass")] + [MemberData(nameof(EvaluateStaticClassFromStaticMethodTestData), parameters: "EvaluateMethodTestsClass")] + public async Task EvaluateStaticClassFromStaticMethod(string type, string method, string bp_function_name, bool is_async) + => await CheckInspectLocalsAtBreakpointSite( + type, method, 1, bp_function_name, + $"window.setTimeout(function() {{ invoke_static_method ('[debugger-test] {type}:{method}'); }})", + wait_for_event_fn: async (pause_location) => + { + var id = pause_location["callFrames"][0]["callFrameId"].Value(); + + var frame = pause_location["callFrames"][0]; + + await EvaluateOnCallFrameAndCheck(id, + ("EvaluateStaticClass.StaticField1", TNumber(10)), + ("EvaluateStaticClass.StaticProperty1", TString("StaticProperty1")), + ("EvaluateStaticClass.StaticPropertyWithError", TString("System.Exception: not implemented")), + ("DebuggerTests.EvaluateStaticClass.StaticField1", TNumber(10)), + ("DebuggerTests.EvaluateStaticClass.StaticProperty1", TString("StaticProperty1")), + ("DebuggerTests.EvaluateStaticClass.StaticPropertyWithError", TString("System.Exception: not implemented"))); + }); + + [Fact] + public async Task EvaluateNonStaticClassWithStaticFields() => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTests.EvaluateMethodTestsClass", "EvaluateAsyncMethods", 3, "EvaluateAsyncMethods", + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateMethodTestsClass:EvaluateAsyncMethods'); })", + wait_for_event_fn: async (pause_location) => + { + var id = pause_location["callFrames"][0]["callFrameId"].Value(); + + var frame = pause_location["callFrames"][0]; + + await EvaluateOnCallFrameAndCheck(id, + ("DebuggerTests.EvaluateNonStaticClassWithStaticFields.StaticField1", TNumber(10)), + ("DebuggerTests.EvaluateNonStaticClassWithStaticFields.StaticProperty1", TString("StaticProperty1")), + ("DebuggerTests.EvaluateNonStaticClassWithStaticFields.StaticPropertyWithError", TString("System.Exception: not implemented")), + ("EvaluateNonStaticClassWithStaticFields.StaticField1", TNumber(10)), + ("EvaluateNonStaticClassWithStaticFields.StaticProperty1", TString("StaticProperty1")), + ("EvaluateNonStaticClassWithStaticFields.StaticPropertyWithError", TString("System.Exception: not implemented"))); + }); + + [Fact] + public async Task EvaluateStaticClassesFromDifferentNamespaceInDifferentFrames() => await CheckInspectLocalsAtBreakpointSite( + "DebuggerTestsV2.EvaluateStaticClass", "Run", 1, "Run", + "window.setTimeout(function() { invoke_static_method ('[debugger-test] DebuggerTests.EvaluateMethodTestsClass:EvaluateMethods'); })", + wait_for_event_fn: async (pause_location) => + { + var id_top = pause_location["callFrames"][0]["callFrameId"].Value(); + var frame = pause_location["callFrames"][0]; + + await EvaluateOnCallFrameAndCheck(id_top, + ("EvaluateStaticClass.StaticField1", TNumber(20)), + ("EvaluateStaticClass.StaticProperty1", TString("StaticProperty2")), + ("EvaluateStaticClass.StaticPropertyWithError", TString("System.Exception: not implemented"))); + + var id_second = pause_location["callFrames"][1]["callFrameId"].Value(); + + await EvaluateOnCallFrameAndCheck(id_second, + ("EvaluateStaticClass.StaticField1", TNumber(10)), + ("EvaluateStaticClass.StaticProperty1", TString("StaticProperty1")), + ("EvaluateStaticClass.StaticPropertyWithError", TString("System.Exception: not implemented"))); + }); + [Fact] public async Task EvaluateStaticClassInvalidField() => await CheckInspectLocalsAtBreakpointSite( "DebuggerTests.EvaluateMethodTestsClass/TestEvaluate", "run", 9, "run", diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs index 74153fa379376..8cafcdbbdf844 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs @@ -403,6 +403,14 @@ public static void EvaluateMethods() { TestEvaluate f = new TestEvaluate(); f.run(100, 200, "9000", "test", 45); + DebuggerTestsV2.EvaluateStaticClass.Run(); + var a = 0; + } + + public static void EvaluateAsyncMethods() + { + var staticClass = new EvaluateNonStaticClassWithStaticFields(); + staticClass.run(); } } @@ -414,6 +422,23 @@ public static class EvaluateStaticClass public static string StaticPropertyWithError => throw new Exception("not implemented"); } + public class EvaluateNonStaticClassWithStaticFields + { + public static int StaticField1 = 10; + public static string StaticProperty1 => "StaticProperty1"; + public static string StaticPropertyWithError => throw new Exception("not implemented"); + + private int HelperMethod() + { + return 5; + } + + public async void run() + { + var makeAwaitable = await Task.Run(() => HelperMethod()); + } + } + public class EvaluateLocalsWithElementAccessTests { public class TestEvaluate @@ -454,3 +479,18 @@ public static void EvaluateLocals() } } + +namespace DebuggerTestsV2 +{ + public static class EvaluateStaticClass + { + public static int StaticField1 = 20; + public static string StaticProperty1 => "StaticProperty2"; + public static string StaticPropertyWithError => throw new Exception("not implemented"); + + public static void Run() + { + var a = 0; + } + } +} \ No newline at end of file