diff --git a/crates/luaffi_impl/Cargo.toml b/crates/luaffi_impl/Cargo.toml index c5e3d01..f4b5234 100644 --- a/crates/luaffi_impl/Cargo.toml +++ b/crates/luaffi_impl/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" proc-macro = true [dependencies] +darling = "0.20.11" proc-macro2 = "1.0.95" quote = "1.0.40" syn = { version = "2.0.103", features = ["full", "visit-mut"] } diff --git a/crates/luaffi_impl/src/cdef.rs b/crates/luaffi_impl/src/cdef.rs index 64f44f9..84741f4 100644 --- a/crates/luaffi_impl/src/cdef.rs +++ b/crates/luaffi_impl/src/cdef.rs @@ -1,9 +1,15 @@ use crate::utils::{ffi_crate, syn_assert, syn_error}; +use darling::FromMeta; use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::{spanned::*, *}; -pub fn transform(mut item: Item) -> Result { +#[derive(Debug, FromMeta)] +pub struct Args { + module: Option, +} + +pub fn transform(args: Args, mut item: Item) -> Result { let (name, impl_type, impl_cdef) = match item { Item::Struct(ref mut str) => ( str.ident.clone(), diff --git a/crates/luaffi_impl/src/lib.rs b/crates/luaffi_impl/src/lib.rs index 5f68a6e..4ed3b1f 100644 --- a/crates/luaffi_impl/src/lib.rs +++ b/crates/luaffi_impl/src/lib.rs @@ -1,3 +1,4 @@ +use darling::{FromMeta, ast::NestedMeta}; use proc_macro::TokenStream as TokenStream1; use quote::ToTokens; use syn::parse_macro_input; @@ -8,7 +9,9 @@ mod utils; #[proc_macro_attribute] pub fn cdef(args: TokenStream1, input: TokenStream1) -> TokenStream1 { - cdef::transform(parse_macro_input!(input)) + NestedMeta::parse_meta_list(args.into()) + .and_then(|meta| cdef::Args::from_list(&meta).map_err(Into::into)) + .and_then(|args| cdef::transform(args, syn::parse(input)?)) .unwrap_or_else(|err| err.into_compile_error().into_token_stream()) .into() } diff --git a/crates/luaffi_impl/src/metatype.rs b/crates/luaffi_impl/src/metatype.rs index 6ac06b5..59fc81b 100644 --- a/crates/luaffi_impl/src/metatype.rs +++ b/crates/luaffi_impl/src/metatype.rs @@ -26,25 +26,36 @@ pub fn transform(mut imp: ItemImpl) -> Result { } fn generate_impls(imp: &mut ItemImpl) -> Result { + let ty = imp.self_ty.clone(); + let ty_name = ty_name(&ty)?; + let ffi = ffi_crate(); let ffi_funcs = get_ffi_functions(imp)?; let ffi_wrappers: Vec<_> = ffi_funcs .iter() .map(generate_ffi_wrapper) .collect::>()?; + let ffi_register: Vec<_> = ffi_funcs .iter() .map(generate_ffi_register) .collect::>()?; + let ffi_drop_fn = format_ident!("__ffi_drop"); + let ffi_drop_name = format!("{ty_name}_drop"); + + let ffi_exports = { + let mut names = vec![&ffi_drop_fn]; + names.extend(ffi_funcs.iter().map(|f| &f.rust_name)); + generate_ffi_exports(&ty, names.into_iter())? + }; + let lua_funcs = get_lua_functions(imp)?; let lua_register: Vec<_> = lua_funcs .iter() .map(generate_lua_register) .collect::>()?; - let ty = &*imp.self_ty; - Ok(quote! { unsafe impl #ffi::Metatype for #ty { type Target = Self; @@ -52,10 +63,22 @@ fn generate_impls(imp: &mut ItemImpl) -> Result { fn build(b: &mut #ffi::MetatypeBuilder) { #(#ffi_register)* #(#lua_register)* + + b.declare::(#ffi_drop_name); + b.metatable_raw("gc", ::std::format_args!("C.{}", #ffi_drop_name)); } } - impl #ty { #(#ffi_wrappers)* } + impl #ty { + #(#ffi_wrappers)* + + #[unsafe(export_name = #ffi_drop_name)] + unsafe extern "C" fn #ffi_drop_fn(&mut self) { + unsafe { ::std::ptr::drop_in_place(self) } + } + } + + #ffi_exports }) } @@ -213,6 +236,19 @@ fn generate_ffi_register(func: &FfiFunction) -> Result { }) } +fn generate_ffi_exports<'a>( + ty: &Type, + names: impl Iterator, +) -> Result { + Ok(quote! { + // hack to prevent ffi functions from being dead code-eliminated + #[used] + static __FFI_EXPORTS: &[fn()] = unsafe { + &[#(::std::mem::transmute(#ty::#names as *const ())),*] + }; + }) +} + struct LuaFunction { name: String, params: Vec,