Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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<long> tcs = new TaskCompletionSource<long>();
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;
Expand All @@ -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<long> tcs = new TaskCompletionSource<long>();
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();
Expand Down Expand Up @@ -469,5 +469,51 @@ 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<short> res = JavaScriptTestHelper.invoke1_TaskOfOutOfRangeShort(Task.FromResult(1 << 16), nameof(JavaScriptTestHelper.AwaitTaskOfShort));
JSException ex = await Assert.ThrowsAsync<JSException>(() => 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<string> res = JavaScriptTestHelper.invoke1_TaskOfLong_ExceptionReturnTypeAssert(Task.FromResult(1L << 32), nameof(JavaScriptTestHelper.AwaitTaskOfString));
JSException ex = await Assert.ThrowsAsync<JSException>(() => res);
Assert.Equal("Error: Assert failed: Value is not a String", ex.Message);
}

[Fact]
public async Task JsExportTaskOfLong_OverflowInt52()
{
long value = 1L << 53;
TaskCompletionSource<long> tcs = new TaskCompletionSource<long>();
var res = JavaScriptTestHelper.invoke1_TaskOfLong(tcs.Task, nameof(JavaScriptTestHelper.AwaitTaskOfInt64));
tcs.SetResult(value);
JSException ex = await Assert.ThrowsAsync<JSException>(() => res);
Assert.Equal("Error: Assert failed: Value is not an integer: 9007199254740992 (bigint)", ex.Message);
}

[Fact]
public async Task JsExportTaskOfDateTime_OverflowNETDateTime()
{
var res = JavaScriptTestHelper.invokeExportWithTaskOfMaxJSDateTime(nameof(JavaScriptTestHelper.AwaitTaskOfDateTime));
JSException ex = await Assert.ThrowsAsync<JSException>(() => res);
Assert.Equal("Error: Assert failed: Overflow: value 8640000000000000 is out of -62135596800000 253402300799999 range", ex.Message);
}

[Fact]
public async Task JsExportDateTime_OverflowNETDateTime()
{
JSException ex = Assert.Throws<JSException>(() => JavaScriptTestHelper.invokeExportWithMaxJSDateTime(nameof(JavaScriptTestHelper.EchoDateTime)));
Assert.Equal("Error: Assert failed: Overflow: value 8640000000000000 is out of -62135596800000 253402300799999 range", ex.Message);
}
#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<JSException>(() => JavaScriptTestHelper.Reject("noodles"));
Assert.Contains("noodles", ex.Message);
}

[Fact]
public async Task RejectException()
{
var expected = new Exception("noodles");
var actual = await Assert.ThrowsAsync<Exception>(() => JavaScriptTestHelper.Reject(expected));
Assert.Equal(expected, actual);
}

[Fact]
public async Task RejectNull()
{
var ex = await Assert.ThrowsAsync<JSException>(() => 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()
{
Expand Down Expand Up @@ -136,40 +172,29 @@ public unsafe void OutOfRange()
}

[Fact]
public async Task RejectString()
public async Task TaskOfShortOutOfRange_ThrowsAssertionInTaskContinuation()
{
var ex = await Assert.ThrowsAsync<JSException>(() => JavaScriptTestHelper.Reject("noodles"));
Assert.Contains("noodles", ex.Message);
Task<short> res = JavaScriptTestHelper.ReturnResolvedPromiseWithIntMaxValue_AsShortToBeOutOfRange();
JSException ex = await Assert.ThrowsAsync<JSException>(() => 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<Exception>(() => JavaScriptTestHelper.Reject(expected));
Assert.Equal(expected, actual);
Task<byte> res = JavaScriptTestHelper.ReturnResolvedPromiseWithIntMaxValue_AsByteToBeOutOfRange();
JSException ex = await Assert.ThrowsAsync<JSException>(() => res);
Assert.Equal("Error: Assert failed: Overflow: value 2147483647 is out of 0 255 range", ex.Message);
}

[Fact]
public async Task RejectNull()
{
var ex = await Assert.ThrowsAsync<JSException>(() => JavaScriptTestHelper.Reject(null));
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))]
public unsafe void OptimizedPaths()
public async Task TaskOfDateTimeOutOfRange_ThrowsAssertionInTaskContinuation()
{
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);
Task<DateTime> res = JavaScriptTestHelper.ReturnResolvedPromiseWithDateMaxValue();
JSException ex = await Assert.ThrowsAsync<JSException>(() => res);
Assert.Equal("Error: Assert failed: Overflow: value 8640000000000000 is out of -62135596800000 253402300799999 range", ex.Message);
}

