Add ctype new support

This commit is contained in:
2025-06-22 18:41:19 +10:00
parent 0fd59f6874
commit 40478fb7de
7 changed files with 118 additions and 81 deletions

View File

@@ -44,6 +44,17 @@ fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
.map(generate_ffi_register)
.collect::<Result<_>>()?;
let ffi_new_fallback = match ffi_funcs
.iter()
.find(|f| f.attrs.metatable.as_deref() == Some("new"))
{
Some(_) => None,
None => Some({
let err = format!(r#"function() error("type '{ty_name}' has no constructor"); end"#);
quote! { b.metatable_raw("new", #err); }
}),
};
let ffi_drop_rname = format_ident!("__ffi_drop");
let ffi_drop_cname = format!("{ty_name}_drop");
@@ -78,6 +89,8 @@ fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
#(#ffi_register)*
#(#lua_register)*
#ffi_new_fallback
b.declare::<unsafe extern "C" fn(*mut Self)>(#ffi_drop_cname);
b.metatable_raw("gc", ::std::format_args!("__C.{}", #ffi_drop_cname));
}
@@ -100,7 +113,7 @@ struct FfiFunction {
#[derive(Default)]
struct FfiFunctionAttrs {
metatable: Option<LitStr>,
metatable: Option<String>,
}
fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
@@ -161,11 +174,11 @@ fn parse_ffi_function_attrs(attrs: &mut Vec<Attribute>) -> Result<FfiFunctionAtt
while let Some(attr) = attrs.get(i) {
if let Some(name) = attr.path().get_ident() {
if name == "metatable" {
parsed.metatable = attr.parse_args()?;
parsed.metatable = Some(attr.parse_args::<LitStr>()?.value());
attrs.remove(i);
continue;
} else if name == "new" {
parsed.metatable = parse_quote!("new");
parsed.metatable = Some("new".into());
attrs.remove(i);
continue;
}
@@ -194,7 +207,7 @@ fn generate_ffi_wrapper(func: &FfiFunction) -> Result<TokenStream> {
let mut args = vec![];
for (i, param) in func.params.iter().enumerate() {
let name = format_ident!("__arg{i}");
let name = format_ident!("arg{i}");
let ty = &param.ty;
match get_ffi_arg_type(ty) {
@@ -208,10 +221,10 @@ fn generate_ffi_wrapper(func: &FfiFunction) -> Result<TokenStream> {
let (ret, call) = if func.ret_by_out {
// make return by out-param the first parameter
let ret = &func.ret;
params.insert(0, quote! { __out: *mut #ret });
params.insert(0, quote! { out: *mut #ret });
(
quote!(()),
quote! { ::std::ptr::write(__out, Self::#name(#(#args),*)) },
quote! { ::std::ptr::write(out, Self::#name(#(#args),*)) },
)
} else {
let ret = &func.ret;
@@ -220,9 +233,7 @@ fn generate_ffi_wrapper(func: &FfiFunction) -> Result<TokenStream> {
Ok(quote! {
#[unsafe(export_name = #c_name)]
unsafe extern "C" fn #rust_name(#(#params),*) -> #ret {
unsafe { #call }
}
unsafe extern "C" fn #rust_name(#(#params),*) -> #ret { unsafe { #call } }
})
}
@@ -234,6 +245,11 @@ fn generate_ffi_register(func: &FfiFunction) -> Result<TokenStream> {
let mut params = vec![];
let mut register = vec![];
// for __new metamethods, ignore the first argument (ctype of self)
if func.attrs.metatable.as_deref() == Some("new") {
register.push(quote! { b.param_ignored(); });
}
for param in func.params.iter() {
let name = format!("{}", pat_ident(&param.pat)?);
let ty = &param.ty;
@@ -255,8 +271,10 @@ fn generate_ffi_register(func: &FfiFunction) -> Result<TokenStream> {
quote! { #ffi::FfiReturnConvention::ByValue }
};
let declare = quote! {
b.declare::<unsafe extern "C" fn(#(#params),*) -> #ret>(#c_name);
let declare = if func.ret_by_out {
quote! { b.declare::<unsafe extern "C" fn(*mut #ret, #(#params),*)>(#c_name); }
} else {
quote! { b.declare::<unsafe extern "C" fn(#(#params),*) -> #ret>(#c_name); }
};
let register = match func.attrs.metatable {
@@ -274,10 +292,7 @@ fn generate_ffi_register(func: &FfiFunction) -> Result<TokenStream> {
},
};
Ok(quote! {
#declare
#register
})
Ok(quote! { #declare #register })
}
fn generate_ffi_exports<'a>(