1 //! Dummy implementations of things that a Wasm module can import.
2 
3 use anyhow::Result;
4 use std::fmt::Write;
5 use wasmtime::*;
6 
7 /// Create a set of dummy functions/globals/etc for the given imports.
8 pub fn dummy_linker<'module, T>(store: &mut Store<T>, module: &Module) -> Result<Linker<T>> {
9     let mut linker = Linker::new(store.engine());
10     linker.allow_shadowing(true);
11     for import in module.imports() {
12         match import.name() {
13             Some(name) => {
14                 linker
15                     .define(import.module(), name, dummy_extern(store, import.ty())?)
16                     .unwrap();
17             }
18             None => match import.ty() {
19                 ExternType::Instance(ty) => {
20                     for ty in ty.exports() {
21                         linker
22                             .define(import.module(), ty.name(), dummy_extern(store, ty.ty())?)
23                             .unwrap();
24                     }
25                 }
26                 other => {
27                     linker
28                         .define_name(import.module(), dummy_extern(store, other)?)
29                         .unwrap();
30                 }
31             },
32         }
33     }
34     Ok(linker)
35 }
36 
37 /// Construct a dummy `Extern` from its type signature
38 pub fn dummy_extern<T>(store: &mut Store<T>, ty: ExternType) -> Result<Extern> {
39     Ok(match ty {
40         ExternType::Func(func_ty) => Extern::Func(dummy_func(store, func_ty)),
41         ExternType::Global(global_ty) => Extern::Global(dummy_global(store, global_ty)),
42         ExternType::Table(table_ty) => Extern::Table(dummy_table(store, table_ty)?),
43         ExternType::Memory(mem_ty) => Extern::Memory(dummy_memory(store, mem_ty)?),
44         ExternType::Instance(instance_ty) => Extern::Instance(dummy_instance(store, instance_ty)?),
45         ExternType::Module(module_ty) => Extern::Module(dummy_module(store.engine(), module_ty)),
46     })
47 }
48 
49 /// Construct a dummy function for the given function type
50 pub fn dummy_func<T>(store: &mut Store<T>, ty: FuncType) -> Func {
51     Func::new(store, ty.clone(), move |_, _, results| {
52         for (ret_ty, result) in ty.results().zip(results) {
53             *result = dummy_value(ret_ty);
54         }
55         Ok(())
56     })
57 }
58 
59 /// Construct a dummy value for the given value type.
60 pub fn dummy_value(val_ty: ValType) -> Val {
61     match val_ty {
62         ValType::I32 => Val::I32(0),
63         ValType::I64 => Val::I64(0),
64         ValType::F32 => Val::F32(0),
65         ValType::F64 => Val::F64(0),
66         ValType::V128 => Val::V128(0),
67         ValType::ExternRef => Val::ExternRef(None),
68         ValType::FuncRef => Val::FuncRef(None),
69     }
70 }
71 
72 /// Construct a sequence of dummy values for the given types.
73 pub fn dummy_values(val_tys: impl IntoIterator<Item = ValType>) -> Vec<Val> {
74     val_tys.into_iter().map(dummy_value).collect()
75 }
76 
77 /// Construct a dummy global for the given global type.
78 pub fn dummy_global<T>(store: &mut Store<T>, ty: GlobalType) -> Global {
79     let val = dummy_value(ty.content().clone());
80     Global::new(store, ty, val).unwrap()
81 }
82 
83 /// Construct a dummy table for the given table type.
84 pub fn dummy_table<T>(store: &mut Store<T>, ty: TableType) -> Result<Table> {
85     let init_val = dummy_value(ty.element().clone());
86     Table::new(store, ty, init_val)
87 }
88 
89 /// Construct a dummy memory for the given memory type.
90 pub fn dummy_memory<T>(store: &mut Store<T>, ty: MemoryType) -> Result<Memory> {
91     Memory::new(store, ty)
92 }
93 
94 /// Construct a dummy instance for the given instance type.
95 ///
96 /// This is done by using the expected type to generate a module on-the-fly
97 /// which we the instantiate.
98 pub fn dummy_instance<T>(store: &mut Store<T>, ty: InstanceType) -> Result<Instance> {
99     let mut wat = WatGenerator::new();
100     for ty in ty.exports() {
101         wat.export(&ty);
102     }
103     let module = Module::new(store.engine(), &wat.finish()).unwrap();
104     Instance::new(store, &module, &[])
105 }
106 
107 /// Construct a dummy module for the given module type.
108 ///
109 /// This is done by using the expected type to generate a module on-the-fly.
110 pub fn dummy_module(engine: &Engine, ty: ModuleType) -> Module {
111     let mut wat = WatGenerator::new();
112     for ty in ty.imports() {
113         wat.import(&ty);
114     }
115     for ty in ty.exports() {
116         wat.export(&ty);
117     }
118     Module::new(engine, &wat.finish()).unwrap()
119 }
120 
121 struct WatGenerator {
122     tmp: usize,
123     dst: String,
124 }
125 
126 impl WatGenerator {
127     fn new() -> WatGenerator {
128         WatGenerator {
129             tmp: 0,
130             dst: String::from("(module\n"),
131         }
132     }
133 
134     fn finish(mut self) -> String {
135         self.dst.push_str(")\n");
136         self.dst
137     }
138 
139     fn import(&mut self, ty: &ImportType<'_>) {
140         write!(self.dst, "(import ").unwrap();
141         self.str(ty.module());
142         write!(self.dst, " ").unwrap();
143         if let Some(field) = ty.name() {
144             self.str(field);
145             write!(self.dst, " ").unwrap();
146         }
147         self.item_ty(&ty.ty());
148         writeln!(self.dst, ")").unwrap();
149     }
150 
151     fn item_ty(&mut self, ty: &ExternType) {
152         match ty {
153             ExternType::Memory(mem) => {
154                 write!(
155                     self.dst,
156                     "(memory {} {})",
157                     mem.limits().min(),
158                     match mem.limits().max() {
159                         Some(max) => max.to_string(),
160                         None => String::new(),
161                     }
162                 )
163                 .unwrap();
164             }
165             ExternType::Table(table) => {
166                 write!(
167                     self.dst,
168                     "(table {} {} {})",
169                     table.limits().min(),
170                     match table.limits().max() {
171                         Some(max) => max.to_string(),
172                         None => String::new(),
173                     },
174                     wat_ty(table.element()),
175                 )
176                 .unwrap();
177             }
178             ExternType::Global(ty) => {
179                 if ty.mutability() == Mutability::Const {
180                     write!(self.dst, "(global {})", wat_ty(ty.content())).unwrap();
181                 } else {
182                     write!(self.dst, "(global (mut {}))", wat_ty(ty.content())).unwrap();
183                 }
184             }
185             ExternType::Func(ty) => {
186                 write!(self.dst, "(func ").unwrap();
187                 self.func_sig(ty);
188                 write!(self.dst, ")").unwrap();
189             }
190             ExternType::Instance(ty) => {
191                 writeln!(self.dst, "(instance").unwrap();
192                 for ty in ty.exports() {
193                     write!(self.dst, "(export ").unwrap();
194                     self.str(ty.name());
195                     write!(self.dst, " ").unwrap();
196                     self.item_ty(&ty.ty());
197                     writeln!(self.dst, ")").unwrap();
198                 }
199                 write!(self.dst, ")").unwrap();
200             }
201             ExternType::Module(ty) => {
202                 writeln!(self.dst, "(module").unwrap();
203                 for ty in ty.imports() {
204                     self.import(&ty);
205                     writeln!(self.dst, "").unwrap();
206                 }
207                 for ty in ty.exports() {
208                     write!(self.dst, "(export ").unwrap();
209                     self.str(ty.name());
210                     write!(self.dst, " ").unwrap();
211                     self.item_ty(&ty.ty());
212                     writeln!(self.dst, ")").unwrap();
213                 }
214                 write!(self.dst, ")").unwrap();
215             }
216         }
217     }
218 
219     fn export(&mut self, ty: &ExportType<'_>) {
220         let wat_name = format!("item{}", self.tmp);
221         self.tmp += 1;
222         let item_ty = ty.ty();
223         self.item(&wat_name, &item_ty);
224 
225         write!(self.dst, "(export ").unwrap();
226         self.str(ty.name());
227         write!(self.dst, " (").unwrap();
228         match item_ty {
229             ExternType::Memory(_) => write!(self.dst, "memory").unwrap(),
230             ExternType::Global(_) => write!(self.dst, "global").unwrap(),
231             ExternType::Func(_) => write!(self.dst, "func").unwrap(),
232             ExternType::Instance(_) => write!(self.dst, "instance").unwrap(),
233             ExternType::Table(_) => write!(self.dst, "table").unwrap(),
234             ExternType::Module(_) => write!(self.dst, "module").unwrap(),
235         }
236         writeln!(self.dst, " ${}))", wat_name).unwrap();
237     }
238 
239     fn item(&mut self, name: &str, ty: &ExternType) {
240         match ty {
241             ExternType::Memory(mem) => {
242                 write!(
243                     self.dst,
244                     "(memory ${} {} {})\n",
245                     name,
246                     mem.limits().min(),
247                     match mem.limits().max() {
248                         Some(max) => max.to_string(),
249                         None => String::new(),
250                     }
251                 )
252                 .unwrap();
253             }
254             ExternType::Table(table) => {
255                 write!(
256                     self.dst,
257                     "(table ${} {} {} {})\n",
258                     name,
259                     table.limits().min(),
260                     match table.limits().max() {
261                         Some(max) => max.to_string(),
262                         None => String::new(),
263                     },
264                     wat_ty(table.element()),
265                 )
266                 .unwrap();
267             }
268             ExternType::Global(ty) => {
269                 write!(self.dst, "(global ${} ", name).unwrap();
270                 if ty.mutability() == Mutability::Var {
271                     write!(self.dst, "(mut ").unwrap();
272                 }
273                 write!(self.dst, "{}", wat_ty(ty.content())).unwrap();
274                 if ty.mutability() == Mutability::Var {
275                     write!(self.dst, ")").unwrap();
276                 }
277                 write!(self.dst, " (").unwrap();
278                 self.value(ty.content());
279                 writeln!(self.dst, "))").unwrap();
280             }
281             ExternType::Func(ty) => {
282                 write!(self.dst, "(func ${} ", name).unwrap();
283                 self.func_sig(ty);
284                 for ty in ty.results() {
285                     writeln!(self.dst, "").unwrap();
286                     self.value(&ty);
287                 }
288                 writeln!(self.dst, ")").unwrap();
289             }
290             ExternType::Module(ty) => {
291                 writeln!(self.dst, "(module ${}", name).unwrap();
292                 for ty in ty.imports() {
293                     self.import(&ty);
294                 }
295                 for ty in ty.exports() {
296                     self.export(&ty);
297                 }
298                 self.dst.push_str(")\n");
299             }
300             ExternType::Instance(ty) => {
301                 writeln!(self.dst, "(module ${}_module", name).unwrap();
302                 for ty in ty.exports() {
303                     self.export(&ty);
304                 }
305                 self.dst.push_str(")\n");
306                 writeln!(self.dst, "(instance ${} (instantiate ${0}_module))", name).unwrap();
307             }
308         }
309     }
310 
311     fn func_sig(&mut self, ty: &FuncType) {
312         write!(self.dst, "(param ").unwrap();
313         for ty in ty.params() {
314             write!(self.dst, "{} ", wat_ty(&ty)).unwrap();
315         }
316         write!(self.dst, ") (result ").unwrap();
317         for ty in ty.results() {
318             write!(self.dst, "{} ", wat_ty(&ty)).unwrap();
319         }
320         write!(self.dst, ")").unwrap();
321     }
322 
323     fn value(&mut self, ty: &ValType) {
324         match ty {
325             ValType::I32 => write!(self.dst, "i32.const 0").unwrap(),
326             ValType::I64 => write!(self.dst, "i64.const 0").unwrap(),
327             ValType::F32 => write!(self.dst, "f32.const 0").unwrap(),
328             ValType::F64 => write!(self.dst, "f64.const 0").unwrap(),
329             ValType::V128 => write!(self.dst, "v128.const i32x4 0 0 0 0").unwrap(),
330             ValType::ExternRef => write!(self.dst, "ref.null extern").unwrap(),
331             ValType::FuncRef => write!(self.dst, "ref.null func").unwrap(),
332         }
333     }
334 
335     fn str(&mut self, name: &str) {
336         let mut bytes = [0; 4];
337         self.dst.push_str("\"");
338         for c in name.chars() {
339             let v = c as u32;
340             if v >= 0x20 && v < 0x7f && c != '"' && c != '\\' && v < 0xff {
341                 self.dst.push(c);
342             } else {
343                 for byte in c.encode_utf8(&mut bytes).as_bytes() {
344                     self.hex_byte(*byte);
345                 }
346             }
347         }
348         self.dst.push_str("\"");
349     }
350 
351     fn hex_byte(&mut self, byte: u8) {
352         fn to_hex(b: u8) -> char {
353             if b < 10 {
354                 (b'0' + b) as char
355             } else {
356                 (b'a' + b - 10) as char
357             }
358         }
359         self.dst.push('\\');
360         self.dst.push(to_hex((byte >> 4) & 0xf));
361         self.dst.push(to_hex(byte & 0xf));
362     }
363 }
364 
365 fn wat_ty(ty: &ValType) -> &'static str {
366     match ty {
367         ValType::I32 => "i32",
368         ValType::I64 => "i64",
369         ValType::F32 => "f32",
370         ValType::F64 => "f64",
371         ValType::V128 => "v128",
372         ValType::ExternRef => "externref",
373         ValType::FuncRef => "funcref",
374     }
375 }
376 
377 #[cfg(test)]
378 mod tests {
379     use super::*;
380     use std::collections::HashSet;
381 
382     fn store() -> Store<()> {
383         let mut config = Config::default();
384         config.wasm_module_linking(true);
385         config.wasm_multi_memory(true);
386         let engine = wasmtime::Engine::new(&config).unwrap();
387         Store::new(&engine, ())
388     }
389 
390     #[test]
391     fn dummy_table_import() {
392         let mut store = store();
393         let table = dummy_table(
394             &mut store,
395             TableType::new(ValType::ExternRef, Limits::at_least(10)),
396         )
397         .unwrap();
398         assert_eq!(table.size(&store), 10);
399         for i in 0..10 {
400             assert!(table
401                 .get(&mut store, i)
402                 .unwrap()
403                 .unwrap_externref()
404                 .is_none());
405         }
406     }
407 
408     #[test]
409     fn dummy_global_import() {
410         let mut store = store();
411         let global = dummy_global(&mut store, GlobalType::new(ValType::I32, Mutability::Const));
412         assert_eq!(*global.ty(&store).content(), ValType::I32);
413         assert_eq!(global.ty(&store).mutability(), Mutability::Const);
414     }
415 
416     #[test]
417     fn dummy_memory_import() {
418         let mut store = store();
419         let memory = dummy_memory(&mut store, MemoryType::new(Limits::at_least(1))).unwrap();
420         assert_eq!(memory.size(&store), 1);
421     }
422 
423     #[test]
424     fn dummy_function_import() {
425         let mut store = store();
426         let func_ty = FuncType::new(vec![ValType::I32], vec![ValType::I64]);
427         let func = dummy_func(&mut store, func_ty.clone());
428         assert_eq!(func.ty(&store), func_ty);
429     }
430 
431     #[test]
432     fn dummy_instance_import() {
433         let mut store = store();
434 
435         let mut instance_ty = InstanceType::new();
436 
437         // Functions.
438         instance_ty.add_named_export("func0", FuncType::new(vec![ValType::I32], vec![]).into());
439         instance_ty.add_named_export("func1", FuncType::new(vec![], vec![ValType::I64]).into());
440 
441         // Globals.
442         instance_ty.add_named_export(
443             "global0",
444             GlobalType::new(ValType::I32, Mutability::Const).into(),
445         );
446         instance_ty.add_named_export(
447             "global1",
448             GlobalType::new(ValType::I64, Mutability::Var).into(),
449         );
450 
451         // Tables.
452         instance_ty.add_named_export(
453             "table0",
454             TableType::new(ValType::ExternRef, Limits::at_least(1)).into(),
455         );
456         instance_ty.add_named_export(
457             "table1",
458             TableType::new(ValType::ExternRef, Limits::at_least(1)).into(),
459         );
460 
461         // Memories.
462         instance_ty.add_named_export("memory0", MemoryType::new(Limits::at_least(1)).into());
463         instance_ty.add_named_export("memory1", MemoryType::new(Limits::at_least(1)).into());
464 
465         // Modules.
466         instance_ty.add_named_export("module0", ModuleType::new().into());
467         instance_ty.add_named_export("module1", ModuleType::new().into());
468 
469         // Instances.
470         instance_ty.add_named_export("instance0", InstanceType::new().into());
471         instance_ty.add_named_export("instance1", InstanceType::new().into());
472 
473         let instance = dummy_instance(&mut store, instance_ty.clone()).unwrap();
474 
475         let mut expected_exports = vec![
476             "func0",
477             "func1",
478             "global0",
479             "global1",
480             "table0",
481             "table1",
482             "memory0",
483             "memory1",
484             "module0",
485             "module1",
486             "instance0",
487             "instance1",
488         ]
489         .into_iter()
490         .collect::<HashSet<_>>();
491         for exp in instance.ty(&store).exports() {
492             let was_expected = expected_exports.remove(exp.name());
493             assert!(was_expected);
494         }
495         assert!(expected_exports.is_empty());
496     }
497 
498     #[test]
499     fn dummy_module_import() {
500         let store = store();
501 
502         let mut module_ty = ModuleType::new();
503 
504         // Multiple exported and imported functions.
505         module_ty.add_named_export("func0", FuncType::new(vec![ValType::I32], vec![]).into());
506         module_ty.add_named_export("func1", FuncType::new(vec![], vec![ValType::I64]).into());
507         module_ty.add_named_import(
508             "func2",
509             None,
510             FuncType::new(vec![ValType::I64], vec![]).into(),
511         );
512         module_ty.add_named_import(
513             "func3",
514             None,
515             FuncType::new(vec![], vec![ValType::I32]).into(),
516         );
517 
518         // Multiple exported and imported globals.
519         module_ty.add_named_export(
520             "global0",
521             GlobalType::new(ValType::I32, Mutability::Const).into(),
522         );
523         module_ty.add_named_export(
524             "global1",
525             GlobalType::new(ValType::I64, Mutability::Var).into(),
526         );
527         module_ty.add_named_import(
528             "global2",
529             None,
530             GlobalType::new(ValType::I32, Mutability::Var).into(),
531         );
532         module_ty.add_named_import(
533             "global3",
534             None,
535             GlobalType::new(ValType::I64, Mutability::Const).into(),
536         );
537 
538         // Multiple exported and imported tables.
539         module_ty.add_named_export(
540             "table0",
541             TableType::new(ValType::ExternRef, Limits::at_least(1)).into(),
542         );
543         module_ty.add_named_export(
544             "table1",
545             TableType::new(ValType::ExternRef, Limits::at_least(1)).into(),
546         );
547         module_ty.add_named_import(
548             "table2",
549             None,
550             TableType::new(ValType::ExternRef, Limits::at_least(1)).into(),
551         );
552         module_ty.add_named_import(
553             "table3",
554             None,
555             TableType::new(ValType::ExternRef, Limits::at_least(1)).into(),
556         );
557 
558         // Multiple exported and imported memories.
559         module_ty.add_named_export("memory0", MemoryType::new(Limits::at_least(1)).into());
560         module_ty.add_named_export("memory1", MemoryType::new(Limits::at_least(1)).into());
561         module_ty.add_named_import("memory2", None, MemoryType::new(Limits::at_least(1)).into());
562         module_ty.add_named_import("memory3", None, MemoryType::new(Limits::at_least(1)).into());
563 
564         // An exported and an imported module.
565         module_ty.add_named_export("module0", ModuleType::new().into());
566         module_ty.add_named_import("module1", None, ModuleType::new().into());
567 
568         // An exported and an imported instance.
569         module_ty.add_named_export("instance0", InstanceType::new().into());
570         module_ty.add_named_import("instance1", None, InstanceType::new().into());
571 
572         // Create the module.
573         let module = dummy_module(store.engine(), module_ty);
574 
575         // Check that we have the expected exports.
576         assert!(module.get_export("func0").is_some());
577         assert!(module.get_export("func1").is_some());
578         assert!(module.get_export("global0").is_some());
579         assert!(module.get_export("global1").is_some());
580         assert!(module.get_export("table0").is_some());
581         assert!(module.get_export("table1").is_some());
582         assert!(module.get_export("memory0").is_some());
583         assert!(module.get_export("memory1").is_some());
584         assert!(module.get_export("instance0").is_some());
585         assert!(module.get_export("module0").is_some());
586 
587         // Check that we have the exported imports.
588         let mut expected_imports = vec![
589             "func2",
590             "func3",
591             "global2",
592             "global3",
593             "table2",
594             "table3",
595             "memory2",
596             "memory3",
597             "instance1",
598             "module1",
599         ]
600         .into_iter()
601         .collect::<HashSet<_>>();
602         for imp in module.imports() {
603             assert!(imp.name().is_none());
604             let was_expected = expected_imports.remove(imp.module());
605             assert!(was_expected);
606         }
607         assert!(expected_imports.is_empty());
608     }
609 }
610