1 //! Evaluate an exported Wasm function using Wasmtime. 2 3 use crate::generators::{self, DiffValue, DiffValueType}; 4 use crate::oracles::dummy; 5 use crate::oracles::engine::DiffInstance; 6 use crate::oracles::{compile_module, engine::DiffEngine, StoreLimits}; 7 use anyhow::{Context, Error, Result}; 8 use wasmtime::{Extern, FuncType, Instance, Module, Store, Trap, TrapCode, Val}; 9 10 /// A wrapper for using Wasmtime as a [`DiffEngine`]. 11 pub struct WasmtimeEngine { 12 pub(crate) config: generators::Config, 13 } 14 15 impl WasmtimeEngine { 16 /// Merely store the configuration; the engine is actually constructed 17 /// later. Ideally the store and engine could be built here but 18 /// `compile_module` takes a [`generators::Config`]; TODO re-factor this if 19 /// that ever changes. 20 pub fn new(config: generators::Config) -> Result<Self> { 21 Ok(Self { config }) 22 } 23 } 24 25 impl DiffEngine for WasmtimeEngine { 26 fn name(&self) -> &'static str { 27 "wasmtime" 28 } 29 30 fn instantiate(&mut self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> { 31 let store = self.config.to_store(); 32 let module = compile_module(store.engine(), wasm, true, &self.config).unwrap(); 33 let instance = WasmtimeInstance::new(store, module)?; 34 Ok(Box::new(instance)) 35 } 36 37 fn assert_error_match(&self, trap: &Trap, err: &Error) { 38 let trap2 = err 39 .downcast_ref::<Trap>() 40 .expect(&format!("not a trap: {:?}", err)); 41 assert_eq!( 42 trap.trap_code(), 43 trap2.trap_code(), 44 "{}\nis not equal to\n{}", 45 trap, 46 trap2 47 ); 48 } 49 50 fn is_stack_overflow(&self, err: &Error) -> bool { 51 match err.downcast_ref::<Trap>() { 52 Some(trap) => trap.trap_code() == Some(TrapCode::StackOverflow), 53 None => false, 54 } 55 } 56 } 57 58 /// A wrapper around a Wasmtime instance. 59 /// 60 /// The Wasmtime engine constructs a new store and compiles an instance of a 61 /// Wasm module. 62 pub struct WasmtimeInstance { 63 store: Store<StoreLimits>, 64 instance: Instance, 65 } 66 67 impl WasmtimeInstance { 68 /// Instantiate a new Wasmtime instance. 69 pub fn new(mut store: Store<StoreLimits>, module: Module) -> Result<Self> { 70 let instance = dummy::dummy_linker(&mut store, &module) 71 .and_then(|l| l.instantiate(&mut store, &module)) 72 .context("unable to instantiate module in wasmtime")?; 73 Ok(Self { store, instance }) 74 } 75 76 /// Retrieve the names and types of all exported functions in the instance. 77 /// 78 /// This is useful for evaluating each exported function with different 79 /// values. The [`DiffInstance`] trait asks for the function name and we 80 /// need to know the function signature in order to pass in the right 81 /// arguments. 82 pub fn exported_functions(&mut self) -> Vec<(String, FuncType)> { 83 let exported_functions = self 84 .instance 85 .exports(&mut self.store) 86 .map(|e| (e.name().to_owned(), e.into_func())) 87 .filter_map(|(n, f)| f.map(|f| (n, f))) 88 .collect::<Vec<_>>(); 89 exported_functions 90 .into_iter() 91 .map(|(n, f)| (n, f.ty(&self.store))) 92 .collect() 93 } 94 95 /// Returns the list of globals and their types exported from this instance. 96 pub fn exported_globals(&mut self) -> Vec<(String, DiffValueType)> { 97 let globals = self 98 .instance 99 .exports(&mut self.store) 100 .filter_map(|e| { 101 let name = e.name(); 102 e.into_global().map(|g| (name.to_string(), g)) 103 }) 104 .collect::<Vec<_>>(); 105 106 globals 107 .into_iter() 108 .map(|(name, global)| { 109 ( 110 name, 111 global.ty(&self.store).content().clone().try_into().unwrap(), 112 ) 113 }) 114 .collect() 115 } 116 117 /// Returns the list of exported memories and whether or not it's a shared 118 /// memory. 119 pub fn exported_memories(&mut self) -> Vec<(String, bool)> { 120 self.instance 121 .exports(&mut self.store) 122 .filter_map(|e| { 123 let name = e.name(); 124 match e.into_extern() { 125 Extern::Memory(_) => Some((name.to_string(), false)), 126 Extern::SharedMemory(_) => Some((name.to_string(), true)), 127 _ => None, 128 } 129 }) 130 .collect() 131 } 132 } 133 134 impl DiffInstance for WasmtimeInstance { 135 fn name(&self) -> &'static str { 136 "wasmtime" 137 } 138 139 fn evaluate( 140 &mut self, 141 function_name: &str, 142 arguments: &[DiffValue], 143 _results: &[DiffValueType], 144 ) -> Result<Option<Vec<DiffValue>>> { 145 let arguments: Vec<_> = arguments.iter().map(Val::from).collect(); 146 147 let function = self 148 .instance 149 .get_func(&mut self.store, function_name) 150 .expect("unable to access exported function"); 151 let ty = function.ty(&self.store); 152 let mut results = vec![Val::I32(0); ty.results().len()]; 153 function.call(&mut self.store, &arguments, &mut results)?; 154 155 let results = results.into_iter().map(Val::into).collect(); 156 Ok(Some(results)) 157 } 158 159 fn get_global(&mut self, name: &str, _ty: DiffValueType) -> Option<DiffValue> { 160 Some( 161 self.instance 162 .get_global(&mut self.store, name) 163 .unwrap() 164 .get(&mut self.store) 165 .into(), 166 ) 167 } 168 169 fn get_memory(&mut self, name: &str, shared: bool) -> Option<Vec<u8>> { 170 Some(if shared { 171 let data = self 172 .instance 173 .get_shared_memory(&mut self.store, name) 174 .unwrap() 175 .data(); 176 unsafe { (*data).to_vec() } 177 } else { 178 self.instance 179 .get_memory(&mut self.store, name) 180 .unwrap() 181 .data(&self.store) 182 .to_vec() 183 }) 184 } 185 } 186 187 impl From<&DiffValue> for Val { 188 fn from(v: &DiffValue) -> Self { 189 match *v { 190 DiffValue::I32(n) => Val::I32(n), 191 DiffValue::I64(n) => Val::I64(n), 192 DiffValue::F32(n) => Val::F32(n), 193 DiffValue::F64(n) => Val::F64(n), 194 DiffValue::V128(n) => Val::V128(n), 195 DiffValue::FuncRef { null } => { 196 assert!(null); 197 Val::FuncRef(None) 198 } 199 DiffValue::ExternRef { null } => { 200 assert!(null); 201 Val::ExternRef(None) 202 } 203 } 204 } 205 } 206 207 impl Into<DiffValue> for Val { 208 fn into(self) -> DiffValue { 209 match self { 210 Val::I32(n) => DiffValue::I32(n), 211 Val::I64(n) => DiffValue::I64(n), 212 Val::F32(n) => DiffValue::F32(n), 213 Val::F64(n) => DiffValue::F64(n), 214 Val::V128(n) => DiffValue::V128(n), 215 Val::FuncRef(f) => DiffValue::FuncRef { null: f.is_none() }, 216 Val::ExternRef(e) => DiffValue::ExternRef { null: e.is_none() }, 217 } 218 } 219 } 220 221 #[test] 222 fn smoke() { 223 crate::oracles::engine::smoke_test_engine(|config| WasmtimeEngine::new(config)) 224 } 225