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