1#![recursion_limit = "128"]
2extern crate proc_macro;
3
4use quote::quote;
5use syn::spanned::Spanned;
6use syn::visit_mut::visit_type_mut;
7use syn::{
8 parse_macro_input, parse_quote, FnArg, Ident, ItemTrait, Lifetime, ReturnType, TraitItem,
9 TraitItemMethod, Type,
10};
11
12type Error = syn::parse::Error;
13
14#[proc_macro_attribute]
15pub fn shadow(
16 _args: proc_macro::TokenStream,
17 item_tokens: proc_macro::TokenStream,
18) -> proc_macro::TokenStream {
19 let item_tokens_clone = item_tokens.clone();
20 let item_tokens = proc_macro2::TokenStream::from(item_tokens);
21 let shadow = parse_macro_input!(item_tokens_clone as ItemTrait);
22
23 let shadow_tokens = match generate_shadow(shadow) {
24 Err(err) => err.to_compile_error().into(),
25 Ok(tokens) => tokens,
26 };
27
28 let output = quote! {
29 #item_tokens
30
31 #shadow_tokens
32 };
33 output.into()
34}
35
36fn generate_shadow(mut shadow_trait: ItemTrait) -> Result<proc_macro2::TokenStream, Error> {
37 let target_ident = shadow_trait.ident.clone();
38 let shadow_ident = Ident::new(&format!("{}Shadow", target_ident), target_ident.span());
39 let details_ident = Ident::new(&format!("{}ShadowInfo", target_ident), target_ident.span());
40 let bind_box_ident = Ident::new(&format!("bind_{}_box", target_ident), target_ident.span());
41 let visibility = shadow_trait.vis.clone();
42
43 shadow_trait.ident = shadow_ident.clone();
44
45 shadow_trait.unsafety = Some(syn::token::Unsafe {
47 span: shadow_trait.ident.span(),
48 });
49
50 if !shadow_trait.supertraits.is_empty() {
51 return Err(Error::new(
52 shadow_trait.supertraits.span(),
53 "supertraits are not supported yet!",
54 ));
55 }
56
57 let mut funcs = Vec::new();
58 for item in &mut shadow_trait.items {
59 if let TraitItem::Method(ref mut method) = item {
60 let (lifetime, mutability) = self_lifetime(method)?.clone();
61 let first_mut = method.sig.decl.inputs.iter_mut().next().unwrap();
63
64 *first_mut = parse_quote! { data: &#lifetime #mutability Self::Data };
65
66 let mut arg_types = Vec::new();
68 for arg in &method.sig.decl.inputs {
69 if let FnArg::Captured(arg) = arg {
70 let mut ty = arg.ty.clone();
71 visit_type_mut(&mut EraseLifetimes {}, &mut ty);
72 arg_types.push(ty);
73 } else {
74 return Err(Error::new(arg.span(), "argument not supported"));
75 }
76 }
77
78 let mut ret_type: Type = match method.sig.decl.output {
80 ReturnType::Default => parse_quote! { () },
81 ReturnType::Type(_, ref ty) => ty.as_ref().clone(),
82 };
83 visit_type_mut(&mut EraseLifetimes {}, &mut ret_type);
84
85 let func_ident = &method.sig.ident;
86 funcs.push(quote! {
87 traitor::details::ShadowLayout {
88 ret_slots: ret_slots::<#ret_type>(),
89 arg_slots: #(slots::<#arg_types>())+*,
90 func_ptr: <S as #shadow_ident>::#func_ident as *const u8,
91 }
92 });
93
94 method
98 .sig
99 .decl
100 .inputs
101 .push(parse_quote! { meta: &#lifetime Self });
102 }
103 }
104
105 shadow_trait.items.push(parse_quote! {
108 type Data: Sized;
109 });
110
111 let details_struct = quote! {
112 #visibility struct #details_ident<'meta, S>
113 where
114 S: #shadow_ident + 'meta,
115 {
116 inner: S,
117 _phantom: std::marker::PhantomData<&'meta S>,
118 }
119 };
120
121 let details_impl = quote! {
122 impl<'meta, S> #details_ident<'meta, S>
123 where
124 S: #shadow_ident + 'meta,
125 {
126 pub fn new(meta: S) -> Self {
127 Self {
128 inner: meta,
129 _phantom: Default::default(),
130 }
131 }
132 }
133 };
134
135 let details_binding = quote! {
137 impl<'meta, S> traitor::details::InternalBindingInfo for #details_ident<'meta, S>
138 where
139 S: #shadow_ident + 'meta,
140 {
141 type Data = <S as #shadow_ident>::Data;
142 type Shadow = S;
143 type Target = #target_ident;
144
145 fn layout() -> traitor::details::LayoutInfo {
146 const US: usize = std::mem::size_of::<usize>();
147 fn slots<T>() -> usize {
148 match (std::mem::size_of::<T>() + US - 1) / US {
149 v if v <= 2 => v,
150 _ => 1,
152 }
153 }
154 fn ret_slots<T>() -> usize {
155 match (std::mem::size_of::<T>() + US - 1) / US {
156 v if v > 2 => 1,
158 _ => 0,
160 }
161 }
162 traitor::details::LayoutInfo {
163 shadow: vec![
164 #(#funcs),*
165 ],
166 other: vec![
167 ],
172 }
173 }
174
175 fn into_shadow(self) -> S {
176 self.inner
177 }
178 }
179
180 };
181
182 let bind_box = quote! {
183 pub fn #bind_box_ident<'b, D>(
184 data: Box<D>,
185 binder: &'b traitor::Binder<D, #target_ident>
186 ) -> Box<#target_ident + 'b> {
187 unsafe {
188 let raw = Box::into_raw(data);
189 let bound = binder.bind_mut(std::mem::transmute::<*mut D, &mut D>(raw));
190 Box::from_raw(bound)
191 }
192 }
193 };
194
195 let result = quote! {
196 #shadow_trait
197
198 #details_struct
199 #details_impl
200 #details_binding
201 #bind_box
202 };
203 Ok(result)
204}
205
206fn self_lifetime(method: &TraitItemMethod) -> Result<(Lifetime, Option<syn::token::Mut>), Error> {
207 match method.sig.decl.inputs.first().as_ref().map(|v| v.value()) {
208 Some(FnArg::SelfRef(self_ref)) => {
209 let lifetime = self_ref.lifetime.as_ref().ok_or_else(|| {
210 Error::new(
211 self_ref.self_token.span,
212 "&self argument must have an explicit lifetime!",
213 )
214 })?;
215
216 Ok((lifetime.clone(), self_ref.mutability.clone()))
217 }
218 _ => {
219 return Err(Error::new(
220 method.sig.ident.span(),
221 "function must be object-safe and have `&self` as a first argument!",
222 ));
223 }
224 }
225}
226
227struct EraseLifetimes {}
228
229impl syn::visit_mut::VisitMut for EraseLifetimes {
230 fn visit_type_reference_mut(&mut self, tr: &mut syn::TypeReference) {
231 tr.lifetime = None;
232 syn::visit_mut::visit_type_reference_mut(self, tr)
233 }
234
235 fn visit_angle_bracketed_generic_arguments_mut(
236 &mut self,
237 list: &mut syn::AngleBracketedGenericArguments,
238 ) {
239 let old = std::mem::replace(&mut list.args, Default::default());
240 for item in old {
241 if let syn::GenericArgument::Lifetime(_) = item {
242 } else {
244 list.args.push(item);
245 }
246 }
247 syn::visit_mut::visit_angle_bracketed_generic_arguments_mut(self, list)
248 }
249}