diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs index bc0f4faeb456b5..950acbb2dcf915 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs @@ -267,6 +267,14 @@ internal enum StepKind Out } + internal enum PauseOnExceptionsKind + { + Unset, + None, + Uncaught, + All + } + internal class ExecutionContext { public string DebugId { get; set; } @@ -279,8 +287,7 @@ internal class ExecutionContext public int Id { get; set; } public object AuxData { get; set; } - public bool PauseOnUncaught { get; set; } - public bool PauseOnCaught { get; set; } + public PauseOnExceptionsKind PauseOnExceptions { get; set; } public List CallStack { get; set; } diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index 1f8c498cad9018..9ac22527877056 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -134,9 +134,7 @@ protected override async Task AcceptEvent(SessionId sessionId, string meth { string exceptionError = args?["exceptionDetails"]?["exception"]?["value"]?.Value(); if (exceptionError == sPauseOnUncaught || exceptionError == sPauseOnCaught) - { return true; - } } break; } @@ -156,17 +154,19 @@ protected override async Task AcceptEvent(SessionId sessionId, string meth if (exceptionError == sPauseOnUncaught) { await SendCommand(sessionId, "Debugger.resume", new JObject(), token); - context.PauseOnUncaught = true; + if (context.PauseOnExceptions == PauseOnExceptionsKind.Unset) + context.PauseOnExceptions = PauseOnExceptionsKind.Uncaught; return true; } if (exceptionError == sPauseOnCaught) { await SendCommand(sessionId, "Debugger.resume", new JObject(), token); - context.PauseOnCaught = true; + context.PauseOnExceptions = PauseOnExceptionsKind.All; return true; } } } + //TODO figure out how to stich out more frames and, in particular what happens when real wasm is on the stack string top_func = args?["callFrames"]?[0]?["functionName"]?.Value(); switch (top_func) { @@ -442,23 +442,16 @@ protected override async Task AcceptCommand(MessageId id, string method, J case "Debugger.setPauseOnExceptions": { string state = args["state"].Value(); - if (!context.IsRuntimeReady) + context.PauseOnExceptions = state switch { - context.PauseOnCaught = false; - context.PauseOnUncaught = false; - switch (state) - { - case "all": - context.PauseOnCaught = true; - context.PauseOnUncaught = true; - break; - case "uncaught": - context.PauseOnUncaught = true; - break; - } - } - else - await SdbHelper.EnableExceptions(id, state, token); + "all" => PauseOnExceptionsKind.All, + "uncaught" => PauseOnExceptionsKind.Uncaught, + "none" => PauseOnExceptionsKind.None, + _ => PauseOnExceptionsKind.Unset + }; + + if (context.IsRuntimeReady) + await SdbHelper.EnableExceptions(id, context.PauseOnExceptions, token); // Pass this on to JS too return false; } @@ -874,7 +867,7 @@ private async Task OnReceiveDebuggerAgentEvent(SessionId sessionId, JObjec int object_id = retDebuggerCmdReader.ReadInt32(); var caught = retDebuggerCmdReader.ReadByte(); var exceptionObject = await SdbHelper.GetObjectValues(sessionId, object_id, GetObjectCommandOptions.WithProperties | GetObjectCommandOptions.OwnProperties, token); - var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value().Equals("message")); + var exceptionObjectMessage = exceptionObject.FirstOrDefault(attr => attr["name"].Value().Equals("_message")); var data = JObject.FromObject(new { type = "object", @@ -964,6 +957,7 @@ private async Task OnDefaultContext(SessionId sessionId, ExecutionContext contex { context.BreakpointRequests[kvp.Key] = kvp.Value.Clone(); } + context.PauseOnExceptions = previousContext.PauseOnExceptions; } if (await IsRuntimeAlreadyReadyAlready(sessionId, token)) @@ -1227,10 +1221,8 @@ private async Task RuntimeReady(SessionId sessionId, CancellationTok Log("verbose", $"Failed to clear breakpoints"); } - if (context.PauseOnCaught && context.PauseOnUncaught) - await SdbHelper.EnableExceptions(sessionId, "all", token); - else if (context.PauseOnUncaught) - await SdbHelper.EnableExceptions(sessionId, "uncaught", token); + if (context.PauseOnExceptions != PauseOnExceptionsKind.None && context.PauseOnExceptions != PauseOnExceptionsKind.Unset) + await SdbHelper.EnableExceptions(sessionId, context.PauseOnExceptions, token); await SdbHelper.SetProtocolVersion(sessionId, token); await SdbHelper.EnableReceiveRequests(sessionId, EventKind.UserBreak, token); @@ -1392,8 +1384,19 @@ private async Task AttachToTarget(SessionId sessionId, CancellationToken token) // see https://github.com/mono/mono/issues/19549 for background if (sessions.Add(sessionId)) { - string checkUncaughtExceptions = $"throw \"{sPauseOnUncaught}\";"; - string checkCaughtExceptions = $"try {{throw \"{sPauseOnCaught}\";}} catch {{}}"; + string checkUncaughtExceptions = string.Empty; + string checkCaughtExceptions = string.Empty; + + //we only need this check if it's a non-vs debugging + if (sessionId == SessionId.Null) + { + if (!contexts.TryGetValue(sessionId, out ExecutionContext context) || context.PauseOnExceptions == PauseOnExceptionsKind.Unset) + { + checkUncaughtExceptions = $"throw \"{sPauseOnUncaught}\";"; + checkCaughtExceptions = $"try {{throw \"{sPauseOnCaught}\";}} catch {{}}"; + } + } + await SendMonoCommand(sessionId, new MonoCommands("globalThis.dotnetDebugger = true"), token); Result res = await SendCommand(sessionId, "Page.addScriptToEvaluateOnNewDocument", diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index eb7d078829ac8e..44eccc2d7d55af 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -2210,8 +2210,14 @@ public async Task GetArrayValues(SessionId sessionId, int arrayId, Cance } return array; } - public async Task EnableExceptions(SessionId sessionId, string state, CancellationToken token) + public async Task EnableExceptions(SessionId sessionId, PauseOnExceptionsKind state, CancellationToken token) { + if (state == PauseOnExceptionsKind.Unset) + { + logger.LogDebug($"Trying to setPauseOnExceptions using status Unset"); + return false; + } + var commandParams = new MemoryStream(); var commandParamsWriter = new MonoBinaryWriter(commandParams); commandParamsWriter.Write((byte)EventKind.Exception); @@ -2219,14 +2225,16 @@ public async Task EnableExceptions(SessionId sessionId, string state, Canc commandParamsWriter.Write((byte)1); commandParamsWriter.Write((byte)ModifierKind.ExceptionOnly); commandParamsWriter.Write(0); //exc_class - if (state == "all") + if (state == PauseOnExceptionsKind.All) commandParamsWriter.Write((byte)1); //caught else commandParamsWriter.Write((byte)0); //caught - if (state == "uncaught" || state == "all") + + if (state == PauseOnExceptionsKind.Uncaught || state == PauseOnExceptionsKind.All) commandParamsWriter.Write((byte)1); //uncaught else commandParamsWriter.Write((byte)0); //uncaught + commandParamsWriter.Write((byte)1);//subclasses commandParamsWriter.Write((byte)0);//not_filtered_feature commandParamsWriter.Write((byte)0);//everything_else diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs index eb0ebe0cc0d540..33dbd2b6db64f8 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs @@ -232,10 +232,11 @@ await CheckValue(pause_location["data"], JObject.FromObject(new CheckString(exception_members, "message", "not implemented uncaught"); } - [Fact] - public async Task ExceptionTestAllWithReload() + [Theory] + [InlineData("[debugger-test] DebuggerTests.ExceptionTestsClassDefault:TestExceptions", "System.Exception", 76)] + [InlineData("[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions", "DebuggerTests.CustomException", 28)] + public async Task ExceptionTestAllWithReload(string entry_method_name, string class_name, int line_number) { - string entry_method_name = "[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions"; var debugger_test_loc = "dotnet://debugger-test.dll/debugger-exception-test.cs"; await SetPauseOnException("all"); @@ -255,7 +256,7 @@ public async Task ExceptionTestAllWithReload() i++; } - + var eval_expr = "window.setTimeout(function() { invoke_static_method (" + $"'{entry_method_name}'" + "); }, 1);"; @@ -270,29 +271,31 @@ await CheckValue(pause_location["data"], JObject.FromObject(new { type = "object", subtype = "error", - className = "DebuggerTests.CustomException", - uncaught = false + className = class_name, + uncaught = false, + description = "not implemented caught" }), "exception0.data"); var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); - CheckString(exception_members, "message", "not implemented caught"); + CheckString(exception_members, "_message", "not implemented caught"); pause_location = await WaitForManagedException(null); AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value(), "pause1"); //stop in the uncaught exception - CheckLocation(debugger_test_loc, 28, 16, scripts, pause_location["callFrames"][0]["location"]); + CheckLocation(debugger_test_loc, line_number, 16, scripts, pause_location["callFrames"][0]["location"]); await CheckValue(pause_location["data"], JObject.FromObject(new { type = "object", subtype = "error", - className = "DebuggerTests.CustomException", - uncaught = true + className = class_name, + uncaught = true, + description = "not implemented uncaught" }), "exception1.data"); exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); - CheckString(exception_members, "message", "not implemented uncaught"); + CheckString(exception_members, "_message", "not implemented uncaught"); } diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-exception-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-exception-test.cs index 86904ca27ea879..ebf82dc4909200 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-exception-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-exception-test.cs @@ -52,4 +52,40 @@ public CustomException(string message) this.message = message; } } + + public class ExceptionTestsClassDefault + { + public class TestCaughtException + { + public void run() + { + try + { + throw new Exception("not implemented caught"); + } + catch + { + Console.WriteLine("caught exception"); + } + } + } + + public class TestUncaughtException + { + public void run() + { + throw new Exception("not implemented uncaught"); + } + } + + public static void TestExceptions() + { + TestCaughtException f = new TestCaughtException(); + f.run(); + + TestUncaughtException g = new TestUncaughtException(); + g.run(); + } + + } }