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