use anyhow::{bail, Result}; use wasmtime::*; const MODULE: &'static str = r#" (module (import "" "" (func $add (param i32 i32) (result i32))) (func $test (result i32) (i32.const 42) ) (func $sum10 (param $arg_1 i32) (param $arg_2 i32) (param $arg_3 i32) (param $arg_4 i32) (param $arg_5 i32) (param $arg_6 i32) (param $arg_7 i32) (param $arg_8 i32) (param $arg_9 i32) (param $arg_10 i32) (result i32) local.get $arg_1 local.get $arg_2 i32.add local.get $arg_3 i32.add local.get $arg_4 i32.add local.get $arg_5 i32.add local.get $arg_6 i32.add local.get $arg_7 i32.add local.get $arg_8 i32.add local.get $arg_9 i32.add local.get $arg_10 i32.add) (func $call_add (param i32 i32) (result i32) (local.get 0) (local.get 1) (call $add)) (export "42" (func $test)) (export "sum10" (func $sum10)) (export "call_add" (func $call_add)) ) "#; fn add_fn(store: impl AsContextMut) -> Func { Func::wrap(store, |a: i32, b: i32| a + b) } #[test] #[cfg_attr(miri, ignore)] fn array_to_wasm() -> Result<()> { let mut c = Config::new(); c.strategy(Strategy::Winch); let engine = Engine::new(&c)?; let mut store = Store::new(&engine, ()); let module = Module::new(&engine, MODULE)?; let add_fn = add_fn(store.as_context_mut()); let instance = Instance::new(&mut store, &module, &[add_fn.into()])?; let constant = instance .get_func(&mut store, "42") .ok_or(anyhow::anyhow!("test function not found"))?; let mut returns = vec![Val::null(); 1]; constant.call(&mut store, &[], &mut returns)?; assert_eq!(returns.len(), 1); assert_eq!(returns[0].unwrap_i32(), 42); let sum = instance .get_func(&mut store, "sum10") .ok_or(anyhow::anyhow!("sum10 function not found"))?; let mut returns = vec![Val::null(); 1]; let args = vec![Val::I32(1); 10]; sum.call(&mut store, &args, &mut returns)?; assert_eq!(returns.len(), 1); assert_eq!(returns[0].unwrap_i32(), 10); Ok(()) } #[test] #[cfg_attr(miri, ignore)] fn native_to_wasm() -> Result<()> { let mut c = Config::new(); c.strategy(Strategy::Winch); let engine = Engine::new(&c)?; let mut store = Store::new(&engine, ()); let module = Module::new(&engine, MODULE)?; let add_fn = add_fn(store.as_context_mut()); let instance = Instance::new(&mut store, &module, &[add_fn.into()])?; let f = instance.get_typed_func::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32), i32>( &mut store, "sum10", )?; let args = (1, 1, 1, 1, 1, 1, 1, 1, 1, 1); let result = f.call(&mut store, args)?; assert_eq!(result, 10); Ok(()) } #[test] #[cfg_attr(miri, ignore)] fn wasm_to_native() -> Result<()> { let mut c = Config::new(); c.strategy(Strategy::Winch); let engine = Engine::new(&c)?; let mut store = Store::new(&engine, ()); let module = Module::new(&engine, MODULE)?; let add_fn = add_fn(store.as_context_mut()); let instance = Instance::new(&mut store, &module, &[add_fn.into()])?; let f = instance.get_typed_func::<(i32, i32), i32>(&mut store, "call_add")?; let args = (41, 1); let result = f.call(&mut store, args)?; assert_eq!(result, 42); Ok(()) } #[test] #[cfg_attr(miri, ignore)] #[cfg_attr(windows, ignore)] // NB // // This and the following test(`native_to_wasm_trap` and `wasm_to_native_trap`), // are mostly smoke tests to ensure Winch's trampolines are compliant with fast // stack walking. The ideal state is one in which we should not have to worry // about testing the backtrace implementation per compiler, but instead be // certain that a single set of test cases is enough to ensure that the machine // code generated by Winch and Cranelift is compliant. One way to achieve this // could be to share the implementation of trampolines between Cranelift and // Winch. // // FIXME The following two tests are also temporarily ignored on Windows, since // we are not emitting the require unwind information yet. fn native_to_wasm_trap() -> Result<()> { let mut c = Config::new(); c.strategy(Strategy::Winch); let engine = Engine::new(&c)?; let wat = r#" (module (func $div_by_zero (result i32) (i32.const 1) (i32.const 0) i32.div_u) (export "div_by_zero" (func $div_by_zero))) "#; let mut store = Store::new(&engine, ()); let module = Module::new(&engine, wat)?; let instance = Instance::new(&mut store, &module, &[])?; let f = instance.get_typed_func::<(), i32>(&mut store, "div_by_zero")?; let result = f.call(&mut store, ()).unwrap_err(); assert!(result.downcast_ref::().is_some()); Ok(()) } #[test] #[cfg_attr(miri, ignore)] #[cfg_attr(windows, ignore)] fn wasm_to_native_trap() -> Result<()> { let mut c = Config::new(); c.strategy(Strategy::Winch); let engine = Engine::new(&c)?; let wat = r#" (module (import "" "" (func $fail)) (func $call_fail call $fail) (export "call_fail" (func $call_fail))) "#; let mut store = Store::new(&engine, ()); let module = Module::new(&engine, wat)?; let func = Func::wrap::<(), (), Result<()>>(&mut store, || bail!("error")); let instance = Instance::new(&mut store, &module, &[func.into()])?; let f = instance.get_typed_func::<(), ()>(&mut store, "call_fail")?; let result = f.call(&mut store, ()).unwrap_err(); assert!(result.downcast_ref::().is_some()); Ok(()) }