diff --git a/deno_bindgen/lib.rs b/deno_bindgen/lib.rs index 523d2ef..e4b6e1f 100644 --- a/deno_bindgen/lib.rs +++ b/deno_bindgen/lib.rs @@ -7,7 +7,11 @@ pub use linkme; use linkme::distributed_slice; #[distributed_slice] -pub static INVENTORY: [Symbol]; +pub static INVENTORY: [Inventory]; + +pub trait BindgenType { + fn type_name() -> &'static str; +} #[no_mangle] fn init_deno_bindgen(opt: Options) { diff --git a/deno_bindgen_ir/codegen/deno.rs b/deno_bindgen_ir/codegen/deno.rs index 64b5cfb..0818c2b 100644 --- a/deno_bindgen_ir/codegen/deno.rs +++ b/deno_bindgen_ir/codegen/deno.rs @@ -3,8 +3,10 @@ use std::{ io::{Result, Write}, }; +use syn::token::In; + use super::Generator; -use crate::{Symbol, Type}; +use crate::{inventory::Inventory, Symbol, Type}; struct TypeScriptType<'a>(&'a str); @@ -93,11 +95,11 @@ impl From for DenoFfiType { } pub struct Codegen<'a> { - symbols: &'a [Symbol], + symbols: &'a [Inventory], } impl<'a> Codegen<'a> { - pub fn new(symbols: &'a [Symbol]) -> Self { + pub fn new(symbols: &'a [Inventory]) -> Self { Self { symbols } } @@ -132,21 +134,26 @@ impl<'a> Codegen<'a> { } for symbol in self.symbols { - writeln!(writer, " {}: {{", symbol.name)?; - write!(writer, " parameters: ")?; - format_bracket(writer, symbol.parameters, |writer, parameters| { - for parameter in parameters { - writeln!(writer, " {},", DenoFfiType::from(*parameter))?; + match symbol { + Inventory::Symbol(symbol) => { + writeln!(writer, " {}: {{", symbol.name)?; + write!(writer, " parameters: ")?; + format_bracket(writer, symbol.parameters, |writer, parameters| { + for parameter in parameters { + writeln!(writer, " {},", DenoFfiType::from(*parameter))?; + } + Ok(()) + })?; + writeln!( + writer, + " result: {},", + DenoFfiType::from(symbol.return_type) + )?; + writeln!(writer, " nonblocking: {}", symbol.non_blocking)?; + writeln!(writer, " }},")?; } - Ok(()) - })?; - writeln!( - writer, - " result: {},", - DenoFfiType::from(symbol.return_type) - )?; - writeln!(writer, " nonblocking: {}", symbol.non_blocking)?; - writeln!(writer, " }},")?; + _ => {} + } } Ok(()) @@ -166,39 +173,59 @@ impl<'a> Codegen<'a> { write!(writer, "{:indent$})", "", indent = nesting_spaces)?; } else { write!(writer, ")")?; - } + } Ok(()) } for symbol in self.symbols { - write!(writer, "export function {}", symbol.name)?; - format_paren(writer, symbol.parameters, |writer, parameters| { - for (i, parameter) in parameters.iter().enumerate() { + match symbol { + Inventory::Symbol(symbol) => { + write!(writer, "export function {}", symbol.name)?; + format_paren( + writer, + symbol.parameters, + |writer, parameters| { + for (i, parameter) in parameters.iter().enumerate() { + writeln!( + writer, + " arg{}: {},", + i, + TypeScriptType::from(*parameter) + )?; + } + Ok(()) + }, + 0, + )?; writeln!( writer, - " arg{}: {},", - i, - TypeScriptType::from(*parameter) + ": {} {{", + TypeScriptType::from(symbol.return_type) + .apply_promise(symbol.non_blocking) + )?; + write!(writer, " return symbols.{}", symbol.name)?; + format_paren( + writer, + symbol.parameters, + |writer, parameters| { + for (i, parameter) in parameters.iter().enumerate() { + let ident = format!("arg{}", i); + writeln!( + writer, + " {},", + TypeScriptType::from(*parameter).into_raw(&ident) + )?; + } + Ok(()) + }, + 2, )?; - } - Ok(()) - }, 0)?; - writeln!(writer, ": {} {{", TypeScriptType::from(symbol.return_type).apply_promise(symbol.non_blocking))?; - write!(writer, " return symbols.{}", symbol.name)?; - format_paren(writer, symbol.parameters, |writer, parameters| { - for (i, parameter) in parameters.iter().enumerate() { - let ident = format!("arg{}", i); - writeln!( - writer, - " {},", - TypeScriptType::from(*parameter).into_raw(&ident) - )?; - } - Ok(()) - }, 2)?; - writeln!(writer, "\n}}\n")?; + writeln!(writer, "\n}}\n")?; + } + _ => {} + } } Ok(()) diff --git a/deno_bindgen_ir/codegen/mod.rs b/deno_bindgen_ir/codegen/mod.rs index b3c2698..6c39349 100644 --- a/deno_bindgen_ir/codegen/mod.rs +++ b/deno_bindgen_ir/codegen/mod.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use crate::Symbol; +use crate::{inventory::Inventory, Symbol}; mod deno; @@ -18,7 +18,7 @@ pub trait Generator { } pub fn generate( - symbols: &'static [Symbol], + symbols: &'static [Inventory], opt: Options, ) -> std::io::Result<()> { let mut codegen = match opt.target { diff --git a/deno_bindgen_ir/inventory.rs b/deno_bindgen_ir/inventory.rs new file mode 100644 index 0000000..8609608 --- /dev/null +++ b/deno_bindgen_ir/inventory.rs @@ -0,0 +1,13 @@ +use crate::Symbol; + +#[derive(Debug)] +pub struct Struct { + pub name: &'static str, + pub constructor: Option, + pub methods: &'static [Symbol], +} + +pub enum Inventory { + Symbol(Symbol), + Struct(Struct), +} diff --git a/deno_bindgen_ir/lib.rs b/deno_bindgen_ir/lib.rs index 56ab6ac..cede4c7 100644 --- a/deno_bindgen_ir/lib.rs +++ b/deno_bindgen_ir/lib.rs @@ -1,8 +1,11 @@ use proc_macro2::Ident; -use quote::{format_ident, quote, ToTokens}; +use quote::{quote, ToTokens}; use syn::{parse_quote, Pat}; pub mod codegen; +pub mod inventory; + +pub use inventory::Inventory; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] pub enum Type { diff --git a/deno_bindgen_macro/src/fn_.rs b/deno_bindgen_macro/src/fn_.rs index 10bede6..9a2240a 100644 --- a/deno_bindgen_macro/src/fn_.rs +++ b/deno_bindgen_macro/src/fn_.rs @@ -8,7 +8,10 @@ use syn::{ ItemFn, PatType, ReturnType, TypePath, TypePtr, TypeReference, TypeSlice, }; -use crate::{util::{Error, Result}, FnAttributes}; +use crate::{ + util::{Error, Result}, + FnAttributes, +}; fn parse_type(ty: &Box) -> Result { match **ty { @@ -168,7 +171,7 @@ pub fn handle(fn_: ItemFn, attrs: FnAttributes) -> Result { Ok(quote::quote! { const _: () = { #[deno_bindgen::linkme::distributed_slice(deno_bindgen::INVENTORY)] - pub static _A: deno_bindgen::Symbol = #symbol; + pub static _A: deno_bindgen::Inventory = deno_bindgen::Inventory::Symbol(#symbol); }; #[no_mangle] diff --git a/deno_bindgen_macro/src/impl_.rs b/deno_bindgen_macro/src/impl_.rs new file mode 100644 index 0000000..199c511 --- /dev/null +++ b/deno_bindgen_macro/src/impl_.rs @@ -0,0 +1,46 @@ +use proc_macro2::TokenStream as TokenStream2; +use syn::ItemImpl; + +use crate::util::{self, Result}; + +pub fn handle(impl_: ItemImpl) -> Result { + if impl_.generics.params.first().is_some() { + return Err(util::Error::Generics); + } + + if impl_.generics.where_clause.is_some() { + return Err(util::Error::WhereClause); + } + + let self_ty = match *impl_.self_ty { + syn::Type::Path(ref type_path) => type_path.path.clone(), + _ => return Err(util::Error::UnsupportedType), + }; + + let ref ty_str @ _ = self_ty.get_ident().unwrap(); + + // TODO: + // - create a new quoted function for each method and codegen using fn_::handle + // where first arg is self ptr and rest are method args + // - constructor is a simply special case with no self ptr. + // - we also need to be aware of &mut self and Self types. + + Ok(quote::quote! { + #impl_ + + const _: () = { + // Assert that the type implements `BindgenType`. + const fn _assert_impl() {} + _assert_impl::<#ty_str>(); + + #[deno_bindgen::linkme::distributed_slice(deno_bindgen::INVENTORY)] + pub static _B: deno_bindgen::Inventory = deno_bindgen::Inventory::Struct( + deno_bindgen::inventory::Struct { + name: stringify!(#ty_str), + constructor: None, + methods: &[], + } + ); + }; + }) +} diff --git a/deno_bindgen_macro/src/lib.rs b/deno_bindgen_macro/src/lib.rs index be37273..be5230f 100644 --- a/deno_bindgen_macro/src/lib.rs +++ b/deno_bindgen_macro/src/lib.rs @@ -3,9 +3,13 @@ use deno_bindgen_ir::Symbol; use proc_macro::TokenStream; use quote::{format_ident, quote}; -use syn::{parse_macro_input, parse_quote, ItemFn, meta::ParseNestedMeta}; +use syn::{ + meta::ParseNestedMeta, parse2, parse_macro_input, parse_quote, Item, ItemFn, +}; mod fn_; +mod impl_; +mod struct_; mod util; #[derive(Default)] @@ -26,10 +30,16 @@ impl FnAttributes { #[proc_macro_attribute] pub fn deno_bindgen(args: TokenStream, input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as ItemFn); - let mut attrs = FnAttributes::default(); - let attrs_parser = syn::meta::parser(|meta| attrs.parse(meta)); - parse_macro_input!(args with attrs_parser); + match parse2::(input.into()).unwrap() { + Item::Fn(input) => { + let mut attrs = FnAttributes::default(); + let attrs_parser = syn::meta::parser(|meta| attrs.parse(meta)); + parse_macro_input!(args with attrs_parser); - fn_::handle(input, attrs).unwrap().into() + fn_::handle(input, attrs).unwrap().into() + } + Item::Struct(input) => struct_::handle(input).unwrap().into(), + Item::Impl(input) => impl_::handle(input).unwrap().into(), + _ => panic!("only functions are supported"), + } } diff --git a/deno_bindgen_macro/src/struct_.rs b/deno_bindgen_macro/src/struct_.rs new file mode 100644 index 0000000..9034bdc --- /dev/null +++ b/deno_bindgen_macro/src/struct_.rs @@ -0,0 +1,25 @@ +use proc_macro2::TokenStream as TokenStream2; +use syn::ItemStruct; + +use crate::util::{self, Result}; + +pub fn handle(struct_: ItemStruct) -> Result { + if struct_.generics.params.first().is_some() { + return Err(util::Error::Generics); + } + + if struct_.generics.where_clause.is_some() { + return Err(util::Error::WhereClause); + } + + let ref ty_str @ _ = struct_.ident; + Ok(quote::quote! { + #struct_ + + impl ::deno_bindgen::BindgenType for #ty_str { + fn type_name() -> &'static str { + stringify!(#ty_str) + } + } + }) +} diff --git a/deno_bindgen_macro/src/util.rs b/deno_bindgen_macro/src/util.rs index ec5c196..4ed673e 100644 --- a/deno_bindgen_macro/src/util.rs +++ b/deno_bindgen_macro/src/util.rs @@ -3,6 +3,8 @@ pub enum Error { Asyncness, Reciever, UnsupportedType, + Generics, + WhereClause, } impl std::fmt::Display for Error { @@ -11,6 +13,8 @@ impl std::fmt::Display for Error { Error::Asyncness => write!(f, "async functions are not supported"), Error::Reciever => write!(f, "methods are not supported"), Error::UnsupportedType => write!(f, "unsupported type"), + Error::Generics => write!(f, "generics are not supported"), + Error::WhereClause => write!(f, "where clauses are not supported"), } } } diff --git a/example/bindings_test.ts b/example/bindings_test.ts index d4f58a4..92107ab 100644 --- a/example/bindings_test.ts +++ b/example/bindings_test.ts @@ -50,11 +50,13 @@ Deno.test({ }, }); - Deno.test({ name: "non_blocking#test", fn: async () => { const result = await non_blocking(); assertEquals(result, 42); }, -}); \ No newline at end of file +}); + +// struct (glorified pointers) +// impl on struct \ No newline at end of file diff --git a/example/s.ts b/example/s.ts new file mode 100644 index 0000000..c420a67 --- /dev/null +++ b/example/s.ts @@ -0,0 +1,28 @@ +class Example { + ptr: Deno.PointerObject | null = null; + + static from_ptr(ptr: Deno.PointerObject | null) { + const obj = Object.create(Example.prototype); + obj.ptr = ptr; + + return obj; + } + + constructor() { + // ... + } + + close() { + ffi_free(this.ptr); + } + + [Symbol.dispose]() { + this.close(); + } +} + +export function make_example(): Example { + return Example.from_ptr(ffi_make_example()); +} + +using obj = make_example(); diff --git a/example/src/lib.rs b/example/src/lib.rs index 57ed067..2f0383a 100644 --- a/example/src/lib.rs +++ b/example/src/lib.rs @@ -40,4 +40,16 @@ fn strlen(s: *const u8) -> u32 { #[deno_bindgen(non_blocking)] fn non_blocking() -> i32 { 42 +} + +#[deno_bindgen] +pub struct Foo { + internal: i32, +} + +#[deno_bindgen] +impl Foo { + fn new() -> Self { + Self { internal: 42 } + } } \ No newline at end of file