Skip to main content

wasmer_derive_asml_fork/
lib.rs

1extern crate proc_macro;
2
3use proc_macro2::TokenStream;
4use proc_macro_error::{abort, proc_macro_error, set_dummy};
5use quote::{quote, quote_spanned, ToTokens};
6use syn::{spanned::Spanned, *};
7
8mod parse;
9
10use crate::parse::WasmerAttr;
11
12#[proc_macro_error]
13#[proc_macro_derive(WasmerEnv, attributes(wasmer))]
14pub fn derive_wasmer_env(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
15    let input: DeriveInput = syn::parse(input).unwrap();
16    let gen = impl_wasmer_env(&input);
17    gen.into()
18}
19
20fn impl_wasmer_env_for_struct(
21    name: &Ident,
22    data: &DataStruct,
23    generics: &Generics,
24    _attrs: &[Attribute],
25) -> TokenStream {
26    let (trait_methods, helper_methods) = derive_struct_fields(data);
27    let lifetimes_and_generics = generics.params.clone();
28    let where_clause = generics.where_clause.clone();
29    quote! {
30        impl < #lifetimes_and_generics > ::wasmer::WasmerEnv for #name < #lifetimes_and_generics > #where_clause{
31            #trait_methods
32        }
33
34        #[allow(dead_code)]
35        impl < #lifetimes_and_generics > #name < #lifetimes_and_generics > #where_clause {
36            #helper_methods
37        }
38    }
39}
40
41fn impl_wasmer_env(input: &DeriveInput) -> TokenStream {
42    let struct_name = &input.ident;
43
44    set_dummy(quote! {
45        impl ::wasmer::WasmerEnv for #struct_name {
46            fn init_with_instance(&mut self, instance: &::wasmer::Instance) -> Result<(), ::wasmer::HostEnvInitError> {
47                Ok(())
48            }
49        }
50    });
51
52    match &input.data {
53        Data::Struct(ds) => {
54            impl_wasmer_env_for_struct(struct_name, ds, &input.generics, &input.attrs)
55        }
56        _ => todo!(),
57    }
58    /*match input.data {
59        Struct(ds /*DataStruct {
60            fields: syn::Fields::Named(ref fields),
61            ..
62        }*/) => ,
63        Enum(ref e) => impl_wasmer_env_for_enum(struct_name, &e.variants, &input.attrs),
64        _ => abort_call_site!("Clap only supports non-tuple structs and enums"),
65    }*/
66}
67
68fn derive_struct_fields(data: &DataStruct) -> (TokenStream, TokenStream) {
69    let mut finish = vec![];
70    let mut helpers = vec![];
71    //let mut assign_tokens = vec![];
72    let mut touched_fields = vec![];
73    let fields: Vec<Field> = match &data.fields {
74        Fields::Named(ref fields) => fields.named.iter().cloned().collect(),
75        Fields::Unit => vec![],
76        Fields::Unnamed(fields) => fields.unnamed.iter().cloned().collect(),
77    };
78    for (field_num, f) in fields.into_iter().enumerate() {
79        let field_idx = syn::Index::from(field_num);
80        let name = f.ident.clone();
81        let top_level_ty: &Type = &f.ty;
82        touched_fields.push(name.clone());
83        let mut wasmer_attr = None;
84        for attr in &f.attrs {
85            // if / filter
86            if attr.path.is_ident(&Ident::new("wasmer", attr.span())) {
87                let tokens = attr.tokens.clone();
88                match syn::parse2(tokens) {
89                    Ok(attr) => {
90                        wasmer_attr = Some(attr);
91                        break;
92                    }
93                    Err(e) => {
94                        abort!(attr, "Failed to parse `wasmer` attribute: {}", e);
95                    }
96                }
97            }
98        }
99
100        if let Some(wasmer_attr) = wasmer_attr {
101            let inner_type = get_identifier(top_level_ty);
102            if let Some(name) = &name {
103                let name_ref_str = format!("{}_ref", name);
104                let name_ref = syn::Ident::new(&name_ref_str, name.span());
105                let name_ref_unchecked_str = format!("{}_ref_unchecked", name);
106                let name_ref_unchecked = syn::Ident::new(&name_ref_unchecked_str, name.span());
107                let helper_tokens = quote_spanned! {f.span()=>
108                    /// Get access to the underlying data.
109                    ///
110                    /// If `WasmerEnv::finish` has been called, this function will never
111                    /// return `None` unless the underlying data has been mutated manually.
112                    pub fn #name_ref(&self) -> Option<&#inner_type> {
113                        self.#name.get_ref()
114                    }
115                    /// Gets the item without checking if it's been initialized.
116                    ///
117                    /// # Safety
118                    /// `WasmerEnv::finish` must have been called on this function or
119                    /// this type manually initialized.
120                    pub unsafe fn #name_ref_unchecked(&self) -> &#inner_type {
121                        self.#name.get_unchecked()
122                    }
123                };
124                helpers.push(helper_tokens);
125            }
126            match wasmer_attr {
127                WasmerAttr::Export {
128                    identifier,
129                    optional,
130                    aliases,
131                    span,
132                } => {
133                    let finish_tokens = if let Some(name) = name {
134                        let name_str = name.to_string();
135                        let item_name =
136                            identifier.unwrap_or_else(|| LitStr::new(&name_str, name.span()));
137                        let mut access_expr = quote_spanned! {
138                            f.span() =>
139                                instance.exports.get_with_generics_weak::<#inner_type, _, _>(#item_name)
140                        };
141                        for alias in aliases {
142                            access_expr = quote_spanned! {
143                                f.span()=>
144                                    #access_expr .or_else(|_| instance.exports.get_with_generics_weak::<#inner_type, _, _>(#alias))
145                            };
146                        }
147                        if optional {
148                            quote_spanned! {
149                                f.span()=>
150                                    match #access_expr {
151                                        Ok(#name) => { self.#name.initialize(#name); },
152                                        Err(_) => (),
153                                    };
154                            }
155                        } else {
156                            quote_spanned! {
157                                f.span()=>
158                                    let #name: #inner_type = #access_expr?;
159                                    self.#name.initialize(#name);
160                            }
161                        }
162                    } else {
163                        if let Some(identifier) = identifier {
164                            let mut access_expr = quote_spanned! {
165                                f.span() =>
166                                    instance.exports.get_with_generics_weak::<#inner_type, _, _>(#identifier)
167                            };
168                            for alias in aliases {
169                                access_expr = quote_spanned! {
170                                    f.span()=>
171                                        #access_expr .or_else(|_| instance.exports.get_with_generics_weak::<#inner_type, _, _>(#alias))
172                                };
173                            }
174                            let local_var =
175                                Ident::new(&format!("field_{}", field_num), identifier.span());
176                            if optional {
177                                quote_spanned! {
178                                    f.span()=>
179                                        match #access_expr {
180                                            Ok(#local_var) => {
181                                                self.#field_idx.initialize(#local_var);
182                                            },
183                                            Err(_) => (),
184                                        }
185                                }
186                            } else {
187                                quote_spanned! {
188                                    f.span()=>
189                                        let #local_var: #inner_type = #access_expr?;
190                                    self.#field_idx.initialize(#local_var);
191                                }
192                            }
193                        } else {
194                            abort!(
195                                span,
196                                "Expected `name` field on export attribute because field does not have a name. For example: `#[wasmer(export(name = \"wasm_ident\"))]`.",
197                            );
198                        }
199                    };
200
201                    finish.push(finish_tokens);
202                }
203            }
204        }
205    }
206
207    let trait_methods = quote! {
208        fn init_with_instance(&mut self, instance: &::wasmer::Instance) -> Result<(), ::wasmer::HostEnvInitError> {
209            #(#finish)*
210            Ok(())
211        }
212    };
213
214    let helper_methods = quote! {
215        #(#helpers)*
216    };
217
218    (trait_methods, helper_methods)
219}
220
221// TODO: name this something that makes sense
222fn get_identifier(ty: &Type) -> TokenStream {
223    match ty {
224        Type::Path(TypePath {
225            path: Path { segments, .. },
226            ..
227        }) => {
228            if let Some(PathSegment { ident, arguments }) = segments.last() {
229                if ident != "LazyInit" {
230                    abort!(
231                        ident,
232                        "WasmerEnv derive expects all `export`s to be wrapped in `LazyInit`"
233                    );
234                }
235                if let PathArguments::AngleBracketed(AngleBracketedGenericArguments {
236                    args, ..
237                }) = arguments
238                {
239                    // TODO: proper error handling
240                    assert_eq!(args.len(), 1);
241                    if let GenericArgument::Type(Type::Path(TypePath {
242                        path: Path { segments, .. },
243                        ..
244                    })) = &args[0]
245                    {
246                        segments
247                            .last()
248                            .expect("there must be at least one segment; TODO: error handling")
249                            .to_token_stream()
250                    } else {
251                        abort!(
252                            &args[0],
253                            "unrecognized type in first generic position on `LazyInit`"
254                        );
255                    }
256                } else {
257                    abort!(arguments, "Expected a generic parameter on `LazyInit`");
258                }
259            } else {
260                abort!(segments, "Unknown type found");
261            }
262        }
263        _ => abort!(ty, "Unrecognized/unsupported type"),
264    }
265}