wasm_runtime_layer/
backend.rs

1use alloc::{
2    boxed::Box,
3    string::{String, ToString},
4};
5use core::fmt;
6
7use anyhow::Result;
8use fxhash::FxBuildHasher;
9use hashbrown::HashMap;
10
11use crate::{
12    ExportType, ExternType, FuncType, GlobalType, ImportType, MemoryType, TableType, ValueType,
13};
14
15/// Runtime representation of a value.
16#[derive(Clone)]
17pub enum Value<E: WasmEngine> {
18    /// Value of 32-bit signed or unsigned integer.
19    I32(i32),
20    /// Value of 64-bit signed or unsigned integer.
21    I64(i64),
22    /// Value of 32-bit floating point number.
23    F32(f32),
24    /// Value of 64-bit floating point number.
25    F64(f64),
26    /// An optional function reference.
27    FuncRef(Option<E::Func>),
28    /// An optional external reference.
29    ExternRef(Option<E::ExternRef>),
30}
31
32impl<E: WasmEngine> Value<E> {
33    /// Returns the [`ValueType`] for this [`Value`].
34    #[must_use]
35    pub const fn ty(&self) -> ValueType {
36        match self {
37            Value::I32(_) => ValueType::I32,
38            Value::I64(_) => ValueType::I64,
39            Value::F32(_) => ValueType::F32,
40            Value::F64(_) => ValueType::F64,
41            Value::FuncRef(_) => ValueType::FuncRef,
42            Value::ExternRef(_) => ValueType::ExternRef,
43        }
44    }
45}
46
47impl<E: WasmEngine> fmt::Debug for Value<E>
48where
49    E::Func: fmt::Debug,
50    E::ExternRef: fmt::Debug,
51{
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        match self {
54            Value::I32(v) => f.debug_tuple("I32").field(v).finish(),
55            Value::I64(v) => f.debug_tuple("I64").field(v).finish(),
56            Value::F32(v) => f.debug_tuple("F32").field(v).finish(),
57            Value::F64(v) => f.debug_tuple("F64").field(v).finish(),
58            Value::FuncRef(v) => f.debug_tuple("FuncRef").field(v).finish(),
59            Value::ExternRef(v) => f.debug_tuple("ExternRef").field(v).finish(),
60        }
61    }
62}
63
64/// An external item to a WebAssembly module.
65///
66/// This is returned from [`Instance::exports`](crate::Instance::exports)
67/// or [`Instance::get_export`](crate::Instance::get_export).
68pub enum Extern<E: WasmEngine> {
69    /// A WebAssembly global which acts like a [`Cell<T>`] of sorts, supporting `get` and `set` operations.
70    ///
71    /// [`Cell<T>`]: https://doc.rust-lang.org/core/cell/struct.Cell.html
72    Global(E::Global),
73    /// A WebAssembly table which is an array of funtion references.
74    Table(E::Table),
75    /// A WebAssembly linear memory.
76    Memory(E::Memory),
77    /// A WebAssembly function which can be called.
78    Func(E::Func),
79}
80
81impl<E: WasmEngine> Extern<E> {
82    /// Returns the underlying global variable if `self` is a global variable.
83    ///
84    /// Returns `None` otherwise.
85    pub fn into_global(self) -> Option<E::Global> {
86        if let Self::Global(global) = self {
87            return Some(global);
88        }
89        None
90    }
91
92    /// Returns the underlying table if `self` is a table.
93    ///
94    /// Returns `None` otherwise.
95    pub fn into_table(self) -> Option<E::Table> {
96        if let Self::Table(table) = self {
97            return Some(table);
98        }
99        None
100    }
101
102    /// Returns the underlying linear memory if `self` is a linear memory.
103    ///
104    /// Returns `None` otherwise.
105    pub fn into_memory(self) -> Option<E::Memory> {
106        if let Self::Memory(memory) = self {
107            return Some(memory);
108        }
109        None
110    }
111
112    /// Returns the underlying function if `self` is a function.
113    ///
114    /// Returns `None` otherwise.
115    pub fn into_func(self) -> Option<E::Func> {
116        if let Self::Func(func) = self {
117            return Some(func);
118        }
119        None
120    }
121
122    /// Returns the type associated with this [`Extern`].
123    ///
124    /// # Panics
125    ///
126    /// If this item does not belong to the `store` provided.
127    pub fn ty(&self, ctx: impl AsContext<E>) -> ExternType {
128        match self {
129            Extern::Global(global) => global.ty(ctx).into(),
130            Extern::Table(table) => table.ty(ctx).into(),
131            Extern::Memory(memory) => memory.ty(ctx).into(),
132            Extern::Func(func) => func.ty(ctx).into(),
133        }
134    }
135}
136
137impl<E: WasmEngine> Clone for Extern<E> {
138    fn clone(&self) -> Self {
139        match self {
140            Self::Global(arg0) => Self::Global(arg0.clone()),
141            Self::Table(arg0) => Self::Table(arg0.clone()),
142            Self::Memory(arg0) => Self::Memory(arg0.clone()),
143            Self::Func(arg0) => Self::Func(arg0.clone()),
144        }
145    }
146}
147
148impl<E: WasmEngine> fmt::Debug for Extern<E>
149where
150    E::Global: fmt::Debug,
151    E::Func: fmt::Debug,
152    E::Memory: fmt::Debug,
153    E::Table: fmt::Debug,
154{
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        match self {
157            Self::Global(arg0) => f.debug_tuple("Global").field(arg0).finish(),
158            Self::Table(arg0) => f.debug_tuple("Table").field(arg0).finish(),
159            Self::Memory(arg0) => f.debug_tuple("Memory").field(arg0).finish(),
160            Self::Func(arg0) => f.debug_tuple("Func").field(arg0).finish(),
161        }
162    }
163}
164
165/// A descriptor for an exported WebAssembly value of an [`Instance`].
166///
167/// This type is primarily accessed from the [`Instance::exports`] method and describes
168/// what names are exported from a Wasm [`Instance`] and the type of the item that is exported.
169#[derive(Clone)]
170pub struct Export<E: WasmEngine> {
171    /// The name by which the export is known.
172    pub name: String,
173    /// The value of the exported item.
174    pub value: Extern<E>,
175}
176
177impl<E: WasmEngine> fmt::Debug for Export<E>
178where
179    Extern<E>: fmt::Debug,
180{
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        f.debug_struct("Export")
183            .field("name", &self.name)
184            .field("value", &self.value)
185            .finish()
186    }
187}
188
189/// All of the import data used when instantiating.
190#[derive(Clone)]
191pub struct Imports<E: WasmEngine> {
192    /// The inner list of external imports.
193    pub(crate) map: HashMap<(String, String), Extern<E>, FxBuildHasher>,
194}
195
196impl<E: WasmEngine> Imports<E> {
197    /// Create a new `Imports`.
198    pub fn new() -> Self {
199        Self {
200            map: HashMap::default(),
201        }
202    }
203
204    /// Gets an export given a module and a name
205    pub fn get_export(&self, module: &str, name: &str) -> Option<Extern<E>> {
206        if self.exists(module, name) {
207            let ext = &self.map[&(module.to_string(), name.to_string())];
208            return Some(ext.clone());
209        }
210        None
211    }
212
213    /// Returns if an export exist for a given module and name.
214    pub fn exists(&self, module: &str, name: &str) -> bool {
215        self.map
216            .contains_key(&(module.to_string(), name.to_string()))
217    }
218
219    /// Returns true if the Imports contains namespace with the provided name.
220    pub fn contains_namespace(&self, name: &str) -> bool {
221        self.map.keys().any(|(k, _)| (k == name))
222    }
223
224    /// Register a list of externs into a namespace.
225    pub fn register_namespace(
226        &mut self,
227        ns: &str,
228        contents: impl IntoIterator<Item = (String, Extern<E>)>,
229    ) {
230        for (name, extern_) in contents.into_iter() {
231            self.map.insert((ns.to_string(), name.clone()), extern_);
232        }
233    }
234
235    /// Add a single import with a namespace `ns` and name `name`.
236    pub fn define(&mut self, ns: &str, name: &str, val: impl Into<Extern<E>>) {
237        self.map
238            .insert((ns.to_string(), name.to_string()), val.into());
239    }
240
241    /// Iterates through all the imports in this structure
242    pub fn iter(&self) -> ImportsIterator<E> {
243        ImportsIterator::new(self)
244    }
245}
246
247/// An iterator over imports.
248pub struct ImportsIterator<'a, E: WasmEngine> {
249    /// The inner iterator over external items.
250    iter: hashbrown::hash_map::Iter<'a, (String, String), Extern<E>>,
251}
252
253impl<'a, E: WasmEngine> ImportsIterator<'a, E> {
254    /// Creates a new iterator over the imports of an instance.
255    fn new(imports: &'a Imports<E>) -> Self {
256        let iter = imports.map.iter();
257        Self { iter }
258    }
259}
260
261impl<'a, E: WasmEngine> Iterator for ImportsIterator<'a, E> {
262    type Item = (&'a str, &'a str, &'a Extern<E>);
263
264    fn next(&mut self) -> Option<Self::Item> {
265        self.iter
266            .next()
267            .map(|(k, v)| (k.0.as_str(), k.1.as_str(), v))
268    }
269}
270
271impl<E: WasmEngine> IntoIterator for &Imports<E> {
272    type IntoIter = hashbrown::hash_map::IntoIter<(String, String), Extern<E>>;
273    type Item = ((String, String), Extern<E>);
274
275    fn into_iter(self) -> Self::IntoIter {
276        self.map.clone().into_iter()
277    }
278}
279
280impl<E: WasmEngine> Default for Imports<E> {
281    fn default() -> Self {
282        Self::new()
283    }
284}
285
286impl<E: WasmEngine> Extend<((String, String), Extern<E>)> for Imports<E> {
287    fn extend<T: IntoIterator<Item = ((String, String), Extern<E>)>>(&mut self, iter: T) {
288        for ((ns, name), ext) in iter.into_iter() {
289            self.define(&ns, &name, ext);
290        }
291    }
292}
293
294impl<E: WasmEngine> fmt::Debug for Imports<E> {
295    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
296        /// Stores backing debug data.
297        enum SecretMap {
298            /// The empty variant.
299            Empty,
300            /// The filled index variant.
301            Some(usize),
302        }
303
304        impl SecretMap {
305            /// Creates a new secret map representation of the given size.
306            fn new(len: usize) -> Self {
307                if len == 0 {
308                    Self::Empty
309                } else {
310                    Self::Some(len)
311                }
312            }
313        }
314
315        impl fmt::Debug for SecretMap {
316            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
317                match self {
318                    Self::Empty => write!(f, "(empty)"),
319                    Self::Some(len) => write!(f, "(... {} item(s) ...)", len),
320                }
321            }
322        }
323
324        f.debug_struct("Imports")
325            .field("map", &SecretMap::new(self.map.len()))
326            .finish()
327    }
328}
329
330/// Provides a backing implementation for a WebAssembly runtime.
331pub trait WasmEngine: 'static + Clone + Sized {
332    /// The external reference type.
333    type ExternRef: WasmExternRef<Self>;
334    /// The function type.
335    type Func: WasmFunc<Self>;
336    /// The global type.
337    type Global: WasmGlobal<Self>;
338    /// The instance type.
339    type Instance: WasmInstance<Self>;
340    /// The memory type.
341    type Memory: WasmMemory<Self>;
342    /// The module type.
343    type Module: WasmModule<Self>;
344    /// The store type.
345    type Store<T>: WasmStore<T, Self>;
346    /// The store context type.
347    type StoreContext<'a, T: 'a>: WasmStoreContext<'a, T, Self>;
348    /// The mutable store context type.
349    type StoreContextMut<'a, T: 'a>: WasmStoreContextMut<'a, T, Self>;
350    /// The table type.
351    type Table: WasmTable<Self>;
352}
353
354/// Provides an opaque reference to any data within WebAssembly.
355pub trait WasmExternRef<E: WasmEngine>: Clone + Sized + Send + Sync {
356    /// Creates a new reference wrapping the given value.
357    fn new<T: 'static + Send + Sync>(ctx: impl AsContextMut<E>, object: T) -> Self;
358    /// Returns a shared reference to the underlying data.
359    fn downcast<'a, 's: 'a, T: 'static, S: 's>(
360        &'a self,
361        store: E::StoreContext<'s, S>,
362    ) -> Result<&'a T>;
363}
364
365/// Provides a Wasm or host function reference.
366pub trait WasmFunc<E: WasmEngine>: Clone + Sized + Send + Sync {
367    /// Creates a new function with the given arguments.
368    fn new<T>(
369        ctx: impl AsContextMut<E, UserState = T>,
370        ty: FuncType,
371        func: impl 'static
372            + Send
373            + Sync
374            + Fn(E::StoreContextMut<'_, T>, &[Value<E>], &mut [Value<E>]) -> Result<()>,
375    ) -> Self;
376    /// Gets the function type of this object.
377    fn ty(&self, ctx: impl AsContext<E>) -> FuncType;
378    /// Calls the object with the given arguments.
379    fn call<T>(
380        &self,
381        ctx: impl AsContextMut<E>,
382        args: &[Value<E>],
383        results: &mut [Value<E>],
384    ) -> Result<()>;
385}
386
387/// Provides a Wasm global variable reference.
388pub trait WasmGlobal<E: WasmEngine>: Clone + Sized + Send + Sync {
389    /// Creates a new global variable to the store.
390    fn new(ctx: impl AsContextMut<E>, value: Value<E>, mutable: bool) -> Self;
391    /// Returns the type of the global variable.
392    fn ty(&self, ctx: impl AsContext<E>) -> GlobalType;
393    /// Sets the value of the global variable.
394    fn set(&self, ctx: impl AsContextMut<E>, new_value: Value<E>) -> Result<()>;
395    /// Gets the value of the global variable.
396    fn get(&self, ctx: impl AsContextMut<E>) -> Value<E>;
397}
398
399/// Provides a Wasm linear memory reference.
400pub trait WasmMemory<E: WasmEngine>: Clone + Sized + Send + Sync {
401    /// Creates a new linear memory to the store.
402    fn new(ctx: impl AsContextMut<E>, ty: MemoryType) -> Result<Self>;
403    /// Returns the memory type of the linear memory.
404    fn ty(&self, ctx: impl AsContext<E>) -> MemoryType;
405    /// Grows the linear memory by the given amount of new pages.
406    fn grow(&self, ctx: impl AsContextMut<E>, additional: u32) -> Result<u32>;
407    /// Returns the amount of pages in use by the linear memory.
408    fn current_pages(&self, ctx: impl AsContext<E>) -> u32;
409    /// Reads `n` bytes from `memory[offset..offset+n]` into `buffer`
410    /// where `n` is the length of `buffer`.
411    fn read(&self, ctx: impl AsContext<E>, offset: usize, buffer: &mut [u8]) -> Result<()>;
412    /// Writes `n` bytes to `memory[offset..offset+n]` from `buffer`
413    /// where `n` if the length of `buffer`.
414    fn write(&self, ctx: impl AsContextMut<E>, offset: usize, buffer: &[u8]) -> Result<()>;
415}
416
417/// Provides a Wasm table reference.
418pub trait WasmTable<E: WasmEngine>: Clone + Sized + Send + Sync {
419    /// Creates a new table to the store.
420    fn new(ctx: impl AsContextMut<E>, ty: TableType, init: Value<E>) -> Result<Self>;
421    /// Returns the type and limits of the table.
422    fn ty(&self, ctx: impl AsContext<E>) -> TableType;
423    /// Returns the current size of the table.
424    fn size(&self, ctx: impl AsContext<E>) -> u32;
425    /// Grows the table by the given amount of elements.
426    fn grow(&self, ctx: impl AsContextMut<E>, delta: u32, init: Value<E>) -> Result<u32>;
427    /// Returns the table element value at `index`.
428    fn get(&self, ctx: impl AsContextMut<E>, index: u32) -> Option<Value<E>>;
429    /// Sets the value of this table at `index`.
430    fn set(&self, ctx: impl AsContextMut<E>, index: u32, value: Value<E>) -> Result<()>;
431}
432
433/// Provides an instantiated WASM module.
434pub trait WasmInstance<E: WasmEngine>: Clone + Sized + Send + Sync {
435    /// Creates a new instance.
436    fn new(store: impl AsContextMut<E>, module: &E::Module, imports: &Imports<E>) -> Result<Self>;
437    /// Gets the exports of this instance.
438    fn exports(&self, store: impl AsContext<E>) -> Box<dyn Iterator<Item = Export<E>>>;
439    /// Gets the export of the given name, if any, from this instance.
440    fn get_export(&self, store: impl AsContext<E>, name: &str) -> Option<Extern<E>>;
441}
442
443/// Provides a parsed and validated WASM module.
444pub trait WasmModule<E: WasmEngine>: Clone + Sized + Send + Sync {
445    /// Creates a new module from the given byte slice.
446    fn new(engine: &E, bytes: &[u8]) -> Result<Self>;
447    /// Gets the export types of the module.
448    fn exports(&self) -> Box<dyn '_ + Iterator<Item = ExportType<'_>>>;
449    /// Gets the export type of the given name, if any, from this module.
450    fn get_export(&self, name: &str) -> Option<ExternType>;
451    /// Gets the import types of the module.
452    fn imports(&self) -> Box<dyn '_ + Iterator<Item = ImportType<'_>>>;
453}
454
455/// Provides all of the global state that can be manipulated by WASM programs.
456pub trait WasmStore<T, E: WasmEngine>:
457    AsContext<E, UserState = T> + AsContextMut<E, UserState = T>
458{
459    /// Creates a new store atop the given engine.
460    fn new(engine: &E, data: T) -> Self;
461    /// Gets the engine associated with this store.
462    fn engine(&self) -> &E;
463    /// Gets an immutable reference to the underlying stored data.
464    fn data(&self) -> &T;
465    /// Gets a mutable reference to the underlying stored data.
466    fn data_mut(&mut self) -> &mut T;
467    /// Consumes `self` and returns its user provided data.
468    fn into_data(self) -> T;
469}
470
471/// Provides a temporary immutable handle to a store.
472pub trait WasmStoreContext<'a, T, E: WasmEngine>: AsContext<E, UserState = T> {
473    /// Gets the engine associated with this store.
474    fn engine(&self) -> &E;
475    /// Gets an immutable reference to the underlying stored data.
476    fn data(&self) -> &T;
477}
478
479/// Provides a temporary mutable handle to a store.
480pub trait WasmStoreContextMut<'a, T, E: WasmEngine>:
481    WasmStoreContext<'a, T, E> + AsContextMut<E, UserState = T>
482{
483    /// Gets a mutable reference to the underlying stored data.
484    fn data_mut(&mut self) -> &mut T;
485}
486
487/// A trait used to get shared access to a store.
488pub trait AsContext<E: WasmEngine> {
489    /// The type of data associated with the store.
490    type UserState;
491
492    /// Returns the store context that this type provides access to.
493    fn as_context(&self) -> E::StoreContext<'_, Self::UserState>;
494}
495
496/// A trait used to get mutable access to a store.
497pub trait AsContextMut<E: WasmEngine>: AsContext<E> {
498    /// Returns the store context that this type provides access to.
499    fn as_context_mut(&mut self) -> E::StoreContextMut<'_, Self::UserState>;
500}
501
502impl<E, C> AsContext<E> for C
503where
504    C: crate::AsContext<Engine = E>,
505    E: WasmEngine,
506{
507    type UserState = C::UserState;
508
509    fn as_context(&self) -> <E as WasmEngine>::StoreContext<'_, Self::UserState> {
510        <Self as crate::AsContext>::as_context(self).inner
511    }
512}
513
514impl<E, C> AsContextMut<E> for C
515where
516    C: crate::AsContextMut<Engine = E>,
517    E: WasmEngine,
518{
519    fn as_context_mut(&mut self) -> <E as WasmEngine>::StoreContextMut<'_, Self::UserState> {
520        <Self as crate::AsContextMut>::as_context_mut(self).inner
521    }
522}