Next Generation WASM Microkernel Operating System
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use crate::util::zip_eq::IteratorExt;
9use crate::wasm::indices::EntityIndex;
10use crate::wasm::module::Module;
11use crate::wasm::store::{StoreOpaque, Stored};
12use crate::wasm::vm::{ConstExprEvaluator, Imports, InstanceHandle};
13use crate::wasm::{Extern, Func, Global, Memory, Table};
14use alloc::vec;
15use alloc::vec::Vec;
16
17/// An instantiated WebAssembly module.
18///
19/// This is the main representation of all runtime state associated with a running WebAssembly module.
20///
21/// # Instance and `VMContext`
22///
23/// `Instance` and `VMContext` are essentially two halves of the same data structure. `Instance` is
24/// the privileged host-side half responsible for administrating execution, while `VMContext` holds the
25/// actual data that is accessed by compiled WASM code.
26#[derive(Debug, Clone, Copy)]
27#[repr(transparent)]
28pub struct Instance(Stored<InstanceData>);
29#[derive(Debug)]
30pub(super) struct InstanceData {
31 pub handle: InstanceHandle,
32 /// A lazily-populated list of exports of this instance. The order of
33 /// exports here matches the order of the exports in the original
34 /// module.
35 exports: Vec<Option<Extern>>,
36}
37
38#[derive(Clone)]
39pub struct Export<'instance> {
40 /// The name of the export.
41 pub name: &'instance str,
42 /// The definition of the export.
43 pub definition: Extern,
44}
45
46impl Instance {
47 /// Instantiates a new `Instance`.
48 ///
49 /// # Safety
50 ///
51 /// This functions assumes the provided `imports` have already been validated and typechecked for
52 /// compatibility with the `module` being instantiated.
53 pub(crate) unsafe fn new_unchecked(
54 store: &mut StoreOpaque,
55 const_eval: &mut ConstExprEvaluator,
56 module: Module,
57 imports: Imports,
58 ) -> crate::Result<Self> {
59 let mut handle = store.alloc_mut().allocate_module(module.clone())?;
60
61 let is_bulk_memory = module.required_features().bulk_memory();
62
63 handle.initialize(store, const_eval, &module, imports, is_bulk_memory)?;
64
65 let stored = store.add_instance(InstanceData {
66 handle,
67 exports: vec![None; module.exports().len()],
68 });
69
70 Ok(Self(stored))
71 }
72
73 /// Returns the module this instance was instantiated from.
74 pub fn module(self, store: &StoreOpaque) -> &Module {
75 store[self.0].handle.module()
76 }
77
78 /// Returns an iterator over the exports of this instance.
79 pub(crate) fn exports(
80 self,
81 store: &mut StoreOpaque,
82 ) -> impl ExactSizeIterator<Item = Export<'_>> {
83 let exports = &store[self.0].exports;
84
85 if exports.iter().any(Option::is_none) {
86 let module = store[self.0].handle.module().clone();
87
88 for name in module.translated().exports.keys() {
89 if let Some((export_name_index, _, &entity)) =
90 module.translated().exports.get_full(name)
91 {
92 self.get_export_inner(store, entity, export_name_index);
93 }
94 }
95 }
96
97 let instance = &store[self.0];
98 let module = instance.handle.module();
99 module
100 .translated()
101 .exports
102 .iter()
103 .zip_eq(&instance.exports)
104 .map(|((name, _), export)| Export {
105 name,
106 definition: export.clone().unwrap(),
107 })
108 }
109
110 /// Attempts to get an export from this instance.
111 pub fn get_export(self, store: &mut StoreOpaque, name: &str) -> Option<Extern> {
112 let (export_name_index, _, index) =
113 self.module(store).translated().exports.get_full(name)?;
114 Some(self.get_export_inner(store, *index, export_name_index))
115 }
116
117 /// Attempts to get an exported `Func` from this instance.
118 pub fn get_func(self, store: &mut StoreOpaque, name: &str) -> Option<Func> {
119 self.get_export(store, name)?.into_func()
120 }
121
122 /// Attempts to get an exported `Table` from this instance.
123 pub fn get_table(self, store: &mut StoreOpaque, name: &str) -> Option<Table> {
124 self.get_export(store, name)?.into_table()
125 }
126
127 /// Attempts to get an exported `Memory` from this instance.
128 pub fn get_memory(self, store: &mut StoreOpaque, name: &str) -> Option<Memory> {
129 self.get_export(store, name)?.into_memory()
130 }
131
132 /// Attempts to get an exported `Global` from this instance.
133 pub fn get_global(self, store: &mut StoreOpaque, name: &str) -> Option<Global> {
134 self.get_export(store, name)?.into_global()
135 }
136
137 fn get_export_inner(
138 self,
139 store: &mut StoreOpaque,
140 entity: EntityIndex,
141 export_name_index: usize,
142 ) -> Extern {
143 // Instantiated instances will lazily fill in exports, so we process
144 // all that lazy logic here.
145 let data = &store[self.0];
146
147 if let Some(export) = &data.exports[export_name_index] {
148 return export.clone();
149 }
150
151 let instance = &mut store[self.0]; // Reborrow the &mut InstanceHandle
152 // Safety: we just took `instance` from the store, so all its exports must also belong to the store
153 let item =
154 unsafe { Extern::from_export(instance.handle.get_export_by_index(entity), store) };
155 let data = &mut store[self.0];
156 data.exports[export_name_index] = Some(item.clone());
157 item
158 }
159
160 pub(crate) fn comes_from_same_store(self, store: &StoreOpaque) -> bool {
161 store.has_instance(self.0)
162 }
163
164 /// Print a debug representation of this instances `VMContext` to the logger.
165 pub fn debug_vmctx(self, store: &StoreOpaque) {
166 store[self.0].handle.debug_vmctx();
167 }
168}