1 //! Evaluate an exported Wasm function using Wasmtime.
2 
3 use crate::generators::{self, DiffValue};
4 use crate::oracles::engine::DiffInstance;
5 use crate::oracles::{compile_module, engine::DiffEngine, instantiate_with_dummy, StoreLimits};
6 use anyhow::{Context, Result};
7 use std::hash::Hash;
8 use std::slice;
9 use wasmtime::{AsContextMut, Extern, FuncType, Instance, Module, Store, Val};
10 
11 /// A wrapper for using Wasmtime as a [`DiffEngine`].
12 pub struct WasmtimeEngine {
13     pub(crate) config: generators::Config,
14 }
15 
16 impl WasmtimeEngine {
17     /// Merely store the configuration; the engine is actually constructed
18     /// later. Ideally the store and engine could be built here but
19     /// `compile_module` takes a [`generators::Config`]; TODO re-factor this if
20     /// that ever changes.
21     pub fn new(config: &generators::Config) -> Result<Box<Self>> {
22         Ok(Box::new(Self {
23             config: config.clone(),
24         }))
25     }
26 }
27 
28 impl DiffEngine for WasmtimeEngine {
29     fn name(&self) -> &'static str {
30         "wasmtime"
31     }
32 
33     fn instantiate(&self, wasm: &[u8]) -> Result<Box<dyn DiffInstance>> {
34         let store = self.config.to_store();
35         let module = compile_module(store.engine(), wasm, true, &self.config).unwrap();
36         let instance = WasmtimeInstance::new(store, module)?;
37         Ok(Box::new(instance))
38     }
39 }
40 
41 /// A wrapper around a Wasmtime instance.
42 ///
43 /// The Wasmtime engine constructs a new store and compiles an instance of a
44 /// Wasm module.
45 pub struct WasmtimeInstance {
46     store: Store<StoreLimits>,
47     instance: Instance,
48 }
49 
50 impl WasmtimeInstance {
51     /// Instantiate a new Wasmtime instance.
52     pub fn new(mut store: Store<StoreLimits>, module: Module) -> Result<Self> {
53         let instance = instantiate_with_dummy(&mut store, &module)
54             .context("unable to instantiate module in wasmtime")?;
55         Ok(Self { store, instance })
56     }
57 
58     /// Retrieve the names and types of all exported functions in the instance.
59     ///
60     /// This is useful for evaluating each exported function with different
61     /// values. The [`DiffInstance`] trait asks for the function name and we
62     /// need to know the function signature in order to pass in the right
63     /// arguments.
64     pub fn exported_functions(&mut self) -> Vec<(String, FuncType)> {
65         let exported_functions = self
66             .instance
67             .exports(&mut self.store)
68             .map(|e| (e.name().to_owned(), e.into_func()))
69             .filter_map(|(n, f)| f.map(|f| (n, f)))
70             .collect::<Vec<_>>();
71         exported_functions
72             .into_iter()
73             .map(|(n, f)| (n, f.ty(&self.store)))
74             .collect()
75     }
76 }
77 
78 impl DiffInstance for WasmtimeInstance {
79     fn name(&self) -> &'static str {
80         "wasmtime"
81     }
82 
83     fn evaluate(&mut self, function_name: &str, arguments: &[DiffValue]) -> Result<Vec<DiffValue>> {
84         let arguments: Vec<_> = arguments.iter().map(Val::from).collect();
85 
86         let function = self
87             .instance
88             .get_func(&mut self.store, function_name)
89             .expect("unable to access exported function");
90         let ty = function.ty(&self.store);
91         let mut results = vec![Val::I32(0); ty.results().len()];
92         function.call(&mut self.store, &arguments, &mut results)?;
93 
94         let results = results.into_iter().map(Val::into).collect();
95         Ok(results)
96     }
97 
98     fn is_hashable(&self) -> bool {
99         true
100     }
101 
102     fn hash(&mut self, state: &mut std::collections::hash_map::DefaultHasher) -> Result<()> {
103         let exports: Vec<_> = self
104             .instance
105             .exports(self.store.as_context_mut())
106             .map(|e| e.into_extern())
107             .collect();
108         for e in exports {
109             match e {
110                 Extern::Global(g) => {
111                     let val: DiffValue = g.get(&mut self.store).into();
112                     val.hash(state)
113                 }
114                 Extern::Memory(m) => {
115                     let data = m.data(&mut self.store);
116                     data.hash(state)
117                 }
118                 Extern::SharedMemory(m) => {
119                     let data = unsafe { slice::from_raw_parts(m.data() as *mut u8, m.data_size()) };
120                     data.hash(state)
121                 }
122                 Extern::Table(_) => {
123                     // TODO: it's unclear whether it is worth it to iterate
124                     // through the table and hash the values.
125                 }
126                 Extern::Func(_) => {
127                     // Note: no need to hash exported functions.
128                 }
129             }
130         }
131         Ok(())
132     }
133 }
134 
135 impl From<&DiffValue> for Val {
136     fn from(v: &DiffValue) -> Self {
137         match *v {
138             DiffValue::I32(n) => Val::I32(n),
139             DiffValue::I64(n) => Val::I64(n),
140             DiffValue::F32(n) => Val::F32(n),
141             DiffValue::F64(n) => Val::F64(n),
142             DiffValue::V128(n) => Val::V128(n),
143         }
144     }
145 }
146 
147 impl Into<DiffValue> for Val {
148     fn into(self) -> DiffValue {
149         match self {
150             Val::I32(n) => DiffValue::I32(n),
151             Val::I64(n) => DiffValue::I64(n),
152             Val::F32(n) => DiffValue::F32(n),
153             Val::F64(n) => DiffValue::F64(n),
154             Val::V128(n) => DiffValue::V128(n),
155             Val::FuncRef(_) => unimplemented!(),
156             Val::ExternRef(_) => unimplemented!(),
157         }
158     }
159 }
160