diff options
author | Dj <43033058+DjDeveloperr@users.noreply.github.com> | 2023-01-07 19:58:10 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-08 09:28:10 +0530 |
commit | ad82918f56b215a428ebe7c533ee825e1152d1b4 (patch) | |
tree | 2669f9d8d419a88e9d58b12b98579e884aec6988 /test_ffi | |
parent | 84ef26ac9b5f0e1199d77837cd97cb203baa8729 (diff) |
feat(ext/ffi): structs by value (#15060)
Adds support for passing and returning structs as buffers to FFI. This does not implement fastapi support for structs. Needed for certain system APIs such as AppKit on macOS.
Diffstat (limited to 'test_ffi')
-rw-r--r-- | test_ffi/src/lib.rs | 58 | ||||
-rw-r--r-- | test_ffi/tests/ffi_types.ts | 9 | ||||
-rw-r--r-- | test_ffi/tests/integration_tests.rs | 4 | ||||
-rw-r--r-- | test_ffi/tests/test.js | 76 |
4 files changed, 138 insertions, 9 deletions
diff --git a/test_ffi/src/lib.rs b/test_ffi/src/lib.rs index b608877f7..ad15ee5ae 100644 --- a/test_ffi/src/lib.rs +++ b/test_ffi/src/lib.rs @@ -485,3 +485,61 @@ pub static static_char: [u8; 14] = [ 0xC0, 0xC1, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0x00, ]; + +#[derive(Debug)] +#[repr(C)] +pub struct Rect { + x: f64, + y: f64, + w: f64, + h: f64, +} + +#[no_mangle] +pub extern "C" fn make_rect(x: f64, y: f64, w: f64, h: f64) -> Rect { + Rect { x, y, w, h } +} + +#[no_mangle] +pub extern "C" fn print_rect(rect: Rect) { + println!("{:?}", rect); +} + +#[derive(Debug)] +#[repr(C)] +pub struct Mixed { + u8: u8, + f32: f32, + rect: Rect, + usize: usize, + array: [u32; 2], +} + +/// # Safety +/// +/// The array pointer to the buffer must be valid and initalized, and the length must +/// be 2. +#[no_mangle] +pub unsafe extern "C" fn create_mixed( + u8: u8, + f32: f32, + rect: Rect, + usize: usize, + array: *const [u32; 2], +) -> Mixed { + let array = *array + .as_ref() + .expect("Array parameter should contain value"); + Mixed { + u8, + f32, + rect, + usize, + array, + } +} + +#[no_mangle] +pub extern "C" fn print_mixed(mixed: Mixed) { + println!("{:?}", mixed); +} diff --git a/test_ffi/tests/ffi_types.ts b/test_ffi/tests/ffi_types.ts index 058138a8d..d7c66203c 100644 --- a/test_ffi/tests/ffi_types.ts +++ b/test_ffi/tests/ffi_types.ts @@ -66,17 +66,10 @@ const remote = Deno.dlopen( Deno.dlopen( "dummy_lib_2.so", - // is declared using "pointer" or "function" + UnsafeFnPointer { wrong_method1: { parameters: [], - // @ts-expect-error not assignable to type 'NativeResultType' - result: { - function: { - parameters: [], - result: "void", - }, - }, + result: "function", }, }, ); diff --git a/test_ffi/tests/integration_tests.rs b/test_ffi/tests/integration_tests.rs index 6564f1e14..3c86d6d4d 100644 --- a/test_ffi/tests/integration_tests.rs +++ b/test_ffi/tests/integration_tests.rs @@ -127,6 +127,10 @@ fn basic() { Static i64: -1242464576485\n\ Static ptr: true\n\ Static ptr value: 42\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\ arrayBuffer.byteLength: 4\n\ uint32Array.length: 1\n\ uint32Array[0]: 42\n\ diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js index 3bbf475e9..0ada7dc0a 100644 --- a/test_ffi/tests/test.js +++ b/test_ffi/tests/test.js @@ -3,7 +3,7 @@ // Run using cargo test or `--v8-options=--allow-natives-syntax` -import { assertEquals, assertNotEquals } from "https://deno.land/std@0.149.0/testing/asserts.ts"; +import { assertEquals, assertInstanceOf, assertNotEquals } from "https://deno.land/std@0.149.0/testing/asserts.ts"; import { assertThrows, assert, @@ -37,6 +37,12 @@ assertThrows( "Failed to register symbol non_existent_symbol", ); +const Point = ["f64", "f64"]; +const Size = ["f64", "f64"]; +const Rect = ["f64", "f64", "f64", "f64"]; +const RectNested = [{ struct: Point }, { struct: Size }]; +const Mixed = ["u8", "f32", { struct: Rect }, "usize", { struct: ["u32", "u32"] }]; + const dylib = Deno.dlopen(libPath, { "printSomething": { name: "print_something", @@ -210,6 +216,34 @@ const dylib = Deno.dlopen(libPath, { type: "pointer", }, "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: Rect }], + 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", + }, }); const { symbols } = dylib; @@ -571,6 +605,46 @@ console.log( const view = new Deno.UnsafePointerView(dylib.symbols.static_ptr); console.log("Static ptr value:", view.getUint32()); +// 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, 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); console.log("arrayBuffer.byteLength:", arrayBuffer.byteLength); |