From 6cd71076315746095031f66f636360bf7ccd563e Mon Sep 17 00:00:00 2001 From: usrtax Date: Thu, 29 Sep 2022 16:16:22 +0800 Subject: [PATCH] add support for Vec and Box<[u8]> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ♨ --- .rustfmt.toml | 10 +- deno_bindgen/Cargo.toml | 2 +- deno_bindgen_macro/Cargo.toml | 2 +- deno_bindgen_macro/src/derive_fn.rs | 46 ++++- deno_bindgen_macro/src/lib.rs | 2 +- example/bindings/bindings.ts | 278 ++++++++++++++++------------ example/src/lib.rs | 10 + 7 files changed, 231 insertions(+), 119 deletions(-) diff --git a/.rustfmt.toml b/.rustfmt.toml index 233fe75..de76f84 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,3 +1,11 @@ +edition = "2021" max_width = 80 tab_spaces = 2 -edition = "2018" \ No newline at end of file +unstable_features = true +condense_wildcard_suffixes = true +newline_style = "Unix" +use_field_init_shorthand = true +use_try_shorthand = true +imports_granularity = "Crate" +group_imports = "StdExternalCrate" +reorder_imports = true diff --git a/deno_bindgen/Cargo.toml b/deno_bindgen/Cargo.toml index 47e84f9..26801c5 100644 --- a/deno_bindgen/Cargo.toml +++ b/deno_bindgen/Cargo.toml @@ -9,7 +9,7 @@ keywords = ["deno", "ffi", "bindgen", "bindings", "macro"] categories = ["development-tools::ffi", "development-tools"] readme = "../README.md" license = "MIT" -edition = "2018" +edition = "2021" [lib] path = "./lib.rs" diff --git a/deno_bindgen_macro/Cargo.toml b/deno_bindgen_macro/Cargo.toml index 9d0d30b..d3e4d95 100644 --- a/deno_bindgen_macro/Cargo.toml +++ b/deno_bindgen_macro/Cargo.toml @@ -9,7 +9,7 @@ keywords = ["deno", "ffi", "bindgen", "bindings", "macro"] categories = ["development-tools::ffi", "development-tools"] readme = "../README.md" license = "MIT" -edition = "2018" +edition = "2021" [lib] proc-macro = true diff --git a/deno_bindgen_macro/src/derive_fn.rs b/deno_bindgen_macro/src/derive_fn.rs index 65d5686..a06242c 100644 --- a/deno_bindgen_macro/src/derive_fn.rs +++ b/deno_bindgen_macro/src/derive_fn.rs @@ -8,6 +8,7 @@ use syn::FnArg; use syn::ItemFn; use syn::Meta; use syn::NestedMeta; +use syn::PathArguments; use syn::ReturnType; pub fn process_function( @@ -26,7 +27,6 @@ pub fn process_function( syn::Type::Path(ref ty) => { let segment = ty.path.segments.first().unwrap(); let ident = segment.ident.to_string(); - match ident.as_str() { "i8" => Type::I8, "u8" => Type::U8, @@ -113,6 +113,50 @@ pub fn process_function( // This isn't a &str but i really but // don't want to add another type for just owned strings. "String" => Type::Str, + "Box" => { + let mut buf = false; + let mut gn = String::default(); + if let PathArguments::AngleBracketed(args) = &segment.arguments { + if let Some(syn::GenericArgument::Type(syn::Type::Slice(args))) = + &args.args.first() + { + if let syn::Type::Path(args) = &*args.elem { + if let Some(args) = args.path.segments.first() { + gn = args.ident.to_string(); + if gn == "u8" { + buf = true; + } + } + } + } + } + if buf { + Type::Buffer + } else { + panic!("{}<{}> return type not supported by Deno FFI", ident, gn) + } + } + "Vec" => { + let mut buf = false; + let mut gn = String::default(); + if let PathArguments::AngleBracketed(args) = &segment.arguments { + if let Some(syn::GenericArgument::Type(syn::Type::Path(args))) = + &args.args.first() + { + if let Some(args) = &args.path.segments.first() { + gn = args.ident.to_string(); + if gn == "u8" { + buf = true; + } + } + } + } + if buf { + Type::Buffer + } else { + panic!("{}<{}> return type not supported by Deno FFI", ident, gn) + } + } _ => match metadata.type_defs.get(&ident) { Some(_) => Type::StructEnum { ident }, None => panic!("{} return type not supported by Deno FFI", ident), diff --git a/deno_bindgen_macro/src/lib.rs b/deno_bindgen_macro/src/lib.rs index de1623a..39cbbac 100644 --- a/deno_bindgen_macro/src/lib.rs +++ b/deno_bindgen_macro/src/lib.rs @@ -160,7 +160,7 @@ pub fn deno_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream { let transformer = quote! { let length = (result.len() as u32).to_be_bytes(); let mut v = length.to_vec(); - v.extend_from_slice(#slice); + v.extend_from_slice(&#slice); ::std::mem::forget(result); let result = v.as_ptr(); diff --git a/example/bindings/bindings.ts b/example/bindings/bindings.ts index bb815c1..05f2120 100644 --- a/example/bindings/bindings.ts +++ b/example/bindings/bindings.ts @@ -1,4 +1,6 @@ // Auto-generated with deno_bindgen +import { CachePolicy, prepare } from "https://deno.land/x/plug@0.5.2/plug.ts" + function encode(v: string | Uint8Array): Uint8Array { if (typeof v !== "string") return v return new TextEncoder().encode(v) @@ -19,98 +21,112 @@ function readPointer(v: any): Uint8Array { } const url = new URL("../target/debug", import.meta.url) - -let uri = url.pathname +let uri = url.toString() if (!uri.endsWith("/")) uri += "/" -// https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya#parameters -if (Deno.build.os === "windows") { - uri = uri.replace(/\//g, "\\") +let darwin: string | { aarch64: string; x86_64: string } = uri + + "libdeno_bindgen_test.dylib" + +if (url.protocol !== "file:") { + // Assume that remote assets follow naming scheme + // for each macOS artifact. + darwin = { + aarch64: uri + "libdeno_bindgen_test_arm64.dylib", + x86_64: uri + "libdeno_bindgen_test.dylib", + } } -const { symbols } = Deno.dlopen( - { - darwin: uri + "libdeno_bindgen_test.dylib", +const opts = { + name: "deno_bindgen_test", + urls: { + darwin, windows: uri + "deno_bindgen_test.dll", linux: uri + "libdeno_bindgen_test.so", - }[Deno.build.os], - { - add: { parameters: ["i32", "i32"], result: "i32", nonblocking: false }, - add2: { - parameters: ["buffer", "usize"], - result: "i32", - nonblocking: false, - }, - add3: { parameters: ["f32", "f32"], result: "f32", nonblocking: false }, - add4: { parameters: ["f64", "f64"], result: "f64", nonblocking: false }, - sleep: { parameters: ["u64"], result: "void", nonblocking: true }, - test_buf: { - parameters: ["buffer", "usize"], - result: "u8", - nonblocking: false, - }, - test_buffer_return: { - parameters: ["buffer", "usize"], - result: "buffer", - nonblocking: false, - }, - test_buffer_return_async: { - parameters: ["buffer", "usize"], - result: "buffer", - nonblocking: true, - }, - test_hashmap: { parameters: [], result: "buffer", nonblocking: false }, - test_lifetime: { - parameters: ["buffer", "usize"], - result: "usize", - nonblocking: false, - }, - test_manual_ptr: { parameters: [], result: "buffer", nonblocking: false }, - test_manual_ptr_async: { - parameters: [], - result: "buffer", - nonblocking: true, - }, - test_mixed: { - parameters: ["isize", "buffer", "usize"], - result: "i32", - nonblocking: false, - }, - test_mixed_order: { - parameters: ["i32", "buffer", "usize", "i32"], - result: "i32", - nonblocking: false, - }, - test_mut_buf: { - parameters: ["buffer", "usize"], - result: "void", - nonblocking: false, - }, - test_output: { parameters: [], result: "buffer", nonblocking: false }, - test_output_async: { parameters: [], result: "buffer", nonblocking: true }, - test_reserved_field: { - parameters: [], - result: "buffer", - nonblocking: false, - }, - test_serde: { - parameters: ["buffer", "usize"], - result: "u8", - nonblocking: false, - }, - test_str: { - parameters: ["buffer", "usize"], - result: "void", - nonblocking: false, - }, - test_str_ret: { parameters: [], result: "buffer", nonblocking: false }, - test_tag_and_content: { - parameters: ["buffer", "usize"], - result: "i32", - nonblocking: false, - }, }, -) + policy: CachePolicy.NONE, +} +const _lib = await prepare(opts, { + add: { parameters: ["i32", "i32"], result: "i32", nonblocking: false }, + add2: { parameters: ["pointer", "usize"], result: "i32", nonblocking: false }, + add3: { parameters: ["f32", "f32"], result: "f32", nonblocking: false }, + add4: { parameters: ["f64", "f64"], result: "f64", nonblocking: false }, + add5: { + parameters: ["buffer", "usize", "buffer", "usize"], + result: "buffer", + nonblocking: false, + }, + add6: { + parameters: ["buffer", "usize", "buffer", "usize"], + result: "buffer", + nonblocking: false, + }, + sleep: { parameters: ["u64"], result: "void", nonblocking: true }, + test_buf: { + parameters: ["buffer", "usize"], + result: "u8", + nonblocking: false, + }, + test_buffer_return: { + parameters: ["buffer", "usize"], + result: "buffer", + nonblocking: false, + }, + test_buffer_return_async: { + parameters: ["buffer", "usize"], + result: "buffer", + nonblocking: true, + }, + test_hashmap: { parameters: [], result: "pointer", nonblocking: false }, + test_lifetime: { + parameters: ["pointer", "usize"], + result: "usize", + nonblocking: false, + }, + test_manual_ptr: { parameters: [], result: "pointer", nonblocking: false }, + test_manual_ptr_async: { + parameters: [], + result: "pointer", + nonblocking: true, + }, + test_mixed: { + parameters: ["isize", "pointer", "usize"], + result: "i32", + nonblocking: false, + }, + test_mixed_order: { + parameters: ["i32", "pointer", "usize", "i32"], + result: "i32", + nonblocking: false, + }, + test_mut_buf: { + parameters: ["buffer", "usize"], + result: "void", + nonblocking: false, + }, + test_output: { parameters: [], result: "pointer", nonblocking: false }, + test_output_async: { parameters: [], result: "pointer", nonblocking: true }, + test_reserved_field: { + parameters: [], + result: "pointer", + nonblocking: false, + }, + test_serde: { + parameters: ["pointer", "usize"], + result: "u8", + nonblocking: false, + }, + test_str: { + parameters: ["pointer", "usize"], + result: "void", + nonblocking: false, + }, + test_str_ret: { parameters: [], result: "pointer", nonblocking: false }, + test_tag_and_content: { + parameters: ["pointer", "usize"], + result: "i32", + nonblocking: false, + }, +}) /** * Doc comment for `Input` struct. * ...testing multiline @@ -160,134 +176,168 @@ export type WithRecord = { my_map: Record } export function add(a0: number, a1: number) { - let rawResult = symbols.add(a0, a1) + let rawResult = _lib.symbols.add(a0, a1) const result = rawResult return result } export function add2(a0: Input) { const a0_buf = encode(JSON.stringify(a0)) - - let rawResult = symbols.add2(a0_buf, a0_buf.byteLength) + const a0_ptr = Deno.UnsafePointer.of(a0_buf) + let rawResult = _lib.symbols.add2(a0_ptr, a0_buf.byteLength) const result = rawResult return result } export function add3(a0: number, a1: number) { - let rawResult = symbols.add3(a0, a1) + let rawResult = _lib.symbols.add3(a0, a1) const result = rawResult return result } export function add4(a0: number, a1: number) { - let rawResult = symbols.add4(a0, a1) + let rawResult = _lib.symbols.add4(a0, a1) const result = rawResult return result } +export function add5(a0: Uint8Array, a1: Uint8Array) { + const a0_buf = encode(a0) + const a1_buf = encode(a1) + + let rawResult = _lib.symbols.add5( + a0_buf, + a0_buf.byteLength, + a1_buf, + a1_buf.byteLength, + ) + const result = readPointer(rawResult) + return result +} +export function add6(a0: Uint8Array, a1: Uint8Array) { + const a0_buf = encode(a0) + const a1_buf = encode(a1) + + let rawResult = _lib.symbols.add6( + a0_buf, + a0_buf.byteLength, + a1_buf, + a1_buf.byteLength, + ) + const result = readPointer(rawResult) + return result +} export function sleep(a0: bigint) { - let rawResult = symbols.sleep(a0) + let rawResult = _lib.symbols.sleep(a0) const result = rawResult return result } export function test_buf(a0: Uint8Array) { const a0_buf = encode(a0) - let rawResult = symbols.test_buf(a0_buf, a0_buf.byteLength) + let rawResult = _lib.symbols.test_buf(a0_buf, a0_buf.byteLength) const result = rawResult return result } export function test_buffer_return(a0: Uint8Array) { const a0_buf = encode(a0) - let rawResult = symbols.test_buffer_return(a0_buf, a0_buf.byteLength) + let rawResult = _lib.symbols.test_buffer_return(a0_buf, a0_buf.byteLength) const result = readPointer(rawResult) return result } export function test_buffer_return_async(a0: Uint8Array) { const a0_buf = encode(a0) - let rawResult = symbols.test_buffer_return_async(a0_buf, a0_buf.byteLength) + let rawResult = _lib.symbols.test_buffer_return_async( + a0_buf, + a0_buf.byteLength, + ) const result = rawResult.then(readPointer) return result } export function test_hashmap() { - let rawResult = symbols.test_hashmap() + let rawResult = _lib.symbols.test_hashmap() const result = readPointer(rawResult) return JSON.parse(decode(result)) as WithRecord } export function test_lifetime(a0: TestLifetimes) { const a0_buf = encode(JSON.stringify(a0)) - - let rawResult = symbols.test_lifetime(a0_buf, a0_buf.byteLength) + const a0_ptr = Deno.UnsafePointer.of(a0_buf) + let rawResult = _lib.symbols.test_lifetime(a0_ptr, a0_buf.byteLength) const result = rawResult return result } export function test_manual_ptr() { - let rawResult = symbols.test_manual_ptr() + let rawResult = _lib.symbols.test_manual_ptr() const result = readPointer(rawResult) return result } export function test_manual_ptr_async() { - let rawResult = symbols.test_manual_ptr_async() + let rawResult = _lib.symbols.test_manual_ptr_async() const result = rawResult.then(readPointer) return result } export function test_mixed(a0: bigint, a1: Input) { const a1_buf = encode(JSON.stringify(a1)) - - let rawResult = symbols.test_mixed(a0, a1_buf, a1_buf.byteLength) + const a1_ptr = Deno.UnsafePointer.of(a1_buf) + let rawResult = _lib.symbols.test_mixed(a0, a1_ptr, a1_buf.byteLength) const result = rawResult return result } export function test_mixed_order(a0: number, a1: Input, a2: number) { const a1_buf = encode(JSON.stringify(a1)) - - let rawResult = symbols.test_mixed_order(a0, a1_buf, a1_buf.byteLength, a2) + const a1_ptr = Deno.UnsafePointer.of(a1_buf) + let rawResult = _lib.symbols.test_mixed_order( + a0, + a1_ptr, + a1_buf.byteLength, + a2, + ) const result = rawResult return result } export function test_mut_buf(a0: Uint8Array) { const a0_buf = encode(a0) - let rawResult = symbols.test_mut_buf(a0_buf, a0_buf.byteLength) + let rawResult = _lib.symbols.test_mut_buf(a0_buf, a0_buf.byteLength) const result = rawResult return result } export function test_output() { - let rawResult = symbols.test_output() + let rawResult = _lib.symbols.test_output() const result = readPointer(rawResult) return JSON.parse(decode(result)) as Input } export function test_output_async() { - let rawResult = symbols.test_output_async() + let rawResult = _lib.symbols.test_output_async() const result = rawResult.then(readPointer) return result.then(r => JSON.parse(decode(r))) as Promise } export function test_reserved_field() { - let rawResult = symbols.test_reserved_field() + let rawResult = _lib.symbols.test_reserved_field() const result = readPointer(rawResult) return JSON.parse(decode(result)) as TestReservedField } export function test_serde(a0: MyStruct) { const a0_buf = encode(JSON.stringify(a0)) - - let rawResult = symbols.test_serde(a0_buf, a0_buf.byteLength) + const a0_ptr = Deno.UnsafePointer.of(a0_buf) + let rawResult = _lib.symbols.test_serde(a0_ptr, a0_buf.byteLength) const result = rawResult return result } export function test_str(a0: string) { const a0_buf = encode(a0) - - let rawResult = symbols.test_str(a0_buf, a0_buf.byteLength) + const a0_ptr = Deno.UnsafePointer.of(a0_buf) + let rawResult = _lib.symbols.test_str(a0_ptr, a0_buf.byteLength) const result = rawResult return result } export function test_str_ret() { - let rawResult = symbols.test_str_ret() + let rawResult = _lib.symbols.test_str_ret() const result = readPointer(rawResult) return decode(result) } export function test_tag_and_content(a0: TagAndContent) { const a0_buf = encode(JSON.stringify(a0)) - - let rawResult = symbols.test_tag_and_content(a0_buf, a0_buf.byteLength) + const a0_ptr = Deno.UnsafePointer.of(a0_buf) + let rawResult = _lib.symbols.test_tag_and_content(a0_ptr, a0_buf.byteLength) const result = rawResult return result } diff --git a/example/src/lib.rs b/example/src/lib.rs index 22c2a9a..75c0aca 100644 --- a/example/src/lib.rs +++ b/example/src/lib.rs @@ -34,6 +34,16 @@ fn add4(a: f64, b: f64) -> f64 { a + b } +#[deno_bindgen] +fn add5(a: &[u8], b: &[u8]) -> Vec { + [a, b].concat() +} + +#[deno_bindgen] +fn add6(a: &[u8], b: &[u8]) -> Box<[u8]> { + [a, b].concat().into() +} + // Test mixed types #[deno_bindgen] fn test_mixed(a: isize, b: Input) -> i32 {