From: Thays Grazia Date: Fri, 7 Aug 2020 23:07:48 +0000 (-0300) Subject: [wasm] [debugger] Support Exception Break on Debugger (handled and unhandled) (#40480) X-Git-Tag: submit/tizen/20210909.063632~6136 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=20e481843bfbead4987045a5cca5c1aaf9124517;p=platform%2Fupstream%2Fdotnet%2Fruntime.git [wasm] [debugger] Support Exception Break on Debugger (handled and unhandled) (#40480) * Support Exception Break on Debugger (handled and unhandled) Co-authored-by: Ankit Jain --- diff --git a/src/mono/mono/mini/mini-wasm-debugger.c b/src/mono/mono/mini/mini-wasm-debugger.c index dc1f5d3..90855b5 100644 --- a/src/mono/mono/mini/mini-wasm-debugger.c +++ b/src/mono/mono/mini/mini-wasm-debugger.c @@ -24,6 +24,12 @@ static int log_level = 1; #define DEBUG_PRINTF(level, ...) do { if (G_UNLIKELY ((level) <= log_level)) { fprintf (stdout, __VA_ARGS__); } } while (0) +enum { + EXCEPTION_MODE_NONE, + EXCEPTION_MODE_UNCAUGHT, + EXCEPTION_MODE_ALL +}; + //functions exported to be used by JS G_BEGIN_DECLS @@ -34,6 +40,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_enum_frames (void); EMSCRIPTEN_KEEPALIVE void mono_wasm_get_var_info (int scope, int* pos, int len); EMSCRIPTEN_KEEPALIVE void mono_wasm_clear_all_breakpoints (void); EMSCRIPTEN_KEEPALIVE int mono_wasm_setup_single_step (int kind); +EMSCRIPTEN_KEEPALIVE int mono_wasm_pause_on_exceptions (int state); EMSCRIPTEN_KEEPALIVE void mono_wasm_get_object_properties (int object_id, gboolean expand_value_types); EMSCRIPTEN_KEEPALIVE void mono_wasm_get_array_values (int object_id); EMSCRIPTEN_KEEPALIVE void mono_wasm_get_array_value_expanded (int object_id, int idx); @@ -43,6 +50,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_get_deref_ptr_value (void *value_addr, MonoC //JS functions imported that we use extern void mono_wasm_add_frame (int il_offset, int method_token, const char *assembly_name, const char *method_name); extern void mono_wasm_fire_bp (void); +extern void mono_wasm_fire_exception (int exception_obj_id, const char* message, const char* class_name, gboolean uncaught); extern void mono_wasm_add_obj_var (const char*, const char*, guint64); extern void mono_wasm_add_value_type_unexpanded_var (const char*, const char*); extern void mono_wasm_begin_value_type_var (const char*, const char*); @@ -57,6 +65,7 @@ extern void mono_wasm_add_typed_value (const char *type, const char *str_value, G_END_DECLS static void describe_object_properties_for_klass (void *obj, MonoClass *klass, gboolean isAsyncLocalThis, gboolean expandValueType); +static void handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame); //FIXME move all of those fields to the profiler object static gboolean debugger_enabled; @@ -65,6 +74,7 @@ static int event_request_id; static GHashTable *objrefs; static GHashTable *obj_to_objref; static int objref_id = 0; +static int pause_on_exc = EXCEPTION_MODE_NONE; static const char* all_getters_allowed_class_names[] = { @@ -363,6 +373,8 @@ mono_wasm_debugger_init (void) obj_to_objref = g_hash_table_new (NULL, NULL); objrefs = g_hash_table_new_full (NULL, NULL, NULL, mono_debugger_free_objref); + + mini_get_dbg_callbacks ()->handle_exception = handle_exception; } MONO_API void @@ -374,6 +386,14 @@ mono_wasm_enable_debugging (int debug_level) } EMSCRIPTEN_KEEPALIVE int +mono_wasm_pause_on_exceptions (int state) +{ + pause_on_exc = state; + DEBUG_PRINTF (1, "setting pause on exception: %d\n", pause_on_exc); + return 1; +} + +EMSCRIPTEN_KEEPALIVE int mono_wasm_setup_single_step (int kind) { int nmodifiers = 1; @@ -428,6 +448,50 @@ mono_wasm_setup_single_step (int kind) return isBPOnNativeCode; } +static int +get_object_id(MonoObject *obj) +{ + ObjRef *ref; + if (!obj) + return 0; + + ref = (ObjRef *)g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj))); + if (ref) + return ref->id; + ref = g_new0 (ObjRef, 1); + ref->id = mono_atomic_inc_i32 (&objref_id); + ref->handle = mono_gchandle_new_weakref_internal (obj, FALSE); + g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref); + g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref); + return ref->id; +} + +static void +handle_exception (MonoException *exc, MonoContext *throw_ctx, MonoContext *catch_ctx, StackFrameInfo *catch_frame) +{ + ERROR_DECL (error); + DEBUG_PRINTF (1, "handle exception - %d - %p - %p - %p\n", pause_on_exc, exc, throw_ctx, catch_ctx); + + if (pause_on_exc == EXCEPTION_MODE_NONE) + return; + if (pause_on_exc == EXCEPTION_MODE_UNCAUGHT && catch_ctx != NULL) + return; + + int obj_id = get_object_id ((MonoObject *)exc); + const char *error_message = mono_string_to_utf8_checked_internal (exc->message, error); + + if (!is_ok (error)) + error_message = "Failed to get exception message."; + + const char *class_name = mono_class_full_name (mono_object_class (exc)); + DEBUG_PRINTF (2, "handle exception - calling mono_wasm_fire_exc(): %d - message - %s, class_name: %s\n", obj_id, error_message, class_name); + + mono_wasm_fire_exception (obj_id, error_message, class_name, !catch_ctx); + + DEBUG_PRINTF (2, "handle exception - done\n"); +} + + EMSCRIPTEN_KEEPALIVE void mono_wasm_clear_all_breakpoints (void) { @@ -567,23 +631,6 @@ mono_wasm_current_bp_id (void) return evt->id; } -static int get_object_id(MonoObject *obj) -{ - ObjRef *ref; - if (!obj) - return 0; - - ref = (ObjRef *)g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj))); - if (ref) - return ref->id; - ref = g_new0 (ObjRef, 1); - ref->id = mono_atomic_inc_i32 (&objref_id); - ref->handle = mono_gchandle_new_weakref_internal (obj, FALSE); - g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref); - g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref); - return ref->id; -} - static gboolean list_frames (MonoStackFrameInfo *info, MonoContext *ctx, gpointer data) { diff --git a/src/mono/wasm/debugger/BrowserDebugHost/BrowserDebugHost.csproj b/src/mono/wasm/debugger/BrowserDebugHost/BrowserDebugHost.csproj index aee852c..9650dc8 100644 --- a/src/mono/wasm/debugger/BrowserDebugHost/BrowserDebugHost.csproj +++ b/src/mono/wasm/debugger/BrowserDebugHost/BrowserDebugHost.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0 + $(NetCoreAppCurrent) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/AssemblyInfo.cs b/src/mono/wasm/debugger/BrowserDebugProxy/AssemblyInfo.cs deleted file mode 100644 index 5d9d173..0000000 --- a/src/mono/wasm/debugger/BrowserDebugProxy/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; \ No newline at end of file diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/BrowserDebugProxy.csproj b/src/mono/wasm/debugger/BrowserDebugProxy/BrowserDebugProxy.csproj index 4063416..7da1586 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/BrowserDebugProxy.csproj +++ b/src/mono/wasm/debugger/BrowserDebugProxy/BrowserDebugProxy.csproj @@ -1,7 +1,7 @@  - netstandard2.1 + $(NetCoreAppCurrent) true diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs index 45edb96..d17c9e7 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs @@ -175,6 +175,8 @@ namespace Microsoft.WebAssembly.Diagnostics public static MonoCommands GetCallStack() => new MonoCommands("MONO.mono_wasm_get_call_stack()"); + public static MonoCommands GetExceptionObject () => new MonoCommands ("MONO.mono_wasm_get_exception_object()"); + public static MonoCommands IsRuntimeReady() => new MonoCommands("MONO.mono_wasm_runtime_is_ready"); public static MonoCommands StartSingleStepping(StepKind kind) => new MonoCommands($"MONO.mono_wasm_start_single_stepping ({(int)kind})"); @@ -200,6 +202,8 @@ namespace Microsoft.WebAssembly.Diagnostics public static MonoCommands CallFunctionOn(JToken args) => new MonoCommands($"MONO.mono_wasm_call_function_on ({args.ToString ()})"); public static MonoCommands Resume() => new MonoCommands($"MONO.mono_wasm_debugger_resume ()"); + + public static MonoCommands SetPauseOnExceptions (string state) => new MonoCommands ($"MONO.mono_wasm_set_pause_on_exceptions(\"{state}\")"); } internal enum MonoErrorCodes diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index 61707ae..1f1079f 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -99,9 +99,9 @@ namespace Microsoft.WebAssembly.Diagnostics //TODO figure out how to stich out more frames and, in particular what happens when real wasm is on the stack var top_func = args?["callFrames"] ? [0] ? ["functionName"]?.Value(); - if (top_func == "mono_wasm_fire_bp" || top_func == "_mono_wasm_fire_bp") + if (top_func == "mono_wasm_fire_bp" || top_func == "_mono_wasm_fire_bp" || top_func == "_mono_wasm_fire_exception") { - return await OnBreakpointHit(sessionId, args, token); + return await OnPause(sessionId, args, token); } break; } @@ -329,6 +329,14 @@ namespace Microsoft.WebAssembly.Diagnostics return true; } + case "Debugger.setPauseOnExceptions": + { + string state = args["state"].Value (); + await SendMonoCommand(id, MonoCommands.SetPauseOnExceptions (state), token); + // Pass this on to JS too + return false; + } + // Protocol extensions case "DotnetDebugger.getMethodLocation": { @@ -449,12 +457,14 @@ namespace Microsoft.WebAssembly.Diagnostics } //static int frame_id=0; - async Task OnBreakpointHit(SessionId sessionId, JObject args, CancellationToken token) + async Task OnPause(SessionId sessionId, JObject args, CancellationToken token) { //FIXME we should send release objects every now and then? Or intercept those we inject and deal in the runtime var res = await SendMonoCommand(sessionId, MonoCommands.GetCallStack(), token); var orig_callframes = args?["callFrames"]?.Values(); var context = GetContext(sessionId); + JObject data = null; + var reason = "other";//other means breakpoint if (res.IsErr) { @@ -486,8 +496,24 @@ namespace Microsoft.WebAssembly.Diagnostics { var function_name = frame["functionName"]?.Value(); var url = frame["url"]?.Value(); - if ("mono_wasm_fire_bp" == function_name ||"_mono_wasm_fire_bp" == function_name) + if ("mono_wasm_fire_bp" == function_name ||"_mono_wasm_fire_bp" == function_name || + "_mono_wasm_fire_exception" == function_name) { + if ("_mono_wasm_fire_exception" == function_name) { + var exception_obj_id = await SendMonoCommand(sessionId, MonoCommands.GetExceptionObject (), token); + var res_val = exception_obj_id.Value? ["result"]? ["value"]; + var exception_dotnet_obj_id = new DotnetObjectId("object", res_val?["exception_id"]?.Value ()); + data = JObject.FromObject (new { + type = "object", + subtype = "error", + className = res_val? ["class_name"]?.Value(), + uncaught = res_val? ["uncaught"]?.Value(), + description = res_val? ["message"]?.Value() + "\n", + objectId = exception_dotnet_obj_id.ToString() + }); + reason = "exception"; + } + var frames = new List(); int frame_id = 0; var the_mono_frames = res.Value?["result"] ? ["value"] ? ["frames"]?.Values(); @@ -580,7 +606,8 @@ namespace Microsoft.WebAssembly.Diagnostics var o = JObject.FromObject(new { callFrames, - reason = "other", //other means breakpoint + reason, + data, hitBreakpoints = bp_list, }); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs new file mode 100644 index 0000000..301c422 --- /dev/null +++ b/src/mono/wasm/debugger/DebuggerTestSuite/ExceptionTests.cs @@ -0,0 +1,264 @@ +// 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.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; +using Microsoft.WebAssembly.Diagnostics; +using Xunit; + +namespace DebuggerTests +{ + + public class ExceptionTests : DebuggerTestBase + { + [Fact] + public async Task ExceptionTestAll() + { + var insp = new Inspector(); + //Collect events + var scripts = SubscribeToScripts(insp); + int line = 15; + int col = 20; + string entry_method_name = "[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions"; + + await Ready(); + await insp.Ready(async(cli, token) => + { + ctx = new DebugTestContext(cli, insp, token, scripts); + var debugger_test_loc = "dotnet://debugger-test.dll/debugger-exception-test.cs"; + + await SetPauseOnException("all"); + + var eval_expr = "window.setTimeout(function() { invoke_static_method (" + + $"'{entry_method_name}'" + + "); }, 1);"; + + var pause_location = await EvaluateAndCheck(eval_expr, null, 0, 0, null); + //stop in the managed caught exception + pause_location = await WaitForManagedException(pause_location); + + AssertEqual("run", pause_location["callFrames"] ? [0] ? ["functionName"]?.Value(), "pause0"); + + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "DebuggerTests.CustomException", + uncaught = false + }), "exception0.data"); + + var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + 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"]); + + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "DebuggerTests.CustomException", + uncaught = true + }), "exception1.data"); + + exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", "not implemented uncaught"); + }); + } + + [Fact] + public async Task JSExceptionTestAll() + { + var insp = new Inspector(); + //Collect events + var scripts = SubscribeToScripts(insp); + + await Ready(); + await insp.Ready(async(cli, token) => + { + ctx = new DebugTestContext(cli, insp, token, scripts); + + await SetPauseOnException("all"); + + var eval_expr = "window.setTimeout(function () { exceptions_test (); }, 1)"; + var pause_location = await EvaluateAndCheck(eval_expr, null, 0, 0, "exception_caught_test", null, null); + + Assert.Equal("exception", pause_location["reason"]); + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "TypeError", + uncaught = false + }), "exception0.data"); + + var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", "exception caught"); + + pause_location = await SendCommandAndCheck(null, "Debugger.resume", null, 0, 0, "exception_uncaught_test"); + + Assert.Equal("exception", pause_location["reason"]); + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "RangeError", + uncaught = true + }), "exception1.data"); + + exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", "exception uncaught"); + }); + } + + // FIXME? BUG? We seem to get the stack trace for Runtime.exceptionThrown at `call_method`, + // but JS shows the original error type, and original trace + [Fact] + public async Task ExceptionTestNone() + { + var insp = new Inspector(); + //Collect events + var scripts = SubscribeToScripts(insp); + string entry_method_name = "[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions"; + + await Ready(); + await insp.Ready(async(cli, token) => + { + ctx = new DebugTestContext(cli, insp, token, scripts); + + await SetPauseOnException("none"); + + var eval_expr = "window.setTimeout(function() { invoke_static_method (" + + $"'{entry_method_name}'" + + "); }, 1);"; + + try + { + await EvaluateAndCheck(eval_expr, null, 0, 0, "", null, null); + } + catch (ArgumentException ae) + { + var eo = JObject.Parse(ae.Message); + + // AssertEqual (line, eo ["exceptionDetails"]?["lineNumber"]?.Value (), "lineNumber"); + AssertEqual("Uncaught", eo["exceptionDetails"] ? ["text"]?.Value(), "text"); + + await CheckValue(eo["exceptionDetails"] ? ["exception"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "Error" // BUG?: "DebuggerTests.CustomException" + }), "exception"); + + return; + } + + Assert.True(false, "Expected to get an ArgumentException from the uncaught user exception"); + }); + } + + [Fact] + public async Task JSExceptionTestNone() + { + var insp = new Inspector(); + //Collect events + var scripts = SubscribeToScripts(insp); + + await Ready(); + await insp.Ready(async(cli, token) => + { + ctx = new DebugTestContext(cli, insp, token, scripts); + + await SetPauseOnException("none"); + + var eval_expr = "window.setTimeout(function () { exceptions_test (); }, 1)"; + + int line = 44; + try + { + await EvaluateAndCheck(eval_expr, null, 0, 0, "", null, null); + } + catch (ArgumentException ae) + { + Console.WriteLine($"{ae}"); + var eo = JObject.Parse(ae.Message); + + AssertEqual(line, eo["exceptionDetails"] ? ["lineNumber"]?.Value(), "lineNumber"); + AssertEqual("Uncaught", eo["exceptionDetails"] ? ["text"]?.Value(), "text"); + + await CheckValue(eo["exceptionDetails"] ? ["exception"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = "RangeError" + }), "exception"); + + return; + } + + Assert.True(false, "Expected to get an ArgumentException from the uncaught user exception"); + }); + } + + [Theory] + [InlineData("function () { exceptions_test (); }", null, 0, 0, "exception_uncaught_test", "RangeError", "exception uncaught")] + [InlineData("function () { invoke_static_method ('[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions'); }", + "dotnet://debugger-test.dll/debugger-exception-test.cs", 28, 16, "run", + "DebuggerTests.CustomException", "not implemented uncaught")] + public async Task ExceptionTestUncaught(string eval_fn, string loc, int line, int col, string fn_name, + string exception_type, string exception_message) + { + var insp = new Inspector(); + //Collect events + var scripts = SubscribeToScripts(insp); + await Ready(); + await insp.Ready(async(cli, token) => + { + ctx = new DebugTestContext(cli, insp, token, scripts); + + await SetPauseOnException("uncaught"); + + var eval_expr = $"window.setTimeout({eval_fn}, 1);"; + var pause_location = await EvaluateAndCheck(eval_expr, loc, line, col, fn_name); + + Assert.Equal("exception", pause_location["reason"]); + await CheckValue(pause_location["data"], JObject.FromObject(new + { + type = "object", + subtype = "error", + className = exception_type, + uncaught = true + }), "exception.data"); + + var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value()); + CheckString(exception_members, "message", exception_message); + }); + } + + async Task WaitForManagedException(JObject pause_location) + { + while (true) + { + if (pause_location != null) + { + AssertEqual("exception", pause_location ["reason"]?.Value (), $"Expected to only pause because of an exception. {pause_location}"); + + // return in case of a managed exception, and ignore JS ones + if (pause_location["data"]?["objectId"]?.Value ()?.StartsWith("dotnet:object:") == true) + { + break; + } + } + + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", null, 0, 0, null); + } + + return pause_location; + } + } +} diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/Support.cs b/src/mono/wasm/debugger/DebuggerTestSuite/Support.cs index b038d2c..cac5e73 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/Support.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/Support.cs @@ -869,6 +869,12 @@ namespace DebuggerTests return bp1_res; } + internal async Task SetPauseOnException(string state) + { + var exc_res = await ctx.cli.SendCommand("Debugger.setPauseOnExceptions", JObject.FromObject(new { state = state }), ctx.token); + return exc_res; + } + internal async Task SetBreakpointInMethod(string assembly, string type, string method, int lineOffset = 0, int col = 0) { var req = JObject.FromObject(new { assemblyName = assembly, typeName = type, methodName = method, lineOffset = lineOffset }); diff --git a/src/mono/wasm/debugger/tests/debugger-exception-test.cs b/src/mono/wasm/debugger/tests/debugger-exception-test.cs new file mode 100644 index 0000000..3eb2535 --- /dev/null +++ b/src/mono/wasm/debugger/tests/debugger-exception-test.cs @@ -0,0 +1,55 @@ +// 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; +namespace DebuggerTests +{ + public class ExceptionTestsClass + { + public class TestCaughtException + { + public void run() + { + try + { + throw new CustomException ("not implemented caught"); + } + catch + { + Console.WriteLine("caught exception"); + } + } + } + + public class TestUncaughtException + { + public void run() + { + throw new CustomException ("not implemented uncaught"); + } + } + + public static void TestExceptions() + { + TestCaughtException f = new TestCaughtException(); + f.run(); + + TestUncaughtException g = new TestUncaughtException(); + g.run(); + } + + } + + public class CustomException : Exception + { + // Using this name to match with what js has. + // helps with the tests + public string message; + public CustomException (string message) + : base (message) + { + this.message = message; + } + } +} \ No newline at end of file diff --git a/src/mono/wasm/debugger/tests/other.js b/src/mono/wasm/debugger/tests/other.js index 7ba8e66..a1b121e 100644 --- a/src/mono/wasm/debugger/tests/other.js +++ b/src/mono/wasm/debugger/tests/other.js @@ -31,3 +31,22 @@ function getters_js_test () { console.log (`break here`); return ptd; } + +function exception_caught_test () { + try { + throw new TypeError ('exception caught'); + } catch (e) { + console.log(e); + } +} + +function exception_uncaught_test () { + console.log('uncaught test'); + throw new RangeError ('exception uncaught'); +} + +function exceptions_test () { + exception_caught_test (); + exception_uncaught_test (); +} + diff --git a/src/mono/wasm/runtime/library_mono.js b/src/mono/wasm/runtime/library_mono.js index 0692c65..5e1a177 100644 --- a/src/mono/wasm/runtime/library_mono.js +++ b/src/mono/wasm/runtime/library_mono.js @@ -1588,6 +1588,16 @@ var MonoSupportLib = { console.log ("mono_wasm_fire_bp"); debugger; }, + + mono_wasm_fire_exception: function (exception_id, message, class_name, uncaught) { + MONO.active_exception = { + exception_id: exception_id, + message : Module.UTF8ToString (message), + class_name : Module.UTF8ToString (class_name), + uncaught : uncaught + }; + debugger; + }, }; autoAddDeps(MonoSupportLib, '$MONO')