Working
This commit is contained in:
@@ -22,7 +22,7 @@ pub fn transform(_args: Args, mut item: Item) -> Result<TokenStream> {
|
||||
_ => syn_error!(item, "expected struct or enum"),
|
||||
};
|
||||
|
||||
let mod_name = format_ident!("__cdef__{name}");
|
||||
let mod_name = format_ident!("__{name}_cdef");
|
||||
|
||||
Ok(quote! {
|
||||
#[repr(C)]
|
||||
@@ -42,23 +42,28 @@ pub fn transform(_args: Args, mut item: Item) -> Result<TokenStream> {
|
||||
fn generate_type(ty: &Ident) -> Result<TokenStream> {
|
||||
let ffi = ffi_crate();
|
||||
let fmt = quote!(::std::format!);
|
||||
let name_fmt = LitStr::new(&format!("{ty}"), ty.span());
|
||||
let cdecl_fmt = LitStr::new(&format!("struct {ty} {{name}}"), ty.span());
|
||||
let name = LitStr::new(&format!("{ty}"), ty.span());
|
||||
let cdecl_fmt = LitStr::new(&format!("struct {ty} {{}}"), ty.span());
|
||||
|
||||
Ok(quote! {
|
||||
unsafe impl #ffi::Type for #ty {
|
||||
fn name() -> ::std::string::String {
|
||||
#fmt(#name_fmt)
|
||||
fn name() -> impl ::std::fmt::Display {
|
||||
#name
|
||||
}
|
||||
|
||||
fn cdecl(name: impl ::std::fmt::Display) -> ::std::string::String {
|
||||
#fmt(#cdecl_fmt)
|
||||
fn cdecl(name: impl ::std::fmt::Display) -> impl ::std::fmt::Display {
|
||||
#fmt(#cdecl_fmt, name)
|
||||
}
|
||||
|
||||
fn build(b: &mut #ffi::TypeBuilder) {
|
||||
b.cdef::<Self>().metatype::<Self>();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl #ffi::ToFfi for #ty {
|
||||
type To = Self;
|
||||
fn convert(self) -> Self::To { self }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -113,6 +118,11 @@ struct CField {
|
||||
attrs: CFieldAttrs,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CFieldAttrs {
|
||||
opaque: bool,
|
||||
}
|
||||
|
||||
fn to_cfields(fields: &mut Fields) -> Result<Vec<CField>> {
|
||||
match fields {
|
||||
Fields::Named(fields) => fields.named.iter_mut(),
|
||||
@@ -133,11 +143,6 @@ fn to_cfields(fields: &mut Fields) -> Result<Vec<CField>> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CFieldAttrs {
|
||||
opaque: bool,
|
||||
}
|
||||
|
||||
fn parse_attrs(attrs: &mut Vec<Attribute>) -> Result<CFieldAttrs> {
|
||||
let mut parsed = CFieldAttrs::default();
|
||||
let mut i = 0;
|
||||
|
||||
@@ -11,7 +11,7 @@ pub fn transform(mut imp: ItemImpl) -> Result<TokenStream> {
|
||||
);
|
||||
|
||||
let impls = generate_impls(&mut imp)?;
|
||||
let mod_name = format_ident!("__metatype__{}", ty_name(&imp.self_ty)?);
|
||||
let mod_name = format_ident!("__{}_metatype", ty_name(&imp.self_ty)?);
|
||||
|
||||
Ok(quote! {
|
||||
#imp
|
||||
@@ -31,25 +31,30 @@ fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
||||
|
||||
let ffi = ffi_crate();
|
||||
let ffi_funcs = get_ffi_functions(imp)?;
|
||||
|
||||
// wrapper extern "C" functions that call the actual implementation
|
||||
let ffi_wrappers: Vec<_> = ffi_funcs
|
||||
.iter()
|
||||
.map(generate_ffi_wrapper)
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
// ffi function registration code
|
||||
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_drop_rname = format_ident!("__ffi_drop");
|
||||
let ffi_drop_cname = format!("{ty_name}_drop");
|
||||
|
||||
// ffi function symbol export code
|
||||
let ffi_exports = {
|
||||
let mut names = vec![&ffi_drop_fn];
|
||||
let mut names = vec![&ffi_drop_rname];
|
||||
names.extend(ffi_funcs.iter().map(|f| &f.rust_name));
|
||||
generate_ffi_exports(&ty, names.into_iter())?
|
||||
};
|
||||
|
||||
// lua function registration code
|
||||
let lua_funcs = get_lua_functions(imp)?;
|
||||
let lua_register: Vec<_> = lua_funcs
|
||||
.iter()
|
||||
@@ -57,6 +62,15 @@ fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
Ok(quote! {
|
||||
impl #ty {
|
||||
#(#ffi_wrappers)*
|
||||
|
||||
#[unsafe(export_name = #ffi_drop_cname)]
|
||||
unsafe extern "C" fn #ffi_drop_rname(ptr: *mut Self) {
|
||||
unsafe { ::std::ptr::drop_in_place(ptr) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl #ffi::Metatype for #ty {
|
||||
type Target = Self;
|
||||
|
||||
@@ -64,17 +78,8 @@ fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
||||
#(#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)*
|
||||
|
||||
#[unsafe(export_name = #ffi_drop_name)]
|
||||
unsafe extern "C" fn #ffi_drop_fn(&mut self) {
|
||||
unsafe { ::std::ptr::drop_in_place(self) }
|
||||
b.declare::<unsafe extern "C" fn(*mut Self)>(#ffi_drop_cname);
|
||||
b.metatable_raw("gc", ::std::format_args!("__C.{}", #ffi_drop_cname));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +94,13 @@ struct FfiFunction {
|
||||
c_name: String,
|
||||
params: Vec<PatType>,
|
||||
ret: Type,
|
||||
ret_out: bool,
|
||||
ret_by_out: bool,
|
||||
attrs: FfiFunctionAttrs,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct FfiFunctionAttrs {
|
||||
metatable: Option<LitStr>,
|
||||
}
|
||||
|
||||
fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
||||
@@ -103,6 +114,7 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
||||
{
|
||||
func.sig.abi = None;
|
||||
|
||||
// normalise inputs to PatType
|
||||
let params = func
|
||||
.sig
|
||||
.inputs
|
||||
@@ -118,13 +130,14 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
||||
})
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
// normalise output to Type
|
||||
let ret = match func.sig.output {
|
||||
ReturnType::Default => parse_quote!(()),
|
||||
ReturnType::Type(_, ref ty) => (**ty).clone(),
|
||||
};
|
||||
|
||||
// whether to use out-param for return values
|
||||
let ret_out = !is_primitive(&ret);
|
||||
let ret_by_out = !is_primitive(&ret);
|
||||
|
||||
funcs.push(FfiFunction {
|
||||
name: func.sig.ident.clone(),
|
||||
@@ -133,7 +146,8 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
||||
c_name: format!("{}_{}", ty_name(&imp.self_ty)?, func.sig.ident),
|
||||
params,
|
||||
ret,
|
||||
ret_out,
|
||||
ret_by_out,
|
||||
attrs: parse_ffi_function_attrs(&mut func.attrs)?,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -141,6 +155,27 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
||||
Ok(funcs)
|
||||
}
|
||||
|
||||
fn parse_ffi_function_attrs(attrs: &mut Vec<Attribute>) -> Result<FfiFunctionAttrs> {
|
||||
let mut parsed = FfiFunctionAttrs::default();
|
||||
let mut i = 0;
|
||||
while let Some(attr) = attrs.get(i) {
|
||||
if let Some(name) = attr.path().get_ident() {
|
||||
if name == "metatable" {
|
||||
parsed.metatable = attr.parse_args()?;
|
||||
attrs.remove(i);
|
||||
continue;
|
||||
} else if name == "new" {
|
||||
parsed.metatable = parse_quote!("new");
|
||||
attrs.remove(i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum FfiArgType {
|
||||
Default,
|
||||
@@ -150,14 +185,6 @@ fn get_ffi_arg_type(_ty: &Type) -> FfiArgType {
|
||||
FfiArgType::Default
|
||||
}
|
||||
|
||||
fn escape_self(name: &Ident) -> Ident {
|
||||
if name == "self" {
|
||||
format_ident!("__self")
|
||||
} else {
|
||||
name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_ffi_wrapper(func: &FfiFunction) -> Result<TokenStream> {
|
||||
let ffi = ffi_crate();
|
||||
let name = &func.name;
|
||||
@@ -166,36 +193,35 @@ fn generate_ffi_wrapper(func: &FfiFunction) -> Result<TokenStream> {
|
||||
let mut params = vec![];
|
||||
let mut args = vec![];
|
||||
|
||||
for param in func.params.iter() {
|
||||
let name = escape_self(pat_ident(¶m.pat)?);
|
||||
for (i, param) in func.params.iter().enumerate() {
|
||||
let name = format_ident!("__arg{i}");
|
||||
let ty = ¶m.ty;
|
||||
|
||||
match get_ffi_arg_type(ty) {
|
||||
FfiArgType::Default => {
|
||||
params.push(quote! { #name: <#ty as #ffi::FromFfi>::From });
|
||||
args.push(quote! { <#ty as #ffi::FromFfi>::convert(#name) });
|
||||
params.push(quote! { #name: <#ty as #ffi::FromFfi>::FromArg });
|
||||
args.push(quote! { <#ty as #ffi::FromFfi>::convert_arg(#name) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make return by out-param the first parameter
|
||||
let (ret, do_ret) = if func.ret_out {
|
||||
let (ret, call) = if func.ret_by_out {
|
||||
// make return by out-param the first parameter
|
||||
let ret = &func.ret;
|
||||
params.insert(0, quote! { __ret_out: *mut #ret });
|
||||
params.insert(0, quote! { __out: *mut #ret });
|
||||
(
|
||||
quote! { () },
|
||||
quote! { unsafe { ::std::ptr::write(__ret_out, __ret) }; },
|
||||
quote!(()),
|
||||
quote! { ::std::ptr::write(__out, Self::#name(#(#args),*)) },
|
||||
)
|
||||
} else {
|
||||
let ret = &func.ret;
|
||||
(quote! { #ret }, quote! { return __ret; })
|
||||
(quote! { #ret }, quote! { Self::#name(#(#args),*) })
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
#[unsafe(export_name = #c_name)]
|
||||
unsafe extern "C" fn #rust_name(#(#params),*) -> #ret {
|
||||
let __ret = Self::#name(#(#args),*);
|
||||
#do_ret
|
||||
unsafe { #call }
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -204,35 +230,53 @@ fn generate_ffi_register(func: &FfiFunction) -> Result<TokenStream> {
|
||||
let ffi = ffi_crate();
|
||||
let lua_name = &func.lua_name;
|
||||
let c_name = &func.c_name;
|
||||
|
||||
let mut params = vec![];
|
||||
let mut asserts = vec![];
|
||||
let mut register = vec![];
|
||||
|
||||
for param in func.params.iter() {
|
||||
let name = format!("{}", pat_ident(¶m.pat)?);
|
||||
let ty = ¶m.ty;
|
||||
|
||||
params.push(match get_ffi_arg_type(ty) {
|
||||
FfiArgType::Default => quote! { b.param::<#ty>(#name); },
|
||||
});
|
||||
match get_ffi_arg_type(ty) {
|
||||
FfiArgType::Default => {
|
||||
params.push(quote! { <#ty as #ffi::FromFfi>::FromArg });
|
||||
register.push(quote! { b.param::<#ty>(#name); })
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let ret = &func.ret;
|
||||
let ret_conv = if is_unit(ret) {
|
||||
quote! { #ffi::FfiReturnConvention::Void }
|
||||
} else if func.ret_out {
|
||||
asserts.push(quote! { #ffi::__internal::assert_type_ne_all!(#ret, ()); });
|
||||
quote! { #ffi::FfiReturnConvention::OutParam }
|
||||
} else if func.ret_by_out {
|
||||
quote! { #ffi::FfiReturnConvention::ByOutParam }
|
||||
} else {
|
||||
asserts.push(quote! { #ffi::__internal::assert_type_ne_all!(#ret, ()); });
|
||||
quote! { #ffi::FfiReturnConvention::ByValue }
|
||||
};
|
||||
|
||||
let declare = quote! {
|
||||
b.declare::<unsafe extern "C" fn(#(#params),*) -> #ret>(#c_name);
|
||||
};
|
||||
|
||||
let register = match func.attrs.metatable {
|
||||
Some(ref mt) => quote! {
|
||||
b.metatable(#mt, |b| {
|
||||
#(#register)*
|
||||
b.call::<#ret>(#c_name, #ret_conv);
|
||||
});
|
||||
},
|
||||
None => quote! {
|
||||
b.index(#lua_name, |b| {
|
||||
#(#register)*
|
||||
b.call::<#ret>(#c_name, #ret_conv);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
b.index(#lua_name, |b| {
|
||||
#(#asserts)*
|
||||
#(#params)*
|
||||
b.call::<#ret>(#c_name, #ret_conv);
|
||||
});
|
||||
#declare
|
||||
#register
|
||||
})
|
||||
}
|
||||
|
||||
@@ -241,7 +285,8 @@ fn generate_ffi_exports<'a>(
|
||||
names: impl Iterator<Item = &'a Ident>,
|
||||
) -> Result<TokenStream> {
|
||||
Ok(quote! {
|
||||
// hack to prevent ffi functions from being dead code-eliminated
|
||||
// this ensures ffi function symbol exports are actually present in the resulting binary,
|
||||
// otherwise they may get dead code-eliminated before it reaches the linker
|
||||
#[used]
|
||||
static __FFI_EXPORTS: &[fn()] = unsafe {
|
||||
&[#(::std::mem::transmute(#ty::#names as *const ())),*]
|
||||
@@ -265,6 +310,7 @@ fn get_lua_functions(imp: &mut ItemImpl) -> Result<Vec<LuaFunction>> {
|
||||
&& let Some(ref abi) = abi.name
|
||||
&& abi.value() == "Lua"
|
||||
{
|
||||
// normalise inputs to PatType
|
||||
let params = func
|
||||
.sig
|
||||
.inputs
|
||||
@@ -281,6 +327,13 @@ fn get_lua_functions(imp: &mut ItemImpl) -> Result<Vec<LuaFunction>> {
|
||||
})
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
// shouldn't specify an output type
|
||||
syn_assert!(
|
||||
matches!(func.sig.output, ReturnType::Default),
|
||||
func.sig.output,
|
||||
"cannot have return type"
|
||||
);
|
||||
|
||||
funcs.push(LuaFunction {
|
||||
name: format!("{}", func.sig.ident),
|
||||
body: func.block.clone(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::env;
|
||||
use syn::*;
|
||||
use syn::{spanned::Spanned, *};
|
||||
|
||||
macro_rules! syn_error {
|
||||
($src:expr, $($fmt:expr),+) => {{
|
||||
@@ -35,11 +35,15 @@ pub fn ty_name(ty: &Type) -> Result<&Ident> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pat_ident(pat: &Pat) -> Result<&Ident> {
|
||||
match pat {
|
||||
Pat::Ident(ident) => Ok(&ident.ident),
|
||||
pub fn pat_ident(pat: &Pat) -> Result<Ident> {
|
||||
Ok(match pat {
|
||||
Pat::Ident(ident) => match ident.subpat {
|
||||
Some((_, ref subpat)) => syn_error!(subpat, "unexpected subpattern"),
|
||||
None => ident.ident.clone(),
|
||||
},
|
||||
Pat::Wild(wild) => Ident::new("_", wild.span()),
|
||||
_ => syn_error!(pat, "expected ident"),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_unit(ty: &Type) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user