Implement async support in metatype
This commit is contained in:
@@ -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));
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user