use std::ops::Deref; use proc_macro::TokenStream; use quote::quote; use syn::{Fields, ImplItem, Path}; mod method_helpers; use method_helpers::{filter_overrides, make_extern_c, remove_override_attr}; mod parsers; use parsers::{InheritImplAttr, NamedField}; mod vtable; use vtable::generate_vtable_const; #[proc_macro_attribute] pub fn inherit_from(attr: TokenStream, item: TokenStream) -> TokenStream { let mut struct_def = syn::parse_macro_input!(item as syn::ItemStruct); let ty = syn::parse_macro_input!(attr as syn::Type); let fields = match struct_def.fields { Fields::Named(ref mut fields) => &mut fields.named, Fields::Unit => { struct_def.fields = Fields::Named(syn::parse_quote!({})); if let Fields::Named(ref mut fields) = struct_def.fields { &mut fields.named } else { unreachable!() } } _ => panic!("Tuple-type structs cannot inherit from classes"), }; let base_field: NamedField = syn::parse_quote!( _base: #ty ); fields.insert(0, base_field.0); let struct_name = &struct_def.ident; struct_def.attrs.push(syn::parse_quote! { #[repr(C)] }); quote!( #struct_def impl ::core::ops::Deref for #struct_name { type Target = #ty; fn deref(&self) -> &Self::Target { &self._base } } impl ::core::ops::DerefMut for #struct_name { fn deref_mut(&mut self) -> &mut Self::Target { &mut self._base } } ) .into() } fn into_path_segment(ident: &&syn::Ident) -> syn::PathSegment { (*ident).clone().into() } #[proc_macro_attribute] pub fn inherit_from_impl(attr: TokenStream, item: TokenStream) -> TokenStream { let mut impl_block = syn::parse_macro_input!(item as syn::ItemImpl); let InheritImplAttr { class, header, .. } = syn::parse_macro_input!(attr as InheritImplAttr); let header = header.value(); // List of methods with #[overridden] attrbiute let mut override_items = impl_block .items .iter_mut() .filter_map(|item| { if let ImplItem::Method(ref mut method) = item { filter_overrides(method) } else { None } }) .collect::>(); // Make all override methods `extern "C"` override_items.iter_mut().for_each(make_extern_c); // Remove fake overridden attributes override_items.iter_mut().for_each(remove_override_attr); let vtable_info = vtable::get_vtable_info(&header, &class.to_string()); // List of method override names let override_list = override_items .into_iter() .map(|method| method.sig.ident.clone()) .collect::>(); let type_ident = match *impl_block.self_ty { syn::Type::Path(ref path) => path.path.get_ident().expect("Class type must be an ident"), _ => panic!("Class type must be an ident"), // Error about how class type must be ident }; match vtable_info.get(&class.to_string()) { Some(base_type_vtable) => { // Generate a vtable before overrides let base_vtable: Vec> = vec![None; base_type_vtable.len()]; let mut vtable = base_vtable; // Apply each override to the base vtable for o in override_list { match base_type_vtable.binary_search_by_key(&&o.to_string(), |entry| &entry.name) { Ok(index) => { vtable[index] = Some(Path { leading_colon: None, // $class::$method segments: [&type_ident, &o].iter().map(into_path_segment).collect(), }); } Err(..) => panic!("Cannot override a virtual method that doesn't exist in the original vtable"), } } let mut bindings_to_gen = vec![]; let vtable = vtable .into_iter() .enumerate() .map(|(i, x)| { x.unwrap_or_else(|| { bindings_to_gen.push(base_type_vtable[i].default.deref()); vtable::get_binding_symbol(&base_type_vtable[i].default).into() }) }) .collect(); let self_type = &impl_block.self_ty; let vtable_const = generate_vtable_const(vtable, self_type); let bindings = bindings_to_gen.into_iter().map(vtable::generate_binding); quote!( #impl_block #vtable_const #( #bindings )* ) .into() } None => panic!("Class does not exist in header"), // add compiler error for class not existing in header } }