Skip to content

Commit 3056135

Browse files
authored
* fix missing managed stack trace on managed exceptions marshaled to JS (#75799)
* override `get stack` * fix Firefox tests
1 parent a66c066 commit 3056135

File tree

9 files changed

+155
-47
lines changed

9 files changed

+155
-47
lines changed

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments
124124
public static void CreateTaskCallback(JSMarshalerArgument* arguments_buffer)
125125
{
126126
ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame()
127-
ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return vaule
127+
ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return value
128128
try
129129
{
130130
JSHostImplementation.TaskCallback holder = new JSHostImplementation.TaskCallback();
@@ -195,6 +195,32 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer)
195195
}
196196
}
197197

198+
[MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
199+
// the marshaled signature is:
200+
// string GetManagedStackTrace(GCHandle exception)
201+
public static void GetManagedStackTrace(JSMarshalerArgument* arguments_buffer)
202+
{
203+
ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame()
204+
ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return value
205+
ref JSMarshalerArgument arg_1 = ref arguments_buffer[2];// initialized and set by caller
206+
try
207+
{
208+
GCHandle exception_gc_handle = (GCHandle)arg_1.slot.GCHandle;
209+
if (exception_gc_handle.Target is Exception exception)
210+
{
211+
arg_return.ToJS(exception.StackTrace);
212+
}
213+
else
214+
{
215+
throw new InvalidOperationException("Exception is null");
216+
}
217+
}
218+
catch (Exception ex)
219+
{
220+
arg_exc.ToJS(ex);
221+
}
222+
}
223+
198224
#if FEATURE_WASM_THREADS
199225

200226
[MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425

src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,11 +1358,28 @@ public void JsExportException(Exception value, string clazz)
13581358
[Fact]
13591359
public void JsExportThrows()
13601360
{
1361-
var ex = Assert.Throws<ArgumentException>(() => JavaScriptTestHelper.invoke1_String("-t-e-s-t-", nameof(JavaScriptTestHelper.Throw)));
1361+
var ex = Assert.Throws<ArgumentException>(() => JavaScriptTestHelper.invoke1_String("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport)));
13621362
Assert.DoesNotContain("Unexpected error", ex.Message);
13631363
Assert.Contains("-t-e-s-t-", ex.Message);
13641364
}
13651365

1366+
[Fact]
1367+
public void JsExportCatchToString()
1368+
{
1369+
var toString = JavaScriptTestHelper.catch1toString("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport));
1370+
Assert.DoesNotContain("Unexpected error", toString);
1371+
Assert.Contains("-t-e-s-t-", toString);
1372+
Assert.DoesNotContain(nameof(JavaScriptTestHelper.ThrowFromJSExport), toString);
1373+
}
1374+
1375+
[Fact]
1376+
public void JsExportCatchStack()
1377+
{
1378+
var stack = JavaScriptTestHelper.catch1stack("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport));
1379+
Assert.Contains(nameof(JavaScriptTestHelper.ThrowFromJSExport), stack);
1380+
Assert.Contains("catch1stack", stack);
1381+
}
1382+
13661383
#endregion Exception
13671384

13681385
#region JSObject
@@ -1906,12 +1923,12 @@ private void JsImportTest<T>(T value
19061923
var exThrow0 = Assert.Throws<JSException>(() => JavaScriptTestHelper.throw0());
19071924
Assert.Contains("throw-0-msg", exThrow0.Message);
19081925
Assert.DoesNotContain(" at ", exThrow0.Message);
1909-
Assert.Contains(" at Module.throw0", exThrow0.StackTrace);
1926+
Assert.Contains("throw0fn", exThrow0.StackTrace);
19101927

19111928
var exThrow1 = Assert.Throws<JSException>(() => throw1(value));
19121929
Assert.Contains("throw1-msg", exThrow1.Message);
19131930
Assert.DoesNotContain(" at ", exThrow1.Message);
1914-
Assert.Contains(" at Module.throw1", exThrow1.StackTrace);
1931+
Assert.Contains("throw1fn", exThrow1.StackTrace);
19151932

19161933
// anything is a system.object, sometimes it would be JSObject wrapper
19171934
if (typeof(T).IsPrimitive)

src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,14 @@ public static void ConsoleWriteLine([JSMarshalAs<JSType.String>] string message)
3232
Console.WriteLine(message);
3333
}
3434

35+
[JSImport("catch1toString", "JavaScriptTestHelper")]
36+
public static partial string catch1toString(string message, string functionName);
37+
38+
[JSImport("catch1stack", "JavaScriptTestHelper")]
39+
public static partial string catch1stack(string message, string functionName);
40+
3541
[JSExport]
36-
public static void Throw(string message)
42+
public static void ThrowFromJSExport(string message)
3743
{
3844
throw new ArgumentException(message);
3945
}
@@ -57,7 +63,7 @@ public static DateTime Now()
5763
[return: JSMarshalAs<JSType.String>]
5864
internal static partial string getClass1();
5965

60-
[JSImport("throw0", "JavaScriptTestHelper")]
66+
[JSImport("throw0fn", "JavaScriptTestHelper")]
6167
[return: JSMarshalAs<JSType.Discard>]
6268
internal static partial void throw0();
6369

@@ -215,7 +221,7 @@ internal static partial void Relaxed(string a1, Exception ex,
215221
[JSImport("identity1", "JavaScriptTestHelper")]
216222
[return: JSMarshalAs<JSType.Boolean>]
217223
internal static partial bool identity1_Int32([JSMarshalAs<JSType.Number>] int value);
218-
[JSImport("throw1", "JavaScriptTestHelper")]
224+
[JSImport("throw1fn", "JavaScriptTestHelper")]
219225
[return: JSMarshalAs<JSType.Number>]
220226
internal static partial int throw1_Int32([JSMarshalAs<JSType.Number>] int value);
221227
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -241,7 +247,7 @@ public static int EchoInt32([JSMarshalAs<JSType.Number>] int arg1)
241247
[JSImport("identity1", "JavaScriptTestHelper")]
242248
[return: JSMarshalAs<JSType.Boolean>]
243249
internal static partial bool identity1_String([JSMarshalAs<JSType.String>] string value);
244-
[JSImport("throw1", "JavaScriptTestHelper")]
250+
[JSImport("throw1fn", "JavaScriptTestHelper")]
245251
[return: JSMarshalAs<JSType.String>]
246252
internal static partial string throw1_String([JSMarshalAs<JSType.String>] string value);
247253
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -273,7 +279,7 @@ public static string EchoString([JSMarshalAs<JSType.String>] string arg1)
273279
[JSImport("identity1", "JavaScriptTestHelper")]
274280
[return: JSMarshalAs<JSType.Boolean>]
275281
internal static partial bool identity1_Object([JSMarshalAs<JSType.Any>] object value);
276-
[JSImport("throw1", "JavaScriptTestHelper")]
282+
[JSImport("throw1fn", "JavaScriptTestHelper")]
277283
[return: JSMarshalAs<JSType.Any>]
278284
internal static partial object throw1_Object([JSMarshalAs<JSType.Any>] object value);
279285
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -299,7 +305,7 @@ public static object EchoObject([JSMarshalAs<JSType.Any>] object arg1)
299305
[JSImport("identity1", "JavaScriptTestHelper")]
300306
[return: JSMarshalAs<JSType.Boolean>]
301307
internal static partial bool identity1_Exception([JSMarshalAs<JSType.Error>] Exception value);
302-
[JSImport("throw1", "JavaScriptTestHelper")]
308+
[JSImport("throw1fn", "JavaScriptTestHelper")]
303309
[return: JSMarshalAs<JSType.Error>]
304310
internal static partial Exception throw1_Exception([JSMarshalAs<JSType.Error>] Exception value);
305311
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -431,7 +437,7 @@ public static Func<int, int> BackFuncOfIntInt([JSMarshalAs<JSType.Function<JSTyp
431437
[JSImport("identity1", "JavaScriptTestHelper")]
432438
[return: JSMarshalAs<JSType.Boolean>]
433439
internal static partial bool identity1_Boolean([JSMarshalAs<JSType.Boolean>] bool value);
434-
[JSImport("throw1", "JavaScriptTestHelper")]
440+
[JSImport("throw1fn", "JavaScriptTestHelper")]
435441
[return: JSMarshalAs<JSType.Boolean>]
436442
internal static partial bool throw1_Boolean([JSMarshalAs<JSType.Boolean>] bool value);
437443
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -458,7 +464,7 @@ public static bool EchoBoolean([JSMarshalAs<JSType.Boolean>] bool arg1)
458464
[JSImport("identity1", "JavaScriptTestHelper")]
459465
[return: JSMarshalAs<JSType.Boolean>]
460466
internal static partial bool identity1_Char([JSMarshalAs<JSType.String>] char value);
461-
[JSImport("throw1", "JavaScriptTestHelper")]
467+
[JSImport("throw1fn", "JavaScriptTestHelper")]
462468
[return: JSMarshalAs<JSType.String>]
463469
internal static partial char throw1_Char([JSMarshalAs<JSType.String>] char value);
464470
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -484,7 +490,7 @@ public static char EchoChar([JSMarshalAs<JSType.String>] char arg1)
484490
[JSImport("identity1", "JavaScriptTestHelper")]
485491
[return: JSMarshalAs<JSType.Boolean>]
486492
internal static partial bool identity1_Byte([JSMarshalAs<JSType.Number>] byte value);
487-
[JSImport("throw1", "JavaScriptTestHelper")]
493+
[JSImport("throw1fn", "JavaScriptTestHelper")]
488494
[return: JSMarshalAs<JSType.Number>]
489495
internal static partial byte throw1_Byte([JSMarshalAs<JSType.Number>] byte value);
490496
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -510,7 +516,7 @@ public static byte EchoByte([JSMarshalAs<JSType.Number>] byte arg1)
510516
[JSImport("identity1", "JavaScriptTestHelper")]
511517
[return: JSMarshalAs<JSType.Boolean>]
512518
internal static partial bool identity1_Int16([JSMarshalAs<JSType.Number>] short value);
513-
[JSImport("throw1", "JavaScriptTestHelper")]
519+
[JSImport("throw1fn", "JavaScriptTestHelper")]
514520
[return: JSMarshalAs<JSType.Number>]
515521
internal static partial short throw1_Int16([JSMarshalAs<JSType.Number>] short value);
516522
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -536,7 +542,7 @@ public static short EchoInt16([JSMarshalAs<JSType.Number>] short arg1)
536542
[JSImport("identity1", "JavaScriptTestHelper")]
537543
[return: JSMarshalAs<JSType.Boolean>]
538544
internal static partial bool identity1_Int52([JSMarshalAs<JSType.Number>] long value);
539-
[JSImport("throw1", "JavaScriptTestHelper")]
545+
[JSImport("throw1fn", "JavaScriptTestHelper")]
540546
[return: JSMarshalAs<JSType.Number>]
541547
internal static partial long throw1_Int52([JSMarshalAs<JSType.Number>] long value);
542548
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -562,7 +568,7 @@ public static long EchoInt52([JSMarshalAs<JSType.Number>] long arg1)
562568
[JSImport("identity1", "JavaScriptTestHelper")]
563569
[return: JSMarshalAs<JSType.Boolean>]
564570
internal static partial bool identity1_BigInt64([JSMarshalAs<JSType.BigInt>] long value);
565-
[JSImport("throw1", "JavaScriptTestHelper")]
571+
[JSImport("throw1fn", "JavaScriptTestHelper")]
566572
[return: JSMarshalAs<JSType.BigInt>]
567573
internal static partial long throw1_BigInt64([JSMarshalAs<JSType.BigInt>] long value);
568574
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -588,7 +594,7 @@ public static long EchoBigInt64([JSMarshalAs<JSType.BigInt>] long arg1)
588594
[JSImport("identity1", "JavaScriptTestHelper")]
589595
[return: JSMarshalAs<JSType.Boolean>]
590596
internal static partial bool identity1_Double([JSMarshalAs<JSType.Number>] double value);
591-
[JSImport("throw1", "JavaScriptTestHelper")]
597+
[JSImport("throw1fn", "JavaScriptTestHelper")]
592598
[return: JSMarshalAs<JSType.Number>]
593599
internal static partial double throw1_Double([JSMarshalAs<JSType.Number>] double value);
594600
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -614,7 +620,7 @@ public static double EchoDouble([JSMarshalAs<JSType.Number>] double arg1)
614620
[JSImport("identity1", "JavaScriptTestHelper")]
615621
[return: JSMarshalAs<JSType.Boolean>]
616622
internal static partial bool identity1_Single([JSMarshalAs<JSType.Number>] float value);
617-
[JSImport("throw1", "JavaScriptTestHelper")]
623+
[JSImport("throw1fn", "JavaScriptTestHelper")]
618624
[return: JSMarshalAs<JSType.Number>]
619625
internal static partial float throw1_Single([JSMarshalAs<JSType.Number>] float value);
620626
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -640,7 +646,7 @@ public static float EchoSingle([JSMarshalAs<JSType.Number>] float arg1)
640646
[JSImport("identity1", "JavaScriptTestHelper")]
641647
[return: JSMarshalAs<JSType.Boolean>]
642648
internal static partial bool identity1_IntPtr([JSMarshalAs<JSType.Number>] IntPtr value);
643-
[JSImport("throw1", "JavaScriptTestHelper")]
649+
[JSImport("throw1fn", "JavaScriptTestHelper")]
644650
[return: JSMarshalAs<JSType.Number>]
645651
internal static partial IntPtr throw1_IntPtr([JSMarshalAs<JSType.Number>] IntPtr value);
646652
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -667,7 +673,7 @@ public static IntPtr EchoIntPtr([JSMarshalAs<JSType.Number>] IntPtr arg1)
667673
[JSImport("identity1", "JavaScriptTestHelper")]
668674
[return: JSMarshalAs<JSType.Boolean>]
669675
internal unsafe static partial bool identity1_VoidPtr([JSMarshalAs<JSType.Number>] void* value);
670-
[JSImport("throw1", "JavaScriptTestHelper")]
676+
[JSImport("throw1fn", "JavaScriptTestHelper")]
671677
[return: JSMarshalAs<JSType.Number>]
672678
internal unsafe static partial void* throw1_VoidPtr([JSMarshalAs<JSType.Number>] void* value);
673679
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -693,7 +699,7 @@ public static IntPtr EchoIntPtr([JSMarshalAs<JSType.Number>] IntPtr arg1)
693699
[JSImport("identity1", "JavaScriptTestHelper")]
694700
[return: JSMarshalAs<JSType.Boolean>]
695701
internal static partial bool identity1_DateTime([JSMarshalAs<JSType.Date>] DateTime value);
696-
[JSImport("throw1", "JavaScriptTestHelper")]
702+
[JSImport("throw1fn", "JavaScriptTestHelper")]
697703
[return: JSMarshalAs<JSType.Date>]
698704
internal static partial DateTime throw1_DateTime([JSMarshalAs<JSType.Date>] DateTime value);
699705
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -719,7 +725,7 @@ public static DateTime EchoDateTime([JSMarshalAs<JSType.Date>] DateTime arg1)
719725
[JSImport("identity1", "JavaScriptTestHelper")]
720726
[return: JSMarshalAs<JSType.Boolean>]
721727
internal static partial bool identity1_DateTimeOffset([JSMarshalAs<JSType.Date>] DateTimeOffset value);
722-
[JSImport("throw1", "JavaScriptTestHelper")]
728+
[JSImport("throw1fn", "JavaScriptTestHelper")]
723729
[return: JSMarshalAs<JSType.Date>]
724730
internal static partial DateTimeOffset throw1_DateTimeOffset([JSMarshalAs<JSType.Date>] DateTimeOffset value);
725731
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -746,7 +752,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs<JSType.Date>] DateT
746752
[JSImport("identity1", "JavaScriptTestHelper")]
747753
[return: JSMarshalAs<JSType.Boolean>]
748754
internal static partial bool identity1_NullableBoolean([JSMarshalAs<JSType.Boolean>] bool? value);
749-
[JSImport("throw1", "JavaScriptTestHelper")]
755+
[JSImport("throw1fn", "JavaScriptTestHelper")]
750756
[return: JSMarshalAs<JSType.Boolean>]
751757
internal static partial bool? throw1_NullableBoolean([JSMarshalAs<JSType.Boolean>] bool? value);
752758
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -773,7 +779,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs<JSType.Date>] DateT
773779
[JSImport("identity1", "JavaScriptTestHelper")]
774780
[return: JSMarshalAs<JSType.Boolean>]
775781
internal static partial bool identity1_NullableInt32([JSMarshalAs<JSType.Number>] int? value);
776-
[JSImport("throw1", "JavaScriptTestHelper")]
782+
[JSImport("throw1fn", "JavaScriptTestHelper")]
777783
[return: JSMarshalAs<JSType.Number>]
778784
internal static partial int? throw1_NullableInt32([JSMarshalAs<JSType.Number>] int? value);
779785
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -800,7 +806,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs<JSType.Date>] DateT
800806
[JSImport("identity1", "JavaScriptTestHelper")]
801807
[return: JSMarshalAs<JSType.Boolean>]
802808
internal static partial bool identity1_NullableBigInt64([JSMarshalAs<JSType.BigInt>] long? value);
803-
[JSImport("throw1", "JavaScriptTestHelper")]
809+
[JSImport("throw1fn", "JavaScriptTestHelper")]
804810
[return: JSMarshalAs<JSType.BigInt>]
805811
internal static partial long? throw1_NullableBigInt64([JSMarshalAs<JSType.BigInt>] long? value);
806812
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -827,7 +833,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs<JSType.Date>] DateT
827833
[JSImport("identity1", "JavaScriptTestHelper")]
828834
[return: JSMarshalAs<JSType.Boolean>]
829835
internal static partial bool identity1_NullableIntPtr([JSMarshalAs<JSType.Number>] IntPtr? value);
830-
[JSImport("throw1", "JavaScriptTestHelper")]
836+
[JSImport("throw1fn", "JavaScriptTestHelper")]
831837
[return: JSMarshalAs<JSType.Number>]
832838
internal static partial IntPtr? throw1_NullableIntPtr([JSMarshalAs<JSType.Number>] IntPtr? value);
833839
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -854,7 +860,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs<JSType.Date>] DateT
854860
[JSImport("identity1", "JavaScriptTestHelper")]
855861
[return: JSMarshalAs<JSType.Boolean>]
856862
internal static partial bool identity1_NullableDouble([JSMarshalAs<JSType.Number>] double? value);
857-
[JSImport("throw1", "JavaScriptTestHelper")]
863+
[JSImport("throw1fn", "JavaScriptTestHelper")]
858864
[return: JSMarshalAs<JSType.Number>]
859865
internal static partial double? throw1_NullableDouble([JSMarshalAs<JSType.Number>] double? value);
860866
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -881,7 +887,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs<JSType.Date>] DateT
881887
[JSImport("identity1", "JavaScriptTestHelper")]
882888
[return: JSMarshalAs<JSType.Boolean>]
883889
internal static partial bool identity1_NullableDateTime([JSMarshalAs<JSType.Date>] DateTime? value);
884-
[JSImport("throw1", "JavaScriptTestHelper")]
890+
[JSImport("throw1fn", "JavaScriptTestHelper")]
885891
[return: JSMarshalAs<JSType.Date>]
886892
internal static partial DateTime? throw1_NullableDateTime([JSMarshalAs<JSType.Date>] DateTime? value);
887893
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -907,7 +913,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs<JSType.Date>] DateT
907913
[JSImport("identity1", "JavaScriptTestHelper")]
908914
[return: JSMarshalAs<JSType.Boolean>]
909915
internal static partial bool identity1_JSObject([JSMarshalAs<JSType.Object>] JSObject value);
910-
[JSImport("throw1", "JavaScriptTestHelper")]
916+
[JSImport("throw1fn", "JavaScriptTestHelper")]
911917
[return: JSMarshalAs<JSType.Object>]
912918
internal static partial JSObject throw1_JSObject([JSMarshalAs<JSType.Object>] JSObject value);
913919
[JSImport("invoke1", "JavaScriptTestHelper")]
@@ -1236,4 +1242,4 @@ public partial record struct NestedRecordStruct
12361242
[System.Runtime.InteropServices.JavaScript.JSExport]
12371243
public static string EchoString(string message) => message + "85";
12381244
}
1239-
}
1245+
}

0 commit comments

Comments
 (0)