Automatically generate drop for #[metatype]

This commit is contained in:
lumi 2025-06-19 23:07:53 +10:00
parent 8f6fc64f7a
commit f9676a1436
Signed by: luaneko
GPG Key ID: 406809B8763FF07A
4 changed files with 51 additions and 5 deletions

View File

@ -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"] }

View File

@ -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<TokenStream> {
#[derive(Debug, FromMeta)]
pub struct Args {
module: Option<String>,
}
pub fn transform(args: Args, mut item: Item) -> Result<TokenStream> {
let (name, impl_type, impl_cdef) = match item {
Item::Struct(ref mut str) => (
str.ident.clone(),

View File

@ -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()
}

View File

@ -26,25 +26,36 @@ pub fn transform(mut imp: ItemImpl) -> Result<TokenStream> {
}
fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
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::<Result<_>>()?;
let ffi_register: Vec<_> = ffi_funcs
.iter()
.map(generate_ffi_register)
.collect::<Result<_>>()?;
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::<Result<_>>()?;
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<TokenStream> {
fn build(b: &mut #ffi::MetatypeBuilder) {
#(#ffi_register)*
#(#lua_register)*
b.declare::<unsafe extern "C" fn(*mut Self)>(#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<TokenStream> {
})
}
fn generate_ffi_exports<'a>(
ty: &Type,
names: impl Iterator<Item = &'a Ident>,
) -> Result<TokenStream> {
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<PatType>,