radix_wasmi/
instance.rs

1extern crate radix_wasmi_arena as wasmi_arena;
2
3use super::{
4    engine::DedupFuncType,
5    AsContext,
6    Extern,
7    Func,
8    Global,
9    Memory,
10    Module,
11    StoreContext,
12    Stored,
13    Table,
14};
15use crate::{
16    func::FuncError,
17    module::FuncIdx,
18    Error,
19    ExternType,
20    TypedFunc,
21    WasmParams,
22    WasmResults,
23};
24use alloc::{
25    boxed::Box,
26    collections::{btree_map, BTreeMap},
27    sync::Arc,
28    vec::Vec,
29};
30use core::iter::FusedIterator;
31use wasmi_arena::ArenaIndex;
32
33/// A raw index to a module instance entity.
34#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
35pub struct InstanceIdx(u32);
36
37impl ArenaIndex for InstanceIdx {
38    fn into_usize(self) -> usize {
39        self.0 as usize
40    }
41
42    fn from_usize(value: usize) -> Self {
43        let value = value.try_into().unwrap_or_else(|error| {
44            panic!("index {value} is out of bounds as instance index: {error}")
45        });
46        Self(value)
47    }
48}
49
50/// A module instance entity.
51#[derive(Debug, Clone)]
52pub struct InstanceEntity {
53    initialized: bool,
54    func_types: Arc<[DedupFuncType]>,
55    tables: Box<[Table]>,
56    funcs: Box<[Func]>,
57    memories: Box<[Memory]>,
58    globals: Box<[Global]>,
59    exports: BTreeMap<Box<str>, Extern>,
60}
61
62impl InstanceEntity {
63    /// Creates an uninitialized [`InstanceEntity`].
64    pub(crate) fn uninitialized() -> InstanceEntity {
65        Self {
66            initialized: false,
67            func_types: Arc::new([]),
68            tables: [].into(),
69            funcs: [].into(),
70            memories: [].into(),
71            globals: [].into(),
72            exports: BTreeMap::new(),
73        }
74    }
75
76    /// Creates a new [`InstanceEntityBuilder`].
77    pub(crate) fn build(module: &Module) -> InstanceEntityBuilder {
78        InstanceEntityBuilder::new(module)
79    }
80
81    /// Returns `true` if the [`InstanceEntity`] has been fully initialized.
82    pub(crate) fn is_initialized(&self) -> bool {
83        self.initialized
84    }
85
86    /// Returns the linear memory at the `index` if any.
87    pub(crate) fn get_memory(&self, index: u32) -> Option<Memory> {
88        self.memories.get(index as usize).copied()
89    }
90
91    /// Returns the table at the `index` if any.
92    pub(crate) fn get_table(&self, index: u32) -> Option<Table> {
93        self.tables.get(index as usize).copied()
94    }
95
96    /// Returns the global variable at the `index` if any.
97    pub(crate) fn get_global(&self, index: u32) -> Option<Global> {
98        self.globals.get(index as usize).copied()
99    }
100
101    /// Returns the function at the `index` if any.
102    pub(crate) fn get_func(&self, index: u32) -> Option<Func> {
103        self.funcs.get(index as usize).copied()
104    }
105
106    /// Returns the signature at the `index` if any.
107    pub(crate) fn get_signature(&self, index: u32) -> Option<DedupFuncType> {
108        self.func_types.get(index as usize).copied()
109    }
110
111    /// Returns the value exported to the given `name` if any.
112    pub(crate) fn get_export(&self, name: &str) -> Option<Extern> {
113        self.exports.get(name).copied()
114    }
115
116    /// Returns an iterator over the exports of the [`Instance`].
117    ///
118    /// The order of the yielded exports is not specified.
119    pub fn exports(&self) -> ExportsIter {
120        ExportsIter::new(self.exports.iter())
121    }
122}
123
124/// An exported WebAssembly value.
125///
126/// This type is primarily accessed from the [`Instance::exports`] method
127/// and describes what names and items are exported from a Wasm [`Instance`].
128#[derive(Debug, Clone)]
129pub struct Export<'instance> {
130    /// The name of the exported item.
131    name: &'instance str,
132    /// The definition of the exported item.
133    definition: Extern,
134}
135
136impl<'instance> Export<'instance> {
137    /// Creates a new [`Export`] with the given `name` and `definition`.
138    pub(crate) fn new(name: &'instance str, definition: Extern) -> Export<'instance> {
139        Self { name, definition }
140    }
141
142    /// Returns the name by which this export is known.
143    pub fn name(&self) -> &'instance str {
144        self.name
145    }
146
147    /// Return the [`ExternType`] of this export.
148    ///
149    /// # Panics
150    ///
151    /// If `ctx` does not own this [`Export`].
152    pub fn ty(&self, ctx: impl AsContext) -> ExternType {
153        self.definition.ty(ctx)
154    }
155
156    /// Consume this [`Export`] and return the underlying [`Extern`].
157    pub fn into_extern(self) -> Extern {
158        self.definition
159    }
160
161    /// Returns the underlying [`Func`], if the [`Export`] is a function or `None` otherwise.
162    pub fn into_func(self) -> Option<Func> {
163        self.definition.into_func()
164    }
165
166    /// Returns the underlying [`Table`], if the [`Export`] is a table or `None` otherwise.
167    pub fn into_table(self) -> Option<Table> {
168        self.definition.into_table()
169    }
170
171    /// Returns the underlying [`Memory`], if the [`Export`] is a linear memory or `None` otherwise.
172    pub fn into_memory(self) -> Option<Memory> {
173        self.definition.into_memory()
174    }
175
176    /// Returns the underlying [`Global`], if the [`Export`] is a global variable or `None` otherwise.
177    pub fn into_global(self) -> Option<Global> {
178        self.definition.into_global()
179    }
180}
181
182/// An iterator over the [`Extern`] declarations of an [`Instance`].
183#[derive(Debug)]
184pub struct ExportsIter<'instance> {
185    iter: btree_map::Iter<'instance, Box<str>, Extern>,
186}
187
188impl<'instance> ExportsIter<'instance> {
189    /// Creates a new [`ExportsIter`].
190    fn new(iter: btree_map::Iter<'instance, Box<str>, Extern>) -> Self {
191        Self { iter }
192    }
193
194    /// Prepares an item to match the expected iterator `Item` signature.
195    #[allow(clippy::borrowed_box)]
196    fn convert_item((name, export): (&'instance Box<str>, &'instance Extern)) -> Export {
197        Export::new(name, *export)
198    }
199}
200
201impl<'instance> Iterator for ExportsIter<'instance> {
202    type Item = Export<'instance>;
203
204    fn size_hint(&self) -> (usize, Option<usize>) {
205        self.iter.size_hint()
206    }
207
208    fn next(&mut self) -> Option<Self::Item> {
209        self.iter.next().map(Self::convert_item)
210    }
211}
212
213impl DoubleEndedIterator for ExportsIter<'_> {
214    fn next_back(&mut self) -> Option<Self::Item> {
215        self.iter.next_back().map(Self::convert_item)
216    }
217}
218
219impl ExactSizeIterator for ExportsIter<'_> {
220    fn len(&self) -> usize {
221        self.iter.len()
222    }
223}
224
225impl FusedIterator for ExportsIter<'_> {}
226
227/// A module instance entity builder.
228#[derive(Debug)]
229pub struct InstanceEntityBuilder {
230    func_types: Arc<[DedupFuncType]>,
231    tables: Vec<Table>,
232    funcs: Vec<Func>,
233    memories: Vec<Memory>,
234    globals: Vec<Global>,
235    start_fn: Option<FuncIdx>,
236    exports: BTreeMap<Box<str>, Extern>,
237}
238
239impl InstanceEntityBuilder {
240    /// Creates a new [`InstanceEntityBuilder`] optimized for the [`Module`].
241    pub fn new(module: &Module) -> Self {
242        fn vec_with_capacity_exact<T>(capacity: usize) -> Vec<T> {
243            let mut v = Vec::new();
244            v.reserve_exact(capacity);
245            v
246        }
247        let mut len_funcs = module.len_funcs();
248        let mut len_globals = module.len_globals();
249        let mut len_tables = module.len_tables();
250        let mut len_memories = module.len_memories();
251        for import in module.imports() {
252            match import.ty() {
253                ExternType::Func(_) => {
254                    len_funcs += 1;
255                }
256                ExternType::Table(_) => {
257                    len_tables += 1;
258                }
259                ExternType::Memory(_) => {
260                    len_memories += 1;
261                }
262                ExternType::Global(_) => {
263                    len_globals += 1;
264                }
265            }
266        }
267        Self {
268            func_types: Arc::new([]),
269            tables: vec_with_capacity_exact(len_tables),
270            funcs: vec_with_capacity_exact(len_funcs),
271            memories: vec_with_capacity_exact(len_memories),
272            globals: vec_with_capacity_exact(len_globals),
273            start_fn: None,
274            exports: BTreeMap::default(),
275        }
276    }
277
278    /// Sets the start function of the built instance.
279    ///
280    /// # Panics
281    ///
282    /// If the start function has already been set.
283    pub fn set_start(&mut self, start_fn: FuncIdx) {
284        match &mut self.start_fn {
285            Some(_) => panic!("already set start function"),
286            None => {
287                self.start_fn = Some(start_fn);
288            }
289        }
290    }
291
292    /// Returns the optional start function index.
293    pub fn get_start(&self) -> Option<FuncIdx> {
294        self.start_fn
295    }
296
297    /// Returns the linear memory at the `index`.
298    ///
299    /// # Panics
300    ///
301    /// If there is no linear memory at the given `index.
302    pub fn get_memory(&self, index: u32) -> Memory {
303        self.memories
304            .get(index as usize)
305            .copied()
306            .unwrap_or_else(|| panic!("missing `Memory` at index: {index}"))
307    }
308
309    /// Returns the table at the `index`.
310    ///
311    /// # Panics
312    ///
313    /// If there is no table at the given `index.
314    pub fn get_table(&self, index: u32) -> Table {
315        self.tables
316            .get(index as usize)
317            .copied()
318            .unwrap_or_else(|| panic!("missing `Table` at index: {index}"))
319    }
320
321    /// Returns the global variable at the `index`.
322    ///
323    /// # Panics
324    ///
325    /// If there is no global variable at the given `index.
326    pub fn get_global(&self, index: u32) -> Global {
327        self.globals
328            .get(index as usize)
329            .copied()
330            .unwrap_or_else(|| panic!("missing `Global` at index: {index}"))
331    }
332
333    /// Returns the function at the `index`.
334    ///
335    /// # Panics
336    ///
337    /// If there is no function at the given `index.
338    pub fn get_func(&self, index: u32) -> Func {
339        self.funcs
340            .get(index as usize)
341            .copied()
342            .unwrap_or_else(|| panic!("missing `Func` at index: {index}"))
343    }
344
345    /// Pushes a new [`Memory`] to the [`InstanceEntity`] under construction.
346    pub fn push_memory(&mut self, memory: Memory) {
347        self.memories.push(memory);
348    }
349
350    /// Pushes a new [`Table`] to the [`InstanceEntity`] under construction.
351    pub fn push_table(&mut self, table: Table) {
352        self.tables.push(table);
353    }
354
355    /// Pushes a new [`Global`] to the [`InstanceEntity`] under construction.
356    pub fn push_global(&mut self, global: Global) {
357        self.globals.push(global);
358    }
359
360    /// Pushes a new [`Func`] to the [`InstanceEntity`] under construction.
361    pub fn push_func(&mut self, func: Func) {
362        self.funcs.push(func);
363    }
364
365    /// Pushes a new deduplicated [`FuncType`] to the [`InstanceEntity`]
366    /// under construction.
367    ///
368    /// [`FuncType`]: [`crate::FuncType`]
369    pub fn set_func_types(&mut self, func_types: &Arc<[DedupFuncType]>) {
370        self.func_types = func_types.clone();
371    }
372
373    /// Pushes a new [`Extern`] under the given `name` to the [`InstanceEntity`] under construction.
374    ///
375    /// # Panics
376    ///
377    /// If the name has already been used by an already pushed [`Extern`].
378    pub fn push_export(&mut self, name: &str, new_value: Extern) {
379        if let Some(old_value) = self.exports.get(name) {
380            panic!(
381                "tried to register {new_value:?} for name {name} \
382                but name is already used by {old_value:?}",
383            )
384        }
385        self.exports.insert(name.into(), new_value);
386    }
387
388    /// Finishes constructing the [`InstanceEntity`].
389    pub fn finish(self) -> InstanceEntity {
390        InstanceEntity {
391            initialized: true,
392            func_types: self.func_types,
393            tables: self.tables.into(),
394            funcs: self.funcs.into(),
395            memories: self.memories.into(),
396            globals: self.globals.into(),
397            exports: self.exports,
398        }
399    }
400}
401
402/// An instantiated WebAssembly [`Module`].
403///
404/// This type represents an instantiation of a [`Module`].
405/// It primarily allows to access its [`exports`](Instance::exports)
406/// to call functions, get or set globals, read or write memory, etc.
407///
408/// When interacting with any Wasm code you will want to create an
409/// [`Instance`] in order to execute anything.
410///
411/// Instances are owned by a [`Store`](crate::Store).
412/// Create new instances using [`Linker::instantiate`](crate::Linker::instantiate).
413#[derive(Debug, Copy, Clone, PartialEq, Eq)]
414#[repr(transparent)]
415pub struct Instance(Stored<InstanceIdx>);
416
417impl Instance {
418    /// Creates a new stored instance reference.
419    ///
420    /// # Note
421    ///
422    /// This API is primarily used by the [`Store`] itself.
423    ///
424    /// [`Store`]: [`crate::Store`]
425    pub(super) fn from_inner(stored: Stored<InstanceIdx>) -> Self {
426        Self(stored)
427    }
428
429    /// Returns the underlying stored representation.
430    pub(super) fn into_inner(self) -> Stored<InstanceIdx> {
431        self.0
432    }
433
434    /// Returns the function at the `index` if any.
435    ///
436    /// # Panics
437    ///
438    /// Panics if `store` does not own this [`Instance`].
439    pub(crate) fn get_func_by_index(&self, store: impl AsContext, index: u32) -> Option<Func> {
440        store
441            .as_context()
442            .store
443            .resolve_instance(*self)
444            .get_func(index)
445    }
446
447    /// Returns the value exported to the given `name` if any.
448    ///
449    /// # Panics
450    ///
451    /// Panics if `store` does not own this [`Instance`].
452    pub fn get_export(&self, store: impl AsContext, name: &str) -> Option<Extern> {
453        store
454            .as_context()
455            .store
456            .resolve_instance(*self)
457            .get_export(name)
458    }
459
460    /// Looks up an exported [`Func`] value by `name`.
461    ///
462    /// Returns `None` if there was no export named `name`,
463    /// or if there was but it wasn’t a function.
464    ///
465    /// # Panics
466    ///
467    /// If `store` does not own this [`Instance`].
468    pub fn get_func(&self, store: impl AsContext, name: &str) -> Option<Func> {
469        self.get_export(store, name)?.into_func()
470    }
471
472    /// Looks up an exported [`Func`] value by `name`.
473    ///
474    /// Returns `None` if there was no export named `name`,
475    /// or if there was but it wasn’t a function.
476    ///
477    /// # Errors
478    ///
479    /// - If there is no export named `name`.
480    /// - If there is no exported function named `name`.
481    /// - If `Params` or `Results` do not match the exported function type.
482    ///
483    /// # Panics
484    ///
485    /// If `store` does not own this [`Instance`].
486    pub fn get_typed_func<Params, Results>(
487        &self,
488        store: impl AsContext,
489        name: &str,
490    ) -> Result<TypedFunc<Params, Results>, Error>
491    where
492        Params: WasmParams,
493        Results: WasmResults,
494    {
495        self.get_export(&store, name)
496            .and_then(Extern::into_func)
497            .ok_or_else(|| Error::Func(FuncError::ExportedFuncNotFound))?
498            .typed::<Params, Results>(store)
499    }
500
501    /// Looks up an exported [`Global`] value by `name`.
502    ///
503    /// Returns `None` if there was no export named `name`,
504    /// or if there was but it wasn’t a global variable.
505    ///
506    /// # Panics
507    ///
508    /// If `store` does not own this [`Instance`].
509    pub fn get_global(&self, store: impl AsContext, name: &str) -> Option<Global> {
510        self.get_export(store, name)?.into_global()
511    }
512
513    /// Looks up an exported [`Table`] value by `name`.
514    ///
515    /// Returns `None` if there was no export named `name`,
516    /// or if there was but it wasn’t a table.
517    ///
518    /// # Panics
519    ///
520    /// If `store` does not own this [`Instance`].
521    pub fn get_table(&self, store: impl AsContext, name: &str) -> Option<Table> {
522        self.get_export(store, name)?.into_table()
523    }
524
525    /// Looks up an exported [`Memory`] value by `name`.
526    ///
527    /// Returns `None` if there was no export named `name`,
528    /// or if there was but it wasn’t a table.
529    ///
530    /// # Panics
531    ///
532    /// If `store` does not own this [`Instance`].
533    pub fn get_memory(&self, store: impl AsContext, name: &str) -> Option<Memory> {
534        self.get_export(store, name)?.into_memory()
535    }
536
537    /// Returns an iterator over the exports of the [`Instance`].
538    ///
539    /// The order of the yielded exports is not specified.
540    ///
541    /// # Panics
542    ///
543    /// Panics if `store` does not own this [`Instance`].
544    pub fn exports<'ctx, T: 'ctx>(
545        &self,
546        store: impl Into<StoreContext<'ctx, T>>,
547    ) -> ExportsIter<'ctx> {
548        store.into().store.resolve_instance(*self).exports()
549    }
550}