wasm/execution/store/
instances.rs

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