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