1#![recursion_limit="256"]
2
3extern crate proc_macro;
10extern crate proc_macro2;
11extern crate syn;
12#[macro_use]
13extern crate quote;
14use proc_macro::TokenStream;
15use proc_macro2::Span;
16use syn::Meta::{List, NameValue, Word};
17use syn::NestedMeta::Meta;
18
19#[proc_macro_derive(RTTI, attributes(rtti))]
20pub fn macro_rtti(input: TokenStream) -> TokenStream {
21 let ast: syn::DeriveInput = syn::parse(input).unwrap();
22 let gen = impl_rtti(&ast);
23 gen.into()
24}
25
26fn impl_rtti(ast: &syn::DeriveInput) -> quote::Tokens {
27
28 let ident = ast.ident;
29 let name = ast.ident.to_string();
30 let visibility = translate_visibility(&ast.vis);
31 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
32 let dummy_const = syn::Ident::new(&format!("_IMPL_RTTI_FOR_{}", ident), Span::def_site());
33 let dummy_type = dummy_type();
34
35 let body = if let syn::Data::Struct(ref data) = ast.data {
36 if let syn::Fields::Named(ref fields) = data.fields {
37
38 let idents: Vec<_> = fields.named.iter().map(|field| field.ident.unwrap()).collect();
41 let names: Vec<_> = idents.iter().map(|ident| ident.to_string()).collect();
42 let visibilities: Vec<_> = fields.named.iter().map(|field| translate_visibility(&field.vis)).collect();
43
44 let types: Vec<_> = fields.named.iter().map(|field| {
45 if parse_attr_ignore(&field.attrs) {
46 &dummy_type
47 } else {
48 &field.ty
49 }
50 }).collect();
51
52 let hints: Vec<_> = fields.named.iter().map(|field| {
53 parse_attr_hint(&field.attrs)
54 }).collect();
55
56 quote! {
57 Type::Struct(Struct {
58 name: #name,
59 vis: Visibility::#visibility,
60 size: ::std::mem::size_of::<#ident #impl_generics>(),
61 fields: {
62 let mut fields = Vec::new();
63 let dummy: #ident #impl_generics = unsafe { ::std::mem::uninitialized() };
64 #(
65 fields.push((#names, Field {
66 vis: Visibility::#visibilities,
67 offset: {
68 let dummy_ref = &dummy;
69 let field_ref = &dummy.#idents;
70 (field_ref as *const _ as usize) - (dummy_ref as *const _ as usize)
71 },
72 ty: <#types>::ctti(),
73 hints: {
74 let mut hints = Vec::new();
75 #(
76 hints.push(#hints);
77 )*
78 hints
79 }
80 }));
81 )*
82 std::mem::forget(dummy);
83 fields
84 }
85 })
86 }
87
88 } else if let syn::Fields::Unnamed(ref fields) = data.fields {
89
90 let visibilities: Vec<_> = fields.unnamed.iter().map(|field| translate_visibility(&field.vis)).collect();
93 let indices: Vec<_> = (0..visibilities.len()).map(|x| syn::Index::from(x)).collect();
94
95 let types: Vec<_> = fields.unnamed.iter().map(|field| {
96 if parse_attr_ignore(&field.attrs) {
97 &dummy_type
98 } else {
99 &field.ty
100 }
101 }).collect();
102
103 let hints: Vec<_> = fields.unnamed.iter().map(|field| {
104 parse_attr_hint(&field.attrs)
105 }).collect();
106
107 quote! {
108 Type::Tuple(Tuple {
109 name: #name,
110 vis: Visibility::#visibility,
111 size: ::std::mem::size_of::<#ident #impl_generics>(),
112 fields: {
113 let mut fields = Vec::new();
114 let dummy: #ident #impl_generics = unsafe { ::std::mem::uninitialized() };
115 #(
116 fields.push(Field {
117 vis: Visibility::#visibilities,
118 offset: {
119 let dummy_ref = &dummy;
120 let field_ref = &(dummy.#indices);
121 (field_ref as *const _ as usize) - (dummy_ref as *const _ as usize)
122 },
123 ty: <#types>::ctti(),
124 hints: {
125 let mut hints = Vec::new();
126 #(
127 hints.push(#hints);
128 )*
129 hints
130 }
131 });
132 )*
133 std::mem::forget(dummy);
134 fields
135 }
136 })
137 }
138 } else {
139 panic!("#[derive(RTTI)] NYI unit struct.");
140 }
141 } else if let syn::Data::Enum(ref data) = ast.data {
142
143 let variants = &data.variants;
144 let idents: Vec<_> = variants.iter().map(|variant| variant.ident).collect();
145 let names: Vec<_> = idents.iter().map(|ident| ident.to_string()).collect();
146
147 let variant_hints: Vec<_> = variants.iter().map(|variant| {
148 parse_attr_hint(&variant.attrs)
149 }).collect();
150
151 let field_types: Vec<Vec<_>> = variants.iter().map(|variant| {
152 variant.fields.iter().map(|field| {
153 if parse_attr_ignore(&field.attrs) {
154 &dummy_type
155 } else {
156 &field.ty
157 }
158 }).collect()
159 }).collect();
160
161 let field_hints: Vec<Vec<_>> = variants.iter().map(|variant| {
162 variant.fields.iter().map(|field| parse_attr_hint(&field.attrs)).collect()
163 }).collect();
164
165 quote! {
166 Type::Enum(Enum {
167 name: #name,
168 vis: Visibility::#visibility,
169 size: ::std::mem::size_of::<#ident #impl_generics>(),
170 variants: {
171 let mut variants = Vec::new();
172 #(
174 variants.push((#names, Variant {
175 fields: {
176 let mut fields = Vec::new();
177 #(
178 fields.push(Field {
179 vis: Visibility::Public,
180 offset: 0, ty: <#field_types>::ctti(),
186 hints: {
187 let mut hints = Vec::new();
188 #(
189 hints.push(#field_hints);
190 )*
191 hints
192 }
193 });
194 )*
195 fields
196 },
197 hints: {
198 let mut hints = Vec::new();
199 #(
200 hints.push(#variant_hints);
201 )*
202 hints
203 }
204 }));
205 )*
206 variants
208 }
209 })
210 }
211 } else {
212 panic!("#[derive(RTTI)] NYI union");
213 };
214
215 quote! {
216 #[allow(non_upper_case_globals,unused_mut)]
217 const #dummy_const: () = {
218 extern crate rtti;
219 use rtti::*;
220 impl #impl_generics RTTI for #ident #ty_generics #where_clause {
221 fn ctti() -> Type {
222 #body
223 }
224 }
225 };
226 }
227}
228
229fn translate_visibility(vis: &syn::Visibility) -> syn::Ident {
230 #[allow(unreachable_patterns)]
231 match vis {
232 &syn::Visibility::Public(_) => syn::Ident::from("Public"),
233 &syn::Visibility::Crate(_) => syn::Ident::from("Crate"),
234 &syn::Visibility::Restricted(_) => syn::Ident::from("Restricted"),
235 &syn::Visibility::Inherited => syn::Ident::from("Inherited"),
236 _ => syn::Ident::from("Unknown"),
237 }
238}
239
240fn parse_attr_hint(attrs: &Vec<syn::Attribute>) -> Vec<String> {
241 let mut hints = Vec::new();
242 for meta_items in attrs.iter().filter_map(filter_attr_rtti) {
243 for meta_item in meta_items {
244 match meta_item {
245 Meta(NameValue(ref m)) if m.ident == "hint" => {
247 if let Some(s) = parse_lit(&m.lit) {
248 hints.push(s.value().to_string());
249 }
250 },
251 _ => {}
252 }
253 }
254 }
255 hints
256}
257
258fn parse_attr_ignore(attrs: &Vec<syn::Attribute>) -> bool {
259 for meta_items in attrs.iter().filter_map(filter_attr_rtti) {
260 for meta_item in meta_items {
261 match meta_item {
262 Meta(Word(ref ident)) if ident == "ignore" => {
264 return true;
265 },
266 _ => {}
267 }
268 }
269 }
270 false
271}
272
273fn parse_lit(lit: &syn::Lit) -> Option<&syn::LitStr> {
274 if let syn::Lit::Str(ref lit) = *lit {
275 Some(lit)
276 } else {
277 None
278 }
279}
280
281fn filter_attr_rtti(attr: &syn::Attribute) -> Option<Vec<syn::NestedMeta>> {
282 if attr.path.segments.len() == 1 && attr.path.segments[0].ident == "rtti" {
283 match attr.interpret_meta() {
284 Some(List(ref meta)) => Some(meta.nested.iter().cloned().collect()),
285 _ => {
286 None
288 }
289 }
290 } else {
291 None
292 }
293}
294
295fn dummy_type() -> &'static syn::Type {
296 static mut DUMMY: Option<syn::Type> = None;
298 unsafe {
299 if DUMMY.is_none() {
300 DUMMY = Some(syn::Type::Path(syn::TypePath {
301 qself: None,
302 path: {
303 let p1: syn::PathSegment = "rtti".into();
305 let p2: syn::PathSegment = "Ignored".into();
306 syn::Path {
307 leading_colon: None,
308 segments: {
309 let mut punc = syn::punctuated::Punctuated::new();
310 punc.push(p1);
311 punc.push(p2);
312 punc }
314 }
315 }
316 }));
317 }
318 DUMMY.as_ref().unwrap()
319 }
320}