1 use std::fmt::Write;
2 use std::iter;
3 use wasmtime::component::{
4     Component, ComponentNamedList, Instance, Lift, Linker, Lower, ResourceAny, TypedFunc,
5 };
6 use wasmtime::{Config, Result, Store};
7 use wasmtime_component_util::REALLOC_AND_FREE;
8 use wasmtime_test_util::component::{async_engine, config, engine, map_engine};
9 
10 mod aot;
11 mod r#async;
12 mod async_dynamic;
13 mod bindgen;
14 mod call_hook;
15 mod dynamic;
16 mod func;
17 mod import;
18 mod instance;
19 mod linker;
20 mod macros;
21 mod missing_async;
22 mod nested;
23 mod post_return;
24 mod resources;
25 mod strings;
26 
27 #[derive(Copy, Clone)]
28 enum ApiStyle {
29     Sync,
30     Async,
31     AsyncNotConcurrent,
32     Concurrent,
33 }
34 
35 impl ApiStyle {
config(self) -> Config36     fn config(self) -> Config {
37         let mut config = config();
38         match self {
39             ApiStyle::AsyncNotConcurrent => {
40                 config.concurrency_support(false);
41             }
42             ApiStyle::Sync | ApiStyle::Async | ApiStyle::Concurrent => {
43                 config.wasm_component_model_threading(true);
44             }
45         }
46         config
47     }
48 
instantiate<T: Send>( self, store: &mut Store<T>, linker: &Linker<T>, component: &Component, ) -> Result<Instance>49     async fn instantiate<T: Send>(
50         self,
51         store: &mut Store<T>,
52         linker: &Linker<T>,
53         component: &Component,
54     ) -> Result<Instance> {
55         match self {
56             ApiStyle::Sync => linker.instantiate(store, &component),
57             ApiStyle::Async | ApiStyle::AsyncNotConcurrent | ApiStyle::Concurrent => {
58                 linker.instantiate_async(store, &component).await
59             }
60         }
61     }
62 
call< P: ComponentNamedList + Lower + 'static, R: ComponentNamedList + Lift + 'static, T: Send, >( self, store: &mut Store<T>, func: TypedFunc<P, R>, params: P, ) -> Result<R>63     async fn call<
64         P: ComponentNamedList + Lower + 'static,
65         R: ComponentNamedList + Lift + 'static,
66         T: Send,
67     >(
68         self,
69         store: &mut Store<T>,
70         func: TypedFunc<P, R>,
71         params: P,
72     ) -> Result<R> {
73         match self {
74             ApiStyle::Sync => func.call(&mut *store, params),
75             ApiStyle::Async | ApiStyle::AsyncNotConcurrent => {
76                 func.call_async(&mut *store, params).await
77             }
78             ApiStyle::Concurrent => Ok(store
79                 .run_concurrent(async |access| func.call_concurrent(access, params).await)
80                 .await??),
81         }
82     }
83 
resource_drop<T: Send>( self, store: &mut Store<T>, resource: ResourceAny, ) -> Result<()>84     async fn resource_drop<T: Send>(
85         self,
86         store: &mut Store<T>,
87         resource: ResourceAny,
88     ) -> Result<()> {
89         match self {
90             ApiStyle::Sync => resource.resource_drop(store),
91             ApiStyle::Async | ApiStyle::AsyncNotConcurrent | ApiStyle::Concurrent => {
92                 resource.resource_drop_async(store).await
93             }
94         }
95     }
96 }
97 
98 #[test]
99 #[cfg_attr(miri, ignore)]
components_importing_modules() -> Result<()>100 fn components_importing_modules() -> Result<()> {
101     let engine = engine();
102 
103     // FIXME: these components should actually get instantiated in `*.wast`
104     // tests once supplying imports has actually been implemented.
105 
106     Component::new(
107         &engine,
108         r#"
109         (component
110             (import "a" (core module))
111         )
112         "#,
113     )?;
114 
115     Component::new(
116         &engine,
117         r#"
118         (component
119             (import "a" (core module $m1
120                 (import "" "" (func))
121                 (import "" "x" (global i32))
122 
123                 (export "a" (table 1 funcref))
124                 (export "b" (memory 1))
125                 (export "c" (func (result f32)))
126                 (export "d" (global i64))
127             ))
128 
129             (core module $m2
130                 (func (export ""))
131                 (global (export "x") i32 i32.const 0)
132             )
133             (core instance $i2 (instantiate (module $m2)))
134             (core instance $i1 (instantiate (module $m1) (with "" (instance $i2))))
135 
136             (core module $m3
137                 (import "mod" "1" (memory 1))
138                 (import "mod" "2" (table 1 funcref))
139                 (import "mod" "3" (global i64))
140                 (import "mod" "4" (func (result f32)))
141             )
142 
143             (core instance $i3 (instantiate (module $m3)
144                 (with "mod" (instance
145                     (export "1" (memory $i1 "b"))
146                     (export "2" (table $i1 "a"))
147                     (export "3" (global $i1 "d"))
148                     (export "4" (func $i1 "c"))
149                 ))
150             ))
151         )
152         "#,
153     )?;
154 
155     Ok(())
156 }
157 
158 #[derive(Copy, Clone, PartialEq, Eq)]
159 enum Type {
160     S8,
161     U8,
162     S16,
163     U16,
164     I32,
165     I64,
166     F32,
167     F64,
168 }
169 
170 impl Type {
store(&self) -> &'static str171     fn store(&self) -> &'static str {
172         match self {
173             Self::S8 | Self::U8 => "store8",
174             Self::S16 | Self::U16 => "store16",
175             Self::I32 | Self::F32 | Self::I64 | Self::F64 => "store",
176         }
177     }
178 
primitive(&self) -> &'static str179     fn primitive(&self) -> &'static str {
180         match self {
181             Self::S8 | Self::U8 | Self::S16 | Self::U16 | Self::I32 => "i32",
182             Self::I64 => "i64",
183             Self::F32 => "f32",
184             Self::F64 => "f64",
185         }
186     }
187 }
188 
189 #[derive(Copy, Clone, PartialEq, Eq)]
190 struct Param(Type, Option<usize>);
191 
make_echo_component(type_definition: &str, type_size: u32) -> String192 fn make_echo_component(type_definition: &str, type_size: u32) -> String {
193     let mut offset = 0;
194     make_echo_component_with_params(
195         type_definition,
196         &iter::repeat(Type::I32)
197             .map(|ty| {
198                 let param = Param(ty, Some(offset));
199                 offset += 4;
200                 param
201             })
202             .take(usize::try_from(type_size).unwrap() / 4)
203             .collect::<Vec<_>>(),
204     )
205 }
206 
make_echo_component_with_params(type_definition: &str, params: &[Param]) -> String207 fn make_echo_component_with_params(type_definition: &str, params: &[Param]) -> String {
208     let func = if params.len() == 0 {
209         format!("(func (export \"echo\"))")
210     } else if params.len() == 1 || params.len() > 16 {
211         let primitive = if params.len() == 1 {
212             params[0].0.primitive()
213         } else {
214             "i32"
215         };
216 
217         format!(
218             r#"
219             (func (export "echo") (param {primitive}) (result {primitive})
220                  local.get 0
221             )"#,
222         )
223     } else {
224         let mut param_string = String::new();
225         let mut store = String::new();
226         let mut size = 8;
227 
228         for (index, Param(ty, offset)) in params.iter().enumerate() {
229             let primitive = ty.primitive();
230 
231             write!(&mut param_string, " {primitive}").unwrap();
232             if let Some(offset) = offset {
233                 write!(
234                     &mut store,
235                     "({primitive}.{} offset={offset} (local.get $base) (local.get {index}))",
236                     ty.store(),
237                 )
238                 .unwrap();
239 
240                 size = size.max(offset + 8);
241             }
242         }
243 
244         format!(
245             r#"
246             (func (export "echo") (param{param_string}) (result i32)
247                 (local $base i32)
248                 (local.set $base
249                     (call $realloc
250                         (i32.const 0)
251                         (i32.const 0)
252                         (i32.const 4)
253                         (i32.const {size})))
254                 {store}
255                 local.get $base
256             )"#
257         )
258     };
259 
260     let type_section = if type_definition.contains("(type ") {
261         type_definition.to_string()
262     } else {
263         format!("(type $Foo' {type_definition})")
264     };
265 
266     format!(
267         r#"
268         (component
269             (core module $m
270                 {func}
271 
272                 (memory (export "memory") 1)
273                 {REALLOC_AND_FREE}
274             )
275 
276             (core instance $i (instantiate $m))
277 
278             {type_section}
279             (export $Foo "foo" (type $Foo'))
280 
281             (func (export "echo") (param "a" $Foo) (result $Foo)
282                 (canon lift
283                     (core func $i "echo")
284                     (memory $i "memory")
285                     (realloc (func $i "realloc"))
286                 )
287             )
288         )"#
289     )
290 }
291