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