wasmer_derive_asml_fork/
lib.rs1extern 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 }
67
68fn derive_struct_fields(data: &DataStruct) -> (TokenStream, TokenStream) {
69 let mut finish = vec![];
70 let mut helpers = vec![];
71 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 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 pub fn #name_ref(&self) -> Option<&#inner_type> {
113 self.#name.get_ref()
114 }
115 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
221fn 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 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}