wasm/execution/
store.rs

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