wasm/execution/store/
mod.rs

1use core::mem;
2
3use crate::addrs::{
4    AddrVec, DataAddr, ElemAddr, FuncAddr, GlobalAddr, MemAddr, ModuleAddr, TableAddr,
5};
6use crate::config::Config;
7use crate::core::indices::TypeIdx;
8use crate::core::reader::span::Span;
9use crate::core::reader::types::data::{DataModeActive, DataSegment};
10use crate::core::reader::types::element::{ActiveElem, ElemItems, ElemMode, ElemType};
11use crate::core::reader::types::export::{Export, ExportDesc};
12use crate::core::reader::types::global::{Global, GlobalType};
13use crate::core::reader::types::import::Import;
14use crate::core::reader::types::{ExternType, FuncType, ImportSubTypeRelation, MemType, TableType};
15use crate::core::reader::WasmReader;
16use crate::execution::interpreter_loop::{self, memory_init, table_init};
17use crate::execution::value::{Ref, Value};
18use crate::execution::{run_const_span, Stack};
19use crate::registry::Registry;
20use crate::resumable::{
21    Dormitory, FreshResumableRef, InvokedResumableRef, Resumable, ResumableRef, RunState,
22};
23use crate::{RefType, RuntimeError, ValidationInfo};
24use alloc::borrow::ToOwned;
25use alloc::collections::btree_map::BTreeMap;
26use alloc::string::String;
27use alloc::sync::Arc;
28use alloc::vec;
29use alloc::vec::Vec;
30use instances::{
31    DataInst, ElemInst, FuncInst, GlobalInst, HostFuncInst, MemInst, ModuleInst, TableInst,
32    WasmFuncInst,
33};
34use linear_memory::LinearMemory;
35
36use super::interpreter_loop::{data_drop, elem_drop};
37use super::UnwrapValidatedExt;
38
39pub mod addrs;
40pub(crate) mod instances;
41pub(crate) mod linear_memory;
42
43/// The store represents all global state that can be manipulated by WebAssembly programs. It
44/// consists of the runtime representation of all instances of functions, tables, memories, and
45/// globals, element segments, and data segments that have been allocated during the life time of
46/// the abstract machine.
47/// <https://webassembly.github.io/spec/core/exec/runtime.html#store>
48pub struct Store<'b, T: Config> {
49    pub(crate) functions: AddrVec<FuncAddr, FuncInst<T>>,
50    pub(crate) tables: AddrVec<TableAddr, TableInst>,
51    pub(crate) memories: AddrVec<MemAddr, MemInst>,
52    pub(crate) globals: AddrVec<GlobalAddr, GlobalInst>,
53    pub(crate) elements: AddrVec<ElemAddr, ElemInst>,
54    pub(crate) data: AddrVec<DataAddr, DataInst>,
55
56    // fields outside of the spec but are convenient are below
57    /// An address space of modules instantiated within the context of this [`Store`].
58    ///
59    /// Although the WebAssembly Specification 2.0 does not specify module instances
60    /// to be part of the [`Store`], in reality they can be managed very similar to
61    /// other instance types. Therefore, we extend the [`Store`] by a module address
62    /// space along with a `ModuleAddr` index type.
63    pub(crate) modules: AddrVec<ModuleAddr, ModuleInst<'b>>,
64
65    // all visible exports and entities added by hand or module instantiation by the interpreter
66    // currently, all of the exports of an instantiated module is made visible (this is outside of spec)
67    pub registry: Registry,
68    pub user_data: T,
69
70    // data structure holding all resumable objects that belong to this store
71    pub(crate) dormitory: Dormitory,
72}
73
74impl<'b, T: Config> Store<'b, T> {
75    /// Creates a new empty store with some user data
76    ///
77    /// See: WebAssembly Specification 2.0 - 7.1.4 - store_init
78    pub fn new(user_data: T) -> Self {
79        // 1. Return the empty store.
80        // For us the store is empty except for the user data, which we do not have control over.
81        Self {
82            functions: AddrVec::default(),
83            tables: AddrVec::default(),
84            memories: AddrVec::default(),
85            globals: AddrVec::default(),
86            elements: AddrVec::default(),
87            data: AddrVec::default(),
88            modules: AddrVec::default(),
89            registry: Registry::default(),
90            dormitory: Dormitory::default(),
91            user_data,
92        }
93    }
94
95    /// instantiates a validated module with `validation_info` as validation evidence with name `name`
96    /// with the steps in <https://webassembly.github.io/spec/core/exec/modules.html#instantiation>
97    /// this method roughly matches the suggested embedder function`module_instantiate`
98    /// <https://webassembly.github.io/spec/core/appendix/embedding.html#modules>
99    /// except external values for module instantiation are retrieved from `self`.
100    /// Returns the module addr of the new module instance
101    pub fn add_module(
102        &mut self,
103        name: &str,
104        validation_info: &ValidationInfo<'b>,
105        maybe_fuel: Option<u32>,
106    ) -> Result<ModuleAddr, RuntimeError> {
107        // instantiation step -1: collect extern_vals, this section basically acts as a linker between modules
108        // best attempt at trying to match the spec implementation in terms of errors
109        debug!("adding module with name {:?}", name);
110        let mut extern_vals = Vec::new();
111
112        for Import {
113            module_name: exporting_module_name,
114            name: import_name,
115            desc: import_desc,
116        } in &validation_info.imports
117        {
118            trace!(
119                "trying to import from exporting module instance named {:?}, the entity with name {:?} with desc: {:?}",
120                exporting_module_name,
121                import_name,
122                import_desc
123            );
124            let import_extern_type = import_desc.extern_type(validation_info);
125            let export_extern_val_candidate = *self.registry.lookup(
126                exporting_module_name.clone().into(),
127                import_name.clone().into(),
128            )?;
129            trace!("export candidate found: {:?}", export_extern_val_candidate);
130            if !export_extern_val_candidate
131                .extern_type(self)
132                .is_subtype_of(&import_extern_type)
133            {
134                return Err(RuntimeError::InvalidImportType);
135            }
136            trace!("import and export matches. Adding to externvals");
137            extern_vals.push(export_extern_val_candidate)
138        }
139
140        // instantiation: step 5
141        // module_inst_init is unfortunately circularly defined from parts of module_inst that would be defined in step 11, which uses module_inst_init again implicitly.
142        // therefore I am mimicking the reference interpreter code here, I will allocate functions in the store in this step instead of step 11.
143        // https://github.com/WebAssembly/spec/blob/8d6792e3d6709e8d3e90828f9c8468253287f7ed/interpreter/exec/eval.ml#L789
144        let module_inst = ModuleInst {
145            types: validation_info.types.clone(),
146            func_addrs: extern_vals.iter().funcs().collect(),
147            table_addrs: Vec::new(),
148            mem_addrs: Vec::new(),
149            global_addrs: extern_vals.iter().globals().collect(),
150            elem_addrs: Vec::new(),
151            data_addrs: Vec::new(),
152            exports: BTreeMap::new(),
153            wasm_bytecode: validation_info.wasm,
154            sidetable: validation_info.sidetable.clone(),
155        };
156        let module_addr = self.modules.insert(module_inst);
157
158        // TODO rewrite this part
159        // <https://webassembly.github.io/spec/core/exec/modules.html#functions>
160        let func_addrs: Vec<FuncAddr> = validation_info
161            .functions
162            .iter()
163            .zip(validation_info.func_blocks_stps.iter())
164            .map(|(ty_idx, (span, stp))| self.alloc_func((*ty_idx, (*span, *stp)), module_addr))
165            .collect();
166
167        self.modules
168            .get_mut(module_addr)
169            .func_addrs
170            .extend(func_addrs);
171
172        // instantiation: this roughly matches step 6,7,8
173        // validation guarantees these will evaluate without errors.
174        let maybe_global_init_vals: Result<Vec<Value>, _> = validation_info
175            .globals
176            .iter()
177            .map(|global| {
178                run_const_span(validation_info.wasm, &global.init_expr, module_addr, self)
179                    .transpose()
180                    .unwrap_validated()
181            })
182            .collect();
183        let global_init_vals = maybe_global_init_vals?;
184
185        // instantiation: this roughly matches step 9,10
186
187        let mut element_init_ref_lists: Vec<Vec<Ref>> =
188            Vec::with_capacity(validation_info.elements.len());
189
190        for elem in &validation_info.elements {
191            let mut new_list = Vec::new();
192            match &elem.init {
193                // shortcut of evaluation of "ref.func <func_idx>; end;"
194                // validation guarantees corresponding func_idx's existence
195                ElemItems::RefFuncs(ref_funcs) => {
196                    for func_idx in ref_funcs {
197                        let func_addr = *self
198                            .modules
199                            .get(module_addr)
200                            .func_addrs
201                            .get(*func_idx as usize)
202                            .unwrap_validated();
203
204                        new_list.push(Ref::Func(func_addr));
205                    }
206                }
207                ElemItems::Exprs(_, exprs) => {
208                    for expr in exprs {
209                        new_list.push(
210                            run_const_span(validation_info.wasm, expr, module_addr, self)?
211                                .unwrap_validated() // there is a return value
212                                .try_into()
213                                .unwrap_validated(), // return value has the correct type
214                        )
215                    }
216                }
217            }
218            element_init_ref_lists.push(new_list);
219        }
220
221        // instantiation: step 11 - module allocation (except function allocation - which was made in step 5)
222        // https://webassembly.github.io/spec/core/exec/modules.html#alloc-module
223
224        // allocation: begin
225
226        // allocation: step 1
227        let module = validation_info;
228
229        let extern_vals = extern_vals;
230        let vals = global_init_vals;
231        let ref_lists = element_init_ref_lists;
232
233        // allocation: skip step 2 as it was done in instantiation step 5
234
235        // allocation: step 3-13
236        let table_addrs: Vec<TableAddr> = module
237            .tables
238            .iter()
239            .map(|table_type| self.alloc_table(*table_type, Ref::Null(table_type.et)))
240            .collect();
241        let mem_addrs: Vec<MemAddr> = module
242            .memories
243            .iter()
244            .map(|mem_type| self.alloc_mem(*mem_type))
245            .collect();
246        let global_addrs: Vec<GlobalAddr> = module
247            .globals
248            .iter()
249            .zip(vals)
250            .map(
251                |(
252                    Global {
253                        ty: global_type, ..
254                    },
255                    val,
256                )| self.alloc_global(*global_type, val),
257            )
258            .collect();
259        let elem_addrs = module
260            .elements
261            .iter()
262            .zip(ref_lists)
263            .map(|(elem, refs)| self.alloc_elem(elem.ty(), refs))
264            .collect();
265        let data_addrs = module
266            .data
267            .iter()
268            .map(|DataSegment { init: bytes, .. }| self.alloc_data(bytes))
269            .collect();
270
271        // allocation: skip step 14 as it was done in instantiation step 5
272
273        // allocation: step 15,16
274        let mut table_addrs_mod: Vec<TableAddr> = extern_vals.iter().tables().collect();
275        table_addrs_mod.extend(table_addrs);
276
277        let mut mem_addrs_mod: Vec<MemAddr> = extern_vals.iter().mems().collect();
278        mem_addrs_mod.extend(mem_addrs);
279
280        // skipping step 17 partially as it was partially done in instantiation step
281        self.modules
282            .get_mut(module_addr)
283            .global_addrs
284            .extend(global_addrs);
285
286        // allocation: step 18,19
287        let export_insts: BTreeMap<String, ExternVal> = module
288            .exports
289            .iter()
290            .map(|Export { name, desc }| {
291                let module_inst = self.modules.get(module_addr);
292                let value = match desc {
293                    ExportDesc::FuncIdx(func_idx) => {
294                        ExternVal::Func(module_inst.func_addrs[*func_idx])
295                    }
296                    ExportDesc::TableIdx(table_idx) => {
297                        ExternVal::Table(table_addrs_mod[*table_idx])
298                    }
299                    ExportDesc::MemIdx(mem_idx) => ExternVal::Mem(mem_addrs_mod[*mem_idx]),
300                    ExportDesc::GlobalIdx(global_idx) => {
301                        ExternVal::Global(module_inst.global_addrs[*global_idx])
302                    }
303                };
304                (String::from(name), value)
305            })
306            .collect();
307
308        // allocation: step 20,21 initialize module (except functions and globals due to instantiation step 5, allocation step 14,17)
309        let module_inst = self.modules.get_mut(module_addr);
310        module_inst.table_addrs = table_addrs_mod;
311        module_inst.mem_addrs = mem_addrs_mod;
312        module_inst.elem_addrs = elem_addrs;
313        module_inst.data_addrs = data_addrs;
314        module_inst.exports = export_insts;
315
316        // allocation: end
317
318        // register module exports, this is outside of the spec
319        self.registry
320            .register_module(name.to_owned().into(), module_inst)?;
321
322        // instantiation step 11 end: module_inst properly allocated after this point.
323
324        // instantiation: step 12-15
325        // TODO have to stray away from the spec a bit since our codebase does not lend itself well to freely executing instructions by themselves
326        for (
327            i,
328            ElemType {
329                init: elem_items,
330                mode,
331            },
332        ) in validation_info.elements.iter().enumerate()
333        {
334            match mode {
335                ElemMode::Active(ActiveElem {
336                    table_idx: table_idx_i,
337                    init_expr: einstr_i,
338                }) => {
339                    let n = elem_items.len() as i32;
340                    // equivalent to init.len() in spec
341                    // instantiation step 14:
342                    // TODO (for now, we are doing hopefully what is equivalent to it)
343                    // execute:
344                    //   einstr_i
345                    //   i32.const 0
346                    //   i32.const n
347                    //   table.init table_idx_i i
348                    //   elem.drop i
349                    let d: i32 = run_const_span(validation_info.wasm, einstr_i, module_addr, self)?
350                        .unwrap_validated() // there is a return value
351                        .try_into()
352                        .unwrap_validated(); // return value has correct type
353
354                    let s = 0;
355                    table_init(
356                        &self.modules,
357                        &mut self.tables,
358                        &self.elements,
359                        module_addr,
360                        i,
361                        *table_idx_i as usize,
362                        n,
363                        s,
364                        d,
365                    )?;
366                    elem_drop(&self.modules, &mut self.elements, module_addr, i)?;
367                }
368                ElemMode::Declarative => {
369                    // instantiation step 15:
370                    // TODO (for now, we are doing hopefully what is equivalent to it)
371                    // execute:
372                    //   elem.drop i
373                    elem_drop(&self.modules, &mut self.elements, module_addr, i)?;
374                }
375                ElemMode::Passive => (),
376            }
377        }
378
379        // instantiation: step 16
380        // TODO have to stray away from the spec a bit since our codebase does not lend itself well to freely executing instructions by themselves
381        for (i, DataSegment { init, mode }) in validation_info.data.iter().enumerate() {
382            match mode {
383                crate::core::reader::types::data::DataMode::Active(DataModeActive {
384                    memory_idx,
385                    offset: dinstr_i,
386                }) => {
387                    let n = init.len() as i32;
388                    // assert: mem_idx is 0
389                    if *memory_idx != 0 {
390                        // TODO fix error
391                        return Err(RuntimeError::MoreThanOneMemory);
392                    }
393
394                    // TODO (for now, we are doing hopefully what is equivalent to it)
395                    // execute:
396                    //   dinstr_i
397                    //   i32.const 0
398                    //   i32.const n
399                    //   memory.init i
400                    //   data.drop i
401                    let d: i32 = run_const_span(validation_info.wasm, dinstr_i, module_addr, self)?
402                        .unwrap_validated() // there is a return value
403                        .try_into()
404                        .unwrap_validated(); // return value has the correct type
405
406                    let s = 0;
407                    memory_init(
408                        &self.modules,
409                        &mut self.memories,
410                        &self.data,
411                        module_addr,
412                        i,
413                        0,
414                        n,
415                        s,
416                        d,
417                    )?;
418                    data_drop(&self.modules, &mut self.data, module_addr, i)?;
419                }
420                crate::core::reader::types::data::DataMode::Passive => (),
421            }
422        }
423
424        // instantiation: step 17
425        if let Some(func_idx) = validation_info.start {
426            // TODO (for now, we are doing hopefully what is equivalent to it)
427            // execute
428            //   call func_ifx
429            let func_addr = self.modules.get(module_addr).func_addrs[func_idx];
430            self.invoke(func_addr, Vec::new(), maybe_fuel)?;
431        };
432
433        Ok(module_addr)
434    }
435
436    /// Gets an export of a specific module instance by its name
437    ///
438    /// See: WebAssembly Specification 2.0 - 7.1.6 - instance_export
439    pub fn instance_export(
440        &self,
441        module_addr: ModuleAddr,
442        name: &str,
443    ) -> Result<ExternVal, RuntimeError> {
444        // Fetch the module instance because we store them in the [`Store`]
445        let module_inst = self.modules.get(module_addr);
446
447        // 1. Assert: due to validity of the module instance `moduleinst`, all its export names are different
448
449        // 2. If there exists an `exportinst_i` in `moduleinst.exports` such that name `exportinst_i.name` equals `name`, then:
450        //   a. Return the external value `exportinst_i.value`.
451        // 3. Else return `error`.
452        module_inst
453            .exports
454            .get(name)
455            .copied()
456            .ok_or(RuntimeError::UnknownExport)
457    }
458
459    /// Allocates a new function with some host code.
460    ///
461    /// This type of function is also called a host function.
462    ///
463    /// # Panics & Unexpected Behavior
464    /// The specification states that:
465    ///
466    /// > This operation must make sure that the provided host function satisfies the pre-
467    /// > and post-conditions required for a function instance with type `functype`.
468    ///
469    /// Therefore, all "invalid" host functions (e.g. those which return incorrect return values)
470    /// can cause the interpreter to panic or behave unexpectedly.
471    ///
472    /// See: <https://webassembly.github.io/spec/core/exec/modules.html#host-functions>
473    /// See: WebAssembly Specification 2.0 - 7.1.7 - func_alloc
474    pub fn func_alloc(
475        &mut self,
476        func_type: FuncType,
477        host_func: fn(&mut T, Vec<Value>) -> Result<Vec<Value>, HaltExecutionError>,
478    ) -> FuncAddr {
479        // 1. Pre-condition: `functype` is valid.
480
481        // 2. Let `funcaddr` be the result of allocating a host function in `store` with
482        //    function type `functype` and host function code `hostfunc`.
483        // 3. Return the new store paired with `funcaddr`.
484        //
485        // Note: Returning the new store is a noop for us because we mutate the store instead.
486        self.functions.insert(FuncInst::HostFunc(HostFuncInst {
487            function_type: func_type,
488            hostcode: host_func,
489        }))
490    }
491
492    /// Gets the type of a function by its addr.
493    ///
494    /// See: WebAssembly Specification 2.0 - 7.1.7 - func_type
495    pub fn func_type(&self, func_addr: FuncAddr) -> FuncType {
496        // 1. Return `S.funcs[a].type`.
497        self.functions.get(func_addr).ty()
498
499        // 2. Post-condition: the returned function type is valid.
500    }
501
502    /// See: WebAssembly Specification 2.0 - 7.1.7 - func_invoke
503    pub fn invoke(
504        &mut self,
505        func_addr: FuncAddr,
506        params: Vec<Value>,
507        maybe_fuel: Option<u32>,
508    ) -> Result<RunState, RuntimeError> {
509        self.resume(self.create_resumable(func_addr, params, maybe_fuel)?)
510    }
511
512    /// Allocates a new table with some table type and an initialization value `ref` and returns its table address.
513    ///
514    /// See: WebAssembly Specification 2.0 - 7.1.8 - table_alloc
515    pub fn table_alloc(
516        &mut self,
517        table_type: TableType,
518        r#ref: Ref,
519    ) -> Result<TableAddr, RuntimeError> {
520        // Check pre-condition: ref has correct type
521        if table_type.et != r#ref.ty() {
522            return Err(RuntimeError::TableTypeMismatch);
523        }
524
525        // 1. Pre-condition: `tabletype` is valid
526
527        // 2. Let `tableaddr` be the result of allocating a table in `store` with table type `tabletype`
528        //    and initialization value `ref`.
529        let table_addr = self.alloc_table(table_type, r#ref);
530
531        // 3. Return the new store paired with `tableaddr`.
532        //
533        // Note: Returning the new store is a noop for us because we mutate the store instead.
534        Ok(table_addr)
535    }
536
537    /// Gets the type of some table by its addr.
538    ///
539    /// See: WebAssembly Specification 2.0 - 7.1.8 - table_type
540    pub fn table_type(&self, table_addr: TableAddr) -> TableType {
541        // 1. Return `S.tables[a].type`.
542        self.tables.get(table_addr).ty
543
544        // 2. Post-condition: the returned table type is valid.
545    }
546
547    /// Reads a single reference from a table by its table address and an index into the table.
548    ///
549    /// See: WebAssembly Specification 2.0 - 7.1.8 - table_read
550    pub fn table_read(&self, table_addr: TableAddr, i: u32) -> Result<Ref, RuntimeError> {
551        // Convert `i` to usize for indexing
552        let i = usize::try_from(i).expect("the architecture to be at least 32-bit");
553
554        // 1. Let `ti` be the table instance `store.tables[tableaddr]`
555        let ti = self.tables.get(table_addr);
556
557        // 2. If `i` is larger than or equal to the length of `ti.elem`, then return `error`.
558        // 3. Else, return the reference value `ti.elem[i]`.
559        ti.elem
560            .get(i)
561            .copied()
562            .ok_or(RuntimeError::TableAccessOutOfBounds)
563    }
564
565    /// Writes a single reference into a table by its table address and an index into the table.
566    ///
567    /// See: WebAssembly Specification 2.0 - 7.1.8 - table_write
568    pub fn table_write(
569        &mut self,
570        table_addr: TableAddr,
571        i: u32,
572        r#ref: Ref,
573    ) -> Result<(), RuntimeError> {
574        // Convert `i` to usize for indexing
575        let i = usize::try_from(i).expect("the architecture to be at least 32-bit");
576
577        // 1. Let `ti` be the table instance `store.tables[tableaddr]`.
578        let ti = self.tables.get_mut(table_addr);
579
580        // Check pre-condition: ref has correct type
581        if ti.ty.et != r#ref.ty() {
582            return Err(RuntimeError::TableTypeMismatch);
583        }
584
585        // 2. If `i` is larger than or equal to the length of `ti.elem`, then return `error`.
586        // 3. Replace `ti.elem[i]` with the reference value `ref`
587        *ti.elem
588            .get_mut(i)
589            .ok_or(RuntimeError::TableAccessOutOfBounds)? = r#ref;
590
591        // 4. Return the updated store.
592        //
593        // Note: Returning the new store is a noop for us because we mutate the store instead.
594        Ok(())
595    }
596
597    /// Gets the current size of a table by its table address.
598    ///
599    /// See: WebAssembly Specification 2.0 - 7.1.8 - table_size
600    pub fn table_size(&self, table_addr: TableAddr) -> u32 {
601        // 1. Return the length of `store.tables[tableaddr].elem`.
602        let len = self.tables.get(table_addr).elem.len();
603
604        // In addition we have to convert the length back to a `u32`
605        u32::try_from(len).expect(
606            "the maximum table length to be u32::MAX because thats what the specification allows for indexing",
607        )
608    }
609
610    /// Grows a table referenced by its table address by `n` elements.
611    ///
612    /// See: WebAssembly Specification 2.0 - 7.1.8 - table_grow
613    pub fn table_grow(
614        &mut self,
615        table_addr: TableAddr,
616        n: u32,
617        r#ref: Ref,
618    ) -> Result<(), RuntimeError> {
619        // 1. Try growing the table instance `store.tables[tableaddr] by `n` elements with initialization value `ref`:
620        //   a. If it succeeds, return the updated store.
621        //   b. Else, return `error`.
622        //
623        // Note: Returning the new store is a noop for us because we mutate the store instead.
624        self.tables.get_mut(table_addr).grow(n, r#ref)
625    }
626
627    /// Allocates a new linear memory and returns its memory address.
628    ///
629    /// See: WebAssembly Specification 2.0 - 7.1.9 - mem_alloc
630    pub fn mem_alloc(&mut self, mem_type: MemType) -> MemAddr {
631        // 1. Pre-condition: `memtype` is valid.
632
633        // 2. Let `memaddr` be the result of allocating a memory in `store` with memory type `memtype`.
634        // 3. Return the new store paired with `memaddr`.
635        //
636        // Note: Returning the new store is a noop for us because we mutate the store instead.
637        self.alloc_mem(mem_type)
638    }
639
640    /// Gets the memory type of some memory by its memory address
641    ///
642    /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_type
643    pub fn mem_type(&self, mem_addr: MemAddr) -> MemType {
644        // 1. Return `S.mems[a].type`.
645        self.memories.get(mem_addr).ty
646
647        // 2. Post-condition: the returned memory type is valid.
648    }
649
650    /// Reads a byte from some memory by its memory address and an index into the memory
651    ///
652    /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_read
653    pub fn mem_read(&self, mem_addr: MemAddr, i: u32) -> Result<u8, RuntimeError> {
654        // Convert the index type
655        let i = usize::try_from(i).expect("the architecture to be at least 32-bit");
656
657        // 1. Let `mi` be the memory instance `store.mems[memaddr]`.
658        let mi = self.memories.get(mem_addr);
659
660        // 2. If `i` is larger than or equal to the length of `mi.data`, then return `error`.
661        // 3. Else, return the byte `mi.data[i]`.
662        mi.mem.load(i)
663    }
664
665    /// Writes a byte into some memory by its memory address and an index into the memory
666    ///
667    /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_write
668    pub fn mem_write(&self, mem_addr: MemAddr, i: u32, byte: u8) -> Result<(), RuntimeError> {
669        // Convert the index type
670        let i = usize::try_from(i).expect("the architecture to be at least 32-bit");
671
672        // 1. Let `mi` be the memory instance `store.mems[memaddr]`.
673        let mi = self.memories.get(mem_addr);
674
675        mi.mem.store(i, byte)
676    }
677
678    /// Gets the size of some memory by its memory address in pages.
679    ///
680    /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_size
681    pub fn mem_size(&self, mem_addr: MemAddr) -> u32 {
682        // 1. Return the length of `store.mems[memaddr].data` divided by the page size.
683        let length = self.memories.get(mem_addr).size();
684
685        // In addition we have to convert the length back to a `u32`
686        length.try_into().expect(
687            "the maximum memory length to be smaller than u32::MAX because thats what the specification allows for indexing into the memory. Also the memory size is measured in pages, not bytes.")
688    }
689
690    /// Grows some memory by its memory address by `n` pages.
691    ///
692    /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_grow
693    pub fn mem_grow(&mut self, mem_addr: MemAddr, n: u32) -> Result<(), RuntimeError> {
694        // 1. Try growing the memory instance `store.mems[memaddr]` by `n` pages:
695        //   a. If it succeeds, then return the updated store.
696        //   b. Else, return `error`.
697        //
698        // Note: Returning the new store is a noop for us because we mutate the store instead.
699        self.memories.get_mut(mem_addr).grow(n)
700    }
701
702    /// Allocates a new global and returns its global address.
703    ///
704    /// See: WebAssemblySpecification 2.0 - 7.1.10 - global_alloc
705    pub fn global_alloc(
706        &mut self,
707        global_type: GlobalType,
708        val: Value,
709    ) -> Result<GlobalAddr, RuntimeError> {
710        // Check pre-condition: val has correct type
711        if global_type.ty != val.to_ty() {
712            return Err(RuntimeError::GlobalTypeMismatch);
713        }
714
715        // 1. Pre-condition: `globaltype` is valid.
716
717        // 2. Let `globaladdr` be the result of allocating a global with global type `globaltype` and initialization value `val`.
718        let global_addr = self.alloc_global(global_type, val);
719
720        // 3. Return the new store paired with `globaladdr`.
721        //
722        // Note: Returning the new store is a noop for us because we mutate the store instead.
723        Ok(global_addr)
724    }
725
726    /// Returns the global type of some global instance by its addr.
727    ///
728    /// See: WebAssembly Specification 2.0 - 7.1.10 - global_type
729    pub fn global_type(&self, global_addr: GlobalAddr) -> GlobalType {
730        // 1. Return `S.globals[a].type`.
731        self.globals.get(global_addr).ty
732        // 2. Post-condition: the returned global type is valid
733    }
734
735    /// Returns the current value of some global instance by its addr.
736    ///
737    /// See: WebAssembly Specification 2.0 - 7.1.10 - global_read
738    pub fn global_read(&self, global_addr: GlobalAddr) -> Value {
739        // 1. Let `gi` be the global instance `store.globals[globaladdr].
740        let gi = self.globals.get(global_addr);
741
742        // 2. Return the value `gi.value`.
743        gi.value
744    }
745
746    /// Sets a new value of some global instance by its addr.
747    ///
748    /// # Errors
749    /// - [` RuntimeError::WriteOnImmutableGlobal`]
750    /// - [` RuntimeError::GlobalTypeMismatch`]
751    ///
752    /// See: WebAssembly Specification 2.0 - 7.1.10 - global_write
753    pub fn global_write(
754        &mut self,
755        global_addr: GlobalAddr,
756        val: Value,
757    ) -> Result<(), RuntimeError> {
758        // 1. Let `gi` be the global instance `store.globals[globaladdr]`.
759        let gi = self.globals.get_mut(global_addr);
760
761        // 2. Let `mut t` be the structure of the global type `gi.type`.
762        let r#mut = gi.ty.is_mut;
763        let t = gi.ty.ty;
764
765        // 3. If `mut` is not `var`, then return error.
766        if !r#mut {
767            return Err(RuntimeError::WriteOnImmutableGlobal);
768        }
769
770        // Check invariant:
771        //   It is an invariant of the semantics that the value has a type equal to the value type of `globaltype`.
772        // See: WebAssembly Specification 2.0 - 4.2.9
773        if t != val.to_ty() {
774            return Err(RuntimeError::GlobalTypeMismatch);
775        }
776
777        // 4. Replace `gi.value` with the value `val`.
778        gi.value = val;
779
780        // 5. Return the updated store.
781        // This is a noop for us, as our store `self` is mutable.
782
783        Ok(())
784    }
785
786    /// roughly matches <https://webassembly.github.io/spec/core/exec/modules.html#functions> with the addition of sidetable pointer to the input signature
787    // TODO refactor the type of func
788    // TODO module_addr
789    fn alloc_func(&mut self, func: (TypeIdx, (Span, usize)), module_addr: ModuleAddr) -> FuncAddr {
790        let (ty, (span, stp)) = func;
791
792        // TODO rewrite this huge chunk of parsing after generic way to re-parse(?) structs lands
793        let mut wasm_reader = WasmReader::new(self.modules.get(module_addr).wasm_bytecode);
794        wasm_reader.move_start_to(span).unwrap_validated();
795
796        let (locals, bytes_read) = wasm_reader
797            .measure_num_read_bytes(crate::code::read_declared_locals)
798            .unwrap_validated();
799
800        let code_expr = wasm_reader
801            .make_span(span.len() - bytes_read)
802            .unwrap_validated();
803
804        // core of the method below
805
806        // validation guarantees func_ty_idx exists within module_inst.types
807        // TODO fix clone
808        let func_inst = FuncInst::WasmFunc(WasmFuncInst {
809            function_type: self.modules.get(module_addr).types[ty].clone(),
810            _ty: ty,
811            locals,
812            code_expr,
813            stp,
814            module_addr,
815        });
816        self.functions.insert(func_inst)
817    }
818
819    /// <https://webassembly.github.io/spec/core/exec/modules.html#tables>
820    fn alloc_table(&mut self, table_type: TableType, reff: Ref) -> TableAddr {
821        let table_inst = TableInst {
822            ty: table_type,
823            elem: vec![reff; table_type.lim.min as usize],
824        };
825
826        self.tables.insert(table_inst)
827    }
828
829    /// <https://webassembly.github.io/spec/core/exec/modules.html#memories>
830    fn alloc_mem(&mut self, mem_type: MemType) -> MemAddr {
831        let mem_inst = MemInst {
832            ty: mem_type,
833            mem: LinearMemory::new_with_initial_pages(
834                mem_type.limits.min.try_into().unwrap_validated(),
835            ),
836        };
837
838        self.memories.insert(mem_inst)
839    }
840
841    /// <https://webassembly.github.io/spec/core/exec/modules.html#globals>
842    fn alloc_global(&mut self, global_type: GlobalType, val: Value) -> GlobalAddr {
843        let global_inst = GlobalInst {
844            ty: global_type,
845            value: val,
846        };
847
848        self.globals.insert(global_inst)
849    }
850
851    /// <https://webassembly.github.io/spec/core/exec/modules.html#element-segments>
852    fn alloc_elem(&mut self, ref_type: RefType, refs: Vec<Ref>) -> ElemAddr {
853        let elem_inst = ElemInst {
854            _ty: ref_type,
855            references: refs,
856        };
857
858        self.elements.insert(elem_inst)
859    }
860
861    /// <https://webassembly.github.io/spec/core/exec/modules.html#data-segments>
862    fn alloc_data(&mut self, bytes: &[u8]) -> DataAddr {
863        let data_inst = DataInst {
864            data: Vec::from(bytes),
865        };
866
867        self.data.insert(data_inst)
868    }
869
870    /// This function allows an already instantiated module to be reregistered
871    /// under a different name. All previous registers of this module are not
872    /// affected.
873    ///
874    /// Note: This method exists as a temporary solution because our suboptimal registry
875    /// design. Because [`Store::add_module`] automatically registers all
876    /// modules directly after instantiation, we still need to provide some way
877    /// for only registering a module.
878    pub fn reregister_module(
879        &mut self,
880        module_addr: ModuleAddr,
881        name: &str,
882    ) -> Result<(), RuntimeError> {
883        self.registry
884            .register_module(name.to_owned().into(), self.modules.get(module_addr))
885    }
886
887    pub fn create_resumable(
888        &self,
889        func_addr: FuncAddr,
890        params: Vec<Value>,
891        maybe_fuel: Option<u32>,
892    ) -> Result<ResumableRef, RuntimeError> {
893        let func_inst = self.functions.get(func_addr);
894
895        let func_ty = func_inst.ty();
896
897        // Verify that the given parameters match the function parameters
898        let param_types = params.iter().map(|v| v.to_ty()).collect::<Vec<_>>();
899
900        if func_ty.params.valtypes != param_types {
901            trace!(
902                "Func param types len: {}; Given args len: {}",
903                func_ty.params.valtypes.len(),
904                param_types.len()
905            );
906            panic!("Invalid parameters for function");
907        }
908
909        Ok(ResumableRef::Fresh(FreshResumableRef {
910            func_addr,
911            params,
912            maybe_fuel,
913        }))
914    }
915
916    pub fn resume(&mut self, mut resumable_ref: ResumableRef) -> Result<RunState, RuntimeError> {
917        match resumable_ref {
918            ResumableRef::Fresh(FreshResumableRef {
919                func_addr,
920                params,
921                maybe_fuel,
922            }) => {
923                let func_inst = self.functions.get(func_addr);
924
925                match func_inst {
926                    FuncInst::HostFunc(host_func_inst) => {
927                        let returns = (host_func_inst.hostcode)(&mut self.user_data, params);
928
929                        debug!("Successfully invoked function");
930
931                        let returns = returns.map_err(|HaltExecutionError| {
932                            RuntimeError::HostFunctionHaltedExecution
933                        })?;
934
935                        // Verify that the return parameters match the host function parameters
936                        // since we have no validation guarantees for host functions
937
938                        let return_types = returns.iter().map(|v| v.to_ty()).collect::<Vec<_>>();
939                        if host_func_inst.function_type.returns.valtypes != return_types {
940                            trace!(
941                                "Func return types len: {}; returned args len: {}",
942                                host_func_inst.function_type.returns.valtypes.len(),
943                                return_types.len()
944                            );
945                            return Err(RuntimeError::HostFunctionSignatureMismatch);
946                        }
947
948                        Ok(RunState::Finished(returns))
949                    }
950                    FuncInst::WasmFunc(wasm_func_inst) => {
951                        // Prepare a new stack with the locals for the entry function
952                        let mut stack = Stack::new_with_values(params);
953
954                        stack.push_call_frame::<T>(
955                            FuncAddr::INVALID, // TODO using a default value like this is dangerous
956                            &wasm_func_inst.function_type,
957                            &wasm_func_inst.locals,
958                            usize::MAX,
959                            usize::MAX,
960                        )?;
961
962                        let mut resumable = Resumable {
963                            current_func_addr: func_addr,
964                            stack,
965                            pc: wasm_func_inst.code_expr.from,
966                            stp: wasm_func_inst.stp,
967                            maybe_fuel,
968                        };
969
970                        // Run the interpreter
971                        let result = interpreter_loop::run(&mut resumable, self)?;
972
973                        match result {
974                            None => {
975                                debug!("Successfully invoked function");
976                                Ok(RunState::Finished(resumable.stack.into_values()))
977                            }
978                            Some(required_fuel) => {
979                                debug!("Successfully invoked function, but ran out of fuel");
980                                Ok(RunState::Resumable {
981                                    resumable_ref: ResumableRef::Invoked(
982                                        self.dormitory.insert(resumable),
983                                    ),
984                                    required_fuel,
985                                })
986                            }
987                        }
988                    }
989                }
990            }
991            ResumableRef::Invoked(InvokedResumableRef {
992                dormitory: ref mut dormitory_weak,
993                ref key,
994            }) => {
995                // Resuming requires `self`'s dormitory to still be alive
996                let Some(dormitory) = dormitory_weak.upgrade() else {
997                    return Err(RuntimeError::ResumableNotFound);
998                };
999
1000                // Check the given `RuntimeInstance` is the same one used to create `self`
1001                if !Arc::ptr_eq(&dormitory, &self.dormitory.0) {
1002                    return Err(RuntimeError::ResumableNotFound);
1003                }
1004
1005                // Obtain a write lock to the `Dormitory`
1006                let mut dormitory = dormitory.write();
1007
1008                // TODO We might want to remove the `Resumable` here already and later reinsert it.
1009                // This would prevent holding the lock across the interpreter loop.
1010                let resumable = dormitory
1011                    .get_mut(key)
1012                    .expect("the key to always be valid as self was not dropped yet");
1013
1014                // Resume execution
1015                let result = interpreter_loop::run(resumable, self)?;
1016
1017                match result {
1018                    None => {
1019                        let resumable = dormitory.remove(key)
1020                            .expect("that the resumable could not have been removed already, because then this self could not exist");
1021
1022                        // Take the `Weak` pointing to the dormitory out of `self` and replace it with a default `Weak`.
1023                        // This causes the `Drop` impl of `self` to directly quit preventing it from unnecessarily locking the dormitory.
1024                        let _dormitory = mem::take(dormitory_weak);
1025
1026                        Ok(RunState::Finished(resumable.stack.into_values()))
1027                    }
1028                    Some(required_fuel) => Ok(RunState::Resumable {
1029                        resumable_ref,
1030                        required_fuel,
1031                    }),
1032                }
1033            }
1034        }
1035    }
1036
1037    pub fn access_fuel_mut<R>(
1038        &mut self,
1039        resumable_ref: &mut ResumableRef,
1040        f: impl FnOnce(&mut Option<u32>) -> R,
1041    ) -> Result<R, RuntimeError> {
1042        match resumable_ref {
1043            ResumableRef::Fresh(FreshResumableRef { maybe_fuel, .. }) => Ok(f(maybe_fuel)),
1044            ResumableRef::Invoked(resumable_ref) => {
1045                // Resuming requires `self`'s dormitory to still be alive
1046                let Some(dormitory) = resumable_ref.dormitory.upgrade() else {
1047                    return Err(RuntimeError::ResumableNotFound);
1048                };
1049
1050                // Check the given `RuntimeInstance` is the same one used to create `self`
1051                if !Arc::ptr_eq(&dormitory, &self.dormitory.0) {
1052                    return Err(RuntimeError::ResumableNotFound);
1053                }
1054
1055                let mut dormitory = dormitory.write();
1056
1057                let resumable = dormitory
1058                    .get_mut(&resumable_ref.key)
1059                    .expect("the key to always be valid as self was not dropped yet");
1060
1061                Ok(f(&mut resumable.maybe_fuel))
1062            }
1063        }
1064    }
1065}
1066
1067/// A marker error for host functions to return, in case they want execution to be halted.
1068pub struct HaltExecutionError;
1069
1070///<https://webassembly.github.io/spec/core/exec/runtime.html#external-values>
1071#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1072pub enum ExternVal {
1073    Func(FuncAddr),
1074    Table(TableAddr),
1075    Mem(MemAddr),
1076    Global(GlobalAddr),
1077}
1078
1079impl ExternVal {
1080    /// returns the external type of `self` according to typing relation,
1081    /// taking `store` as context S.
1082    ///
1083    /// Note: This method may panic if self does not come from the given [`Store`].
1084    ///<https://webassembly.github.io/spec/core/valid/modules.html#imports>
1085    pub fn extern_type<T: Config>(&self, store: &Store<T>) -> ExternType {
1086        match self {
1087            // TODO: fix ugly clone in function types
1088            ExternVal::Func(func_addr) => ExternType::Func(store.functions.get(*func_addr).ty()),
1089            ExternVal::Table(table_addr) => ExternType::Table(store.tables.get(*table_addr).ty),
1090            ExternVal::Mem(mem_addr) => ExternType::Mem(store.memories.get(*mem_addr).ty),
1091            ExternVal::Global(global_addr) => {
1092                ExternType::Global(store.globals.get(*global_addr).ty)
1093            }
1094        }
1095    }
1096}
1097
1098/// common convention functions defined for lists of ExternVals, ExternTypes, Exports
1099/// <https://webassembly.github.io/spec/core/exec/runtime.html#conventions>
1100/// <https://webassembly.github.io/spec/core/syntax/types.html#id3>
1101/// <https://webassembly.github.io/spec/core/syntax/modules.html?highlight=convention#id1>
1102// TODO implement this trait for ExternType lists Export lists
1103pub trait ExternFilterable {
1104    fn funcs(self) -> impl Iterator<Item = FuncAddr>;
1105    fn tables(self) -> impl Iterator<Item = TableAddr>;
1106    fn mems(self) -> impl Iterator<Item = MemAddr>;
1107    fn globals(self) -> impl Iterator<Item = GlobalAddr>;
1108}
1109
1110impl<'a, I> ExternFilterable for I
1111where
1112    I: Iterator<Item = &'a ExternVal>,
1113{
1114    fn funcs(self) -> impl Iterator<Item = FuncAddr> {
1115        self.filter_map(|extern_val| {
1116            if let ExternVal::Func(func_addr) = extern_val {
1117                Some(*func_addr)
1118            } else {
1119                None
1120            }
1121        })
1122    }
1123
1124    fn tables(self) -> impl Iterator<Item = TableAddr> {
1125        self.filter_map(|extern_val| {
1126            if let ExternVal::Table(table_addr) = extern_val {
1127                Some(*table_addr)
1128            } else {
1129                None
1130            }
1131        })
1132    }
1133
1134    fn mems(self) -> impl Iterator<Item = MemAddr> {
1135        self.filter_map(|extern_val| {
1136            if let ExternVal::Mem(mem_addr) = extern_val {
1137                Some(*mem_addr)
1138            } else {
1139                None
1140            }
1141        })
1142    }
1143
1144    fn globals(self) -> impl Iterator<Item = GlobalAddr> {
1145        self.filter_map(|extern_val| {
1146            if let ExternVal::Global(global_addr) = extern_val {
1147                Some(*global_addr)
1148            } else {
1149                None
1150            }
1151        })
1152    }
1153}