1 #![cfg(not(miri))]
2
3 use super::{ApiStyle, REALLOC_AND_FREE};
4 use std::sync::{
5 Arc,
6 atomic::{AtomicBool, Ordering::SeqCst},
7 };
8 use wasmtime::Result;
9 use wasmtime::component::*;
10 use wasmtime::{Config, Engine, Store, StoreContextMut, Trap};
11
12 const CANON_32BIT_NAN: u32 = 0b01111111110000000000000000000000;
13 const CANON_64BIT_NAN: u64 = 0b0111111111111000000000000000000000000000000000000000000000000000;
14
15 #[test]
thunks() -> Result<()>16 fn thunks() -> Result<()> {
17 let component = r#"
18 (component
19 (core module $m
20 (func (export "thunk"))
21 (func (export "thunk-trap") unreachable)
22 )
23 (core instance $i (instantiate $m))
24 (func (export "thunk")
25 (canon lift (core func $i "thunk"))
26 )
27 (func (export "thunk-trap")
28 (canon lift (core func $i "thunk-trap"))
29 )
30 )
31 "#;
32
33 let engine = super::engine();
34 let component = Component::new(&engine, component)?;
35 let mut store = Store::new(&engine, ());
36 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
37 instance
38 .get_typed_func::<(), ()>(&mut store, "thunk")?
39 .call(&mut store, ())?;
40 let err = instance
41 .get_typed_func::<(), ()>(&mut store, "thunk-trap")?
42 .call(&mut store, ())
43 .unwrap_err();
44 assert_eq!(err.downcast::<Trap>()?, Trap::UnreachableCodeReached);
45
46 Ok(())
47 }
48
49 #[test]
typecheck() -> Result<()>50 fn typecheck() -> Result<()> {
51 let component = r#"
52 (component
53 (core module $m
54 (func (export "thunk"))
55 (func (export "take-string") (param i32 i32))
56 (func (export "two-args") (param i32 i32 i32))
57 (func (export "ret-one") (result i32) unreachable)
58
59 (memory (export "memory") 1)
60 (func (export "realloc") (param i32 i32 i32 i32) (result i32)
61 unreachable)
62 )
63 (core instance $i (instantiate (module $m)))
64 (func (export "thunk")
65 (canon lift (core func $i "thunk"))
66 )
67 (func (export "take-string") (param "a" string)
68 (canon lift (core func $i "take-string") (memory $i "memory") (realloc (func $i "realloc")))
69 )
70 (func (export "take-two-args") (param "a" s32) (param "b" (list u8))
71 (canon lift (core func $i "two-args") (memory $i "memory") (realloc (func $i "realloc")))
72 )
73 (func (export "ret-tuple") (result (tuple u8 s8))
74 (canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc")))
75 )
76 (func (export "ret-tuple1") (result (tuple u32))
77 (canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc")))
78 )
79 (func (export "ret-string") (result string)
80 (canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc")))
81 )
82 (func (export "ret-list-u8") (result (list u8))
83 (canon lift (core func $i "ret-one") (memory $i "memory") (realloc (func $i "realloc")))
84 )
85 )
86 "#;
87
88 let engine = Engine::default();
89 let component = Component::new(&engine, component)?;
90 let mut store = Store::new(&engine, ());
91 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
92 let thunk = instance.get_func(&mut store, "thunk").unwrap();
93 let take_string = instance.get_func(&mut store, "take-string").unwrap();
94 let take_two_args = instance.get_func(&mut store, "take-two-args").unwrap();
95 let ret_tuple = instance.get_func(&mut store, "ret-tuple").unwrap();
96 let ret_tuple1 = instance.get_func(&mut store, "ret-tuple1").unwrap();
97 let ret_string = instance.get_func(&mut store, "ret-string").unwrap();
98 let ret_list_u8 = instance.get_func(&mut store, "ret-list-u8").unwrap();
99 assert!(thunk.typed::<(), (u32,)>(&store).is_err());
100 assert!(thunk.typed::<(u32,), ()>(&store).is_err());
101 assert!(thunk.typed::<(), ()>(&store).is_ok());
102 assert!(take_string.typed::<(), ()>(&store).is_err());
103 assert!(take_string.typed::<(String,), ()>(&store).is_ok());
104 assert!(take_string.typed::<(&str,), ()>(&store).is_ok());
105 assert!(take_string.typed::<(&[u8],), ()>(&store).is_err());
106 assert!(take_two_args.typed::<(), ()>(&store).is_err());
107 assert!(take_two_args.typed::<(i32, &[u8]), (u32,)>(&store).is_err());
108 assert!(take_two_args.typed::<(u32, &[u8]), ()>(&store).is_err());
109 assert!(take_two_args.typed::<(i32, &[u8]), ()>(&store).is_ok());
110 assert!(ret_tuple.typed::<(), ()>(&store).is_err());
111 assert!(ret_tuple.typed::<(), (u8,)>(&store).is_err());
112 assert!(ret_tuple.typed::<(), ((u8, i8),)>(&store).is_ok());
113 assert!(ret_tuple1.typed::<(), ((u32,),)>(&store).is_ok());
114 assert!(ret_tuple1.typed::<(), (u32,)>(&store).is_err());
115 assert!(ret_string.typed::<(), ()>(&store).is_err());
116 assert!(ret_string.typed::<(), (WasmStr,)>(&store).is_ok());
117 assert!(ret_list_u8.typed::<(), (WasmList<u16>,)>(&store).is_err());
118 assert!(ret_list_u8.typed::<(), (WasmList<i8>,)>(&store).is_err());
119 assert!(ret_list_u8.typed::<(), (WasmList<u8>,)>(&store).is_ok());
120
121 Ok(())
122 }
123
124 #[test]
integers() -> Result<()>125 fn integers() -> Result<()> {
126 let component = r#"
127 (component
128 (core module $m
129 (func (export "take-i32-100") (param i32)
130 local.get 0
131 i32.const 100
132 i32.eq
133 br_if 0
134 unreachable
135 )
136 (func (export "take-i64-100") (param i64)
137 local.get 0
138 i64.const 100
139 i64.eq
140 br_if 0
141 unreachable
142 )
143 (func (export "ret-i32-0") (result i32) i32.const 0)
144 (func (export "ret-i64-0") (result i64) i64.const 0)
145 (func (export "ret-i32-minus-1") (result i32) i32.const -1)
146 (func (export "ret-i64-minus-1") (result i64) i64.const -1)
147 (func (export "ret-i32-100000") (result i32) i32.const 100000)
148 )
149 (core instance $i (instantiate (module $m)))
150 (func (export "take-u8") (param "a" u8) (canon lift (core func $i "take-i32-100")))
151 (func (export "take-s8") (param "a" s8) (canon lift (core func $i "take-i32-100")))
152 (func (export "take-u16") (param "a" u16) (canon lift (core func $i "take-i32-100")))
153 (func (export "take-s16") (param "a" s16) (canon lift (core func $i "take-i32-100")))
154 (func (export "take-u32") (param "a" u32) (canon lift (core func $i "take-i32-100")))
155 (func (export "take-s32") (param "a" s32) (canon lift (core func $i "take-i32-100")))
156 (func (export "take-u64") (param "a" u64) (canon lift (core func $i "take-i64-100")))
157 (func (export "take-s64") (param "a" s64) (canon lift (core func $i "take-i64-100")))
158
159 (func (export "ret-u8") (result u8) (canon lift (core func $i "ret-i32-0")))
160 (func (export "ret-s8") (result s8) (canon lift (core func $i "ret-i32-0")))
161 (func (export "ret-u16") (result u16) (canon lift (core func $i "ret-i32-0")))
162 (func (export "ret-s16") (result s16) (canon lift (core func $i "ret-i32-0")))
163 (func (export "ret-u32") (result u32) (canon lift (core func $i "ret-i32-0")))
164 (func (export "ret-s32") (result s32) (canon lift (core func $i "ret-i32-0")))
165 (func (export "ret-u64") (result u64) (canon lift (core func $i "ret-i64-0")))
166 (func (export "ret-s64") (result s64) (canon lift (core func $i "ret-i64-0")))
167
168 (func (export "retm1-u8") (result u8) (canon lift (core func $i "ret-i32-minus-1")))
169 (func (export "retm1-s8") (result s8) (canon lift (core func $i "ret-i32-minus-1")))
170 (func (export "retm1-u16") (result u16) (canon lift (core func $i "ret-i32-minus-1")))
171 (func (export "retm1-s16") (result s16) (canon lift (core func $i "ret-i32-minus-1")))
172 (func (export "retm1-u32") (result u32) (canon lift (core func $i "ret-i32-minus-1")))
173 (func (export "retm1-s32") (result s32) (canon lift (core func $i "ret-i32-minus-1")))
174 (func (export "retm1-u64") (result u64) (canon lift (core func $i "ret-i64-minus-1")))
175 (func (export "retm1-s64") (result s64) (canon lift (core func $i "ret-i64-minus-1")))
176
177 (func (export "retbig-u8") (result u8) (canon lift (core func $i "ret-i32-100000")))
178 (func (export "retbig-s8") (result s8) (canon lift (core func $i "ret-i32-100000")))
179 (func (export "retbig-u16") (result u16) (canon lift (core func $i "ret-i32-100000")))
180 (func (export "retbig-s16") (result s16) (canon lift (core func $i "ret-i32-100000")))
181 (func (export "retbig-u32") (result u32) (canon lift (core func $i "ret-i32-100000")))
182 (func (export "retbig-s32") (result s32) (canon lift (core func $i "ret-i32-100000")))
183 )
184 "#;
185
186 let engine = super::engine();
187 let component = Component::new(&engine, component)?;
188 let mut store = Store::new(&engine, ());
189 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
190
191 // Passing in 100 is valid for all primitives
192 instance
193 .get_typed_func::<(u8,), ()>(&mut store, "take-u8")?
194 .call(&mut store, (100,))?;
195 instance
196 .get_typed_func::<(i8,), ()>(&mut store, "take-s8")?
197 .call(&mut store, (100,))?;
198 instance
199 .get_typed_func::<(u16,), ()>(&mut store, "take-u16")?
200 .call(&mut store, (100,))?;
201 instance
202 .get_typed_func::<(i16,), ()>(&mut store, "take-s16")?
203 .call(&mut store, (100,))?;
204 instance
205 .get_typed_func::<(u32,), ()>(&mut store, "take-u32")?
206 .call(&mut store, (100,))?;
207 instance
208 .get_typed_func::<(i32,), ()>(&mut store, "take-s32")?
209 .call(&mut store, (100,))?;
210 instance
211 .get_typed_func::<(u64,), ()>(&mut store, "take-u64")?
212 .call(&mut store, (100,))?;
213 instance
214 .get_typed_func::<(i64,), ()>(&mut store, "take-s64")?
215 .call(&mut store, (100,))?;
216
217 // This specific wasm instance traps if any value other than 100 is passed
218 with_new_instance(&engine, &component, |store, instance| {
219 instance
220 .get_typed_func::<(u8,), ()>(&mut *store, "take-u8")?
221 .call(store, (101,))
222 .unwrap_err()
223 .downcast::<Trap>()
224 })?;
225 with_new_instance(&engine, &component, |store, instance| {
226 instance
227 .get_typed_func::<(i8,), ()>(&mut *store, "take-s8")?
228 .call(store, (101,))
229 .unwrap_err()
230 .downcast::<Trap>()
231 })?;
232 with_new_instance(&engine, &component, |store, instance| {
233 instance
234 .get_typed_func::<(u16,), ()>(&mut *store, "take-u16")?
235 .call(store, (101,))
236 .unwrap_err()
237 .downcast::<Trap>()
238 })?;
239 with_new_instance(&engine, &component, |store, instance| {
240 instance
241 .get_typed_func::<(i16,), ()>(&mut *store, "take-s16")?
242 .call(store, (101,))
243 .unwrap_err()
244 .downcast::<Trap>()
245 })?;
246 with_new_instance(&engine, &component, |store, instance| {
247 instance
248 .get_typed_func::<(u32,), ()>(&mut *store, "take-u32")?
249 .call(store, (101,))
250 .unwrap_err()
251 .downcast::<Trap>()
252 })?;
253 with_new_instance(&engine, &component, |store, instance| {
254 instance
255 .get_typed_func::<(i32,), ()>(&mut *store, "take-s32")?
256 .call(store, (101,))
257 .unwrap_err()
258 .downcast::<Trap>()
259 })?;
260 with_new_instance(&engine, &component, |store, instance| {
261 instance
262 .get_typed_func::<(u64,), ()>(&mut *store, "take-u64")?
263 .call(store, (101,))
264 .unwrap_err()
265 .downcast::<Trap>()
266 })?;
267 with_new_instance(&engine, &component, |store, instance| {
268 instance
269 .get_typed_func::<(i64,), ()>(&mut *store, "take-s64")?
270 .call(store, (101,))
271 .unwrap_err()
272 .downcast::<Trap>()
273 })?;
274
275 // Zero can be returned as any integer
276 assert_eq!(
277 instance
278 .get_typed_func::<(), (u8,)>(&mut store, "ret-u8")?
279 .call(&mut store, ())?,
280 (0,)
281 );
282 assert_eq!(
283 instance
284 .get_typed_func::<(), (i8,)>(&mut store, "ret-s8")?
285 .call(&mut store, ())?,
286 (0,)
287 );
288 assert_eq!(
289 instance
290 .get_typed_func::<(), (u16,)>(&mut store, "ret-u16")?
291 .call(&mut store, ())?,
292 (0,)
293 );
294 assert_eq!(
295 instance
296 .get_typed_func::<(), (i16,)>(&mut store, "ret-s16")?
297 .call(&mut store, ())?,
298 (0,)
299 );
300 assert_eq!(
301 instance
302 .get_typed_func::<(), (u32,)>(&mut store, "ret-u32")?
303 .call(&mut store, ())?,
304 (0,)
305 );
306 assert_eq!(
307 instance
308 .get_typed_func::<(), (i32,)>(&mut store, "ret-s32")?
309 .call(&mut store, ())?,
310 (0,)
311 );
312 assert_eq!(
313 instance
314 .get_typed_func::<(), (u64,)>(&mut store, "ret-u64")?
315 .call(&mut store, ())?,
316 (0,)
317 );
318 assert_eq!(
319 instance
320 .get_typed_func::<(), (i64,)>(&mut store, "ret-s64")?
321 .call(&mut store, ())?,
322 (0,)
323 );
324
325 // Returning -1 should reinterpret the bytes as defined by each type.
326 assert_eq!(
327 instance
328 .get_typed_func::<(), (u8,)>(&mut store, "retm1-u8")?
329 .call(&mut store, ())?,
330 (0xff,)
331 );
332 assert_eq!(
333 instance
334 .get_typed_func::<(), (i8,)>(&mut store, "retm1-s8")?
335 .call(&mut store, ())?,
336 (-1,)
337 );
338 assert_eq!(
339 instance
340 .get_typed_func::<(), (u16,)>(&mut store, "retm1-u16")?
341 .call(&mut store, ())?,
342 (0xffff,)
343 );
344 assert_eq!(
345 instance
346 .get_typed_func::<(), (i16,)>(&mut store, "retm1-s16")?
347 .call(&mut store, ())?,
348 (-1,)
349 );
350 assert_eq!(
351 instance
352 .get_typed_func::<(), (u32,)>(&mut store, "retm1-u32")?
353 .call(&mut store, ())?,
354 (0xffffffff,)
355 );
356 assert_eq!(
357 instance
358 .get_typed_func::<(), (i32,)>(&mut store, "retm1-s32")?
359 .call(&mut store, ())?,
360 (-1,)
361 );
362 assert_eq!(
363 instance
364 .get_typed_func::<(), (u64,)>(&mut store, "retm1-u64")?
365 .call(&mut store, ())?,
366 (0xffffffff_ffffffff,)
367 );
368 assert_eq!(
369 instance
370 .get_typed_func::<(), (i64,)>(&mut store, "retm1-s64")?
371 .call(&mut store, ())?,
372 (-1,)
373 );
374
375 // Returning 100000 should chop off bytes as necessary
376 let ret: u32 = 100000;
377 assert_eq!(
378 instance
379 .get_typed_func::<(), (u8,)>(&mut store, "retbig-u8")?
380 .call(&mut store, ())?,
381 (ret as u8,),
382 );
383 assert_eq!(
384 instance
385 .get_typed_func::<(), (i8,)>(&mut store, "retbig-s8")?
386 .call(&mut store, ())?,
387 (ret as i8,),
388 );
389 assert_eq!(
390 instance
391 .get_typed_func::<(), (u16,)>(&mut store, "retbig-u16")?
392 .call(&mut store, ())?,
393 (ret as u16,),
394 );
395 assert_eq!(
396 instance
397 .get_typed_func::<(), (i16,)>(&mut store, "retbig-s16")?
398 .call(&mut store, ())?,
399 (ret as i16,),
400 );
401 assert_eq!(
402 instance
403 .get_typed_func::<(), (u32,)>(&mut store, "retbig-u32")?
404 .call(&mut store, ())?,
405 (ret,),
406 );
407 assert_eq!(
408 instance
409 .get_typed_func::<(), (i32,)>(&mut store, "retbig-s32")?
410 .call(&mut store, ())?,
411 (ret as i32,),
412 );
413
414 Ok(())
415 }
416
417 #[test]
type_layers() -> Result<()>418 fn type_layers() -> Result<()> {
419 let component = r#"
420 (component
421 (core module $m
422 (func (export "take-i32-100") (param i32)
423 local.get 0
424 i32.const 2
425 i32.eq
426 br_if 0
427 unreachable
428 )
429 )
430 (core instance $i (instantiate $m))
431 (func (export "take-u32") (param "a" u32) (canon lift (core func $i "take-i32-100")))
432 )
433 "#;
434
435 let engine = super::engine();
436 let component = Component::new(&engine, component)?;
437 let mut store = Store::new(&engine, ());
438 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
439
440 instance
441 .get_typed_func::<(Box<u32>,), ()>(&mut store, "take-u32")?
442 .call(&mut store, (Box::new(2),))?;
443 instance
444 .get_typed_func::<(&u32,), ()>(&mut store, "take-u32")?
445 .call(&mut store, (&2,))?;
446 instance
447 .get_typed_func::<(Arc<u32>,), ()>(&mut store, "take-u32")?
448 .call(&mut store, (Arc::new(2),))?;
449 instance
450 .get_typed_func::<(&Box<Arc<Box<u32>>>,), ()>(&mut store, "take-u32")?
451 .call(&mut store, (&Box::new(Arc::new(Box::new(2))),))?;
452
453 Ok(())
454 }
455
456 #[test]
floats() -> Result<()>457 fn floats() -> Result<()> {
458 let component = r#"
459 (component
460 (core module $m
461 (func (export "i32.reinterpret_f32") (param f32) (result i32)
462 local.get 0
463 i32.reinterpret_f32
464 )
465 (func (export "i64.reinterpret_f64") (param f64) (result i64)
466 local.get 0
467 i64.reinterpret_f64
468 )
469 (func (export "f32.reinterpret_i32") (param i32) (result f32)
470 local.get 0
471 f32.reinterpret_i32
472 )
473 (func (export "f64.reinterpret_i64") (param i64) (result f64)
474 local.get 0
475 f64.reinterpret_i64
476 )
477 )
478 (core instance $i (instantiate $m))
479
480 (func (export "f32-to-u32") (param "a" float32) (result u32)
481 (canon lift (core func $i "i32.reinterpret_f32"))
482 )
483 (func (export "f64-to-u64") (param "a" float64) (result u64)
484 (canon lift (core func $i "i64.reinterpret_f64"))
485 )
486 (func (export "u32-to-f32") (param "a" u32) (result float32)
487 (canon lift (core func $i "f32.reinterpret_i32"))
488 )
489 (func (export "u64-to-f64") (param "a" u64) (result float64)
490 (canon lift (core func $i "f64.reinterpret_i64"))
491 )
492 )
493 "#;
494
495 let engine = super::engine();
496 let component = Component::new(&engine, component)?;
497 let mut store = Store::new(&engine, ());
498 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
499 let f32_to_u32 = instance.get_typed_func::<(f32,), (u32,)>(&mut store, "f32-to-u32")?;
500 let f64_to_u64 = instance.get_typed_func::<(f64,), (u64,)>(&mut store, "f64-to-u64")?;
501 let u32_to_f32 = instance.get_typed_func::<(u32,), (f32,)>(&mut store, "u32-to-f32")?;
502 let u64_to_f64 = instance.get_typed_func::<(u64,), (f64,)>(&mut store, "u64-to-f64")?;
503
504 assert_eq!(f32_to_u32.call(&mut store, (1.0,))?, (1.0f32.to_bits(),));
505 assert_eq!(f64_to_u64.call(&mut store, (2.0,))?, (2.0f64.to_bits(),));
506 assert_eq!(u32_to_f32.call(&mut store, (3.0f32.to_bits(),))?, (3.0,));
507 assert_eq!(u64_to_f64.call(&mut store, (4.0f64.to_bits(),))?, (4.0,));
508
509 assert_eq!(
510 u32_to_f32
511 .call(&mut store, (CANON_32BIT_NAN | 1,))?
512 .0
513 .to_bits(),
514 CANON_32BIT_NAN | 1
515 );
516 assert_eq!(
517 u64_to_f64
518 .call(&mut store, (CANON_64BIT_NAN | 1,))?
519 .0
520 .to_bits(),
521 CANON_64BIT_NAN | 1,
522 );
523
524 assert_eq!(
525 f32_to_u32.call(&mut store, (f32::from_bits(CANON_32BIT_NAN | 1),))?,
526 (CANON_32BIT_NAN | 1,)
527 );
528 assert_eq!(
529 f64_to_u64.call(&mut store, (f64::from_bits(CANON_64BIT_NAN | 1),))?,
530 (CANON_64BIT_NAN | 1,)
531 );
532
533 Ok(())
534 }
535
536 #[test]
bools() -> Result<()>537 fn bools() -> Result<()> {
538 let component = r#"
539 (component
540 (core module $m
541 (func (export "pass") (param i32) (result i32) local.get 0)
542 )
543 (core instance $i (instantiate $m))
544
545 (func (export "u32-to-bool") (param "a" u32) (result bool)
546 (canon lift (core func $i "pass"))
547 )
548 (func (export "bool-to-u32") (param "a" bool) (result u32)
549 (canon lift (core func $i "pass"))
550 )
551 )
552 "#;
553
554 let engine = super::engine();
555 let component = Component::new(&engine, component)?;
556 let mut store = Store::new(&engine, ());
557 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
558 let u32_to_bool = instance.get_typed_func::<(u32,), (bool,)>(&mut store, "u32-to-bool")?;
559 let bool_to_u32 = instance.get_typed_func::<(bool,), (u32,)>(&mut store, "bool-to-u32")?;
560
561 assert_eq!(bool_to_u32.call(&mut store, (false,))?, (0,));
562 assert_eq!(bool_to_u32.call(&mut store, (true,))?, (1,));
563 assert_eq!(u32_to_bool.call(&mut store, (0,))?, (false,));
564 assert_eq!(u32_to_bool.call(&mut store, (1,))?, (true,));
565 assert_eq!(u32_to_bool.call(&mut store, (2,))?, (true,));
566
567 Ok(())
568 }
569
570 #[test]
chars() -> Result<()>571 fn chars() -> Result<()> {
572 let component = r#"
573 (component
574 (core module $m
575 (func (export "pass") (param i32) (result i32) local.get 0)
576 )
577 (core instance $i (instantiate $m))
578
579 (func (export "u32-to-char") (param "a" u32) (result char)
580 (canon lift (core func $i "pass"))
581 )
582 (func (export "char-to-u32") (param "a" char) (result u32)
583 (canon lift (core func $i "pass"))
584 )
585 )
586 "#;
587
588 let engine = super::engine();
589 let component = Component::new(&engine, component)?;
590 let mut store = Store::new(&engine, ());
591 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
592 let u32_to_char = instance.get_typed_func::<(u32,), (char,)>(&mut store, "u32-to-char")?;
593 let char_to_u32 = instance.get_typed_func::<(char,), (u32,)>(&mut store, "char-to-u32")?;
594
595 let mut roundtrip = |x: char| -> Result<()> {
596 assert_eq!(char_to_u32.call(&mut store, (x,))?, (x as u32,));
597 assert_eq!(u32_to_char.call(&mut store, (x as u32,))?, (x,));
598 Ok(())
599 };
600
601 roundtrip('x')?;
602 roundtrip('a')?;
603 roundtrip('\0')?;
604 roundtrip('\n')?;
605 roundtrip('')?;
606
607 let u32_to_char = |store: &mut Store<()>| {
608 Linker::new(&engine)
609 .instantiate(&mut *store, &component)?
610 .get_typed_func::<(u32,), (char,)>(&mut *store, "u32-to-char")
611 };
612 let err = u32_to_char(&mut store)?
613 .call(&mut store, (0xd800,))
614 .unwrap_err();
615 assert!(err.to_string().contains("integer out of range"), "{}", err);
616 let err = u32_to_char(&mut store)?
617 .call(&mut store, (0xdfff,))
618 .unwrap_err();
619 assert!(err.to_string().contains("integer out of range"), "{}", err);
620 let err = u32_to_char(&mut store)?
621 .call(&mut store, (0x110000,))
622 .unwrap_err();
623 assert!(err.to_string().contains("integer out of range"), "{}", err);
624 let err = u32_to_char(&mut store)?
625 .call(&mut store, (u32::MAX,))
626 .unwrap_err();
627 assert!(err.to_string().contains("integer out of range"), "{}", err);
628
629 Ok(())
630 }
631
632 #[test]
tuple_result() -> Result<()>633 fn tuple_result() -> Result<()> {
634 let component = r#"
635 (component
636 (core module $m
637 (memory (export "memory") 1)
638 (func (export "foo") (param i32 i32 f32 f64) (result i32)
639 (local $base i32)
640 (local.set $base (i32.const 8))
641 (i32.store8 offset=0 (local.get $base) (local.get 0))
642 (i32.store16 offset=2 (local.get $base) (local.get 1))
643 (f32.store offset=4 (local.get $base) (local.get 2))
644 (f64.store offset=8 (local.get $base) (local.get 3))
645 local.get $base
646 )
647
648 (func (export "invalid") (result i32)
649 i32.const -8
650 )
651 )
652 (core instance $i (instantiate $m))
653
654 (type $result (tuple s8 u16 float32 float64))
655 (func (export "tuple")
656 (param "a" s8) (param "b" u16) (param "c" float32) (param "d" float64) (result $result)
657 (canon lift (core func $i "foo") (memory $i "memory"))
658 )
659 (func (export "invalid") (result $result)
660 (canon lift (core func $i "invalid") (memory $i "memory"))
661 )
662 )
663 "#;
664
665 let engine = super::engine();
666 let component = Component::new(&engine, component)?;
667 let mut store = Store::new(&engine, ());
668 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
669
670 let input = (-1, 100, 3.0, 100.0);
671 let output = instance
672 .get_typed_func::<(i8, u16, f32, f64), ((i8, u16, f32, f64),)>(&mut store, "tuple")?
673 .call(&mut store, input)?;
674 assert_eq!((input,), output);
675
676 let invalid_func =
677 instance.get_typed_func::<(), ((i8, u16, f32, f64),)>(&mut store, "invalid")?;
678 let err = invalid_func.call(&mut store, ()).err().unwrap();
679 assert!(
680 err.to_string().contains("pointer out of bounds of memory"),
681 "{}",
682 err
683 );
684
685 Ok(())
686 }
687
688 #[test]
strings() -> Result<()>689 fn strings() -> Result<()> {
690 let component = format!(
691 r#"(component
692 (core module $m
693 (memory (export "memory") 1)
694 (func (export "roundtrip") (param i32 i32) (result i32)
695 (local $base i32)
696 (local.set $base
697 (call $realloc
698 (i32.const 0)
699 (i32.const 0)
700 (i32.const 4)
701 (i32.const 8)))
702 (i32.store offset=0
703 (local.get $base)
704 (local.get 0))
705 (i32.store offset=4
706 (local.get $base)
707 (local.get 1))
708 (local.get $base)
709 )
710
711 {REALLOC_AND_FREE}
712 )
713 (core instance $i (instantiate $m))
714
715 (func (export "list8-to-str") (param "a" (list u8)) (result string)
716 (canon lift
717 (core func $i "roundtrip")
718 (memory $i "memory")
719 (realloc (func $i "realloc"))
720 )
721 )
722 (func (export "str-to-list8") (param "a" string) (result (list u8))
723 (canon lift
724 (core func $i "roundtrip")
725 (memory $i "memory")
726 (realloc (func $i "realloc"))
727 )
728 )
729 (func (export "list16-to-str") (param "a" (list u16)) (result string)
730 (canon lift
731 (core func $i "roundtrip")
732 string-encoding=utf16
733 (memory $i "memory")
734 (realloc (func $i "realloc"))
735 )
736 )
737 (func (export "str-to-list16") (param "a" string) (result (list u16))
738 (canon lift
739 (core func $i "roundtrip")
740 string-encoding=utf16
741 (memory $i "memory")
742 (realloc (func $i "realloc"))
743 )
744 )
745 )"#
746 );
747
748 let engine = super::engine();
749 let component = Component::new(&engine, component)?;
750 let mut store = Store::new(&engine, ());
751 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
752 let list8_to_str =
753 instance.get_typed_func::<(&[u8],), (WasmStr,)>(&mut store, "list8-to-str")?;
754 let str_to_list8 =
755 instance.get_typed_func::<(&str,), (WasmList<u8>,)>(&mut store, "str-to-list8")?;
756 let list16_to_str =
757 instance.get_typed_func::<(&[u16],), (WasmStr,)>(&mut store, "list16-to-str")?;
758 let str_to_list16 =
759 instance.get_typed_func::<(&str,), (WasmList<u16>,)>(&mut store, "str-to-list16")?;
760
761 let mut roundtrip = |x: &str| -> Result<()> {
762 let ret = list8_to_str.call(&mut store, (x.as_bytes(),))?.0;
763 assert_eq!(ret.to_str(&store)?, x);
764
765 let utf16 = x.encode_utf16().collect::<Vec<_>>();
766 let ret = list16_to_str.call(&mut store, (&utf16[..],))?.0;
767 assert_eq!(ret.to_str(&store)?, x);
768
769 let ret = str_to_list8.call(&mut store, (x,))?.0;
770 assert_eq!(
771 ret.iter(&mut store).collect::<Result<Vec<_>>>()?,
772 x.as_bytes()
773 );
774
775 let ret = str_to_list16.call(&mut store, (x,))?.0;
776 assert_eq!(ret.iter(&mut store).collect::<Result<Vec<_>>>()?, utf16,);
777
778 Ok(())
779 };
780
781 roundtrip("")?;
782 roundtrip("foo")?;
783 roundtrip("hello there")?;
784 roundtrip("")?;
785 roundtrip("Löwe 老虎 Léopard")?;
786
787 let ret = list8_to_str.call(&mut store, (b"\xff",))?.0;
788 let err = ret.to_str(&store).unwrap_err();
789 assert!(err.to_string().contains("invalid utf-8"), "{}", err);
790
791 let ret = list8_to_str
792 .call(&mut store, (b"hello there \xff invalid",))?
793 .0;
794 let err = ret.to_str(&store).unwrap_err();
795 assert!(err.to_string().contains("invalid utf-8"), "{}", err);
796
797 let ret = list16_to_str.call(&mut store, (&[0xd800],))?.0;
798 let err = ret.to_str(&store).unwrap_err();
799 assert!(err.to_string().contains("unpaired surrogate"), "{}", err);
800
801 let ret = list16_to_str.call(&mut store, (&[0xdfff],))?.0;
802 let err = ret.to_str(&store).unwrap_err();
803 assert!(err.to_string().contains("unpaired surrogate"), "{}", err);
804
805 let ret = list16_to_str.call(&mut store, (&[0xd800, 0xff00],))?.0;
806 let err = ret.to_str(&store).unwrap_err();
807 assert!(err.to_string().contains("unpaired surrogate"), "{}", err);
808
809 Ok(())
810 }
811
812 #[tokio::test]
async_reentrance() -> Result<()>813 async fn async_reentrance() -> Result<()> {
814 _ = env_logger::try_init();
815
816 let component = r#"
817 (component
818 (core module $shim
819 (import "" "task.return" (func $task-return (param i32)))
820 (table (export "funcs") 1 1 funcref)
821 (func (export "export") (param i32) (result i32)
822 (call_indirect (i32.const 0) (local.get 0))
823 )
824 (func (export "callback") (param i32 i32 i32) (result i32) unreachable)
825 )
826 (core func $task-return (canon task.return (result u32)))
827 (core instance $shim (instantiate $shim
828 (with "" (instance (export "task.return" (func $task-return))))
829 ))
830 (func $shim-export (param "p1" u32) (result u32)
831 (canon lift (core func $shim "export") async (callback (func $shim "callback")))
832 )
833
834 (component $inner
835 (import "import" (func $import (param "p1" u32) (result u32)))
836 (core module $libc (memory (export "memory") 1))
837 (core instance $libc (instantiate $libc))
838 (core func $import (canon lower (func $import) async (memory $libc "memory")))
839
840 (core module $m
841 (import "libc" "memory" (memory 1))
842 (import "" "import" (func $import (param i32 i32) (result i32)))
843 (import "" "task.return" (func $task-return (param i32)))
844 (func (export "export") (param i32) (result i32)
845 (i32.store offset=0 (i32.const 1200) (local.get 0))
846 (call $import (i32.const 1200) (i32.const 1204))
847 drop
848 (call $task-return (i32.load offset=0 (i32.const 1204)))
849 i32.const 0
850 )
851 (func (export "callback") (param i32 i32 i32) (result i32) unreachable)
852 )
853 (core type $task-return-type (func (param i32)))
854 (core func $task-return (canon task.return (result u32)))
855 (core instance $i (instantiate $m
856 (with "" (instance
857 (export "task.return" (func $task-return))
858 (export "import" (func $import))
859 ))
860 (with "libc" (instance $libc))
861 ))
862 (func (export "export") (param "p1" u32) (result u32)
863 (canon lift (core func $i "export") async (callback (func $i "callback")))
864 )
865 )
866 (instance $inner (instantiate $inner (with "import" (func $shim-export))))
867
868 (core module $libc (memory (export "memory") 1))
869 (core instance $libc (instantiate $libc))
870 (core func $inner-export (canon lower (func $inner "export") async (memory $libc "memory")))
871
872 (core module $donut
873 (import "" "funcs" (table 1 1 funcref))
874 (import "libc" "memory" (memory 1))
875 (import "" "import" (func $import (param i32 i32) (result i32)))
876 (import "" "task.return" (func $task-return (param i32)))
877 (func $host-export (export "export") (param i32) (result i32)
878 (i32.store offset=0 (i32.const 1200) (local.get 0))
879 (call $import (i32.const 1200) (i32.const 1204))
880 drop
881 (call $task-return (i32.load offset=0 (i32.const 1204)))
882 i32.const 0
883 )
884 (func $guest-export (export "guest-export") (param i32) (result i32) unreachable)
885 (func (export "callback") (param i32 i32 i32) (result i32) unreachable)
886 (func $start
887 (table.set (i32.const 0) (ref.func $guest-export))
888 )
889 (start $start)
890 )
891
892 (core instance $donut (instantiate $donut
893 (with "" (instance
894 (export "task.return" (func $task-return))
895 (export "import" (func $inner-export))
896 (export "funcs" (table $shim "funcs"))
897 ))
898 (with "libc" (instance $libc))
899 ))
900 (func (export "export") (param "p1" u32) (result u32)
901 (canon lift (core func $donut "export") async (callback (func $donut "callback")))
902 )
903 )"#;
904
905 let mut config = Config::new();
906 config.wasm_component_model_async(true);
907 config.wasm_component_model_async_stackful(true);
908 config.wasm_component_model_threading(true);
909 let engine = &Engine::new(&config)?;
910 let component = Component::new(&engine, component)?;
911 let mut store = Store::new(&engine, ());
912
913 let instance = Linker::new(&engine)
914 .instantiate_async(&mut store, &component)
915 .await?;
916 let func = instance.get_typed_func::<(u32,), (u32,)>(&mut store, "export")?;
917 let message = "cannot enter component instance";
918 match store
919 .run_concurrent(async move |accessor| {
920 wasmtime::error::Ok(func.call_concurrent(accessor, (42,)).await?.0)
921 })
922 .await
923 {
924 Ok(_) => panic!(),
925 Err(e) => assert!(
926 format!("{e:?}").contains(message),
927 "expected `{message}`; got `{e:?}`"
928 ),
929 }
930
931 Ok(())
932 }
933
934 #[tokio::test]
missing_task_return_call_stackless() -> Result<()>935 async fn missing_task_return_call_stackless() -> Result<()> {
936 task_return_trap(
937 r#"(component
938 (core module $m
939 (import "" "task.return" (func $task-return))
940 (func (export "foo") (result i32)
941 i32.const 0
942 )
943 (func (export "callback") (param i32 i32 i32) (result i32) unreachable)
944 )
945 (core func $task-return (canon task.return))
946 (core instance $i (instantiate $m
947 (with "" (instance (export "task.return" (func $task-return))))
948 ))
949 (func (export "foo") (canon lift (core func $i "foo") async (callback (func $i "callback"))))
950 )"#,
951 "wasm trap: async-lifted export failed to produce a result",
952 )
953 .await
954 }
955
956 #[tokio::test]
missing_task_return_call_stackless_explicit_thread() -> Result<()>957 async fn missing_task_return_call_stackless_explicit_thread() -> Result<()> {
958 task_return_trap(
959 r#"(component
960 (core module $libc
961 (table (export "__indirect_function_table") 1 funcref))
962 (core module $m
963 (import "" "task.return" (func $task-return))
964 (import "" "thread.new-indirect" (func $thread-new-indirect (param i32 i32) (result i32)))
965 (import "" "thread.unsuspend" (func $thread-unsuspend (param i32)))
966 (import "libc" "__indirect_function_table" (table $indirect-function-table 1 funcref))
967 (func $thread-start (param i32) (; empty ;))
968 (elem (table $indirect-function-table) (i32.const 0) func $thread-start)
969 (func (export "foo") (result i32)
970 (call $thread-unsuspend
971 (call $thread-new-indirect (i32.const 0) (i32.const 0)))
972 i32.const 0
973 )
974 (func (export "callback") (param i32 i32 i32) (result i32) unreachable)
975 )
976 (core instance $libc (instantiate $libc))
977 (core type $start-func-ty (func (param i32)))
978 (alias core export $libc "__indirect_function_table" (core table $indirect-function-table))
979 (core func $thread-new-indirect
980 (canon thread.new-indirect $start-func-ty (table $indirect-function-table)))
981 (core func $thread-unsuspend (canon thread.unsuspend))
982 (core func $task-return (canon task.return))
983 (core instance $i (instantiate $m
984 (with "" (instance
985 (export "thread.new-indirect" (func $thread-new-indirect))
986 (export "thread.unsuspend" (func $thread-unsuspend))
987 (export "task.return" (func $task-return))
988 ))
989 (with "libc" (instance $libc))
990 ))
991 (func (export "foo") (canon lift (core func $i "foo") async (callback (func $i "callback"))))
992 )"#,
993 "wasm trap: async-lifted export failed to produce a result",
994 )
995 .await
996 }
997
998 #[tokio::test]
missing_task_return_call_stackful_explicit_thread() -> Result<()>999 async fn missing_task_return_call_stackful_explicit_thread() -> Result<()> {
1000 task_return_trap(
1001 r#"(component
1002 (core module $libc
1003 (table (export "__indirect_function_table") 1 funcref))
1004 (core module $m
1005 (import "" "task.return" (func $task-return))
1006 (import "" "thread.new-indirect" (func $thread-new-indirect (param i32 i32) (result i32)))
1007 (import "" "thread.unsuspend" (func $thread-unsuspend (param i32)))
1008 (import "libc" "__indirect_function_table" (table $indirect-function-table 1 funcref))
1009 (func $thread-start (param i32) (; empty ;))
1010 (elem (table $indirect-function-table) (i32.const 0) func $thread-start)
1011 (func (export "foo")
1012 (call $thread-unsuspend
1013 (call $thread-new-indirect (i32.const 0) (i32.const 0)))
1014 )
1015 )
1016 (core instance $libc (instantiate $libc))
1017 (core type $start-func-ty (func (param i32)))
1018 (alias core export $libc "__indirect_function_table" (core table $indirect-function-table))
1019 (core func $thread-new-indirect
1020 (canon thread.new-indirect $start-func-ty (table $indirect-function-table)))
1021 (core func $thread-unsuspend (canon thread.unsuspend))
1022 (core func $task-return (canon task.return))
1023 (core instance $i (instantiate $m
1024 (with "" (instance
1025 (export "thread.new-indirect" (func $thread-new-indirect))
1026 (export "thread.unsuspend" (func $thread-unsuspend))
1027 (export "task.return" (func $task-return))
1028 ))
1029 (with "libc" (instance $libc))
1030 ))
1031 (func (export "foo") (canon lift (core func $i "foo") async))
1032 )"#,
1033 "wasm trap: async-lifted export failed to produce a result",
1034 )
1035 .await
1036 }
1037
1038 #[tokio::test]
missing_task_return_call_stackful() -> Result<()>1039 async fn missing_task_return_call_stackful() -> Result<()> {
1040 task_return_trap(
1041 r#"(component
1042 (core module $m
1043 (import "" "task.return" (func $task-return))
1044 (func (export "foo"))
1045 )
1046 (core func $task-return (canon task.return))
1047 (core instance $i (instantiate $m
1048 (with "" (instance (export "task.return" (func $task-return))))
1049 ))
1050 (func (export "foo") (canon lift (core func $i "foo") async))
1051 )"#,
1052 "wasm trap: async-lifted export failed to produce a result",
1053 )
1054 .await
1055 }
1056
1057 #[tokio::test]
task_return_type_mismatch() -> Result<()>1058 async fn task_return_type_mismatch() -> Result<()> {
1059 task_return_trap(
1060 r#"(component
1061 (core module $m
1062 (import "" "task.return" (func $task-return (param i32)))
1063 (func (export "foo") (call $task-return (i32.const 42)))
1064 )
1065 (core func $task-return (canon task.return (result u32)))
1066 (core instance $i (instantiate $m
1067 (with "" (instance (export "task.return" (func $task-return))))
1068 ))
1069 (func (export "foo") (canon lift (core func $i "foo") async))
1070 )"#,
1071 "invalid `task.return` signature and/or options for current task",
1072 )
1073 .await
1074 }
1075
1076 #[tokio::test]
task_return_memory_mismatch() -> Result<()>1077 async fn task_return_memory_mismatch() -> Result<()> {
1078 task_return_trap(
1079 r#"(component
1080 (core module $libc (memory (export "memory") 1))
1081 (core instance $libc (instantiate $libc))
1082 (core module $m
1083 (import "" "task.return" (func $task-return))
1084 (func (export "foo") (call $task-return))
1085 )
1086 (core func $task-return (canon task.return (memory $libc "memory")))
1087 (core instance $i (instantiate $m
1088 (with "" (instance (export "task.return" (func $task-return))))
1089 ))
1090 (func (export "foo") (canon lift (core func $i "foo") async))
1091 )"#,
1092 "invalid `task.return` signature and/or options for current task",
1093 )
1094 .await
1095 }
1096
1097 #[tokio::test]
task_return_string_encoding_mismatch() -> Result<()>1098 async fn task_return_string_encoding_mismatch() -> Result<()> {
1099 task_return_trap(
1100 r#"(component
1101 (core module $m
1102 (import "" "task.return" (func $task-return))
1103 (func (export "foo") (call $task-return))
1104 )
1105 (core func $task-return (canon task.return string-encoding=utf16))
1106 (core instance $i (instantiate $m
1107 (with "" (instance (export "task.return" (func $task-return))))
1108 ))
1109 (func (export "foo") (canon lift (core func $i "foo") async))
1110 )"#,
1111 "invalid `task.return` signature and/or options for current task",
1112 )
1113 .await
1114 }
1115
task_return_trap(component: &str, substring: &str) -> Result<()>1116 async fn task_return_trap(component: &str, substring: &str) -> Result<()> {
1117 let mut config = Config::new();
1118 config.wasm_component_model_async(true);
1119 config.wasm_component_model_async_stackful(true);
1120 config.wasm_component_model_threading(true);
1121 let engine = &Engine::new(&config)?;
1122 let component = Component::new(&engine, component)?;
1123 let mut store = Store::new(&engine, ());
1124
1125 let instance = Linker::new(&engine)
1126 .instantiate_async(&mut store, &component)
1127 .await?;
1128
1129 let func = instance.get_typed_func::<(), ()>(&mut store, "foo")?;
1130 match store
1131 .run_concurrent(async move |accessor| {
1132 wasmtime::error::Ok(func.call_concurrent(accessor, ()).await?)
1133 })
1134 .await
1135 {
1136 Ok(_) => panic!(),
1137 Err(e) => {
1138 assert!(
1139 format!("{e:?}").contains(substring),
1140 "could not find `{substring}` in `{e:?}`"
1141 )
1142 }
1143 }
1144
1145 Ok(())
1146 }
1147
1148 #[tokio::test]
many_parameters() -> Result<()>1149 async fn many_parameters() -> Result<()> {
1150 test_many_parameters(false, false).await
1151 }
1152
1153 #[tokio::test]
many_parameters_concurrent() -> Result<()>1154 async fn many_parameters_concurrent() -> Result<()> {
1155 test_many_parameters(false, true).await
1156 }
1157
1158 #[tokio::test]
many_parameters_dynamic() -> Result<()>1159 async fn many_parameters_dynamic() -> Result<()> {
1160 test_many_parameters(true, false).await
1161 }
1162
1163 #[tokio::test]
many_parameters_dynamic_concurrent() -> Result<()>1164 async fn many_parameters_dynamic_concurrent() -> Result<()> {
1165 test_many_parameters(true, true).await
1166 }
1167
test_many_parameters(dynamic: bool, concurrent: bool) -> Result<()>1168 async fn test_many_parameters(dynamic: bool, concurrent: bool) -> Result<()> {
1169 let (body, async_opts) = if concurrent {
1170 (
1171 r#"
1172 (call $task-return
1173 (i32.const 0)
1174 (i32.mul
1175 (memory.size)
1176 (i32.const 65536)
1177 )
1178 (local.get 0)
1179 )
1180
1181 (i32.const 0)
1182 "#,
1183 r#"async (callback (func $i "callback"))"#,
1184 )
1185 } else {
1186 (
1187 r#"
1188 (local $base i32)
1189
1190 ;; Allocate space for the return
1191 (local.set $base
1192 (call $realloc
1193 (i32.const 0)
1194 (i32.const 0)
1195 (i32.const 4)
1196 (i32.const 12)))
1197
1198 ;; Store the pointer/length of the entire linear memory
1199 ;; so we have access to everything.
1200 (i32.store offset=0
1201 (local.get $base)
1202 (i32.const 0))
1203 (i32.store offset=4
1204 (local.get $base)
1205 (i32.mul
1206 (memory.size)
1207 (i32.const 65536)))
1208
1209 ;; And also store our pointer parameter
1210 (i32.store offset=8
1211 (local.get $base)
1212 (local.get 0))
1213
1214 (local.get $base)
1215 "#,
1216 "",
1217 )
1218 };
1219
1220 let component = format!(
1221 r#"(component
1222 (core module $libc
1223 (memory (export "memory") 1)
1224
1225 {REALLOC_AND_FREE}
1226 )
1227 (core instance $libc (instantiate $libc))
1228 (core module $m
1229 (import "libc" "memory" (memory 1))
1230 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
1231 (import "" "task.return" (func $task-return (param i32 i32 i32)))
1232 (func (export "foo") (param i32) (result i32)
1233 {body}
1234 )
1235 (func (export "callback") (param i32 i32 i32) (result i32) unreachable)
1236 )
1237 (type $tuple (tuple (list u8) u32))
1238 (core func $task-return (canon task.return
1239 (result $tuple)
1240 (memory $libc "memory")
1241 ))
1242 (core instance $i (instantiate $m
1243 (with "" (instance (export "task.return" (func $task-return))))
1244 (with "libc" (instance $libc))
1245 ))
1246
1247 (type $t (func
1248 (param "p1" s8) ;; offset 0, size 1
1249 (param "p2" u64) ;; offset 8, size 8
1250 (param "p3" float32) ;; offset 16, size 4
1251 (param "p4" u8) ;; offset 20, size 1
1252 (param "p5" s16) ;; offset 22, size 2
1253 (param "p6" string) ;; offset 24, size 8
1254 (param "p7" (list u32)) ;; offset 32, size 8
1255 (param "p8" bool) ;; offset 40, size 1
1256 (param "p9" bool) ;; offset 41, size 1
1257 (param "p0" char) ;; offset 44, size 4
1258 (param "pa" (list bool)) ;; offset 48, size 8
1259 (param "pb" (list char)) ;; offset 56, size 8
1260 (param "pc" (list string)) ;; offset 64, size 8
1261
1262 (result $tuple)
1263 ))
1264 (func (export "many-param") (type $t)
1265 (canon lift
1266 (core func $i "foo")
1267 (memory $libc "memory")
1268 (realloc (func $libc "realloc"))
1269 {async_opts}
1270 )
1271 )
1272 )"#
1273 );
1274
1275 let mut config = Config::new();
1276 config.wasm_component_model_async(true);
1277 let engine = &Engine::new(&config)?;
1278 let component = Component::new(&engine, component)?;
1279 let mut store = Store::new(&engine, ());
1280
1281 let instance = Linker::new(&engine)
1282 .instantiate_async(&mut store, &component)
1283 .await?;
1284
1285 let input = (
1286 -100,
1287 u64::MAX / 2,
1288 f32::from_bits(CANON_32BIT_NAN | 1),
1289 38,
1290 18831,
1291 "this is the first string",
1292 [1, 2, 3, 4, 5, 6, 7, 8].as_slice(),
1293 true,
1294 false,
1295 '',
1296 [false, true, false, true, true].as_slice(),
1297 ['', '', '', '', ''].as_slice(),
1298 [
1299 "the quick",
1300 "brown fox",
1301 "was too lazy",
1302 "to jump over the dog",
1303 "what a demanding dog",
1304 ]
1305 .as_slice(),
1306 );
1307
1308 let (memory, pointer) = if dynamic {
1309 let input = vec![
1310 Val::S8(input.0),
1311 Val::U64(input.1),
1312 Val::Float32(input.2),
1313 Val::U8(input.3),
1314 Val::S16(input.4),
1315 Val::String(input.5.into()),
1316 Val::List(input.6.iter().copied().map(Val::U32).collect()),
1317 Val::Bool(input.7),
1318 Val::Bool(input.8),
1319 Val::Char(input.9),
1320 Val::List(input.10.iter().copied().map(Val::Bool).collect()),
1321 Val::List(input.11.iter().copied().map(Val::Char).collect()),
1322 Val::List(input.12.iter().map(|&s| Val::String(s.into())).collect()),
1323 ];
1324 let func = instance.get_func(&mut store, "many-param").unwrap();
1325
1326 let mut results = vec![Val::Bool(false)];
1327 if concurrent {
1328 store
1329 .run_concurrent(async |store| {
1330 func.call_concurrent(store, &input, &mut results).await?;
1331 wasmtime::error::Ok(())
1332 })
1333 .await??;
1334 } else {
1335 func.call_async(&mut store, &input, &mut results).await?;
1336 };
1337 let mut results = results.into_iter();
1338 let Some(Val::Tuple(results)) = results.next() else {
1339 panic!()
1340 };
1341 let mut results = results.into_iter();
1342 let Some(Val::List(memory)) = results.next() else {
1343 panic!()
1344 };
1345 let Some(Val::U32(pointer)) = results.next() else {
1346 panic!()
1347 };
1348 (
1349 memory
1350 .into_iter()
1351 .map(|v| if let Val::U8(v) = v { v } else { panic!() })
1352 .collect(),
1353 pointer,
1354 )
1355 } else {
1356 let func = instance.get_typed_func::<(
1357 i8,
1358 u64,
1359 f32,
1360 u8,
1361 i16,
1362 &str,
1363 &[u32],
1364 bool,
1365 bool,
1366 char,
1367 &[bool],
1368 &[char],
1369 &[&str],
1370 ), ((Vec<u8>, u32),)>(&mut store, "many-param")?;
1371
1372 if concurrent {
1373 store
1374 .run_concurrent(async move |accessor| {
1375 wasmtime::error::Ok(func.call_concurrent(accessor, input).await?)
1376 })
1377 .await??
1378 .0
1379 } else {
1380 func.call_async(&mut store, input).await?.0
1381 }
1382 };
1383 let memory = &memory[..];
1384
1385 let mut actual = &memory[pointer as usize..][..72];
1386 assert_eq!(i8::from_le_bytes(*actual.take_n::<1>()), input.0);
1387 actual.skip::<7>();
1388 assert_eq!(u64::from_le_bytes(*actual.take_n::<8>()), input.1);
1389 assert_eq!(
1390 u32::from_le_bytes(*actual.take_n::<4>()),
1391 CANON_32BIT_NAN | 1
1392 );
1393 assert_eq!(u8::from_le_bytes(*actual.take_n::<1>()), input.3);
1394 actual.skip::<1>();
1395 assert_eq!(i16::from_le_bytes(*actual.take_n::<2>()), input.4);
1396 assert_eq!(actual.ptr_len(memory, 1), input.5.as_bytes());
1397 let mut mem = actual.ptr_len(memory, 4);
1398 for expected in input.6.iter() {
1399 assert_eq!(u32::from_le_bytes(*mem.take_n::<4>()), *expected);
1400 }
1401 assert!(mem.is_empty());
1402 assert_eq!(actual.take_n::<1>(), &[input.7 as u8]);
1403 assert_eq!(actual.take_n::<1>(), &[input.8 as u8]);
1404 actual.skip::<2>();
1405 assert_eq!(u32::from_le_bytes(*actual.take_n::<4>()), input.9 as u32);
1406
1407 // (list bool)
1408 mem = actual.ptr_len(memory, 1);
1409 for expected in input.10.iter() {
1410 assert_eq!(mem.take_n::<1>(), &[*expected as u8]);
1411 }
1412 assert!(mem.is_empty());
1413
1414 // (list char)
1415 mem = actual.ptr_len(memory, 4);
1416 for expected in input.11.iter() {
1417 assert_eq!(u32::from_le_bytes(*mem.take_n::<4>()), *expected as u32);
1418 }
1419 assert!(mem.is_empty());
1420
1421 // (list string)
1422 mem = actual.ptr_len(memory, 8);
1423 for expected in input.12.iter() {
1424 let actual = mem.ptr_len(memory, 1);
1425 assert_eq!(actual, expected.as_bytes());
1426 }
1427 assert!(mem.is_empty());
1428 assert!(actual.is_empty());
1429
1430 Ok(())
1431 }
1432
1433 #[tokio::test]
many_results() -> Result<()>1434 async fn many_results() -> Result<()> {
1435 test_many_results(false, false).await
1436 }
1437
1438 #[tokio::test]
many_results_concurrent() -> Result<()>1439 async fn many_results_concurrent() -> Result<()> {
1440 test_many_results(false, true).await
1441 }
1442
1443 #[tokio::test]
many_results_dynamic() -> Result<()>1444 async fn many_results_dynamic() -> Result<()> {
1445 test_many_results(true, false).await
1446 }
1447
1448 #[tokio::test]
many_results_dynamic_concurrent() -> Result<()>1449 async fn many_results_dynamic_concurrent() -> Result<()> {
1450 test_many_results(true, true).await
1451 }
1452
test_many_results(dynamic: bool, concurrent: bool) -> Result<()>1453 async fn test_many_results(dynamic: bool, concurrent: bool) -> Result<()> {
1454 let (ret, async_opts) = if concurrent {
1455 (
1456 r#"
1457 call $task-return
1458 i32.const 0
1459 "#,
1460 r#"async (callback (func $i "callback"))"#,
1461 )
1462 } else {
1463 ("", "")
1464 };
1465
1466 let my_nan = CANON_32BIT_NAN | 1;
1467
1468 let component = format!(
1469 r#"(component
1470 (core module $libc
1471 (memory (export "memory") 1)
1472
1473 {REALLOC_AND_FREE}
1474 )
1475 (core instance $libc (instantiate $libc))
1476 (core module $m
1477 (import "libc" "memory" (memory 1))
1478 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
1479 (import "" "task.return" (func $task-return (param i32)))
1480 (func (export "foo") (result i32)
1481 (local $base i32)
1482 (local $string i32)
1483 (local $list i32)
1484
1485 (local.set $base
1486 (call $realloc
1487 (i32.const 0)
1488 (i32.const 0)
1489 (i32.const 8)
1490 (i32.const 72)))
1491
1492 (i32.store8 offset=0
1493 (local.get $base)
1494 (i32.const -100))
1495
1496 (i64.store offset=8
1497 (local.get $base)
1498 (i64.const 9223372036854775807))
1499
1500 (f32.store offset=16
1501 (local.get $base)
1502 (f32.reinterpret_i32 (i32.const {my_nan})))
1503
1504 (i32.store8 offset=20
1505 (local.get $base)
1506 (i32.const 38))
1507
1508 (i32.store16 offset=22
1509 (local.get $base)
1510 (i32.const 18831))
1511
1512 (local.set $string
1513 (call $realloc
1514 (i32.const 0)
1515 (i32.const 0)
1516 (i32.const 1)
1517 (i32.const 6)))
1518
1519 (i32.store8 offset=0
1520 (local.get $string)
1521 (i32.const 97)) ;; 'a'
1522 (i32.store8 offset=1
1523 (local.get $string)
1524 (i32.const 98)) ;; 'b'
1525 (i32.store8 offset=2
1526 (local.get $string)
1527 (i32.const 99)) ;; 'c'
1528 (i32.store8 offset=3
1529 (local.get $string)
1530 (i32.const 100)) ;; 'd'
1531 (i32.store8 offset=4
1532 (local.get $string)
1533 (i32.const 101)) ;; 'e'
1534 (i32.store8 offset=5
1535 (local.get $string)
1536 (i32.const 102)) ;; 'f'
1537
1538 (i32.store offset=24
1539 (local.get $base)
1540 (local.get $string))
1541
1542 (i32.store offset=28
1543 (local.get $base)
1544 (i32.const 2))
1545
1546 (local.set $list
1547 (call $realloc
1548 (i32.const 0)
1549 (i32.const 0)
1550 (i32.const 4)
1551 (i32.const 32)))
1552
1553 (i32.store offset=0
1554 (local.get $list)
1555 (i32.const 1))
1556 (i32.store offset=4
1557 (local.get $list)
1558 (i32.const 2))
1559 (i32.store offset=8
1560 (local.get $list)
1561 (i32.const 3))
1562 (i32.store offset=12
1563 (local.get $list)
1564 (i32.const 4))
1565 (i32.store offset=16
1566 (local.get $list)
1567 (i32.const 5))
1568 (i32.store offset=20
1569 (local.get $list)
1570 (i32.const 6))
1571 (i32.store offset=24
1572 (local.get $list)
1573 (i32.const 7))
1574 (i32.store offset=28
1575 (local.get $list)
1576 (i32.const 8))
1577
1578 (i32.store offset=32
1579 (local.get $base)
1580 (local.get $list))
1581
1582 (i32.store offset=36
1583 (local.get $base)
1584 (i32.const 8))
1585
1586 (i32.store8 offset=40
1587 (local.get $base)
1588 (i32.const 1))
1589
1590 (i32.store8 offset=41
1591 (local.get $base)
1592 (i32.const 0))
1593
1594 (i32.store offset=44
1595 (local.get $base)
1596 (i32.const 128681)) ;; ''
1597
1598 (local.set $list
1599 (call $realloc
1600 (i32.const 0)
1601 (i32.const 0)
1602 (i32.const 1)
1603 (i32.const 5)))
1604
1605 (i32.store8 offset=0
1606 (local.get $list)
1607 (i32.const 0))
1608 (i32.store8 offset=1
1609 (local.get $list)
1610 (i32.const 1))
1611 (i32.store8 offset=2
1612 (local.get $list)
1613 (i32.const 0))
1614 (i32.store8 offset=3
1615 (local.get $list)
1616 (i32.const 1))
1617 (i32.store8 offset=4
1618 (local.get $list)
1619 (i32.const 1))
1620
1621 (i32.store offset=48
1622 (local.get $base)
1623 (local.get $list))
1624
1625 (i32.store offset=52
1626 (local.get $base)
1627 (i32.const 5))
1628
1629 (local.set $list
1630 (call $realloc
1631 (i32.const 0)
1632 (i32.const 0)
1633 (i32.const 4)
1634 (i32.const 20)))
1635
1636 (i32.store offset=0
1637 (local.get $list)
1638 (i32.const 127820)) ;; ''
1639 (i32.store offset=4
1640 (local.get $list)
1641 (i32.const 129360)) ;; ''
1642 (i32.store offset=8
1643 (local.get $list)
1644 (i32.const 127831)) ;; ''
1645 (i32.store offset=12
1646 (local.get $list)
1647 (i32.const 127833)) ;; ''
1648 (i32.store offset=16
1649 (local.get $list)
1650 (i32.const 127841)) ;; ''
1651
1652 (i32.store offset=56
1653 (local.get $base)
1654 (local.get $list))
1655
1656 (i32.store offset=60
1657 (local.get $base)
1658 (i32.const 5))
1659
1660 (local.set $list
1661 (call $realloc
1662 (i32.const 0)
1663 (i32.const 0)
1664 (i32.const 4)
1665 (i32.const 16)))
1666
1667 (i32.store offset=0
1668 (local.get $list)
1669 (i32.add (local.get $string) (i32.const 2)))
1670 (i32.store offset=4
1671 (local.get $list)
1672 (i32.const 2))
1673 (i32.store offset=8
1674 (local.get $list)
1675 (i32.add (local.get $string) (i32.const 4)))
1676 (i32.store offset=12
1677 (local.get $list)
1678 (i32.const 2))
1679
1680 (i32.store offset=64
1681 (local.get $base)
1682 (local.get $list))
1683
1684 (i32.store offset=68
1685 (local.get $base)
1686 (i32.const 2))
1687
1688 local.get $base
1689
1690 {ret}
1691 )
1692 (func (export "callback") (param i32 i32 i32) (result i32) unreachable)
1693 )
1694 (type $tuple (tuple
1695 s8
1696 u64
1697 float32
1698 u8
1699 s16
1700 string
1701 (list u32)
1702 bool
1703 bool
1704 char
1705 (list bool)
1706 (list char)
1707 (list string)
1708 ))
1709 (core func $task-return (canon task.return
1710 (result $tuple)
1711 (memory $libc "memory")
1712 ))
1713 (core instance $i (instantiate $m
1714 (with "" (instance (export "task.return" (func $task-return))))
1715 (with "libc" (instance $libc))
1716 ))
1717
1718 (type $t (func (result $tuple)))
1719 (func (export "many-results") (type $t)
1720 (canon lift
1721 (core func $i "foo")
1722 (memory $libc "memory")
1723 (realloc (func $libc "realloc"))
1724 {async_opts}
1725 )
1726 )
1727 )"#
1728 );
1729
1730 let mut config = Config::new();
1731 config.wasm_component_model_async(true);
1732 let engine = &Engine::new(&config)?;
1733 let component = Component::new(&engine, component)?;
1734 let mut store = Store::new(&engine, ());
1735
1736 let instance = Linker::new(&engine)
1737 .instantiate_async(&mut store, &component)
1738 .await?;
1739
1740 let expected = (
1741 -100i8,
1742 u64::MAX / 2,
1743 f32::from_bits(CANON_32BIT_NAN | 1),
1744 38u8,
1745 18831i16,
1746 "ab".to_string(),
1747 vec![1u32, 2, 3, 4, 5, 6, 7, 8],
1748 true,
1749 false,
1750 '',
1751 vec![false, true, false, true, true],
1752 vec!['', '', '', '', ''],
1753 vec!["cd".to_string(), "ef".to_string()],
1754 );
1755
1756 let actual = if dynamic {
1757 let func = instance.get_func(&mut store, "many-results").unwrap();
1758
1759 let mut results = vec![Val::Bool(false)];
1760 if concurrent {
1761 store
1762 .run_concurrent(async |store| {
1763 func.call_concurrent(store, &[], &mut results).await?;
1764 wasmtime::error::Ok(())
1765 })
1766 .await??;
1767 } else {
1768 func.call_async(&mut store, &[], &mut results).await?;
1769 };
1770 let mut results = results.into_iter();
1771
1772 let Some(Val::Tuple(results)) = results.next() else {
1773 panic!()
1774 };
1775 let mut results = results.into_iter();
1776 let Some(Val::S8(p1)) = results.next() else {
1777 panic!()
1778 };
1779 let Some(Val::U64(p2)) = results.next() else {
1780 panic!()
1781 };
1782 let Some(Val::Float32(p3)) = results.next() else {
1783 panic!()
1784 };
1785 let Some(Val::U8(p4)) = results.next() else {
1786 panic!()
1787 };
1788 let Some(Val::S16(p5)) = results.next() else {
1789 panic!()
1790 };
1791 let Some(Val::String(p6)) = results.next() else {
1792 panic!()
1793 };
1794 let Some(Val::List(p7)) = results.next() else {
1795 panic!()
1796 };
1797 let p7 = p7
1798 .into_iter()
1799 .map(|v| if let Val::U32(v) = v { v } else { panic!() })
1800 .collect();
1801 let Some(Val::Bool(p8)) = results.next() else {
1802 panic!()
1803 };
1804 let Some(Val::Bool(p9)) = results.next() else {
1805 panic!()
1806 };
1807 let Some(Val::Char(p0)) = results.next() else {
1808 panic!()
1809 };
1810 let Some(Val::List(pa)) = results.next() else {
1811 panic!()
1812 };
1813 let pa = pa
1814 .into_iter()
1815 .map(|v| if let Val::Bool(v) = v { v } else { panic!() })
1816 .collect();
1817 let Some(Val::List(pb)) = results.next() else {
1818 panic!()
1819 };
1820 let pb = pb
1821 .into_iter()
1822 .map(|v| if let Val::Char(v) = v { v } else { panic!() })
1823 .collect();
1824 let Some(Val::List(pc)) = results.next() else {
1825 panic!()
1826 };
1827 let pc = pc
1828 .into_iter()
1829 .map(|v| if let Val::String(v) = v { v } else { panic!() })
1830 .collect();
1831
1832 (p1, p2, p3, p4, p5, p6, p7, p8, p9, p0, pa, pb, pc)
1833 } else {
1834 let func = instance.get_typed_func::<(), ((
1835 i8,
1836 u64,
1837 f32,
1838 u8,
1839 i16,
1840 String,
1841 Vec<u32>,
1842 bool,
1843 bool,
1844 char,
1845 Vec<bool>,
1846 Vec<char>,
1847 Vec<String>,
1848 ),)>(&mut store, "many-results")?;
1849
1850 if concurrent {
1851 store
1852 .run_concurrent(async move |accessor| {
1853 wasmtime::error::Ok(func.call_concurrent(accessor, ()).await?)
1854 })
1855 .await??
1856 .0
1857 } else {
1858 func.call_async(&mut store, ()).await?.0
1859 }
1860 };
1861
1862 assert_eq!(expected.0, actual.0);
1863 assert_eq!(expected.1, actual.1);
1864 assert!(expected.2.is_nan());
1865 assert!(actual.2.is_nan());
1866 assert_eq!(expected.3, actual.3);
1867 assert_eq!(expected.4, actual.4);
1868 assert_eq!(expected.5, actual.5);
1869 assert_eq!(expected.6, actual.6);
1870 assert_eq!(expected.7, actual.7);
1871 assert_eq!(expected.8, actual.8);
1872 assert_eq!(expected.9, actual.9);
1873 assert_eq!(expected.10, actual.10);
1874 assert_eq!(expected.11, actual.11);
1875 assert_eq!(expected.12, actual.12);
1876
1877 Ok(())
1878 }
1879
1880 #[test]
some_traps() -> Result<()>1881 fn some_traps() -> Result<()> {
1882 let middle_of_memory = (i32::MAX / 2) & (!0xff);
1883 let component = format!(
1884 r#"(component
1885 (core module $m
1886 (memory (export "memory") 1)
1887 (func (export "take-many") (param i32))
1888 (func (export "take-list") (param i32 i32))
1889
1890 (func (export "realloc") (param i32 i32 i32 i32) (result i32)
1891 unreachable)
1892 )
1893 (core instance $i (instantiate $m))
1894
1895 (func (export "take-list-unreachable") (param "a" (list u8))
1896 (canon lift (core func $i "take-list") (memory $i "memory") (realloc (func $i "realloc")))
1897 )
1898 (func (export "take-string-unreachable") (param "a" string)
1899 (canon lift (core func $i "take-list") (memory $i "memory") (realloc (func $i "realloc")))
1900 )
1901
1902 (type $t (func
1903 (param "s1" string)
1904 (param "s2" string)
1905 (param "s3" string)
1906 (param "s4" string)
1907 (param "s5" string)
1908 (param "s6" string)
1909 (param "s7" string)
1910 (param "s8" string)
1911 (param "s9" string)
1912 (param "s10" string)
1913 ))
1914 (func (export "take-many-unreachable") (type $t)
1915 (canon lift (core func $i "take-many") (memory $i "memory") (realloc (func $i "realloc")))
1916 )
1917
1918 (core module $m2
1919 (memory (export "memory") 1)
1920 (func (export "take-many") (param i32))
1921 (func (export "take-list") (param i32 i32))
1922
1923 (func (export "realloc") (param i32 i32 i32 i32) (result i32)
1924 i32.const {middle_of_memory})
1925 )
1926 (core instance $i2 (instantiate $m2))
1927
1928 (func (export "take-list-base-oob") (param "a" (list u8))
1929 (canon lift (core func $i2 "take-list") (memory $i2 "memory") (realloc (func $i2 "realloc")))
1930 )
1931 (func (export "take-string-base-oob") (param "a" string)
1932 (canon lift (core func $i2 "take-list") (memory $i2 "memory") (realloc (func $i2 "realloc")))
1933 )
1934 (func (export "take-many-base-oob") (type $t)
1935 (canon lift (core func $i2 "take-many") (memory $i2 "memory") (realloc (func $i2 "realloc")))
1936 )
1937
1938 (core module $m3
1939 (memory (export "memory") 1)
1940 (func (export "take-many") (param i32))
1941 (func (export "take-list") (param i32 i32))
1942
1943 (func (export "realloc") (param i32 i32 i32 i32) (result i32)
1944 i32.const 65532)
1945 )
1946 (core instance $i3 (instantiate $m3))
1947
1948 (func (export "take-list-end-oob") (param "a" (list u8))
1949 (canon lift (core func $i3 "take-list") (memory $i3 "memory") (realloc (func $i3 "realloc")))
1950 )
1951 (func (export "take-string-end-oob") (param "a" string)
1952 (canon lift (core func $i3 "take-list") (memory $i3 "memory") (realloc (func $i3 "realloc")))
1953 )
1954 (func (export "take-many-end-oob") (type $t)
1955 (canon lift (core func $i3 "take-many") (memory $i3 "memory") (realloc (func $i3 "realloc")))
1956 )
1957
1958 (core module $m4
1959 (memory (export "memory") 1)
1960 (func (export "take-many") (param i32))
1961
1962 (global $cnt (mut i32) (i32.const 0))
1963 (func (export "realloc") (param i32 i32 i32 i32) (result i32)
1964 global.get $cnt
1965 if (result i32)
1966 i32.const 100000
1967 else
1968 i32.const 1
1969 global.set $cnt
1970 i32.const 0
1971 end
1972 )
1973 )
1974 (core instance $i4 (instantiate $m4))
1975
1976 (func (export "take-many-second-oob") (type $t)
1977 (canon lift (core func $i4 "take-many") (memory $i4 "memory") (realloc (func $i4 "realloc")))
1978 )
1979 )"#
1980 );
1981
1982 let engine = super::engine();
1983 let component = Component::new(&engine, component)?;
1984
1985 // This should fail when calling the allocator function for the argument
1986 let err = with_new_instance(&engine, &component, |store, instance| {
1987 instance
1988 .get_typed_func::<(&[u8],), ()>(&mut *store, "take-list-unreachable")?
1989 .call(store, (&[],))
1990 .unwrap_err()
1991 .downcast::<Trap>()
1992 })?;
1993 assert_eq!(err, Trap::UnreachableCodeReached);
1994
1995 // This should fail when calling the allocator function for the argument
1996 let err = with_new_instance(&engine, &component, |store, instance| {
1997 instance
1998 .get_typed_func::<(&str,), ()>(&mut *store, "take-string-unreachable")?
1999 .call(store, ("",))
2000 .unwrap_err()
2001 .downcast::<Trap>()
2002 })?;
2003 assert_eq!(err, Trap::UnreachableCodeReached);
2004
2005 // This should fail when calling the allocator function for the space
2006 // to store the arguments (before arguments are even lowered)
2007 let err = with_new_instance(&engine, &component, |store, instance| {
2008 instance
2009 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(
2010 &mut *store,
2011 "take-many-unreachable",
2012 )?
2013 .call(store, ("", "", "", "", "", "", "", "", "", ""))
2014 .unwrap_err()
2015 .downcast::<Trap>()
2016 })?;
2017 assert_eq!(err, Trap::UnreachableCodeReached);
2018
2019 // Assert that when the base pointer returned by malloc is out of bounds
2020 // that errors are reported as such. Both empty and lists with contents
2021 // should all be invalid here.
2022 //
2023 // FIXME(WebAssembly/component-model#32) confirm the semantics here are
2024 // what's desired.
2025 #[track_caller]
2026 fn assert_oob(err: &wasmtime::Error) {
2027 assert!(
2028 err.to_string()
2029 .contains("realloc return: beyond end of memory"),
2030 "{err:?}",
2031 );
2032 }
2033 let err = with_new_instance(&engine, &component, |store, instance| {
2034 instance
2035 .get_typed_func::<(&[u8],), ()>(&mut *store, "take-list-base-oob")?
2036 .call(store, (&[],))
2037 })
2038 .unwrap_err();
2039 assert_oob(&err);
2040 let err = with_new_instance(&engine, &component, |store, instance| {
2041 instance
2042 .get_typed_func::<(&[u8],), ()>(&mut *store, "take-list-base-oob")?
2043 .call(store, (&[1],))
2044 })
2045 .unwrap_err();
2046 assert_oob(&err);
2047 let err = with_new_instance(&engine, &component, |store, instance| {
2048 instance
2049 .get_typed_func::<(&str,), ()>(&mut *store, "take-string-base-oob")?
2050 .call(store, ("",))
2051 })
2052 .unwrap_err();
2053 assert_oob(&err);
2054 let err = with_new_instance(&engine, &component, |store, instance| {
2055 instance
2056 .get_typed_func::<(&str,), ()>(&mut *store, "take-string-base-oob")?
2057 .call(store, ("x",))
2058 })
2059 .unwrap_err();
2060 assert_oob(&err);
2061 let err = with_new_instance(&engine, &component, |store, instance| {
2062 instance
2063 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(
2064 &mut *store,
2065 "take-many-base-oob",
2066 )?
2067 .call(store, ("", "", "", "", "", "", "", "", "", ""))
2068 })
2069 .unwrap_err();
2070 assert_oob(&err);
2071
2072 // Test here that when the returned pointer from malloc is one byte from the
2073 // end of memory that empty things are fine, but larger things are not.
2074
2075 with_new_instance(&engine, &component, |store, instance| {
2076 instance
2077 .get_typed_func::<(&[u8],), ()>(&mut *store, "take-list-end-oob")?
2078 .call(store, (&[],))
2079 })?;
2080 with_new_instance(&engine, &component, |store, instance| {
2081 instance
2082 .get_typed_func::<(&[u8],), ()>(&mut *store, "take-list-end-oob")?
2083 .call(store, (&[1, 2, 3, 4],))
2084 })?;
2085 let err = with_new_instance(&engine, &component, |store, instance| {
2086 instance
2087 .get_typed_func::<(&[u8],), ()>(&mut *store, "take-list-end-oob")?
2088 .call(store, (&[1, 2, 3, 4, 5],))
2089 })
2090 .unwrap_err();
2091 assert_oob(&err);
2092 with_new_instance(&engine, &component, |store, instance| {
2093 instance
2094 .get_typed_func::<(&str,), ()>(&mut *store, "take-string-end-oob")?
2095 .call(store, ("",))
2096 })?;
2097 with_new_instance(&engine, &component, |store, instance| {
2098 instance
2099 .get_typed_func::<(&str,), ()>(&mut *store, "take-string-end-oob")?
2100 .call(store, ("abcd",))
2101 })?;
2102 let err = with_new_instance(&engine, &component, |store, instance| {
2103 instance
2104 .get_typed_func::<(&str,), ()>(&mut *store, "take-string-end-oob")?
2105 .call(store, ("abcde",))
2106 })
2107 .unwrap_err();
2108 assert_oob(&err);
2109 let err = with_new_instance(&engine, &component, |store, instance| {
2110 instance
2111 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(
2112 &mut *store,
2113 "take-many-end-oob",
2114 )?
2115 .call(store, ("", "", "", "", "", "", "", "", "", ""))
2116 })
2117 .unwrap_err();
2118 assert_oob(&err);
2119
2120 // For this function the first allocation, the space to store all the
2121 // arguments, is in-bounds but then all further allocations, such as for
2122 // each individual string, are all out of bounds.
2123 let err = with_new_instance(&engine, &component, |store, instance| {
2124 instance
2125 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(
2126 &mut *store,
2127 "take-many-second-oob",
2128 )?
2129 .call(store, ("", "", "", "", "", "", "", "", "", ""))
2130 })
2131 .unwrap_err();
2132 assert_oob(&err);
2133 let err = with_new_instance(&engine, &component, |store, instance| {
2134 instance
2135 .get_typed_func::<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str), ()>(
2136 &mut *store,
2137 "take-many-second-oob",
2138 )?
2139 .call(store, ("", "", "", "", "", "", "", "", "", "x"))
2140 })
2141 .unwrap_err();
2142 assert_oob(&err);
2143 Ok(())
2144 }
2145
2146 #[test]
char_bool_memory() -> Result<()>2147 fn char_bool_memory() -> Result<()> {
2148 let component = format!(
2149 r#"(component
2150 (core module $m
2151 (memory (export "memory") 1)
2152 (func (export "ret-tuple") (param i32 i32) (result i32)
2153 (local $base i32)
2154
2155 ;; Allocate space for the return
2156 (local.set $base
2157 (call $realloc
2158 (i32.const 0)
2159 (i32.const 0)
2160 (i32.const 4)
2161 (i32.const 8)))
2162
2163 ;; store the boolean
2164 (i32.store offset=0
2165 (local.get $base)
2166 (local.get 0))
2167
2168 ;; store the char
2169 (i32.store offset=4
2170 (local.get $base)
2171 (local.get 1))
2172
2173 (local.get $base)
2174 )
2175
2176 {REALLOC_AND_FREE}
2177 )
2178 (core instance $i (instantiate $m))
2179
2180 (func (export "ret-tuple") (param "a" u32) (param "b" u32) (result (tuple bool char))
2181 (canon lift (core func $i "ret-tuple")
2182 (memory $i "memory")
2183 (realloc (func $i "realloc")))
2184 )
2185 )"#
2186 );
2187
2188 let engine = super::engine();
2189 let component = Component::new(&engine, component)?;
2190 let mut store = Store::new(&engine, ());
2191 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
2192 let func = instance.get_typed_func::<(u32, u32), ((bool, char),)>(&mut store, "ret-tuple")?;
2193
2194 let (ret,) = func.call(&mut store, (0, 'a' as u32))?;
2195 assert_eq!(ret, (false, 'a'));
2196
2197 let (ret,) = func.call(&mut store, (1, '' as u32))?;
2198 assert_eq!(ret, (true, ''));
2199
2200 let (ret,) = func.call(&mut store, (2, 'a' as u32))?;
2201 assert_eq!(ret, (true, 'a'));
2202
2203 assert!(func.call(&mut store, (0, 0xd800)).is_err());
2204
2205 Ok(())
2206 }
2207
2208 #[test]
string_list_oob() -> Result<()>2209 fn string_list_oob() -> Result<()> {
2210 let component = format!(
2211 r#"(component
2212 (core module $m
2213 (memory (export "memory") 1)
2214 (func (export "ret-list") (result i32)
2215 (local $base i32)
2216
2217 ;; Allocate space for the return
2218 (local.set $base
2219 (call $realloc
2220 (i32.const 0)
2221 (i32.const 0)
2222 (i32.const 4)
2223 (i32.const 8)))
2224
2225 (i32.store offset=0
2226 (local.get $base)
2227 (i32.const 100000))
2228 (i32.store offset=4
2229 (local.get $base)
2230 (i32.const 1))
2231
2232 (local.get $base)
2233 )
2234
2235 {REALLOC_AND_FREE}
2236 )
2237 (core instance $i (instantiate $m))
2238
2239 (func (export "ret-list-u8") (result (list u8))
2240 (canon lift (core func $i "ret-list")
2241 (memory $i "memory")
2242 (realloc (func $i "realloc"))
2243 )
2244 )
2245 (func (export "ret-string") (result string)
2246 (canon lift (core func $i "ret-list")
2247 (memory $i "memory")
2248 (realloc (func $i "realloc"))
2249 )
2250 )
2251 )"#
2252 );
2253
2254 let engine = super::engine();
2255 let component = Component::new(&engine, component)?;
2256 let mut store = Store::new(&engine, ());
2257 let ret_list_u8 = Linker::new(&engine)
2258 .instantiate(&mut store, &component)?
2259 .get_typed_func::<(), (WasmList<u8>,)>(&mut store, "ret-list-u8")?;
2260 let ret_string = Linker::new(&engine)
2261 .instantiate(&mut store, &component)?
2262 .get_typed_func::<(), (WasmStr,)>(&mut store, "ret-string")?;
2263
2264 let err = ret_list_u8.call(&mut store, ()).err().unwrap();
2265 assert!(err.to_string().contains("out of bounds"), "{}", err);
2266
2267 let err = ret_string.call(&mut store, ()).err().unwrap();
2268 assert!(err.to_string().contains("out of bounds"), "{}", err);
2269
2270 Ok(())
2271 }
2272
2273 #[test]
tuples() -> Result<()>2274 fn tuples() -> Result<()> {
2275 let component = format!(
2276 r#"(component
2277 (core module $m
2278 (memory (export "memory") 1)
2279 (func (export "foo")
2280 (param i32 f64 i32)
2281 (result i32)
2282
2283 local.get 0
2284 i32.const 0
2285 i32.ne
2286 if unreachable end
2287
2288 local.get 1
2289 f64.const 1
2290 f64.ne
2291 if unreachable end
2292
2293 local.get 2
2294 i32.const 2
2295 i32.ne
2296 if unreachable end
2297
2298 i32.const 3
2299 )
2300 )
2301 (core instance $i (instantiate $m))
2302
2303 (func (export "foo")
2304 (param "a" (tuple s32 float64))
2305 (param "b" (tuple s8))
2306 (result (tuple u16))
2307 (canon lift (core func $i "foo"))
2308 )
2309 )"#
2310 );
2311
2312 let engine = super::engine();
2313 let component = Component::new(&engine, component)?;
2314 let mut store = Store::new(&engine, ());
2315 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
2316 let foo = instance.get_typed_func::<((i32, f64), (i8,)), ((u16,),)>(&mut store, "foo")?;
2317 assert_eq!(foo.call(&mut store, ((0, 1.0), (2,)))?, ((3,),));
2318
2319 Ok(())
2320 }
2321
2322 #[test]
option() -> Result<()>2323 fn option() -> Result<()> {
2324 let component = format!(
2325 r#"(component
2326 (core module $m
2327 (memory (export "memory") 1)
2328 (func (export "pass1") (param i32 i32) (result i32)
2329 (local $base i32)
2330 (local.set $base
2331 (call $realloc
2332 (i32.const 0)
2333 (i32.const 0)
2334 (i32.const 4)
2335 (i32.const 8)))
2336
2337 (i32.store offset=0
2338 (local.get $base)
2339 (local.get 0))
2340 (i32.store offset=4
2341 (local.get $base)
2342 (local.get 1))
2343
2344 (local.get $base)
2345 )
2346 (func (export "pass2") (param i32 i32 i32) (result i32)
2347 (local $base i32)
2348 (local.set $base
2349 (call $realloc
2350 (i32.const 0)
2351 (i32.const 0)
2352 (i32.const 4)
2353 (i32.const 12)))
2354
2355 (i32.store offset=0
2356 (local.get $base)
2357 (local.get 0))
2358 (i32.store offset=4
2359 (local.get $base)
2360 (local.get 1))
2361 (i32.store offset=8
2362 (local.get $base)
2363 (local.get 2))
2364
2365 (local.get $base)
2366 )
2367
2368 {REALLOC_AND_FREE}
2369 )
2370 (core instance $i (instantiate $m))
2371
2372 (func (export "option-u8-to-tuple") (param "a" (option u8)) (result (tuple u32 u32))
2373 (canon lift (core func $i "pass1") (memory $i "memory"))
2374 )
2375 (func (export "option-u32-to-tuple") (param "a" (option u32)) (result (tuple u32 u32))
2376 (canon lift (core func $i "pass1") (memory $i "memory"))
2377 )
2378 (func (export "option-string-to-tuple") (param "a" (option string)) (result (tuple u32 string))
2379 (canon lift
2380 (core func $i "pass2")
2381 (memory $i "memory")
2382 (realloc (func $i "realloc"))
2383 )
2384 )
2385 (func (export "to-option-u8") (param "a" u32) (param "b" u32) (result (option u8))
2386 (canon lift (core func $i "pass1") (memory $i "memory"))
2387 )
2388 (func (export "to-option-u32") (param "a" u32) (param "b" u32) (result (option u32))
2389 (canon lift
2390 (core func $i "pass1")
2391 (memory $i "memory")
2392 )
2393 )
2394 (func (export "to-option-string") (param "a" u32) (param "b" string) (result (option string))
2395 (canon lift
2396 (core func $i "pass2")
2397 (memory $i "memory")
2398 (realloc (func $i "realloc"))
2399 )
2400 )
2401 )"#
2402 );
2403
2404 let engine = super::engine();
2405 let component = Component::new(&engine, component)?;
2406 let mut store = Store::new(&engine, ());
2407 let linker = Linker::new(&engine);
2408 let instance = linker.instantiate(&mut store, &component)?;
2409
2410 let option_u8_to_tuple = instance
2411 .get_typed_func::<(Option<u8>,), ((u32, u32),)>(&mut store, "option-u8-to-tuple")?;
2412 assert_eq!(option_u8_to_tuple.call(&mut store, (None,))?, ((0, 0),));
2413 assert_eq!(option_u8_to_tuple.call(&mut store, (Some(0),))?, ((1, 0),));
2414 assert_eq!(
2415 option_u8_to_tuple.call(&mut store, (Some(100),))?,
2416 ((1, 100),)
2417 );
2418
2419 let option_u32_to_tuple = instance
2420 .get_typed_func::<(Option<u32>,), ((u32, u32),)>(&mut store, "option-u32-to-tuple")?;
2421 assert_eq!(option_u32_to_tuple.call(&mut store, (None,))?, ((0, 0),));
2422 assert_eq!(option_u32_to_tuple.call(&mut store, (Some(0),))?, ((1, 0),));
2423 assert_eq!(
2424 option_u32_to_tuple.call(&mut store, (Some(100),))?,
2425 ((1, 100),)
2426 );
2427
2428 let option_string_to_tuple = instance.get_typed_func::<(Option<&str>,), ((u32, WasmStr),)>(
2429 &mut store,
2430 "option-string-to-tuple",
2431 )?;
2432 let ((a, b),) = option_string_to_tuple.call(&mut store, (None,))?;
2433 assert_eq!(a, 0);
2434 assert_eq!(b.to_str(&store)?, "");
2435 let ((a, b),) = option_string_to_tuple.call(&mut store, (Some(""),))?;
2436 assert_eq!(a, 1);
2437 assert_eq!(b.to_str(&store)?, "");
2438 let ((a, b),) = option_string_to_tuple.call(&mut store, (Some("hello"),))?;
2439 assert_eq!(a, 1);
2440 assert_eq!(b.to_str(&store)?, "hello");
2441
2442 let instance = linker.instantiate(&mut store, &component)?;
2443 let to_option_u8 =
2444 instance.get_typed_func::<(u32, u32), (Option<u8>,)>(&mut store, "to-option-u8")?;
2445 assert_eq!(to_option_u8.call(&mut store, (0x00_00, 0))?, (None,));
2446 assert_eq!(to_option_u8.call(&mut store, (0x00_01, 0))?, (Some(0),));
2447 assert_eq!(to_option_u8.call(&mut store, (0xfd_01, 0))?, (Some(0xfd),));
2448 assert!(to_option_u8.call(&mut store, (0x00_02, 0)).is_err());
2449
2450 let instance = linker.instantiate(&mut store, &component)?;
2451 let to_option_u32 =
2452 instance.get_typed_func::<(u32, u32), (Option<u32>,)>(&mut store, "to-option-u32")?;
2453 assert_eq!(to_option_u32.call(&mut store, (0, 0))?, (None,));
2454 assert_eq!(to_option_u32.call(&mut store, (1, 0))?, (Some(0),));
2455 assert_eq!(
2456 to_option_u32.call(&mut store, (1, 0x1234fead))?,
2457 (Some(0x1234fead),)
2458 );
2459 assert!(to_option_u32.call(&mut store, (2, 0)).is_err());
2460
2461 let instance = linker.instantiate(&mut store, &component)?;
2462 let to_option_string = instance
2463 .get_typed_func::<(u32, &str), (Option<WasmStr>,)>(&mut store, "to-option-string")?;
2464 let ret = to_option_string.call(&mut store, (0, ""))?.0;
2465 assert!(ret.is_none());
2466 let ret = to_option_string.call(&mut store, (1, ""))?.0;
2467 assert_eq!(ret.unwrap().to_str(&store)?, "");
2468 let ret = to_option_string.call(&mut store, (1, "cheesecake"))?.0;
2469 assert_eq!(ret.unwrap().to_str(&store)?, "cheesecake");
2470 assert!(to_option_string.call(&mut store, (2, "")).is_err());
2471
2472 Ok(())
2473 }
2474
2475 #[test]
expected() -> Result<()>2476 fn expected() -> Result<()> {
2477 let component = format!(
2478 r#"(component
2479 (core module $m
2480 (memory (export "memory") 1)
2481 (func (export "pass0") (param i32) (result i32)
2482 local.get 0
2483 )
2484 (func (export "pass1") (param i32 i32) (result i32)
2485 (local $base i32)
2486 (local.set $base
2487 (call $realloc
2488 (i32.const 0)
2489 (i32.const 0)
2490 (i32.const 4)
2491 (i32.const 8)))
2492
2493 (i32.store offset=0
2494 (local.get $base)
2495 (local.get 0))
2496 (i32.store offset=4
2497 (local.get $base)
2498 (local.get 1))
2499
2500 (local.get $base)
2501 )
2502 (func (export "pass2") (param i32 i32 i32) (result i32)
2503 (local $base i32)
2504 (local.set $base
2505 (call $realloc
2506 (i32.const 0)
2507 (i32.const 0)
2508 (i32.const 4)
2509 (i32.const 12)))
2510
2511 (i32.store offset=0
2512 (local.get $base)
2513 (local.get 0))
2514 (i32.store offset=4
2515 (local.get $base)
2516 (local.get 1))
2517 (i32.store offset=8
2518 (local.get $base)
2519 (local.get 2))
2520
2521 (local.get $base)
2522 )
2523
2524 {REALLOC_AND_FREE}
2525 )
2526 (core instance $i (instantiate $m))
2527
2528 (func (export "take-expected-unit") (param "a" (result)) (result u32)
2529 (canon lift (core func $i "pass0"))
2530 )
2531 (func (export "take-expected-u8-f32") (param "a" (result u8 (error float32))) (result (tuple u32 u32))
2532 (canon lift (core func $i "pass1") (memory $i "memory"))
2533 )
2534 (type $list (list u8))
2535 (func (export "take-expected-string") (param "a" (result string (error $list))) (result (tuple u32 string))
2536 (canon lift
2537 (core func $i "pass2")
2538 (memory $i "memory")
2539 (realloc (func $i "realloc"))
2540 )
2541 )
2542 (func (export "to-expected-unit") (param "a" u32) (result (result))
2543 (canon lift (core func $i "pass0"))
2544 )
2545 (func (export "to-expected-s16-f32") (param "a" u32) (param "b" u32) (result (result s16 (error float32)))
2546 (canon lift
2547 (core func $i "pass1")
2548 (memory $i "memory")
2549 (realloc (func $i "realloc"))
2550 )
2551 )
2552 )"#
2553 );
2554
2555 let engine = super::engine();
2556 let component = Component::new(&engine, component)?;
2557 let mut store = Store::new(&engine, ());
2558 let linker = Linker::new(&engine);
2559 let instance = linker.instantiate(&mut store, &component)?;
2560 let take_expected_unit =
2561 instance.get_typed_func::<(Result<(), ()>,), (u32,)>(&mut store, "take-expected-unit")?;
2562 assert_eq!(take_expected_unit.call(&mut store, (Ok(()),))?, (0,));
2563 assert_eq!(take_expected_unit.call(&mut store, (Err(()),))?, (1,));
2564
2565 let take_expected_u8_f32 = instance
2566 .get_typed_func::<(Result<u8, f32>,), ((u32, u32),)>(&mut store, "take-expected-u8-f32")?;
2567 assert_eq!(take_expected_u8_f32.call(&mut store, (Ok(1),))?, ((0, 1),));
2568 assert_eq!(
2569 take_expected_u8_f32.call(&mut store, (Err(2.0),))?,
2570 ((1, 2.0f32.to_bits()),)
2571 );
2572
2573 let take_expected_string = instance
2574 .get_typed_func::<(Result<&str, &[u8]>,), ((u32, WasmStr),)>(
2575 &mut store,
2576 "take-expected-string",
2577 )?;
2578 let ((a, b),) = take_expected_string.call(&mut store, (Ok("hello"),))?;
2579 assert_eq!(a, 0);
2580 assert_eq!(b.to_str(&store)?, "hello");
2581 let ((a, b),) = take_expected_string.call(&mut store, (Err(b"goodbye"),))?;
2582 assert_eq!(a, 1);
2583 assert_eq!(b.to_str(&store)?, "goodbye");
2584
2585 let instance = linker.instantiate(&mut store, &component)?;
2586 let to_expected_unit =
2587 instance.get_typed_func::<(u32,), (Result<(), ()>,)>(&mut store, "to-expected-unit")?;
2588 assert_eq!(to_expected_unit.call(&mut store, (0,))?, (Ok(()),));
2589 assert_eq!(to_expected_unit.call(&mut store, (1,))?, (Err(()),));
2590 let err = to_expected_unit.call(&mut store, (2,)).unwrap_err();
2591 assert!(err.to_string().contains("invalid expected"), "{}", err);
2592
2593 let instance = linker.instantiate(&mut store, &component)?;
2594 let to_expected_s16_f32 = instance
2595 .get_typed_func::<(u32, u32), (Result<i16, f32>,)>(&mut store, "to-expected-s16-f32")?;
2596 assert_eq!(to_expected_s16_f32.call(&mut store, (0, 0))?, (Ok(0),));
2597 assert_eq!(to_expected_s16_f32.call(&mut store, (0, 100))?, (Ok(100),));
2598 assert_eq!(
2599 to_expected_s16_f32.call(&mut store, (1, 1.0f32.to_bits()))?,
2600 (Err(1.0),)
2601 );
2602 let ret = to_expected_s16_f32
2603 .call(&mut store, (1, CANON_32BIT_NAN | 1))?
2604 .0;
2605 assert_eq!(ret.unwrap_err().to_bits(), CANON_32BIT_NAN | 1);
2606 assert!(to_expected_s16_f32.call(&mut store, (2, 0)).is_err());
2607
2608 Ok(())
2609 }
2610
2611 #[test]
fancy_list() -> Result<()>2612 fn fancy_list() -> Result<()> {
2613 let component = format!(
2614 r#"(component
2615 (core module $m
2616 (memory (export "memory") 1)
2617 (func (export "take") (param i32 i32) (result i32)
2618 (local $base i32)
2619 (local.set $base
2620 (call $realloc
2621 (i32.const 0)
2622 (i32.const 0)
2623 (i32.const 4)
2624 (i32.const 16)))
2625
2626 (i32.store offset=0
2627 (local.get $base)
2628 (local.get 0))
2629 (i32.store offset=4
2630 (local.get $base)
2631 (local.get 1))
2632 (i32.store offset=8
2633 (local.get $base)
2634 (i32.const 0))
2635 (i32.store offset=12
2636 (local.get $base)
2637 (i32.mul
2638 (memory.size)
2639 (i32.const 65536)))
2640
2641 (local.get $base)
2642 )
2643
2644 {REALLOC_AND_FREE}
2645 )
2646 (core instance $i (instantiate $m))
2647
2648 (type $a (option u8))
2649 (type $b (result (error string)))
2650 (type $input (list (tuple $a $b)))
2651 (func (export "take")
2652 (param "a" $input)
2653 (result (tuple u32 u32 (list u8)))
2654 (canon lift
2655 (core func $i "take")
2656 (memory $i "memory")
2657 (realloc (func $i "realloc"))
2658 )
2659 )
2660 )"#
2661 );
2662
2663 let engine = super::engine();
2664 let component = Component::new(&engine, component)?;
2665 let mut store = Store::new(&engine, ());
2666 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
2667
2668 let func = instance
2669 .get_typed_func::<(&[(Option<u8>, Result<(), &str>)],), ((u32, u32, WasmList<u8>),)>(
2670 &mut store, "take",
2671 )?;
2672
2673 let input = [
2674 (None, Ok(())),
2675 (Some(2), Err("hello there")),
2676 (Some(200), Err("general kenobi")),
2677 ];
2678 let ((ptr, len, list),) = func.call(&mut store, (&input,))?;
2679 let memory = list.as_le_slice(&store);
2680 let ptr = usize::try_from(ptr).unwrap();
2681 let len = usize::try_from(len).unwrap();
2682 let mut array = &memory[ptr..][..len * 16];
2683
2684 for (a, b) in input.iter() {
2685 match a {
2686 Some(val) => {
2687 assert_eq!(*array.take_n::<2>(), [1, *val]);
2688 }
2689 None => {
2690 assert_eq!(*array.take_n::<1>(), [0]);
2691 array.skip::<1>();
2692 }
2693 }
2694 array.skip::<2>();
2695 match b {
2696 Ok(()) => {
2697 assert_eq!(*array.take_n::<1>(), [0]);
2698 array.skip::<11>();
2699 }
2700 Err(s) => {
2701 assert_eq!(*array.take_n::<1>(), [1]);
2702 array.skip::<3>();
2703 assert_eq!(array.ptr_len(memory, 1), s.as_bytes());
2704 }
2705 }
2706 }
2707 assert!(array.is_empty());
2708
2709 Ok(())
2710 }
2711
2712 trait SliceExt<'a> {
take_n<const N: usize>(&mut self) -> &'a [u8; N]2713 fn take_n<const N: usize>(&mut self) -> &'a [u8; N];
2714
skip<const N: usize>(&mut self)2715 fn skip<const N: usize>(&mut self) {
2716 self.take_n::<N>();
2717 }
2718
ptr_len<'b>(&mut self, all_memory: &'b [u8], size: usize) -> &'b [u8]2719 fn ptr_len<'b>(&mut self, all_memory: &'b [u8], size: usize) -> &'b [u8] {
2720 let ptr = u32::from_le_bytes(*self.take_n::<4>());
2721 let len = u32::from_le_bytes(*self.take_n::<4>());
2722 let ptr = usize::try_from(ptr).unwrap();
2723 let len = usize::try_from(len).unwrap();
2724 &all_memory[ptr..][..len * size]
2725 }
2726 }
2727
2728 impl<'a> SliceExt<'a> for &'a [u8] {
take_n<const N: usize>(&mut self) -> &'a [u8; N]2729 fn take_n<const N: usize>(&mut self) -> &'a [u8; N] {
2730 let (a, b) = self.split_at(N);
2731 *self = b;
2732 a.try_into().unwrap()
2733 }
2734 }
2735
2736 #[test]
invalid_alignment() -> Result<()>2737 fn invalid_alignment() -> Result<()> {
2738 let component = format!(
2739 r#"(component
2740 (core module $m
2741 (memory (export "memory") 1)
2742 (func (export "realloc") (param i32 i32 i32 i32) (result i32)
2743 i32.const 1)
2744
2745 (func (export "take-i32") (param i32))
2746 (func (export "ret-1") (result i32) i32.const 1)
2747 (func (export "ret-unaligned-list") (result i32)
2748 (i32.store offset=0 (i32.const 8) (i32.const 1))
2749 (i32.store offset=4 (i32.const 8) (i32.const 1))
2750 i32.const 8)
2751 )
2752 (core instance $i (instantiate $m))
2753
2754 (func (export "many-params")
2755 (param "s1" string) (param "s2" string) (param "s3" string) (param "s4" string)
2756 (param "s5" string) (param "s6" string) (param "s7" string) (param "s8" string)
2757 (param "s9" string) (param "s10" string) (param "s11" string) (param "s12" string)
2758 (canon lift
2759 (core func $i "take-i32")
2760 (memory $i "memory")
2761 (realloc (func $i "realloc"))
2762 )
2763 )
2764 (func (export "string-ret") (result string)
2765 (canon lift
2766 (core func $i "ret-1")
2767 (memory $i "memory")
2768 (realloc (func $i "realloc"))
2769 )
2770 )
2771 (func (export "list-u32-ret") (result (list u32))
2772 (canon lift
2773 (core func $i "ret-unaligned-list")
2774 (memory $i "memory")
2775 (realloc (func $i "realloc"))
2776 )
2777 )
2778 )"#
2779 );
2780
2781 let engine = super::engine();
2782 let component = Component::new(&engine, component)?;
2783 let mut store = Store::new(&engine, ());
2784 let instance = |store: &mut Store<()>| Linker::new(&engine).instantiate(store, &component);
2785
2786 let err = instance(&mut store)?
2787 .get_typed_func::<(
2788 &str,
2789 &str,
2790 &str,
2791 &str,
2792 &str,
2793 &str,
2794 &str,
2795 &str,
2796 &str,
2797 &str,
2798 &str,
2799 &str,
2800 ), ()>(&mut store, "many-params")?
2801 .call(&mut store, ("", "", "", "", "", "", "", "", "", "", "", ""))
2802 .unwrap_err();
2803 assert!(
2804 err.to_string()
2805 .contains("realloc return: result not aligned"),
2806 "{}",
2807 err
2808 );
2809
2810 let err = instance(&mut store)?
2811 .get_typed_func::<(), (WasmStr,)>(&mut store, "string-ret")?
2812 .call(&mut store, ())
2813 .err()
2814 .unwrap();
2815 assert!(
2816 err.to_string().contains("return pointer not aligned"),
2817 "{}",
2818 err
2819 );
2820
2821 let err = instance(&mut store)?
2822 .get_typed_func::<(), (WasmList<u32>,)>(&mut store, "list-u32-ret")?
2823 .call(&mut store, ())
2824 .err()
2825 .unwrap();
2826 assert!(
2827 err.to_string().contains("list pointer is not aligned"),
2828 "{}",
2829 err
2830 );
2831
2832 Ok(())
2833 }
2834
2835 #[test]
drop_component_still_works() -> Result<()>2836 fn drop_component_still_works() -> Result<()> {
2837 let component = r#"
2838 (component
2839 (import "f" (func $f))
2840
2841 (core func $f_lower
2842 (canon lower (func $f))
2843 )
2844 (core module $m
2845 (import "" "" (func $f))
2846
2847 (func $f2
2848 call $f
2849 call $f
2850 )
2851
2852 (export "f" (func $f2))
2853 )
2854 (core instance $i (instantiate $m
2855 (with "" (instance
2856 (export "" (func $f_lower))
2857 ))
2858 ))
2859 (func (export "g")
2860 (canon lift
2861 (core func $i "f")
2862 )
2863 )
2864 )
2865 "#;
2866
2867 let (mut store, instance) = {
2868 let engine = super::engine();
2869 let component = Component::new(&engine, component)?;
2870 let mut store = Store::new(&engine, 0);
2871 let mut linker = Linker::new(&engine);
2872 linker.root().func_wrap(
2873 "f",
2874 |mut store: StoreContextMut<'_, u32>, _: ()| -> Result<()> {
2875 *store.data_mut() += 1;
2876 Ok(())
2877 },
2878 )?;
2879 let instance = linker.instantiate(&mut store, &component)?;
2880 (store, instance)
2881 };
2882
2883 let f = instance.get_typed_func::<(), ()>(&mut store, "g")?;
2884 assert_eq!(*store.data(), 0);
2885 f.call(&mut store, ())?;
2886 assert_eq!(*store.data(), 2);
2887
2888 Ok(())
2889 }
2890
2891 #[test]
raw_slice_of_various_types() -> Result<()>2892 fn raw_slice_of_various_types() -> Result<()> {
2893 let component = r#"
2894 (component
2895 (core module $m
2896 (memory (export "memory") 1)
2897
2898 (func (export "list8") (result i32)
2899 (call $setup_list (i32.const 16))
2900 )
2901 (func (export "list16") (result i32)
2902 (call $setup_list (i32.const 8))
2903 )
2904 (func (export "list32") (result i32)
2905 (call $setup_list (i32.const 4))
2906 )
2907 (func (export "list64") (result i32)
2908 (call $setup_list (i32.const 2))
2909 )
2910
2911 (func $setup_list (param i32) (result i32)
2912 (i32.store offset=0 (i32.const 100) (i32.const 8))
2913 (i32.store offset=4 (i32.const 100) (local.get 0))
2914 i32.const 100
2915 )
2916
2917 (data (i32.const 8) "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f")
2918 )
2919 (core instance $i (instantiate $m))
2920 (func (export "list-u8") (result (list u8))
2921 (canon lift (core func $i "list8") (memory $i "memory"))
2922 )
2923 (func (export "list-i8") (result (list s8))
2924 (canon lift (core func $i "list8") (memory $i "memory"))
2925 )
2926 (func (export "list-u16") (result (list u16))
2927 (canon lift (core func $i "list16") (memory $i "memory"))
2928 )
2929 (func (export "list-i16") (result (list s16))
2930 (canon lift (core func $i "list16") (memory $i "memory"))
2931 )
2932 (func (export "list-u32") (result (list u32))
2933 (canon lift (core func $i "list32") (memory $i "memory"))
2934 )
2935 (func (export "list-i32") (result (list s32))
2936 (canon lift (core func $i "list32") (memory $i "memory"))
2937 )
2938 (func (export "list-u64") (result (list u64))
2939 (canon lift (core func $i "list64") (memory $i "memory"))
2940 )
2941 (func (export "list-i64") (result (list s64))
2942 (canon lift (core func $i "list64") (memory $i "memory"))
2943 )
2944 )
2945 "#;
2946
2947 let (mut store, instance) = {
2948 let engine = super::engine();
2949 let component = Component::new(&engine, component)?;
2950 let mut store = Store::new(&engine, ());
2951 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
2952 (store, instance)
2953 };
2954
2955 let list = instance
2956 .get_typed_func::<(), (WasmList<u8>,)>(&mut store, "list-u8")?
2957 .call(&mut store, ())?
2958 .0;
2959 assert_eq!(
2960 list.as_le_slice(&store),
2961 [
2962 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
2963 0x0e, 0x0f,
2964 ]
2965 );
2966 let list = instance
2967 .get_typed_func::<(), (WasmList<i8>,)>(&mut store, "list-i8")?
2968 .call(&mut store, ())?
2969 .0;
2970 assert_eq!(
2971 list.as_le_slice(&store),
2972 [
2973 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
2974 0x0e, 0x0f,
2975 ]
2976 );
2977
2978 let list = instance
2979 .get_typed_func::<(), (WasmList<u16>,)>(&mut store, "list-u16")?
2980 .call(&mut store, ())?
2981 .0;
2982 assert_eq!(
2983 list.as_le_slice(&store),
2984 [
2985 u16::to_le(0x01_00),
2986 u16::to_le(0x03_02),
2987 u16::to_le(0x05_04),
2988 u16::to_le(0x07_06),
2989 u16::to_le(0x09_08),
2990 u16::to_le(0x0b_0a),
2991 u16::to_le(0x0d_0c),
2992 u16::to_le(0x0f_0e),
2993 ]
2994 );
2995 let list = instance
2996 .get_typed_func::<(), (WasmList<i16>,)>(&mut store, "list-i16")?
2997 .call(&mut store, ())?
2998 .0;
2999 assert_eq!(
3000 list.as_le_slice(&store),
3001 [
3002 i16::to_le(0x01_00),
3003 i16::to_le(0x03_02),
3004 i16::to_le(0x05_04),
3005 i16::to_le(0x07_06),
3006 i16::to_le(0x09_08),
3007 i16::to_le(0x0b_0a),
3008 i16::to_le(0x0d_0c),
3009 i16::to_le(0x0f_0e),
3010 ]
3011 );
3012 let list = instance
3013 .get_typed_func::<(), (WasmList<u32>,)>(&mut store, "list-u32")?
3014 .call(&mut store, ())?
3015 .0;
3016 assert_eq!(
3017 list.as_le_slice(&store),
3018 [
3019 u32::to_le(0x03_02_01_00),
3020 u32::to_le(0x07_06_05_04),
3021 u32::to_le(0x0b_0a_09_08),
3022 u32::to_le(0x0f_0e_0d_0c),
3023 ]
3024 );
3025 let list = instance
3026 .get_typed_func::<(), (WasmList<i32>,)>(&mut store, "list-i32")?
3027 .call(&mut store, ())?
3028 .0;
3029 assert_eq!(
3030 list.as_le_slice(&store),
3031 [
3032 i32::to_le(0x03_02_01_00),
3033 i32::to_le(0x07_06_05_04),
3034 i32::to_le(0x0b_0a_09_08),
3035 i32::to_le(0x0f_0e_0d_0c),
3036 ]
3037 );
3038 let list = instance
3039 .get_typed_func::<(), (WasmList<u64>,)>(&mut store, "list-u64")?
3040 .call(&mut store, ())?
3041 .0;
3042 assert_eq!(
3043 list.as_le_slice(&store),
3044 [
3045 u64::to_le(0x07_06_05_04_03_02_01_00),
3046 u64::to_le(0x0f_0e_0d_0c_0b_0a_09_08),
3047 ]
3048 );
3049 let list = instance
3050 .get_typed_func::<(), (WasmList<i64>,)>(&mut store, "list-i64")?
3051 .call(&mut store, ())?
3052 .0;
3053 assert_eq!(
3054 list.as_le_slice(&store),
3055 [
3056 i64::to_le(0x07_06_05_04_03_02_01_00),
3057 i64::to_le(0x0f_0e_0d_0c_0b_0a_09_08),
3058 ]
3059 );
3060
3061 Ok(())
3062 }
3063
3064 #[test]
lower_then_lift() -> Result<()>3065 fn lower_then_lift() -> Result<()> {
3066 // First test simple integers when the import/export ABI happen to line up
3067 let component = r#"
3068 (component $c
3069 (import "f" (func $f (result u32)))
3070
3071 (core func $f_lower
3072 (canon lower (func $f))
3073 )
3074 (func $f2 (result s32)
3075 (canon lift (core func $f_lower))
3076 )
3077 (export "f2" (func $f2))
3078 )
3079 "#;
3080
3081 let engine = super::engine();
3082 let component = Component::new(&engine, component)?;
3083 let mut store = Store::new(&engine, ());
3084 let mut linker = Linker::new(&engine);
3085 linker.root().func_wrap("f", |_, _: ()| Ok((2u32,)))?;
3086 let instance = linker.instantiate(&mut store, &component)?;
3087
3088 let f = instance.get_typed_func::<(), (i32,)>(&mut store, "f2")?;
3089 assert_eq!(f.call(&mut store, ())?, (2,));
3090
3091 // First test strings when the import/export ABI happen to line up
3092 let component = format!(
3093 r#"
3094 (component $c
3095 (import "s" (func $f (param "a" string)))
3096
3097 (core module $libc
3098 (memory (export "memory") 1)
3099 {REALLOC_AND_FREE}
3100 )
3101 (core instance $libc (instantiate $libc))
3102
3103 (core func $f_lower
3104 (canon lower (func $f) (memory $libc "memory"))
3105 )
3106 (func $f2 (param "a" string)
3107 (canon lift (core func $f_lower)
3108 (memory $libc "memory")
3109 (realloc (func $libc "realloc"))
3110 )
3111 )
3112 (export "f" (func $f2))
3113 )
3114 "#
3115 );
3116
3117 let component = Component::new(&engine, component)?;
3118 let mut store = Store::new(&engine, ());
3119 linker
3120 .root()
3121 .func_wrap("s", |store: StoreContextMut<'_, ()>, (x,): (WasmStr,)| {
3122 assert_eq!(x.to_str(&store)?, "hello");
3123 Ok(())
3124 })?;
3125 let instance = linker.instantiate(&mut store, &component)?;
3126
3127 let f = instance.get_typed_func::<(&str,), ()>(&mut store, "f")?;
3128 f.call(&mut store, ("hello",))?;
3129
3130 // Next test "type punning" where return values are reinterpreted just
3131 // because the return ABI happens to line up.
3132 let component = format!(
3133 r#"
3134 (component $c
3135 (import "s2" (func $f (param "a" string) (result u32)))
3136
3137 (core module $libc
3138 (memory (export "memory") 1)
3139 {REALLOC_AND_FREE}
3140 )
3141 (core instance $libc (instantiate $libc))
3142
3143 (core func $f_lower
3144 (canon lower (func $f) (memory $libc "memory"))
3145 )
3146 (func $f2 (param "a" string) (result string)
3147 (canon lift (core func $f_lower)
3148 (memory $libc "memory")
3149 (realloc (func $libc "realloc"))
3150 )
3151 )
3152 (export "f" (func $f2))
3153 )
3154 "#
3155 );
3156
3157 let component = Component::new(&engine, component)?;
3158 let mut store = Store::new(&engine, ());
3159 linker
3160 .root()
3161 .func_wrap("s2", |store: StoreContextMut<'_, ()>, (x,): (WasmStr,)| {
3162 assert_eq!(x.to_str(&store)?, "hello");
3163 Ok((u32::MAX,))
3164 })?;
3165 let instance = linker.instantiate(&mut store, &component)?;
3166
3167 let f = instance.get_typed_func::<(&str,), (WasmStr,)>(&mut store, "f")?;
3168 let err = f.call(&mut store, ("hello",)).err().unwrap();
3169 assert!(
3170 err.to_string().contains("return pointer not aligned"),
3171 "{}",
3172 err
3173 );
3174
3175 Ok(())
3176 }
3177
3178 #[test]
errors_that_poison_instance() -> Result<()>3179 fn errors_that_poison_instance() -> Result<()> {
3180 let component = format!(
3181 r#"
3182 (component $c
3183 (core module $m1
3184 (func (export "f1") unreachable)
3185 (func (export "f2"))
3186 )
3187 (core instance $m1 (instantiate $m1))
3188 (func (export "f1") (canon lift (core func $m1 "f1")))
3189 (func (export "f2") (canon lift (core func $m1 "f2")))
3190
3191 (core module $m2
3192 (func (export "f") (param i32 i32))
3193 (func (export "r") (param i32 i32 i32 i32) (result i32) unreachable)
3194 (memory (export "m") 1)
3195 )
3196 (core instance $m2 (instantiate $m2))
3197 (func (export "f3") (param "a" string)
3198 (canon lift (core func $m2 "f") (realloc (func $m2 "r")) (memory $m2 "m"))
3199 )
3200
3201 (core module $m3
3202 (func (export "f") (result i32) i32.const 1)
3203 (memory (export "m") 1)
3204 )
3205 (core instance $m3 (instantiate $m3))
3206 (func (export "f4") (result string)
3207 (canon lift (core func $m3 "f") (memory $m3 "m"))
3208 )
3209 )
3210 "#
3211 );
3212
3213 let engine = super::engine();
3214 let component = Component::new(&engine, component)?;
3215 let linker = Linker::new(&engine);
3216
3217 {
3218 let mut store = Store::new(&engine, ());
3219 let instance = linker.instantiate(&mut store, &component)?;
3220 let f1 = instance.get_typed_func::<(), ()>(&mut store, "f1")?;
3221 let f2 = instance.get_typed_func::<(), ()>(&mut store, "f2")?;
3222 assert_unreachable(f1.call(&mut store, ()));
3223 assert_poisoned(f1.call(&mut store, ()));
3224 assert_poisoned(f2.call(&mut store, ()));
3225 }
3226
3227 {
3228 let mut store = Store::new(&engine, ());
3229 let instance = linker.instantiate(&mut store, &component)?;
3230 let f3 = instance.get_typed_func::<(&str,), ()>(&mut store, "f3")?;
3231 assert_unreachable(f3.call(&mut store, ("x",)));
3232 assert_poisoned(f3.call(&mut store, ("x",)));
3233
3234 // Since we actually poison the store, even an unrelated instance will
3235 // be considered poisoned:
3236 let instance = linker.instantiate(&mut store, &component)?;
3237 let f3 = instance.get_typed_func::<(&str,), ()>(&mut store, "f3")?;
3238 assert_poisoned(f3.call(&mut store, ("x",)));
3239 }
3240
3241 {
3242 let mut store = Store::new(&engine, ());
3243 let instance = linker.instantiate(&mut store, &component)?;
3244 let f4 = instance.get_typed_func::<(), (WasmStr,)>(&mut store, "f4")?;
3245 assert!(f4.call(&mut store, ()).is_err());
3246 assert_poisoned(f4.call(&mut store, ()));
3247 }
3248
3249 return Ok(());
3250
3251 #[track_caller]
3252 fn assert_unreachable<T>(err: Result<T>) {
3253 let err = match err {
3254 Ok(_) => panic!("expected an error"),
3255 Err(e) => e,
3256 };
3257 assert_eq!(
3258 err.downcast::<Trap>().unwrap(),
3259 Trap::UnreachableCodeReached
3260 );
3261 }
3262
3263 #[track_caller]
3264 fn assert_poisoned<T>(err: Result<T>) {
3265 let err = match err {
3266 Ok(_) => panic!("expected an error"),
3267 Err(e) => e,
3268 };
3269 assert_eq!(
3270 err.downcast_ref::<Trap>(),
3271 Some(&Trap::CannotEnterComponent),
3272 "{err}",
3273 );
3274 }
3275 }
3276
3277 #[test]
run_export_with_internal_adapter() -> Result<()>3278 fn run_export_with_internal_adapter() -> Result<()> {
3279 let component = r#"
3280 (component
3281 (type $t (func (param "a" u32) (result u32)))
3282 (component $a
3283 (core module $m
3284 (func (export "add-five") (param i32) (result i32)
3285 local.get 0
3286 i32.const 5
3287 i32.add)
3288 )
3289 (core instance $m (instantiate $m))
3290 (func (export "add-five") (type $t) (canon lift (core func $m "add-five")))
3291 )
3292 (component $b
3293 (import "interface-v1" (instance $i
3294 (export "add-five" (func (type $t)))))
3295 (core module $m
3296 (func $add-five (import "interface-0.1.0" "add-five") (param i32) (result i32))
3297 (func) ;; causes index out of bounds
3298 (func (export "run") (result i32) i32.const 0 call $add-five)
3299 )
3300 (core func $add-five (canon lower (func $i "add-five")))
3301 (core instance $i (instantiate 0
3302 (with "interface-0.1.0" (instance
3303 (export "add-five" (func $add-five))
3304 ))
3305 ))
3306 (func (result u32) (canon lift (core func $i "run")))
3307 (export "run" (func 1))
3308 )
3309 (instance $a (instantiate $a))
3310 (instance $b (instantiate $b (with "interface-v1" (instance $a))))
3311 (export "run" (func $b "run"))
3312 )
3313 "#;
3314 let engine = super::engine();
3315 let component = Component::new(&engine, component)?;
3316 let mut store = Store::new(&engine, ());
3317 let linker = Linker::new(&engine);
3318 let instance = linker.instantiate(&mut store, &component)?;
3319 let run = instance.get_typed_func::<(), (u32,)>(&mut store, "run")?;
3320 assert_eq!(run.call(&mut store, ())?, (5,));
3321 Ok(())
3322 }
3323
3324 enum RecurseKind {
3325 AThenA,
3326 AThenB,
3327 AThenBThenA,
3328 }
3329
3330 #[test]
recurse() -> Result<()>3331 fn recurse() -> Result<()> {
3332 test_recurse(RecurseKind::AThenB)
3333 }
3334
3335 #[test]
recurse_trap() -> Result<()>3336 fn recurse_trap() -> Result<()> {
3337 let error = test_recurse(RecurseKind::AThenA).unwrap_err();
3338
3339 assert_eq!(error.downcast::<Trap>()?, Trap::CannotEnterComponent);
3340
3341 Ok(())
3342 }
3343
3344 #[test]
recurse_more_trap() -> Result<()>3345 fn recurse_more_trap() -> Result<()> {
3346 let error = test_recurse(RecurseKind::AThenBThenA).unwrap_err();
3347
3348 assert_eq!(error.downcast::<Trap>()?, Trap::CannotEnterComponent);
3349
3350 Ok(())
3351 }
3352
test_recurse(kind: RecurseKind) -> Result<()>3353 fn test_recurse(kind: RecurseKind) -> Result<()> {
3354 #[derive(Default)]
3355 struct Ctx {
3356 instances: Vec<Arc<Instance>>,
3357 }
3358
3359 let component = r#"
3360 (component
3361 (import "import" (func $import))
3362 (core func $import (canon lower (func $import)))
3363 (core module $m
3364 (func $import (import "" "import"))
3365 (func (export "export") call $import)
3366 )
3367 (core instance $m (instantiate $m (with "" (instance
3368 (export "import" (func $import))
3369 ))))
3370 (func (export "export") (canon lift (core func $m "export")))
3371 )
3372 "#;
3373 let engine = super::engine();
3374 let component = Component::new(&engine, component)?;
3375 let mut store = Store::new(&engine, Ctx::default());
3376 let mut linker = Linker::<Ctx>::new(&engine);
3377 linker.root().func_wrap("import", |mut store, (): ()| {
3378 if let Some(instance) = store.data_mut().instances.pop() {
3379 let run = instance.get_typed_func::<(), ()>(&mut store, "export")?;
3380 run.call(&mut store, ())?;
3381 store.data_mut().instances.push(instance);
3382 }
3383 Ok(())
3384 })?;
3385 let instance = Arc::new(linker.instantiate(&mut store, &component)?);
3386 let instance = match kind {
3387 RecurseKind::AThenA => {
3388 store.data_mut().instances.push(instance.clone());
3389 instance
3390 }
3391 RecurseKind::AThenB => {
3392 let other = Arc::new(linker.instantiate(&mut store, &component)?);
3393 store.data_mut().instances.push(other);
3394 instance
3395 }
3396 RecurseKind::AThenBThenA => {
3397 store.data_mut().instances.push(instance.clone());
3398 let other = Arc::new(linker.instantiate(&mut store, &component)?);
3399 store.data_mut().instances.push(other);
3400 instance
3401 }
3402 };
3403
3404 let export = instance.get_typed_func::<(), ()>(&mut store, "export")?;
3405 export.call(&mut store, ())?;
3406 Ok(())
3407 }
3408
3409 #[tokio::test]
thread_index_via_instantiation_sync() -> Result<()>3410 async fn thread_index_via_instantiation_sync() -> Result<()> {
3411 thread_index_via_instantiation(ApiStyle::Sync).await
3412 }
3413
3414 #[tokio::test]
thread_index_via_instantiation_async() -> Result<()>3415 async fn thread_index_via_instantiation_async() -> Result<()> {
3416 thread_index_via_instantiation(ApiStyle::Async).await
3417 }
3418
thread_index_via_instantiation(style: ApiStyle) -> Result<()>3419 async fn thread_index_via_instantiation(style: ApiStyle) -> Result<()> {
3420 let component = r#"
3421 (component
3422 (core module $m
3423 (import "" "thread.index" (func $thread-index (result i32)))
3424 (func $start
3425 (if (i32.eqz (call $thread-index)) (then unreachable))
3426 )
3427 (start $start)
3428 )
3429 (core func $thread-index (canon thread.index))
3430 (core instance $m (instantiate $m (with "" (instance
3431 (export "thread.index" (func $thread-index))
3432 ))))
3433 )
3434 "#;
3435 let engine = Engine::new(&style.config())?;
3436 let component = Component::new(&engine, component)?;
3437 let mut store = Store::new(&engine, ());
3438 let linker = Linker::new(&engine);
3439 style.instantiate(&mut store, &linker, &component).await?;
3440 Ok(())
3441 }
3442
3443 #[tokio::test]
thread_index_via_call_sync() -> Result<()>3444 async fn thread_index_via_call_sync() -> Result<()> {
3445 thread_index_via_call(ApiStyle::Sync).await
3446 }
3447
3448 #[tokio::test]
thread_index_via_call_async() -> Result<()>3449 async fn thread_index_via_call_async() -> Result<()> {
3450 thread_index_via_call(ApiStyle::Async).await
3451 }
3452
3453 #[tokio::test]
thread_index_via_call_concurrent() -> Result<()>3454 async fn thread_index_via_call_concurrent() -> Result<()> {
3455 thread_index_via_call(ApiStyle::Concurrent).await
3456 }
3457
thread_index_via_call(style: ApiStyle) -> Result<()>3458 async fn thread_index_via_call(style: ApiStyle) -> Result<()> {
3459 let component = r#"
3460 (component
3461 (core module $m
3462 (import "" "thread.index" (func $thread-index (result i32)))
3463 (func (export "run")
3464 (if (i32.eqz (call $thread-index)) (then unreachable))
3465 )
3466 )
3467 (core func $thread-index (canon thread.index))
3468 (core instance $m (instantiate $m (with "" (instance
3469 (export "thread.index" (func $thread-index))
3470 ))))
3471 (func (export "run") (canon lift (core func $m "run")))
3472 )
3473 "#;
3474 let engine = Engine::new(&style.config())?;
3475 let component = Component::new(&engine, component)?;
3476 let mut store = Store::new(&engine, ());
3477 let linker = Linker::new(&engine);
3478 let instance = style.instantiate(&mut store, &linker, &component).await?;
3479 let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
3480 style.call(&mut store, run, ()).await?;
3481 Ok(())
3482 }
3483
3484 #[tokio::test]
thread_index_via_post_return_sync() -> Result<()>3485 async fn thread_index_via_post_return_sync() -> Result<()> {
3486 thread_index_via_post_return(ApiStyle::Sync).await
3487 }
3488
3489 #[tokio::test]
thread_index_via_post_return_async() -> Result<()>3490 async fn thread_index_via_post_return_async() -> Result<()> {
3491 thread_index_via_post_return(ApiStyle::Async).await
3492 }
3493
3494 #[tokio::test]
thread_index_via_post_return_concurrent() -> Result<()>3495 async fn thread_index_via_post_return_concurrent() -> Result<()> {
3496 thread_index_via_post_return(ApiStyle::Concurrent).await
3497 }
3498
thread_index_via_post_return(style: ApiStyle) -> Result<()>3499 async fn thread_index_via_post_return(style: ApiStyle) -> Result<()> {
3500 let component = r#"
3501 (component
3502 (core module $m
3503 (import "" "thread.index" (func $thread-index (result i32)))
3504 (global $index (mut i32) (i32.const 0))
3505 (func (export "run")
3506 (global.set $index (call $thread-index))
3507 (if (i32.eqz (global.get $index)) (then unreachable))
3508 )
3509 (func (export "run-post-return")
3510 (local $index i32)
3511 (local.set $index (call $thread-index))
3512 (if (i32.eqz (local.get $index)) (then unreachable))
3513 (if (i32.ne (local.get $index) (global.get $index)) (then unreachable))
3514 )
3515 )
3516 (core func $thread-index (canon thread.index))
3517 (core instance $m (instantiate $m (with "" (instance
3518 (export "thread.index" (func $thread-index))
3519 ))))
3520 (func (export "run") (canon lift (core func $m "run") (post-return (func $m "run-post-return"))))
3521 )
3522 "#;
3523 let engine = Engine::new(&style.config())?;
3524 let component = Component::new(&engine, component)?;
3525 let mut store = Store::new(&engine, ());
3526 let linker = Linker::new(&engine);
3527 let instance = style.instantiate(&mut store, &linker, &component).await?;
3528 let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
3529 style.call(&mut store, run, ()).await?;
3530 Ok(())
3531 }
3532
3533 #[tokio::test]
thread_index_via_cabi_realloc_sync() -> Result<()>3534 async fn thread_index_via_cabi_realloc_sync() -> Result<()> {
3535 thread_index_via_cabi_realloc(ApiStyle::Sync).await
3536 }
3537
3538 #[tokio::test]
thread_index_via_cabi_realloc_async() -> Result<()>3539 async fn thread_index_via_cabi_realloc_async() -> Result<()> {
3540 thread_index_via_cabi_realloc(ApiStyle::Async).await
3541 }
3542
3543 #[tokio::test]
thread_index_via_cabi_realloc_concurrent() -> Result<()>3544 async fn thread_index_via_cabi_realloc_concurrent() -> Result<()> {
3545 thread_index_via_cabi_realloc(ApiStyle::Concurrent).await
3546 }
3547
thread_index_via_cabi_realloc(style: ApiStyle) -> Result<()>3548 async fn thread_index_via_cabi_realloc(style: ApiStyle) -> Result<()> {
3549 let component = r#"
3550 (component
3551 (core module $m
3552 (import "" "thread.index" (func $thread-index (result i32)))
3553 (global $index (mut i32) (i32.const 0))
3554 (memory (export "memory") 1)
3555 (func (export "realloc") (param i32 i32 i32 i32) (result i32)
3556 (global.set $index (call $thread-index))
3557 (if (i32.eqz (global.get $index)) (then unreachable))
3558 (i32.const 100)
3559 )
3560 (func (export "run") (param i32 i32)
3561 (local $index i32)
3562 (local.set $index (call $thread-index))
3563 (if (i32.eqz (local.get $index)) (then unreachable))
3564 (if (i32.ne (local.get $index) (global.get $index)) (then unreachable))
3565 )
3566 )
3567 (core func $thread-index (canon thread.index))
3568 (core instance $m (instantiate $m (with "" (instance
3569 (export "thread.index" (func $thread-index))
3570 ))))
3571 (func (export "run") (param "s" string) (canon lift
3572 (core func $m "run")
3573 (memory $m "memory")
3574 (realloc (func $m "realloc"))
3575 ))
3576 )
3577 "#;
3578 let engine = Engine::new(&style.config())?;
3579 let component = Component::new(&engine, component)?;
3580 let mut store = Store::new(&engine, ());
3581 let linker = Linker::new(&engine);
3582 let instance = style.instantiate(&mut store, &linker, &component).await?;
3583 let run = instance.get_typed_func::<(String,), ()>(&mut store, "run")?;
3584 style.call(&mut store, run, ("hola".to_string(),)).await?;
3585 Ok(())
3586 }
3587
3588 #[tokio::test]
thread_index_via_resource_drop_sync() -> Result<()>3589 async fn thread_index_via_resource_drop_sync() -> Result<()> {
3590 thread_index_via_resource_drop(ApiStyle::Sync).await
3591 }
3592
3593 #[tokio::test]
thread_index_via_resource_drop_async() -> Result<()>3594 async fn thread_index_via_resource_drop_async() -> Result<()> {
3595 thread_index_via_resource_drop(ApiStyle::Async).await
3596 }
3597
3598 #[tokio::test]
thread_index_via_resource_drop_concurrent() -> Result<()>3599 async fn thread_index_via_resource_drop_concurrent() -> Result<()> {
3600 thread_index_via_resource_drop(ApiStyle::Concurrent).await
3601 }
3602
thread_index_via_resource_drop(style: ApiStyle) -> Result<()>3603 async fn thread_index_via_resource_drop(style: ApiStyle) -> Result<()> {
3604 let component = r#"
3605 (component
3606 (core module $m
3607 (import "" "thread.index" (func $thread-index (result i32)))
3608 (func (export "dtor") (param i32)
3609 (if (i32.eqz (call $thread-index)) (then unreachable))
3610 )
3611 )
3612 (core func $thread-index (canon thread.index))
3613 (core instance $m (instantiate $m (with "" (instance
3614 (export "thread.index" (func $thread-index))
3615 ))))
3616 (type $r (resource (rep i32) (dtor (func $m "dtor"))))
3617 (core func $new (canon resource.new $r))
3618 (core module $m2
3619 (import "" "new" (func $new (param i32) (result i32)))
3620 (func (export "new") (result i32)
3621 (call $new (i32.const 100))
3622 )
3623 )
3624 (core instance $m2 (instantiate $m2 (with "" (instance
3625 (export "new" (func $new))
3626 ))))
3627 (func $new (result (own $r)) (canon lift (core func $m2 "new")))
3628 (component $c
3629 (import "r" (type $r (sub resource)))
3630 (import "new" (func $new (result (own $r))))
3631 (export $r-export "r" (type $r))
3632 (export "new" (func $new) (func (result (own $r-export))))
3633 )
3634 (instance $c (instantiate $c
3635 (with "r" (type $r))
3636 (with "new" (func $new))
3637 ))
3638 (export "i" (instance $c))
3639 )
3640 "#;
3641 let engine = Engine::new(&style.config())?;
3642 let component = Component::new(&engine, component)?;
3643 let mut store = Store::new(&engine, ());
3644 let linker = Linker::new(&engine);
3645 let instance = style.instantiate(&mut store, &linker, &component).await?;
3646 let instance_index = instance.get_export_index(&mut store, None, "i").unwrap();
3647 let func_index = instance
3648 .get_export_index(&mut store, Some(&instance_index), "new")
3649 .unwrap();
3650 let run = instance.get_typed_func::<(), (ResourceAny,)>(&mut store, &func_index)?;
3651 let (resource,) = style.call(&mut store, run, ()).await?;
3652 style.resource_drop(&mut store, resource).await?;
3653 Ok(())
3654 }
3655
3656 #[tokio::test]
thread_index_via_guest_call_sync() -> Result<()>3657 async fn thread_index_via_guest_call_sync() -> Result<()> {
3658 thread_index_via_guest_call(ApiStyle::Sync).await
3659 }
3660
3661 #[tokio::test]
thread_index_via_guest_call_async() -> Result<()>3662 async fn thread_index_via_guest_call_async() -> Result<()> {
3663 thread_index_via_guest_call(ApiStyle::Async).await
3664 }
3665
3666 #[tokio::test]
thread_index_via_guest_call_concurrent() -> Result<()>3667 async fn thread_index_via_guest_call_concurrent() -> Result<()> {
3668 thread_index_via_guest_call(ApiStyle::Concurrent).await
3669 }
3670
thread_index_via_guest_call(style: ApiStyle) -> Result<()>3671 async fn thread_index_via_guest_call(style: ApiStyle) -> Result<()> {
3672 let component = r#"
3673 (component
3674 (component $c
3675 (core module $m
3676 (import "" "thread.index" (func $thread-index (result i32)))
3677 (func (export "run") (result i32)
3678 (call $thread-index)
3679 )
3680 )
3681 (core func $thread-index (canon thread.index))
3682 (core instance $m (instantiate $m (with "" (instance
3683 (export "thread.index" (func $thread-index))
3684 ))))
3685 (func (export "run") (result u32) (canon lift (core func $m "run")))
3686 )
3687 (instance $c (instantiate $c))
3688
3689 (component $d
3690 (import "c" (instance $c
3691 (export "run" (func (result u32)))
3692 ))
3693 (core func $run (canon lower (func $c "run")))
3694 (core module $m
3695 (import "" "thread.index" (func $thread-index (result i32)))
3696 (import "" "run" (func $run (result i32)))
3697 (func (export "run")
3698 (local $mine i32)
3699 (local $theirs i32)
3700 (local.set $mine (call $thread-index))
3701 (if (i32.eqz (local.get $mine)) (then unreachable))
3702 (local.set $theirs (call $run))
3703 (if (i32.eqz (local.get $theirs)) (then unreachable))
3704 )
3705 )
3706 (core func $thread-index (canon thread.index))
3707 (core instance $m (instantiate $m (with "" (instance
3708 (export "thread.index" (func $thread-index))
3709 (export "run" (func $run))
3710 ))))
3711 (func (export "run") (canon lift (core func $m "run")))
3712 )
3713 (instance $d (instantiate $d (with "c" (instance $c))))
3714 (func (export "run") (alias export $d "run"))
3715 )
3716 "#;
3717 let engine = Engine::new(&style.config())?;
3718 let component = Component::new(&engine, component)?;
3719 let mut store = Store::new(&engine, ());
3720 let linker = Linker::new(&engine);
3721 let instance = style.instantiate(&mut store, &linker, &component).await?;
3722 let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
3723 style.call(&mut store, run, ()).await?;
3724 Ok(())
3725 }
3726
with_new_instance<T>( engine: &Engine, component: &Component, fun: impl Fn(&mut Store<()>, Instance) -> wasmtime::Result<T>, ) -> wasmtime::Result<T>3727 fn with_new_instance<T>(
3728 engine: &Engine,
3729 component: &Component,
3730 fun: impl Fn(&mut Store<()>, Instance) -> wasmtime::Result<T>,
3731 ) -> wasmtime::Result<T> {
3732 let mut store = Store::new(engine, ());
3733 let instance = Linker::new(engine).instantiate(&mut store, component)?;
3734 fun(&mut store, instance)
3735 }
3736
3737 #[tokio::test]
drop_call_async_future() -> Result<()>3738 async fn drop_call_async_future() -> Result<()> {
3739 let component = r#"
3740 (component
3741 (import "foo" (func $f))
3742 (core module $m
3743 (func $f (import "" "foo"))
3744 (func (export "foo") call $f)
3745 )
3746 (core func $f (canon lower (func $f)))
3747 (core instance $m (instantiate $m (with "" (instance
3748 (export "foo" (func $f))
3749 ))))
3750 (func (export "foo") (canon lift (core func $m "foo")))
3751 )
3752 "#;
3753
3754 let engine = &Engine::new(&Config::new())?;
3755 let component = Component::new(&engine, component)?;
3756 let mut store = Store::new(&engine, ());
3757 let mut linker = Linker::new(&engine);
3758 linker.root().func_wrap_async("foo", |_, _: ()| {
3759 Box::new(async {
3760 tokio::task::yield_now().await;
3761 Ok(())
3762 })
3763 })?;
3764 let instance = linker.instantiate_async(&mut store, &component).await?;
3765 let foo = instance.get_typed_func::<(), ()>(&mut store, "foo")?;
3766 // Here we'll use `call_async` a few times but only poll each returned
3767 // future once. This will put the instance in a weird state but shouldn't
3768 // cause a panic.
3769 for _ in 0..5 {
3770 let mut future = std::pin::pin!(foo.call_async(&mut store, ()));
3771 if let std::task::Poll::Ready(result) =
3772 std::future::poll_fn(|cx| std::task::Poll::Ready(future.as_mut().poll(cx))).await
3773 {
3774 _ = result;
3775 }
3776 }
3777
3778 Ok(())
3779 }
3780
3781 #[test]
host_call_with_concurrency_disabled() -> Result<()>3782 fn host_call_with_concurrency_disabled() -> Result<()> {
3783 let mut config = Config::default();
3784 config.concurrency_support(false);
3785
3786 struct MyResource;
3787
3788 let engine = Engine::new(&config)?;
3789 let mut store = Store::new(&engine, ());
3790 let mut linker = Linker::<()>::new(&engine);
3791
3792 linker
3793 .root()
3794 .resource("r", ResourceType::host::<MyResource>(), |_, _| Ok(()))?;
3795
3796 let f_called = Arc::new(AtomicBool::new(false));
3797 linker.root().func_wrap("f", {
3798 let f_called = f_called.clone();
3799 move |_ctx, _: (Resource<MyResource>,)| -> Result<()> {
3800 f_called.store(true, SeqCst);
3801 Ok(())
3802 }
3803 })?;
3804
3805 let component = Component::new(
3806 &engine,
3807 r#"
3808 (component
3809 (import "r" (type $r (sub resource)))
3810 (import "f" (func $f (param "r" (borrow $r))))
3811
3812 (core func $f' (canon lower (func $f)))
3813 (core func $drop (canon resource.drop $r))
3814
3815 (core module $m
3816 (import "" "f" (func $f (param i32)))
3817 (import "" "drop" (func $drop (param i32)))
3818 (func (export "g") (param i32)
3819 (call $f (local.get 0))
3820 (call $drop (local.get 0))
3821 )
3822 )
3823
3824 (core instance $i (instantiate $m (with
3825 "" (instance
3826 (export "f" (func $f'))
3827 (export "drop" (func $drop))
3828 )
3829 )))
3830
3831 (func (export "g") (param "r" (borrow $r))
3832 (canon lift (core func $i "g"))
3833 )
3834 )
3835 "#
3836 .as_bytes(),
3837 )?;
3838
3839 let instance = linker.instantiate(&mut store, &component)?;
3840 let g = instance.get_typed_func::<(&Resource<MyResource>,), ()>(&mut store, "g")?;
3841
3842 let resource = Resource::new_own(100);
3843 g.call(&mut store, (&resource,))?;
3844
3845 assert!(f_called.load(SeqCst));
3846
3847 Ok(())
3848 }
3849
3850 /// Tests map types with misaligned key/value combinations through the adapter
3851 /// trampoline (component-to-component translation).
3852 ///
3853 /// This specifically tests the alignment bug where the value offset was
3854 /// calculated as `key_size` instead of `align(key_size, value_align)`.
3855 /// For map<u8, u64>, the value should be at offset 8 (not 1).
3856 ///
3857 #[test]
map_trampoline_alignment() -> Result<()>3858 fn map_trampoline_alignment() -> Result<()> {
3859 // Test map<u8, u64> - key_size=1, value_align=8
3860 // With the alignment bug, value would be read/written at offset 1 instead of 8
3861 let component = format!(
3862 r#"
3863 (component
3864 (import "host" (func $host (param "m" (map u8 u64)) (result (map u8 u64))))
3865
3866 ;; Component A: the "destination" that receives and echoes back
3867 (component $dst
3868 (import "echo" (func $echo (param "m" (map u8 u64)) (result (map u8 u64))))
3869 (core module $libc
3870 (memory (export "memory") 1)
3871 {REALLOC_AND_FREE}
3872 )
3873 (core module $echo_mod
3874 (import "" "echo" (func $echo (param i32 i32 i32)))
3875 (import "libc" "memory" (memory 0))
3876 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
3877
3878 (func (export "echo") (param i32 i32) (result i32)
3879 (local $retptr i32)
3880 (local.set $retptr
3881 (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
3882 (call $echo (local.get 0) (local.get 1) (local.get $retptr))
3883 local.get $retptr
3884 )
3885 )
3886 (core instance $libc (instantiate $libc))
3887 (core func $echo_lower (canon lower (func $echo)
3888 (memory $libc "memory")
3889 (realloc (func $libc "realloc"))
3890 ))
3891 (core instance $echo_inst (instantiate $echo_mod
3892 (with "libc" (instance $libc))
3893 (with "" (instance (export "echo" (func $echo_lower))))
3894 ))
3895 (func (export "echo2") (param "m" (map u8 u64)) (result (map u8 u64))
3896 (canon lift
3897 (core func $echo_inst "echo")
3898 (memory $libc "memory")
3899 (realloc (func $libc "realloc"))
3900 )
3901 )
3902 )
3903
3904 ;; Component B: the "source" that calls dst
3905 (component $src
3906 (import "echo" (func $echo (param "m" (map u8 u64)) (result (map u8 u64))))
3907 (core module $libc
3908 (memory (export "memory") 1)
3909 {REALLOC_AND_FREE}
3910 )
3911 (core module $echo_mod
3912 (import "" "echo" (func $echo (param i32 i32 i32)))
3913 (import "libc" "memory" (memory 0))
3914 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
3915
3916 (func (export "echo") (param i32 i32) (result i32)
3917 (local $retptr i32)
3918 (local.set $retptr
3919 (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
3920 (call $echo (local.get 0) (local.get 1) (local.get $retptr))
3921 local.get $retptr
3922 )
3923 )
3924 (core instance $libc (instantiate $libc))
3925 (core func $echo_lower (canon lower (func $echo)
3926 (memory $libc "memory")
3927 (realloc (func $libc "realloc"))
3928 ))
3929 (core instance $echo_inst (instantiate $echo_mod
3930 (with "libc" (instance $libc))
3931 (with "" (instance (export "echo" (func $echo_lower))))
3932 ))
3933 (func (export "echo2") (param "m" (map u8 u64)) (result (map u8 u64))
3934 (canon lift
3935 (core func $echo_inst "echo")
3936 (memory $libc "memory")
3937 (realloc (func $libc "realloc"))
3938 )
3939 )
3940 )
3941
3942 ;; Wire: host -> dst -> src creates adapter trampolines between components
3943 (instance $dst (instantiate $dst (with "echo" (func $host))))
3944 (instance $src (instantiate $src (with "echo" (func $dst "echo2"))))
3945 (export "echo" (func $src "echo2"))
3946 )
3947 "#
3948 );
3949
3950 let mut config = Config::new();
3951 config.wasm_component_model(true);
3952 config.wasm_component_model_map(true);
3953 let engine = Engine::new(&config)?;
3954 let component = Component::new(&engine, component)?;
3955
3956 let mut store = Store::new(&engine, ());
3957 let mut linker = Linker::new(&engine);
3958
3959 linker.root().func_new("host", |_cx, _ty, args, results| {
3960 results[0] = args[0].clone();
3961 Ok(())
3962 })?;
3963
3964 let instance = linker.instantiate(&mut store, &component)?;
3965 let func = instance.get_func(&mut store, "echo").unwrap();
3966
3967 let test_data = vec![
3968 (Val::U8(1), Val::U64(0x0102030405060708)),
3969 (Val::U8(2), Val::U64(0x1112131415161718)),
3970 (Val::U8(255), Val::U64(0xFFFFFFFFFFFFFFFF)),
3971 ];
3972 let input = Val::Map(test_data.clone());
3973
3974 let mut results = [Val::Bool(false)];
3975 func.call(&mut store, &[input], &mut results)?;
3976
3977 match &results[0] {
3978 Val::Map(output) => {
3979 assert_eq!(output.len(), 3);
3980 for (key, value) in &test_data {
3981 assert!(
3982 output.iter().any(|(k, v)| k == key && v == value),
3983 "Missing or corrupted entry"
3984 );
3985 }
3986 }
3987 _ => panic!("expected map"),
3988 }
3989
3990 Ok(())
3991 }
3992
3993 /// Tests map<u32, u64> alignment through trampoline
3994 #[test]
map_trampoline_alignment_u32_u64() -> Result<()>3995 fn map_trampoline_alignment_u32_u64() -> Result<()> {
3996 // Test map<u32, u64> - key_size=4, value_align=8
3997 // With the alignment bug, value would be read/written at offset 4 instead of 8
3998 let component = format!(
3999 r#"
4000 (component
4001 (import "host" (func $host (param "m" (map u32 u64)) (result (map u32 u64))))
4002
4003 (component $dst
4004 (import "echo" (func $echo (param "m" (map u32 u64)) (result (map u32 u64))))
4005 (core module $libc
4006 (memory (export "memory") 1)
4007 {REALLOC_AND_FREE}
4008 )
4009 (core module $echo_mod
4010 (import "" "echo" (func $echo (param i32 i32 i32)))
4011 (import "libc" "memory" (memory 0))
4012 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
4013
4014 (func (export "echo") (param i32 i32) (result i32)
4015 (local $retptr i32)
4016 (local.set $retptr
4017 (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
4018 (call $echo (local.get 0) (local.get 1) (local.get $retptr))
4019 local.get $retptr
4020 )
4021 )
4022 (core instance $libc (instantiate $libc))
4023 (core func $echo_lower (canon lower (func $echo)
4024 (memory $libc "memory")
4025 (realloc (func $libc "realloc"))
4026 ))
4027 (core instance $echo_inst (instantiate $echo_mod
4028 (with "libc" (instance $libc))
4029 (with "" (instance (export "echo" (func $echo_lower))))
4030 ))
4031 (func (export "echo2") (param "m" (map u32 u64)) (result (map u32 u64))
4032 (canon lift
4033 (core func $echo_inst "echo")
4034 (memory $libc "memory")
4035 (realloc (func $libc "realloc"))
4036 )
4037 )
4038 )
4039
4040 (component $src
4041 (import "echo" (func $echo (param "m" (map u32 u64)) (result (map u32 u64))))
4042 (core module $libc
4043 (memory (export "memory") 1)
4044 {REALLOC_AND_FREE}
4045 )
4046 (core module $echo_mod
4047 (import "" "echo" (func $echo (param i32 i32 i32)))
4048 (import "libc" "memory" (memory 0))
4049 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
4050
4051 (func (export "echo") (param i32 i32) (result i32)
4052 (local $retptr i32)
4053 (local.set $retptr
4054 (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
4055 (call $echo (local.get 0) (local.get 1) (local.get $retptr))
4056 local.get $retptr
4057 )
4058 )
4059 (core instance $libc (instantiate $libc))
4060 (core func $echo_lower (canon lower (func $echo)
4061 (memory $libc "memory")
4062 (realloc (func $libc "realloc"))
4063 ))
4064 (core instance $echo_inst (instantiate $echo_mod
4065 (with "libc" (instance $libc))
4066 (with "" (instance (export "echo" (func $echo_lower))))
4067 ))
4068 (func (export "echo2") (param "m" (map u32 u64)) (result (map u32 u64))
4069 (canon lift
4070 (core func $echo_inst "echo")
4071 (memory $libc "memory")
4072 (realloc (func $libc "realloc"))
4073 )
4074 )
4075 )
4076
4077 (instance $dst (instantiate $dst (with "echo" (func $host))))
4078 (instance $src (instantiate $src (with "echo" (func $dst "echo2"))))
4079 (export "echo" (func $src "echo2"))
4080 )
4081 "#
4082 );
4083
4084 let mut config = Config::new();
4085 config.wasm_component_model(true);
4086 config.wasm_component_model_map(true);
4087 let engine = Engine::new(&config)?;
4088 let component = Component::new(&engine, component)?;
4089
4090 let mut store = Store::new(&engine, ());
4091 let mut linker = Linker::new(&engine);
4092
4093 linker.root().func_new("host", |_cx, _ty, args, results| {
4094 results[0] = args[0].clone();
4095 Ok(())
4096 })?;
4097
4098 let instance = linker.instantiate(&mut store, &component)?;
4099 let func = instance.get_func(&mut store, "echo").unwrap();
4100
4101 let test_data = vec![
4102 (Val::U32(1), Val::U64(0x0102030405060708)),
4103 (Val::U32(2), Val::U64(0x1112131415161718)),
4104 ];
4105 let input = Val::Map(test_data.clone());
4106
4107 let mut results = [Val::Bool(false)];
4108 func.call(&mut store, &[input], &mut results)?;
4109
4110 match &results[0] {
4111 Val::Map(output) => {
4112 assert_eq!(output.len(), 2);
4113 for (key, value) in &test_data {
4114 assert!(
4115 output.iter().any(|(k, v)| k == key && v == value),
4116 "Missing or corrupted entry"
4117 );
4118 }
4119 }
4120 _ => panic!("expected map"),
4121 }
4122
4123 Ok(())
4124 }
4125
4126 /// Tests map<u8, u32> alignment through trampoline
4127 #[test]
map_trampoline_alignment_u8_u32() -> Result<()>4128 fn map_trampoline_alignment_u8_u32() -> Result<()> {
4129 let component = format!(
4130 r#"
4131 (component
4132 (import "host" (func $host (param "m" (map u8 u32)) (result (map u8 u32))))
4133
4134 (component $dst
4135 (import "echo" (func $echo (param "m" (map u8 u32)) (result (map u8 u32))))
4136 (core module $libc
4137 (memory (export "memory") 1)
4138 {REALLOC_AND_FREE}
4139 )
4140 (core module $echo_mod
4141 (import "" "echo" (func $echo (param i32 i32 i32)))
4142 (import "libc" "memory" (memory 0))
4143 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
4144
4145 (func (export "echo") (param i32 i32) (result i32)
4146 (local $retptr i32)
4147 (local.set $retptr
4148 (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
4149 (call $echo (local.get 0) (local.get 1) (local.get $retptr))
4150 local.get $retptr
4151 )
4152 )
4153 (core instance $libc (instantiate $libc))
4154 (core func $echo_lower (canon lower (func $echo)
4155 (memory $libc "memory")
4156 (realloc (func $libc "realloc"))
4157 ))
4158 (core instance $echo_inst (instantiate $echo_mod
4159 (with "libc" (instance $libc))
4160 (with "" (instance (export "echo" (func $echo_lower))))
4161 ))
4162 (func (export "echo2") (param "m" (map u8 u32)) (result (map u8 u32))
4163 (canon lift
4164 (core func $echo_inst "echo")
4165 (memory $libc "memory")
4166 (realloc (func $libc "realloc"))
4167 )
4168 )
4169 )
4170
4171 (component $src
4172 (import "echo" (func $echo (param "m" (map u8 u32)) (result (map u8 u32))))
4173 (core module $libc
4174 (memory (export "memory") 1)
4175 {REALLOC_AND_FREE}
4176 )
4177 (core module $echo_mod
4178 (import "" "echo" (func $echo (param i32 i32 i32)))
4179 (import "libc" "memory" (memory 0))
4180 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
4181
4182 (func (export "echo") (param i32 i32) (result i32)
4183 (local $retptr i32)
4184 (local.set $retptr
4185 (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
4186 (call $echo (local.get 0) (local.get 1) (local.get $retptr))
4187 local.get $retptr
4188 )
4189 )
4190 (core instance $libc (instantiate $libc))
4191 (core func $echo_lower (canon lower (func $echo)
4192 (memory $libc "memory")
4193 (realloc (func $libc "realloc"))
4194 ))
4195 (core instance $echo_inst (instantiate $echo_mod
4196 (with "libc" (instance $libc))
4197 (with "" (instance (export "echo" (func $echo_lower))))
4198 ))
4199 (func (export "echo2") (param "m" (map u8 u32)) (result (map u8 u32))
4200 (canon lift
4201 (core func $echo_inst "echo")
4202 (memory $libc "memory")
4203 (realloc (func $libc "realloc"))
4204 )
4205 )
4206 )
4207
4208 (instance $dst (instantiate $dst (with "echo" (func $host))))
4209 (instance $src (instantiate $src (with "echo" (func $dst "echo2"))))
4210 (export "echo" (func $src "echo2"))
4211 )
4212 "#
4213 );
4214
4215 let mut config = Config::new();
4216 config.wasm_component_model(true);
4217 config.wasm_component_model_map(true);
4218 let engine = Engine::new(&config)?;
4219 let component = Component::new(&engine, component)?;
4220
4221 let mut store = Store::new(&engine, ());
4222 let mut linker = Linker::new(&engine);
4223
4224 linker.root().func_new("host", |_cx, _ty, args, results| {
4225 results[0] = args[0].clone();
4226 Ok(())
4227 })?;
4228
4229 let instance = linker.instantiate(&mut store, &component)?;
4230 let func = instance.get_func(&mut store, "echo").unwrap();
4231
4232 let test_data = vec![
4233 (Val::U8(1), Val::U32(0x01020304)),
4234 (Val::U8(2), Val::U32(0x11121314)),
4235 ];
4236 let input = Val::Map(test_data.clone());
4237
4238 let mut results = [Val::Bool(false)];
4239 func.call(&mut store, &[input], &mut results)?;
4240
4241 match &results[0] {
4242 Val::Map(output) => {
4243 assert_eq!(output.len(), 2);
4244 for (key, value) in &test_data {
4245 assert!(
4246 output.iter().any(|(k, v)| k == key && v == value),
4247 "Missing or corrupted entry"
4248 );
4249 }
4250 }
4251 _ => panic!("expected map"),
4252 }
4253
4254 Ok(())
4255 }
4256
4257 /// Tests map<u16, u64> alignment through trampoline
4258 #[test]
map_trampoline_alignment_u16_u64() -> Result<()>4259 fn map_trampoline_alignment_u16_u64() -> Result<()> {
4260 let component = format!(
4261 r#"
4262 (component
4263 (import "host" (func $host (param "m" (map u16 u64)) (result (map u16 u64))))
4264
4265 (component $dst
4266 (import "echo" (func $echo (param "m" (map u16 u64)) (result (map u16 u64))))
4267 (core module $libc
4268 (memory (export "memory") 1)
4269 {REALLOC_AND_FREE}
4270 )
4271 (core module $echo_mod
4272 (import "" "echo" (func $echo (param i32 i32 i32)))
4273 (import "libc" "memory" (memory 0))
4274 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
4275
4276 (func (export "echo") (param i32 i32) (result i32)
4277 (local $retptr i32)
4278 (local.set $retptr
4279 (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
4280 (call $echo (local.get 0) (local.get 1) (local.get $retptr))
4281 local.get $retptr
4282 )
4283 )
4284 (core instance $libc (instantiate $libc))
4285 (core func $echo_lower (canon lower (func $echo)
4286 (memory $libc "memory")
4287 (realloc (func $libc "realloc"))
4288 ))
4289 (core instance $echo_inst (instantiate $echo_mod
4290 (with "libc" (instance $libc))
4291 (with "" (instance (export "echo" (func $echo_lower))))
4292 ))
4293 (func (export "echo2") (param "m" (map u16 u64)) (result (map u16 u64))
4294 (canon lift
4295 (core func $echo_inst "echo")
4296 (memory $libc "memory")
4297 (realloc (func $libc "realloc"))
4298 )
4299 )
4300 )
4301
4302 (component $src
4303 (import "echo" (func $echo (param "m" (map u16 u64)) (result (map u16 u64))))
4304 (core module $libc
4305 (memory (export "memory") 1)
4306 {REALLOC_AND_FREE}
4307 )
4308 (core module $echo_mod
4309 (import "" "echo" (func $echo (param i32 i32 i32)))
4310 (import "libc" "memory" (memory 0))
4311 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
4312
4313 (func (export "echo") (param i32 i32) (result i32)
4314 (local $retptr i32)
4315 (local.set $retptr
4316 (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
4317 (call $echo (local.get 0) (local.get 1) (local.get $retptr))
4318 local.get $retptr
4319 )
4320 )
4321 (core instance $libc (instantiate $libc))
4322 (core func $echo_lower (canon lower (func $echo)
4323 (memory $libc "memory")
4324 (realloc (func $libc "realloc"))
4325 ))
4326 (core instance $echo_inst (instantiate $echo_mod
4327 (with "libc" (instance $libc))
4328 (with "" (instance (export "echo" (func $echo_lower))))
4329 ))
4330 (func (export "echo2") (param "m" (map u16 u64)) (result (map u16 u64))
4331 (canon lift
4332 (core func $echo_inst "echo")
4333 (memory $libc "memory")
4334 (realloc (func $libc "realloc"))
4335 )
4336 )
4337 )
4338
4339 (instance $dst (instantiate $dst (with "echo" (func $host))))
4340 (instance $src (instantiate $src (with "echo" (func $dst "echo2"))))
4341 (export "echo" (func $src "echo2"))
4342 )
4343 "#
4344 );
4345
4346 let mut config = Config::new();
4347 config.wasm_component_model(true);
4348 config.wasm_component_model_map(true);
4349 let engine = Engine::new(&config)?;
4350 let component = Component::new(&engine, component)?;
4351
4352 let mut store = Store::new(&engine, ());
4353 let mut linker = Linker::new(&engine);
4354
4355 linker.root().func_new("host", |_cx, _ty, args, results| {
4356 results[0] = args[0].clone();
4357 Ok(())
4358 })?;
4359
4360 let instance = linker.instantiate(&mut store, &component)?;
4361 let func = instance.get_func(&mut store, "echo").unwrap();
4362
4363 let test_data = vec![
4364 (Val::U16(1), Val::U64(0x0102030405060708)),
4365 (Val::U16(2), Val::U64(0x1112131415161718)),
4366 ];
4367 let input = Val::Map(test_data.clone());
4368
4369 let mut results = [Val::Bool(false)];
4370 func.call(&mut store, &[input], &mut results)?;
4371
4372 match &results[0] {
4373 Val::Map(output) => {
4374 assert_eq!(output.len(), 2);
4375 for (key, value) in &test_data {
4376 assert!(
4377 output.iter().any(|(k, v)| k == key && v == value),
4378 "Missing or corrupted entry"
4379 );
4380 }
4381 }
4382 _ => panic!("expected map"),
4383 }
4384
4385 Ok(())
4386 }
4387
4388 /// Tests map<u8, u16> alignment through trampoline
4389 #[test]
map_trampoline_alignment_u8_u16() -> Result<()>4390 fn map_trampoline_alignment_u8_u16() -> Result<()> {
4391 let component = format!(
4392 r#"
4393 (component
4394 (import "host" (func $host (param "m" (map u8 u16)) (result (map u8 u16))))
4395
4396 (component $dst
4397 (import "echo" (func $echo (param "m" (map u8 u16)) (result (map u8 u16))))
4398 (core module $libc
4399 (memory (export "memory") 1)
4400 {REALLOC_AND_FREE}
4401 )
4402 (core module $echo_mod
4403 (import "" "echo" (func $echo (param i32 i32 i32)))
4404 (import "libc" "memory" (memory 0))
4405 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
4406
4407 (func (export "echo") (param i32 i32) (result i32)
4408 (local $retptr i32)
4409 (local.set $retptr
4410 (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
4411 (call $echo (local.get 0) (local.get 1) (local.get $retptr))
4412 local.get $retptr
4413 )
4414 )
4415 (core instance $libc (instantiate $libc))
4416 (core func $echo_lower (canon lower (func $echo)
4417 (memory $libc "memory")
4418 (realloc (func $libc "realloc"))
4419 ))
4420 (core instance $echo_inst (instantiate $echo_mod
4421 (with "libc" (instance $libc))
4422 (with "" (instance (export "echo" (func $echo_lower))))
4423 ))
4424 (func (export "echo2") (param "m" (map u8 u16)) (result (map u8 u16))
4425 (canon lift
4426 (core func $echo_inst "echo")
4427 (memory $libc "memory")
4428 (realloc (func $libc "realloc"))
4429 )
4430 )
4431 )
4432
4433 (component $src
4434 (import "echo" (func $echo (param "m" (map u8 u16)) (result (map u8 u16))))
4435 (core module $libc
4436 (memory (export "memory") 1)
4437 {REALLOC_AND_FREE}
4438 )
4439 (core module $echo_mod
4440 (import "" "echo" (func $echo (param i32 i32 i32)))
4441 (import "libc" "memory" (memory 0))
4442 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
4443
4444 (func (export "echo") (param i32 i32) (result i32)
4445 (local $retptr i32)
4446 (local.set $retptr
4447 (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
4448 (call $echo (local.get 0) (local.get 1) (local.get $retptr))
4449 local.get $retptr
4450 )
4451 )
4452 (core instance $libc (instantiate $libc))
4453 (core func $echo_lower (canon lower (func $echo)
4454 (memory $libc "memory")
4455 (realloc (func $libc "realloc"))
4456 ))
4457 (core instance $echo_inst (instantiate $echo_mod
4458 (with "libc" (instance $libc))
4459 (with "" (instance (export "echo" (func $echo_lower))))
4460 ))
4461 (func (export "echo2") (param "m" (map u8 u16)) (result (map u8 u16))
4462 (canon lift
4463 (core func $echo_inst "echo")
4464 (memory $libc "memory")
4465 (realloc (func $libc "realloc"))
4466 )
4467 )
4468 )
4469
4470 (instance $dst (instantiate $dst (with "echo" (func $host))))
4471 (instance $src (instantiate $src (with "echo" (func $dst "echo2"))))
4472 (export "echo" (func $src "echo2"))
4473 )
4474 "#
4475 );
4476
4477 let mut config = Config::new();
4478 config.wasm_component_model(true);
4479 config.wasm_component_model_map(true);
4480 let engine = Engine::new(&config)?;
4481 let component = Component::new(&engine, component)?;
4482
4483 let mut store = Store::new(&engine, ());
4484 let mut linker = Linker::new(&engine);
4485
4486 linker.root().func_new("host", |_cx, _ty, args, results| {
4487 results[0] = args[0].clone();
4488 Ok(())
4489 })?;
4490
4491 let instance = linker.instantiate(&mut store, &component)?;
4492 let func = instance.get_func(&mut store, "echo").unwrap();
4493
4494 let test_data = vec![
4495 (Val::U8(1), Val::U16(0x0102)),
4496 (Val::U8(2), Val::U16(0x1112)),
4497 ];
4498 let input = Val::Map(test_data.clone());
4499
4500 let mut results = [Val::Bool(false)];
4501 func.call(&mut store, &[input], &mut results)?;
4502
4503 match &results[0] {
4504 Val::Map(output) => {
4505 assert_eq!(output.len(), 2);
4506 for (key, value) in &test_data {
4507 assert!(
4508 output.iter().any(|(k, v)| k == key && v == value),
4509 "Missing or corrupted entry"
4510 );
4511 }
4512 }
4513 _ => panic!("expected map"),
4514 }
4515
4516 Ok(())
4517 }
4518
4519 /// Tests map<u64, u8> alignment through trampoline (reverse case: key larger than value)
4520 #[test]
map_trampoline_alignment_u64_u8() -> Result<()>4521 fn map_trampoline_alignment_u64_u8() -> Result<()> {
4522 let component = format!(
4523 r#"
4524 (component
4525 (import "host" (func $host (param "m" (map u64 u8)) (result (map u64 u8))))
4526
4527 (component $dst
4528 (import "echo" (func $echo (param "m" (map u64 u8)) (result (map u64 u8))))
4529 (core module $libc
4530 (memory (export "memory") 1)
4531 {REALLOC_AND_FREE}
4532 )
4533 (core module $echo_mod
4534 (import "" "echo" (func $echo (param i32 i32 i32)))
4535 (import "libc" "memory" (memory 0))
4536 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
4537
4538 (func (export "echo") (param i32 i32) (result i32)
4539 (local $retptr i32)
4540 (local.set $retptr
4541 (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
4542 (call $echo (local.get 0) (local.get 1) (local.get $retptr))
4543 local.get $retptr
4544 )
4545 )
4546 (core instance $libc (instantiate $libc))
4547 (core func $echo_lower (canon lower (func $echo)
4548 (memory $libc "memory")
4549 (realloc (func $libc "realloc"))
4550 ))
4551 (core instance $echo_inst (instantiate $echo_mod
4552 (with "libc" (instance $libc))
4553 (with "" (instance (export "echo" (func $echo_lower))))
4554 ))
4555 (func (export "echo2") (param "m" (map u64 u8)) (result (map u64 u8))
4556 (canon lift
4557 (core func $echo_inst "echo")
4558 (memory $libc "memory")
4559 (realloc (func $libc "realloc"))
4560 )
4561 )
4562 )
4563
4564 (component $src
4565 (import "echo" (func $echo (param "m" (map u64 u8)) (result (map u64 u8))))
4566 (core module $libc
4567 (memory (export "memory") 1)
4568 {REALLOC_AND_FREE}
4569 )
4570 (core module $echo_mod
4571 (import "" "echo" (func $echo (param i32 i32 i32)))
4572 (import "libc" "memory" (memory 0))
4573 (import "libc" "realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
4574
4575 (func (export "echo") (param i32 i32) (result i32)
4576 (local $retptr i32)
4577 (local.set $retptr
4578 (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8)))
4579 (call $echo (local.get 0) (local.get 1) (local.get $retptr))
4580 local.get $retptr
4581 )
4582 )
4583 (core instance $libc (instantiate $libc))
4584 (core func $echo_lower (canon lower (func $echo)
4585 (memory $libc "memory")
4586 (realloc (func $libc "realloc"))
4587 ))
4588 (core instance $echo_inst (instantiate $echo_mod
4589 (with "libc" (instance $libc))
4590 (with "" (instance (export "echo" (func $echo_lower))))
4591 ))
4592 (func (export "echo2") (param "m" (map u64 u8)) (result (map u64 u8))
4593 (canon lift
4594 (core func $echo_inst "echo")
4595 (memory $libc "memory")
4596 (realloc (func $libc "realloc"))
4597 )
4598 )
4599 )
4600
4601 (instance $dst (instantiate $dst (with "echo" (func $host))))
4602 (instance $src (instantiate $src (with "echo" (func $dst "echo2"))))
4603 (export "echo" (func $src "echo2"))
4604 )
4605 "#
4606 );
4607
4608 let mut config = Config::new();
4609 config.wasm_component_model(true);
4610 config.wasm_component_model_map(true);
4611 let engine = Engine::new(&config)?;
4612 let component = Component::new(&engine, component)?;
4613
4614 let mut store = Store::new(&engine, ());
4615 let mut linker = Linker::new(&engine);
4616
4617 linker.root().func_new("host", |_cx, _ty, args, results| {
4618 results[0] = args[0].clone();
4619 Ok(())
4620 })?;
4621
4622 let instance = linker.instantiate(&mut store, &component)?;
4623 let func = instance.get_func(&mut store, "echo").unwrap();
4624
4625 let test_data = vec![
4626 (Val::U64(0x0102030405060708), Val::U8(42)),
4627 (Val::U64(0x1112131415161718), Val::U8(99)),
4628 ];
4629 let input = Val::Map(test_data.clone());
4630
4631 let mut results = [Val::Bool(false)];
4632 func.call(&mut store, &[input], &mut results)?;
4633
4634 match &results[0] {
4635 Val::Map(output) => {
4636 assert_eq!(output.len(), 2);
4637 for (key, value) in &test_data {
4638 assert!(
4639 output.iter().any(|(k, v)| k == key && v == value),
4640 "Missing or corrupted entry"
4641 );
4642 }
4643 }
4644 _ => panic!("expected map"),
4645 }
4646
4647 Ok(())
4648 }
4649