diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs index f6e7e99abd310b..9d74128c107601 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs @@ -392,10 +392,10 @@ public async Task JsExportTaskOfInt(int value) [Theory] [MemberData(nameof(MarshalBigInt64Cases))] - public async Task JsExportTaskOfLong(long value) + public async Task JsExportTaskOfBigLong(long value) { TaskCompletionSource tcs = new TaskCompletionSource(); - var res = JavaScriptTestHelper.invoke1_TaskOfLong(tcs.Task, nameof(JavaScriptTestHelper.AwaitTaskOfInt64)); + var res = JavaScriptTestHelper.invoke1_TaskOfBigLong(tcs.Task, nameof(JavaScriptTestHelper.AwaitTaskOfInt64)); tcs.SetResult(value); // unresolved task marshalls promise and resolves on completion await Task.Yield(); var rr = await res; @@ -405,11 +405,11 @@ public async Task JsExportTaskOfLong(long value) [Theory] [MemberData(nameof(MarshalBigInt64Cases))] - public async Task JsExportCompletedTaskOfLong(long value) + public async Task JsExportCompletedTaskOfBigLong(long value) { TaskCompletionSource tcs = new TaskCompletionSource(); tcs.SetResult(value); // completed task marshalls value immediately - var res = JavaScriptTestHelper.invoke1_TaskOfLong(tcs.Task, nameof(JavaScriptTestHelper.AwaitTaskOfInt64)); + var res = JavaScriptTestHelper.invoke1_TaskOfBigLong(tcs.Task, nameof(JavaScriptTestHelper.AwaitTaskOfInt64)); await Task.Yield(); var rr = await res; await Task.Yield(); @@ -449,6 +449,18 @@ public void JsExportCallback_FunctionIntIntThrow() Assert.Same(expected, actual); } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] + public async Task JsExportFunctionDateTimeDateTime() + { + DateTime input = DateTime.Now; + DateTime receivedArg = DateTime.MinValue; + DateTime returnVal = JavaScriptTestHelper.invokeDelegateOfDateTime((DateTime date) => { + return receivedArg = date; + }, input, 0); + Assert.Equal(input, receivedArg); + Assert.Equal(input, returnVal); + } + private void JsExportTest<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] T>(T value , Func invoke, string echoName, string jsType, string? jsClass = null) { @@ -469,5 +481,90 @@ public async Task JSExportCompletedTaskReturnsResolvedPromise() string result = await JavaScriptTestHelper.InvokeReturnCompletedTask(); Assert.Equal("resolved", result); } + + #region Assertion Errors + [Fact] + public async Task JsExportTaskOfShortOutOfRange_ThrowsAssertionInTaskContinuation() + { + // 1<<16 is out of range, passed to js and back, marshalling ts code asserts out of range and throws + Task res = JavaScriptTestHelper.invoke1_TaskOfOutOfRangeShort(Task.FromResult(1 << 16), nameof(JavaScriptTestHelper.AwaitTaskOfShort)); + JSException ex = await Assert.ThrowsAsync(() => res); + Assert.Equal("Error: Assert failed: Overflow: value 65536 is out of -32768 32767 range", ex.Message); + } + + [Fact] + public async Task JsExportTaskOfStringTypeAssertion_ThrowsAssertionInTaskContinuation() + { + // long value cannot be converted to string, error thrown through continuation in CS + Task res = JavaScriptTestHelper.invoke1_TaskOfLong_ExceptionReturnTypeAssert(Task.FromResult(1L << 32), nameof(JavaScriptTestHelper.AwaitTaskOfString)); + JSException ex = await Assert.ThrowsAsync(() => res); + Assert.Equal("Error: Assert failed: Value is not a String", ex.Message); + } + + [Fact] + public async Task JsExportTaskOfLong_TaskReturnValue_OverflowInt52() + { + long value = 1L << 53; + TaskCompletionSource tcs = new TaskCompletionSource(); + var res = JavaScriptTestHelper.invoke1_TaskOfLong(tcs.Task, nameof(JavaScriptTestHelper.AwaitTaskOfInt64)); + tcs.SetResult(value); + JSException ex = await Assert.ThrowsAsync(() => res); + Assert.Equal("Error: Assert failed: Value is not an integer: 9007199254740992 (bigint)", ex.Message); + } + + [Fact] + public async Task JsExportTaskOfDateTime_TaskReturnValue_OverflowNETDateTime() + { + var res = JavaScriptTestHelper.invokeExportWithTaskOfMaxJSDateTime(nameof(JavaScriptTestHelper.AwaitTaskOfDateTime)); + JSException ex = await Assert.ThrowsAsync(() => res); + Assert.Equal("Error: Assert failed: Overflow: value +275760-09-13T00:00:00.000Z is out of 0001-01-01T00:00:00.000Z 9999-12-31T23:59:59.999Z range", ex.Message); + } + + [Fact] + public async Task JsExportDateTime_ReturnValue_OverflowNETDateTime() + { + JSException ex = Assert.Throws(() => JavaScriptTestHelper.invokeExportWithMaxJSDateTime(nameof(JavaScriptTestHelper.EchoDateTime))); + Assert.Equal("Error: Assert failed: Overflow: value +275760-09-13T00:00:00.000Z is out of 0001-01-01T00:00:00.000Z 9999-12-31T23:59:59.999Z range", ex.Message); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] + public async Task JsExportFuncOfDateTime_Argument_OverflowNETDateTime() + { + DateTime receivedArg = DateTime.MinValue; + JSException ex = Assert.Throws(() => JavaScriptTestHelper.invokeDelegateOfDateTime((DateTime date) => { + return receivedArg = date; + }, DateTime.MaxValue, 60_001)); + Assert.Equal("Error: Assert failed: Overflow: value +010000-01-01T00:01:00.000Z is out of 0001-01-01T00:00:00.000Z 9999-12-31T23:59:59.999Z range", ex.Message); + Assert.Equal(DateTime.MinValue, receivedArg); // delegate invoke failed, no change to arg + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] + public void JsExportCallback_FunctionLongLong_OverflowInt52_JSSide() + { + long called = -1; + Assert.Equal(-1, called); + JSException ex = Assert.Throws(() => JavaScriptTestHelper.invokeFuncOfLongLong((long a) => + { + return called = a; + }, 9007199254740991, offset: 1)); + Assert.Equal(-1, called); + Assert.Equal("Error: Assert failed: Value is not an integer: 9007199254740992 (number)", ex.Message); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] + public void JsExportCallback_FunctionLongLong_OverflowInt52_NETSide() + { + long called = -1; + var chain = JavaScriptTestHelper.invoke1_FuncOfLongLong((long a) => + { + return called = a; + }, nameof(JavaScriptTestHelper.BackFuncOfLongLong)); + + Assert.Equal(-1, called); + OverflowException ex = Assert.Throws(() => chain(long.MaxValue)); + Assert.Equal(-1, called); + Assert.Equal("Overflow: value 9223372036854775807 is out of -9007199254740991 9007199254740991 range.", ex.Message); + } + #endregion } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs index b862a970c4aa6b..254338ea4cee29 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs @@ -106,6 +106,42 @@ public unsafe void DotnetInstance() Assert.Equal("Yoda", JSHost.DotnetInstance.GetPropertyAsString("testString")); } + [Fact] + public async Task RejectString() + { + var ex = await Assert.ThrowsAsync(() => JavaScriptTestHelper.Reject("noodles")); + Assert.Contains("noodles", ex.Message); + } + + [Fact] + public async Task RejectException() + { + var expected = new Exception("noodles"); + var actual = await Assert.ThrowsAsync(() => JavaScriptTestHelper.Reject(expected)); + Assert.Equal(expected, actual); + } + + [Fact] + public async Task RejectNull() + { + var ex = await Assert.ThrowsAsync(() => JavaScriptTestHelper.Reject(null)); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] + public unsafe void OptimizedPaths() + { + JavaScriptTestHelper.optimizedReached = 0; + JavaScriptTestHelper.invoke0V(); + Assert.Equal(1, JavaScriptTestHelper.optimizedReached); + JavaScriptTestHelper.invoke1V(42); + Assert.Equal(43, JavaScriptTestHelper.optimizedReached); + Assert.Equal(124, JavaScriptTestHelper.invoke1R(123)); + Assert.Equal(43 + 123, JavaScriptTestHelper.optimizedReached); + Assert.Equal(32, JavaScriptTestHelper.invoke2R(15, 16)); + Assert.Equal(43 + 123 + 31, JavaScriptTestHelper.optimizedReached); + } + + #region Assertion Errors [Fact] public unsafe void BadCast() { @@ -136,40 +172,62 @@ public unsafe void OutOfRange() } [Fact] - public async Task RejectString() + public async Task TaskOfShortOutOfRange_ThrowsAssertionInTaskContinuation() { - var ex = await Assert.ThrowsAsync(() => JavaScriptTestHelper.Reject("noodles")); - Assert.Contains("noodles", ex.Message); + Task res = JavaScriptTestHelper.ReturnResolvedPromiseWithIntMaxValue_AsShortToBeOutOfRange(); + JSException ex = await Assert.ThrowsAsync(() => res); + Assert.Equal("Error: Assert failed: Overflow: value 2147483647 is out of -32768 32767 range", ex.Message); } [Fact] - public async Task RejectException() + public async Task TaskOfByteOutOfRange_ThrowsAssertionInTaskContinuation() { - var expected = new Exception("noodles"); - var actual = await Assert.ThrowsAsync(() => JavaScriptTestHelper.Reject(expected)); - Assert.Equal(expected, actual); + Task res = JavaScriptTestHelper.ReturnResolvedPromiseWithIntMaxValue_AsByteToBeOutOfRange(); + JSException ex = await Assert.ThrowsAsync(() => res); + Assert.Equal("Error: Assert failed: Overflow: value 2147483647 is out of 0 255 range", ex.Message); } [Fact] - public async Task RejectNull() + public async Task TaskOfDateTimeOutOfRange_ThrowsAssertionInTaskContinuation() { - var ex = await Assert.ThrowsAsync(() => JavaScriptTestHelper.Reject(null)); + Task res = JavaScriptTestHelper.ReturnResolvedPromiseWithDateMaxValue(); + JSException ex = await Assert.ThrowsAsync(() => res); + Assert.Equal("Error: Assert failed: Overflow: value +275760-09-13T00:00:00.000Z is out of 0001-01-01T00:00:00.000Z 9999-12-31T23:59:59.999Z range", ex.Message); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] - public unsafe void OptimizedPaths() + [Fact] + public async Task DateTimeMaxValueBoundaryCondition() { - JavaScriptTestHelper.optimizedReached = 0; - JavaScriptTestHelper.invoke0V(); - Assert.Equal(1, JavaScriptTestHelper.optimizedReached); - JavaScriptTestHelper.invoke1V(42); - Assert.Equal(43, JavaScriptTestHelper.optimizedReached); - Assert.Equal(124, JavaScriptTestHelper.invoke1R(123)); - Assert.Equal(43 + 123, JavaScriptTestHelper.optimizedReached); - Assert.Equal(32, JavaScriptTestHelper.invoke2R(15, 16)); - Assert.Equal(43 + 123 + 31, JavaScriptTestHelper.optimizedReached); + DateTime t = JavaScriptTestHelper.ReturnDateTimeWithOffset(DateTime.MaxValue, 0); + Assert.Equal(DateTime.MaxValue.AddTicks(-9_999), t); // Microseconds are lost during marshalling + JSException ex = Assert.Throws(() => JavaScriptTestHelper.ReturnDateTimeWithOffset(DateTime.MaxValue, 1)); + Assert.Equal("Error: Assert failed: Overflow: value +010000-01-01T00:00:00.000Z is out of 0001-01-01T00:00:00.000Z 9999-12-31T23:59:59.999Z range", ex.Message); + } + + [Fact] + public async Task DateTimeMinValueBoundaryCondition() + { + DateTime t = JavaScriptTestHelper.ReturnDateTimeWithOffset(DateTime.MinValue, 0); + Assert.Equal(DateTime.MinValue, t); + JSException ex = Assert.Throws(() => JavaScriptTestHelper.ReturnDateTimeWithOffset(DateTime.MinValue, -1)); + Assert.Equal("Error: Assert failed: Overflow: value 0000-12-31T23:59:59.999Z is out of 0001-01-01T00:00:00.000Z 9999-12-31T23:59:59.999Z range", ex.Message); + } + + [Fact] + public async Task Int32ArrayWithOutOfRangeValues() + { + int[] arr = JavaScriptTestHelper.getInt32ArrayWithOutOfRangeValues(); + Assert.Equal(0, arr[0]); + Assert.Equal(1, arr[1]); + Assert.Equal(-2147483648, arr[2]); + Assert.Equal(-1147483648, arr[3]); + Assert.Equal(-1, arr[4]); + Assert.Equal(0, arr[5]); + // Currently, values > int32.MaxValue are wrapped around when marshaled from JS to C#. + // TODO: Instead throw OverflowException when out of range value is encountered. } + #endregion #region Get/Set Property @@ -718,6 +776,15 @@ public void JSImportDateTime(DateTime value) "object", "Date"); } + [Fact] // JavaScript Date has millisecond precision, the microseconds are lost during marshalling + public async Task DateTimeMarshallingLosesMicrosecondComponentPrecisionLoss() + { + DateTime now = new DateTime(1995, 4, 1, 10, 43, 6, 94, microsecond: 20); + DateTime t = JavaScriptTestHelper.ReturnDateTimeWithOffset(now, 0); + Assert.NotEqual(now, t); + DateTime nowWithJSPrecision = now.AddMicroseconds(-now.Microsecond); + Assert.Equal(nowWithJSPrecision, t); + } #endregion Datetime #region DateTimeOffset 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 c064839d530677..8d0ca9efe49422 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 @@ -247,6 +247,10 @@ internal static partial void Relaxed(string a1, Exception ex, [return: JSMarshalAs] internal static partial JSObject? store_JSObjectArray([JSMarshalAs>] JSObject[]? value, [JSMarshalAs] int index); + [JSImport("getInt32ArrayWithOutOfRangeValues", "JavaScriptTestHelper")] + [return: JSMarshalAs>] + internal static partial int[] getInt32ArrayWithOutOfRangeValues(); + #endregion #region Views @@ -432,24 +436,69 @@ public static Exception EchoException([JSMarshalAs] Exception arg1 [JSImport("await1", "JavaScriptTestHelper")] [return: JSMarshalAs>] internal static partial Task await1([JSMarshalAs>] Task arg1); + [JSImport("await1", "JavaScriptTestHelper")] [return: JSMarshalAs>] internal static partial Task await1_TaskOfException([JSMarshalAs>] Task arg1); + [JSImport("invoke1", "JavaScriptTestHelper")] [return: JSMarshalAs>] internal static partial Task invoke1_TaskOfObject([JSMarshalAs>] Task value, [JSMarshalAs] string name); + [JSImport("invoke1", "JavaScriptTestHelper")] [return: JSMarshalAs>] internal static partial Task invoke1_TaskOfInt([JSMarshalAs>] Task value, [JSMarshalAs] string name); + [JSImport("invoke1", "JavaScriptTestHelper")] [return: JSMarshalAs>] + internal static partial Task invoke1_TaskOfBigLong([JSMarshalAs>] Task value, [JSMarshalAs] string name); + + [JSImport("invoke1", "JavaScriptTestHelper")] + [return: JSMarshalAs>] internal static partial Task invoke1_TaskOfLong([JSMarshalAs>] Task value, [JSMarshalAs] string name); + + [JSImport("invokeExportWithPromiseWithDateMaxValue", "JavaScriptTestHelper")] + [return: JSMarshalAs>] + internal static partial Task invokeExportWithTaskOfMaxJSDateTime([JSMarshalAs] string name); + + [JSImport("invokeExportWithDateMaxValue", "JavaScriptTestHelper")] + [return: JSMarshalAs] + internal static partial DateTime invokeExportWithMaxJSDateTime([JSMarshalAs] string name); + + [JSImport("invoke1", "JavaScriptTestHelper")] + [return: JSMarshalAs>] + internal static partial Task invoke1_TaskOfLong_ExceptionReturnTypeAssert([JSMarshalAs>] Task value, [JSMarshalAs] string name); + + [JSImport("invoke1", "JavaScriptTestHelper")] + [return: JSMarshalAs>] + internal static partial Task invoke1_TaskOfOutOfRangeShort([JSMarshalAs>] Task value, [JSMarshalAs] string name); + + [JSImport("invokeDelegate_DateTimeWithOffset", "JavaScriptTestHelper")] + [return: JSMarshalAs] + internal static partial DateTime invokeDelegateOfDateTime([JSMarshalAs>] Func datetransformer, [JSMarshalAs] DateTime date, int offsetMilliseconds); + [JSImport("returnResolvedPromise", "JavaScriptTestHelper")] internal static partial Task ReturnResolvedPromise(); [JSImport("invokeReturnCompletedTask", "JavaScriptTestHelper")] internal static partial Task InvokeReturnCompletedTask(); + [JSImport("returnResolvedPromiseWithIntMaxValue", "JavaScriptTestHelper")] + [return: JSMarshalAs>] + internal static partial Task ReturnResolvedPromiseWithIntMaxValue_AsShortToBeOutOfRange(); + + [JSImport("returnResolvedPromiseWithIntMaxValue", "JavaScriptTestHelper")] + [return: JSMarshalAs>] + internal static partial Task ReturnResolvedPromiseWithIntMaxValue_AsByteToBeOutOfRange(); + + [JSImport("returnResolvedPromiseWithDateMaxValue", "JavaScriptTestHelper")] + [return: JSMarshalAs>] + internal static partial Task ReturnResolvedPromiseWithDateMaxValue(); + + [JSImport("returnDateWithOffset", "JavaScriptTestHelper")] + [return: JSMarshalAs] + internal static partial DateTime ReturnDateTimeWithOffset([JSMarshalAs] DateTime date, int offsetMilliseconds); + [JSExport] internal static Task ReturnCompletedTask() { @@ -472,6 +521,30 @@ public static async Task AwaitTaskOfInt64([JSMarshalAs>] + public static async Task AwaitTaskOfShort([JSMarshalAs>] Task arg1) + { + var res = await arg1; + return res; + } + + [JSExport] + [return: JSMarshalAs>] + public static async Task AwaitTaskOfString([JSMarshalAs>] Task arg1) + { + var res = await arg1; + return res; + } + + [JSExport] + [return: JSMarshalAs>] + public static async Task AwaitTaskOfDateTime([JSMarshalAs>] Task arg1) + { + var res = await arg1; + return res; + } + #endregion #region Action + Func @@ -535,6 +608,24 @@ public static Func BackFuncOfIntInt([JSMarshalAs>] + internal static partial Func invoke1_FuncOfLongLong([JSMarshalAs>] Func value, [JSMarshalAs] string name); + + [JSExport] + [return: JSMarshalAs>] + public static Func BackFuncOfLongLong([JSMarshalAs>] Func arg1) + { + return (long a) => + { + return arg1(a); + }; + } + + [JSImport("invokeFuncWithOffset", "JavaScriptTestHelper")] + [return: JSMarshalAs] + internal static partial long invokeFuncOfLongLong([JSMarshalAs>] Func fn, [JSMarshalAs] long value, [JSMarshalAs] int offset); + #endregion #region Boolean 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 f4f2772fdda8b5..1c91dbdb1dfe6b 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 @@ -246,15 +246,55 @@ export function invoke2(arg1, name) { return res; } +export function invokeExportWithPromiseWithDateMaxValue(exportName) { + const fn1 = dllExports.System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper[exportName]; + const res = fn1(returnResolvedPromiseWithDateMaxValue()); + return res; +} + +export function invokeExportWithDateMaxValue(exportName) { + const fn1 = dllExports.System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper[exportName]; + const res = fn1(returnDateMaxValue()); + return res; +} + +export function invokeDelegate_DateTimeWithOffset(delegate, date, offset) { + return delegate(returnDateWithOffset(date, offset)); +} + +export function getInt32ArrayWithOutOfRangeValues() { + return [0, 1, 2147483648, 3147483648, 9007199254740991, 9007199254740992]; +} + export function returnResolvedPromise() { return Promise.resolve(); } +export function returnResolvedPromiseWithIntMaxValue() { + return Promise.resolve(2147483647); +} + +export function returnResolvedPromiseWithDateMaxValue() { + return Promise.resolve(new Date(8640000000000000)); +} + +export function returnDateMaxValue() { + return new Date(8640000000000000); +} + +export function returnDateWithOffset(date, offset) { + return new Date(date.getTime() + offset); +} + export async function invokeReturnCompletedTask() { await dllExports.System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper.ReturnCompletedTask(); return "resolved"; } +export function invokeFuncWithOffset(fn, arg, offset) { + return fn(arg + offset); +} + export function invokeStructClassRecords(arg1) { return [ dllExports.JavaScriptTestHelperNamespace.JavaScriptTestHelper.EchoString(arg1), @@ -490,4 +530,4 @@ export function isSetTimeoutHit() { export function isPromiseThenHit() { return promiseThenHit; -} \ No newline at end of file +} diff --git a/src/mono/browser/runtime/managed-exports.ts b/src/mono/browser/runtime/managed-exports.ts index 159b250d58f1e0..e1d2dc06cb5027 100644 --- a/src/mono/browser/runtime/managed-exports.ts +++ b/src/mono/browser/runtime/managed-exports.ts @@ -151,13 +151,18 @@ export function complete_task (holder_gc_handle: GCHandle, error?: any, data?: a set_arg_type(arg1, MarshalerType.Object); set_gc_handle(arg1, holder_gc_handle); const arg2 = get_arg(args, 3); - if (error) { - marshal_exception_to_cs(arg2, error); - } else { + if (!error) { set_arg_type(arg2, MarshalerType.None); const arg3 = get_arg(args, 4); mono_assert(res_converter, "res_converter missing"); - res_converter(arg3, data); + try { + res_converter(arg3, data); + } catch (ex) { + error = ex; + } + } + if (error) { + marshal_exception_to_cs(arg2, error); } invoke_async_jsexport(runtimeHelpers.ioThreadTID, managedExports.CompleteTask, args, size); } finally { diff --git a/src/mono/browser/runtime/marshal.ts b/src/mono/browser/runtime/marshal.ts index e369da6418e9cd..f3186d7cf2592a 100644 --- a/src/mono/browser/runtime/marshal.ts +++ b/src/mono/browser/runtime/marshal.ts @@ -307,10 +307,13 @@ export function set_arg_i64_big (arg: JSMarshalerArgument, value: bigint): void setI64Big(arg, value); } +const minDateUnixTime = -0x3883122CD800; +const maxDateUnixTime = 0xE677D21FDBFF; export function set_arg_date (arg: JSMarshalerArgument, value: Date): void { mono_assert(arg, "Null arg"); // getTime() is always UTC const unixTime = value.getTime(); + mono_check(unixTime >= minDateUnixTime && unixTime <= maxDateUnixTime, () => `Overflow: value ${value.toISOString()} is out of ${new Date(minDateUnixTime).toISOString()} ${new Date(maxDateUnixTime).toISOString()} range`); setF64(arg, unixTime); } diff --git a/src/native/corehost/browserhost/loader/logging.ts b/src/native/corehost/browserhost/loader/logging.ts index 1f8530fa870fc1..65ee07bc4ea8de 100644 --- a/src/native/corehost/browserhost/loader/logging.ts +++ b/src/native/corehost/browserhost/loader/logging.ts @@ -5,7 +5,7 @@ import { loaderConfig } from "./config"; export function check(condition: unknown, message: string): asserts condition { if (!condition) { - throw new Error(`dotnetAssert failed: ${message}`); + throw new Error(`Assert failed: ${message}`); } } @@ -15,7 +15,7 @@ export function check(condition: unknown, message: string): asserts condition { export function fastCheck(condition: unknown, messageFactory: (() => string)): asserts condition { if (!condition) { const message = messageFactory(); - throw new Error(`dotnetAssert failed: ${message}`); + throw new Error(`Assert failed: ${message}`); } } diff --git a/src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/managed-exports.ts b/src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/managed-exports.ts index bbabc7a024e907..653ab7ebfaaed0 100644 --- a/src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/managed-exports.ts +++ b/src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/managed-exports.ts @@ -101,13 +101,18 @@ export function completeTask(holderGcHandle: GCHandle, error?: any, data?: any, setArgType(arg1, MarshalerType.Object); setGcHandle(arg1, holderGcHandle); const arg2 = getArg(args, 3); + if (!error) { + try { + setArgType(arg2, MarshalerType.None); + const arg3 = getArg(args, 4); + dotnetAssert.check(resConverter, "resConverter missing"); + resConverter(arg3, data); + } catch (e) { + error = e; + } + } if (error) { marshalExceptionToCs(arg2, error); - } else { - setArgType(arg2, MarshalerType.None); - const arg3 = getArg(args, 4); - dotnetAssert.check(resConverter, "resConverter missing"); - resConverter(arg3, data); } dotnetInteropJSExports.SystemInteropJS_CompleteTask(args); } finally { diff --git a/src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/marshal.ts b/src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/marshal.ts index eff31c4d305274..7814c24f8be87d 100644 --- a/src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/marshal.ts +++ b/src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/marshal.ts @@ -275,10 +275,13 @@ export function setArgI64Big(arg: JSMarshalerArgument, value: bigint): void { dotnetApi.setHeapI64Big(arg, value); } +const minDateUnixTime = -0x3883122CD800; +const maxDateUnixTime = 0xE677D21FDBFF; export function setArgDate(arg: JSMarshalerArgument, value: Date): void { dotnetAssert.check(arg, "Null arg"); // getTime() is always UTC const unixTime = value.getTime(); + dotnetAssert.check(unixTime >= minDateUnixTime && unixTime <= maxDateUnixTime, `Overflow: value ${value.toISOString()} is out of ${new Date(minDateUnixTime).toISOString()} ${new Date(maxDateUnixTime).toISOString()} range`); dotnetApi.setHeapF64(arg, unixTime); }