wasm/execution/
store.rs

1use crate::core::error::Result as CustomResult;
2use crate::core::indices::TypeIdx;
3use crate::core::reader::span::Span;
4use crate::core::reader::types::data::{DataModeActive, DataSegment};
5use crate::core::reader::types::element::{ActiveElem, ElemItems, ElemMode, ElemType};
6use crate::core::reader::types::export::{Export, ExportDesc};
7use crate::core::reader::types::global::{Global, GlobalType};
8use crate::core::reader::types::import::Import;
9use crate::core::reader::types::{
10    ExternType, FuncType, ImportSubTypeRelation, MemType, TableType, ValType,
11};
12use crate::core::reader::WasmReader;
13use crate::core::sidetable::Sidetable;
14use crate::execution::interpreter_loop::{self, memory_init, table_init};
15use crate::execution::value::{Ref, Value};
16use crate::execution::{run_const_span, Stack};
17use crate::registry::Registry;
18use crate::value::FuncAddr;
19use crate::{Error, Limits, RefType, RuntimeError, ValidationInfo};
20use alloc::borrow::ToOwned;
21use alloc::collections::btree_map::BTreeMap;
22use alloc::string::String;
23use alloc::vec;
24use alloc::vec::Vec;
25
26use super::hooks::EmptyHookSet;
27use super::interpreter_loop::{data_drop, elem_drop};
28use super::locals::Locals;
29use super::value::ExternAddr;
30use super::UnwrapValidatedExt;
31
32use crate::linear_memory::LinearMemory;
33
34/// The store represents all global state that can be manipulated by WebAssembly programs. It
35/// consists of the runtime representation of all instances of functions, tables, memories, and
36/// globals, element segments, and data segments that have been allocated during the life time of
37/// the abstract machine.
38/// <https://webassembly.github.io/spec/core/exec/runtime.html#store>
39#[derive(Default, Debug)]
40pub struct Store<'b> {
41    pub functions: Vec<FuncInst>,
42    pub memories: Vec<MemInst>,
43    pub globals: Vec<GlobalInst>,
44    pub data: Vec<DataInst>,
45    pub tables: Vec<TableInst>,
46    pub elements: Vec<ElemInst>,
47    pub modules: Vec<ModuleInst<'b>>,
48
49    // fields outside of the spec but are convenient are below
50
51    // all visible exports and entities added by hand or module instantiation by the interpreter
52    // currently, all of the exports of an instantiated module is made visible (this is outside of spec)
53    pub registry: Registry,
54}
55
56impl<'b> Store<'b> {
57    /// instantiates a validated module with `validation_info` as validation evidence with name `name`
58    /// with the steps in <https://webassembly.github.io/spec/core/exec/modules.html#instantiation>
59    /// this method roughly matches the suggested embedder function`module_instantiate`
60    /// <https://webassembly.github.io/spec/core/appendix/embedding.html#modules>
61    /// except external values for module instantiation are retrieved from `self`.
62    pub fn add_module(
63        &mut self,
64        name: &str,
65        validation_info: &ValidationInfo<'b>,
66    ) -> CustomResult<()> {
67        // instantiation step -1: collect extern_vals, this section basically acts as a linker between modules
68        // best attempt at trying to match the spec implementation in terms of errors
69        debug!("adding module with name {:?}", name);
70        let mut extern_vals = Vec::new();
71
72        for Import {
73            module_name: exporting_module_name,
74            name: import_name,
75            desc: import_desc,
76        } in &validation_info.imports
77        {
78            trace!(
79                "trying to import from exporting module instance named {:?}, the entity with name {:?} with desc: {:?}",
80                exporting_module_name,
81                import_name,
82                import_desc
83            );
84            let import_extern_type = import_desc.extern_type(validation_info)?;
85            let export_extern_val_candidate = *self.registry.lookup(
86                exporting_module_name.clone().into(),
87                import_name.clone().into(),
88            )?;
89            trace!("export candidate found: {:?}", export_extern_val_candidate);
90            if !export_extern_val_candidate
91                .extern_type(self)?
92                .is_subtype_of(&import_extern_type)
93            {
94                return Err(Error::InvalidImportType);
95            }
96            trace!("import and export matches. Adding to externvals");
97            extern_vals.push(export_extern_val_candidate)
98        }
99
100        // instantiation: step 5
101        // 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.
102        // therefore I am mimicking the reference interpreter code here, I will allocate functions in the store in this step instead of step 11.
103        // https://github.com/WebAssembly/spec/blob/8d6792e3d6709e8d3e90828f9c8468253287f7ed/interpreter/exec/eval.ml#L789
104        let mut module_inst = ModuleInst {
105            types: validation_info.types.clone(),
106            func_addrs: extern_vals.iter().funcs().collect(),
107            table_addrs: Vec::new(),
108            mem_addrs: Vec::new(),
109            global_addrs: extern_vals.iter().globals().collect(),
110            elem_addrs: Vec::new(),
111            data_addrs: Vec::new(),
112            exports: BTreeMap::new(),
113            wasm_bytecode: validation_info.wasm,
114            sidetable: validation_info.sidetable.clone(),
115        };
116
117        // TODO rewrite this part
118        // <https://webassembly.github.io/spec/core/exec/modules.html#functions>
119        let func_addrs: Vec<usize> = validation_info
120            .functions
121            .iter()
122            .zip(validation_info.func_blocks_stps.iter())
123            .map(|(ty_idx, (span, stp))| {
124                self.alloc_func((*ty_idx, (*span, *stp)), &module_inst, self.modules.len())
125            })
126            .collect();
127
128        module_inst.func_addrs.extend(func_addrs);
129
130        // instantiation: this roughly matches step 6,7,8
131        // validation guarantees these will evaluate without errors.
132        let maybe_global_init_vals: Result<Vec<Value>, _> = validation_info
133            .globals
134            .iter()
135            .map(|global| {
136                run_const_span(validation_info.wasm, &global.init_expr, &module_inst, self)
137                    .transpose()
138                    .unwrap_validated()
139            })
140            .collect();
141        let global_init_vals = maybe_global_init_vals?;
142
143        // instantiation: this roughly matches step 9,10
144
145        let mut element_init_ref_lists: Vec<Vec<Ref>> =
146            Vec::with_capacity(validation_info.elements.len());
147
148        for elem in &validation_info.elements {
149            let mut new_list = Vec::new();
150            match &elem.init {
151                // shortcut of evaluation of "ref.func <func_idx>; end;"
152                // validation guarantees corresponding func_idx's existence
153                ElemItems::RefFuncs(ref_funcs) => {
154                    for func_idx in ref_funcs {
155                        new_list.push(Ref::Func(FuncAddr {
156                            addr: Some(module_inst.func_addrs[*func_idx as usize]),
157                        }))
158                    }
159                }
160                ElemItems::Exprs(_, exprs) => {
161                    for expr in exprs {
162                        new_list.push(
163                            run_const_span(validation_info.wasm, expr, &module_inst, self)?
164                                .unwrap_validated()
165                                .into(),
166                        )
167                    }
168                }
169            }
170            element_init_ref_lists.push(new_list);
171        }
172
173        // instantiation: step 11 - module allocation (except function allocation - which was made in step 5)
174        // https://webassembly.github.io/spec/core/exec/modules.html#alloc-module
175
176        // allocation: begin
177
178        // allocation: step 1
179        let module = validation_info;
180
181        let extern_vals = extern_vals;
182        let vals = global_init_vals;
183        let ref_lists = element_init_ref_lists;
184
185        // allocation: skip step 2 as it was done in instantiation step 5
186
187        // allocation: step 3-13
188        let table_addrs: Vec<usize> = module
189            .tables
190            .iter()
191            .map(|table_type| {
192                let null_ref = match table_type.et {
193                    RefType::FuncRef => Ref::Func(FuncAddr { addr: None }),
194                    RefType::ExternRef => Ref::Extern(ExternAddr { addr: None }),
195                };
196                self.alloc_table(*table_type, null_ref)
197            })
198            .collect();
199        let mem_addrs: Vec<usize> = module
200            .memories
201            .iter()
202            .map(|mem_type| self.alloc_mem(*mem_type))
203            .collect();
204        let global_addrs: Vec<usize> = module
205            .globals
206            .iter()
207            .zip(vals)
208            .map(
209                |(
210                    Global {
211                        ty: global_type, ..
212                    },
213                    val,
214                )| self.alloc_global(*global_type, val),
215            )
216            .collect();
217        let elem_addrs = module
218            .elements
219            .iter()
220            .zip(ref_lists)
221            .map(|(elem, refs)| self.alloc_elem(elem.ty(), refs))
222            .collect();
223        let data_addrs = module
224            .data
225            .iter()
226            .map(|DataSegment { init: bytes, .. }| self.alloc_data(bytes))
227            .collect();
228
229        // allocation: skip step 14 as it was done in instantiation step 5
230
231        // allocation: step 15,16
232        let mut table_addrs_mod: Vec<usize> = extern_vals.iter().tables().collect();
233        table_addrs_mod.extend(table_addrs);
234
235        let mut mem_addrs_mod: Vec<usize> = extern_vals.iter().mems().collect();
236        mem_addrs_mod.extend(mem_addrs);
237
238        // skipping step 17 partially as it was partially done in instantiation step
239        module_inst.global_addrs.extend(global_addrs);
240
241        // allocation: step 18,19
242        let export_insts: BTreeMap<String, ExternVal> = module
243            .exports
244            .iter()
245            .map(|Export { name, desc }| {
246                let value = match desc {
247                    ExportDesc::FuncIdx(func_idx) => {
248                        ExternVal::Func(module_inst.func_addrs[*func_idx])
249                    }
250                    ExportDesc::TableIdx(table_idx) => {
251                        ExternVal::Table(table_addrs_mod[*table_idx])
252                    }
253                    ExportDesc::MemIdx(mem_idx) => ExternVal::Mem(mem_addrs_mod[*mem_idx]),
254                    ExportDesc::GlobalIdx(global_idx) => {
255                        ExternVal::Global(module_inst.global_addrs[*global_idx])
256                    }
257                };
258                (String::from(name), value)
259            })
260            .collect();
261
262        // allocation: step 20,21 initialize module (except functions and globals due to instantiation step 5, allocation step 14,17)
263        module_inst.table_addrs = table_addrs_mod;
264        module_inst.mem_addrs = mem_addrs_mod;
265        module_inst.elem_addrs = elem_addrs;
266        module_inst.data_addrs = data_addrs;
267        module_inst.exports = export_insts;
268
269        // allocation: end
270
271        // register module exports, this is outside of the spec
272        self.registry
273            .register_module(name.to_owned().into(), &module_inst)?;
274
275        // instantiation step 11 end: module_inst properly allocated after this point.
276        // TODO: it is too hard with our codebase to do the following steps without adding the module to the store
277        let current_module_idx = self.modules.len();
278        self.modules.push(module_inst);
279
280        // instantiation: step 12-15
281        // TODO have to stray away from the spec a bit since our codebase does not lend itself well to freely executing instructions by themselves
282        for (
283            i,
284            ElemType {
285                init: elem_items,
286                mode,
287            },
288        ) in validation_info.elements.iter().enumerate()
289        {
290            match mode {
291                ElemMode::Active(ActiveElem {
292                    table_idx: table_idx_i,
293                    init_expr: einstr_i,
294                }) => {
295                    let n = elem_items.len() as i32;
296                    // equivalent to init.len() in spec
297                    // instantiation step 14:
298                    // TODO (for now, we are doing hopefully what is equivalent to it)
299                    // execute:
300                    //   einstr_i
301                    //   i32.const 0
302                    //   i32.const n
303                    //   table.init table_idx_i i
304                    //   elem.drop i
305                    let d: i32 = run_const_span(
306                        validation_info.wasm,
307                        einstr_i,
308                        &self.modules[current_module_idx],
309                        self,
310                    )?
311                    .unwrap_validated()
312                    .into();
313                    let s = 0;
314                    table_init(
315                        &self.modules,
316                        &mut self.tables,
317                        &self.elements,
318                        current_module_idx,
319                        i,
320                        *table_idx_i as usize,
321                        n,
322                        s,
323                        d,
324                    )
325                    .map_err(Error::RuntimeError)?;
326                    elem_drop(&self.modules, &mut self.elements, current_module_idx, i)
327                        .map_err(Error::RuntimeError)?;
328                }
329                ElemMode::Declarative => {
330                    // instantiation step 15:
331                    // TODO (for now, we are doing hopefully what is equivalent to it)
332                    // execute:
333                    //   elem.drop i
334                    elem_drop(&self.modules, &mut self.elements, current_module_idx, i)
335                        .map_err(Error::RuntimeError)?;
336                }
337                ElemMode::Passive => (),
338            }
339        }
340
341        // instantiation: step 16
342        // TODO have to stray away from the spec a bit since our codebase does not lend itself well to freely executing instructions by themselves
343        for (i, DataSegment { init, mode }) in validation_info.data.iter().enumerate() {
344            match mode {
345                crate::core::reader::types::data::DataMode::Active(DataModeActive {
346                    memory_idx,
347                    offset: dinstr_i,
348                }) => {
349                    let n = init.len() as i32;
350                    // assert: mem_idx is 0
351                    if *memory_idx != 0 {
352                        // TODO fix error
353                        return Err(Error::MoreThanOneMemory);
354                    }
355
356                    // TODO (for now, we are doing hopefully what is equivalent to it)
357                    // execute:
358                    //   dinstr_i
359                    //   i32.const 0
360                    //   i32.const n
361                    //   memory.init i
362                    //   data.drop i
363                    let d: i32 = run_const_span(
364                        validation_info.wasm,
365                        dinstr_i,
366                        &self.modules[current_module_idx],
367                        self,
368                    )?
369                    .unwrap_validated()
370                    .into();
371                    let s = 0;
372                    memory_init(
373                        &self.modules,
374                        &mut self.memories,
375                        &self.data,
376                        current_module_idx,
377                        i,
378                        0,
379                        n,
380                        s,
381                        d,
382                    )
383                    .map_err(Error::RuntimeError)?;
384                    data_drop(&self.modules, &mut self.data, current_module_idx, i)
385                        .map_err(Error::RuntimeError)?;
386                }
387                crate::core::reader::types::data::DataMode::Passive => (),
388            }
389        }
390
391        // instantiation: step 17
392        if let Some(func_idx) = validation_info.start {
393            // TODO (for now, we are doing hopefully what is equivalent to it)
394            // execute
395            //   call func_ifx
396            let func_addr = self.modules[current_module_idx].func_addrs[func_idx];
397            self.invoke(func_addr, Vec::new())
398                .map_err(Error::RuntimeError)?;
399        };
400
401        Ok(())
402    }
403
404    /// roughly matches <https://webassembly.github.io/spec/core/exec/modules.html#functions> with the addition of sidetable pointer to the input signature
405    // TODO refactor the type of func
406    // TODO module_addr
407    fn alloc_func(
408        &mut self,
409        func: (TypeIdx, (Span, usize)),
410        module_inst: &ModuleInst,
411        module_addr: usize,
412    ) -> usize {
413        let (ty, (span, stp)) = func;
414
415        // TODO rewrite this huge chunk of parsing after generic way to re-parse(?) structs lands
416        let mut wasm_reader = WasmReader::new(module_inst.wasm_bytecode);
417        wasm_reader.move_start_to(span).unwrap_validated();
418
419        let (locals, bytes_read) = wasm_reader
420            .measure_num_read_bytes(crate::code::read_declared_locals)
421            .unwrap_validated();
422
423        let code_expr = wasm_reader
424            .make_span(span.len() - bytes_read)
425            .unwrap_validated();
426
427        // core of the method below
428
429        // validation guarantees func_ty_idx exists within module_inst.types
430        // TODO fix clone
431        let func_inst = FuncInst::WasmFunc(WasmFuncInst {
432            function_type: module_inst.types[ty].clone(),
433            ty,
434            locals,
435            code_expr,
436            stp,
437            module_addr,
438        });
439
440        let addr = self.functions.len();
441        self.functions.push(func_inst);
442        addr
443    }
444
445    /// <https://webassembly.github.io/spec/core/exec/modules.html#host-functions>
446    pub(super) fn alloc_host_func(
447        &mut self,
448        func_type: FuncType,
449        host_func: fn(Vec<Value>) -> Vec<Value>,
450    ) -> usize {
451        let func_inst = FuncInst::HostFunc(HostFuncInst {
452            function_type: func_type,
453            hostcode: host_func,
454        });
455        let addr = self.functions.len();
456        self.functions.push(func_inst);
457        addr
458    }
459
460    /// <https://webassembly.github.io/spec/core/exec/modules.html#tables>
461    fn alloc_table(&mut self, table_type: TableType, reff: Ref) -> usize {
462        let table_inst = TableInst {
463            ty: table_type,
464            elem: vec![reff; table_type.lim.min as usize],
465        };
466
467        let addr = self.tables.len();
468        self.tables.push(table_inst);
469        addr
470    }
471
472    /// <https://webassembly.github.io/spec/core/exec/modules.html#memories>
473    fn alloc_mem(&mut self, mem_type: MemType) -> usize {
474        let mem_inst = MemInst {
475            ty: mem_type,
476            mem: LinearMemory::new_with_initial_pages(
477                mem_type.limits.min.try_into().unwrap_validated(),
478            ),
479        };
480
481        let addr = self.memories.len();
482        self.memories.push(mem_inst);
483        addr
484    }
485
486    /// <https://webassembly.github.io/spec/core/exec/modules.html#globals>
487    fn alloc_global(&mut self, global_type: GlobalType, val: Value) -> usize {
488        let global_inst = GlobalInst {
489            ty: global_type,
490            value: val,
491        };
492
493        let addr = self.globals.len();
494        self.globals.push(global_inst);
495        addr
496    }
497
498    /// <https://webassembly.github.io/spec/core/exec/modules.html#element-segments>
499    fn alloc_elem(&mut self, ref_type: RefType, refs: Vec<Ref>) -> usize {
500        let elem_inst = ElemInst {
501            ty: ref_type,
502            references: refs,
503        };
504
505        let addr = self.elements.len();
506        self.elements.push(elem_inst);
507        addr
508    }
509
510    /// <https://webassembly.github.io/spec/core/exec/modules.html#data-segments>
511    fn alloc_data(&mut self, bytes: &[u8]) -> usize {
512        let data_inst = DataInst {
513            data: Vec::from(bytes),
514        };
515
516        let addr = self.data.len();
517        self.data.push(data_inst);
518        addr
519    }
520
521    pub fn invoke(
522        &mut self,
523        func_addr: usize,
524        params: Vec<Value>,
525    ) -> Result<Vec<Value>, RuntimeError> {
526        let func_inst = self
527            .functions
528            .get(func_addr)
529            .ok_or(RuntimeError::FunctionNotFound)?;
530
531        let func_ty = func_inst.ty();
532
533        // Verify that the given parameters match the function parameters
534        let param_types = params.iter().map(|v| v.to_ty()).collect::<Vec<_>>();
535
536        if func_ty.params.valtypes != param_types {
537            trace!(
538                "Func param types len: {}; Given args len: {}",
539                func_ty.params.valtypes.len(),
540                param_types.len()
541            );
542            panic!("Invalid parameters for function");
543        }
544
545        match &func_inst {
546            FuncInst::HostFunc(host_func_inst) => {
547                let returns = (host_func_inst.hostcode)(params);
548                debug!("Successfully invoked function");
549
550                // Verify that the return parameters match the host function parameters
551                // since we have no validation guarantees for host functions
552
553                let return_types = returns.iter().map(|v| v.to_ty()).collect::<Vec<_>>();
554                if func_ty.returns.valtypes != return_types {
555                    trace!(
556                        "Func param types len: {}; Given args len: {}",
557                        func_ty.params.valtypes.len(),
558                        param_types.len()
559                    );
560                    return Err(RuntimeError::HostFunctionSignatureMismatch);
561                }
562
563                Ok(returns)
564            }
565            FuncInst::WasmFunc(wasm_func_inst) => {
566                // Prepare a new stack with the locals for the entry function
567                let mut stack = Stack::new();
568                let locals = Locals::new(params.into_iter(), wasm_func_inst.locals.iter().cloned());
569
570                stack.push_stackframe(usize::MAX, &func_ty, locals, usize::MAX, usize::MAX)?;
571
572                // Run the interpreter
573                interpreter_loop::run(
574                    // &mut self.modules,
575                    func_addr,
576                    // self.lut.as_ref().ok_or(RuntimeError::UnmetImport)?,
577                    &mut stack,
578                    EmptyHookSet,
579                    self,
580                )?;
581                debug!("Successfully invoked function");
582                Ok(stack.into_values())
583            }
584        }
585    }
586}
587
588#[derive(Debug)]
589// TODO does not match the spec FuncInst
590pub enum FuncInst {
591    WasmFunc(WasmFuncInst),
592    HostFunc(HostFuncInst),
593}
594
595#[derive(Debug)]
596pub struct WasmFuncInst {
597    pub function_type: FuncType,
598    pub ty: TypeIdx,
599    pub locals: Vec<ValType>,
600    pub code_expr: Span,
601    ///index of the sidetable corresponding to the beginning of this functions code
602    pub stp: usize,
603
604    // implicit back ref required for function invocation and is in the spec
605    // TODO module_addr or module ref?
606    pub module_addr: usize,
607}
608
609#[derive(Debug)]
610pub struct HostFuncInst {
611    pub function_type: FuncType,
612    pub hostcode: fn(Vec<Value>) -> Vec<Value>,
613}
614
615impl FuncInst {
616    pub fn ty(&self) -> FuncType {
617        match self {
618            FuncInst::WasmFunc(wasm_func_inst) => wasm_func_inst.function_type.clone(),
619            FuncInst::HostFunc(host_func_inst) => host_func_inst.function_type.clone(),
620        }
621    }
622}
623
624#[derive(Clone, Debug)]
625/// <https://webassembly.github.io/spec/core/exec/runtime.html#element-instances>
626pub struct ElemInst {
627    pub ty: RefType,
628    pub references: Vec<Ref>,
629}
630
631impl ElemInst {
632    pub fn len(&self) -> usize {
633        self.references.len()
634    }
635    pub fn is_empty(&self) -> bool {
636        self.references.is_empty()
637    }
638}
639
640// TODO: The tables have to be both imported and exported (an enum instead of a struct)
641//       That is because when we import tables we can give a different size to the imported table
642//        thus having a wrapper over the initial table
643#[derive(Debug)]
644pub struct TableInst {
645    pub ty: TableType,
646    pub elem: Vec<Ref>,
647}
648
649impl TableInst {
650    pub fn len(&self) -> usize {
651        self.elem.len()
652    }
653
654    pub fn is_empty(&self) -> bool {
655        self.elem.is_empty()
656    }
657
658    pub fn new(ty: TableType) -> Self {
659        Self {
660            ty,
661            elem: vec![Ref::default_from_ref_type(ty.et); ty.lim.min as usize],
662        }
663    }
664
665    /// <https://webassembly.github.io/spec/core/exec/modules.html#growing-tables>
666    pub fn grow(&mut self, n: u32, reff: Ref) -> Result<(), RuntimeError> {
667        // TODO refactor error, the spec Table.grow raises Table.{SizeOverflow, SizeLimit, OutOfMemory}
668        let len = n
669            .checked_add(self.elem.len() as u32)
670            .ok_or(RuntimeError::TableAccessOutOfBounds)?;
671
672        // roughly matches step 4,5,6
673        // checks limits_prime.valid() for limits_prime := { min: len, max: self.ty.lim.max }
674        // https://webassembly.github.io/spec/core/valid/types.html#limits
675        if self.ty.lim.max.map(|max| len > max).unwrap_or(false) {
676            return Err(RuntimeError::TableAccessOutOfBounds);
677        }
678        let limits_prime = Limits {
679            min: len,
680            max: self.ty.lim.max,
681        };
682
683        self.elem.extend(vec![reff; n as usize]);
684
685        self.ty.lim = limits_prime;
686        Ok(())
687    }
688}
689
690pub struct MemInst {
691    #[allow(warnings)]
692    pub ty: MemType,
693    pub mem: LinearMemory,
694}
695impl core::fmt::Debug for MemInst {
696    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
697        f.debug_struct("MemInst")
698            .field("ty", &self.ty)
699            .finish_non_exhaustive()
700    }
701}
702
703impl MemInst {
704    pub fn new(ty: MemType) -> Self {
705        Self {
706            ty,
707            mem: LinearMemory::new_with_initial_pages(ty.limits.min.try_into().unwrap()),
708        }
709    }
710
711    /// <https://webassembly.github.io/spec/core/exec/modules.html#growing-memories>
712    pub fn grow(&mut self, n: u32) -> Result<(), RuntimeError> {
713        // TODO refactor error, the spec Table.grow raises Memory.{SizeOverflow, SizeLimit, OutOfMemory}
714        let len = n + self.mem.pages() as u32;
715        if len > Limits::MAX_MEM_PAGES {
716            return Err(RuntimeError::MemoryAccessOutOfBounds);
717        }
718
719        // roughly matches step 4,5,6
720        // checks limits_prime.valid() for limits_prime := { min: len, max: self.ty.lim.max }
721        // https://webassembly.github.io/spec/core/valid/types.html#limits
722        if self.ty.limits.max.map(|max| len > max).unwrap_or(false) {
723            return Err(RuntimeError::MemoryAccessOutOfBounds);
724        }
725        let limits_prime = Limits {
726            min: len,
727            max: self.ty.limits.max,
728        };
729
730        self.mem.grow(n.try_into().unwrap());
731
732        self.ty.limits = limits_prime;
733        Ok(())
734    }
735
736    /// Can never be bigger than 65,356 pages
737    pub fn size(&self) -> usize {
738        self.mem.len() / (crate::Limits::MEM_PAGE_SIZE as usize)
739    }
740}
741
742// pub struct GlobalInstV2 {
743//     Local(LocalGlobalInst),
744//     Imported(ImportedGlobalInst)
745// }
746
747#[derive(Debug)]
748pub struct GlobalInst {
749    pub ty: GlobalType,
750    /// Must be of the same type as specified in `ty`
751    pub value: Value,
752}
753
754pub struct DataInst {
755    pub data: Vec<u8>,
756}
757
758impl core::fmt::Debug for DataInst {
759    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
760        f.debug_struct("DataInst").finish_non_exhaustive()
761    }
762}
763
764///<https://webassembly.github.io/spec/core/exec/runtime.html#external-values>
765#[derive(Debug, Copy, Clone, PartialEq, Eq)]
766pub enum ExternVal {
767    Func(usize),
768    Table(usize),
769    Mem(usize),
770    Global(usize),
771}
772
773impl ExternVal {
774    /// returns the external type of `self` according to typing relation,
775    /// taking `store` as context S.
776    /// typing fails if this external value does not exist within S.
777    ///<https://webassembly.github.io/spec/core/valid/modules.html#imports>
778    pub fn extern_type(&self, store: &Store) -> CustomResult<ExternType> {
779        // TODO: implement proper errors
780        Ok(match self {
781            // TODO: fix ugly clone in function types
782            ExternVal::Func(func_addr) => ExternType::Func(
783                store
784                    .functions
785                    .get(*func_addr)
786                    .ok_or(Error::InvalidImportType)?
787                    .ty(),
788            ),
789            ExternVal::Table(table_addr) => ExternType::Table(
790                store
791                    .tables
792                    .get(*table_addr)
793                    .ok_or(Error::InvalidImportType)?
794                    .ty,
795            ),
796            ExternVal::Mem(mem_addr) => ExternType::Mem(
797                store
798                    .memories
799                    .get(*mem_addr)
800                    .ok_or(Error::InvalidImportType)?
801                    .ty,
802            ),
803            ExternVal::Global(global_addr) => ExternType::Global(
804                store
805                    .globals
806                    .get(*global_addr)
807                    .ok_or(Error::InvalidImportType)?
808                    .ty,
809            ),
810        })
811    }
812}
813
814/// common convention functions defined for lists of ExternVals, ExternTypes, Exports
815/// <https://webassembly.github.io/spec/core/exec/runtime.html#conventions>
816/// <https://webassembly.github.io/spec/core/syntax/types.html#id3>
817/// <https://webassembly.github.io/spec/core/syntax/modules.html?highlight=convention#id1>
818// TODO implement this trait for ExternType lists Export lists
819pub trait ExternFilterable<T> {
820    fn funcs(self) -> impl Iterator<Item = T>;
821    fn tables(self) -> impl Iterator<Item = T>;
822    fn mems(self) -> impl Iterator<Item = T>;
823    fn globals(self) -> impl Iterator<Item = T>;
824}
825
826impl<'a, I> ExternFilterable<usize> for I
827where
828    I: Iterator<Item = &'a ExternVal>,
829{
830    fn funcs(self) -> impl Iterator<Item = usize> {
831        self.filter_map(|extern_val| {
832            if let ExternVal::Func(func_addr) = extern_val {
833                Some(*func_addr)
834            } else {
835                None
836            }
837        })
838    }
839
840    fn tables(self) -> impl Iterator<Item = usize> {
841        self.filter_map(|extern_val| {
842            if let ExternVal::Table(table_addr) = extern_val {
843                Some(*table_addr)
844            } else {
845                None
846            }
847        })
848    }
849
850    fn mems(self) -> impl Iterator<Item = usize> {
851        self.filter_map(|extern_val| {
852            if let ExternVal::Mem(mem_addr) = extern_val {
853                Some(*mem_addr)
854            } else {
855                None
856            }
857        })
858    }
859
860    fn globals(self) -> impl Iterator<Item = usize> {
861        self.filter_map(|extern_val| {
862            if let ExternVal::Global(global_addr) = extern_val {
863                Some(*global_addr)
864            } else {
865                None
866            }
867        })
868    }
869}
870
871///<https://webassembly.github.io/spec/core/exec/runtime.html#module-instances>
872#[derive(Debug)]
873pub struct ModuleInst<'b> {
874    pub types: Vec<FuncType>,
875    pub func_addrs: Vec<usize>,
876    pub table_addrs: Vec<usize>,
877    pub mem_addrs: Vec<usize>,
878    pub global_addrs: Vec<usize>,
879    pub elem_addrs: Vec<usize>,
880    pub data_addrs: Vec<usize>,
881    ///<https://webassembly.github.io/spec/core/exec/runtime.html#export-instances>
882    /// matches the list of ExportInst structs in the spec, however the spec never uses the name attribute
883    /// except during linking, which is up to the embedder to implement.
884    /// therefore this is a map data structure instead.
885    pub exports: BTreeMap<String, ExternVal>,
886
887    // TODO the bytecode is not in the spec, but required for re-parsing
888    pub wasm_bytecode: &'b [u8],
889
890    // sidetable is not in the spec, but required for control flow
891    pub sidetable: Sidetable,
892}