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