Refactor luaffi proc-macro

This commit is contained in:
2025-06-24 19:30:39 +10:00
parent f8e7b8ae62
commit 3dd375b071
6 changed files with 740 additions and 522 deletions

View File

@@ -1,8 +1,8 @@
use crate::utils::{ffi_crate, syn_assert, syn_error};
use darling::FromMeta;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{ext::IdentExt, *};
use quote::{format_ident, quote, quote_spanned};
use syn::{ext::IdentExt, spanned::Spanned, *};
#[derive(Debug, FromMeta)]
pub struct Args {}
@@ -24,35 +24,39 @@ pub fn transform(_args: Args, mut item: Item) -> Result<TokenStream> {
let mod_name = format_ident!("__{name}_cdef");
Ok(quote! {
Ok(quote!(
#[repr(C)]
#[allow(non_camel_case_types)]
#item
#[doc(hidden)]
#[allow(unused, non_snake_case)]
/// Automatically generated by luaffi.
mod #mod_name {
use super::*;
#impl_type
#impl_cdef
}
})
))
}
fn generate_type(ty: &Ident) -> Result<TokenStream> {
let ffi = ffi_crate();
let fmt = quote!(::std::format!);
let name = LitStr::new(&format!("{}", ty.unraw()), ty.span());
let cdecl_fmt = LitStr::new(&format!("struct {} {{}}", ty.unraw()), ty.span());
let span = ty.span();
let name = LitStr::new(&ty.unraw().to_string(), span);
Ok(quote! {
Ok(quote_spanned!(span =>
unsafe impl #ffi::Type for #ty {
fn name() -> impl ::std::fmt::Display {
#name
}
fn ty() -> #ffi::TypeType {
#ffi::TypeType::Aggregate
}
fn cdecl(name: impl ::std::fmt::Display) -> impl ::std::fmt::Display {
#fmt(#cdecl_fmt, name)
::std::format!("struct {} {name}", #name)
}
fn build(b: &mut #ffi::TypeBuilder) {
@@ -62,10 +66,10 @@ fn generate_type(ty: &Ident) -> Result<TokenStream> {
// SAFETY: we can always implement `IntoFfi` because it transfers ownership from Rust to Lua
unsafe impl #ffi::IntoFfi for #ty {
type To = Self;
fn convert(self) -> Self::To { self }
type Into = Self;
fn convert(self) -> Self::Into { self }
}
})
))
}
fn generate_cdef_structure(str: &mut ItemStruct) -> Result<TokenStream> {
@@ -77,13 +81,14 @@ fn generate_cdef_structure(str: &mut ItemStruct) -> Result<TokenStream> {
let ffi = ffi_crate();
let ty = &str.ident;
let build = generate_build_cdef(&to_cfields(&mut str.fields)?)?;
let span = ty.span();
let build = generate_cdef_build(&get_cfields(&mut str.fields)?)?;
Ok(quote! {
Ok(quote_spanned!(span =>
unsafe impl #ffi::Cdef for #ty {
fn build(b: &mut #ffi::CdefBuilder) { #build }
}
})
))
}
fn generate_cdef_enum(enu: &mut ItemEnum) -> Result<TokenStream> {
@@ -95,22 +100,24 @@ fn generate_cdef_enum(enu: &mut ItemEnum) -> Result<TokenStream> {
let ffi = ffi_crate();
let ty = &enu.ident;
let span = ty.span();
let build = enu
.variants
.iter_mut()
.map(|variant| {
let build = generate_build_cdef(&to_cfields(&mut variant.fields)?)?;
Ok(quote! { b.inner_struct(|b| { #build }); })
let span = variant.span();
let build = generate_cdef_build(&get_cfields(&mut variant.fields)?)?;
Ok(quote_spanned!(span => b.inner_struct(|b| { #build })))
})
.collect::<Result<Vec<_>>>()?;
Ok(quote! {
Ok(quote_spanned!(span =>
unsafe impl #ffi::Cdef for #ty {
fn build(b: &mut #ffi::CdefBuilder) {
b.field::<::std::ffi::c_int>("__tag").inner_union(|b| { #(#build)* });
b.field::<::std::ffi::c_int>("__tag").inner_union(|b| { #(#build;)* });
}
}
})
))
}
struct CField {
@@ -124,7 +131,7 @@ struct CFieldAttrs {
opaque: bool,
}
fn to_cfields(fields: &mut Fields) -> Result<Vec<CField>> {
fn get_cfields(fields: &mut Fields) -> Result<Vec<CField>> {
match fields {
Fields::Named(fields) => fields.named.iter_mut(),
Fields::Unnamed(fields) => fields.unnamed.iter_mut(),
@@ -134,17 +141,17 @@ fn to_cfields(fields: &mut Fields) -> Result<Vec<CField>> {
.map(|(i, field)| {
Ok(CField {
name: match field.ident {
Some(ref name) => format!("{}", name.unraw()),
Some(ref name) => name.unraw().to_string(),
None => format!("__{i}"),
},
ty: field.ty.clone(),
attrs: parse_attrs(&mut field.attrs)?,
attrs: parse_cfield_attrs(&mut field.attrs)?,
})
})
.collect()
}
fn parse_attrs(attrs: &mut Vec<Attribute>) -> Result<CFieldAttrs> {
fn parse_cfield_attrs(attrs: &mut Vec<Attribute>) -> Result<CFieldAttrs> {
let mut parsed = CFieldAttrs::default();
let mut i = 0;
while let Some(attr) = attrs.get(i) {
@@ -161,11 +168,11 @@ fn parse_attrs(attrs: &mut Vec<Attribute>) -> Result<CFieldAttrs> {
Ok(parsed)
}
fn generate_build_cdef(fields: &[CField]) -> Result<TokenStream> {
let mut body = vec![quote! {
fn generate_cdef_build(fields: &[CField]) -> Result<TokenStream> {
let mut body = vec![quote!(
let mut offset = 0;
let mut align = 1;
}];
)];
fn offset(i: usize) -> Ident {
format_ident!("offset{i}")
@@ -174,40 +181,41 @@ fn generate_build_cdef(fields: &[CField]) -> Result<TokenStream> {
let max = quote!(::std::cmp::Ord::max);
let size_of = quote!(::std::mem::size_of);
let align_of = quote!(::std::mem::align_of);
for (i, field) in fields.iter().enumerate() {
let ty = &field.ty;
let offset = offset(i);
body.push(quote! {
body.push(quote_spanned!(ty.span() =>
// round up current offset to the alignment of field type for field offset
offset = (offset + #align_of::<#ty>() - 1) & !(#align_of::<#ty>() - 1);
align = #max(align, #align_of::<#ty>());
let #offset = offset;
offset += #size_of::<#ty>();
});
));
}
body.push(quote! {
body.push(quote!(
// round up final offset to the total alignment of struct for struct size
let size = (offset + align - 1) & !(align - 1);
});
));
let len = fields.len();
for (i, field) in fields.iter().enumerate() {
let name = &field.name;
let ty = &field.ty;
if field.attrs.opaque {
body.push(if i == len - 1 {
body.push(if field.attrs.opaque {
if i == len - 1 {
let a = offset(i);
quote! { b.field_opaque(size - #a); } // last field
quote_spanned!(ty.span() => b.field_opaque(size - #a);) // last field
} else {
let a = offset(i);
let b = offset(i + 1);
quote! { b.field_opaque(#b - #a); }
});
quote_spanned!(ty.span() => b.field_opaque(#b - #a);)
}
} else {
body.push(quote! { b.field::<#ty>(#name); });
}
quote_spanned!(ty.span() => b.field::<#ty>(#name);)
});
}
Ok(quote! { #(#body)* })
Ok(quote!(#(#body)*))
}