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