wasm/execution/
mod.rs

1use crate::addrs::GlobalAddr;
2use crate::core::reader::types::global::GlobalType;
3use crate::resumable::{ResumableRef, RunState};
4use alloc::borrow::{Cow, ToOwned};
5use alloc::vec::Vec;
6
7use const_interpreter_loop::run_const_span;
8use function_ref::FunctionRef;
9use store::addrs::ModuleAddr;
10use store::ExternVal;
11use store::HaltExecutionError;
12use value_stack::Stack;
13
14use crate::core::reader::types::{FuncType, ResultType};
15use crate::execution::assert_validated::UnwrapValidatedExt;
16use crate::execution::config::Config;
17use crate::execution::store::Store;
18use crate::execution::value::Value;
19use crate::interop::InteropValueList;
20use crate::{RuntimeError, ValidationInfo};
21
22pub(crate) mod assert_validated;
23pub mod config;
24pub mod const_interpreter_loop;
25pub mod error;
26pub mod function_ref;
27pub mod interop;
28mod interpreter_loop;
29pub(crate) mod little_endian;
30pub mod registry;
31pub mod resumable;
32pub mod store;
33pub mod value;
34pub mod value_stack;
35
36/// The default module name if a [RuntimeInstance] was created using [RuntimeInstance::new].
37pub const DEFAULT_MODULE: &str = "__interpreter_default__";
38
39pub struct RuntimeInstance<'b, T: Config = ()> {
40    pub store: Store<'b, T>,
41}
42
43impl<T: Config + Default> Default for RuntimeInstance<'_, T> {
44    fn default() -> Self {
45        Self::new(T::default())
46    }
47}
48
49impl<'b, T: Config> RuntimeInstance<'b, T> {
50    pub fn new(user_data: T) -> Self {
51        RuntimeInstance {
52            store: Store::new(user_data),
53        }
54    }
55
56    // Returns the new [`RuntimeInstance`] and module addr of the default module.
57    pub fn new_with_default_module(
58        user_data: T,
59        validation_info: &'_ ValidationInfo<'b>,
60    ) -> Result<(Self, ModuleAddr), RuntimeError> {
61        let mut instance = Self::new(user_data);
62        let module_addr = instance.add_module(DEFAULT_MODULE, validation_info)?;
63        Ok((instance, module_addr))
64    }
65
66    // Returns the new [`RuntimeInstance`] and module addr of the new named module.
67    pub fn new_named(
68        user_data: T,
69        module_name: &str,
70        validation_info: &'_ ValidationInfo<'b>,
71        // store: &mut Store,
72    ) -> Result<(Self, ModuleAddr), RuntimeError> {
73        let mut instance = Self::new(user_data);
74        let module_addr = instance.add_module(module_name, validation_info)?;
75        Ok((instance, module_addr))
76    }
77
78    // Returns the module addr
79    pub fn add_module(
80        &mut self,
81        module_name: &str,
82        validation_info: &'_ ValidationInfo<'b>,
83    ) -> Result<ModuleAddr, RuntimeError> {
84        self.store.add_module(module_name, validation_info, None)
85    }
86
87    pub fn get_function_by_name(
88        &self,
89        module_name: &str,
90        function_name: &str,
91    ) -> Result<FunctionRef, RuntimeError> {
92        FunctionRef::new_from_name(module_name, function_name, &self.store)
93            .map_err(|_| RuntimeError::FunctionNotFound)
94    }
95
96    pub fn get_function_by_index(
97        &self,
98        module_addr: ModuleAddr,
99        function_idx: usize,
100    ) -> Result<FunctionRef, RuntimeError> {
101        let module_inst = self.store.modules.get(module_addr);
102        let func_addr = *module_inst
103            .func_addrs
104            .get(function_idx)
105            .ok_or(RuntimeError::FunctionNotFound)?;
106
107        Ok(FunctionRef { func_addr })
108    }
109
110    /// Invokes a function with the given parameters of type `Param`, and return types of type `Returns`.
111    pub fn invoke_typed<Params: InteropValueList, Returns: InteropValueList>(
112        &mut self,
113        function_ref: &FunctionRef,
114        params: Params,
115        // store: &mut Store,
116    ) -> Result<Returns, RuntimeError> {
117        self.invoke(function_ref, params.into_values())
118            .map(|values| Returns::try_from_values(values.into_iter()).unwrap_validated())
119    }
120
121    /// Invokes a function with the given parameters. The return types depend on the function signature.
122    pub fn invoke(
123        &mut self,
124        function_ref: &FunctionRef,
125        params: Vec<Value>,
126    ) -> Result<Vec<Value>, RuntimeError> {
127        let FunctionRef { func_addr } = *function_ref;
128        self.store
129            .invoke(func_addr, params, None)
130            .map(|run_state| match run_state {
131                RunState::Finished(values) => values,
132                _ => unreachable!("non metered invoke call"),
133            })
134    }
135
136    /// Creates a new resumable, which when resumed for the first time invokes the function `function_ref` is associated
137    /// to, with the arguments `params`. The newly created resumable initially stores `fuel` units of fuel. Returns a
138    /// `[ResumableRef]` associated to the newly created resumable on success.
139    pub fn create_resumable(
140        &self,
141        function_ref: &FunctionRef,
142        params: Vec<Value>,
143        fuel: u32,
144    ) -> Result<ResumableRef, RuntimeError> {
145        let FunctionRef { func_addr } = *function_ref;
146        self.store.create_resumable(func_addr, params, Some(fuel))
147    }
148
149    /// resumes the resumable associated to `resumable_ref`. Returns a `[RunState]` associated to this resumable  if the
150    /// resumable ran out of fuel or completely executed.
151    pub fn resume(&mut self, resumable_ref: ResumableRef) -> Result<RunState, RuntimeError> {
152        self.store.resume(resumable_ref)
153    }
154
155    /// calls its argument `f` with a mutable reference of the fuel of the respective [`ResumableRef`].
156    ///
157    /// Fuel is stored as an [`Option<u32>`], where `None` means that fuel is disabled and `Some(x)` means that `x` units of fuel is left.
158    /// A ubiquitious use of this method would be using `f` to read or mutate the current fuel amount of the respective [`ResumableRef`].
159    /// # Example
160    /// ```
161    /// use wasm::{resumable::RunState, validate, RuntimeInstance};
162    /// let wasm = [ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
163    ///             0x01, 0x04, 0x01, 0x60, 0x00, 0x00, 0x03, 0x02,
164    ///             0x01, 0x00, 0x07, 0x09, 0x01, 0x05, 0x6c, 0x6f,
165    ///             0x6f, 0x70, 0x73, 0x00, 0x00, 0x0a, 0x09, 0x01,
166    ///             0x07, 0x00, 0x03, 0x40, 0x0c, 0x00, 0x0b, 0x0b ];
167    /// // a simple module with a single function looping forever
168    /// let (mut instance, _module) = RuntimeInstance::new_named((), "module", &validate(&wasm).unwrap()).unwrap();
169    /// let func_ref = instance.get_function_by_name("module", "loops").unwrap();
170    /// let mut resumable_ref = instance.create_resumable(&func_ref, vec![], 0).unwrap();
171    /// instance.access_fuel_mut(&mut resumable_ref, |x| { assert_eq!(*x, Some(0)); *x = None; }).unwrap();
172    /// ```
173    pub fn access_fuel_mut<R>(
174        &mut self,
175        resumable_ref: &mut ResumableRef,
176        f: impl FnOnce(&mut Option<u32>) -> R,
177    ) -> Result<R, RuntimeError> {
178        self.store.access_fuel_mut(resumable_ref, f)
179    }
180
181    /// Adds a host function under module namespace `module_name` with name `name`.
182    /// roughly similar to `func_alloc` in <https://webassembly.github.io/spec/core/appendix/embedding.html#functions>
183    /// except the host function is made visible to other modules through these names.
184    pub fn add_host_function_typed<Params: InteropValueList, Returns: InteropValueList>(
185        &mut self,
186        module_name: &str,
187        name: &str,
188        host_func: fn(&mut T, Vec<Value>) -> Result<Vec<Value>, HaltExecutionError>,
189    ) -> Result<FunctionRef, RuntimeError> {
190        let host_func_ty = FuncType {
191            params: ResultType {
192                valtypes: Vec::from(Params::TYS),
193            },
194            returns: ResultType {
195                valtypes: Vec::from(Returns::TYS),
196            },
197        };
198        self.add_host_function(module_name, name, host_func_ty, host_func)
199    }
200
201    pub fn add_host_function(
202        &mut self,
203        module_name: &str,
204        name: &str,
205        host_func_ty: FuncType,
206        host_func: fn(&mut T, Vec<Value>) -> Result<Vec<Value>, HaltExecutionError>,
207    ) -> Result<FunctionRef, RuntimeError> {
208        let func_addr = self.store.func_alloc(host_func_ty, host_func);
209        self.store.registry.register(
210            module_name.to_owned().into(),
211            name.to_owned().into(),
212            store::ExternVal::Func(func_addr),
213        )?;
214        Ok(FunctionRef { func_addr })
215    }
216
217    pub fn user_data(&self) -> &T {
218        &self.store.user_data
219    }
220
221    pub fn user_data_mut(&mut self) -> &mut T {
222        &mut self.store.user_data
223    }
224
225    /// Returns the global type of some global instance by its addr.
226    pub fn global_type(&self, global_addr: GlobalAddr) -> GlobalType {
227        self.store.global_type(global_addr)
228    }
229
230    /// Returns the current value of some global instance by its addr.
231    pub fn global_read(&self, global_addr: GlobalAddr) -> Value {
232        self.store.global_read(global_addr)
233    }
234
235    /// Sets a new value of some global instance by its addr.
236    ///
237    /// # Errors
238    /// - [`RuntimeError::WriteOnImmutableGlobal`]
239    /// - [`RuntimeError::GlobalTypeMismatch`]
240    pub fn global_write(
241        &mut self,
242        global_addr: GlobalAddr,
243        val: Value,
244    ) -> Result<(), RuntimeError> {
245        self.store.global_write(global_addr, val)
246    }
247
248    /// Look-up an export by its name and the name of its exporting module.
249    pub fn lookup_export(
250        &self,
251        module_name: Cow<'static, str>,
252        name: Cow<'static, str>,
253    ) -> Result<ExternVal, RuntimeError> {
254        self.store.registry.lookup(module_name, name).copied()
255    }
256}
257
258/// Helper function to quickly construct host functions without worrying about wasm to Rust
259/// type conversion. For reading/writing user data into the current configuration, simply move
260/// `user_data` into the passed closure.
261/// # Example
262/// ```
263/// use wasm::{validate, RuntimeInstance, host_function_wrapper, Value, HaltExecutionError};
264/// fn my_wrapped_host_func(user_data: &mut (), params: Vec<Value>) -> Result<Vec<Value>, HaltExecutionError> {
265///     host_function_wrapper(params, |(x, y): (u32, i32)| -> Result<u32, HaltExecutionError> {
266///         let _user_data = user_data;
267///         Ok(x + (y as u32))
268///     })
269/// }
270/// fn main() {
271///     let mut instance = RuntimeInstance::new(());
272///     let foo_bar = instance.add_host_function_typed::<(u32,i32), u32>("foo", "bar", my_wrapped_host_func).unwrap();
273/// }
274/// ```
275pub fn host_function_wrapper<Params: InteropValueList, Results: InteropValueList>(
276    params: Vec<Value>,
277    f: impl FnOnce(Params) -> Result<Results, HaltExecutionError>,
278) -> Result<Vec<Value>, HaltExecutionError> {
279    let params =
280        Params::try_from_values(params.into_iter()).expect("Params match the actual parameters");
281    f(params).map(Results::into_values)
282}