xref: /wasmtime-44.0.1/tests/all/module.rs (revision 25ea52ab)
1 use wasmtime::*;
2 
3 #[test]
4 fn checks_incompatible_target() -> Result<()> {
5     let mut target = target_lexicon::Triple::host();
6     target.operating_system = target_lexicon::OperatingSystem::Unknown;
7     match Module::new(
8         &Engine::new(Config::new().target(&target.to_string())?)?,
9         "(module)",
10     ) {
11         Ok(_) => unreachable!(),
12         Err(e) => assert!(
13             format!("{e:?}").contains("configuration does not match the host"),
14             "bad error: {e:?}"
15         ),
16     }
17 
18     Ok(())
19 }
20 
21 #[test]
22 fn caches_across_engines() {
23     let c = Config::new();
24 
25     let bytes = Module::new(&Engine::new(&c).unwrap(), "(module)")
26         .unwrap()
27         .serialize()
28         .unwrap();
29 
30     unsafe {
31         let res = Module::deserialize(&Engine::default(), &bytes);
32         assert!(res.is_ok());
33 
34         // differ in runtime settings
35         let res = Module::deserialize(
36             &Engine::new(Config::new().static_memory_maximum_size(0)).unwrap(),
37             &bytes,
38         );
39         assert!(res.is_err());
40 
41         // differ in wasm features enabled (which can affect
42         // runtime/compilation settings)
43         let res = Module::deserialize(
44             &Engine::new(Config::new().wasm_threads(false)).unwrap(),
45             &bytes,
46         );
47         assert!(res.is_err());
48     }
49 }
50 
51 #[test]
52 #[cfg_attr(miri, ignore)]
53 fn aot_compiles() -> Result<()> {
54     let engine = Engine::default();
55     let bytes = engine.precompile_module(
56         "(module (func (export \"f\") (param i32) (result i32) local.get 0))".as_bytes(),
57     )?;
58 
59     let module = unsafe { Module::deserialize(&engine, &bytes)? };
60 
61     let mut store = Store::new(&engine, ());
62     let instance = Instance::new(&mut store, &module, &[])?;
63 
64     let f = instance.get_typed_func::<i32, i32>(&mut store, "f")?;
65     assert_eq!(f.call(&mut store, 101)?, 101);
66 
67     Ok(())
68 }
69 
70 #[test]
71 #[cfg_attr(miri, ignore)]
72 fn serialize_deterministic() {
73     let engine = Engine::default();
74 
75     let assert_deterministic = |wasm: &str| {
76         let p1 = engine.precompile_module(wasm.as_bytes()).unwrap();
77         let p2 = engine.precompile_module(wasm.as_bytes()).unwrap();
78         if p1 != p2 {
79             panic!("precompile_module not deterministic for:\n{wasm}");
80         }
81 
82         let module1 = Module::new(&engine, wasm).unwrap();
83         let a1 = module1.serialize().unwrap();
84         let a2 = module1.serialize().unwrap();
85         if a1 != a2 {
86             panic!("Module::serialize not deterministic for:\n{wasm}");
87         }
88 
89         let module2 = Module::new(&engine, wasm).unwrap();
90         let b1 = module2.serialize().unwrap();
91         let b2 = module2.serialize().unwrap();
92         if b1 != b2 {
93             panic!("Module::serialize not deterministic for:\n{wasm}");
94         }
95 
96         if a1 != b2 {
97             panic!("not matching across modules:\n{wasm}");
98         }
99         if b1 != p2 {
100             panic!("not matching across engine/module:\n{wasm}");
101         }
102     };
103 
104     assert_deterministic("(module)");
105     assert_deterministic("(module (func))");
106     assert_deterministic("(module (func nop))");
107     assert_deterministic("(module (func) (func (param i32)))");
108     assert_deterministic("(module (func (export \"f\")) (func (export \"y\")))");
109     assert_deterministic("(module (func $f) (func $g))");
110     assert_deterministic("(module (data \"\") (data \"\"))");
111     assert_deterministic("(module (elem func) (elem func))");
112 }
113 
114 // This test asserts that the optimization to transform separate data segments
115 // into an initialization image doesn't unnecessarily create a massive module by
116 // accident with a very large initialization image in it.
117 #[test]
118 fn serialize_not_overly_massive() -> Result<()> {
119     let mut config = Config::new();
120     config.memory_guaranteed_dense_image_size(1 << 20);
121     let engine = Engine::new(&config)?;
122 
123     let assert_smaller_than_1mb = |module: &str| -> Result<()> {
124         println!("{module}");
125         let bytes = Module::new(&engine, module)?.serialize()?;
126         assert!(bytes.len() < (1 << 20));
127         Ok(())
128     };
129 
130     // Tons of space between data segments should use sparse initialization,
131     // along with various permutations of empty and nonempty segments.
132     assert_smaller_than_1mb(
133         r#"(module
134             (memory 20000)
135             (data (i32.const 0) "a")
136             (data (i32.const 0x200000) "b")
137         )"#,
138     )?;
139     assert_smaller_than_1mb(
140         r#"(module
141             (memory 20000)
142             (data (i32.const 0) "a")
143             (data (i32.const 0x200000) "")
144         )"#,
145     )?;
146     assert_smaller_than_1mb(
147         r#"(module
148             (memory 20000)
149             (data (i32.const 0) "")
150             (data (i32.const 0x200000) "b")
151         )"#,
152     )?;
153     assert_smaller_than_1mb(
154         r#"(module
155             (memory 20000)
156             (data (i32.const 0) "")
157             (data (i32.const 0x200000) "")
158         )"#,
159     )?;
160 
161     // lone data segment
162     assert_smaller_than_1mb(
163         r#"(module
164             (memory 20000)
165             (data (i32.const 0x200000) "b")
166         )"#,
167     )?;
168 
169     Ok(())
170 }
171 
172 // This test specifically disables SSE4.1 in Cranelift which force wasm
173 // instructions like `f32.ceil` to go through libcalls instead of using native
174 // instructions. Note that SIMD is also disabled here because SIMD otherwise
175 // requires SSE4.1 to be enabled.
176 //
177 // This test then also tests that loading modules through various means, e.g.
178 // through precompiled artifacts, all works.
179 #[test]
180 #[cfg_attr(any(not(target_arch = "x86_64"), miri), ignore)]
181 fn missing_sse_and_floats_still_works() -> Result<()> {
182     let mut config = Config::new();
183     config.wasm_simd(false).wasm_relaxed_simd(false);
184     unsafe {
185         config.cranelift_flag_set("has_sse41", "false");
186     }
187     let engine = Engine::new(&config)?;
188     let module = Module::new(
189         &engine,
190         r#"
191             (module
192                 (func (export "f32.ceil") (param f32) (result f32)
193                     local.get 0
194                     f32.ceil)
195             )
196         "#,
197     )?;
198     let bytes = module.serialize()?;
199     let module2 = unsafe { Module::deserialize(&engine, &bytes)? };
200     let tmpdir = tempfile::TempDir::new()?;
201     let path = tmpdir.path().join("module.cwasm");
202     std::fs::write(&path, &bytes)?;
203     let module3 = unsafe { Module::deserialize_file(&engine, &path)? };
204 
205     for module in [module, module2, module3] {
206         let mut store = Store::new(&engine, ());
207         let instance = Instance::new(&mut store, &module, &[])?;
208         let ceil = instance.get_typed_func::<f32, f32>(&mut store, "f32.ceil")?;
209 
210         for f in [1.0, 2.3, -1.3] {
211             assert_eq!(ceil.call(&mut store, f)?, f.ceil());
212         }
213     }
214 
215     Ok(())
216 }
217 
218 #[test]
219 #[cfg_attr(miri, ignore)]
220 fn large_add_chain_no_stack_overflow() -> Result<()> {
221     let mut config = Config::new();
222     config.cranelift_opt_level(OptLevel::None);
223     let engine = Engine::new(&config)?;
224     let mut wat = String::from(
225         "
226         (module
227             (func (result i64)
228                 (i64.const 1)
229         ",
230     );
231     for _ in 0..20_000 {
232         wat.push_str("(i64.add (i64.const 1))\n");
233     }
234 
235     wat.push_str(")\n)");
236     Module::new(&engine, &wat)?;
237 
238     Ok(())
239 }
240 
241 #[test]
242 fn compile_a_component() -> Result<()> {
243     let engine = Engine::default();
244     let err = Module::new(&engine, "(component)").unwrap_err();
245     let err = format!("{err:?}");
246     assert!(
247         err.contains("expected a WebAssembly module but was given a WebAssembly component"),
248         "bad error: {err}"
249     );
250     Ok(())
251 }
252 
253 #[test]
254 fn tail_call_defaults() -> Result<()> {
255     let wasm_with_tail_calls = "(module (func $a return_call $a))";
256 
257     // on by default
258     Module::new(&Engine::default(), wasm_with_tail_calls)?;
259 
260     // on by default for cranelift
261     Module::new(
262         &Engine::new(Config::new().strategy(Strategy::Cranelift))?,
263         wasm_with_tail_calls,
264     )?;
265 
266     if cfg!(target_arch = "x86_64") {
267         // off by default for winch
268         let err = Module::new(
269             &Engine::new(Config::new().strategy(Strategy::Winch))?,
270             wasm_with_tail_calls,
271         );
272         assert!(err.is_err());
273     }
274     Ok(())
275 }
276 
277 #[test]
278 fn cross_engine_module_exports() -> Result<()> {
279     let a_engine = Engine::default();
280     let b_engine = Engine::default();
281 
282     let a_module = Module::new(&a_engine, "(module)")?;
283     let b_module = Module::new(
284         &b_engine,
285         r#"
286             (module
287                 (func (export "x"))
288             )
289         "#,
290     )?;
291 
292     let export = b_module.get_export_index("x").unwrap();
293 
294     let mut store = Store::new(&a_engine, ());
295     let instance = Instance::new(&mut store, &a_module, &[])?;
296     assert!(instance.get_module_export(&mut store, &export).is_none());
297     Ok(())
298 }
299