wasm_wrapper_gen_impl/
lib.rs1extern crate arrayvec;
2#[macro_use]
3extern crate failure;
4#[macro_use]
5extern crate proc_macro_hack;
6#[macro_use]
7extern crate quote;
8extern crate syn;
9
10extern crate wasm_wrapper_gen_shared;
11
12use failure::{Error, ResultExt};
13
14use wasm_wrapper_gen_shared::{extract_func_info, get_argument_types, get_ret_type,
15 transform_macro_input_to_items, SupportedArgumentType,
16 SupportedRetType, TransformedRustIdent};
17
18
19#[derive(Debug, Clone)]
20struct ConstructedArgIdent {
22 base: &'static str,
23 number_suffix: u32,
24 suffixes: arrayvec::ArrayVec<[&'static str; 4]>,
25}
26
27impl ConstructedArgIdent {
28 fn new(base: &'static str, number_suffix: u32) -> Self {
29 ConstructedArgIdent {
30 base,
31 number_suffix,
32 suffixes: arrayvec::ArrayVec::new(),
33 }
34 }
35
36 fn with_suffix(&self, suffix: &'static str) -> Self {
37 let mut cloned = self.clone(); cloned.suffixes.push(suffix);
39 cloned
40 }
41}
42
43impl quote::ToTokens for ConstructedArgIdent {
44 fn to_tokens(&self, tokens: &mut quote::Tokens) {
45 let mut ident = format!("{}{}", self.base, self.number_suffix);
46 for suffix in &self.suffixes {
47 ident.push_str(suffix);
48 }
49 tokens.append(ident);
50 }
51}
52
53proc_macro_item_impl! {
54 pub fn __js_fn_impl(input: &str) -> String {
55 match process_all_functions(input) {
56 Ok(v) => v,
57 Err(e) => {
58 panic!("js_fn macro failed: {}", e);
59 }
60 }
61 }
62}
63
64fn process_all_functions(input: &str) -> Result<String, Error> {
65 let token_trees = syn::parse_token_trees(input)
66 .map_err(|e| format_err!("failed to parse macro input as an item: {}", e))?;
67
68 let ast = transform_macro_input_to_items(token_trees)?;
69
70 let mut full_out = quote::Tokens::new();
71 for item in &ast {
72 let output = process_item(item)
73 .with_context(|e| format!("failed to process function '{:?}': {}", item, e))?;
74
75 full_out.append(output);
76 }
77 Ok(full_out.to_string())
78}
79
80fn process_item(item: &syn::Item) -> Result<quote::Tokens, Error> {
81 let (item, decl, block) = extract_func_info(item)?;
82
83 let out = generate_function_wrapper(item, decl, block)?;
84
85 Ok(out)
86}
87
88fn generate_function_wrapper(
89 item: &syn::Item,
90 decl: &syn::FnDecl,
91 code: &syn::Block,
92) -> Result<quote::Tokens, Error> {
93 let callable_body = generate_callable_body(item, decl, code)?;
94
95 let argument_types = get_argument_types(decl)?;
96 let ret_ty = get_ret_type(decl)?;
97
98 let argument_names = (0..argument_types.len() as u32)
99 .map(|index| ConstructedArgIdent::new("__arg", index))
100 .collect::<Vec<_>>();
101
102 let mut function_body = quote::Tokens::new();
103
104 for (ty, arg_name) in argument_types.iter().zip(&argument_names) {
105 function_body.append(setup_for_argument(&arg_name, ty)?);
106 }
107
108 let mut arg_names_as_argument_list = quote::Tokens::new();
109 for arg_name in &argument_names {
110 arg_names_as_argument_list.append(quote! { #arg_name, });
111 }
112
113 function_body.append(quote! {
114 let result: #ret_ty = (#callable_body)(#arg_names_as_argument_list);
116 });
117
118 function_body.append(return_handling(&ret_ty)?);
119
120 let func_ident = TransformedRustIdent::new(&item.ident);
121
122 let mut real_arguments_list = quote::Tokens::new();
123 for (ty, arg_name) in argument_types.iter().zip(&argument_names) {
124 expand_argument_into(arg_name, ty, &mut real_arguments_list)?;
125 }
126
127 let ret_def = WrittenReturnType(ret_ty);
128
129 let full_definition = quote! {
130 #[no_mangle]
131 #[doc(hidden)]
132 pub extern "C" fn #func_ident (#real_arguments_list) #ret_def {
133 #function_body
134 }
135 };
136
137 Ok(full_definition)
138 }
146
147fn expand_argument_into(
148 arg_name: &ConstructedArgIdent,
149 type_type: &SupportedArgumentType,
150 tokens: &mut quote::Tokens,
151) -> Result<(), Error> {
152 match *type_type {
153 SupportedArgumentType::IntegerSliceRef(int_ty) => {
154 let ptr_arg_name = arg_name.with_suffix("_ptr");
155 let length_arg_name = arg_name.with_suffix("_len");
156 tokens.append(quote! {
157 #ptr_arg_name: *const #int_ty,
158 #length_arg_name: usize,
159 });
160 }
161 SupportedArgumentType::IntegerSliceMutRef(int_ty)
162 | SupportedArgumentType::IntegerVec(int_ty) => {
163 let ptr_arg_name = arg_name.with_suffix("_ptr");
164 let length_arg_name = arg_name.with_suffix("_len");
165 tokens.append(quote! {
166 #ptr_arg_name: *mut #int_ty,
167 #length_arg_name: usize,
168 });
169 }
170 SupportedArgumentType::Integer(int_ty) => tokens.append(quote! {
171 #arg_name: #int_ty,
172 }),
173 }
174
175 Ok(())
176}
177
178struct WrittenReturnType(SupportedRetType);
179
180impl quote::ToTokens for WrittenReturnType {
181 fn to_tokens(&self, tokens: &mut quote::Tokens) {
182 match self.0 {
183 SupportedRetType::Unit => (),
184 SupportedRetType::Integer(int_ty) => {
185 tokens.append(quote! { -> #int_ty });
186 }
187 SupportedRetType::IntegerVec(_) => {
188 tokens.append(quote! { -> *const usize });
189 }
190 }
191 }
192}
193
194fn setup_for_argument(
195 arg_name: &ConstructedArgIdent,
196 ty: &SupportedArgumentType,
197) -> Result<quote::Tokens, Error> {
198 let tokens = match *ty {
199 SupportedArgumentType::IntegerSliceRef(int_ty) => {
200 let ptr_arg_name = arg_name.with_suffix("_ptr");
202 let length_arg_name = arg_name.with_suffix("_len");
203 quote! {
204 let #arg_name: &[#int_ty] = unsafe {
205 ::std::slice::from_raw_parts(#ptr_arg_name, #length_arg_name)
206 };
207 }
208 }
209 SupportedArgumentType::IntegerSliceMutRef(int_ty) => {
210 let ptr_arg_name = arg_name.with_suffix("_ptr");
211 let length_arg_name = arg_name.with_suffix("_len");
212 quote! {
213 let #arg_name: &mut [#int_ty] = unsafe {
214 ::std::slice::from_raw_parts_mut(#ptr_arg_name, #length_arg_name)
215 };
216 }
217 }
218 SupportedArgumentType::IntegerVec(int_ty) => {
219 let ptr_arg_name = arg_name.with_suffix("_ptr");
220 let length_arg_name = arg_name.with_suffix("_len");
221 quote! {
222 let #arg_name: Vec<#int_ty> = unsafe {
223 ::std::vec::Vec::from_raw_parts(#ptr_arg_name,
224 #length_arg_name, #length_arg_name)
225 };
226 }
227 }
228 SupportedArgumentType::Integer(_) => quote::Tokens::new(), };
230
231 Ok(tokens)
232}
233
234fn return_handling(ty: &SupportedRetType) -> Result<quote::Tokens, Error> {
235 let tokens = match *ty {
236 SupportedRetType::Unit | SupportedRetType::Integer(_) => quote! { result },
237 SupportedRetType::IntegerVec(int_ty) => {
238 quote! {
239 {
240 let result_ptr = result.as_slice().as_ptr() as *mut #int_ty;
241 let result_len = result.len();
242 let result_cap = result.capacity();
243 let to_return = Box::new([result_ptr as usize, result_len, result_cap]);
244 ::std::mem::forget(result);
245 ::std::boxed::Box::into_raw(to_return) as *const usize
246 }
247 }
248 }
249 };
250
251 Ok(tokens)
252}
253
254fn generate_callable_body(
255 _item: &syn::Item,
256 decl: &syn::FnDecl,
257 code: &syn::Block,
258) -> Result<quote::Tokens, Error> {
259 if let Some(statement) = code.stmts.first() {
265 if let syn::Stmt::Expr(ref inner_expr) = *statement {
266 if let syn::ExprKind::Path(_, _) = inner_expr.node {
267 return Ok(quote! {
268 (#inner_expr)
270 });
271 }
272 }
273 }
274
275 let mut arguments = quote::Tokens::new();
279 for input in &decl.inputs {
280 arguments.append(quote! {
281 #input,
282 });
283 }
284 Ok(quote! {
285 (|#arguments| #code )
287 })
288}