1 //! Evaluate an exported Wasm function using the wasmi interpreter. 2 3 use crate::generators::{DiffValue, DiffValueType, ModuleConfig}; 4 use crate::oracles::engine::{DiffEngine, DiffInstance}; 5 use anyhow::{bail, Context, Error, Result}; 6 use wasmtime::Trap; 7 8 /// A wrapper for `wasmi` as a [`DiffEngine`]. 9 pub struct WasmiEngine; 10 11 impl WasmiEngine { 12 /// Build a new [`WasmiEngine`] but only if the configuration does not rely 13 /// on features that `wasmi` does not support. 14 pub fn new(config: &ModuleConfig) -> Result<Self> { 15 if config.config.reference_types_enabled { 16 bail!("wasmi does not support reference types") 17 } 18 if config.config.simd_enabled { 19 bail!("wasmi does not support SIMD") 20 } 21 if config.config.multi_value_enabled { 22 bail!("wasmi does not support multi-value") 23 } 24 if config.config.saturating_float_to_int_enabled { 25 bail!("wasmi does not support saturating float-to-int conversions") 26 } 27 if config.config.sign_extension_enabled { 28 bail!("wasmi does not support sign-extension") 29 } 30 if config.config.memory64_enabled { 31 bail!("wasmi does not support memory64"); 32 } 33 if config.config.bulk_memory_enabled { 34 bail!("wasmi does not support bulk memory"); 35 } 36 if config.config.threads_enabled { 37 bail!("wasmi does not support threads"); 38 } 39 Ok(Self) 40 } 41 } 42 43 impl DiffEngine for WasmiEngine { 44 fn name(&self) -> &'static str { 45 "wasmi" 46 } 47 48 fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> { 49 let module = wasmi::Module::from_buffer(wasm).context("unable to validate Wasm module")?; 50 let instance = wasmi::ModuleInstance::new(&module, &wasmi::ImportsBuilder::default()) 51 .context("unable to instantiate module in wasmi")?; 52 let instance = instance.assert_no_start(); 53 Ok(Box::new(WasmiInstance { module, instance })) 54 } 55 56 fn assert_error_match(&self, trap: &Trap, err: Error) { 57 // TODO: should implement this for `wasmi` 58 drop((trap, err)); 59 } 60 } 61 62 /// A wrapper for `wasmi` Wasm instances. 63 struct WasmiInstance { 64 #[allow(dead_code)] // reason = "the module must live as long as its reference" 65 module: wasmi::Module, 66 instance: wasmi::ModuleRef, 67 } 68 69 impl DiffInstance for WasmiInstance { 70 fn name(&self) -> &'static str { 71 "wasmi" 72 } 73 74 fn evaluate( 75 &mut self, 76 function_name: &str, 77 arguments: &[DiffValue], 78 _results: &[DiffValueType], 79 ) -> Result<Option<Vec<DiffValue>>> { 80 let arguments: Vec<_> = arguments.iter().map(wasmi::RuntimeValue::from).collect(); 81 let export = self 82 .instance 83 .export_by_name(function_name) 84 .context(format!( 85 "unable to find function '{}' in wasmi instance", 86 function_name 87 ))?; 88 let function = export.as_func().context("wasmi export is not a function")?; 89 let result = wasmi::FuncInstance::invoke(&function, &arguments, &mut wasmi::NopExternals) 90 .context("failed while invoking function in wasmi")?; 91 Ok(Some(if let Some(result) = result { 92 vec![result.into()] 93 } else { 94 vec![] 95 })) 96 } 97 98 fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> { 99 match self.instance.export_by_name(name) { 100 Some(wasmi::ExternVal::Global(g)) => Some(g.get().into()), 101 _ => unreachable!(), 102 } 103 } 104 105 fn get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>> { 106 assert!(!shared); 107 match self.instance.export_by_name(name) { 108 Some(wasmi::ExternVal::Memory(m)) => { 109 // `wasmi` memory may be stored non-contiguously; copy 110 // it out to a contiguous chunk. 111 let mut buffer: Vec<u8> = vec![0; m.current_size().0 * 65536]; 112 m.get_into(0, &mut buffer[..]) 113 .expect("can access wasmi memory"); 114 Some(buffer) 115 } 116 _ => unreachable!(), 117 } 118 } 119 } 120 121 impl From<&DiffValue> for wasmi::RuntimeValue { 122 fn from(v: &DiffValue) -> Self { 123 use wasmi::RuntimeValue::*; 124 match *v { 125 DiffValue::I32(n) => I32(n), 126 DiffValue::I64(n) => I64(n), 127 DiffValue::F32(n) => F32(wasmi::nan_preserving_float::F32::from_bits(n)), 128 DiffValue::F64(n) => F64(wasmi::nan_preserving_float::F64::from_bits(n)), 129 DiffValue::V128(_) | DiffValue::FuncRef { .. } | DiffValue::ExternRef { .. } => { 130 unimplemented!() 131 } 132 } 133 } 134 } 135 136 impl Into<DiffValue> for wasmi::RuntimeValue { 137 fn into(self) -> DiffValue { 138 use wasmi::RuntimeValue::*; 139 match self { 140 I32(n) => DiffValue::I32(n), 141 I64(n) => DiffValue::I64(n), 142 F32(n) => DiffValue::F32(n.to_bits()), 143 F64(n) => DiffValue::F64(n.to_bits()), 144 } 145 } 146 } 147 148 #[test] 149 fn smoke() { 150 crate::oracles::engine::smoke_test_engine(|config| WasmiEngine::new(&config.module_config)) 151 } 152