vptr_macros/
macros.rs

1/* Copyright (C) 2019 Olivier Goffart <ogoffart@woboq.com>
2
3Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
4associated documentation files (the "Software"), to deal in the Software without restriction,
5including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
6and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
7subject to the following conditions:
8
9The above copyright notice and this permission notice shall be included in all copies or substantial
10portions of the Software.
11
12THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
13NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
14NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
15OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17*/
18
19#![recursion_limit = "128"]
20//! Refer to the [documentation of the `vptr` crate](../vptr/index.html#the-vptr-macro)
21
22extern crate proc_macro;
23use proc_macro::TokenStream;
24use quote::quote;
25use syn::parse::Parser;
26use syn::{self, spanned::Spanned, AttributeArgs, ItemStruct};
27
28/// Refer to the [documentation of the `vptr` crate](../vptr/index.html#the-vptr-macro)
29#[proc_macro_attribute]
30pub fn vptr(attr: TokenStream, item: TokenStream) -> TokenStream {
31    let attr = syn::parse_macro_input!(attr as AttributeArgs);
32    let item = syn::parse_macro_input!(item as ItemStruct);
33    match vptr_impl(attr, item) {
34        Ok(x) => x,
35        Err(e) => e.to_compile_error().into(),
36    }
37}
38
39fn vptr_impl(attr: AttributeArgs, item: ItemStruct) -> Result<TokenStream, syn::Error> {
40    let ItemStruct {
41        attrs,
42        vis,
43        struct_token,
44        ident,
45        generics,
46        fields,
47        semi_token,
48    } = item;
49
50    let attr = attr
51        .iter()
52        .map(|a| {
53            if let syn::NestedMeta::Meta(syn::Meta::Path(i)) = a {
54                Ok(i.clone())
55            } else if let syn::NestedMeta::Lit(syn::Lit::Str(lit_str)) = a {
56                lit_str.parse::<syn::Path>()
57            } else {
58                Err(syn::Error::new(
59                    a.span(),
60                    "attribute of vptr must be a trait",
61                ))
62            }
63        })
64        .collect::<Result<Vec<_>, _>>()?;
65
66    if let Some(tp) = generics.type_params().next() {
67        return Err(syn::Error::new(tp.span(), "vptr does not support generics"));
68    }
69
70    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
71
72    let (fields, attr_with_names) = if let syn::Fields::Named(mut n) = fields {
73        let attr_with_names: Vec<_> = attr
74            .iter()
75            .map(|t| {
76                let field_name = quote::format_ident!("vptr_{}", t.segments.last().unwrap().ident);
77                (t, quote! { #field_name })
78            })
79            .collect();
80        let parser = syn::Field::parse_named;
81        for (trait_, field_name) in &attr_with_names {
82            n.named
83                .push(parser.parse(
84                    quote!(#field_name : vptr::VPtr<#ident #ty_generics, dyn #trait_>).into(),
85                )?);
86        }
87        (syn::Fields::Named(n), attr_with_names)
88    } else {
89        let mut n = if let syn::Fields::Unnamed(n) = fields {
90            n
91        } else {
92            syn::FieldsUnnamed {
93                paren_token: Default::default(),
94                unnamed: Default::default(),
95            }
96        };
97        let count = n.unnamed.len();
98        let parser = syn::Field::parse_unnamed;
99        for trait_ in &attr {
100            n.unnamed
101                .push(parser.parse(quote!(vptr::VPtr<#ident #ty_generics, dyn #trait_>).into())?);
102        }
103        let attr_with_names: Vec<_> = attr
104            .iter()
105            .enumerate()
106            .map(|(i, t)| {
107                let field_name = syn::Index::from(i + count);
108                (t, quote! { #field_name })
109            })
110            .collect();
111        (syn::Fields::Unnamed(n), attr_with_names)
112    };
113
114    let mut result = quote!(
115        #(#attrs)* #[allow(non_snake_case)] #vis #struct_token #ident #generics  #fields  #semi_token
116    );
117
118    for (trait_, field_name) in attr_with_names {
119        result = quote!(#result
120            unsafe impl #impl_generics vptr::HasVPtr<dyn #trait_> for #ident #ty_generics #where_clause {
121                fn init() -> &'static vptr::VTableData {
122                    use vptr::internal::{TransmuterTO, TransmuterPtr};
123                    static VTABLE : vptr::VTableData = vptr::VTableData{
124                        offset: ::core::mem::offset_of!(#ident, #field_name) as isize,
125                        vtable: unsafe {
126                            let x: &'static #ident  = TransmuterPtr::<#ident> { int: 0 }.ptr;
127                            TransmuterTO::<dyn #trait_>{ ptr: x }.to.vtable
128                        }
129                    };
130                    &VTABLE
131                }
132
133                fn get_vptr(&self) -> &vptr::VPtr<Self, dyn #trait_> { &self.#field_name }
134                fn get_vptr_mut(&mut self) -> &mut vptr::VPtr<Self, dyn #trait_> { &mut self.#field_name }
135            }
136        );
137    }
138    //println!("{}", result.to_string());
139    Ok(result.into())
140}