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