use crate::{
attr::StageStash,
vtable::{VtableFnArg, VtableItem},
};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::{token::Colon, Abi, BareFnArg, Path, Signature};
pub fn generate_repr(
stash: &mut StageStash,
inline_vtable: bool,
path_to_box: Path,
drop_abi: Option<&Abi>,
store_layout: bool,
) -> TokenStream {
let StageStash {
repr_name,
vtable_name,
trait_name,
vtable_items,
..
} = stash;
let (vtable_contents, thunk_methods) = generate_vtable_and_thunks(
&trait_name,
&repr_name,
vtable_items.iter().cloned(),
|_| true, );
let (vtable_field_type, ctor_val) = if inline_vtable {
let vtable_field_type = vtable_name.to_token_stream();
let ctor_val = quote! {
Self {
__thintraitobjectmacro_repr_vtable: Self::__THINTRAITOBJECTMACRO_VTABLE,
__thintraitobjectmacro_repr_value: __thintraitobjectmacro_arg0,
}
};
(vtable_field_type, ctor_val)
} else {
let vtable_field_type = quote! {
&'static #vtable_name
};
let ctor_val = quote! {
Self {
__thintraitobjectmacro_repr_vtable: &Self::__THINTRAITOBJECTMACRO_VTABLE,
__thintraitobjectmacro_repr_value: __thintraitobjectmacro_arg0,
}
};
(vtable_field_type, ctor_val)
};
let size_and_align = if store_layout {
quote! {
size: ::core::mem::size_of::<__ThinTraitObjectMacro_ReprGeneric0>(),
align: ::core::mem::align_of::<__ThinTraitObjectMacro_ReprGeneric0>(),
}
} else {
quote! {}
};
let repr = quote! {
#[repr(C)]
struct #repr_name <__ThinTraitObjectMacro_ReprGeneric0: #trait_name> {
__thintraitobjectmacro_repr_vtable: #vtable_field_type,
__thintraitobjectmacro_repr_value: __ThinTraitObjectMacro_ReprGeneric0,
}
impl<
__ThinTraitObjectMacro_ReprGeneric0: #trait_name
> #repr_name<__ThinTraitObjectMacro_ReprGeneric0> {
const __THINTRAITOBJECTMACRO_VTABLE: #vtable_name = #vtable_name {
#size_and_align
#vtable_contents
drop: Self :: __thintraitobjectmacro_repr_drop,
};
fn __thintraitobjectmacro_repr_create(
__thintraitobjectmacro_arg0: __ThinTraitObjectMacro_ReprGeneric0,
) -> *mut #vtable_name {
#path_to_box::into_raw(#path_to_box::new(#ctor_val)) as *mut _
}
unsafe #drop_abi fn __thintraitobjectmacro_repr_drop(
__thintraitobjectmacro_arg0: *mut ::core::ffi::c_void,
) {
let _ = #path_to_box::from_raw(
__thintraitobjectmacro_arg0
as *mut #repr_name<__ThinTraitObjectMacro_ReprGeneric0>
);
}
#thunk_methods
}
};
repr
}
#[inline]
pub fn repr_name_from_trait_name(trait_name: Ident) -> Ident {
format_ident!("__ThinTraitObjectMacro_ReprFor{}", trait_name)
}
fn generate_vtable_and_thunks(
trait_name: &Ident,
repr_name: &Ident,
vtable_entries: impl IntoIterator<Item = VtableItem>,
mut double_hop_predicate: impl FnMut(&VtableItem) -> bool,
) -> (TokenStream, TokenStream) {
let mut vtable_contents = TokenStream::new();
let mut thunk_methods = TokenStream::new();
for mut entry in vtable_entries {
let double_hop = double_hop_predicate(&entry);
let has_receiver = entry.make_raw();
if has_receiver {
entry.make_unsafe();
}
let mut argument_counter = 1_u32;
let thunk_call_args = entry.inputs.clone().into_iter().skip(1).map(|x| {
let arg = to_nth_thunk_arg(x, argument_counter);
argument_counter += 1;
arg
});
if double_hop {
let name = entry.name.clone();
let thunk_name = format_ident!("__thintraitobjectmacro_thunk_{}", &entry.name);
let thunk_signature = {
let mut signature = entry.into_signature(nth_arg);
signature.ident = thunk_name.clone();
signature
};
write_vtable_thunk_entry(&name, &thunk_name, &mut vtable_contents);
write_thunk(
&name,
&repr_name,
thunk_signature,
thunk_call_args,
&mut thunk_methods,
);
} else {
write_vtable_single_hop_entry(&entry.name, &trait_name, &mut vtable_contents);
}
}
(vtable_contents, thunk_methods)
}
fn write_vtable_thunk_entry(name: &Ident, val: &Ident, out: &mut TokenStream) {
(quote! {
#name: Self :: #val,
})
.to_tokens(out);
}
fn write_vtable_single_hop_entry(name: &Ident, trait_name: &Ident, out: &mut TokenStream) {
(quote! {
#name: <__ThinTraitObjectMacro_ReprGeneric0 as #trait_name> :: #name,
})
.to_tokens(out);
}
fn write_thunk(
name: &Ident,
repr_name: &Ident,
signature: Signature,
args: impl IntoIterator<Item = BareFnArg>,
out: &mut TokenStream,
) {
let args = args.into_iter().map(|arg| arg.name.unwrap().0);
(quote! {
#signature {
(
*(__thintraitobjectmacro_arg0
as *mut #repr_name<__ThinTraitObjectMacro_ReprGeneric0>
)
).__thintraitobjectmacro_repr_value.#name(#(#args)*)
}
})
.to_tokens(out);
}
fn nth_arg(n: u32) -> Ident {
format_ident!("__thintraitobjectmacro_arg{}", n)
}
fn to_nth_thunk_arg(arg: VtableFnArg, n: u32) -> BareFnArg {
let mut arg = arg.into_bare_arg_with_ptr_receiver();
arg.name = Some(arg.name.unwrap_or_else(|| (nth_arg(n), Colon::default())));
arg
}