#endregion

#region Get/Set Property

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,24 +432,61 @@ public static Exception EchoException([JSMarshalAs<JSType.Error>] Exception arg1
[JSImport("await1", "JavaScriptTestHelper")]
[return: JSMarshalAs<JSType.Promise<JSType.Any>>]
internal static partial Task<object> await1([JSMarshalAs<JSType.Promise<JSType.Any>>] Task<object> arg1);

[JSImport("await1", "JavaScriptTestHelper")]
[return: JSMarshalAs<JSType.Promise<JSType.Error>>]
internal static partial Task<Exception> await1_TaskOfException([JSMarshalAs<JSType.Promise<JSType.Error>>] Task<Exception> arg1);

[JSImport("invoke1", "JavaScriptTestHelper")]
[return: JSMarshalAs<JSType.Promise<JSType.Any>>]
internal static partial Task<object> invoke1_TaskOfObject([JSMarshalAs<JSType.Promise<JSType.Any>>] Task<object> value, [JSMarshalAs<JSType.String>] string name);

[JSImport("invoke1", "JavaScriptTestHelper")]
[return: JSMarshalAs<JSType.Promise<JSType.Number>>]
internal static partial Task<int> invoke1_TaskOfInt([JSMarshalAs<JSType.Promise<JSType.Number>>] Task<int> value, [JSMarshalAs<JSType.String>] string name);

[JSImport("invoke1", "JavaScriptTestHelper")]
[return: JSMarshalAs<JSType.Promise<JSType.BigInt>>]
internal static partial Task<long> invoke1_TaskOfBigLong([JSMarshalAs<JSType.Promise<JSType.BigInt>>] Task<long> value, [JSMarshalAs<JSType.String>] string name);

[JSImport("invoke1", "JavaScriptTestHelper")]
[return: JSMarshalAs<JSType.Promise<JSType.Number>>]
internal static partial Task<long> invoke1_TaskOfLong([JSMarshalAs<JSType.Promise<JSType.BigInt>>] Task<long> value, [JSMarshalAs<JSType.String>] string name);

[JSImport("invokeExportWithPromiseWithDateMaxValue", "JavaScriptTestHelper")]
[return: JSMarshalAs<JSType.Promise<JSType.Date>>]
internal static partial Task<DateTime> invokeExportWithTaskOfMaxJSDateTime([JSMarshalAs<JSType.String>] string name);

[JSImport("invokeExportWithDateMaxValue", "JavaScriptTestHelper")]
[return: JSMarshalAs<JSType.Date>]
internal static partial DateTime invokeExportWithMaxJSDateTime([JSMarshalAs<JSType.String>] string name);

[JSImport("invoke1", "JavaScriptTestHelper")]
[return: JSMarshalAs<JSType.Promise<JSType.String>>]
internal static partial Task<string> invoke1_TaskOfLong_ExceptionReturnTypeAssert([JSMarshalAs<JSType.Promise<JSType.BigInt>>] Task<long> value, [JSMarshalAs<JSType.String>] string name);

[JSImport("invoke1", "JavaScriptTestHelper")]
[return: JSMarshalAs<JSType.Promise<JSType.Number>>]
internal static partial Task<short> invoke1_TaskOfOutOfRangeShort([JSMarshalAs<JSType.Promise<JSType.Number>>] Task<int> value, [JSMarshalAs<JSType.String>] string name);

[JSImport("returnResolvedPromise", "JavaScriptTestHelper")]
internal static partial Task ReturnResolvedPromise();

[JSImport("invokeReturnCompletedTask", "JavaScriptTestHelper")]
internal static partial Task<string> InvokeReturnCompletedTask();

[JSImport("returnResolvedPromiseWithIntMaxValue", "JavaScriptTestHelper")]
[return: JSMarshalAs<JSType.Promise<JSType.Number>>]
internal static partial Task<short> ReturnResolvedPromiseWithIntMaxValue_AsShortToBeOutOfRange();

[JSImport("returnResolvedPromiseWithIntMaxValue", "JavaScriptTestHelper")]
[return: JSMarshalAs<JSType.Promise<JSType.Number>>]
internal static partial Task<byte> ReturnResolvedPromiseWithIntMaxValue_AsByteToBeOutOfRange();

[JSImport("returnResolvedPromiseWithDateMaxValue", "JavaScriptTestHelper")]
[return: JSMarshalAs<JSType.Promise<JSType.Date>>]
internal static partial Task<DateTime> ReturnResolvedPromiseWithDateMaxValue();

[JSExport]
internal static Task ReturnCompletedTask()
{
Expand All @@ -472,6 +509,30 @@ public static async Task<long> AwaitTaskOfInt64([JSMarshalAs<JSType.Promise<JSTy
return res;
}

[JSExport]
[return: JSMarshalAs<JSType.Promise<JSType.Number>>]
public static async Task<short> AwaitTaskOfShort([JSMarshalAs<JSType.Promise<JSType.Number>>] Task<short> arg1)
{
var res = await arg1;
return res;
}

[JSExport]
[return: JSMarshalAs<JSType.Promise<JSType.String>>]
public static async Task<string> AwaitTaskOfString([JSMarshalAs<JSType.Promise<JSType.String>>] Task<string> arg1)
{
var res = await arg1;
return res;
}

[JSExport]
[return: JSMarshalAs<JSType.Promise<JSType.Date>>]
public static async Task<DateTime> AwaitTaskOfDateTime([JSMarshalAs<JSType.Promise<JSType.Date>>] Task<DateTime> arg1)
{
var res = await arg1;
return res;
}

#endregion

#region Action + Func
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,34 @@ 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 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 async function invokeReturnCompletedTask() {
await dllExports.System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper.ReturnCompletedTask();
return "resolved";
Expand Down Expand Up @@ -490,4 +514,4 @@ export function isSetTimeoutHit() {

export function isPromiseThenHit() {
return promiseThenHit;
}
}
13 changes: 9 additions & 4 deletions src/mono/browser/runtime/managed-exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions src/mono/browser/runtime/marshal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads";

import { js_owned_gc_handle_symbol, teardown_managed_proxy } from "./gc-handles";
import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
import { getF32, getF64, getI16, getI32, getI64Big, getU16, getU32, getU8, setF32, setF64, setI16, setI32, setI64Big, setU16, setU32, setU8, localHeapViewF64, localHeapViewI32, localHeapViewU8, _zero_region, forceThreadMemoryViewRefresh, fixupPointer, setB8, getB8 } from "./memory";
import { getF32, getF64, getI16, getI32, getI64Big, getU16, getU32, getU8, setF32, setF64, setI16, setI32, setI64Big, setU16, setU32, setU8, localHeapViewF64, localHeapViewI32, localHeapViewU8, _zero_region, forceThreadMemoryViewRefresh, fixupPointer, setB8, getB8, setF64Date } from "./memory";
import { mono_wasm_new_external_root } from "./roots";
import { GCHandle, JSHandle, MonoObject, MonoString, GCHandleNull, JSMarshalerArguments, JSFunctionSignature, JSMarshalerType, JSMarshalerArgument, MarshalerToJs, MarshalerToCs, WasmRoot, MarshalerType, PThreadPtr, PThreadPtrNull, VoidPtrNull } from "./types/internal";
import { TypedArray, VoidPtr } from "./types/emscripten";
Expand Down Expand Up @@ -311,7 +311,7 @@ export function set_arg_date (arg: JSMarshalerArgument, value: Date): void {
mono_assert(arg, "Null arg");
// getTime() is always UTC
const unixTime = value.getTime();
setF64(<any>arg, unixTime);
setF64Date(<any>arg, unixTime);
}

export function set_arg_f64 (arg: JSMarshalerArgument, value: number): void {
Expand Down
5 changes: 5 additions & 0 deletions src/mono/browser/runtime/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@ export function setF64 (offset: MemOffset, value: number): void {
Module.HEAPF64[<any>offset >>> 3] = value;
}

export function setF64Date (offset: MemOffset, value: number): void {
assert_int_in_range(value, -0x3883122CD800, 0xE677D21FDBFF);
setF64(offset, value);
}

let warnDirtyBool = true;

export function getB32 (offset: MemOffset): boolean {
Expand Down
Loading