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