use anyhow::{bail, Result}; use std::fs; use wasmtime::*; fn serialize(engine: &Engine, wat: &str) -> Result> { let module = Module::new(&engine, wat)?; Ok(module.serialize()?) } unsafe fn deserialize_and_instantiate(store: &mut Store<()>, buffer: &[u8]) -> Result { let module = Module::deserialize(store.engine(), buffer)?; Ok(Instance::new(store, &module, &[])?) } #[test] fn test_version_mismatch() -> Result<()> { let engine = Engine::default(); let buffer = serialize(&engine, "(module)")?; let mut config = Config::new(); config .module_version(ModuleVersionStrategy::Custom("custom!".to_owned())) .unwrap(); let custom_version_engine = Engine::new(&config).unwrap(); match unsafe { Module::deserialize(&custom_version_engine, &buffer) } { Ok(_) => bail!("expected deserialization to fail"), Err(e) => assert!(e .to_string() .starts_with("Module was compiled with incompatible version")), } let mut config = Config::new(); config.module_version(ModuleVersionStrategy::None).unwrap(); let none_version_engine = Engine::new(&config).unwrap(); unsafe { Module::deserialize(&none_version_engine, &buffer) } .expect("accepts the wasmtime versioned module"); let buffer = serialize(&custom_version_engine, "(module)")?; unsafe { Module::deserialize(&none_version_engine, &buffer) } .expect("accepts the custom versioned module"); Ok(()) } #[test] #[cfg_attr(miri, ignore)] fn test_module_serialize_simple() -> Result<()> { let buffer = serialize( &Engine::default(), "(module (func (export \"run\") (result i32) i32.const 42))", )?; let mut store = Store::default(); let instance = unsafe { deserialize_and_instantiate(&mut store, &buffer)? }; let run = instance.get_typed_func::<(), i32>(&mut store, "run")?; let result = run.call(&mut store, ())?; assert_eq!(42, result); Ok(()) } #[test] #[cfg_attr(miri, ignore)] fn test_module_serialize_fail() -> Result<()> { let buffer = serialize( &Engine::default(), "(module (func (export \"run\") (result i32) i32.const 42))", )?; let mut config = Config::new(); config.static_memory_maximum_size(0); let mut store = Store::new(&Engine::new(&config)?, ()); match unsafe { deserialize_and_instantiate(&mut store, &buffer) } { Ok(_) => bail!("expected failure at deserialization"), Err(_) => (), } Ok(()) } #[test] #[cfg_attr(miri, ignore)] fn test_deserialize_from_file() -> Result<()> { serialize_and_call("(module (func (export \"run\") (result i32) i32.const 42))")?; serialize_and_call( "(module (func (export \"run\") (result i32) call $answer) (func $answer (result i32) i32.const 42)) ", )?; return Ok(()); fn serialize_and_call(wat: &str) -> Result<()> { let mut store = Store::<()>::default(); let td = tempfile::TempDir::new()?; let buffer = serialize(store.engine(), wat)?; let path = td.path().join("module.bin"); fs::write(&path, &buffer)?; let module = unsafe { Module::deserialize_file(store.engine(), &path)? }; let instance = Instance::new(&mut store, &module, &[])?; let func = instance.get_typed_func::<(), i32>(&mut store, "run")?; assert_eq!(func.call(&mut store, ())?, 42); Ok(()) } } #[test] #[cfg_attr(miri, ignore)] fn deserialize_from_serialized() -> Result<()> { let engine = Engine::default(); let buffer1 = serialize( &engine, "(module (func (export \"run\") (result i32) i32.const 42))", )?; let buffer2 = unsafe { Module::deserialize(&engine, &buffer1)?.serialize()? }; assert!(buffer1 == buffer2); Ok(()) }