From be230136bb8226fbb75f534c8a26c953dabe4037 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 26 Aug 2021 16:44:19 +0530 Subject: [PATCH 1/6] rewrite: basic string, buffer & struct support --- cli.ts | 13 +- codegen.ts | 326 +++++++---------------------------- example/bindings/bindings.ts | 30 ++-- example/src/lib.rs | 13 +- src/lib.rs | 159 +++++++++++++---- 5 files changed, 228 insertions(+), 313 deletions(-) diff --git a/cli.ts b/cli.ts index 26a2883..6efc7d1 100644 --- a/cli.ts +++ b/cli.ts @@ -23,16 +23,19 @@ if (Deno.build.os == "windows") { let source = null; async function generate() { + let conf; try { - const conf = JSON.parse(await Deno.readTextFile("bindings.json")); - const pkgName = conf.name; - source = "// Auto-generated with deno_bindgen\n"; - - source += codegen(`target/${profile}/lib${pkgName}${ext}`, conf.bindings); + conf = JSON.parse(await Deno.readTextFile("bindings.json")); } catch (_) { // Nothing to update. return; } + + console.log(conf); + const pkgName = conf.name; + + source = "// Auto-generated with deno_bindgen\n"; + source += codegen(`target/${profile}/lib${pkgName}${ext}`, conf.type_defs, conf.bindings); await Deno.remove("bindings.json"); } diff --git a/codegen.ts b/codegen.ts index 1b0675b..fb3d7e7 100644 --- a/codegen.ts +++ b/codegen.ts @@ -16,257 +16,25 @@ const Type: Record = { f64: "number", }; -function invalidType(type: string) { - throw new TypeError(`Type not supported: ${type}`); -} - -const span = { start: 0, end: 0, ctxt: 0 }; - -function param(value: string, type: string) { - const kind = Type[type] || invalidType(type); - return { - type: "Parameter", - span, - decorators: [], - pat: { - type: "Identifier", - span, - value, - optional: false, - typeAnnotation: { - type: "TsTypeAnnotation", - span, - typeAnnotation: { - type: "TsKeywordType", - span, - kind, - }, - }, - }, - }; -} - -function bodyStmt(fn: Sig) { - return [ - { - type: "ReturnStatement", - span, - argument: { - type: "TsAsExpression", - span, - typeAnnotation: { - type: "TsKeywordType", - span, - kind: Type[fn.result] || invalidType(fn.result), - }, - expression: { - type: "CallExpression", - span, - callee: { - type: "MemberExpression", - span, - object: { - type: "MemberExpression", - span, - object: { - type: "Identifier", - span, - value: "_lib", - optional: false, - }, - property: { - type: "Identifier", - span, - value: "symbols", - optional: false, - }, - computed: false, - }, - property: { - type: "Identifier", - span, - value: fn.func, - optional: false, - }, - computed: false, - }, - arguments: fn.parameters.map((i) => { - return { - spread: null, - expression: { - type: "Identifier", - span, - value: i.ident, - }, - }; - }), - typeArguments: null, - }, - }, - }, - ]; -} +type TypeDef = { + fields: Record; + ident: string; +}; -function libDecl(target: string, signature: Sig[]) { - return { - type: "VariableDeclaration", - span, - kind: "const", - declare: false, - declarations: [ - { - type: "VariableDeclarator", - span, - id: { - type: "Identifier", - span, - value: "_lib", - optional: false, - typeAnnotation: null, - }, - init: { - type: "CallExpression", - span, - callee: { - type: "MemberExpression", - span, - object: { - type: "Identifier", - span, - value: "Deno", - optional: false, - }, - property: { - type: "Identifier", - span, - value: "dlopen", - optional: false, - }, - computed: false, - }, - arguments: [ - { - spread: null, - expression: { - type: "StringLiteral", - span, - value: target, - hasEscape: false, - kind: { type: "normal", containsQuote: true }, - }, - }, - { - spread: null, - expression: { - type: "ObjectExpression", - span, - properties: signature.map((sig) => { - return { - type: "KeyValueProperty", - key: { - type: "Identifier", - span, - value: sig.func, - optional: false, - }, - value: { - type: "ObjectExpression", - span, - properties: [ - { - type: "KeyValueProperty", - key: { - type: "Identifier", - span, - value: "result", - optional: false, - }, - value: { - type: "StringLiteral", - span, - value: sig.result, - hasEscape: false, - kind: { type: "normal", containsQuote: true }, - }, - }, - { - type: "KeyValueProperty", - key: { - type: "Identifier", - span, - value: "parameters", - optional: false, - }, - value: { - type: "ArrayExpression", - span, - elements: sig.parameters.map((p) => { - return { - spread: null, - expression: { - type: "StringLiteral", - span, - value: p.type, - hasEscape: false, - kind: { - type: "normal", - containsQuote: true, - }, - }, - }; - }), - }, - }, - ], - }, - }; - }), - }, - }, - ], - typeArguments: null, - }, - definite: false, - }, - ], - }; +function resolveType(typeDefs: TypeDef[], type: string): string { + if (Type[type] !== undefined) return Type[type]; + if (typeDefs.find((f) => f.ident == type) !== undefined) { + return type; + } + throw new TypeError(`Type not supported: ${type}`); } -function exportDecl(fn: Sig) { - return { - type: "ExportDeclaration", - span, - declaration: { - type: "FunctionDeclaration", - identifier: { - type: "Identifier", - span, - value: fn.func, - optional: false, - }, - declare: false, - params: fn.parameters.map((p) => param(p.ident, p.type)), - decorators: [], - span, - body: { - type: "BlockStatement", - span, - stmts: bodyStmt(fn), - }, - generator: false, - async: false, - typeParameters: null, - returnType: { - type: "TsTypeAnnotation", - span, - typeAnnotation: { - type: "TsKeywordType", - span, - kind: Type[fn.result] || invalidType(fn.result), - }, - }, - }, - }; +function resolveDlopenParameter(typeDefs: TypeDef[], type: string): string { + if (Type[type] !== undefined) return type; + if (typeDefs.find((f) => f.ident == type) !== undefined) { + return "buffer"; + } + throw new TypeError(`Type not supported: ${type}`); } type Sig = { @@ -275,16 +43,56 @@ type Sig = { result: string; }; -export function codegen(dylib: string, signature: Sig[]) { - const { code } = print({ - type: "Module", - span, - body: [ - libDecl(dylib, signature), - ...signature.map((e) => exportDecl(e)), - ], - interpreter: null, - }); +export function codegen(target: string, decl: TypeDef[], signature: Sig[]) { + return `import { Struct, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64 } from "https://deno.land/x/byte_type/mod.ts"; +const usize = u64; +const isize = i64; - return code; +const _lib = Deno.dlopen("${target}", { ${ + signature.map((sig) => + `${sig.func}: { parameters: [ ${ + sig.parameters.map((p) => `"${resolveDlopenParameter(decl, p.type)}"`) + .join(", ") + } ], result: "${sig.result}" }` + ).join(", ") + } }); +${ + decl.map((def) => + `type ${def.ident} = { ${ + Object.keys(def.fields).map((f) => + `${f}: ${resolveType(decl, def.fields[f])}` + ).join("; ") + } };` + ).join("\n") + } +${ + decl.map((def) => + `const _${def.ident} = new Struct({ ${ + Object.keys(def.fields).map((f) => `${f}: ${def.fields[f]}`).join(", ") + } });` + ).join("\n") + } +${ + signature.map((sig) => + `export function ${sig.func}(${ + sig.parameters.map((p) => `${p.ident}: ${resolveType(decl, p.type)}`) + .join(", ") + }) { + ${ + sig.parameters.filter((p) => Type[p.type] == undefined).map((p) => + `const _buf_${p.ident} = new Uint8Array(_${p.type}.size); + const _view_${p.ident} = new DataView(_buf_${p.ident}.buffer); + _${p.type}.write(_view_${p.ident}, 0, ${p.ident});` + ).join("\n") + } + const _result = _lib.symbols.${sig.func}(${ + sig.parameters.map((p) => + Type[p.type] == undefined ? ` _buf_${p.ident}` : p.ident + ).join(", ") + }); + return _result as ${resolveType(decl, sig.result)}; +}` + ).join("\n") + } + `; } diff --git a/example/bindings/bindings.ts b/example/bindings/bindings.ts index daa77cc..a033e7b 100644 --- a/example/bindings/bindings.ts +++ b/example/bindings/bindings.ts @@ -1,13 +1,21 @@ // Auto-generated with deno_bindgen -const _lib = Deno.dlopen("target/debug/libadd.so", { - add: { - result: "i32", - parameters: [ - "i32", - "i32", - ], - }, -}); -export function add(a0: number, a1: number): number { - return _lib.symbols.add(a0, a1) as number; +import { Struct, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64 } from "https://deno.land/x/byte_type/mod.ts"; +const usize = u64; +const isize = i64; + +const _lib = Deno.dlopen("target/debug/libadd.so", { add: { parameters: [ "i32", "i32" ], result: "i32" }, add2: { parameters: [ "buffer" ], result: "i32" } }); +type Input = { a: number; b: number }; +const _Input = new Struct({ a: i32, b: i32 }); +export function add(a0: number, a1: number) { + + const _result = _lib.symbols.add(a0, a1); + return _result as number; } +export function add2(a0: Input) { + const _buf_a0 = new Uint8Array(_Input.size); + const _view_a0 = new DataView(_buf_a0.buffer); + _Input.write(_view_a0, 0, a0); + const _result = _lib.symbols.add2( _buf_a0); + return _result as number; +} + \ No newline at end of file diff --git a/example/src/lib.rs b/example/src/lib.rs index 40c7969..11d4ba3 100644 --- a/example/src/lib.rs +++ b/example/src/lib.rs @@ -2,5 +2,16 @@ use deno_bindgen::deno_bindgen; #[deno_bindgen] fn add(a: i32, b: i32) -> i32 { - a + b + a + b } + +#[deno_bindgen] +pub struct Input { + a: i32, + b: i32, +} + +#[deno_bindgen] +fn add2(input: Input) -> i32 { + input.a + input.b +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index dd7cb8d..ac4d535 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,16 +8,20 @@ use serde_json::json; use std::fs::OpenOptions; use std::io::Read; use std::io::Write; +use syn::Data; +use syn::DataStruct; +use syn::Fields; +use syn::ItemFn; #[derive(Serialize, Deserialize, Default)] struct Bindings { name: String, bindings: Vec, + type_defs: Vec, } #[proc_macro_attribute] pub fn deno_bindgen(_attr: TokenStream, input: TokenStream) -> TokenStream { - let func = syn::parse_macro_input!(input as syn::ItemFn); let mut buf = String::new(); // Load existing bindings match OpenOptions::new().read(true).open("bindings.json") { @@ -37,48 +41,129 @@ pub fn deno_bindgen(_attr: TokenStream, input: TokenStream) -> TokenStream { .open("bindings.json") .unwrap(); - let mut parameters = vec![]; let pkg_name = std::env::var("CARGO_CRATE_NAME").unwrap(); - for (idx, i) in func.sig.inputs.iter().enumerate() { - match i { - syn::FnArg::Typed(ref val) => match &*val.ty { - syn::Type::Path(ref ty) => { - for seg in &ty.path.segments { - let ident = format!("a{}", idx); - parameters.push(json!({ - "ident": ident, - "type": seg.ident.to_string(), - })); + + match syn::parse::(input.clone()) { + // + Ok(func) => { + let mut parameters = vec![]; + + let fn_name = &func.sig.ident; + let fn_inputs = &func.sig.inputs; + let fn_output = &func.sig.output; + let fn_block = &func.block; + let fn_params: Vec<_> = fn_inputs + .iter() + .enumerate() + .map(|(idx, i)| match i { + syn::FnArg::Typed(ref val) => { + match &*val.ty { + syn::Type::Path(ref ty) => { + for seg in &ty.path.segments { + let ident = format!("a{}", idx); + parameters.push(json!( + { + "ident": ident, + "type": type_identifier(&bindings, &seg.ident.to_string()), + } + )); + } + } + _ => {} + }; + + &val.pat } + _ => unimplemented!(), + }) + .collect(); + + let return_type = match &func.sig.output { + syn::ReturnType::Default => "void".to_string(), + syn::ReturnType::Type(_, box syn::Type::Path(ty)) => { + // TODO(@littledivy): Support multiple `Type` path segments + ty.path.segments[0].ident.to_string() } - _ => {} - }, - _ => unreachable!(), - } - } + _ => unimplemented!(), + }; + + bindings.bindings.push(json!( + { + "func": func.sig.ident.to_string(), + "parameters": parameters, + "result": return_type, + } + )); - let return_type = match &func.sig.output { - syn::ReturnType::Default => "void".to_string(), - syn::ReturnType::Type(_, box syn::Type::Path(ty)) => { - // TODO(@littledivy): Support multiple `Type` path segments - ty.path.segments[0].ident.to_string() + bindings.name = pkg_name.to_string(); + config + .write_all(&serde_json::to_vec(&bindings).unwrap()) + .unwrap(); + + TokenStream::from(quote! { + #[no_mangle] + pub extern "C" fn #fn_name (#fn_inputs) #fn_output { + fn __inner_impl (#fn_inputs) #fn_output #fn_block + let result = __inner_impl(#(#fn_params,) *); + result + } + }) } - _ => panic!("Type not supported"), - }; + Err(_) => { + // Try to parse as an DeriveInput + let input = syn::parse_macro_input!(input as syn::DeriveInput); + let fields = match &input.data { + Data::Struct(DataStruct { + fields: Fields::Named(fields), + .. + }) => &fields.named, + _ => panic!("expected a struct with named fields"), + }; + let struct_name = &input.ident; + let mut definition = json!({}); - bindings.bindings.push(json!({ - "func": func.sig.ident.to_string(), - "parameters": parameters, - "result": return_type, + for field in fields.iter() { + if let Some(ident) = &field.ident { + match field.ty { + syn::Type::Path(ref ty) => { + for seg in &ty.path.segments { + definition[ident.to_string()] = + serde_json::Value::String(seg.ident.to_string()); + } + } + _ => {} + } + } + } + + bindings.type_defs.push( + json!({ "ident": struct_name.to_string(), "fields": definition }), + ); + config + .write_all(&serde_json::to_vec(&bindings).unwrap()) + .unwrap(); + TokenStream::from(quote! { + // Preserve the input + #[repr(C)] + #input + }) } - )); - bindings.name = pkg_name.to_string(); - config - .write_all(&serde_json::to_vec(&bindings).unwrap()) - .unwrap(); + } +} - TokenStream::from(quote! { - #[no_mangle] - pub extern "C" #func - }) +fn type_identifier(bindings: &Bindings, ty: &str) -> String { + match ty { + "void" | "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" + | "usize" | "isize" | "f32" | "f64" => ty.to_string(), + _ => { + // Check if a type definition already exists + bindings + .type_defs + .iter() + .find(|&def| def["ident"] == ty) + .expect(&format!("Type definition not found for `{}` identifier", ty)); + ty.to_string() + } + } } + \ No newline at end of file From 67dee2077796c0cc254c388b8c639b5b4ed3ca2c Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 26 Aug 2021 22:24:12 +0530 Subject: [PATCH 2/6] some more works --- src/lib.rs | 63 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ac4d535..5b127d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,24 +47,49 @@ pub fn deno_bindgen(_attr: TokenStream, input: TokenStream) -> TokenStream { // Ok(func) => { let mut parameters = vec![]; + let mut foriegn_types = vec![]; let fn_name = &func.sig.ident; let fn_inputs = &func.sig.inputs; let fn_output = &func.sig.output; let fn_block = &func.block; let fn_params: Vec<_> = fn_inputs - .iter() + .iter_mut() .enumerate() .map(|(idx, i)| match i { - syn::FnArg::Typed(ref val) => { + syn::FnArg::Typed(ref mut val) => { match &*val.ty { - syn::Type::Path(ref ty) => { + syn::Type::Path(ref mut ty) => { for seg in &ty.path.segments { let ident = format!("a{}", idx); + let ident_str = seg.ident.to_string(); + let ty = match ident_str.as_str() { + "void" | "i8" | "u8" | "i16" | "u16" | "i32" | "u32" + | "i64" | "u64" | "usize" | "isize" | "f32" | "f64" => { + ident_str + } + _ => { + // Check if a type definition already exists + bindings + .type_defs + .iter() + .find(|&def| def["ident"] == ident_str) + .expect(&format!( + "Type definition not found for `{}` identifier", + &ident_str + )); + foriegn_types.push(( + &val.pat, + quote::format_ident!("{}", ident_str), + )); + + ident_str + } + }; parameters.push(json!( { "ident": ident, - "type": type_identifier(&bindings, &seg.ident.to_string()), + "type": ty, } )); } @@ -99,11 +124,22 @@ pub fn deno_bindgen(_attr: TokenStream, input: TokenStream) -> TokenStream { config .write_all(&serde_json::to_vec(&bindings).unwrap()) .unwrap(); - + // panic!("{:#?}", foriegn_types); + let overrides = foriegn_types.iter().map(|(ident, ty)| quote! { + let mut _struct: #ty = unsafe { ::std::mem::zeroed() }; + let _size = ::std::mem::size_of::< #ty >(); + unsafe { + let _slice = ::std::slice::from_raw_parts_mut(&mut _struct as *mut _ as *mut u8, _size); + #ident.read_exact(_slice).unwrap(); + } + let #ident = _struct; + }) + .fold(quote! {}, |acc, new| quote! { #acc #new }); TokenStream::from(quote! { #[no_mangle] pub extern "C" fn #fn_name (#fn_inputs) #fn_output { fn __inner_impl (#fn_inputs) #fn_output #fn_block + #overrides let result = __inner_impl(#(#fn_params,) *); result } @@ -150,20 +186,3 @@ pub fn deno_bindgen(_attr: TokenStream, input: TokenStream) -> TokenStream { } } } - -fn type_identifier(bindings: &Bindings, ty: &str) -> String { - match ty { - "void" | "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" - | "usize" | "isize" | "f32" | "f64" => ty.to_string(), - _ => { - // Check if a type definition already exists - bindings - .type_defs - .iter() - .find(|&def| def["ident"] == ty) - .expect(&format!("Type definition not found for `{}` identifier", ty)); - ty.to_string() - } - } -} - \ No newline at end of file From 5ee825dfb5569b67bb2b06c2a23d2a5ba4529f9d Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 7 Oct 2021 13:34:28 +0000 Subject: [PATCH 3/6] work --- example/add_test.ts | 10 ++++++++- example/bindings/bindings.ts | 4 ++-- example/src/lib.rs | 4 ++-- src/lib.rs | 42 ++++++++++++++++++++---------------- 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/example/add_test.ts b/example/add_test.ts index 0940985..cf78f7f 100644 --- a/example/add_test.ts +++ b/example/add_test.ts @@ -1,4 +1,4 @@ -import { add } from "./bindings/bindings.ts"; +import { add, add2 } from "./bindings/bindings.ts"; import { assertEquals } from "https://deno.land/std/testing/asserts.ts"; Deno.test({ @@ -8,3 +8,11 @@ Deno.test({ assertEquals(add(-1, 1), 0); }, }); + +Deno.test({ + name: "add2#test", + fn: () => { + assertEquals(add2({ a: 1, b: 2 }), 3); + + }, +}); diff --git a/example/bindings/bindings.ts b/example/bindings/bindings.ts index a033e7b..a81f7d0 100644 --- a/example/bindings/bindings.ts +++ b/example/bindings/bindings.ts @@ -1,11 +1,11 @@ // Auto-generated with deno_bindgen -import { Struct, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64 } from "https://deno.land/x/byte_type/mod.ts"; +import { Struct, i8, u8, i16, u16, i32le, u32, i64, u64, f32, f64 } from "https://deno.land/x/byte_type/mod.ts"; const usize = u64; const isize = i64; const _lib = Deno.dlopen("target/debug/libadd.so", { add: { parameters: [ "i32", "i32" ], result: "i32" }, add2: { parameters: [ "buffer" ], result: "i32" } }); type Input = { a: number; b: number }; -const _Input = new Struct({ a: i32, b: i32 }); +const _Input = new Struct({ a: i32le, b: i32le }); export function add(a0: number, a1: number) { const _result = _lib.symbols.add(a0, a1); diff --git a/example/src/lib.rs b/example/src/lib.rs index 11d4ba3..8e1441f 100644 --- a/example/src/lib.rs +++ b/example/src/lib.rs @@ -13,5 +13,5 @@ pub struct Input { #[deno_bindgen] fn add2(input: Input) -> i32 { - input.a + input.b -} \ No newline at end of file + input.a + input.b +} diff --git a/src/lib.rs b/src/lib.rs index 5b127d9..b614903 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,16 +51,18 @@ pub fn deno_bindgen(_attr: TokenStream, input: TokenStream) -> TokenStream { let fn_name = &func.sig.ident; let fn_inputs = &func.sig.inputs; + let mut inputs = vec![]; let fn_output = &func.sig.output; let fn_block = &func.block; let fn_params: Vec<_> = fn_inputs - .iter_mut() + .iter() .enumerate() .map(|(idx, i)| match i { - syn::FnArg::Typed(ref mut val) => { - match &*val.ty { - syn::Type::Path(ref mut ty) => { - for seg in &ty.path.segments { + syn::FnArg::Typed(ref val) => { + let mut val = val.clone(); + match *val.ty { + syn::Type::Path(ref ty) => { + for seg in ty.path.segments.clone() { let ident = format!("a{}", idx); let ident_str = seg.ident.to_string(); let ty = match ident_str.as_str() { @@ -79,10 +81,10 @@ pub fn deno_bindgen(_attr: TokenStream, input: TokenStream) -> TokenStream { &ident_str )); foriegn_types.push(( - &val.pat, + val.pat.clone(), quote::format_ident!("{}", ident_str), )); - + val.ty = Box::new(syn::Type::Ptr(syn::parse_quote! { *const u8 })); ident_str } }; @@ -97,7 +99,8 @@ pub fn deno_bindgen(_attr: TokenStream, input: TokenStream) -> TokenStream { _ => {} }; - &val.pat + + inputs.push(val); } _ => unimplemented!(), }) @@ -124,23 +127,26 @@ pub fn deno_bindgen(_attr: TokenStream, input: TokenStream) -> TokenStream { config .write_all(&serde_json::to_vec(&bindings).unwrap()) .unwrap(); - // panic!("{:#?}", foriegn_types); + let overrides = foriegn_types.iter().map(|(ident, ty)| quote! { - let mut _struct: #ty = unsafe { ::std::mem::zeroed() }; - let _size = ::std::mem::size_of::< #ty >(); - unsafe { - let _slice = ::std::slice::from_raw_parts_mut(&mut _struct as *mut _ as *mut u8, _size); - #ident.read_exact(_slice).unwrap(); - } - let #ident = _struct; + let _size = std::mem::size_of::<#ty>(); + let buf = unsafe { std::slice::from_raw_parts(#ident, _size) }; + + let #ident: #ty = unsafe { std::ptr::read(buf.as_ptr() as *const _) }; + + + // println!("{:#?}", buf); + // unsafe { std::mem::transmute_copy(&buf.as_ptr()) }; + println!("{:#?}", #ident.a); }) .fold(quote! {}, |acc, new| quote! { #acc #new }); + let input_idents: Vec<_> = inputs.iter().map(|i| &i.pat).collect(); TokenStream::from(quote! { #[no_mangle] - pub extern "C" fn #fn_name (#fn_inputs) #fn_output { + pub extern "C" fn #fn_name (#(#inputs,) *) #fn_output { fn __inner_impl (#fn_inputs) #fn_output #fn_block #overrides - let result = __inner_impl(#(#fn_params,) *); + let result = __inner_impl(#(#input_idents,) *); result } }) From c6fe48f7888796e1c5edf24286029f032cdfa592 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 7 Oct 2021 15:12:46 +0000 Subject: [PATCH 4/6] fix type issue --- .github/workflows/ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a94a688..25038aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,4 @@ jobs: run: | cd example deno run -A ../cli.ts - deno test --allow-ffi --unstable - - + deno test --no-check --allow-ffi --unstable From 907ca7f7c8469e3d779e663f92ebc870c846bb0f Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 7 Oct 2021 16:06:37 +0000 Subject: [PATCH 5/6] impl --- README.md | 13 ++++++++--- cli.ts | 14 +++++++++--- codegen.ts | 43 ++++++++++++++++++++++++++++++++---- example/add_test.ts | 1 - example/bindings/bindings.ts | 33 ++++++++++++++++++++++----- src/lib.rs | 22 ++++++++++++------ 6 files changed, 102 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index cf3bcb4..7ffb6fe 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,14 @@ deno_bindgen = { git = "https://github.com/littledivy/deno_bindgen" } use deno_bindgen::deno_bindgen; #[deno_bindgen] -fn add(a: i32, b: i32) -> i32 { - a + b +pub struct Input { + a: i32, + b: i32, +} + +#[deno_bindgen] +fn add(input: Input) -> i32 { + input.a + input.b } ``` @@ -33,5 +39,6 @@ $ deno_bindgen ```typescript // add.ts import { add } from "./bindings/bindings.ts"; -add(1, 2); // 3 + +add({ a: 1, b: 2 }); // 3 ``` diff --git a/cli.ts b/cli.ts index 6efc7d1..1d7d93f 100644 --- a/cli.ts +++ b/cli.ts @@ -25,17 +25,24 @@ let source = null; async function generate() { let conf; try { - conf = JSON.parse(await Deno.readTextFile("bindings.json")); + conf = JSON.parse(await Deno.readTextFile("bindings.json")); } catch (_) { // Nothing to update. return; } - + console.log(conf); const pkgName = conf.name; source = "// Auto-generated with deno_bindgen\n"; - source += codegen(`target/${profile}/lib${pkgName}${ext}`, conf.type_defs, conf.bindings); + source += codegen( + `target/${profile}/lib${pkgName}${ext}`, + conf.type_defs, + conf.bindings, + { + le: conf.le, + } + ); await Deno.remove("bindings.json"); } @@ -43,6 +50,7 @@ await build(); await generate(); if (source != null) { + await ensureDir("bindings"); await Deno.writeTextFile("bindings/bindings.ts", source); } diff --git a/codegen.ts b/codegen.ts index fb3d7e7..4b4cf02 100644 --- a/codegen.ts +++ b/codegen.ts @@ -43,11 +43,46 @@ type Sig = { result: string; }; -export function codegen(target: string, decl: TypeDef[], signature: Sig[]) { - return `import { Struct, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64 } from "https://deno.land/x/byte_type/mod.ts"; -const usize = u64; -const isize = i64; +type Options = { + le?: boolean; +}; + +function createByteTypeImport(le?: boolean) { + // Endianess dependent types to be imported from the `byte_type` module. + let types = [ + "i16", + "u16", + "i32", + "u32", + "i64", + "u64", + "f32", + "f64", + ]; + + // Finalize type name based on endianness. + const typeImports = types.map((ty) => ty + (le ? "le" : "be")); + + // TODO(@littledivy): version imports + let code = `import { Struct, i8, u8, ${ + typeImports.join(", ") + } } from "https://deno.land/x/byte_type/mod.ts";\n`; + + code += types.map((ty, idx) => `const ${ty} = ${typeImports[idx]};`).join('\n'); + + code += `\nconst usize = u64;\n`; + code += `const isize = i64;\n`; + + return code; +} +export function codegen( + target: string, + decl: TypeDef[], + signature: Sig[], + options?: Options, +) { + return `${createByteTypeImport(options?.le)} const _lib = Deno.dlopen("${target}", { ${ signature.map((sig) => `${sig.func}: { parameters: [ ${ diff --git a/example/add_test.ts b/example/add_test.ts index cf78f7f..16da8d1 100644 --- a/example/add_test.ts +++ b/example/add_test.ts @@ -13,6 +13,5 @@ Deno.test({ name: "add2#test", fn: () => { assertEquals(add2({ a: 1, b: 2 }), 3); - }, }); diff --git a/example/bindings/bindings.ts b/example/bindings/bindings.ts index a81f7d0..b37d954 100644 --- a/example/bindings/bindings.ts +++ b/example/bindings/bindings.ts @@ -1,13 +1,35 @@ // Auto-generated with deno_bindgen -import { Struct, i8, u8, i16, u16, i32le, u32, i64, u64, f32, f64 } from "https://deno.land/x/byte_type/mod.ts"; +import { + f32le, + f64le, + i16le, + i32le, + i64le, + i8, + Struct, + u16le, + u32le, + u64le, + u8, +} from "https://deno.land/x/byte_type/mod.ts"; +const i16 = i16le; +const u16 = u16le; +const i32 = i32le; +const u32 = u32le; +const i64 = i64le; +const u64 = u64le; +const f32 = f32le; +const f64 = f64le; const usize = u64; const isize = i64; -const _lib = Deno.dlopen("target/debug/libadd.so", { add: { parameters: [ "i32", "i32" ], result: "i32" }, add2: { parameters: [ "buffer" ], result: "i32" } }); +const _lib = Deno.dlopen("target/debug/libadd.so", { + add: { parameters: ["i32", "i32"], result: "i32" }, + add2: { parameters: ["buffer"], result: "i32" }, +}); type Input = { a: number; b: number }; -const _Input = new Struct({ a: i32le, b: i32le }); +const _Input = new Struct({ a: i32, b: i32 }); export function add(a0: number, a1: number) { - const _result = _lib.symbols.add(a0, a1); return _result as number; } @@ -15,7 +37,6 @@ export function add2(a0: Input) { const _buf_a0 = new Uint8Array(_Input.size); const _view_a0 = new DataView(_buf_a0.buffer); _Input.write(_view_a0, 0, a0); - const _result = _lib.symbols.add2( _buf_a0); + const _result = _lib.symbols.add2(_buf_a0); return _result as number; } - \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index b614903..3237128 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,9 +13,18 @@ use syn::DataStruct; use syn::Fields; use syn::ItemFn; +fn is_le() -> bool { + #[cfg(target_endian = "little")] + return true; + + #[cfg(target_endian = "big")] + return false; +} + #[derive(Serialize, Deserialize, Default)] struct Bindings { name: String, + le: bool, bindings: Vec, type_defs: Vec, } @@ -23,6 +32,7 @@ struct Bindings { #[proc_macro_attribute] pub fn deno_bindgen(_attr: TokenStream, input: TokenStream) -> TokenStream { let mut buf = String::new(); + // Load existing bindings match OpenOptions::new().read(true).open("bindings.json") { Ok(mut fd) => { @@ -32,7 +42,9 @@ pub fn deno_bindgen(_attr: TokenStream, input: TokenStream) -> TokenStream { // We assume this was the first macro run. } } + let mut bindings: Bindings = serde_json::from_str(&buf).unwrap_or_default(); + bindings.le = is_le(); // TODO(@littledivy): Use Cargo's `out` directory // let dir = Path::new(env!("PROC_ARTIFACT_DIR")); let mut config = OpenOptions::new() @@ -84,7 +96,9 @@ pub fn deno_bindgen(_attr: TokenStream, input: TokenStream) -> TokenStream { val.pat.clone(), quote::format_ident!("{}", ident_str), )); - val.ty = Box::new(syn::Type::Ptr(syn::parse_quote! { *const u8 })); + val.ty = Box::new(syn::Type::Ptr( + syn::parse_quote! { *const u8 }, + )); ident_str } }; @@ -99,7 +113,6 @@ pub fn deno_bindgen(_attr: TokenStream, input: TokenStream) -> TokenStream { _ => {} }; - inputs.push(val); } _ => unimplemented!(), @@ -133,11 +146,6 @@ pub fn deno_bindgen(_attr: TokenStream, input: TokenStream) -> TokenStream { let buf = unsafe { std::slice::from_raw_parts(#ident, _size) }; let #ident: #ty = unsafe { std::ptr::read(buf.as_ptr() as *const _) }; - - - // println!("{:#?}", buf); - // unsafe { std::mem::transmute_copy(&buf.as_ptr()) }; - println!("{:#?}", #ident.a); }) .fold(quote! {}, |acc, new| quote! { #acc #new }); let input_idents: Vec<_> = inputs.iter().map(|i| &i.pat).collect(); From 7e8a0baa3810ee30b07d76a017ede9559ebcc8dd Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 7 Oct 2021 16:08:03 +0000 Subject: [PATCH 6/6] use deno canary for buffer support --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25038aa..3851859 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: - uses: denoland/setup-deno@v1 with: - deno-version: v1.13.1 + deno-version: ab2e0a465e4eafe4de2cc6ac7ef61d1655db4c2d - name: Install rust uses: hecrj/setup-rust-action@v1