use crate::{handlers::parsers::BaseFnProps, TokenStream2};
use quote::{format_ident, quote, ToTokens};
use syn::{
parse_quote,
punctuated::Punctuated,
token::{Colon, Comma, Fn, Paren, Semi},
BareFnArg, FnArg, Generics, Ident, Pat, PathArguments, TraitItemFn, Type, TypeBareFn,
Visibility,
};
use super::parsers::{BaseImplProps, LayoutFnProps};
pub fn gen_base_inner_dummy_impls(impl_bases_inner: &[BaseImplProps]) -> TokenStream2 {
if impl_bases_inner.is_empty() {
quote!()
} else {
{
let mut base_inner_dummy_impls = quote!();
for BaseImplProps {
impl_base_inner, ..
} in impl_bases_inner
{
base_inner_dummy_impls.extend({
let mut impl_base_inner_dummy = impl_base_inner.clone();
impl_base_inner_dummy.self_ty = Box::new(parse_quote!(Dummy));
impl_base_inner_dummy.into_token_stream()
});
}
quote! {
const _: () = {
struct Dummy;
#base_inner_dummy_impls
};
}
}
}
}
pub fn gen_inner_trait(
vis: &Visibility,
inner_name: &Ident,
layout_fns: &[LayoutFnProps],
generics: &Generics,
impl_bases_inner: &[BaseImplProps],
) -> TokenStream2 {
let mut inner_trait = quote!();
for LayoutFnProps {
generics,
abi,
fn_name_inner,
inputs_inner,
output,
..
} in layout_fns
{
inner_trait.extend(quote! {
#abi fn #fn_name_inner<#generics>(#inputs_inner) #output;
});
}
for BaseFnProps { fun, .. } in impl_bases_inner
.iter()
.flat_map(|BaseImplProps { fns, .. }| fns)
.rev()
{
let inner_trait_fn = TraitItemFn {
attrs: fun.attrs.clone(),
sig: fun.sig.clone(),
default: None,
semi_token: Some(Semi::default()),
};
inner_trait = quote! {
#inner_trait_fn
#inner_trait
};
}
quote! {
#vis trait #inner_name #generics {
#inner_trait
}
}
}
pub fn gen_outer_trait(
vis: &Visibility,
outer_name: &Ident,
layout_fns: &[LayoutFnProps],
generics: &Generics,
impl_bases_inner: &[BaseImplProps],
) -> TokenStream2 {
let mut outer_trait = quote!();
for LayoutFnProps {
generics,
abi,
fn_name,
inputs,
output,
..
} in layout_fns
{
outer_trait.extend(quote! {
#abi fn #fn_name<#generics>(#inputs) #output;
});
}
for BaseFnProps {
fun,
fn_name,
inputs,
..
} in impl_bases_inner
.iter()
.flat_map(|BaseImplProps { fns, .. }| fns)
.rev()
{
let mut sig = fun.sig.clone();
sig.ident = fn_name.clone();
sig.inputs.clone_from(inputs);
let trait_fn = TraitItemFn {
attrs: fun.attrs.clone(),
sig,
default: None,
semi_token: Some(Semi::default()),
};
outer_trait = quote! {
#trait_fn
#outer_trait
};
}
quote! {
#vis trait #outer_name #generics {
#outer_trait
}
}
}
pub fn gen_outer_impl(
name: &Ident,
inner_name: &Ident,
outer_name: &Ident,
layout_fns: &[LayoutFnProps],
generics: &Generics,
generic_args: &PathArguments,
impl_bases_inner: &[BaseImplProps],
self_ident: &Ident,
) -> TokenStream2 {
let mut outer_impl = quote!();
for LayoutFnProps {
generics,
abi,
fn_name,
fn_name_inner,
inputs,
input_names,
output,
..
} in layout_fns
{
outer_impl.extend(quote! {
#abi fn #fn_name<#generics>(#inputs) #output {
<#name #generic_args as #inner_name #generic_args>::#fn_name_inner(#input_names)
}
});
}
for (
BaseFnProps {
fun,
fn_name_inner,
fn_name,
inputs,
unused,
..
},
_,
base_field_name,
) in impl_bases_inner
.iter()
.flat_map(
|BaseImplProps {
fns,
base_name,
base_field_name,
..
}| {
fns.iter()
.map(|fun| (fun, base_name, base_field_name))
.collect::<Vec<_>>()
},
)
.rev()
{
let mut fun = fun.clone();
fun.sig.ident = fn_name.clone();
fun.sig.inputs.clone_from(inputs);
let args = fun.sig.inputs.clone();
let arg_names = args
.iter()
.map(|arg| match arg {
FnArg::Receiver(_) => self_ident.clone(),
FnArg::Typed(arg) => match arg.pat.as_ref() {
Pat::Ident(ident) => ident.ident.clone(),
_ => panic!("expected ident"),
},
})
.collect::<Punctuated<_, Comma>>();
let rest_arg_names = arg_names.iter().skip(1).collect::<Punctuated<_, Comma>>();
fun.block = if *unused {
parse_quote! {
{
self.#base_field_name.#fn_name(#rest_arg_names)
}
}
} else {
parse_quote! {
{
<#name as #inner_name>::#fn_name_inner(#arg_names)
}
}
};
outer_impl = quote! {
#fun
#outer_impl
};
}
quote! {
impl #generics #outer_name #generic_args for #name #generic_args {
#outer_impl
}
}
}
pub fn gen_vtable_getter(
name: &Ident,
layout_name: &Ident,
impl_bases_inner: &[BaseImplProps],
vtable: bool,
) -> TokenStream2 {
if vtable {
let vtbl = if impl_bases_inner.is_empty() {
quote! { self.vtbl }
} else {
let first_base_field_name = &impl_bases_inner[0].base_field_name;
quote! { self.#first_base_field_name.vtbl() }
};
quote! {
impl #name {
pub fn vtbl(&self) -> &'static #layout_name {
unsafe { ::std::mem::transmute(#vtbl) }
}
}
}
} else {
quote!()
}
}
pub fn gen_outer_ptr_impls(
name: &Ident,
outer_name: &Ident,
layout_fns: &[LayoutFnProps],
generics: &Generics,
generic_args: &PathArguments,
impl_bases_inner: &[BaseImplProps],
self_ident: &Ident,
this_ident: &Ident,
) -> TokenStream2 {
let mut impl_outer_const_ptr = quote!();
let mut impl_outer_mut_ptr = quote!();
for LayoutFnProps {
fun,
virt,
generics,
abi,
fn_name,
inputs,
input_names,
output,
..
} in layout_fns
{
let Type::BareFn(bare_fn_ty) = &fun.ty else {
panic!("expected bare fn type");
};
let ptr_args = {
let mut vec = inputs.clone();
vec[0].name = Some((this_ident.clone(), Colon::default()));
vec.iter()
.map(|arg| {
arg.name
.as_ref()
.expect("Only named args allowed")
.0
.clone()
})
.collect::<Punctuated<_, Comma>>()
};
let rest_arg_names = input_names.iter().skip(1).collect::<Punctuated<_, Comma>>();
let ptr_method_ty = {
let mut cloned = bare_fn_ty.clone();
cloned.inputs.first_mut().unwrap().ty = parse_quote! {&#name};
cloned.inputs.iter_mut().for_each(|input| input.name = None);
cloned
};
let ptr_call_line = if *virt {
quote! {
unsafe { core::mem::transmute::<_, #ptr_method_ty>(this.vtbl().#fn_name)(#ptr_args) }
}
} else {
quote! {
this.#fn_name(#rest_arg_names)
}
};
let const_body = if let Some(first_arg) = inputs.first()
&& let Type::Reference(ty) = &first_arg.ty
&& ty.mutability.is_some()
{
quote!(panic!("need mutable ptr to call this fn"))
} else {
quote! {
let this = unsafe { self.as_ref().unwrap() };
#ptr_call_line
}
};
impl_outer_const_ptr.extend(quote! {
#abi fn #fn_name<#generics>(#inputs) #output {
#const_body
}
});
impl_outer_mut_ptr.extend(quote! {
#abi fn #fn_name<#generics>(#inputs) #output {
let this = unsafe { self.as_mut().unwrap() };
#ptr_call_line
}
});
}
for (
BaseFnProps {
fun,
fn_name,
inputs,
output,
virt,
..
},
_,
base_field_name,
) in impl_bases_inner
.iter()
.flat_map(
|BaseImplProps {
fns,
base_name,
base_field_name,
..
}| {
fns.iter()
.map(|fun| (fun, base_name.clone(), base_field_name.clone()))
},
)
.rev()
{
let ptr_args = {
inputs
.clone()
.iter()
.map(|arg| {
if let FnArg::Typed(typed) = arg {
let Pat::Ident(pat) = typed.pat.as_ref() else {
panic!("expected ident")
};
pat.ident.clone()
} else {
this_ident.clone()
}
})
.collect::<Punctuated<_, Comma>>()
};
let arg_names = inputs
.iter()
.map(|arg| match arg {
FnArg::Receiver(_) => self_ident.clone(),
FnArg::Typed(arg) => match arg.pat.as_ref() {
Pat::Ident(ident) => ident.ident.clone(),
_ => panic!("expected ident"),
},
})
.collect::<Punctuated<_, Comma>>();
let rest_arg_names = arg_names.iter().skip(1).collect::<Punctuated<_, Comma>>();
let ptr_method_ty: TypeBareFn = {
let mut cloned = fun.sig.clone();
let FnArg::Typed(typed) = cloned.inputs.first_mut().unwrap() else {
panic!("expecter typed")
};
typed.ty = parse_quote! {&#name};
TypeBareFn {
lifetimes: None,
unsafety: None,
abi: Some(parse_quote! { extern "C" }),
fn_token: Fn::default(),
paren_token: Paren::default(),
inputs: cloned
.inputs
.iter()
.map(|arg| {
let FnArg::Typed(typed) = arg else {
panic!("expecter typed")
};
BareFnArg {
attrs: typed.attrs.clone(),
name: None,
ty: typed.ty.as_ref().clone(),
}
})
.collect(),
variadic: None,
output: cloned.output,
}
};
let ptr_call_line = if *virt {
quote! {
unsafe { core::mem::transmute::<_, #ptr_method_ty>(this.#base_field_name.vtbl().#fn_name)(#ptr_args) }
}
} else {
quote! {
this.#fn_name(#rest_arg_names)
}
};
let const_body = if let Some(FnArg::Receiver(first_arg)) = inputs.first()
&& let Type::Reference(ty) = &*first_arg.ty
&& ty.mutability.is_some()
{
quote!(panic!("need mutable ptr to call this fn"))
} else {
quote! {
let this = unsafe { self.as_ref().unwrap() };
#ptr_call_line
}
};
impl_outer_const_ptr = quote! {
extern "C" fn #fn_name(#inputs) #output {
#const_body
}
#impl_outer_const_ptr
};
impl_outer_mut_ptr = quote! {
extern "C" fn #fn_name(#inputs) #output {
let this = unsafe { self.as_mut().unwrap() };
#ptr_call_line
}
#impl_outer_mut_ptr
};
}
let impl_outer_const_ptr = quote! {
impl #generics #outer_name #generic_args for *const #name #generic_args {
#impl_outer_const_ptr
}
};
let impl_outer_mut_ptr = quote! {
impl #generics #outer_name #generic_args for *mut #name #generic_args {
#impl_outer_mut_ptr
}
};
quote! {
#impl_outer_const_ptr
#impl_outer_mut_ptr
}
}
pub fn gen_base_init_line(
name: &Ident,
inner_name: &Ident,
impl_bases_inner: &[BaseImplProps],
vtable: bool,
) -> TokenStream2 {
if !impl_bases_inner.is_empty() {
let mut res = quote!();
for (
i,
BaseImplProps {
fns,
base_name,
base_field_name,
..
},
) in impl_bases_inner.iter().enumerate()
{
let vtable_part = if vtable && i == 0 {
quote! {
#base_name::__with_vtbl(vtbl)
}
} else if vtable {
let base_layout_name = format_ident!("{base_name}Layout");
let mut vtable_fns = quote!();
for BaseFnProps {
fn_name_inner,
fn_name,
inputs_inner,
unused,
virt,
..
} in fns
{
if !virt {
continue;
}
let arg_num = inputs_inner.len();
let mut arg_infer = quote!(_);
for _ in 0..arg_num - 1 {
arg_infer.extend(quote!(,_));
}
vtable_fns.extend(if *unused {
quote! {
#fn_name: unsafe { core::mem::transmute(#base_name::default_vtable().#fn_name) },
}
} else {
quote! {
#fn_name: unsafe { core::mem::transmute(<#name as #inner_name>::#fn_name_inner as extern "C" fn(#arg_infer) -> _) },
}
});
}
quote! {
#base_name::__with_vtbl(Box::leak(Box::new(#base_layout_name {
#vtable_fns
})) as *const _ as usize)
}
} else {
quote!(::std::default::Default::default())
};
res.extend(quote! { #base_field_name: #vtable_part, });
}
res
} else if vtable {
quote! { vtbl, }
} else {
quote!()
}
}
pub fn gen_construct_with_vtable(
default: bool,
vis: &Visibility,
name: &Ident,
vtable: bool,
base_init_line: &TokenStream2,
field_line: &TokenStream2,
) -> TokenStream2 {
if default && vtable {
quote! {
impl #name {
#[doc(hidden)]
#vis fn __with_vtbl(vtbl: usize) -> Self {
Self {
#base_init_line
#field_line
}
}
}
}
} else {
quote!()
}
}
pub fn gen_default_vtable(
name: &Ident,
inner_name: &Ident,
default_vtable_name: &Ident,
layout_name: &Ident,
layout_fns: &[LayoutFnProps],
impl_bases_inner: &[BaseImplProps],
vtable: bool,
) -> TokenStream2 {
let mut default_vtable = quote!();
for LayoutFnProps {
virt,
fn_name,
fn_name_inner,
inputs_inner,
..
} in layout_fns
{
if !*virt {
continue;
}
let arg_num = inputs_inner.len();
let mut arg_infer = quote!(_);
for _ in 0..arg_num - 1 {
arg_infer.extend(quote!(,_));
}
default_vtable.extend(quote! {
#fn_name: unsafe { core::mem::transmute(<#name as #inner_name>::#fn_name_inner as extern "C" fn(#arg_infer) -> _) },
});
}
if let Some(BaseImplProps {
fns,
base_vtable_name,
..
}) = impl_bases_inner.first()
&& fns.iter().any(|BaseFnProps { virt, .. }| *virt)
{
let mut fns_vals = quote!();
for BaseFnProps {
fn_name_inner,
fn_name,
inputs_inner,
unused,
virt,
..
} in fns
{
if !virt {
continue;
}
let arg_num = inputs_inner.len();
let mut arg_infer = quote!(_);
for _ in 0..arg_num - 1 {
arg_infer.extend(quote!(,_));
}
if *unused {
fns_vals.extend(quote! {
#fn_name: unsafe { core::mem::transmute(#base_vtable_name.#fn_name) },
});
} else {
fns_vals.extend(quote! {
#fn_name: unsafe { core::mem::transmute(<#name as #inner_name>::#fn_name_inner as extern "C" fn(#arg_infer) -> _) },
});
}
}
default_vtable = quote! {
#fns_vals
#default_vtable
};
}
if vtable {
quote! {
static #default_vtable_name: #layout_name = #layout_name {
#default_vtable
};
impl #name {
fn default_vtable() -> &'static #layout_name {
&#default_vtable_name
}
}
}
} else {
quote!()
}
}
pub fn gen_construct_self(
vtable: bool,
impl_bases_inner: &[BaseImplProps],
base_init_line: &TokenStream2,
field_line: &TokenStream2,
) -> TokenStream2 {
if vtable {
quote! {
Self::__with_vtbl(Self::default_vtable() as *const _ as usize)
}
} else if !impl_bases_inner.is_empty() {
quote! {
Self {
#base_init_line
#field_line
}
}
} else {
quote! {
Self { #field_line }
}
}
}
pub fn gen_default_impl(
default: bool,
name: &Ident,
construct_self: &TokenStream2,
) -> TokenStream2 {
if default {
quote! {
impl ::std::default::Default for #name {
#[allow(clippy::needless_update)]
fn default() -> Self {
#construct_self
}
}
}
} else {
quote!()
}
}
pub fn gen_cast_ptr(name: &Ident, impl_bases_inner: &[BaseImplProps]) -> TokenStream2 {
let mut cast_ptr = quote!();
for BaseImplProps {
base_name,
base_field_name,
..
} in impl_bases_inner
{
cast_ptr.extend({
quote! {
impl cpp_oop::CastPtr<#name> for #base_name {
fn cast_const(input: *const #name) -> *const Self {
unsafe { &input.as_ref().unwrap().#base_field_name as *const Self }
}
fn cast_mut(input: *mut #name) -> *mut Self {
unsafe { &mut input.as_mut().unwrap().#base_field_name as *mut Self }
}
}
}
});
}
cast_ptr
}