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