diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs index 5b932edb7965dc..2fbf3f448f5cd5 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs @@ -124,7 +124,7 @@ public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments public static void CreateTaskCallback(JSMarshalerArgument* arguments_buffer) { ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() - ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return vaule + ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return value try { JSHostImplementation.TaskCallback holder = new JSHostImplementation.TaskCallback(); @@ -195,6 +195,32 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer) } } + [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 + // the marshaled signature is: + // string GetManagedStackTrace(GCHandle exception) + public static void GetManagedStackTrace(JSMarshalerArgument* arguments_buffer) + { + ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() + ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return value + ref JSMarshalerArgument arg_1 = ref arguments_buffer[2];// initialized and set by caller + try + { + GCHandle exception_gc_handle = (GCHandle)arg_1.slot.GCHandle; + if (exception_gc_handle.Target is Exception exception) + { + arg_return.ToJS(exception.StackTrace); + } + else + { + throw new InvalidOperationException("Exception is null"); + } + } + catch (Exception ex) + { + arg_exc.ToJS(ex); + } + } + #if FEATURE_WASM_THREADS [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs index a8966dcaf521d0..79e9e3e0cecf50 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs @@ -1358,11 +1358,28 @@ public void JsExportException(Exception value, string clazz) [Fact] public void JsExportThrows() { - var ex = Assert.Throws(() => JavaScriptTestHelper.invoke1_String("-t-e-s-t-", nameof(JavaScriptTestHelper.Throw))); + var ex = Assert.Throws(() => JavaScriptTestHelper.invoke1_String("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport))); Assert.DoesNotContain("Unexpected error", ex.Message); Assert.Contains("-t-e-s-t-", ex.Message); } + [Fact] + public void JsExportCatchToString() + { + var toString = JavaScriptTestHelper.catch1toString("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport)); + Assert.DoesNotContain("Unexpected error", toString); + Assert.Contains("-t-e-s-t-", toString); + Assert.DoesNotContain(nameof(JavaScriptTestHelper.ThrowFromJSExport), toString); + } + + [Fact] + public void JsExportCatchStack() + { + var stack = JavaScriptTestHelper.catch1stack("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport)); + Assert.Contains(nameof(JavaScriptTestHelper.ThrowFromJSExport), stack); + Assert.Contains("catch1stack", stack); + } + #endregion Exception #region JSObject @@ -1906,12 +1923,12 @@ private void JsImportTest(T value var exThrow0 = Assert.Throws(() => JavaScriptTestHelper.throw0()); Assert.Contains("throw-0-msg", exThrow0.Message); Assert.DoesNotContain(" at ", exThrow0.Message); - Assert.Contains(" at Module.throw0", exThrow0.StackTrace); + Assert.Contains("throw0fn", exThrow0.StackTrace); var exThrow1 = Assert.Throws(() => throw1(value)); Assert.Contains("throw1-msg", exThrow1.Message); Assert.DoesNotContain(" at ", exThrow1.Message); - Assert.Contains(" at Module.throw1", exThrow1.StackTrace); + Assert.Contains("throw1fn", exThrow1.StackTrace); // anything is a system.object, sometimes it would be JSObject wrapper if (typeof(T).IsPrimitive) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs index b5a89b7f48fe98..67fe77cfe5fe80 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs @@ -32,8 +32,14 @@ public static void ConsoleWriteLine([JSMarshalAs] string message) Console.WriteLine(message); } + [JSImport("catch1toString", "JavaScriptTestHelper")] + public static partial string catch1toString(string message, string functionName); + + [JSImport("catch1stack", "JavaScriptTestHelper")] + public static partial string catch1stack(string message, string functionName); + [JSExport] - public static void Throw(string message) + public static void ThrowFromJSExport(string message) { throw new ArgumentException(message); } @@ -57,7 +63,7 @@ public static DateTime Now() [return: JSMarshalAs] internal static partial string getClass1(); - [JSImport("throw0", "JavaScriptTestHelper")] + [JSImport("throw0fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial void throw0(); @@ -215,7 +221,7 @@ internal static partial void Relaxed(string a1, Exception ex, [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Int32([JSMarshalAs] int value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial int throw1_Int32([JSMarshalAs] int value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -241,7 +247,7 @@ public static int EchoInt32([JSMarshalAs] int arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_String([JSMarshalAs] string value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial string throw1_String([JSMarshalAs] string value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -273,7 +279,7 @@ public static string EchoString([JSMarshalAs] string arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Object([JSMarshalAs] object value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial object throw1_Object([JSMarshalAs] object value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -299,7 +305,7 @@ public static object EchoObject([JSMarshalAs] object arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Exception([JSMarshalAs] Exception value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial Exception throw1_Exception([JSMarshalAs] Exception value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -431,7 +437,7 @@ public static Func BackFuncOfIntInt([JSMarshalAs] internal static partial bool identity1_Boolean([JSMarshalAs] bool value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool throw1_Boolean([JSMarshalAs] bool value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -458,7 +464,7 @@ public static bool EchoBoolean([JSMarshalAs] bool arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Char([JSMarshalAs] char value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial char throw1_Char([JSMarshalAs] char value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -484,7 +490,7 @@ public static char EchoChar([JSMarshalAs] char arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Byte([JSMarshalAs] byte value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial byte throw1_Byte([JSMarshalAs] byte value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -510,7 +516,7 @@ public static byte EchoByte([JSMarshalAs] byte arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Int16([JSMarshalAs] short value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial short throw1_Int16([JSMarshalAs] short value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -536,7 +542,7 @@ public static short EchoInt16([JSMarshalAs] short arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Int52([JSMarshalAs] long value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial long throw1_Int52([JSMarshalAs] long value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -562,7 +568,7 @@ public static long EchoInt52([JSMarshalAs] long arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_BigInt64([JSMarshalAs] long value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial long throw1_BigInt64([JSMarshalAs] long value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -588,7 +594,7 @@ public static long EchoBigInt64([JSMarshalAs] long arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Double([JSMarshalAs] double value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial double throw1_Double([JSMarshalAs] double value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -614,7 +620,7 @@ public static double EchoDouble([JSMarshalAs] double arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Single([JSMarshalAs] float value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial float throw1_Single([JSMarshalAs] float value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -640,7 +646,7 @@ public static float EchoSingle([JSMarshalAs] float arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_IntPtr([JSMarshalAs] IntPtr value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial IntPtr throw1_IntPtr([JSMarshalAs] IntPtr value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -667,7 +673,7 @@ public static IntPtr EchoIntPtr([JSMarshalAs] IntPtr arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal unsafe static partial bool identity1_VoidPtr([JSMarshalAs] void* value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal unsafe static partial void* throw1_VoidPtr([JSMarshalAs] void* value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -693,7 +699,7 @@ public static IntPtr EchoIntPtr([JSMarshalAs] IntPtr arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_DateTime([JSMarshalAs] DateTime value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial DateTime throw1_DateTime([JSMarshalAs] DateTime value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -719,7 +725,7 @@ public static DateTime EchoDateTime([JSMarshalAs] DateTime arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_DateTimeOffset([JSMarshalAs] DateTimeOffset value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial DateTimeOffset throw1_DateTimeOffset([JSMarshalAs] DateTimeOffset value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -746,7 +752,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableBoolean([JSMarshalAs] bool? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool? throw1_NullableBoolean([JSMarshalAs] bool? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -773,7 +779,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableInt32([JSMarshalAs] int? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial int? throw1_NullableInt32([JSMarshalAs] int? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -800,7 +806,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableBigInt64([JSMarshalAs] long? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial long? throw1_NullableBigInt64([JSMarshalAs] long? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -827,7 +833,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableIntPtr([JSMarshalAs] IntPtr? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial IntPtr? throw1_NullableIntPtr([JSMarshalAs] IntPtr? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -854,7 +860,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableDouble([JSMarshalAs] double? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial double? throw1_NullableDouble([JSMarshalAs] double? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -881,7 +887,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableDateTime([JSMarshalAs] DateTime? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial DateTime? throw1_NullableDateTime([JSMarshalAs] DateTime? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -907,7 +913,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_JSObject([JSMarshalAs] JSObject value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial JSObject throw1_JSObject([JSMarshalAs] JSObject value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -1236,4 +1242,4 @@ public partial record struct NestedRecordStruct [System.Runtime.InteropServices.JavaScript.JSExport] public static string EchoString(string message) => message + "85"; } -} \ No newline at end of file +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs index 314e715e16b815..0811be477c64f6 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs @@ -105,12 +105,34 @@ export function retrieve1() { return val; } -export function throw0() { +export function throw0fn() { //console.log(`throw0()`) throw new Error('throw-0-msg'); } -export function throw1(arg1) { +export function catch1toString(message, functionName) { + const JavaScriptTestHelper = dllExports.System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper; + const fn = JavaScriptTestHelper[functionName]; + try { + fn(message); + return "bad"; + } catch (err) { + return err.toString(); + } +} + +export function catch1stack(message, functionName) { + const JavaScriptTestHelper = dllExports.System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper; + const fn = JavaScriptTestHelper[functionName]; + try { + fn(message); + return "bad"; + } catch (err) { + return err.stack; + } +} + +export function throw1fn(arg1) { //console.log(`throw1(arg1:${arg1 !== null ? arg1 : ''})`) throw new Error('throw1-msg ' + arg1); } diff --git a/src/mono/wasm/runtime/logging.ts b/src/mono/wasm/runtime/logging.ts index 2d40d7c4d20e29..cdb2d00dba723d 100644 --- a/src/mono/wasm/runtime/logging.ts +++ b/src/mono/wasm/runtime/logging.ts @@ -62,8 +62,9 @@ export function mono_wasm_symbolicate_string(message: string): string { export function mono_wasm_stringify_as_error_with_stack(err: Error | string): string { let errObj: any = err; - if (!(err instanceof Error)) - errObj = new Error(err); + if (!(errObj instanceof Error)) { + errObj = new Error(errObj); + } // Error return mono_wasm_symbolicate_string(errObj.stack); diff --git a/src/mono/wasm/runtime/managed-exports.ts b/src/mono/wasm/runtime/managed-exports.ts index 09f6d9a33d0241..65186e4c0b270f 100644 --- a/src/mono/wasm/runtime/managed-exports.ts +++ b/src/mono/wasm/runtime/managed-exports.ts @@ -7,7 +7,7 @@ import { Module, runtimeHelpers, ENVIRONMENT_IS_PTHREAD } from "./imports"; import { alloc_stack_frame, get_arg, get_arg_gc_handle, MarshalerType, set_arg_type, set_gc_handle } from "./marshal"; import { invoke_method_and_handle_exception } from "./invoke-cs"; import { marshal_array_to_cs_impl, marshal_exception_to_cs, marshal_intptr_to_cs } from "./marshal-to-cs"; -import { marshal_int32_to_js, marshal_task_to_js } from "./marshal-to-js"; +import { marshal_int32_to_js, marshal_string_to_js, marshal_task_to_js } from "./marshal-to-js"; export function init_managed_exports(): void { const anyModule = Module as any; @@ -34,6 +34,9 @@ export function init_managed_exports(): void { mono_assert(complete_task_method, "Can't find CompleteTask method"); const call_delegate_method = get_method("CallDelegate"); mono_assert(call_delegate_method, "Can't find CallDelegate method"); + const get_managed_stack_trace_method = get_method("GetManagedStackTrace"); + mono_assert(get_managed_stack_trace_method, "Can't find GetManagedStackTrace method"); + runtimeHelpers.javaScriptExports.call_entry_point = (entry_point: MonoMethod, program_args?: string[]) => { const sp = anyModule.stackSave(); try { @@ -134,6 +137,22 @@ export function init_managed_exports(): void { anyModule.stackRestore(sp); } }; + runtimeHelpers.javaScriptExports.get_managed_stack_trace = (exception_gc_handle: GCHandle) => { + const sp = anyModule.stackSave(); + try { + const args = alloc_stack_frame(3); + + const arg1 = get_arg(args, 2); + set_arg_type(arg1, MarshalerType.Exception); + set_gc_handle(arg1, exception_gc_handle); + + invoke_method_and_handle_exception(get_managed_stack_trace_method, args); + const res = get_arg(args, 1); + return marshal_string_to_js(res); + } finally { + anyModule.stackRestore(sp); + } + }; if (install_sync_context) { runtimeHelpers.javaScriptExports.install_synchronization_context = () => { diff --git a/src/mono/wasm/runtime/marshal-to-js.ts b/src/mono/wasm/runtime/marshal-to-js.ts index 318d10580ffec0..aac0155406c4ec 100644 --- a/src/mono/wasm/runtime/marshal-to-js.ts +++ b/src/mono/wasm/runtime/marshal-to-js.ts @@ -32,7 +32,7 @@ export function initialize_marshalers_to_js(): void { cs_to_js_marshalers.set(MarshalerType.Single, _marshal_float_to_js); cs_to_js_marshalers.set(MarshalerType.IntPtr, _marshal_intptr_to_js); cs_to_js_marshalers.set(MarshalerType.Double, _marshal_double_to_js); - cs_to_js_marshalers.set(MarshalerType.String, _marshal_string_to_js); + cs_to_js_marshalers.set(MarshalerType.String, marshal_string_to_js); cs_to_js_marshalers.set(MarshalerType.Exception, marshal_exception_to_js); cs_to_js_marshalers.set(MarshalerType.JSException, marshal_exception_to_js); cs_to_js_marshalers.set(MarshalerType.JSObject, _marshal_js_object_to_js); @@ -353,7 +353,7 @@ export function mono_wasm_marshal_promise(args: JSMarshalerArguments): void { set_arg_type(exc, MarshalerType.None); } -function _marshal_string_to_js(arg: JSMarshalerArgument): string | null { +export function marshal_string_to_js(arg: JSMarshalerArgument): string | null { const type = get_arg_type(arg); if (type == MarshalerType.None) { return null; @@ -383,7 +383,7 @@ export function marshal_exception_to_js(arg: JSMarshalerArgument): Error | null let result = _lookup_js_owned_object(gc_handle); if (result === null || result === undefined) { // this will create new ManagedError - const message = _marshal_string_to_js(arg); + const message = marshal_string_to_js(arg); result = new ManagedError(message!); setup_managed_proxy(result, gc_handle); @@ -462,7 +462,7 @@ function _marshal_array_to_js_impl(arg: JSMarshalerArgument, element_type: Marsh result = new Array(length); for (let index = 0; index < length; index++) { const element_arg = get_arg(buffer_ptr, index); - result[index] = _marshal_string_to_js(element_arg); + result[index] = marshal_string_to_js(element_arg); } cwraps.mono_wasm_deregister_root(buffer_ptr); } diff --git a/src/mono/wasm/runtime/marshal.ts b/src/mono/wasm/runtime/marshal.ts index 77bec6b6aa762f..f20a620c5fb76b 100644 --- a/src/mono/wasm/runtime/marshal.ts +++ b/src/mono/wasm/runtime/marshal.ts @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import { js_owned_gc_handle_symbol, teardown_managed_proxy } from "./gc-handles"; -import { Module } from "./imports"; +import { Module, runtimeHelpers } from "./imports"; import { getF32, getF64, getI16, getI32, getI64Big, getU16, getU32, getU8, setF32, setF64, setI16, setI32, setI64Big, setU16, setU32, setU8 } from "./memory"; import { mono_wasm_new_external_root } from "./roots"; import { mono_assert, GCHandle, JSHandle, MonoObject, MonoString, GCHandleNull, JSMarshalerArguments, JSFunctionSignature, JSMarshalerType, JSMarshalerArgument, MarshalerToJs, MarshalerToCs, WasmRoot } from "./types"; @@ -316,13 +316,31 @@ export class ManagedObject implements IDisposable { } export class ManagedError extends Error implements IDisposable { + private superStack: any; constructor(message: string) { super(message); + this.superStack = Object.getOwnPropertyDescriptor(this, "stack"); // this works on Chrome + Object.defineProperty(this, "stack", { + get: this.getManageStack, + }); } - get stack(): string | undefined { - //todo implement lazy managed stack strace from this[js_owned_gc_handle_symbol]! - return super.stack; + getSuperStack() { + if (this.superStack) { + return this.superStack.value; + } + return super.stack; // this works on FF + } + + getManageStack() { + const gc_handle = (this)[js_owned_gc_handle_symbol]; + if (gc_handle) { + const managed_stack = runtimeHelpers.javaScriptExports.get_managed_stack_trace(gc_handle); + if (managed_stack) { + return managed_stack + "\n" + this.getSuperStack(); + } + } + return this.getSuperStack(); } dispose(): void { @@ -332,10 +350,6 @@ export class ManagedError extends Error implements IDisposable { get isDisposed(): boolean { return (this)[js_owned_gc_handle_symbol] === GCHandleNull; } - - toString(): string { - return `ManagedError(gc_handle: ${(this)[js_owned_gc_handle_symbol]})`; - } } export function get_signature_marshaler(signature: JSFunctionSignature, index: number): JSHandle { diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts index ea01f16a9e5c85..3664532e54c5e9 100644 --- a/src/mono/wasm/runtime/types.ts +++ b/src/mono/wasm/runtime/types.ts @@ -411,6 +411,9 @@ export interface JavaScriptExports { // the marshaled signature is: void InstallSynchronizationContext() install_synchronization_context(): void; + + // the marshaled signature is: string GetManagedStackTrace(GCHandle exception) + get_managed_stack_trace(exception_gc_handle: GCHandle): string | null } export type MarshalerToJs = (arg: JSMarshalerArgument, sig?: JSMarshalerType, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs) => any;