1 use crate::CodegenSettings;
2 use crate::config::Asyncness;
3 use crate::funcs::func_bounds;
4 use crate::names;
5 use proc_macro2::{Ident, Span, TokenStream};
6 use quote::{format_ident, quote};
7 use std::collections::HashSet;
8 
link_module( module: &witx::Module, target_path: Option<&syn::Path>, settings: &CodegenSettings, ) -> TokenStream9 pub fn link_module(
10     module: &witx::Module,
11     target_path: Option<&syn::Path>,
12     settings: &CodegenSettings,
13 ) -> TokenStream {
14     let module_ident = names::module(&module.name);
15 
16     let send_bound = if settings.async_.contains_async(module) {
17         quote! { + Send, T: Send }
18     } else {
19         quote! {}
20     };
21 
22     let mut bodies = Vec::new();
23     let mut bounds = HashSet::new();
24     for f in module.funcs() {
25         let asyncness = settings.async_.get(module.name.as_str(), f.name.as_str());
26         bodies.push(generate_func(&module, &f, target_path, asyncness));
27         let bound = func_bounds(module, &f, settings);
28         for b in bound {
29             bounds.insert(b);
30         }
31     }
32 
33     let ctx_bound = if let Some(target_path) = target_path {
34         let bounds = bounds
35             .into_iter()
36             .map(|b| quote!(#target_path::#module_ident::#b));
37         quote!( #(#bounds)+* #send_bound )
38     } else {
39         let bounds = bounds.into_iter();
40         quote!( #(#bounds)+* #send_bound )
41     };
42 
43     let func_name = if target_path.is_none() {
44         format_ident!("add_to_linker")
45     } else {
46         format_ident!("add_{}_to_linker", module_ident)
47     };
48 
49     let u = if settings.mutable {
50         quote!(&mut U)
51     } else {
52         quote!(&U)
53     };
54     quote! {
55         /// Adds all instance items to the specified `Linker`.
56         pub fn #func_name<T, U>(
57             linker: &mut wiggle::wasmtime_crate::Linker<T>,
58             get_cx: impl Fn(&mut T) -> #u + Send + Sync + Copy + 'static,
59         ) -> wiggle::error::Result<()>
60             where
61                 T: 'static,
62                 U: #ctx_bound #send_bound
63         {
64             #(#bodies)*
65             Ok(())
66         }
67     }
68 }
69 
generate_func( module: &witx::Module, func: &witx::InterfaceFunc, target_path: Option<&syn::Path>, asyncness: Asyncness, ) -> TokenStream70 fn generate_func(
71     module: &witx::Module,
72     func: &witx::InterfaceFunc,
73     target_path: Option<&syn::Path>,
74     asyncness: Asyncness,
75 ) -> TokenStream {
76     let module_str = module.name.as_str();
77     let module_ident = names::module(&module.name);
78 
79     let field_str = func.name.as_str();
80     let field_ident = names::func(&func.name);
81 
82     let (params, results) = func.wasm_signature();
83 
84     let arg_names = (0..params.len())
85         .map(|i| Ident::new(&format!("arg{i}"), Span::call_site()))
86         .collect::<Vec<_>>();
87     let arg_tys = params
88         .iter()
89         .map(|ty| names::wasm_type(*ty))
90         .collect::<Vec<_>>();
91     let arg_decls = arg_names
92         .iter()
93         .zip(arg_tys.iter())
94         .map(|(name, ty)| {
95             quote! { #name: #ty }
96         })
97         .collect::<Vec<_>>();
98 
99     let ret_ty = match results.len() {
100         0 => quote!(()),
101         1 => names::wasm_type(results[0]),
102         _ => unimplemented!(),
103     };
104 
105     let await_ = if asyncness.is_sync() {
106         quote!()
107     } else {
108         quote!(.await)
109     };
110 
111     let abi_func = if let Some(target_path) = target_path {
112         quote!( #target_path::#module_ident::#field_ident )
113     } else {
114         quote!( #field_ident )
115     };
116 
117     let body = quote! {
118         let export = caller.get_export("memory");
119         let fuel = wiggle::wasmtime_crate::AsContextMut::as_context_mut(&mut caller).hostcall_fuel();
120         let (mut mem, ctx) = match &export {
121             Some(wiggle::wasmtime_crate::Extern::Memory(m)) => {
122                 let (mem, ctx) = m.data_and_store_mut(&mut caller);
123                 let ctx = get_cx(ctx);
124                 ctx.set_hostcall_fuel(fuel);
125                 (wiggle::GuestMemory::Unshared(mem), ctx)
126             }
127             Some(wiggle::wasmtime_crate::Extern::SharedMemory(m)) => {
128                 let ctx = get_cx(caller.data_mut());
129                 ctx.set_hostcall_fuel(fuel);
130                 (wiggle::GuestMemory::Shared(m.data()), ctx)
131             }
132             _ => wiggle::error::bail!("missing required memory export"),
133         };
134         Ok(<#ret_ty>::from(#abi_func(ctx, &mut mem #(, #arg_names)*) #await_ ?))
135     };
136 
137     match asyncness {
138         Asyncness::Async => {
139             let arg_decls = quote! { ( #(#arg_names,)* ) : ( #(#arg_tys,)* ) };
140             quote! {
141                 linker.func_wrap_async(
142                     #module_str,
143                     #field_str,
144                     move |mut caller: wiggle::wasmtime_crate::Caller<'_, T>, #arg_decls| {
145                         Box::new(async move { #body })
146                     },
147                 )?;
148             }
149         }
150 
151         Asyncness::Blocking { block_with } => {
152             quote! {
153                 linker.func_wrap(
154                     #module_str,
155                     #field_str,
156                     move |mut caller: wiggle::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| -> wiggle::error::Result<#ret_ty> {
157                         let result = async { #body };
158                         #block_with(result)?
159                     },
160                 )?;
161             }
162         }
163 
164         Asyncness::Sync => {
165             quote! {
166                 linker.func_wrap(
167                     #module_str,
168                     #field_str,
169                     move |mut caller: wiggle::wasmtime_crate::Caller<'_, T> #(, #arg_decls)*| -> wiggle::error::Result<#ret_ty> {
170                         #body
171                     },
172                 )?;
173             }
174         }
175     }
176 }
177