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};
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 {
36     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 
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 
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                 .0),
82         }
83     }
84 
85     async fn resource_drop<T: Send>(
86         self,
87         store: &mut Store<T>,
88         resource: ResourceAny,
89     ) -> Result<()> {
90         match self {
91             ApiStyle::Sync => resource.resource_drop(store),
92             ApiStyle::Async | ApiStyle::AsyncNotConcurrent | ApiStyle::Concurrent => {
93                 resource.resource_drop_async(store).await
94             }
95         }
96     }
97 }
98 
99 #[test]
100 #[cfg_attr(miri, ignore)]
101 fn components_importing_modules() -> Result<()> {
102     let engine = engine();
103 
104     // FIXME: these components should actually get instantiated in `*.wast`
105     // tests once supplying imports has actually been implemented.
106 
107     Component::new(
108         &engine,
109         r#"
110         (component
111             (import "a" (core module))
112         )
113         "#,
114     )?;
115 
116     Component::new(
117         &engine,
118         r#"
119         (component
120             (import "a" (core module $m1
121                 (import "" "" (func))
122                 (import "" "x" (global i32))
123 
124                 (export "a" (table 1 funcref))
125                 (export "b" (memory 1))
126                 (export "c" (func (result f32)))
127                 (export "d" (global i64))
128             ))
129 
130             (core module $m2
131                 (func (export ""))
132                 (global (export "x") i32 i32.const 0)
133             )
134             (core instance $i2 (instantiate (module $m2)))
135             (core instance $i1 (instantiate (module $m1) (with "" (instance $i2))))
136 
137             (core module $m3
138                 (import "mod" "1" (memory 1))
139                 (import "mod" "2" (table 1 funcref))
140                 (import "mod" "3" (global i64))
141                 (import "mod" "4" (func (result f32)))
142             )
143 
144             (core instance $i3 (instantiate (module $m3)
145                 (with "mod" (instance
146                     (export "1" (memory $i1 "b"))
147                     (export "2" (table $i1 "a"))
148                     (export "3" (global $i1 "d"))
149                     (export "4" (func $i1 "c"))
150                 ))
151             ))
152         )
153         "#,
154     )?;
155 
156     Ok(())
157 }
158 
159 #[derive(Copy, Clone, PartialEq, Eq)]
160 enum Type {
161     S8,
162     U8,
163     S16,
164     U16,
165     I32,
166     I64,
167     F32,
168     F64,
169 }
170 
171 impl Type {
172     fn store(&self) -> &'static str {
173         match self {
174             Self::S8 | Self::U8 => "store8",
175             Self::S16 | Self::U16 => "store16",
176             Self::I32 | Self::F32 | Self::I64 | Self::F64 => "store",
177         }
178     }
179 
180     fn primitive(&self) -> &'static str {
181         match self {
182             Self::S8 | Self::U8 | Self::S16 | Self::U16 | Self::I32 => "i32",
183             Self::I64 => "i64",
184             Self::F32 => "f32",
185             Self::F64 => "f64",
186         }
187     }
188 }
189 
190 #[derive(Copy, Clone, PartialEq, Eq)]
191 struct Param(Type, Option<usize>);
192 
193 fn make_echo_component(type_definition: &str, type_size: u32) -> String {
194     let mut offset = 0;
195     make_echo_component_with_params(
196         type_definition,
197         &iter::repeat(Type::I32)
198             .map(|ty| {
199                 let param = Param(ty, Some(offset));
200                 offset += 4;
201                 param
202             })
203             .take(usize::try_from(type_size).unwrap() / 4)
204             .collect::<Vec<_>>(),
205     )
206 }
207 
208 fn make_echo_component_with_params(type_definition: &str, params: &[Param]) -> String {
209     let func = if params.len() == 0 {
210         format!("(func (export \"echo\"))")
211     } else if params.len() == 1 || params.len() > 16 {
212         let primitive = if params.len() == 1 {
213             params[0].0.primitive()
214         } else {
215             "i32"
216         };
217 
218         format!(
219             r#"
220             (func (export "echo") (param {primitive}) (result {primitive})
221                  local.get 0
222             )"#,
223         )
224     } else {
225         let mut param_string = String::new();
226         let mut store = String::new();
227         let mut size = 8;
228 
229         for (index, Param(ty, offset)) in params.iter().enumerate() {
230             let primitive = ty.primitive();
231 
232             write!(&mut param_string, " {primitive}").unwrap();
233             if let Some(offset) = offset {
234                 write!(
235                     &mut store,
236                     "({primitive}.{} offset={offset} (local.get $base) (local.get {index}))",
237                     ty.store(),
238                 )
239                 .unwrap();
240 
241                 size = size.max(offset + 8);
242             }
243         }
244 
245         format!(
246             r#"
247             (func (export "echo") (param{param_string}) (result i32)
248                 (local $base i32)
249                 (local.set $base
250                     (call $realloc
251                         (i32.const 0)
252                         (i32.const 0)
253                         (i32.const 4)
254                         (i32.const {size})))
255                 {store}
256                 local.get $base
257             )"#
258         )
259     };
260 
261     let type_section = if type_definition.contains("(type ") {
262         type_definition.to_string()
263     } else {
264         format!("(type $Foo' {type_definition})")
265     };
266 
267     format!(
268         r#"
269         (component
270             (core module $m
271                 {func}
272 
273                 (memory (export "memory") 1)
274                 {REALLOC_AND_FREE}
275             )
276 
277             (core instance $i (instantiate $m))
278 
279             {type_section}
280             (export $Foo "foo" (type $Foo'))
281 
282             (func (export "echo") (param "a" $Foo) (result $Foo)
283                 (canon lift
284                     (core func $i "echo")
285                     (memory $i "memory")
286                     (realloc (func $i "realloc"))
287                 )
288             )
289         )"#
290     )
291 }
292