1 use wasmtime::component::{Component, Instance, Linker, Val};
2 use wasmtime::{Engine, Result, Store, ToWasmtimeResult as _, bail, error::Context as _};
3 use wasmtime_wizer::Wizer;
4
fail_wizening(msg: &str, wasm: &[u8]) -> Result<()>5 fn fail_wizening(msg: &str, wasm: &[u8]) -> Result<()> {
6 let _ = env_logger::try_init();
7
8 let wasm = wat::parse_bytes(wasm)?;
9 log::debug!(
10 "testing wizening failure for wasm:\n{}",
11 wasmprinter::print_bytes(&wasm).to_wasmtime_result()?
12 );
13 match Wizer::new().instrument_component(&wasm) {
14 Ok(_) => bail!("expected wizening to fail"),
15 Err(e) => {
16 let err = format!("{e}");
17 if !err.contains(msg) {
18 bail!("unexpected error: {err}");
19 }
20 Ok(())
21 }
22 }
23 }
24
25 #[test]
unsupported_constructs() -> Result<()>26 fn unsupported_constructs() -> Result<()> {
27 fail_wizening(
28 "does not currently support component imports",
29 br#"(component
30 (import "x" (component))
31 )"#,
32 )?;
33
34 fail_wizening(
35 "nested components with modules not currently supported",
36 br#"(component
37 (component (core module))
38 )"#,
39 )?;
40 fail_wizening(
41 "nested components with modules not currently supported",
42 br#"(component
43 (component)
44 (component (core module))
45 )"#,
46 )?;
47 fail_wizening(
48 "wizer does not currently support module imports",
49 br#"(component
50 (component (import "x" (core module)))
51 )"#,
52 )?;
53 fail_wizening(
54 "wizer does not currently support module aliases",
55 br#"(component
56 (core module $a)
57 (component
58 (core instance (instantiate $a))
59 )
60 )"#,
61 )?;
62 fail_wizening(
63 "wizer does not currently support component aliases",
64 br#"(component
65 (component $a)
66 (component
67 (instance (instantiate $a))
68 )
69 )"#,
70 )?;
71
72 fail_wizening(
73 "does not currently support component aliases",
74 br#"(component
75 (import "x" (instance $i (export "x" (component))))
76 (alias export $i "x" (component $c))
77 )"#,
78 )?;
79
80 fail_wizening(
81 "does not currently support module imports",
82 br#"(component
83 (import "x" (core module))
84 )"#,
85 )?;
86
87 fail_wizening(
88 "does not currently support module exports",
89 br#"(component
90 (core module $x)
91 (export "x" (core module $x))
92 )"#,
93 )?;
94
95 fail_wizening(
96 "does not currently support module aliases",
97 br#"(component
98 (import "x" (instance $i (export "x" (core module))))
99 (alias export $i "x" (core module $c))
100 )"#,
101 )?;
102 fail_wizening(
103 "does not currently support component start functions",
104 br#"(component
105 (import "f" (func $f))
106 (start $f)
107 )"#,
108 )?;
109
110 fail_wizening(
111 "modules may be instantiated at most once",
112 br#"(component
113 (core module $a)
114 (core instance $a1 (instantiate $a))
115 (core instance $a2 (instantiate $a))
116 )"#,
117 )?;
118
119 Ok(())
120 }
121
store() -> Result<Store<()>>122 fn store() -> Result<Store<()>> {
123 let engine = Engine::default();
124 Ok(Store::new(&engine, ()))
125 }
126
instantiate(store: &mut Store<()>, component: &Component) -> Result<Instance>127 async fn instantiate(store: &mut Store<()>, component: &Component) -> Result<Instance> {
128 let mut linker = Linker::new(store.engine());
129 linker.define_unknown_imports_as_traps(component)?;
130 linker.instantiate_async(store, component).await
131 }
132
wizen(wat: &str) -> Result<Vec<u8>>133 async fn wizen(wat: &str) -> Result<Vec<u8>> {
134 let _ = env_logger::try_init();
135 let wasm = wat::parse_str(wat)?;
136
137 log::debug!(
138 "=== PreWizened Wasm ==========================================================\n\
139 {}\n\
140 ===========================================================================",
141 wasmprinter::print_bytes(&wasm).unwrap()
142 );
143 let mut store = store()?;
144 let wasm = Wizer::new()
145 .run_component(&mut store, &wasm, instantiate)
146 .await?;
147 log::debug!(
148 "=== Wizened Wasm ==========================================================\n\
149 {}\n\
150 ===========================================================================",
151 wasmprinter::print_bytes(&wasm).unwrap()
152 );
153 if log::log_enabled!(log::Level::Debug) {
154 std::fs::write("test.wasm", &wasm).unwrap();
155 }
156
157 Ok(wasm)
158 }
159
wizen_and_run_wasm(expected: u32, wat: &str) -> Result<()>160 async fn wizen_and_run_wasm(expected: u32, wat: &str) -> Result<()> {
161 let wasm = wizen(wat).await?;
162
163 let mut store = store()?;
164 let module =
165 Component::new(store.engine(), wasm).context("Wasm test case failed to compile")?;
166
167 let linker = Linker::new(store.engine());
168 let instance = linker.instantiate_async(&mut store, &module).await?;
169
170 let run = instance.get_func(&mut store, "run").ok_or_else(|| {
171 wasmtime::format_err!("the test Wasm component does not export a `run` function")
172 })?;
173
174 let mut actual = [Val::U8(0)];
175 run.call_async(&mut store, &[], &mut actual).await?;
176 let actual = match actual[0] {
177 Val::U32(x) => x,
178 _ => wasmtime::bail!("expected an u32 result"),
179 };
180 wasmtime::ensure!(
181 expected == actual,
182 "expected `{expected}`, found `{actual}`",
183 );
184
185 Ok(())
186 }
187
188 #[tokio::test]
simple() -> Result<()>189 async fn simple() -> Result<()> {
190 wizen_and_run_wasm(
191 42,
192 r#"(component
193 (core module $m
194 (func (export "init"))
195
196 (func (export "run") (result i32)
197 i32.const 42
198 )
199 )
200 (core instance $i (instantiate $m))
201 (func (export "run") (result u32) (canon lift (core func $i "run")))
202 (func (export "wizer-initialize") (canon lift (core func $i "init")))
203 )"#,
204 )
205 .await?;
206
207 Ok(())
208 }
209
210 #[tokio::test]
snapshot_global_i32() -> Result<()>211 async fn snapshot_global_i32() -> Result<()> {
212 wizen_and_run_wasm(
213 1,
214 r#"(component
215 (core module $m
216 (global $g (mut i32) i32.const 0)
217 (func (export "init") (global.set $g (i32.const 1)))
218 (func (export "run") (result i32) global.get $g)
219 )
220 (core instance $i (instantiate $m))
221 (func (export "run") (result u32) (canon lift (core func $i "run")))
222 (func (export "wizer-initialize") (canon lift (core func $i "init")))
223 )"#,
224 )
225 .await?;
226
227 Ok(())
228 }
229
230 #[tokio::test]
snapshot_global_i64() -> Result<()>231 async fn snapshot_global_i64() -> Result<()> {
232 wizen_and_run_wasm(
233 1,
234 r#"(component
235 (core module $m
236 (global $g (mut i64) i64.const 0)
237 (func (export "init") (global.set $g (i64.const 1)))
238 (func (export "run") (result i32)
239 global.get $g
240 i32.wrap_i64
241 )
242 )
243 (core instance $i (instantiate $m))
244 (func (export "run") (result u32) (canon lift (core func $i "run")))
245 (func (export "wizer-initialize") (canon lift (core func $i "init")))
246 )"#,
247 )
248 .await?;
249
250 Ok(())
251 }
252
253 #[tokio::test]
snapshot_global_f32() -> Result<()>254 async fn snapshot_global_f32() -> Result<()> {
255 wizen_and_run_wasm(
256 1,
257 r#"(component
258 (core module $m
259 (global $g (mut f32) f32.const 0)
260 (func (export "init") (global.set $g (f32.const 1)))
261 (func (export "run") (result i32)
262 global.get $g
263 i32.trunc_f32_s)
264 )
265 (core instance $i (instantiate $m))
266 (func (export "run") (result u32) (canon lift (core func $i "run")))
267 (func (export "wizer-initialize") (canon lift (core func $i "init")))
268 )"#,
269 )
270 .await?;
271
272 Ok(())
273 }
274
275 #[tokio::test]
snapshot_global_f64() -> Result<()>276 async fn snapshot_global_f64() -> Result<()> {
277 wizen_and_run_wasm(
278 1,
279 r#"(component
280 (core module $m
281 (global $g (mut f64) f64.const 0)
282 (func (export "init") (global.set $g (f64.const 1)))
283 (func (export "run") (result i32)
284 global.get $g
285 i32.trunc_f64_s)
286 )
287 (core instance $i (instantiate $m))
288 (func (export "run") (result u32) (canon lift (core func $i "run")))
289 (func (export "wizer-initialize") (canon lift (core func $i "init")))
290 )"#,
291 )
292 .await?;
293
294 Ok(())
295 }
296
297 #[test]
v128_globals() -> Result<()>298 fn v128_globals() -> Result<()> {
299 fail_wizening(
300 "component wizening does not support v128 globals",
301 br#"(component
302 (core module $a
303 (global (export "x") (mut v128) (v128.const i32x4 1 2 3 4))
304 )
305 (core instance (instantiate $a))
306 )"#,
307 )
308 }
309
310 #[tokio::test]
snapshot_memory() -> Result<()>311 async fn snapshot_memory() -> Result<()> {
312 wizen_and_run_wasm(
313 201,
314 r#"(component
315 (core module $m
316 (memory 1)
317 (func (export "init")
318 i32.const 200
319 i32.const 100
320 i32.store
321 i32.const 300
322 i32.const 101
323 i32.store
324 )
325 (func (export "run") (result i32)
326 i32.const 200
327 i32.load
328 i32.const 300
329 i32.load
330 i32.add
331 )
332 )
333 (core instance $i (instantiate $m))
334 (func (export "run") (result u32) (canon lift (core func $i "run")))
335 (func (export "wizer-initialize") (canon lift (core func $i "init")))
336 )"#,
337 )
338 .await?;
339
340 Ok(())
341 }
342
343 #[tokio::test]
nested_components() -> Result<()>344 async fn nested_components() -> Result<()> {
345 wizen_and_run_wasm(
346 42,
347 r#"(component
348 (component $a)
349 (instance (instantiate $a))
350 (instance (export "hi") (instantiate $a))
351
352 (component $b
353 (type $t string)
354 (import "x" (type (eq $t)))
355 (component $a)
356 (instance (instantiate $a))
357 (instance (export "hi") (instantiate $a))
358 )
359 (type $x string)
360 (instance (instantiate $b
361 (with "x" (type $x))
362 ))
363 (instance (export "hi2") (instantiate $b
364 (with "x" (type $x))
365 ))
366
367 (core module $m
368 (func (export "init"))
369 (func (export "run") (result i32) i32.const 42)
370 )
371 (core instance $i (instantiate $m))
372 (func (export "run") (result u32) (canon lift (core func $i "run")))
373 (func (export "wizer-initialize") (canon lift (core func $i "init")))
374 )"#,
375 )
376 .await?;
377
378 Ok(())
379 }
380
381 #[tokio::test]
multiple_modules() -> Result<()>382 async fn multiple_modules() -> Result<()> {
383 wizen_and_run_wasm(
384 100 + 101 + 200 + 201 + 7 + 112,
385 r#"(component
386 (core module $a
387 (memory 1)
388 (global $g (export "g") (mut i32) (i32.const 0))
389
390 (func (export "init")
391 i32.const 200
392 i32.const 100
393 i32.store
394 i32.const 300
395 i32.const 101
396 i32.store
397 )
398
399 (func (export "run") (result i32)
400 i32.const 200
401 i32.load
402 i32.const 300
403 i32.load
404 i32.add
405 global.get $g
406 i32.add
407 )
408 )
409 (core instance $a (instantiate $a))
410
411 (core module $b
412 (import "a" "g" (global $g (mut i32)))
413 (import "a" "init" (func $init))
414 (import "a" "run" (func $run (result i32)))
415 (memory (export "mem") 1)
416 (func (export "init")
417 call $init
418 i32.const 400
419 i32.const 200
420 i32.store
421 i32.const 500
422 i32.const 201
423 i32.store
424
425 i32.const 111
426 global.set $g
427 )
428 (func (export "run") (result i32)
429 i32.const 400
430 i32.load
431 i32.const 500
432 i32.load
433 i32.add
434 call $run
435 i32.add
436 )
437 )
438 (core instance $b (instantiate $b (with "a" (instance $a))))
439
440 (core module $c
441 (import "a" "g" (global $g (mut i32)))
442 (import "b" "init" (func $init))
443 (import "b" "run" (func $run (result i32)))
444 (import "b" "mem" (memory 1))
445
446 (func (export "init")
447 call $init
448
449 i32.const 1
450 memory.grow
451 i32.const -1
452 i32.eq
453 if unreachable end
454
455 i32.const 65536
456 i32.const 7
457 i32.store
458
459 ;; overwrite a#init with a different value, make sure this
460 ;; one is snapshot
461 i32.const 112
462 global.set $g
463 )
464 (func (export "run") (result i32)
465 i32.const 65536
466 i32.load
467 call $run
468 i32.add
469 )
470 )
471 (core instance $c (instantiate $c
472 (with "a" (instance $a))
473 (with "b" (instance $b))
474 ))
475
476 (func (export "run") (result u32) (canon lift (core func $c "run")))
477 (func (export "wizer-initialize") (canon lift (core func $c "init")))
478 )"#,
479 )
480 .await?;
481
482 Ok(())
483 }
484
485 #[tokio::test]
export_is_removed() -> Result<()>486 async fn export_is_removed() -> Result<()> {
487 let wasm = wizen(
488 r#"(component
489 (core module $a
490 (func (export "init"))
491 )
492 (core instance $a (instantiate $a))
493 (func $a (canon lift (core func $a "init")))
494 (export "wizer-initialize" (func $a))
495 )"#,
496 )
497 .await?;
498
499 let names = exports(&wasm);
500 assert!(names.is_empty());
501
502 let wasm = wizen(
503 r#"(component
504 (core module $a
505 (func (export "init"))
506 )
507 (core instance $a (instantiate $a))
508 (func $a (canon lift (core func $a "init")))
509 (export "other" (func $a))
510 (export "wizer-initialize" (func $a))
511 )"#,
512 )
513 .await?;
514 let names = exports(&wasm);
515 assert_eq!(names, ["other"]);
516
517 let wasm = wizen(
518 r#"(component
519 (core module $a
520 (func (export "init"))
521 )
522 (core instance $a (instantiate $a))
523 (func $a (canon lift (core func $a "init")))
524 (export "other1" (func $a))
525 (export "wizer-initialize" (func $a))
526 (export "other2" (func $a))
527 )"#,
528 )
529 .await?;
530 let names = exports(&wasm);
531 assert_eq!(names, ["other1", "other2"]);
532
533 let wasm = wizen(
534 r#"(component
535 (core module $a
536 (func (export "init"))
537 )
538 (core instance $a (instantiate $a))
539 (func $a (canon lift (core func $a "init")))
540 (export "other1" (func $a))
541 (export "other2" (func $a))
542 (export "wizer-initialize" (func $a))
543 )"#,
544 )
545 .await?;
546 let names = exports(&wasm);
547 assert_eq!(names, ["other1", "other2"]);
548
549 let wasm = wizen(
550 r#"(component
551 (core module $a
552 (func (export "init"))
553 )
554 (core instance $a (instantiate $a))
555 (func $a (canon lift (core func $a "init")))
556 (export "wizer-initialize" (func $a))
557 (export "other1" (func $a))
558 (export "other2" (func $a))
559 )"#,
560 )
561 .await?;
562 let names = exports(&wasm);
563 assert_eq!(names, ["other1", "other2"]);
564
565 let wasm = wizen(
566 r#"(component
567 (core module $a
568 (func (export "init"))
569 )
570 (core instance $a (instantiate $a))
571 (func $a (canon lift (core func $a "init")))
572 (export $x "other1" (func $a))
573 (export "wizer-initialize" (func $a))
574 (export "other2" (func $x))
575 )"#,
576 )
577 .await?;
578 let names = exports(&wasm);
579 assert_eq!(names, ["other1", "other2"]);
580
581 let wasm = wizen(
582 r#"(component
583 (import "x" (func))
584 (core module $a
585 (func (export "init"))
586 )
587 (core instance $a (instantiate $a))
588 (func $a (canon lift (core func $a "init")))
589 (export $x "other1" (func $a))
590 (export "wizer-initialize" (func $a))
591 (export "other2" (func $x))
592 )"#,
593 )
594 .await?;
595 let names = exports(&wasm);
596 assert_eq!(names, ["other1", "other2"]);
597
598 return Ok(());
599
600 fn exports(wasm: &[u8]) -> Vec<&str> {
601 wasmparser::Parser::new(0)
602 .parse_all(&wasm)
603 .filter_map(|r| r.ok())
604 .filter_map(|payload| match payload {
605 wasmparser::Payload::ComponentExportSection(s) => Some(s),
606 _ => None,
607 })
608 .flat_map(|section| section.into_iter().map(|e| e.unwrap().name.0))
609 .collect()
610 }
611 }
612
613 // For the time being, Wizer should _not_ remove the `_initialize` export if
614 // present, even though it will become unreachable by virtual of `start`
615 // functions being removed. That's because `wit-component` creates an alias to
616 // the `_initialize` function, which would require additional surgery to remove.
617 // Ideally, we'd have a general purpose, component-level dead-code-elimination
618 // tool to remove all unreachable code, but for now we accept a bit of
619 // redundancy.
620 #[tokio::test]
leave_wasip1_initialize() -> Result<()>621 async fn leave_wasip1_initialize() -> Result<()> {
622 wizen_and_run_wasm(
623 42,
624 r#"(component
625 (core module $m
626 (func (export "init"))
627
628 (func (export "run") (result i32)
629 i32.const 42
630 )
631
632 (func (export "_initialize"))
633 )
634 (core instance $i (instantiate $m))
635 (alias core export $i "_initialize" (core func $initialize))
636 (core module $shim
637 (import "" "_initialize" (func $initialize))
638 (start $initialize)
639 )
640 (core instance $shim (instantiate $shim (with "" (instance
641 (export "_initialize" (func $initialize))
642 ))))
643 (func (export "run") (result u32) (canon lift (core func $i "run")))
644 (func (export "wizer-initialize") (canon lift (core func $i "init")))
645 )"#,
646 )
647 .await?;
648
649 Ok(())
650 }
651