wasm/execution/store/
instances.rs

1use alloc::{collections::btree_map::BTreeMap, string::String, vec, vec::Vec};
2
3use crate::{
4    core::{
5        indices::TypeIdx,
6        reader::{
7            span::Span,
8            types::{FuncType, MemType, TableType},
9        },
10        sidetable::Sidetable,
11    },
12    linear_memory::LinearMemory,
13    value::Ref,
14    GlobalType, Limits, RefType, RuntimeError, TrapError, ValType, Value,
15};
16
17use super::{
18    addrs::{DataAddr, ElemAddr, FuncAddr, GlobalAddr, MemAddr, ModuleAddr, TableAddr},
19    ExternVal, HaltExecutionError,
20};
21
22#[derive(Debug)]
23// TODO does not match the spec FuncInst
24pub enum FuncInst<T> {
25    WasmFunc(WasmFuncInst),
26    HostFunc(HostFuncInst<T>),
27}
28
29#[derive(Debug)]
30pub struct WasmFuncInst {
31    pub function_type: FuncType,
32    pub _ty: TypeIdx,
33    pub locals: Vec<ValType>,
34    pub code_expr: Span,
35    ///index of the sidetable corresponding to the beginning of this functions code
36    pub stp: usize,
37
38    // implicit back ref required for function invocation and is in the spec
39    // TODO module_addr or module ref?
40    pub module_addr: ModuleAddr,
41}
42
43#[derive(Debug)]
44pub struct HostFuncInst<T> {
45    pub function_type: FuncType,
46    pub hostcode: fn(&mut T, Vec<Value>) -> Result<Vec<Value>, HaltExecutionError>,
47}
48
49impl<T> FuncInst<T> {
50    pub fn ty(&self) -> FuncType {
51        match self {
52            FuncInst::WasmFunc(wasm_func_inst) => wasm_func_inst.function_type.clone(),
53            FuncInst::HostFunc(host_func_inst) => host_func_inst.function_type.clone(),
54        }
55    }
56}
57
58#[derive(Clone, Debug)]
59/// <https://webassembly.github.io/spec/core/exec/runtime.html#element-instances>
60pub struct ElemInst {
61    pub _ty: RefType,
62    pub references: Vec<Ref>,
63}
64
65impl ElemInst {
66    pub fn len(&self) -> usize {
67        self.references.len()
68    }
69}
70
71// TODO: The tables have to be both imported and exported (an enum instead of a struct)
72//       That is because when we import tables we can give a different size to the imported table
73//        thus having a wrapper over the initial table
74#[derive(Debug)]
75pub struct TableInst {
76    pub ty: TableType,
77    pub elem: Vec<Ref>,
78}
79
80impl TableInst {
81    pub fn len(&self) -> usize {
82        self.elem.len()
83    }
84
85    /// <https://webassembly.github.io/spec/core/exec/modules.html#growing-tables>
86    pub fn grow(&mut self, n: u32, reff: Ref) -> Result<(), RuntimeError> {
87        // TODO refactor error, the spec Table.grow raises Table.{SizeOverflow, SizeLimit, OutOfMemory}
88        let len = n
89            .checked_add(self.elem.len() as u32)
90            .ok_or(TrapError::TableOrElementAccessOutOfBounds)?;
91
92        // roughly matches step 4,5,6
93        // checks limits_prime.valid() for limits_prime := { min: len, max: self.ty.lim.max }
94        // https://webassembly.github.io/spec/core/valid/types.html#limits
95        if self.ty.lim.max.map(|max| len > max).unwrap_or(false) {
96            return Err(TrapError::TableOrElementAccessOutOfBounds.into());
97        }
98        let limits_prime = Limits {
99            min: len,
100            max: self.ty.lim.max,
101        };
102
103        self.elem.extend(vec![reff; n as usize]);
104
105        self.ty.lim = limits_prime;
106        Ok(())
107    }
108}
109
110pub struct MemInst {
111    pub ty: MemType,
112    pub mem: LinearMemory,
113}
114impl core::fmt::Debug for MemInst {
115    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
116        f.debug_struct("MemInst")
117            .field("ty", &self.ty)
118            .finish_non_exhaustive()
119    }
120}
121
122impl MemInst {
123    /// <https://webassembly.github.io/spec/core/exec/modules.html#growing-memories>
124    pub fn grow(&mut self, n: u32) -> Result<(), RuntimeError> {
125        // TODO refactor error, the spec Table.grow raises Memory.{SizeOverflow, SizeLimit, OutOfMemory}
126        let len = n + self.mem.pages() as u32;
127        if len > Limits::MAX_MEM_PAGES {
128            return Err(TrapError::MemoryOrDataAccessOutOfBounds.into());
129        }
130
131        // roughly matches step 4,5,6
132        // checks limits_prime.valid() for limits_prime := { min: len, max: self.ty.lim.max }
133        // https://webassembly.github.io/spec/core/valid/types.html#limits
134        if self.ty.limits.max.map(|max| len > max).unwrap_or(false) {
135            return Err(TrapError::MemoryOrDataAccessOutOfBounds.into());
136        }
137        let limits_prime = Limits {
138            min: len,
139            max: self.ty.limits.max,
140        };
141
142        self.mem.grow(n.try_into().unwrap());
143
144        self.ty.limits = limits_prime;
145        Ok(())
146    }
147
148    /// Can never be bigger than 65,356 pages
149    pub fn size(&self) -> usize {
150        self.mem.len() / (crate::Limits::MEM_PAGE_SIZE as usize)
151    }
152}
153
154// pub struct GlobalInstV2 {
155//     Local(LocalGlobalInst),
156//     Imported(ImportedGlobalInst)
157// }
158
159#[derive(Debug)]
160pub struct GlobalInst {
161    pub ty: GlobalType,
162    /// Must be of the same type as specified in `ty`
163    pub value: Value,
164}
165
166pub struct DataInst {
167    pub data: Vec<u8>,
168}
169
170impl core::fmt::Debug for DataInst {
171    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
172        f.debug_struct("DataInst").finish_non_exhaustive()
173    }
174}
175
176///<https://webassembly.github.io/spec/core/exec/runtime.html#module-instances>
177#[derive(Debug)]
178pub struct ModuleInst<'b> {
179    pub types: Vec<FuncType>,
180    pub func_addrs: Vec<FuncAddr>,
181    pub table_addrs: Vec<TableAddr>,
182    pub mem_addrs: Vec<MemAddr>,
183    pub global_addrs: Vec<GlobalAddr>,
184    pub elem_addrs: Vec<ElemAddr>,
185    pub data_addrs: Vec<DataAddr>,
186    ///<https://webassembly.github.io/spec/core/exec/runtime.html#export-instances>
187    /// matches the list of ExportInst structs in the spec, however the spec never uses the name attribute
188    /// except during linking, which is up to the embedder to implement.
189    /// therefore this is a map data structure instead.
190    pub exports: BTreeMap<String, ExternVal>,
191
192    // TODO the bytecode is not in the spec, but required for re-parsing
193    pub wasm_bytecode: &'b [u8],
194
195    // sidetable is not in the spec, but required for control flow
196    pub sidetable: Sidetable,
197}