1use std::ops::Deref;
2
3use proc_macro::TokenStream;
4use proc_macro2::{Ident, Span};
5use quote::quote;
6use syn::{parse::Parser, FnArg, ItemFn, Lit, Pat, Signature};
7
8type AttributeArgs = syn::punctuated::Punctuated<syn::NestedMeta, syn::Token![,]>;
10
11#[proc_macro_attribute]
26#[cfg(not(test))] pub fn vm_handle(args: TokenStream, item: TokenStream) -> TokenStream {
28 let method = parse_method("vm_handle", args).unwrap();
29 let raw_item = proc_macro2::TokenStream::from(item.clone());
30 let raw_sig = syn::parse_macro_input!(item as ItemFn).sig;
31 let has_ctx = raw_sig.inputs.len() == 2;
32 let raw_ident = raw_sig.ident;
33 let new_ident = Ident::new(&format!("_wasmy_vm_handle_{}", method), Span::call_site());
34
35 let new_item = if has_ctx {
36 quote! {
37 #raw_item
38
39
40 #[allow(redundant_semicolons)]
41 fn #new_ident(ctx_ptr: usize, args: &::wasmy_vm::Any) -> ::wasmy_vm::Result<::wasmy_vm::Any> {
42 #raw_ident(unsafe{::wasmy_vm::VmHandlerApi::try_as(ctx_ptr)}, ::wasmy_vm::VmHandlerApi::unpack_any(args)?).and_then(|res|::wasmy_vm::VmHandlerApi::pack_any(res))
43 }
44 ::wasmy_vm::submit_handler!{
45 ::wasmy_vm::VmHandlerApi::new(#method, #new_ident)
46 }
47 }
48 } else {
49 quote! {
50 #raw_item
51
52
53 #[allow(redundant_semicolons)]
54 fn #new_ident(_ctx_ptr: usize, args: &::wasmy_vm::Any) -> ::wasmy_vm::Result<::wasmy_vm::Any> {
55 #raw_ident(::wasmy_vm::VmHandlerApi::unpack_any(args)?).and_then(|res|::wasmy_vm::VmHandlerApi::pack_any(res))
56 }
57 ::wasmy_vm::submit_handler!{
58 ::wasmy_vm::VmHandlerApi::new(#method, #new_ident)
59 }
60 }
61 };
62
63 #[cfg(debug_assertions)]
64 println!("{}", new_item);
65 TokenStream::from(new_item)
66}
67
68#[proc_macro_attribute]
78#[cfg(not(test))] pub fn wasm_handle(args: TokenStream, item: TokenStream) -> TokenStream {
80 let method = parse_method("wasm_handle", args).unwrap();
82 let mut new_item = item.clone();
83 let raw_sig = syn::parse_macro_input!(item as ItemFn).sig;
84 let (inner_ident, inner_item) = wasm_gen_inner(raw_sig);
85 let outer_ident = Ident::new(&format!("_wasmy_wasm_handle_{}", method), Span::call_site());
86 let outer_item = quote! {
87 #[allow(redundant_semicolons)]
88 #[inline]
89 #[no_mangle]
90 pub extern "C" fn #outer_ident(ctx_size: i32, args_size: i32) {
91 #inner_item;
92 ::wasmy_abi::wasm_handle(ctx_size, args_size, #inner_ident)
93 }
94 };
95 new_item.extend(TokenStream::from(outer_item));
96
97 #[cfg(debug_assertions)]
98 println!("{}", new_item);
99
100 new_item
101}
102
103fn wasm_gen_inner(raw_sig: Signature) -> (Ident, proc_macro2::TokenStream) {
104 let inner_ident = Ident::new("_inner", Span::call_site());
105 let raw_ident = raw_sig.ident.clone();
106 let raw_first_input = raw_sig.inputs.first().unwrap();
107 let fn_args = fn_arg_ident(raw_first_input);
108 (
109 inner_ident.clone(),
110 quote! {
111 #[allow(unused_mut)]
112 #[inline]
113 fn #inner_ident(#raw_first_input, args: ::wasmy_abi::InArgs) -> ::wasmy_abi::Result<::wasmy_abi::Any> {
114 ::wasmy_abi::pack_any(#raw_ident(#fn_args, args.get_args()?)?)
115 }
116 },
117 )
118}
119
120#[proc_macro_attribute]
131#[cfg(not(test))] pub fn wasm_onload(_args: TokenStream, item: TokenStream) -> TokenStream {
133 let raw_item = proc_macro2::TokenStream::from(item.clone());
134 let raw_ident = syn::parse_macro_input!(item as syn::ItemFn).sig.ident;
135 let new_ident = Ident::new("_wasmy_wasm_onload", Span::call_site());
136 let new_item = quote! {
137 #[allow(redundant_semicolons)]
138 #[inline]
139 #[no_mangle]
140 pub extern "C" fn #new_ident() {
141 #raw_item;
142 #raw_ident();
143 }
144 };
145 #[cfg(debug_assertions)]
146 println!("{}", new_item);
147 TokenStream::from(new_item)
148}
149
150fn parse_method(marco_name: &str, input: TokenStream) -> Result<i32, syn::Error> {
151 let method = input.to_string().parse::<i32>().unwrap_or(-1);
152 if method >= 0 {
153 return Ok(method);
154 }
155 let err = syn::Error::new_spanned(
156 proc_macro2::TokenStream::from(input.clone()),
157 format!("#[{0}(i32)] or #[{0}(method=i32)]", marco_name),
158 );
159 let attr = AttributeArgs::parse_terminated.parse(input).or(Err(err.clone()))?;
160 for arg in attr {
161 match arg {
162 syn::NestedMeta::Meta(syn::Meta::NameValue(namevalue)) => {
163 let ident = namevalue
164 .path
165 .get_ident()
166 .ok_or_else(|| {
167 syn::Error::new_spanned(&namevalue, "Must have specified ident")
168 })?
169 .to_string()
170 .to_lowercase();
171 match ident.as_str() {
172 "method" => {
173 if let Lit::Int(i) = &namevalue.lit {
174 if let Ok(method) = i.base10_digits().parse::<i32>() {
175 if method >= 0 {
176 return Ok(method);
177 }
178 }
179 }
180 return Err(syn::Error::new_spanned(
181 namevalue,
182 "attribute method is not i32 greater than or equal to 0",
183 ));
184 }
185 name => {
186 let msg =
187 format!("Unknown attribute {} is specified; expected `method`", name,);
188 return Err(syn::Error::new_spanned(namevalue, msg));
189 }
190 }
191 }
192 other => {
193 return Err(syn::Error::new_spanned(other, "Unknown attribute inside the macro"));
194 }
195 }
196 }
197 Err(err)
198}
199
200fn fn_arg_ident(arg: &FnArg) -> Ident {
201 let fn_args;
202 if let FnArg::Typed(a) = arg {
203 if let Pat::Ident(ident) = a.pat.deref() {
204 fn_args = ident.ident.clone();
205 } else {
206 unreachable!()
207 }
208 } else {
209 unreachable!()
210 }
211 fn_args
212}