Implement async support in metatype

This commit is contained in:
2025-06-25 01:36:43 +10:00
parent cbf786206d
commit 2352cb0225
4 changed files with 169 additions and 85 deletions

View File

@@ -3,7 +3,7 @@ use crate::utils::{
};
use proc_macro2::TokenStream;
use quote::{ToTokens, format_ident, quote, quote_spanned};
use std::{collections::HashSet, fmt};
use std::{collections::HashSet, fmt, iter};
use syn::{ext::IdentExt, punctuated::Punctuated, spanned::Spanned, *};
pub fn transform(mut imp: ItemImpl) -> Result<TokenStream> {
@@ -181,6 +181,7 @@ impl ToTokens for Metamethod {
struct FfiFunction {
name: Ident,
is_async: bool,
params: Vec<PatType>,
ret: Type,
attrs: FfiFunctionAttrs,
@@ -225,6 +226,7 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
funcs.push(FfiFunction {
name: func.sig.ident.clone(),
is_async: func.sig.asyncness.is_some(),
params,
ret,
attrs,
@@ -267,8 +269,8 @@ fn get_ffi_param_type(_ty: &Type) -> FfiParameterType {
enum FfiReturnType {
Void,
Primitive,
Aggregate,
ByValue,
ByOutParam,
}
fn get_ffi_ret_type(ty: &Type) -> FfiReturnType {
@@ -288,9 +290,9 @@ fn get_ffi_ret_type(ty: &Type) -> FfiReturnType {
if is_unit(ty) {
FfiReturnType::Void
} else if is_primitivelike(ty) {
FfiReturnType::Primitive
FfiReturnType::ByValue
} else {
FfiReturnType::Aggregate
FfiReturnType::ByOutParam
}
}
@@ -316,16 +318,23 @@ fn add_ffi_function(registry: &mut FfiRegistry, func: &FfiFunction) -> Result<()
let func_name = &func.name;
let shim_name = format_ident!("__ffi_{}", func_name.unraw());
let lua_name = format!("{}", func_name.unraw());
let c_name = format!("{}_{}", ty.unraw(), func_name.unraw());
let c_name = if let Some(priv_name) = lua_name.strip_prefix("__") {
format!("__{}_{priv_name}", ty.unraw())
} else {
format!("{}_{lua_name}", ty.unraw())
};
let func_params = &func.params; // target function parameters
let func_ret = &func.ret; // target function return type
let mut func_args = vec![]; // target function arguments
let mut shim_params = vec![]; // shim function parameters
let mut shim_ret = quote_spanned!(func_ret.span() => // shim function return type
<#func_ret as #ffi::IntoFfi>::Into
);
let mut shim_ret = if func.is_async {
// shim function return type
quote_spanned!(func_ret.span() => #ffi::future::lua_future<impl ::std::future::Future<Output = #func_ret>>)
} else {
quote_spanned!(func_ret.span() => <#func_ret as #ffi::IntoFfi>::Into)
};
let mut asserts = vec![]; // compile-time builder asserts
let mut build = vec![]; // ffi builder body
@@ -358,43 +367,53 @@ fn add_ffi_function(registry: &mut FfiRegistry, func: &FfiFunction) -> Result<()
}
}
let mut shim_body = quote_spanned!(func_name.span() => // shim function body
<#func_ret as #ffi::IntoFfi>::convert(Self::#func_name(#(#func_args),*))
);
match get_ffi_ret_type(func_ret) {
FfiReturnType::Void => {
asserts.push(quote_spanned!(func_ret.span() =>
<<#func_ret as #ffi::IntoFfi>::Into as #ffi::Type>::ty() == #ffi::TypeType::Void
));
}
FfiReturnType::Primitive => {
asserts.push(quote_spanned!(func_ret.span() =>
<<#func_ret as #ffi::IntoFfi>::Into as #ffi::Type>::ty() == #ffi::TypeType::Primitive
));
}
FfiReturnType::Aggregate => {
asserts.push(quote_spanned!(func_ret.span() =>
<<#func_ret as #ffi::IntoFfi>::Into as #ffi::Type>::ty() == #ffi::TypeType::Aggregate
));
shim_params.insert(0, quote!(out: *mut #shim_ret));
(shim_body, shim_ret) = (quote!(::std::ptr::write(out, #shim_body)), quote!(()));
}
// shim function body
let mut shim_body = if func.is_async {
// for async functions, wrapped the returned future in lua_future
quote_spanned!(func_name.span() => #ffi::future::lua_future::new(Self::#func_name(#(#func_args),*)))
} else {
quote_spanned!(func_name.span() => <#func_ret as #ffi::IntoFfi>::convert(Self::#func_name(#(#func_args),*)))
};
build.push(quote_spanned!(func_name.span() =>
b.call::<#func_ret>(#c_name);
));
if !func.is_async {
match get_ffi_ret_type(&func_ret) {
FfiReturnType::Void => {
asserts.push(quote_spanned!(func_ret.span() =>
<<#func_ret as #ffi::IntoFfi>::Into as #ffi::Type>::ty() == #ffi::TypeType::Void
));
}
FfiReturnType::ByValue => {
asserts.push(quote_spanned!(func_ret.span() =>
<#func_ret as #ffi::IntoFfi>::convention() == #ffi::FfiReturnConvention::ByValue
));
}
FfiReturnType::ByOutParam => {
asserts.push(quote_spanned!(func_ret.span() =>
<#func_ret as #ffi::IntoFfi>::convention() == #ffi::FfiReturnConvention::ByOutParam
));
let shim_params_ty = {
let tys: Punctuated<PatType, Token![,]> = parse_quote!(#(#shim_params),*);
tys.iter().map(|pat| (*pat.ty).clone()).collect::<Vec<_>>()
};
shim_params.insert(0, quote!(out: *mut #shim_ret));
(shim_body, shim_ret) = (quote!(::std::ptr::write(out, #shim_body)), quote!(()));
}
};
}
registry.build.push(quote!(
// build.push(quote_spanned!(func_name.span() =>
// b.call_inferred(#c_name, Self::#func_name);
// ));
build.push({
let infer_args = iter::repeat_n(quote!(::std::unreachable!()), func_params.len());
let infer = if func.is_async {
quote!(|| #ffi::future::lua_future::new(Self::#func_name(#(#infer_args),*)))
} else {
quote!(|| Self::#func_name(#(#infer_args),*))
};
quote_spanned!(func_name.span() => b.call_inferred(#c_name, #infer);)
});
registry.build.push(quote_spanned!(func_name.span() =>
#(::std::assert!(#asserts);)*
b.declare::<#ffi::UnsafeExternCFn<(#(#shim_params_ty,)*), #shim_ret>>(#c_name);
));
registry.build.push(match func.attrs.metamethod {
@@ -678,7 +697,7 @@ fn inject_merged_drop(registry: &mut FfiRegistry, lua: Option<&LuaFunction>) ->
if ::std::mem::needs_drop::<Self>() {
// we only have a rust drop
b.declare::<#ffi::UnsafeExternCFn<(*mut Self,), ()>>(#c_name_str);
b.metatable_raw("gc", #luaify(|self| { __C::#c_name(self); }));
b.metatable_raw("gc", ::std::format_args!("__C.{}", #c_name_str));
}
));
}