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