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