diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index e37f75191c5110..3c74bf450911e9 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -332,8 +332,7 @@ internal class MethodInfo internal LocalScopeHandleCollection localScopes; public bool IsStatic() => (methodDef.Attributes & MethodAttributes.Static) != 0; public int IsAsync { get; set; } - public bool IsHiddenFromDebugger { get; } - public bool HasStepThroughAttribute { get; } + public DebuggerAttributesInfo DebuggerAttrInfo { get; set; } public TypeInfo TypeInfo { get; } public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, int token, SourceFile source, TypeInfo type, MetadataReader asmMetadataReader, MetadataReader pdbMetadataReader) @@ -371,6 +370,7 @@ public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, StartLocation = new SourceLocation(this, start); EndLocation = new SourceLocation(this, end); + DebuggerAttrInfo = new DebuggerAttributesInfo(); foreach (var cattr in methodDef.GetCustomAttributes()) { var ctorHandle = asmMetadataReader.GetCustomAttribute(cattr).Constructor; @@ -378,16 +378,24 @@ public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, { var container = asmMetadataReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent; var name = asmMetadataReader.GetString(asmMetadataReader.GetTypeReference((TypeReferenceHandle)container).Name); - if (name == "DebuggerHiddenAttribute") + var stopSearch = true; + switch (name) { - IsHiddenFromDebugger = true; - break; + case "DebuggerHiddenAttribute": + DebuggerAttrInfo.HasDebuggerHidden = true; + break; + case "DebuggerStepThroughAttribute": + DebuggerAttrInfo.HasStepThrough = true; + break; + case "DebuggerNonUserCodeAttribute": + DebuggerAttrInfo.HasNonUserCode = true; + break; + default: + stopSearch = false; + break; } - if (name == "DebuggerStepThroughAttribute") - { - HasStepThroughAttribute = true; + if (stopSearch) break; - } } } @@ -478,6 +486,13 @@ public VarInfo[] GetLiveVarsAt(int offset) } public override string ToString() => "MethodInfo(" + Name + ")"; + + public class DebuggerAttributesInfo + { + public bool HasDebuggerHidden { get; internal set; } + public bool HasStepThrough { get; internal set; } + public bool HasNonUserCode { get; internal set; } + } } internal class TypeInfo diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index 33d1dc4c59d985..5fff1c5a7f1fcc 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -853,9 +853,12 @@ private async Task SendCallStack(SessionId sessionId, ExecutionContext con if (shouldReturn) return true; - if (j == 0 && (method?.Info.HasStepThroughAttribute == true || method?.Info.IsHiddenFromDebugger == true)) + if (j == 0 && + (method?.Info.DebuggerAttrInfo.HasStepThrough == true || + method?.Info.DebuggerAttrInfo.HasDebuggerHidden == true || + (method?.Info.DebuggerAttrInfo.HasNonUserCode == true && JustMyCode))) { - if (method.Info.IsHiddenFromDebugger) + if (method.Info.DebuggerAttrInfo.HasDebuggerHidden) { if (event_kind == EventKind.Step) context.IsSkippingHiddenMethod = true; @@ -1443,7 +1446,7 @@ private async Task SetBreakpoint(SessionId sessionId, DebugStore store, Breakpoi { SourceLocation loc = sourceId.First(); req.Method = loc.IlLocation.Method; - if (req.Method.IsHiddenFromDebugger) + if (req.Method.DebuggerAttrInfo.HasDebuggerHidden) continue; Breakpoint bp = await SetMonoBreakpoint(sessionId, req.Id, loc, req.Condition, token); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs index 01f526e8c78db1..1252bb4135f32b 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs @@ -709,128 +709,148 @@ await SendCommandAndCheck(null, "Debugger.resume", } [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task StepThroughAttributeStepInNoBp(bool justMyCodeEnabled) + [InlineData(false, "RunStepThrough")] + [InlineData(true, "RunStepThrough")] + [InlineData(true, "RunNonUserCode")] + [InlineData(false, "RunNonUserCode")] + public async Task StepThroughOrNonUserCodeAttributeStepInNoBp(bool justMyCodeEnabled, string evalFunName) { - var bp_init = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", "RunStepThrough", 1); + var bp_init = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", evalFunName, 1); if (justMyCodeEnabled) await SetJustMyCode(true); var init_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method('[debugger-test] DebuggerAttribute:RunStepThrough'); }, 1);", + $"window.setTimeout(function() {{ invoke_static_method('[debugger-test] DebuggerAttribute:{evalFunName}'); }}, 1);", "dotnet://debugger-test.dll/debugger-test.cs", bp_init.Value["locations"][0]["lineNumber"].Value(), bp_init.Value["locations"][0]["columnNumber"].Value(), - "RunStepThrough" + evalFunName ); - await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", 868, 8, "RunStepThrough"); + var (funcName, line, col) = (evalFunName, 868, 8); + if (evalFunName == "RunNonUserCode") + (funcName, line, col) = justMyCodeEnabled ? (evalFunName, 888, 8) : ("NonUserCodeBp", 873, 4); + await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", line, col, funcName); } [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task StepThroughAttributeStepInWithBp(bool justMyCodeEnabled) + [InlineData(false, "RunStepThrough", "StepThrougBp")] + [InlineData(true, "RunStepThrough", "StepThrougBp")] + [InlineData(true, "RunNonUserCode", "NonUserCodeBp")] + [InlineData(false, "RunNonUserCode", "NonUserCodeBp")] + public async Task StepThroughOrNonUserCodeAttributeStepInWithBp(bool justMyCodeEnabled, string evalFunName, string decoratedFunName) { - var bp_init = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", "RunStepThrough", 1); - var bp1_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", "NotStopOnJustMyCode", 1); - var bp2_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", "NotStopOnJustMyCode", 3); - + var bp_init = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", evalFunName, 1); var init_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method('[debugger-test] DebuggerAttribute:RunStepThrough'); }, 1);", + $"window.setTimeout(function() {{ invoke_static_method('[debugger-test] DebuggerAttribute:{evalFunName}'); }}, 1);", "dotnet://debugger-test.dll/debugger-test.cs", bp_init.Value["locations"][0]["lineNumber"].Value(), bp_init.Value["locations"][0]["columnNumber"].Value(), - "RunStepThrough" + evalFunName ); if (justMyCodeEnabled) { await SetJustMyCode(true); - await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", 868, 8, "RunStepThrough"); + var line = (evalFunName == "RunNonUserCode") ? 888 : 868; + await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", line, 8, evalFunName); } else { - var line1 = bp1_decorated_fun.Value["locations"][0]["lineNumber"].Value(); - var line2 = bp2_decorated_fun.Value["locations"][0]["lineNumber"].Value(); - var line3 = 867; - var step_throgh_fun = "NotStopOnJustMyCode"; - var outer_fun = "RunStepThrough"; - await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", line1, 8, step_throgh_fun); - await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", line2, 8, step_throgh_fun); - await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", line3, 8, outer_fun); + var (finalFunName, line3, col) = (decoratedFunName, 873, 4); + if (evalFunName == "RunStepThrough") + { + var bp1_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", decoratedFunName, 1); + var bp2_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", decoratedFunName, 3); + (finalFunName, line3, col) = (evalFunName, 867, 8); + var line1 = bp1_decorated_fun.Value["locations"][0]["lineNumber"].Value(); + var line2 = bp2_decorated_fun.Value["locations"][0]["lineNumber"].Value(); + await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", line1, col, decoratedFunName); + await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", line2, col, decoratedFunName); + } + await SendCommandAndCheck(null, "Debugger.stepInto", "dotnet://debugger-test.dll/debugger-test.cs", line3, col, finalFunName); } } [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task StepThroughAttributeResumeWithBp(bool justMyCodeEnabled) + [InlineData(false, "RunStepThrough", "StepThrougBp")] + [InlineData(true, "RunStepThrough", "StepThrougBp")] + [InlineData(true, "RunNonUserCode", "NonUserCodeBp")] + [InlineData(false, "RunNonUserCode", "NonUserCodeBp")] + public async Task StepThroughOrNonUserCodeAttributeResumeWithBp(bool justMyCodeEnabled, string evalFunName, string decoratedFunName) { - var bp_init = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", "RunStepThrough", 1); - var bp1_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", "NotStopOnJustMyCode", 1); - var bp_outside_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", "RunStepThrough", 2); - + var bp_init = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", evalFunName, 1); var init_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method('[debugger-test] DebuggerAttribute:RunStepThrough'); }, 1);", + $"window.setTimeout(function() {{ invoke_static_method('[debugger-test] DebuggerAttribute:{evalFunName}'); }}, 1);", "dotnet://debugger-test.dll/debugger-test.cs", bp_init.Value["locations"][0]["lineNumber"].Value(), bp_init.Value["locations"][0]["columnNumber"].Value(), - "RunStepThrough" + evalFunName ); if (justMyCodeEnabled) await SetJustMyCode(true); else { + var bp1_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", decoratedFunName, 1); var line1 = bp1_decorated_fun.Value["locations"][0]["lineNumber"].Value(); - var function_name1 = "NotStopOnJustMyCode"; - await SendCommandAndCheck(null, "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", line1, 8, function_name1); + await SendCommandAndCheck(null, "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", line1, 8, decoratedFunName); } - + var bp_outside_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", evalFunName, 2); var line2 = bp_outside_decorated_fun.Value["locations"][0]["lineNumber"].Value(); - var function_name2 = "RunStepThrough"; - await SendCommandAndCheck(null, "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", line2, 8, function_name2); + await SendCommandAndCheck(null, "Debugger.resume", "dotnet://debugger-test.dll/debugger-test.cs", line2, 8, evalFunName); } [Theory] - [InlineData(false, "Debugger.resume")] - [InlineData(false, "Debugger.stepInto")] - [InlineData(true, "Debugger.stepInto")] - [InlineData(true, "Debugger.resume")] - public async Task StepThroughAttributeWithUserBp(bool justMyCodeEnabled, string debuggingFunction) + [InlineData(false, "Debugger.resume", "RunStepThrough", "StepThrougUserBp")] + [InlineData(false, "Debugger.stepInto", "RunStepThrough", "StepThrougUserBp")] + [InlineData(true, "Debugger.stepInto", "RunStepThrough", null)] + [InlineData(true, "Debugger.resume", "RunStepThrough", null)] + [InlineData(true, "Debugger.stepInto", "RunNonUserCode", null)] + [InlineData(true, "Debugger.resume", "RunNonUserCode", null)] + [InlineData(false, "Debugger.stepInto", "RunNonUserCode", "NonUserCodeUserBp")] + [InlineData(false, "Debugger.resume", "RunNonUserCode", "NonUserCodeUserBp")] + public async Task StepThroughOrNonUserCodAttributeWithUserBp(bool justMyCodeEnabled, string debuggingFunction, string evalFunName, string decoratedFunName) { - var bp_init = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", "RunStepThrough", 2); - var bp_outside_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", "RunStepThrough", 3); + var bp_init = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", evalFunName, 2); + var bp_outside_decorated_fun = await SetBreakpointInMethod("debugger-test.dll", "DebuggerAttribute", evalFunName, 3); var init_location = await EvaluateAndCheck( - "window.setTimeout(function() { invoke_static_method('[debugger-test] DebuggerAttribute:RunStepThrough'); }, 1);", + $"window.setTimeout(function() {{ invoke_static_method('[debugger-test] DebuggerAttribute:{evalFunName}'); }}, 1);", "dotnet://debugger-test.dll/debugger-test.cs", bp_init.Value["locations"][0]["lineNumber"].Value(), bp_init.Value["locations"][0]["columnNumber"].Value(), - "RunStepThrough" + evalFunName ); - int line1, line2; + int line1, line2; + var (col1, col2) = (8, 4); string function_name1, function_name2; if (justMyCodeEnabled) { await SetJustMyCode(true); line1 = bp_outside_decorated_fun.Value["locations"][0]["lineNumber"].Value() - 1; - function_name1 = "RunStepThrough"; line2 = bp_outside_decorated_fun.Value["locations"][0]["lineNumber"].Value(); - function_name2 = "RunStepThrough"; + function_name1 = function_name2 = evalFunName; } else { - line1 = 862; - function_name1 = "NotStopOnJustMyCodeUserBp"; - line2 = bp_outside_decorated_fun.Value["locations"][0]["lineNumber"].Value(); - function_name2 = "RunStepThrough"; + if (debuggingFunction == "Debugger.stepInto" && evalFunName == "RunNonUserCode") + { + (line1, col1) = (881, 4); + (line2, col2) = (882, 8); + function_name1 = function_name2 = decoratedFunName; + } + else + { + line1 = evalFunName == "RunNonUserCode" ? 882 : 862; + function_name1 = decoratedFunName; + line2 = bp_outside_decorated_fun.Value["locations"][0]["lineNumber"].Value(); + function_name2 = evalFunName; + } } - await SendCommandAndCheck(null, debuggingFunction, "dotnet://debugger-test.dll/debugger-test.cs", line1, 8, function_name1); - await SendCommandAndCheck(null, debuggingFunction, "dotnet://debugger-test.dll/debugger-test.cs", line2, 4, function_name2); + await SendCommandAndCheck(null, debuggingFunction, "dotnet://debugger-test.dll/debugger-test.cs", line1, col1, function_name1); + await SendCommandAndCheck(null, debuggingFunction, "dotnet://debugger-test.dll/debugger-test.cs", line2, col2, function_name2); } [Fact] diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs index 9bb227f43f4092..8752e8a2407571 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs @@ -913,7 +913,7 @@ public async Task InspectLocalsUsingClassFromLibraryUsingDebugTypeFull() await EvaluateAndCheck( "window.setTimeout(function() {" + expression + "; }, 1);", - "dotnet://debugger-test.dll/debugger-test.cs", 880, 8, + "dotnet://debugger-test.dll/debugger-test.cs", 900, 8, "CallToEvaluateLocal", wait_for_event_fn: async (pause_location) => { 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 49c811faaa317f..52c88229823959 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs @@ -850,7 +850,7 @@ public static void RunDebuggerBreak() } [System.Diagnostics.DebuggerStepThroughAttribute] - public static void NotStopOnJustMyCode() + public static void StepThrougBp() { var a = 0; currentCount++; @@ -858,15 +858,35 @@ public static void NotStopOnJustMyCode() } [System.Diagnostics.DebuggerStepThroughAttribute] - public static void NotStopOnJustMyCodeUserBp() + public static void StepThrougUserBp() { System.Diagnostics.Debugger.Break(); } public static void RunStepThrough() { - NotStopOnJustMyCode(); - NotStopOnJustMyCodeUserBp(); + StepThrougBp(); + StepThrougUserBp(); + } + + [System.Diagnostics.DebuggerNonUserCode] + public static void NonUserCodeBp() + { + var a = 0; + currentCount++; + var b = 1; + } + + [System.Diagnostics.DebuggerNonUserCode] + public static void NonUserCodeUserBp() + { + System.Diagnostics.Debugger.Break(); + } + + public static void RunNonUserCode() + { + NonUserCodeBp(); + NonUserCodeUserBp(); } }