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