diff options
Diffstat (limited to 'test_ffi/tests')
-rw-r--r-- | test_ffi/tests/bench.js | 687 | ||||
-rw-r--r-- | test_ffi/tests/event_loop_integration.ts | 78 | ||||
-rw-r--r-- | test_ffi/tests/ffi_callback_errors.ts | 141 | ||||
-rw-r--r-- | test_ffi/tests/ffi_types.ts | 529 | ||||
-rw-r--r-- | test_ffi/tests/integration_tests.rs | 280 | ||||
-rw-r--r-- | test_ffi/tests/test.js | 802 | ||||
-rw-r--r-- | test_ffi/tests/thread_safe_test.js | 105 | ||||
-rw-r--r-- | test_ffi/tests/thread_safe_test_worker.js | 41 |
8 files changed, 0 insertions, 2663 deletions
diff --git a/test_ffi/tests/bench.js b/test_ffi/tests/bench.js deleted file mode 100644 index 49884d32e..000000000 --- a/test_ffi/tests/bench.js +++ /dev/null @@ -1,687 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -// deno-lint-ignore-file - -const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); -const [libPrefix, libSuffix] = { - darwin: ["lib", "dylib"], - linux: ["lib", "so"], - windows: ["", "dll"], -}[Deno.build.os]; -const libPath = `${targetDir}/${libPrefix}test_ffi.${libSuffix}`; - -const dylib = Deno.dlopen(libPath, { - "nop": { parameters: [], result: "void" }, - "add_u32": { parameters: ["u32", "u32"], result: "u32" }, - "add_u64": { parameters: ["u64", "u64"], result: "u64" }, - "ffi_string": { parameters: [], result: "pointer" }, - "hash": { parameters: ["buffer", "u32"], result: "u32" }, - "nop_bool": { parameters: ["bool"], result: "void" }, - "nop_u8": { parameters: ["u8"], result: "void" }, - "nop_i8": { parameters: ["i8"], result: "void" }, - "nop_u16": { parameters: ["u16"], result: "void" }, - "nop_i16": { parameters: ["i16"], result: "void" }, - "nop_u32": { parameters: ["u32"], result: "void" }, - "nop_i32": { parameters: ["i32"], result: "void" }, - "nop_u64": { parameters: ["u64"], result: "void" }, - "nop_i64": { parameters: ["i64"], result: "void" }, - "nop_usize": { parameters: ["usize"], result: "void" }, - "nop_isize": { parameters: ["isize"], result: "void" }, - "nop_f32": { parameters: ["f32"], result: "void" }, - "nop_f64": { parameters: ["f64"], result: "void" }, - "nop_buffer": { parameters: ["buffer"], result: "void" }, - "return_bool": { parameters: [], result: "bool" }, - "return_u8": { parameters: [], result: "u8" }, - "return_i8": { parameters: [], result: "i8" }, - "return_u16": { parameters: [], result: "u16" }, - "return_i16": { parameters: [], result: "i16" }, - "return_u32": { parameters: [], result: "u32" }, - "return_i32": { parameters: [], result: "i32" }, - "return_u64": { parameters: [], result: "u64" }, - "return_i64": { parameters: [], result: "i64" }, - "return_usize": { parameters: [], result: "usize" }, - "return_isize": { parameters: [], result: "isize" }, - "return_f32": { parameters: [], result: "f32" }, - "return_f64": { parameters: [], result: "f64" }, - "return_buffer": { parameters: [], result: "buffer" }, - // Nonblocking calls - "nop_nonblocking": { name: "nop", parameters: [], result: "void" }, - "nop_bool_nonblocking": { - name: "nop_bool", - parameters: ["bool"], - result: "void", - }, - "nop_u8_nonblocking": { name: "nop_u8", parameters: ["u8"], result: "void" }, - "nop_i8_nonblocking": { name: "nop_i8", parameters: ["i8"], result: "void" }, - "nop_u16_nonblocking": { - name: "nop_u16", - parameters: ["u16"], - result: "void", - }, - "nop_i16_nonblocking": { - name: "nop_i16", - parameters: ["i16"], - result: "void", - }, - "nop_u32_nonblocking": { - name: "nop_u32", - parameters: ["u32"], - result: "void", - }, - "nop_i32_nonblocking": { - name: "nop_i32", - parameters: ["i32"], - result: "void", - }, - "nop_u64_nonblocking": { - name: "nop_u64", - parameters: ["u64"], - result: "void", - }, - "nop_i64_nonblocking": { - name: "nop_i64", - parameters: ["i64"], - result: "void", - }, - "nop_usize_nonblocking": { - name: "nop_usize", - parameters: ["usize"], - result: "void", - }, - "nop_isize_nonblocking": { - name: "nop_isize", - parameters: ["isize"], - result: "void", - }, - "nop_f32_nonblocking": { - name: "nop_f32", - parameters: ["f32"], - result: "void", - }, - "nop_f64_nonblocking": { - name: "nop_f64", - parameters: ["f64"], - result: "void", - }, - "nop_buffer_nonblocking": { - name: "nop_buffer", - parameters: ["buffer"], - result: "void", - }, - "return_bool_nonblocking": { - name: "return_bool", - parameters: [], - result: "bool", - }, - "return_u8_nonblocking": { name: "return_u8", parameters: [], result: "u8" }, - "return_i8_nonblocking": { name: "return_i8", parameters: [], result: "i8" }, - "return_u16_nonblocking": { - name: "return_u16", - parameters: [], - result: "u16", - }, - "return_i16_nonblocking": { - name: "return_i16", - parameters: [], - result: "i16", - }, - "return_u32_nonblocking": { - name: "return_u32", - parameters: [], - result: "u32", - }, - "return_i32_nonblocking": { - name: "return_i32", - parameters: [], - result: "i32", - }, - "return_u64_nonblocking": { - name: "return_u64", - parameters: [], - result: "u64", - }, - "return_i64_nonblocking": { - name: "return_i64", - parameters: [], - result: "i64", - }, - "return_usize_nonblocking": { - name: "return_usize", - parameters: [], - result: "usize", - }, - "return_isize_nonblocking": { - name: "return_isize", - parameters: [], - result: "isize", - }, - "return_f32_nonblocking": { - name: "return_f32", - parameters: [], - result: "f32", - }, - "return_f64_nonblocking": { - name: "return_f64", - parameters: [], - result: "f64", - }, - "return_buffer_nonblocking": { - name: "return_buffer", - parameters: [], - result: "buffer", - }, - // Parameter checking - "nop_many_parameters": { - parameters: [ - "u8", - "i8", - "u16", - "i16", - "u32", - "i32", - "u64", - "i64", - "usize", - "isize", - "f32", - "f64", - "buffer", - "u8", - "i8", - "u16", - "i16", - "u32", - "i32", - "u64", - "i64", - "usize", - "isize", - "f32", - "f64", - "buffer", - ], - result: "void", - }, - "nop_many_parameters_nonblocking": { - name: "nop_many_parameters", - parameters: [ - "u8", - "i8", - "u16", - "i16", - "u32", - "i32", - "u64", - "i64", - "usize", - "isize", - "f32", - "f64", - "pointer", - "u8", - "i8", - "u16", - "i16", - "u32", - "i32", - "u64", - "i64", - "usize", - "isize", - "f32", - "f64", - "pointer", - ], - result: "void", - nonblocking: true, - }, -}); - -const { nop } = dylib.symbols; -Deno.bench("nop()", () => { - nop(); -}); - -const bytes = new Uint8Array(64); - -const { hash } = dylib.symbols; -Deno.bench("hash()", () => { - hash(bytes, bytes.byteLength); -}); - -const { ffi_string } = dylib.symbols; -Deno.bench( - "c string", - () => Deno.UnsafePointerView.getCString(ffi_string()), -); - -const { add_u32 } = dylib.symbols; -Deno.bench("add_u32()", () => { - add_u32(1, 2); -}); - -const { return_buffer } = dylib.symbols; -Deno.bench("return_buffer()", () => { - return_buffer(); -}); - -const { add_u64 } = dylib.symbols; -Deno.bench("add_u64()", () => { - add_u64(1, 2); -}); - -const { return_u64 } = dylib.symbols; -Deno.bench("return_u64()", () => { - return_u64(); -}); - -const { return_i64 } = dylib.symbols; -Deno.bench("return_i64()", () => { - return_i64(); -}); - -const { nop_bool } = dylib.symbols; -Deno.bench("nop_bool()", () => { - nop_bool(true); -}); - -const { nop_u8 } = dylib.symbols; -Deno.bench("nop_u8()", () => { - nop_u8(100); -}); - -const { nop_i8 } = dylib.symbols; -Deno.bench("nop_i8()", () => { - nop_i8(100); -}); - -const { nop_u16 } = dylib.symbols; -Deno.bench("nop_u16()", () => { - nop_u16(100); -}); - -const { nop_i16 } = dylib.symbols; -Deno.bench("nop_i16()", () => { - nop_i16(100); -}); - -const { nop_u32 } = dylib.symbols; -Deno.bench("nop_u32()", () => { - nop_u32(100); -}); - -const { nop_i32 } = dylib.symbols; -Deno.bench("nop_i32()", () => { - nop_i32(100); -}); - -const { nop_u64 } = dylib.symbols; -Deno.bench("nop_u64()", () => { - nop_u64(100); -}); - -const { nop_i64 } = dylib.symbols; -Deno.bench("nop_i64()", () => { - nop_i64(100); -}); - -const { nop_usize } = dylib.symbols; -Deno.bench("nop_usize() number", () => { - nop_usize(100); -}); - -Deno.bench("nop_usize() bigint", () => { - nop_usize(100n); -}); - -const { nop_isize } = dylib.symbols; -Deno.bench("nop_isize() number", () => { - nop_isize(100); -}); - -Deno.bench("nop_isize() bigint", () => { - nop_isize(100n); -}); - -const { nop_f32 } = dylib.symbols; -Deno.bench("nop_f32()", () => { - nop_f32(100.1); -}); - -const { nop_f64 } = dylib.symbols; -Deno.bench("nop_f64()", () => { - nop_f64(100.1); -}); - -const { nop_buffer } = dylib.symbols; -const buffer = new Uint8Array(8).fill(5); -Deno.bench("nop_buffer()", () => { - nop_buffer(buffer); -}); - -const { return_bool } = dylib.symbols; -Deno.bench("return_bool()", () => { - return_bool(); -}); - -const { return_u8 } = dylib.symbols; -Deno.bench("return_u8()", () => { - return_u8(); -}); - -const { return_i8 } = dylib.symbols; -Deno.bench("return_i8()", () => { - return_i8(); -}); - -const { return_u16 } = dylib.symbols; -Deno.bench("return_u16()", () => { - return_u16(); -}); - -const { return_i16 } = dylib.symbols; -Deno.bench("return_i16()", () => { - return_i16(); -}); - -const { return_u32 } = dylib.symbols; -Deno.bench("return_u32()", () => { - return_u32(); -}); - -const { return_i32 } = dylib.symbols; -Deno.bench("return_i32()", () => { - return_i32(); -}); - -const { return_usize } = dylib.symbols; -Deno.bench("return_usize()", () => { - return_usize(); -}); - -const { return_isize } = dylib.symbols; -Deno.bench("return_isize()", () => { - return_isize(); -}); - -const { return_f32 } = dylib.symbols; -Deno.bench("return_f32()", () => { - return_f32(); -}); - -const { return_f64 } = dylib.symbols; -Deno.bench("return_f64()", () => { - return_f64(); -}); - -// Nonblocking calls - -const { nop_nonblocking } = dylib.symbols; -Deno.bench("nop_nonblocking()", async () => { - await nop_nonblocking(); -}); - -const { nop_bool_nonblocking } = dylib.symbols; -Deno.bench("nop_bool_nonblocking()", async () => { - await nop_bool_nonblocking(true); -}); - -const { nop_u8_nonblocking } = dylib.symbols; -Deno.bench("nop_u8_nonblocking()", async () => { - await nop_u8_nonblocking(100); -}); - -const { nop_i8_nonblocking } = dylib.symbols; -Deno.bench("nop_i8_nonblocking()", async () => { - await nop_i8_nonblocking(100); -}); - -const { nop_u16_nonblocking } = dylib.symbols; -Deno.bench("nop_u16_nonblocking()", async () => { - await nop_u16_nonblocking(100); -}); - -const { nop_i16_nonblocking } = dylib.symbols; -Deno.bench("nop_i16_nonblocking()", async () => { - await nop_i16_nonblocking(100); -}); - -const { nop_u32_nonblocking } = dylib.symbols; -Deno.bench("nop_u32_nonblocking()", async () => { - await nop_u32_nonblocking(100); -}); - -const { nop_i32_nonblocking } = dylib.symbols; - -Deno.bench("nop_i32_nonblocking()", async () => { - await nop_i32_nonblocking(100); -}); - -const { nop_u64_nonblocking } = dylib.symbols; -Deno.bench("nop_u64_nonblocking()", async () => { - await nop_u64_nonblocking(100); -}); - -const { nop_i64_nonblocking } = dylib.symbols; -Deno.bench("nop_i64_nonblocking()", async () => { - await nop_i64_nonblocking(100); -}); - -const { nop_usize_nonblocking } = dylib.symbols; -Deno.bench("nop_usize_nonblocking()", async () => { - await nop_usize_nonblocking(100); -}); - -const { nop_isize_nonblocking } = dylib.symbols; -Deno.bench("nop_isize_nonblocking()", async () => { - await nop_isize_nonblocking(100); -}); - -const { nop_f32_nonblocking } = dylib.symbols; -Deno.bench("nop_f32_nonblocking()", async () => { - await nop_f32_nonblocking(100); -}); - -const { nop_f64_nonblocking } = dylib.symbols; -Deno.bench("nop_f64_nonblocking()", async () => { - await nop_f64_nonblocking(100); -}); - -const { nop_buffer_nonblocking } = dylib.symbols; -Deno.bench("nop_buffer_nonblocking()", async () => { - await nop_buffer_nonblocking(buffer); -}); - -const { return_bool_nonblocking } = dylib.symbols; -Deno.bench("return_bool_nonblocking()", async () => { - await return_bool_nonblocking(); -}); - -const { return_u8_nonblocking } = dylib.symbols; -Deno.bench("return_u8_nonblocking()", async () => { - await return_u8_nonblocking(); -}); - -const { return_i8_nonblocking } = dylib.symbols; -Deno.bench("return_i8_nonblocking()", async () => { - await return_i8_nonblocking(); -}); - -const { return_u16_nonblocking } = dylib.symbols; -Deno.bench("return_u16_nonblocking()", async () => { - await return_u16_nonblocking(); -}); - -const { return_i16_nonblocking } = dylib.symbols; -Deno.bench("return_i16_nonblocking()", async () => { - await return_i16_nonblocking(); -}); - -const { return_u32_nonblocking } = dylib.symbols; -Deno.bench("return_u32_nonblocking()", async () => { - await return_u32_nonblocking(); -}); - -const { return_i32_nonblocking } = dylib.symbols; -Deno.bench("return_i32_nonblocking()", async () => { - await return_i32_nonblocking(); -}); - -const { return_u64_nonblocking } = dylib.symbols; -Deno.bench("return_u64_nonblocking()", async () => { - await return_u64_nonblocking(); -}); - -const { return_i64_nonblocking } = dylib.symbols; -Deno.bench("return_i64_nonblocking()", async () => { - await return_i64_nonblocking(); -}); - -const { return_usize_nonblocking } = dylib.symbols; -Deno.bench("return_usize_nonblocking()", async () => { - await return_usize_nonblocking(); -}); - -const { return_isize_nonblocking } = dylib.symbols; -Deno.bench("return_isize_nonblocking()", async () => { - await return_isize_nonblocking(); -}); - -const { return_f32_nonblocking } = dylib.symbols; -Deno.bench("return_f32_nonblocking()", async () => { - await return_f32_nonblocking(); -}); - -const { return_f64_nonblocking } = dylib.symbols; -Deno.bench("return_f64_nonblocking()", async () => { - await return_f64_nonblocking(); -}); - -const { return_buffer_nonblocking } = dylib.symbols; -Deno.bench("return_buffer_nonblocking()", async () => { - await return_buffer_nonblocking(); -}); - -const { nop_many_parameters } = dylib.symbols; -const buffer2 = new Uint8Array(8).fill(25); -Deno.bench("nop_many_parameters()", () => { - nop_many_parameters( - 135, - 47, - 356, - -236, - 7457, - -1356, - 16471468n, - -1334748136n, - 132658769535n, - -42745856824n, - 13567.26437, - 7.686234e-3, - buffer, - 64, - -42, - 83, - -136, - 3657, - -2376, - 3277918n, - -474628146n, - 344657895n, - -2436732n, - 135.26437e3, - 264.3576468623546834, - buffer2, - ); -}); - -const { nop_many_parameters_nonblocking } = dylib.symbols; -Deno.bench("nop_many_parameters_nonblocking()", () => { - nop_many_parameters_nonblocking( - 135, - 47, - 356, - -236, - 7457, - -1356, - 16471468n, - -1334748136n, - 132658769535n, - -42745856824n, - 13567.26437, - 7.686234e-3, - buffer, - 64, - -42, - 83, - -136, - 3657, - -2376, - 3277918n, - -474628146n, - 344657895n, - -2436732n, - 135.26437e3, - 264.3576468623546834, - buffer2, - ); -}); - -Deno.bench("Deno.UnsafePointer.of", () => { - Deno.UnsafePointer.of(buffer); -}); - -const cstringBuffer = new TextEncoder().encode("Best believe it!\0"); -const cstringPointerView = new Deno.UnsafePointerView( - Deno.UnsafePointer.of(cstringBuffer), -); -Deno.bench("Deno.UnsafePointerView#getCString", () => { - cstringPointerView.getCString(); -}); - -const bufferPointerView = new Deno.UnsafePointerView( - Deno.UnsafePointer.of(buffer), -); - -Deno.bench("Deno.UnsafePointerView#getBool", () => { - bufferPointerView.getBool(); -}); - -Deno.bench("Deno.UnsafePointerView#getUint8", () => { - bufferPointerView.getUint8(); -}); - -Deno.bench("Deno.UnsafePointerView#getInt8", () => { - bufferPointerView.getInt8(); -}); - -Deno.bench("Deno.UnsafePointerView#getUint16", () => { - bufferPointerView.getUint16(); -}); - -Deno.bench("Deno.UnsafePointerView#getInt16", () => { - bufferPointerView.getInt16(); -}); - -Deno.bench("Deno.UnsafePointerView#getUint32", () => { - bufferPointerView.getUint32(); -}); - -Deno.bench("Deno.UnsafePointerView#getInt32", () => { - bufferPointerView.getInt32(); -}); - -Deno.bench("Deno.UnsafePointerView#getBigUint64", () => { - bufferPointerView.getBigUint64(); -}); - -Deno.bench("Deno.UnsafePointerView#getBigInt64", () => { - bufferPointerView.getBigInt64(); -}); - -Deno.bench("Deno.UnsafePointerView#getFloat32", () => { - bufferPointerView.getFloat32(); -}); - -Deno.bench("Deno.UnsafePointerView#getFloat64", () => { - bufferPointerView.getFloat64(); -}); diff --git a/test_ffi/tests/event_loop_integration.ts b/test_ffi/tests/event_loop_integration.ts deleted file mode 100644 index d9ada6027..000000000 --- a/test_ffi/tests/event_loop_integration.ts +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); -const [libPrefix, libSuffix] = { - darwin: ["lib", "dylib"], - linux: ["lib", "so"], - windows: ["", "dll"], -}[Deno.build.os]; -const libPath = `${targetDir}/${libPrefix}test_ffi.${libSuffix}`; - -const dylib = Deno.dlopen( - libPath, - { - store_function: { - parameters: ["function"], - result: "void", - }, - call_stored_function: { - parameters: [], - result: "void", - }, - call_stored_function_thread_safe_and_log: { - parameters: [], - result: "void", - }, - } as const, -); - -let retry = false; -const tripleLogCallback = () => { - console.log("Sync"); - queueMicrotask(() => { - console.log("Async"); - callback.unref(); - }); - setTimeout(() => { - console.log("Timeout"); - callback.unref(); - - if (retry) { - // Re-ref and retry the call to make sure re-refing works. - console.log("RETRY THREAD SAFE"); - retry = false; - callback.ref(); - dylib.symbols.call_stored_function_thread_safe_and_log(); - } - }, 100); -}; - -const callback = Deno.UnsafeCallback.threadSafe( - { - parameters: [], - result: "void", - } as const, - tripleLogCallback, -); - -// Store function -dylib.symbols.store_function(callback.pointer); - -// Synchronous callback logging -console.log("SYNCHRONOUS"); -// This function only calls the callback, and does not log. -dylib.symbols.call_stored_function(); -console.log("STORED_FUNCTION called"); - -// Wait to make sure synch logging and async logging -await new Promise((res) => setTimeout(res, 200)); - -// Ref once to make sure both `queueMicrotask()` and `setTimeout()` -// must resolve and unref before isolate exists. -// One ref'ing has been done by `threadSafe` constructor. -callback.ref(); - -console.log("THREAD SAFE"); -retry = true; -// This function calls the callback and logs 'STORED_FUNCTION called' -dylib.symbols.call_stored_function_thread_safe_and_log(); diff --git a/test_ffi/tests/ffi_callback_errors.ts b/test_ffi/tests/ffi_callback_errors.ts deleted file mode 100644 index dbd60636c..000000000 --- a/test_ffi/tests/ffi_callback_errors.ts +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); -const [libPrefix, libSuffix] = { - darwin: ["lib", "dylib"], - linux: ["lib", "so"], - windows: ["", "dll"], -}[Deno.build.os]; -const libPath = `${targetDir}/${libPrefix}test_ffi.${libSuffix}`; - -const dylib = Deno.dlopen( - libPath, - { - store_function_2: { - parameters: ["function"], - result: "void", - }, - call_stored_function_2: { - parameters: ["u8"], - result: "void", - }, - call_stored_function_2_from_other_thread: { - name: "call_stored_function_2", - parameters: ["u8"], - result: "void", - nonblocking: true, - }, - call_stored_function_2_thread_safe: { - parameters: ["u8"], - result: "void", - }, - } as const, -); - -globalThis.addEventListener("error", (data) => { - console.log("Unhandled error"); - data.preventDefault(); -}); -globalThis.onerror = (data) => { - console.log("Unhandled error"); - if (typeof data !== "string") { - data.preventDefault(); - } -}; - -globalThis.addEventListener("unhandledrejection", (data) => { - console.log("Unhandled rejection"); - data.preventDefault(); -}); - -const timer = setTimeout(() => { - console.error( - "Test failed, final callback did not get picked up by Deno event loop", - ); - Deno.exit(-1); -}, 5_000); - -Deno.unrefTimer(timer); - -enum CallCase { - SyncSelf, - SyncFfi, - AsyncSelf, - AsyncSyncFfi, - AsyncFfi, -} -type U8CallCase = Deno.NativeU8Enum<CallCase>; - -const throwCb = (c: CallCase): number => { - console.log("CallCase:", CallCase[c]); - if (c === CallCase.AsyncFfi) { - cb.unref(); - } - throw new Error("Error"); -}; - -const THROW_CB_DEFINITION = { - parameters: ["u8" as U8CallCase], - result: "u8", -} as const; - -const cb = new Deno.UnsafeCallback(THROW_CB_DEFINITION, throwCb); - -try { - const fnPointer = new Deno.UnsafeFnPointer(cb.pointer, THROW_CB_DEFINITION); - - fnPointer.call(CallCase.SyncSelf); -} catch (_err) { - console.log( - "Throwing errors from an UnsafeCallback called from a synchronous UnsafeFnPointer works. Terribly excellent.", - ); -} - -dylib.symbols.store_function_2(cb.pointer); -try { - dylib.symbols.call_stored_function_2(CallCase.SyncFfi); -} catch (_err) { - console.log( - "Throwing errors from an UnsafeCallback called from a synchronous FFI symbol works. Terribly excellent.", - ); -} - -try { - const fnPointer = new Deno.UnsafeFnPointer(cb.pointer, { - ...THROW_CB_DEFINITION, - nonblocking: true, - }); - await fnPointer.call(CallCase.AsyncSelf); -} catch (err) { - throw new Error( - "Nonblocking UnsafeFnPointer should not be threading through a JS error thrown on the other side of the call", - { - cause: err, - }, - ); -} - -try { - await dylib.symbols.call_stored_function_2_from_other_thread( - CallCase.AsyncSyncFfi, - ); -} catch (err) { - throw new Error( - "Nonblocking symbol call should not be threading through a JS error thrown on the other side of the call", - { - cause: err, - }, - ); -} -try { - // Ref the callback to make sure we do not exit before the call is done. - cb.ref(); - dylib.symbols.call_stored_function_2_thread_safe(CallCase.AsyncFfi); -} catch (err) { - throw new Error( - "Blocking symbol call should not be travelling 1.5 seconds forward in time to figure out that it call will trigger a JS error to be thrown", - { - cause: err, - }, - ); -} diff --git a/test_ffi/tests/ffi_types.ts b/test_ffi/tests/ffi_types.ts deleted file mode 100644 index 596662873..000000000 --- a/test_ffi/tests/ffi_types.ts +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -// deno-lint-ignore-file -// Only for testing types. Invoke with `deno cache` - -const remote = Deno.dlopen( - "dummy_lib.so", - { - method1: { parameters: ["usize", "bool"], result: "void", callback: true }, - method2: { parameters: [], result: "void" }, - method3: { parameters: ["usize"], result: "void" }, - method4: { parameters: ["isize"], result: "void" }, - method5: { parameters: ["u8"], result: "void" }, - method6: { parameters: ["u16"], result: "void" }, - method7: { parameters: ["u32"], result: "void" }, - method8: { parameters: ["u64"], result: "void" }, - method9: { parameters: ["i8"], result: "void" }, - method10: { parameters: ["i16"], result: "void" }, - method11: { parameters: ["i32"], result: "void" }, - method12: { parameters: ["i64"], result: "void" }, - method13: { parameters: ["f32"], result: "void" }, - method14: { parameters: ["f64"], result: "void" }, - method15: { parameters: ["pointer"], result: "void" }, - method16: { parameters: [], result: "usize" }, - method17: { parameters: [], result: "usize", nonblocking: true }, - method18: { parameters: [], result: "pointer" }, - method19: { parameters: [], result: "pointer", nonblocking: true }, - method20: { - parameters: ["pointer"], - result: "void", - }, - method21: { - parameters: [ - "pointer", - ], - result: "void", - }, - method22: { - parameters: ["pointer"], - result: "void", - }, - method23: { - parameters: ["buffer"], - result: "void", - }, - method24: { - parameters: ["bool"], - result: "bool", - }, - method25: { - parameters: [], - result: "void", - optional: true, - }, - static1: { type: "usize" }, - static2: { type: "pointer" }, - static3: { type: "usize" }, - static4: { type: "isize" }, - static5: { type: "u8" }, - static6: { type: "u16" }, - static7: { type: "u32" }, - static8: { type: "u64" }, - static9: { type: "i8" }, - static10: { type: "i16" }, - static11: { type: "i32" }, - static12: { type: "i64" }, - static13: { type: "f32" }, - static14: { type: "f64" }, - static15: { type: "bool" }, - static16: { - type: "bool", - optional: true, - }, - }, -); - -Deno.dlopen( - "dummy_lib_2.so", - { - wrong_method1: { - parameters: [], - result: "function", - }, - }, -); - -// @ts-expect-error: Invalid argument -remote.symbols.method1(0); -// @ts-expect-error: Invalid argument -remote.symbols.method1(0, 0); -// @ts-expect-error: Invalid argument -remote.symbols.method1(true, true); -// @ts-expect-error: Invalid return type -<number> remote.symbols.method1(0, true); -<void> remote.symbols.method1(0n, true); - -// @ts-expect-error: Expected 0 arguments, but got 1. -remote.symbols.method2(null); -remote.symbols.method2(); - -// @ts-expect-error: Invalid argument -remote.symbols.method3(null); -remote.symbols.method3(0n); - -// @ts-expect-error: Invalid argument -remote.symbols.method4(null); -remote.symbols.method4(0n); - -// @ts-expect-error: Invalid argument -remote.symbols.method5(null); -remote.symbols.method5(0); - -// @ts-expect-error: Invalid argument -remote.symbols.method6(null); -remote.symbols.method6(0); - -// @ts-expect-error: Invalid argument -remote.symbols.method7(null); -remote.symbols.method7(0); - -// @ts-expect-error: Invalid argument -remote.symbols.method8(null); -remote.symbols.method8(0n); - -// @ts-expect-error: Invalid argument -remote.symbols.method9(null); -remote.symbols.method9(0); - -// @ts-expect-error: Invalid argument -remote.symbols.method10(null); -remote.symbols.method10(0); - -// @ts-expect-error: Invalid argument -remote.symbols.method11(null); -remote.symbols.method11(0); - -// @ts-expect-error: Invalid argument -remote.symbols.method12(null); -remote.symbols.method12(0n); - -// @ts-expect-error: Invalid argument -remote.symbols.method13(null); -remote.symbols.method13(0); - -// @ts-expect-error: Invalid argument -remote.symbols.method14(null); -remote.symbols.method14(0); - -// @ts-expect-error: Invalid argument -remote.symbols.method15("foo"); -// @ts-expect-error: Invalid argument -remote.symbols.method15(new Uint16Array(1)); -remote.symbols.method15(null); -remote.symbols.method15({} as Deno.PointerValue); - -const result = remote.symbols.method16(); -// @ts-expect-error: Invalid argument -let r_0: string = result; -let r_1: number | bigint = result; - -const result2 = remote.symbols.method17(); -// @ts-expect-error: Invalid argument -result2.then((_0: string) => {}); -result2.then((_1: number | bigint) => {}); - -const result3 = remote.symbols.method18(); -// @ts-expect-error: Invalid argument -let r3_0: Deno.BufferSource = result3; -let r3_1: null | Deno.UnsafePointer = result3; - -const result4 = remote.symbols.method19(); -// @ts-expect-error: Invalid argument -result4.then((_0: Deno.BufferSource) => {}); -result4.then((_1: null | Deno.UnsafePointer) => {}); - -const fnptr = new Deno.UnsafeFnPointer( - {} as Deno.PointerObject, - { - parameters: ["u32", "pointer"], - result: "void", - }, -); -// @ts-expect-error: Invalid argument -fnptr.call(null, null); -fnptr.call(0, null); - -const unsafe_callback_wrong1 = new Deno.UnsafeCallback( - { - parameters: ["i8"], - result: "void", - }, - // @ts-expect-error: i8 is not a pointer - (_: bigint) => {}, -); -const unsafe_callback_wrong2 = new Deno.UnsafeCallback( - { - parameters: ["pointer"], - result: "u64", - }, - // @ts-expect-error: must return a number or bigint - (_: Deno.UnsafePointer) => {}, -); -const unsafe_callback_wrong3 = new Deno.UnsafeCallback( - { - parameters: [], - result: "void", - }, - // @ts-expect-error: no parameters - (_: Deno.UnsafePointer) => {}, -); -const unsafe_callback_wrong4 = new Deno.UnsafeCallback( - { - parameters: ["u64"], - result: "void", - }, - // @ts-expect-error: Callback's 64bit parameters are either number or bigint - (_: number) => {}, -); -const unsafe_callback_right1 = new Deno.UnsafeCallback( - { - parameters: ["u8", "u32", "pointer"], - result: "void", - }, - (_1: number, _2: number, _3: null | Deno.PointerValue) => {}, -); -const unsafe_callback_right2 = new Deno.UnsafeCallback( - { - parameters: [], - result: "u8", - }, - () => 3, -); -const unsafe_callback_right3 = new Deno.UnsafeCallback( - { - parameters: [], - result: "function", - }, - // Callbacks can return other callbacks' pointers, if really wanted. - () => unsafe_callback_right2.pointer, -); -const unsafe_callback_right4 = new Deno.UnsafeCallback( - { - parameters: ["u8", "u32", "pointer"], - result: "u8", - }, - (_1: number, _2: number, _3: null | Deno.PointerValue) => 3, -); -const unsafe_callback_right5 = new Deno.UnsafeCallback( - { - parameters: ["u8", "i32", "pointer"], - result: "void", - }, - (_1: number, _2: number, _3: null | Deno.PointerValue) => {}, -); - -// @ts-expect-error: Must pass callback -remote.symbols.method20(); -// nullptr is okay -remote.symbols.method20(null); -// @ts-expect-error: Callback cannot be passed directly -remote.symbols.method20(unsafe_callback_right2); -remote.symbols.method20(unsafe_callback_right1.pointer); - -remote.symbols.method23(new Uint8Array(1)); -remote.symbols.method23(new Uint32Array(1)); -remote.symbols.method23(new Uint8Array(1)); - -// @ts-expect-error: Cannot pass pointer values as buffer. -remote.symbols.method23({}); -// @ts-expect-error: Cannot pass pointer values as buffer. -remote.symbols.method23({}); -remote.symbols.method23(null); - -// @ts-expect-error: Cannot pass number as bool. -remote.symbols.method24(0); -// @ts-expect-error: Cannot pass number as bool. -remote.symbols.method24(1); -// @ts-expect-error: Cannot pass null as bool. -remote.symbols.method24(null); -remote.symbols.method24(true); -remote.symbols.method24(false); -// @ts-expect-error: Cannot assert return type as a number. -<number> remote.symbols.method24(true); -// @ts-expect-error: Cannot assert return type truthiness. -let r24_0: true = remote.symbols.method24(true); -// @ts-expect-error: Cannot assert return type as a number. -let r42_1: number = remote.symbols.method24(true); -<boolean> remote.symbols.method24(Math.random() > 0.5); - -// @ts-expect-error: Optional symbol; can be null. -remote.symbols.method25(); - -// @ts-expect-error: Invalid member type -const static1_wrong: null = remote.symbols.static1; -const static1_right: number | bigint = remote.symbols.static1; -// @ts-expect-error: Invalid member type -const static2_wrong: null = remote.symbols.static2; -const static2_right: null | Deno.UnsafePointer = remote.symbols.static2; -// @ts-expect-error: Invalid member type -const static3_wrong: null = remote.symbols.static3; -const static3_right: number | bigint = remote.symbols.static3; -// @ts-expect-error: Invalid member type -const static4_wrong: null = remote.symbols.static4; -const static4_right: number | bigint = remote.symbols.static4; -// @ts-expect-error: Invalid member type -const static5_wrong: null = remote.symbols.static5; -const static5_right: number = remote.symbols.static5; -// @ts-expect-error: Invalid member type -const static6_wrong: null = remote.symbols.static6; -const static6_right: number = remote.symbols.static6; -// @ts-expect-error: Invalid member type -const static7_wrong: null = remote.symbols.static7; -const static7_right: number = remote.symbols.static7; -// @ts-expect-error: Invalid member type -const static8_wrong: null = remote.symbols.static8; -const static8_right: number | bigint = remote.symbols.static8; -// @ts-expect-error: Invalid member type -const static9_wrong: null = remote.symbols.static9; -const static9_right: number = remote.symbols.static9; -// @ts-expect-error: Invalid member type -const static10_wrong: null = remote.symbols.static10; -const static10_right: number = remote.symbols.static10; -// @ts-expect-error: Invalid member type -const static11_wrong: null = remote.symbols.static11; -const static11_right: number = remote.symbols.static11; -// @ts-expect-error: Invalid member type -const static12_wrong: null = remote.symbols.static12; -const static12_right: number | bigint = remote.symbols.static12; -// @ts-expect-error: Invalid member type -const static13_wrong: null = remote.symbols.static13; -const static13_right: number = remote.symbols.static13; -// @ts-expect-error: Invalid member type -const static14_wrong: null = remote.symbols.static14; -const static14_right: number = remote.symbols.static14; -// @ts-expect-error: Invalid member type -const static15_wrong: number = remote.symbols.static15; -const static15_right: boolean = remote.symbols.static15; -// @ts-expect-error: Invalid member type -const static16_wrong: boolean = remote.symbols.static16; -const static16_right: boolean | null = remote.symbols.static16; - -// Adapted from https://stackoverflow.com/a/53808212/10873797 -type Equal<T, U> = (<G>() => G extends T ? 1 : 2) extends - (<G>() => G extends U ? 1 : 2) ? true - : false; - -type AssertEqual< - Expected extends $, - Got extends $$, - $ = [Equal<Got, Expected>] extends [true] ? Expected - : ([Expected] extends [Got] ? never : Got), - $$ = [Equal<Expected, Got>] extends [true] ? Got - : ([Got] extends [Expected] ? never : Got), -> = never; - -type AssertNotEqual< - Expected extends $, - Got, - $ = [Equal<Expected, Got>] extends [true] ? never : Expected, -> = never; - -const enum FooEnum { - Foo, - Bar, -} -const foo = "u8" as Deno.NativeU8Enum<FooEnum>; - -declare const brand: unique symbol; -class MyPointerClass {} -type MyPointer = Deno.PointerObject & { [brand]: MyPointerClass }; -const myPointer = "pointer" as Deno.NativeTypedPointer<MyPointer>; -type MyFunctionDefinition = Deno.UnsafeCallbackDefinition< - [typeof foo, "u32"], - typeof myPointer ->; -const myFunction = "function" as Deno.NativeTypedFunction< - MyFunctionDefinition ->; - -type __Tests__ = [ - empty: AssertEqual< - { symbols: Record<never, never>; close(): void }, - Deno.DynamicLibrary<Record<never, never>> - >, - basic: AssertEqual< - { symbols: { add: (n1: number, n2: number) => number }; close(): void }, - Deno.DynamicLibrary<{ add: { parameters: ["i32", "u8"]; result: "i32" } }> - >, - higher_order_params: AssertEqual< - { - symbols: { - pushBuf: ( - buf: BufferSource | null, - ptr: Deno.PointerValue | null, - func: Deno.PointerValue | null, - ) => void; - }; - close(): void; - }, - Deno.DynamicLibrary< - { - pushBuf: { - parameters: ["buffer", "pointer", "function"]; - result: "void"; - }; - } - > - >, - higher_order_returns: AssertEqual< - { - symbols: { - pushBuf: ( - buf: BufferSource | null, - ptr: Deno.PointerValue, - func: Deno.PointerValue, - ) => Deno.PointerValue; - }; - close(): void; - }, - Deno.DynamicLibrary< - { - pushBuf: { - parameters: ["buffer", "pointer", "function"]; - result: "pointer"; - }; - } - > - >, - non_exact_params: AssertEqual< - { - symbols: { - foo: ( - ...args: (number | Deno.PointerValue | null)[] - ) => number | bigint; - }; - close(): void; - }, - Deno.DynamicLibrary< - { foo: { parameters: ("i32" | "pointer")[]; result: "u64" } } - > - >, - non_exact_params_empty: AssertEqual< - { - symbols: { - foo: () => number; - }; - close(): void; - }, - Deno.DynamicLibrary< - { foo: { parameters: []; result: "i32" } } - > - >, - non_exact_params_empty: AssertNotEqual< - { - symbols: { - foo: (a: number) => number; - }; - close(): void; - }, - Deno.DynamicLibrary< - { foo: { parameters: []; result: "i32" } } - > - >, - enum_param: AssertEqual< - { - symbols: { - foo: (arg: FooEnum) => void; - }; - close(): void; - }, - Deno.DynamicLibrary< - { foo: { parameters: [typeof foo]; result: "void" } } - > - >, - enum_return: AssertEqual< - { - symbols: { - foo: () => FooEnum; - }; - close(): void; - }, - Deno.DynamicLibrary< - { foo: { parameters: []; result: typeof foo } } - > - >, - typed_pointer_param: AssertEqual< - { - symbols: { - foo: (arg: MyPointer | null) => void; - }; - close(): void; - }, - Deno.DynamicLibrary< - { foo: { parameters: [typeof myPointer]; result: "void" } } - > - >, - typed_pointer_return: AssertEqual< - { - symbols: { - foo: () => MyPointer | null; - }; - close(): void; - }, - Deno.DynamicLibrary< - { foo: { parameters: []; result: typeof myPointer } } - > - >, - typed_function_param: AssertEqual< - { - symbols: { - foo: (arg: Deno.PointerObject<MyFunctionDefinition> | null) => void; - }; - close(): void; - }, - Deno.DynamicLibrary< - { foo: { parameters: [typeof myFunction]; result: "void" } } - > - >, - typed_function_return: AssertEqual< - { - symbols: { - foo: () => Deno.PointerObject<MyFunctionDefinition> | null; - }; - close(): void; - }, - Deno.DynamicLibrary< - { foo: { parameters: []; result: typeof myFunction } } - > - >, -]; diff --git a/test_ffi/tests/integration_tests.rs b/test_ffi/tests/integration_tests.rs deleted file mode 100644 index 642fbed03..000000000 --- a/test_ffi/tests/integration_tests.rs +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use pretty_assertions::assert_eq; -use std::process::Command; -use test_util::deno_cmd; - -#[cfg(debug_assertions)] -const BUILD_VARIANT: &str = "debug"; - -#[cfg(not(debug_assertions))] -const BUILD_VARIANT: &str = "release"; - -fn build() { - let mut build_plugin_base = Command::new("cargo"); - let mut build_plugin = - build_plugin_base.arg("build").arg("-p").arg("test_ffi"); - if BUILD_VARIANT == "release" { - build_plugin = build_plugin.arg("--release"); - } - let build_plugin_output = build_plugin.output().unwrap(); - assert!(build_plugin_output.status.success()); -} - -#[test] -fn basic() { - build(); - - let output = deno_cmd() - .arg("run") - .arg("--allow-ffi") - .arg("--allow-read") - .arg("--unstable-ffi") - .arg("--quiet") - .arg(r#"--v8-flags=--allow-natives-syntax"#) - .arg("tests/test.js") - .env("NO_COLOR", "1") - .output() - .unwrap(); - let stdout = std::str::from_utf8(&output.stdout).unwrap(); - let stderr = std::str::from_utf8(&output.stderr).unwrap(); - if !output.status.success() { - println!("stdout {stdout}"); - println!("stderr {stderr}"); - } - println!("{:?}", output.status); - assert!(output.status.success()); - let expected = "\ - something\n\ - [1, 2, 3, 4, 5, 6, 7, 8]\n\ - [4, 5, 6]\n\ - [1, 2, 3, 4, 5, 6, 7, 8] [9, 10]\n\ - [1, 2, 3, 4, 5, 6, 7, 8]\n\ - [ 1, 2, 3, 4, 5, 6 ]\n\ - [ 4, 5, 6 ]\n\ - [ 4, 5, 6 ]\n\ - Hello from pointer!\n\ - pointer!\n\ - false false\n\ - true true\n\ - false false\n\ - true true\n\ - false false\n\ - 579\n\ - true\n\ - 579\n\ - 579\n\ - 5\n\ - 5\n\ - 579\n\ - 8589934590\n\ - -8589934590\n\ - 8589934590\n\ - -8589934590\n\ - 9007199254740992n\n\ - 9007199254740992n\n\ - -9007199254740992n\n\ - 9007199254740992n\n\ - 9007199254740992n\n\ - -9007199254740992n\n\ - 579.9119873046875\n\ - 579.912\n\ - true\n\ - false\n\ - 579.9119873046875\n\ - 579.9119873046875\n\ - 579.912\n\ - 579.912\n\ - 579\n\ - 8589934590\n\ - -8589934590\n\ - 8589934590\n\ - -8589934590\n\ - 9007199254740992n\n\ - 9007199254740992n\n\ - -9007199254740992n\n\ - 9007199254740992n\n\ - 9007199254740992n\n\ - -9007199254740992n\n\ - 579.9119873046875\n\ - 579.912\n\ - Before\n\ - After\n\ - logCallback\n\ - 1 -1 2 -2 3 -3 4 -4 0.5 -0.5 1 2 3 4 5 6 7 8\n\ - u8: 8\n\ - buf: [1, 2, 3, 4, 5, 6, 7, 8]\n\ - logCallback\n\ - 30\n\ - 255 65535 4294967295 4294967296 123.456 789.876 -1 -2 -3 -4 -1000 1000 12345.67891 12345.679 12345.67891 12345.679 12345.67891 12345.679 12345.67891\n\ - 255 65535 4294967295 4294967296 123.456 789.876 -1 -2 -3 -4 -1000 1000 12345.67891 12345.679 12345.67891 12345.679 12345.67891 12345.679 12345.67891\n\ - 0\n\ - 0\n\ - 0\n\ - 0\n\ - 78\n\ - 78\n\ - STORED_FUNCTION cleared\n\ - STORED_FUNCTION_2 cleared\n\ - logCallback\n\ - u8: 8\n\ - Rect { x: 10.0, y: 20.0, w: 100.0, h: 200.0 }\n\ - Rect { x: 10.0, y: 20.0, w: 100.0, h: 200.0 }\n\ - Rect { x: 20.0, y: 20.0, w: 100.0, h: 200.0 }\n\ - Mixed { u8: 3, f32: 12.515, rect: Rect { x: 10.0, y: 20.0, w: 100.0, h: 200.0 }, usize: 12456789, array: [8, 32] }\n\ - 2264956937\n\ - 2264956937\n\ - Correct number of resources\n"; - assert_eq!(stdout, expected); - assert_eq!(stderr, ""); -} - -#[test] -fn symbol_types() { - build(); - - let output = deno_cmd() - .arg("check") - .arg("--unstable-ffi") - .arg("--quiet") - .arg("tests/ffi_types.ts") - .env("NO_COLOR", "1") - .output() - .unwrap(); - let stdout = std::str::from_utf8(&output.stdout).unwrap(); - let stderr = std::str::from_utf8(&output.stderr).unwrap(); - if !output.status.success() { - println!("stdout {stdout}"); - println!("stderr {stderr}"); - } - println!("{:?}", output.status); - assert!(output.status.success()); - assert_eq!(stderr, ""); -} - -#[test] -fn thread_safe_callback() { - build(); - - let output = deno_cmd() - .arg("run") - .arg("--allow-ffi") - .arg("--allow-read") - .arg("--unstable-ffi") - .arg("--quiet") - .arg("tests/thread_safe_test.js") - .env("NO_COLOR", "1") - .output() - .unwrap(); - let stdout = std::str::from_utf8(&output.stdout).unwrap(); - let stderr = std::str::from_utf8(&output.stderr).unwrap(); - if !output.status.success() { - println!("stdout {stdout}"); - println!("stderr {stderr}"); - } - println!("{:?}", output.status); - assert!(output.status.success()); - let expected = "\ - Callback on main thread\n\ - Callback on worker thread\n\ - STORED_FUNCTION cleared\n\ - Calling callback, isolate should stay asleep until callback is called\n\ - Callback being called\n\ - STORED_FUNCTION cleared\n\ - Isolate should now exit\n"; - assert_eq!(stdout, expected); - assert_eq!(stderr, ""); -} - -#[test] -fn event_loop_integration() { - build(); - - let output = deno_cmd() - .arg("run") - .arg("--allow-ffi") - .arg("--allow-read") - .arg("--unstable-ffi") - .arg("--quiet") - .arg("tests/event_loop_integration.ts") - .env("NO_COLOR", "1") - .output() - .unwrap(); - let stdout = std::str::from_utf8(&output.stdout).unwrap(); - let stderr = std::str::from_utf8(&output.stderr).unwrap(); - if !output.status.success() { - println!("stdout {stdout}"); - println!("stderr {stderr}"); - } - println!("{:?}", output.status); - assert!(output.status.success()); - // TODO(aapoalas): The order of logging in thread safe callbacks is - // unexpected: The callback logs synchronously and creates an asynchronous - // logging task, which then gets called synchronously before the callback - // actually yields to the calling thread. This is in contrast to what the - // logging would look like if the call was coming from within Deno itself, - // and may lead users to unknowingly run heavy asynchronous tasks from thread - // safe callbacks synchronously. - // The fix would be to make sure microtasks are only run after the event loop - // middleware that polls them has completed its work. This just does not seem - // to work properly with Linux release builds. - let expected = "\ - SYNCHRONOUS\n\ - Sync\n\ - STORED_FUNCTION called\n\ - Async\n\ - Timeout\n\ - THREAD SAFE\n\ - Sync\n\ - Async\n\ - STORED_FUNCTION called\n\ - Timeout\n\ - RETRY THREAD SAFE\n\ - Sync\n\ - Async\n\ - STORED_FUNCTION called\n\ - Timeout\n"; - assert_eq!(stdout, expected); - assert_eq!(stderr, ""); -} - -#[test] -fn ffi_callback_errors_test() { - build(); - - let output = deno_cmd() - .arg("run") - .arg("--allow-ffi") - .arg("--allow-read") - .arg("--unstable-ffi") - .arg("--quiet") - .arg("tests/ffi_callback_errors.ts") - .env("NO_COLOR", "1") - .output() - .unwrap(); - let stdout = std::str::from_utf8(&output.stdout).unwrap(); - let stderr = std::str::from_utf8(&output.stderr).unwrap(); - if !output.status.success() { - println!("stdout {stdout}"); - println!("stderr {stderr}"); - } - println!("{:?}", output.status); - assert!(output.status.success()); - - let expected = "\ - CallCase: SyncSelf\n\ - Throwing errors from an UnsafeCallback called from a synchronous UnsafeFnPointer works. Terribly excellent.\n\ - CallCase: SyncFfi\n\ - 0\n\ - Throwing errors from an UnsafeCallback called from a synchronous FFI symbol works. Terribly excellent.\n\ - CallCase: AsyncSelf\n\ - CallCase: AsyncSyncFfi\n\ - 0\n\ - Calling\n\ - CallCase: AsyncFfi\n"; - assert_eq!(stdout, expected); - assert_eq!( - stderr, - "Illegal unhandled exception in nonblocking callback.\n".repeat(3) - ); -} diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js deleted file mode 100644 index 8e57cd34b..000000000 --- a/test_ffi/tests/test.js +++ /dev/null @@ -1,802 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -// deno-lint-ignore-file - -// Run using cargo test or `--v8-flags=--allow-natives-syntax` - -import { - assertThrows, - assert, - assertNotEquals, - assertInstanceOf, - assertEquals, - assertFalse, -} from "../../test_util/std/assert/mod.ts"; - -const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); -const [libPrefix, libSuffix] = { - darwin: ["lib", "dylib"], - linux: ["lib", "so"], - windows: ["", "dll"], -}[Deno.build.os]; -const libPath = `${targetDir}/${libPrefix}test_ffi.${libSuffix}`; - -const resourcesPre = Deno[Deno.internal].core.resources(); - -// dlopen shouldn't panic -assertThrows(() => { - Deno.dlopen("cli/src/main.rs", {}); -}); - -assertThrows( - () => { - Deno.dlopen(libPath, { - non_existent_symbol: { - parameters: [], - result: "void", - }, - }); - }, - Error, - "Failed to register symbol non_existent_symbol", -); - -assertThrows(() => { - Deno.dlopen(libPath, { - print_something: { - parameters: [], - result: { struct: [] } - }, - }), - TypeError, - "Struct must have at least one field" -}); - -assertThrows(() => { - Deno.dlopen(libPath, { - print_something: { - parameters: [ { struct: [] } ], - result: "void", - }, - }), - TypeError, - "Struct must have at least one field" -}); - -const Empty = { struct: [] } -assertThrows(() => { - Deno.dlopen(libPath, { - print_something: { - parameters: [ { struct: [Empty] } ], - result: "void", - }, - }), - TypeError, - "Struct must have at least one field" -}); - -const Point = ["f64", "f64"]; -const Size = ["f64", "f64"]; -const Rect = ["f64", "f64", "f64", "f64"]; -const RectNested = [{ struct: Point }, { struct: Size }]; -const RectNestedCached = [{ struct: Size }, { struct: Size }]; -const Mixed = ["u8", "f32", { struct: Rect }, "usize", { struct: ["u32", "u32"] }]; - -const dylib = Deno.dlopen(libPath, { - "printSomething": { - name: "print_something", - parameters: [], - result: "void", - }, - "print_buffer": { parameters: ["buffer", "usize"], result: "void" }, - "print_pointer": { name: "print_buffer", parameters: ["pointer", "usize"], result: "void" }, - "print_buffer2": { - parameters: ["buffer", "usize", "buffer", "usize"], - result: "void", - }, - "return_buffer": { parameters: [], result: "buffer" }, - "is_null_ptr": { parameters: ["pointer"], result: "bool" }, - "is_null_buf": { name: "is_null_ptr", parameters: ["buffer"], result: "bool" }, - "add_u32": { parameters: ["u32", "u32"], result: "u32" }, - "add_i32": { parameters: ["i32", "i32"], result: "i32" }, - "add_u64": { parameters: ["u64", "u64"], result: "u64" }, - "add_i64": { parameters: ["i64", "i64"], result: "i64" }, - "add_usize": { parameters: ["usize", "usize"], result: "usize" }, - "add_usize_fast": { parameters: ["usize", "usize"], result: "u32" }, - "add_isize": { parameters: ["isize", "isize"], result: "isize" }, - "add_f32": { parameters: ["f32", "f32"], result: "f32" }, - "add_f64": { parameters: ["f64", "f64"], result: "f64" }, - "and": { parameters: ["bool", "bool"], result: "bool" }, - "add_u32_nonblocking": { - name: "add_u32", - parameters: ["u32", "u32"], - result: "u32", - nonblocking: true, - }, - "add_i32_nonblocking": { - name: "add_i32", - parameters: ["i32", "i32"], - result: "i32", - nonblocking: true, - }, - "add_u64_nonblocking": { - name: "add_u64", - parameters: ["u64", "u64"], - result: "u64", - nonblocking: true, - }, - "add_i64_nonblocking": { - name: "add_i64", - parameters: ["i64", "i64"], - result: "i64", - nonblocking: true, - }, - "add_usize_nonblocking": { - name: "add_usize", - parameters: ["usize", "usize"], - result: "usize", - nonblocking: true, - }, - "add_isize_nonblocking": { - name: "add_isize", - parameters: ["isize", "isize"], - result: "isize", - nonblocking: true, - }, - "add_f32_nonblocking": { - name: "add_f32", - parameters: ["f32", "f32"], - result: "f32", - nonblocking: true, - }, - "add_f64_nonblocking": { - name: "add_f64", - parameters: ["f64", "f64"], - result: "f64", - nonblocking: true, - }, - "fill_buffer": { parameters: ["u8", "buffer", "usize"], result: "void" }, - "sleep_nonblocking": { - name: "sleep_blocking", - parameters: ["u64"], - result: "void", - nonblocking: true, - }, - "sleep_blocking": { parameters: ["u64"], result: "void" }, - "nonblocking_buffer": { - parameters: ["buffer", "usize"], - result: "void", - nonblocking: true, - }, - "get_add_u32_ptr": { - parameters: [], - result: "pointer", - }, - "get_sleep_blocking_ptr": { - parameters: [], - result: "pointer", - }, - // Callback function - call_fn_ptr: { - parameters: ["function"], - result: "void", - }, - call_fn_ptr_thread_safe: { - name: "call_fn_ptr", - parameters: ["function"], - result: "void", - nonblocking: true, - }, - call_fn_ptr_many_parameters: { - parameters: ["function"], - result: "void", - }, - call_fn_ptr_return_u8: { - parameters: ["function"], - result: "void", - }, - call_fn_ptr_return_u8_thread_safe: { - name: "call_fn_ptr_return_u8", - parameters: ["function"], - result: "void", - }, - call_fn_ptr_return_buffer: { - parameters: ["function"], - result: "void", - }, - store_function: { - parameters: ["function"], - result: "void", - }, - store_function_2: { - parameters: ["function"], - result: "void", - }, - call_stored_function: { - parameters: [], - result: "void", - callback: true, - }, - call_stored_function_2: { - parameters: ["u8"], - result: "void", - callback: true, - }, - log_many_parameters: { - parameters: ["u8", "u16", "u32", "u64", "f64", "f32", "i64", "i32", "i16", "i8", "isize", "usize", "f64", "f32", "f64", "f32", "f64", "f32", "f64"], - result: "void", - }, - cast_u8_u32: { - parameters: ["u8"], - result: "u32", - }, - cast_u32_u8: { - parameters: ["u32"], - result: "u8", - }, - add_many_u16: { - parameters: ["u16", "u16", "u16", "u16", "u16", "u16", "u16", "u16", "u16", "u16", "u16", "u16", "u16"], - result: "u16", - }, - // Statics - "static_u32": { - type: "u32", - }, - "static_i64": { - type: "i64", - }, - "static_ptr": { - type: "pointer", - }, - /** - * Invalid UTF-8 characters, buffer of length 14 - */ - "static_char": { - type: "pointer", - optional: true, - }, - "hash": { parameters: ["buffer", "u32"], result: "u32" }, - make_rect: { - parameters: ["f64", "f64", "f64", "f64"], - result: { struct: Rect }, - }, - make_rect_async: { - name: "make_rect", - nonblocking: true, - parameters: ["f64", "f64", "f64", "f64"], - result: { struct: RectNested }, - }, - print_rect: { - parameters: [{ struct: RectNestedCached }], - result: "void", - }, - print_rect_async: { - name: "print_rect", - nonblocking: true, - parameters: [{ struct: Rect }], - result: "void", - }, - create_mixed: { - parameters: ["u8", "f32", { struct: Rect }, "pointer", "buffer"], - result: { struct: Mixed } - }, - print_mixed: { - parameters: [{ struct: Mixed }], - result: "void", - optional: true, - }, - non_existent_symbol: { - parameters: [], - result: "void", - optional: true, - }, - non_existent_nonblocking_symbol: { - parameters: [], - result: "void", - nonblocking: true, - optional: true, - }, - non_existent_static: { - type: "u32", - optional: true, - }, -}); -const { symbols } = dylib; - -symbols.printSomething(); -const buffer = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); -const buffer2 = new Uint8Array([9, 10]); -dylib.symbols.print_buffer(buffer, buffer.length); -// Test subarrays -const subarray = buffer.subarray(3); -dylib.symbols.print_buffer(subarray, subarray.length - 2); -dylib.symbols.print_buffer2(buffer, buffer.length, buffer2, buffer2.length); - -const { return_buffer } = symbols; -function returnBuffer() { return return_buffer(); }; - -%PrepareFunctionForOptimization(returnBuffer); -returnBuffer(); -%OptimizeFunctionOnNextCall(returnBuffer); -const ptr0 = returnBuffer(); -assertIsOptimized(returnBuffer); - -dylib.symbols.print_pointer(ptr0, 8); -const ptrView = new Deno.UnsafePointerView(ptr0); -const into = new Uint8Array(6); -const into2 = new Uint8Array(3); -const into2ptr = Deno.UnsafePointer.of(into2); -const into2ptrView = new Deno.UnsafePointerView(into2ptr); -const into3 = new Uint8Array(3); -const into4 = new Uint16Array(3); -ptrView.copyInto(into4); -ptrView.copyInto(into); -console.log([...into]); -ptrView.copyInto(into2, 3); -console.log([...into2]); -into2ptrView.copyInto(into3); -console.log([...into3]); -const string = new Uint8Array([ - ...new TextEncoder().encode("Hello from pointer!"), - 0, -]); -const stringPtr = Deno.UnsafePointer.of(string); -const stringPtrview = new Deno.UnsafePointerView(stringPtr); -console.log(stringPtrview.getCString()); -console.log(stringPtrview.getCString(11)); -console.log("false", dylib.symbols.is_null_ptr(ptr0)); -console.log("true", dylib.symbols.is_null_ptr(null)); -console.log("false", dylib.symbols.is_null_ptr(Deno.UnsafePointer.of(into))); -const emptyBuffer = new Uint8Array(0); -console.log("true", dylib.symbols.is_null_ptr(Deno.UnsafePointer.of(emptyBuffer))); -const emptySlice = into.subarray(6); -console.log("false", dylib.symbols.is_null_ptr(Deno.UnsafePointer.of(emptySlice))); - -const { is_null_buf } = symbols; -function isNullBuffer(buffer) { return is_null_buf(buffer); }; -function isNullBufferDeopt(buffer) { return is_null_buf(buffer); }; -%PrepareFunctionForOptimization(isNullBuffer); -isNullBuffer(emptyBuffer); -%NeverOptimizeFunction(isNullBufferDeopt); -%OptimizeFunctionOnNextCall(isNullBuffer); -isNullBuffer(emptyBuffer); -assertIsOptimized(isNullBuffer); - -// ==== ZERO LENGTH BUFFER TESTS ==== -assertEquals(isNullBuffer(emptyBuffer), true, "isNullBuffer(emptyBuffer) !== true"); -assertEquals(isNullBufferDeopt(emptyBuffer), true, "isNullBufferDeopt(emptyBuffer) !== true"); -assertEquals(isNullBuffer(emptySlice), false, "isNullBuffer(emptySlice) !== false"); -assertEquals(isNullBufferDeopt(emptySlice), false, "isNullBufferDeopt(emptySlice) !== false"); -assertEquals(isNullBufferDeopt(new Uint8Array()), true, "isNullBufferDeopt(new Uint8Array()) !== true"); - -// ==== V8 ZERO LENGTH BUFFER ANOMALIES ==== - -// V8 bug: inline Uint8Array creation to fast call sees non-null pointer -// https://bugs.chromium.org/p/v8/issues/detail?id=13489 -if (Deno.build.os != "linux" || Deno.build.arch != "aarch64") { - assertEquals(isNullBuffer(new Uint8Array()), false, "isNullBuffer(new Uint8Array()) !== false"); -} - -// Externally backed ArrayBuffer has a non-null data pointer, even though its length is zero. -const externalZeroBuffer = new Uint8Array(Deno.UnsafePointerView.getArrayBuffer(ptr0, 0)); -// V8 Fast calls used to get null pointers for all zero-sized buffers no matter their external backing. -assertEquals(isNullBuffer(externalZeroBuffer), false, "isNullBuffer(externalZeroBuffer) !== false"); -// V8's `Local<ArrayBuffer>->Data()` method also used to similarly return null pointers for all -// zero-sized buffers which would not match what `Local<ArrayBuffer>->GetBackingStore()->Data()` -// API returned. These issues have been fixed in https://bugs.chromium.org/p/v8/issues/detail?id=13488. -assertEquals(isNullBufferDeopt(externalZeroBuffer), false, "isNullBufferDeopt(externalZeroBuffer) !== false"); - -// The same pointer with a non-zero byte length for the buffer will return non-null pointers in -// both Fast call and V8 API calls. -const externalOneBuffer = new Uint8Array(Deno.UnsafePointerView.getArrayBuffer(ptr0, 1)); -assertEquals(isNullBuffer(externalOneBuffer), false, "isNullBuffer(externalOneBuffer) !== false"); -assertEquals(isNullBufferDeopt(externalOneBuffer), false, "isNullBufferDeopt(externalOneBuffer) !== false"); - -// UnsafePointer.of uses an exact-pointer fallback for zero-length buffers and slices to ensure that it always gets -// the underlying pointer right. -assertNotEquals(Deno.UnsafePointer.of(externalZeroBuffer), null, "Deno.UnsafePointer.of(externalZeroBuffer) === null"); -assertNotEquals(Deno.UnsafePointer.of(externalOneBuffer), null, "Deno.UnsafePointer.of(externalOneBuffer) === null"); - -const addU32Ptr = dylib.symbols.get_add_u32_ptr(); -const addU32 = new Deno.UnsafeFnPointer(addU32Ptr, { - parameters: ["u32", "u32"], - result: "u32", -}); -console.log(addU32.call(123, 456)); - -const sleepBlockingPtr = dylib.symbols.get_sleep_blocking_ptr(); -const sleepNonBlocking = new Deno.UnsafeFnPointer(sleepBlockingPtr, { - nonblocking: true, - parameters: ["u64"], - result: "void", -}); -const before = performance.now(); -await sleepNonBlocking.call(100); -console.log(performance.now() - before >= 100); - -const { add_u32, add_usize_fast } = symbols; -function addU32Fast(a, b) { - return add_u32(a, b); -}; -testOptimized(addU32Fast, () => addU32Fast(123, 456)); - -function addU64Fast(a, b) { return add_usize_fast(a, b); }; -testOptimized(addU64Fast, () => addU64Fast(2, 3)); - -console.log(dylib.symbols.add_i32(123, 456)); -console.log(dylib.symbols.add_u64(0xffffffffn, 0xffffffffn)); -console.log(dylib.symbols.add_i64(-0xffffffffn, -0xffffffffn)); -console.log(dylib.symbols.add_usize(0xffffffffn, 0xffffffffn)); -console.log(dylib.symbols.add_isize(-0xffffffffn, -0xffffffffn)); -console.log(dylib.symbols.add_u64(Number.MAX_SAFE_INTEGER, 1)); -console.log(dylib.symbols.add_i64(Number.MAX_SAFE_INTEGER, 1)); -console.log(dylib.symbols.add_i64(Number.MIN_SAFE_INTEGER, -1)); -console.log(dylib.symbols.add_usize(Number.MAX_SAFE_INTEGER, 1)); -console.log(dylib.symbols.add_isize(Number.MAX_SAFE_INTEGER, 1)); -console.log(dylib.symbols.add_isize(Number.MIN_SAFE_INTEGER, -1)); -console.log(dylib.symbols.add_f32(123.123, 456.789)); -console.log(dylib.symbols.add_f64(123.123, 456.789)); -console.log(dylib.symbols.and(true, true)); -console.log(dylib.symbols.and(true, false)); - -function addF32Fast(a, b) { - return dylib.symbols.add_f32(a, b); -}; -testOptimized(addF32Fast, () => addF32Fast(123.123, 456.789)); - -function addF64Fast(a, b) { - return dylib.symbols.add_f64(a, b); -}; -testOptimized(addF64Fast, () => addF64Fast(123.123, 456.789)); - -// Test adders as nonblocking calls -console.log(await dylib.symbols.add_i32_nonblocking(123, 456)); -console.log(await dylib.symbols.add_u64_nonblocking(0xffffffffn, 0xffffffffn)); -console.log( - await dylib.symbols.add_i64_nonblocking(-0xffffffffn, -0xffffffffn), -); -console.log( - await dylib.symbols.add_usize_nonblocking(0xffffffffn, 0xffffffffn), -); -console.log( - await dylib.symbols.add_isize_nonblocking(-0xffffffffn, -0xffffffffn), -); -console.log(await dylib.symbols.add_u64_nonblocking(Number.MAX_SAFE_INTEGER, 1)); -console.log(await dylib.symbols.add_i64_nonblocking(Number.MAX_SAFE_INTEGER, 1)); -console.log(await dylib.symbols.add_i64_nonblocking(Number.MIN_SAFE_INTEGER, -1)); -console.log(await dylib.symbols.add_usize_nonblocking(Number.MAX_SAFE_INTEGER, 1)); -console.log(await dylib.symbols.add_isize_nonblocking(Number.MAX_SAFE_INTEGER, 1)); -console.log(await dylib.symbols.add_isize_nonblocking(Number.MIN_SAFE_INTEGER, -1)); -console.log(await dylib.symbols.add_f32_nonblocking(123.123, 456.789)); -console.log(await dylib.symbols.add_f64_nonblocking(123.123, 456.789)); - -// test mutating sync calls - -function test_fill_buffer(fillValue, arr) { - let buf = new Uint8Array(arr); - dylib.symbols.fill_buffer(fillValue, buf, buf.length); - for (let i = 0; i < buf.length; i++) { - if (buf[i] !== fillValue) { - throw new Error(`Found '${buf[i]}' in buffer, expected '${fillValue}'.`); - } - } -} - -test_fill_buffer(0, [2, 3, 4]); -test_fill_buffer(5, [2, 7, 3, 2, 1]); - -const deferred = Promise.withResolvers(); -const buffer3 = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); -dylib.symbols.nonblocking_buffer(buffer3, buffer3.length).then(() => { - deferred.resolve(); -}); -await deferred.promise; - -let start = performance.now(); -dylib.symbols.sleep_blocking(100); -assert(performance.now() - start >= 100); - -start = performance.now(); -const promise_2 = dylib.symbols.sleep_nonblocking(100).then(() => { - console.log("After"); - assert(performance.now() - start >= 100); -}); -console.log("Before"); -assert(performance.now() - start < 100); - -// Await to make sure `sleep_nonblocking` calls and logs before we proceed -await promise_2; - -// Test calls with callback parameters -const logCallback = new Deno.UnsafeCallback( - { parameters: [], result: "void" }, - () => console.log("logCallback"), -); -const logManyParametersCallback = new Deno.UnsafeCallback({ - parameters: [ - "u8", - "i8", - "u16", - "i16", - "u32", - "i32", - "u64", - "i64", - "f32", - "f64", - "pointer", - ], - result: "void", -}, (u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, pointer) => { - const view = new Deno.UnsafePointerView(pointer); - const copy_buffer = new Uint8Array(8); - view.copyInto(copy_buffer); - console.log(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, ...copy_buffer); -}); -const returnU8Callback = new Deno.UnsafeCallback( - { parameters: [], result: "u8" }, - () => 8, -); -const returnBufferCallback = new Deno.UnsafeCallback({ - parameters: [], - result: "buffer", -}, () => { - return buffer; -}); -const add10Callback = new Deno.UnsafeCallback({ - parameters: ["u8"], - result: "u8", -}, (value) => value + 10); -const throwCallback = new Deno.UnsafeCallback({ - parameters: [], - result: "void", -}, () => { - throw new TypeError("hi"); -}); - -assertThrows( - () => { - dylib.symbols.call_fn_ptr(throwCallback.pointer); - }, - TypeError, - "hi", -); - -const { call_stored_function } = dylib.symbols; - -dylib.symbols.call_fn_ptr(logCallback.pointer); -dylib.symbols.call_fn_ptr_many_parameters(logManyParametersCallback.pointer); -dylib.symbols.call_fn_ptr_return_u8(returnU8Callback.pointer); -dylib.symbols.call_fn_ptr_return_buffer(returnBufferCallback.pointer); -dylib.symbols.store_function(logCallback.pointer); -call_stored_function(); -dylib.symbols.store_function_2(add10Callback.pointer); -dylib.symbols.call_stored_function_2(20); - -function logManyParametersFast(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) { - return symbols.log_many_parameters(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s); -}; -testOptimized( - logManyParametersFast, - () => logManyParametersFast( - 255, 65535, 4294967295, 4294967296, 123.456, 789.876, -1, -2, -3, -4, -1000, 1000, - 12345.678910, 12345.678910, 12345.678910, 12345.678910, 12345.678910, 12345.678910, 12345.678910 - ) -); - -// Some ABIs rely on the convention to zero/sign-extend arguments by the caller to optimize the callee function. -// If the trampoline did not zero/sign-extend arguments, this would return 256 instead of the expected 0 (in optimized builds) -function castU8U32Fast(x) { return symbols.cast_u8_u32(x); }; -testOptimized(castU8U32Fast, () => castU8U32Fast(256)); - -// Some ABIs rely on the convention to expect garbage in the bits beyond the size of the return value to optimize the callee function. -// If the trampoline did not zero/sign-extend the return value, this would return 256 instead of the expected 0 (in optimized builds) -function castU32U8Fast(x) { return symbols.cast_u32_u8(x); }; -testOptimized(castU32U8Fast, () => castU32U8Fast(256)); - -// Generally the trampoline tail-calls into the FFI function, but in certain cases (e.g. when returning 8 or 16 bit integers) -// the tail call is not possible and a new stack frame must be created. We need enough parameters to have some on the stack -function addManyU16Fast(a, b, c, d, e, f, g, h, i, j, k, l, m) { - return symbols.add_many_u16(a, b, c, d, e, f, g, h, i, j, k, l, m); -}; -// N.B. V8 does not currently follow Aarch64 Apple's calling convention. -// The current implementation of the JIT trampoline follows the V8 incorrect calling convention. This test covers the use-case -// and is expected to fail once Deno uses a V8 version with the bug fixed. -// The V8 bug is being tracked in https://bugs.chromium.org/p/v8/issues/detail?id=13171 -testOptimized(addManyU16Fast, () => addManyU16Fast(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)); - - -const nestedCallback = new Deno.UnsafeCallback( - { parameters: [], result: "void" }, - () => { - dylib.symbols.call_stored_function_2(10); - }, -); -dylib.symbols.store_function(nestedCallback.pointer); - -dylib.symbols.store_function(null); -dylib.symbols.store_function_2(null); - -let counter = 0; -const addToFooCallback = new Deno.UnsafeCallback({ - parameters: [], - result: "void", -}, () => counter++); - -// Test thread safe callbacks -assertEquals(counter, 0); -addToFooCallback.ref(); -await dylib.symbols.call_fn_ptr_thread_safe(addToFooCallback.pointer); -addToFooCallback.unref(); -logCallback.ref(); -await dylib.symbols.call_fn_ptr_thread_safe(logCallback.pointer); -logCallback.unref(); -assertEquals(counter, 1); -returnU8Callback.ref(); -await dylib.symbols.call_fn_ptr_return_u8_thread_safe(returnU8Callback.pointer); -// Purposefully do not unref returnU8Callback: Instead use it to test close() unrefing. - -// Test statics -assertEquals(dylib.symbols.static_u32, 42); -assertEquals(dylib.symbols.static_i64, -1242464576485); -assert( - typeof dylib.symbols.static_ptr === "object" -); -assertEquals( - Object.keys(dylib.symbols.static_ptr).length, 0 -); -const view = new Deno.UnsafePointerView(dylib.symbols.static_ptr); -assertEquals(view.getUint32(), 42); - -// Test struct returning -const rect_sync = dylib.symbols.make_rect(10, 20, 100, 200); -assertInstanceOf(rect_sync, Uint8Array); -assertEquals(rect_sync.length, 4 * 8); -assertEquals(Array.from(new Float64Array(rect_sync.buffer)), [10, 20, 100, 200]); -// Test struct passing -dylib.symbols.print_rect(rect_sync); -// Test struct passing asynchronously -await dylib.symbols.print_rect_async(rect_sync); -dylib.symbols.print_rect(new Float64Array([20, 20, 100, 200])); -// Test struct returning asynchronously -const rect_async = await dylib.symbols.make_rect_async(10, 20, 100, 200); -assertInstanceOf(rect_async, Uint8Array); -assertEquals(rect_async.length, 4 * 8); -assertEquals(Array.from(new Float64Array(rect_async.buffer)), [10, 20, 100, 200]); - -// Test complex, mixed struct returning and passing -const mixedStruct = dylib.symbols.create_mixed(3, 12.515000343322754, rect_async, Deno.UnsafePointer.create(12456789), new Uint32Array([8, 32])); -assertEquals(mixedStruct.length, 56); -assertEquals(Array.from(mixedStruct.subarray(0, 4)), [3, 0, 0, 0]); -assertEquals(new Float32Array(mixedStruct.buffer, 4, 1)[0], 12.515000343322754); -assertEquals(new Float64Array(mixedStruct.buffer, 8, 4), new Float64Array(rect_async.buffer)); -assertEquals(new BigUint64Array(mixedStruct.buffer, 40, 1)[0], 12456789n); -assertEquals(new Uint32Array(mixedStruct.buffer, 48, 2), new Uint32Array([8, 32])); -dylib.symbols.print_mixed(mixedStruct); - -const cb = new Deno.UnsafeCallback({ - parameters: [{ struct: Rect }], - result: { struct: Rect }, -}, (innerRect) => { - innerRect = new Float64Array(innerRect.buffer); - return new Float64Array([innerRect[0] + 10, innerRect[1] + 10, innerRect[2] + 10, innerRect[3] + 10]); -}); - -const cbFfi = new Deno.UnsafeFnPointer(cb.pointer, cb.definition); -const cbResult = new Float64Array(cbFfi.call(rect_async).buffer); -assertEquals(Array.from(cbResult), [20, 30, 110, 210]); - -cb.close(); - -const arrayBuffer = view.getArrayBuffer(4); -const uint32Array = new Uint32Array(arrayBuffer); -assertEquals(arrayBuffer.byteLength, 4); -assertEquals(uint32Array.length, 1); -assertEquals(uint32Array[0], 42); -uint32Array[0] = 55; // MUTATES! -assertEquals(uint32Array[0], 55); -assertEquals(view.getUint32(), 55); - - -{ - // Test UnsafePointer APIs - assertEquals(Deno.UnsafePointer.create(0), null); - const createdPointer = Deno.UnsafePointer.create(1); - assertNotEquals(createdPointer, null); - assertEquals(typeof createdPointer, "object"); - assertEquals(Deno.UnsafePointer.value(null), 0); - assertEquals(Deno.UnsafePointer.value(createdPointer), 1); - assert(Deno.UnsafePointer.equals(null, null)); - assertFalse(Deno.UnsafePointer.equals(null, createdPointer)); - assertFalse(Deno.UnsafePointer.equals(Deno.UnsafePointer.create(2), createdPointer)); - // Do not allow offsetting from null, `create` function should be used instead. - assertThrows(() => Deno.UnsafePointer.offset(null, 5)); - const offsetPointer = Deno.UnsafePointer.offset(createdPointer, 5); - assertEquals(Deno.UnsafePointer.value(offsetPointer), 6); - const zeroPointer = Deno.UnsafePointer.offset(offsetPointer, -6); - assertEquals(Deno.UnsafePointer.value(zeroPointer), 0); - assertEquals(zeroPointer, null); -} - -// Test non-UTF-8 characters - -const charView = new Deno.UnsafePointerView(dylib.symbols.static_char); - -const charArrayBuffer = charView.getArrayBuffer(14); -const uint8Array = new Uint8Array(charArrayBuffer); -assertEquals([...uint8Array], [ - 0xC0, 0xC1, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, - 0x00 -]); - -// Check that `getCString` works equally to `TextDecoder` -assertEquals(charView.getCString(), new TextDecoder().decode(uint8Array.subarray(0, uint8Array.length - 1))); - -// Check a selection of various invalid UTF-8 sequences in C strings and verify -// that the `getCString` API does not cause unexpected behaviour. -for (const charBuffer of [ - Uint8Array.from([0xA0, 0xA1, 0x00]), - Uint8Array.from([0xE2, 0x28, 0xA1, 0x00]), - Uint8Array.from([0xE2, 0x82, 0x28, 0x00]), - Uint8Array.from([0xF0, 0x28, 0x8C, 0xBC, 0x00]), - Uint8Array.from([0xF0, 0x90, 0x28, 0xBC, 0x00]), - Uint8Array.from([0xF0, 0x28, 0x8C, 0x28, 0x00]), - Uint8Array.from([0xF8, 0xA1, 0xA1, 0xA1, 0xA1, 0x00]), - Uint8Array.from([0xFC, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0x00]), -]) { - const charBufferPointer = Deno.UnsafePointer.of(charBuffer); - const charString = Deno.UnsafePointerView.getCString(charBufferPointer); - const charBufferPointerArrayBuffer = new Uint8Array(Deno.UnsafePointerView.getArrayBuffer(charBufferPointer, charBuffer.length - 1)); - assertEquals(charString, new TextDecoder().decode(charBufferPointerArrayBuffer)); - assertEquals([...charBuffer.subarray(0, charBuffer.length - 1)], [...charBufferPointerArrayBuffer]); -} - - -const bytes = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); -function hash() { return dylib.symbols.hash(bytes, bytes.byteLength); }; - -testOptimized(hash, () => hash()); - -(function cleanup() { - dylib.close(); - throwCallback.close(); - logCallback.close(); - logManyParametersCallback.close(); - returnU8Callback.close(); - returnBufferCallback.close(); - add10Callback.close(); - nestedCallback.close(); - addToFooCallback.close(); - - const resourcesPost = Deno[Deno.internal].core.resources(); - - const preStr = JSON.stringify(resourcesPre, null, 2); - const postStr = JSON.stringify(resourcesPost, null, 2); - if (preStr !== postStr) { - throw new Error( - `Difference in open resources before dlopen and after closing: -Before: ${preStr} -After: ${postStr}`, - ); - } - - console.log("Correct number of resources"); -})(); - -function assertIsOptimized(fn) { - const status = %GetOptimizationStatus(fn); - assert(status & (1 << 4), `expected ${fn.name} to be optimized, but wasn't`); -} - -function testOptimized(fn, callback) { - %PrepareFunctionForOptimization(fn); - const r1 = callback(); - if (r1 !== undefined) { - console.log(r1); - } - %OptimizeFunctionOnNextCall(fn); - const r2 = callback(); - if (r2 !== undefined) { - console.log(r2); - } - assertIsOptimized(fn); -} diff --git a/test_ffi/tests/thread_safe_test.js b/test_ffi/tests/thread_safe_test.js deleted file mode 100644 index fffa61a04..000000000 --- a/test_ffi/tests/thread_safe_test.js +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -// deno-lint-ignore-file - -const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); -const [libPrefix, libSuffix] = { - darwin: ["lib", "dylib"], - linux: ["lib", "so"], - windows: ["", "dll"], -}[Deno.build.os]; -const libPath = `${targetDir}/${libPrefix}test_ffi.${libSuffix}`; - -const dylib = Deno.dlopen(libPath, { - store_function: { - parameters: ["function"], - result: "void", - }, - call_stored_function: { - parameters: [], - result: "void", - }, - call_stored_function_thread_safe: { - parameters: [], - result: "void", - }, -}); - -let resolveWorker; -let workerResponsePromise; - -const worker = new Worker( - new URL("./thread_safe_test_worker.js", import.meta.url).href, - { type: "module" }, -); - -worker.addEventListener("message", () => { - if (resolveWorker) { - resolveWorker(); - } -}); - -const sendWorkerMessage = async (data) => { - workerResponsePromise = new Promise((res) => { - resolveWorker = res; - }); - worker.postMessage(data); - await workerResponsePromise; -}; - -// Test step 1: Register main thread callback, trigger on worker thread - -const mainThreadCallback = new Deno.UnsafeCallback( - { parameters: [], result: "void" }, - () => { - console.log("Callback on main thread"); - }, -); - -mainThreadCallback.ref(); - -dylib.symbols.store_function(mainThreadCallback.pointer); - -await sendWorkerMessage("call"); - -// Test step 2: Register on worker thread, trigger on main thread - -await sendWorkerMessage("register"); - -dylib.symbols.call_stored_function(); - -// Unref both main and worker thread callbacks and terminate the worker: Note, the stored function pointer in lib is now dangling. - -dylib.symbols.store_function(null); - -mainThreadCallback.unref(); -await sendWorkerMessage("unref"); -worker.terminate(); - -// Test step 3: Register a callback that will be the only thing left keeping the isolate from exiting. -// Rely on it to keep Deno running until the callback comes in and unrefs the callback, after which Deno should exit. - -const cleanupCallback = new Deno.UnsafeCallback( - { parameters: [], result: "void" }, - () => { - console.log("Callback being called"); - // Defer the cleanup to give the spawned thread all the time it needs to properly shut down - setTimeout(() => cleanup(), 100); - }, -); - -cleanupCallback.ref(); - -function cleanup() { - cleanupCallback.unref(); - dylib.symbols.store_function(null); - mainThreadCallback.close(); - cleanupCallback.close(); - console.log("Isolate should now exit"); -} - -dylib.symbols.store_function(cleanupCallback.pointer); - -console.log( - "Calling callback, isolate should stay asleep until callback is called", -); -dylib.symbols.call_stored_function_thread_safe(); diff --git a/test_ffi/tests/thread_safe_test_worker.js b/test_ffi/tests/thread_safe_test_worker.js deleted file mode 100644 index fc4854436..000000000 --- a/test_ffi/tests/thread_safe_test_worker.js +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -// deno-lint-ignore-file - -const targetDir = Deno.execPath().replace(/[^\/\\]+$/, ""); -const [libPrefix, libSuffix] = { - darwin: ["lib", "dylib"], - linux: ["lib", "so"], - windows: ["", "dll"], -}[Deno.build.os]; -const libPath = `${targetDir}/${libPrefix}test_ffi.${libSuffix}`; - -const dylib = Deno.dlopen(libPath, { - store_function: { - parameters: ["function"], - result: "void", - }, - call_stored_function: { - parameters: [], - result: "void", - }, -}); - -const callback = new Deno.UnsafeCallback( - { parameters: [], result: "void" }, - () => { - console.log("Callback on worker thread"); - }, -); - -callback.ref(); - -self.addEventListener("message", ({ data }) => { - if (data === "register") { - dylib.symbols.store_function(callback.pointer); - } else if (data === "call") { - dylib.symbols.call_stored_function(); - } else if (data === "unref") { - callback.close(); - } - self.postMessage("done"); -}); |