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::{memory_init, table_init};
15use crate::execution::value::{Ref, Value};
16use crate::execution::{run_const_span, Stack};
17use crate::value::FuncAddr;
18use crate::{Error, Limits, RefType, RuntimeError, ValidationInfo};
19use alloc::borrow::ToOwned;
20use alloc::collections::btree_map::BTreeMap;
21use alloc::string::String;
22use alloc::vec;
23use alloc::vec::Vec;
24
25use super::hooks::EmptyHookSet;
26use super::interpreter_loop::{data_drop, elem_drop};
27use super::locals::Locals;
28use super::value::{ExternAddr, InteropValueList};
29use super::{run, 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(Default, Debug)]
39pub struct Store<'b> {
40    pub functions: Vec<FuncInst>,
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    pub module_names: BTreeMap<String, usize>,
48}
49
50impl<'b> Store<'b> {
51    /// instantiates a validated module with `validation_info` as validation evidence with name `name`
52    /// with the steps in <https://webassembly.github.io/spec/core/exec/modules.html#instantiation>
53    /// this method roughly matches the suggested embedder function`module_instantiate`
54    /// <https://webassembly.github.io/spec/core/appendix/embedding.html#modules>
55    /// except external values for module instantiation are retrieved from `self`.
56    pub fn add_module(
57        &mut self,
58        name: &str,
59        validation_info: &ValidationInfo<'b>,
60    ) -> CustomResult<()> {
61        // instantiation step -1: collect extern_vals, this section basically acts as a linker between modules
62        // best attempt at trying to match the spec implementation in terms of errors
63        debug!("adding module with name {:?}", name);
64        let mut extern_vals = Vec::new();
65
66        for Import {
67            module_name: exporting_module_name,
68            name: import_name,
69            desc: import_desc,
70        } in &validation_info.imports
71        {
72            trace!(
73                "trying to import from exporting module instance named {:?}, the entity with name {:?} with desc: {:?}",
74                exporting_module_name,
75                import_name,
76                import_desc
77            );
78            let import_extern_type = import_desc.extern_type(validation_info)?;
79            let exporting_module = self
80                .modules
81                .get(
82                    *self
83                        .module_names
84                        .get(exporting_module_name)
85                        .ok_or(Error::RuntimeError(RuntimeError::ModuleNotFound))?,
86                )
87                .ok_or(Error::RuntimeError(RuntimeError::ModuleNotFound))?;
88
89            let export_extern_val_candidate = *exporting_module
90                .exports
91                .iter()
92                .find_map(
93                    |ExportInst {
94                         name: export_name,
95                         value: export_extern_val,
96                     }| {
97                        (import_name == export_name).then_some(export_extern_val)
98                    },
99                )
100                .ok_or(Error::UnknownImport)?;
101            trace!("export candidate found: {:?}", export_extern_val_candidate);
102            if !export_extern_val_candidate
103                .extern_type(self)?
104                .is_subtype_of(&import_extern_type)
105            {
106                return Err(Error::InvalidImportType);
107            }
108            trace!("import and export matches. Adding to externvals");
109            extern_vals.push(export_extern_val_candidate)
110        }
111
112        // instantiation: step 5
113        // 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.
114        // therefore I am mimicking the reference interpreter code here, I will allocate functions in the store in this step instead of step 11.
115        // https://github.com/WebAssembly/spec/blob/8d6792e3d6709e8d3e90828f9c8468253287f7ed/interpreter/exec/eval.ml#L789
116        let mut module_inst = ModuleInst {
117            types: validation_info.types.clone(),
118            func_addrs: extern_vals.iter().funcs().collect(),
119            table_addrs: Vec::new(),
120            mem_addrs: Vec::new(),
121            global_addrs: extern_vals.iter().globals().collect(),
122            elem_addrs: Vec::new(),
123            data_addrs: Vec::new(),
124            exports: Vec::new(),
125            wasm_bytecode: validation_info.wasm,
126            sidetable: validation_info.sidetable.clone(),
127            name: name.to_owned(),
128        };
129
130        // TODO rewrite this part
131        // <https://webassembly.github.io/spec/core/exec/modules.html#functions>
132        let func_addrs: Vec<usize> = validation_info
133            .functions
134            .iter()
135            .zip(validation_info.func_blocks_stps.iter())
136            .map(|(ty_idx, (span, stp))| {
137                self.alloc_func((*ty_idx, (*span, *stp)), &module_inst, self.modules.len())
138            })
139            .collect();
140
141        module_inst.func_addrs.extend(func_addrs);
142
143        // instantiation: this roughly matches step 6,7,8
144        // validation guarantees these will evaluate without errors.
145        let maybe_global_init_vals: Result<Vec<Value>, _> = validation_info
146            .globals
147            .iter()
148            .map(|global| {
149                run_const_span(validation_info.wasm, &global.init_expr, &module_inst, self)
150                    .transpose()
151                    .unwrap_validated()
152            })
153            .collect();
154        let global_init_vals = maybe_global_init_vals?;
155
156        // instantiation: this roughly matches step 9,10
157
158        let mut element_init_ref_lists: Vec<Vec<Ref>> =
159            Vec::with_capacity(validation_info.elements.len());
160
161        for elem in &validation_info.elements {
162            let mut new_list = Vec::new();
163            match &elem.init {
164                // shortcut of evaluation of "ref.func <func_idx>; end;"
165                // validation guarantees corresponding func_idx's existence
166                ElemItems::RefFuncs(ref_funcs) => {
167                    for func_idx in ref_funcs {
168                        new_list.push(Ref::Func(FuncAddr {
169                            addr: Some(module_inst.func_addrs[*func_idx as usize]),
170                        }))
171                    }
172                }
173                ElemItems::Exprs(_, exprs) => {
174                    for expr in exprs {
175                        new_list.push(
176                            run_const_span(validation_info.wasm, expr, &module_inst, self)?
177                                .unwrap_validated()
178                                .into(),
179                        )
180                    }
181                }
182            }
183            element_init_ref_lists.push(new_list);
184        }
185
186        // instantiation: step 11 - module allocation (except function allocation - which was made in step 5)
187        // https://webassembly.github.io/spec/core/exec/modules.html#alloc-module
188
189        // allocation: begin
190
191        // allocation: step 1
192        let module = validation_info;
193
194        let extern_vals = extern_vals;
195        let vals = global_init_vals;
196        let ref_lists = element_init_ref_lists;
197
198        // allocation: skip step 2 as it was done in instantiation step 5
199
200        // allocation: step 3-13
201        let table_addrs: Vec<usize> = module
202            .tables
203            .iter()
204            .map(|table_type| {
205                let null_ref = match table_type.et {
206                    RefType::FuncRef => Ref::Func(FuncAddr { addr: None }),
207                    RefType::ExternRef => Ref::Extern(ExternAddr { addr: None }),
208                };
209                self.alloc_table(*table_type, null_ref)
210            })
211            .collect();
212        let mem_addrs: Vec<usize> = module
213            .memories
214            .iter()
215            .map(|mem_type| self.alloc_mem(*mem_type))
216            .collect();
217        let global_addrs: Vec<usize> = module
218            .globals
219            .iter()
220            .zip(vals)
221            .map(
222                |(
223                    Global {
224                        ty: global_type, ..
225                    },
226                    val,
227                )| self.alloc_global(*global_type, val),
228            )
229            .collect();
230        let elem_addrs = module
231            .elements
232            .iter()
233            .zip(ref_lists)
234            .map(|(elem, refs)| self.alloc_elem(elem.ty(), refs))
235            .collect();
236        let data_addrs = module
237            .data
238            .iter()
239            .map(|DataSegment { init: bytes, .. }| self.alloc_data(bytes))
240            .collect();
241
242        // allocation: skip step 14 as it was done in instantiation step 5
243
244        // allocation: step 15,16
245        let mut table_addrs_mod: Vec<usize> = extern_vals.iter().tables().collect();
246        table_addrs_mod.extend(table_addrs);
247
248        let mut mem_addrs_mod: Vec<usize> = extern_vals.iter().mems().collect();
249        mem_addrs_mod.extend(mem_addrs);
250
251        // skipping step 17 partially as it was partially done in instantiation step
252        module_inst.global_addrs.extend(global_addrs);
253
254        // allocation: step 18,19
255        let export_insts = module
256            .exports
257            .iter()
258            .map(|Export { name, desc }| {
259                let value = match desc {
260                    ExportDesc::FuncIdx(func_idx) => {
261                        ExternVal::Func(module_inst.func_addrs[*func_idx])
262                    }
263                    ExportDesc::TableIdx(table_idx) => {
264                        ExternVal::Table(table_addrs_mod[*table_idx])
265                    }
266                    ExportDesc::MemIdx(mem_idx) => ExternVal::Mem(mem_addrs_mod[*mem_idx]),
267                    ExportDesc::GlobalIdx(global_idx) => {
268                        ExternVal::Global(module_inst.global_addrs[*global_idx])
269                    }
270                };
271                ExportInst {
272                    name: String::from(name),
273                    value,
274                }
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        // instantiation step 11 end: module_inst properly allocated after this point.
288        // TODO: it is too hard with our codebase to do the following steps without adding the module to the store
289        let current_module_idx = &self.modules.len();
290        self.modules.push(module_inst);
291        self.module_names
292            .insert(String::from(name), *current_module_idx);
293
294        // instantiation: step 12-15
295        // TODO have to stray away from the spec a bit since our codebase does not lend itself well to freely executing instructions by themselves
296        for (
297            i,
298            ElemType {
299                init: elem_items,
300                mode,
301            },
302        ) in validation_info.elements.iter().enumerate()
303        {
304            match mode {
305                ElemMode::Active(ActiveElem {
306                    table_idx: table_idx_i,
307                    init_expr: einstr_i,
308                }) => {
309                    let n = elem_items.len() as i32;
310                    // equivalent to init.len() in spec
311                    // instantiation step 14:
312                    // TODO (for now, we are doing hopefully what is equivalent to it)
313                    // execute:
314                    //   einstr_i
315                    //   i32.const 0
316                    //   i32.const n
317                    //   table.init table_idx_i i
318                    //   elem.drop i
319                    let d: i32 = run_const_span(
320                        validation_info.wasm,
321                        einstr_i,
322                        &self.modules[*current_module_idx],
323                        self,
324                    )?
325                    .unwrap_validated()
326                    .into();
327                    let s = 0;
328                    table_init(
329                        &self.modules,
330                        &mut self.tables,
331                        &self.elements,
332                        current_module_idx,
333                        i,
334                        *table_idx_i as usize,
335                        n,
336                        s,
337                        d,
338                    )
339                    .map_err(Error::RuntimeError)?;
340                    elem_drop(&self.modules, &mut self.elements, current_module_idx, i)
341                        .map_err(Error::RuntimeError)?;
342                }
343                ElemMode::Declarative => {
344                    // instantiation step 15:
345                    // TODO (for now, we are doing hopefully what is equivalent to it)
346                    // execute:
347                    //   elem.drop i
348                    elem_drop(&self.modules, &mut self.elements, current_module_idx, i)
349                        .map_err(Error::RuntimeError)?;
350                }
351                ElemMode::Passive => (),
352            }
353        }
354
355        // instantiation: step 16
356        // TODO have to stray away from the spec a bit since our codebase does not lend itself well to freely executing instructions by themselves
357        for (i, DataSegment { init, mode }) in validation_info.data.iter().enumerate() {
358            match mode {
359                crate::core::reader::types::data::DataMode::Active(DataModeActive {
360                    memory_idx,
361                    offset: dinstr_i,
362                }) => {
363                    let n = init.len() as i32;
364                    // assert: mem_idx is 0
365                    if *memory_idx != 0 {
366                        // TODO fix error
367                        return Err(Error::MoreThanOneMemory);
368                    }
369
370                    // TODO (for now, we are doing hopefully what is equivalent to it)
371                    // execute:
372                    //   dinstr_i
373                    //   i32.const 0
374                    //   i32.const n
375                    //   memory.init i
376                    //   data.drop i
377                    let d: i32 = run_const_span(
378                        validation_info.wasm,
379                        dinstr_i,
380                        &self.modules[*current_module_idx],
381                        self,
382                    )?
383                    .unwrap_validated()
384                    .into();
385                    let s = 0;
386                    memory_init(
387                        &self.modules,
388                        &mut self.memories,
389                        &self.data,
390                        current_module_idx,
391                        i,
392                        0,
393                        n,
394                        s,
395                        d,
396                    )
397                    .map_err(Error::RuntimeError)?;
398                    data_drop(&self.modules, &mut self.data, current_module_idx, i)
399                        .map_err(Error::RuntimeError)?;
400                }
401                crate::core::reader::types::data::DataMode::Passive => (),
402            }
403        }
404
405        // instantiation: step 17
406        if let Some(func_idx) = validation_info.start {
407            // TODO (for now, we are doing hopefully what is equivalent to it)
408            // execute
409            //   call func_ifx
410            let func_addr = self.modules[*current_module_idx].func_addrs[func_idx];
411            self.invoke_dynamic(func_addr, Vec::new(), &[])
412                .map_err(Error::RuntimeError)?;
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        let func_inst = FuncInst {
444            ty,
445            locals,
446            code_expr,
447            stp,
448            // validation guarantees func_ty_idx exists within module_inst.types
449            // TODO fix clone
450            function_type: module_inst.types[ty].clone(),
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#tables>
460    fn alloc_table(&mut self, table_type: TableType, reff: Ref) -> usize {
461        let table_inst = TableInst {
462            ty: table_type,
463            elem: vec![reff; table_type.lim.min as usize],
464        };
465
466        let addr = self.tables.len();
467        self.tables.push(table_inst);
468        addr
469    }
470
471    /// <https://webassembly.github.io/spec/core/exec/modules.html#memories>
472    fn alloc_mem(&mut self, mem_type: MemType) -> usize {
473        let mem_inst = MemInst {
474            ty: mem_type,
475            mem: LinearMemory::new_with_initial_pages(
476                mem_type.limits.min.try_into().unwrap_validated(),
477            ),
478        };
479
480        let addr = self.memories.len();
481        self.memories.push(mem_inst);
482        addr
483    }
484
485    /// <https://webassembly.github.io/spec/core/exec/modules.html#globals>
486    fn alloc_global(&mut self, global_type: GlobalType, val: Value) -> usize {
487        let global_inst = GlobalInst {
488            ty: global_type,
489            value: val,
490        };
491
492        let addr = self.globals.len();
493        self.globals.push(global_inst);
494        addr
495    }
496
497    /// <https://webassembly.github.io/spec/core/exec/modules.html#element-segments>
498    fn alloc_elem(&mut self, ref_type: RefType, refs: Vec<Ref>) -> usize {
499        let elem_inst = ElemInst {
500            ty: ref_type,
501            references: refs,
502        };
503
504        let addr = self.elements.len();
505        self.elements.push(elem_inst);
506        addr
507    }
508
509    /// <https://webassembly.github.io/spec/core/exec/modules.html#data-segments>
510    fn alloc_data(&mut self, bytes: &[u8]) -> usize {
511        let data_inst = DataInst {
512            data: Vec::from(bytes),
513        };
514
515        let addr = self.data.len();
516        self.data.push(data_inst);
517        addr
518    }
519
520    pub fn invoke<Param: InteropValueList, Returns: InteropValueList>(
521        &mut self,
522        func_addr: usize,
523        params: Param,
524    ) -> Result<Returns, RuntimeError> {
525        let func_inst = self
526            .functions
527            .get(func_addr)
528            .ok_or(RuntimeError::FunctionNotFound)?;
529
530        let func_ty = func_inst.ty();
531
532        if func_ty.params.valtypes != Param::TYS {
533            panic!("Invalid `Param` generics");
534        }
535        if func_ty.returns.valtypes != Returns::TYS {
536            panic!("Invalid `Returns` generics");
537        }
538
539        let mut stack = Stack::new();
540        let locals = Locals::new(
541            params.into_values().into_iter(),
542            func_inst.locals.iter().cloned(),
543        );
544
545        let module_addr = func_inst.module_addr;
546
547        // TODO handle this bad linear search that is unavoidable
548        let (func_idx, _) = self.modules[module_addr]
549            .func_addrs
550            .iter()
551            .enumerate()
552            .find(|&(_idx, addr)| *addr == func_addr)
553            .ok_or(RuntimeError::FunctionNotFound)?;
554        // setting `usize::MAX` as return address for the outermost function ensures that we
555        // observably fail upon errornoeusly continuing execution after that function returns.
556        stack.push_stackframe(
557            module_addr,
558            func_idx,
559            &func_ty,
560            locals,
561            usize::MAX,
562            usize::MAX,
563        )?;
564
565        let mut current_module_idx = module_addr;
566        // Run the interpreter
567        run(
568            // &mut self.modules,
569            &mut current_module_idx,
570            // self.lut.as_ref().ok_or(RuntimeError::UnmetImport)?,
571            &mut stack,
572            EmptyHookSet,
573            self,
574        )?;
575
576        // Pop return values from stack
577        let return_values = Returns::TYS
578            .iter()
579            .rev()
580            .map(|ty| stack.pop_value(*ty))
581            .collect::<Vec<Value>>();
582
583        // Values are reversed because they were popped from stack one-by-one. Now reverse them back
584        let reversed_values = return_values.into_iter().rev();
585        let ret: Returns = Returns::from_values(reversed_values);
586        debug!("Successfully invoked function");
587        Ok(ret)
588    }
589
590    pub fn invoke_dynamic(
591        &mut self,
592        func_addr: usize,
593        params: Vec<Value>,
594        ret_types: &[ValType],
595    ) -> Result<Vec<Value>, RuntimeError> {
596        let func_inst = self
597            .functions
598            .get(func_addr)
599            .ok_or(RuntimeError::FunctionNotFound)?;
600
601        let func_ty = func_inst.ty();
602
603        // Verify that the given parameters match the function parameters
604        let param_types = params.iter().map(|v| v.to_ty()).collect::<Vec<_>>();
605
606        if func_ty.params.valtypes != param_types {
607            // format!()
608            trace!(
609                "Func param types len: {}; Given args len: {}",
610                func_ty.params.valtypes.len(),
611                param_types.len()
612            );
613            panic!("Invalid parameters for function");
614        }
615
616        // Verify that the given return types match the function return types
617        if func_ty.returns.valtypes != ret_types {
618            panic!("Invalid return types for function");
619        }
620
621        // Prepare a new stack with the locals for the entry function
622        let mut stack = Stack::new();
623        let locals = Locals::new(params.into_iter(), func_inst.locals.iter().cloned());
624        let module_addr = func_inst.module_addr;
625
626        // TODO handle this bad linear search that is unavoidable
627        let (func_idx, _) = self.modules[module_addr]
628            .func_addrs
629            .iter()
630            .enumerate()
631            .find(|&(_idx, addr)| *addr == func_addr)
632            .ok_or(RuntimeError::FunctionNotFound)?;
633        stack.push_stackframe(
634            module_addr,
635            func_idx,
636            &func_ty,
637            locals,
638            usize::MAX,
639            usize::MAX,
640        )?;
641
642        let mut currrent_module_idx = module_addr;
643        // Run the interpreter
644        run(
645            // &mut self.modules,
646            &mut currrent_module_idx,
647            // self.lut.as_ref().ok_or(RuntimeError::UnmetImport)?,
648            &mut stack,
649            EmptyHookSet,
650            self,
651        )?;
652
653        let func_inst = self
654            .functions
655            .get(func_addr)
656            .ok_or(RuntimeError::FunctionNotFound)?;
657
658        let func_ty = func_inst.ty();
659
660        // Pop return values from stack
661        let return_values = func_ty
662            .returns
663            .valtypes
664            .iter()
665            .rev()
666            .map(|ty| stack.pop_value(*ty))
667            .collect::<Vec<Value>>();
668
669        // Values are reversed because they were popped from stack one-by-one. Now reverse them back
670        let reversed_values = return_values.into_iter().rev();
671        let ret = reversed_values.collect();
672        debug!("Successfully invoked function");
673        Ok(ret)
674    }
675
676    pub fn invoke_dynamic_unchecked_return_ty(
677        &mut self,
678        func_addr: usize,
679        params: Vec<Value>,
680    ) -> Result<Vec<Value>, RuntimeError> {
681        let func_inst = self
682            .functions
683            .get(func_addr)
684            .ok_or(RuntimeError::FunctionNotFound)?;
685
686        let module_addr = func_inst.module_addr;
687
688        let func_ty = func_inst.ty();
689
690        // Verify that the given parameters match the function parameters
691        let param_types = params.iter().map(|v| v.to_ty()).collect::<Vec<_>>();
692
693        if func_ty.params.valtypes != param_types {
694            trace!(
695                "Func param types len: {}; Given args len: {}",
696                func_ty.params.valtypes.len(),
697                param_types.len()
698            );
699            panic!("Invalid parameters for function");
700        }
701
702        // Prepare a new stack with the locals for the entry function
703        let mut stack = Stack::new();
704        let locals = Locals::new(params.into_iter(), func_inst.locals.iter().cloned());
705
706        // TODO handle this bad linear search that is unavoidable
707        let (func_idx, _) = self.modules[module_addr]
708            .func_addrs
709            .iter()
710            .enumerate()
711            .find(|&(_idx, addr)| *addr == func_addr)
712            .ok_or(RuntimeError::FunctionNotFound)?;
713
714        stack.push_stackframe(
715            module_addr,
716            func_idx,
717            &func_ty,
718            locals,
719            usize::MAX,
720            usize::MAX,
721        )?;
722
723        let mut currrent_module_idx = module_addr;
724        // Run the interpreter
725        run(
726            // &mut self.modules,
727            &mut currrent_module_idx,
728            // self.lut.as_ref().ok_or(RuntimeError::UnmetImport)?,
729            &mut stack,
730            EmptyHookSet,
731            self,
732        )?;
733
734        // Pop return values from stack
735        let return_values = func_ty
736            .returns
737            .valtypes
738            .iter()
739            .rev()
740            .map(|ty| stack.pop_value(*ty))
741            .collect::<Vec<Value>>();
742
743        // Values are reversed because they were popped from stack one-by-one. Now reverse them back
744        let reversed_values = return_values.into_iter().rev();
745        let ret = reversed_values.collect();
746        debug!("Successfully invoked function");
747        Ok(ret)
748    }
749
750    //TODO consider further refactor
751    pub fn get_module_idx_from_name(&self, module_name: &str) -> Result<usize, RuntimeError> {
752        self.module_names
753            .get(module_name)
754            .copied()
755            .ok_or(RuntimeError::ModuleNotFound)
756    }
757
758    //TODO consider further refactor
759    pub fn get_global_function_idx_by_name(
760        &self,
761        module_addr: usize,
762        function_name: &str,
763    ) -> Option<usize> {
764        self.modules
765            .get(module_addr)?
766            .exports
767            .iter()
768            .find_map(|ExportInst { name, value }| {
769                if name != function_name {
770                    return None;
771                };
772                match value {
773                    ExternVal::Func(func_addr) => Some(*func_addr),
774                    _ => None,
775                }
776            })
777    }
778
779    //TODO consider further refactor
780    pub fn register_alias(&mut self, alias_name: String, module_idx: usize) {
781        self.module_names.insert(alias_name, module_idx);
782    }
783
784    //TODO consider further refactor
785    pub fn lookup_function(&self, target_module: &str, target_function: &str) -> Option<usize> {
786        let module_addr = *self.module_names.get(target_module)?;
787        self.get_global_function_idx_by_name(module_addr, target_function)
788    }
789}
790
791#[derive(Debug)]
792// TODO does not match the spec FuncInst
793
794pub struct FuncInst {
795    pub ty: TypeIdx,
796    pub locals: Vec<ValType>,
797    pub code_expr: Span,
798    ///index of the sidetable corresponding to the beginning of this functions code
799    pub stp: usize,
800    pub function_type: FuncType,
801    // implicit back ref required for function invocation and is in the spec
802    // TODO module_addr or module ref?
803    pub module_addr: usize,
804}
805
806impl FuncInst {
807    pub fn ty_idx(&self) -> TypeIdx {
808        self.ty
809    }
810
811    pub fn ty(&self) -> FuncType {
812        self.function_type.clone()
813    }
814}
815
816#[derive(Clone, Debug)]
817/// <https://webassembly.github.io/spec/core/exec/runtime.html#element-instances>
818pub struct ElemInst {
819    pub ty: RefType,
820    pub references: Vec<Ref>,
821}
822
823impl ElemInst {
824    pub fn len(&self) -> usize {
825        self.references.len()
826    }
827    pub fn is_empty(&self) -> bool {
828        self.references.is_empty()
829    }
830}
831
832// TODO: The tables have to be both imported and exported (an enum instead of a struct)
833//       That is because when we import tables we can give a different size to the imported table
834//        thus having a wrapper over the initial table
835#[derive(Debug)]
836pub struct TableInst {
837    pub ty: TableType,
838    pub elem: Vec<Ref>,
839}
840
841impl TableInst {
842    pub fn len(&self) -> usize {
843        self.elem.len()
844    }
845
846    pub fn is_empty(&self) -> bool {
847        self.elem.is_empty()
848    }
849
850    pub fn new(ty: TableType) -> Self {
851        Self {
852            ty,
853            elem: vec![Ref::default_from_ref_type(ty.et); ty.lim.min as usize],
854        }
855    }
856
857    /// <https://webassembly.github.io/spec/core/exec/modules.html#growing-tables>
858    pub fn grow(&mut self, n: u32, reff: Ref) -> Result<(), RuntimeError> {
859        // TODO refactor error, the spec Table.grow raises Table.{SizeOverflow, SizeLimit, OutOfMemory}
860        let len = n
861            .checked_add(self.elem.len() as u32)
862            .ok_or(RuntimeError::TableAccessOutOfBounds)?;
863
864        // roughly matches step 4,5,6
865        // checks limits_prime.valid() for limits_prime := { min: len, max: self.ty.lim.max }
866        // https://webassembly.github.io/spec/core/valid/types.html#limits
867        if self.ty.lim.max.map(|max| len > max).unwrap_or(false) {
868            return Err(RuntimeError::TableAccessOutOfBounds);
869        }
870        let limits_prime = Limits {
871            min: len,
872            max: self.ty.lim.max,
873        };
874
875        self.elem.extend(vec![reff; n as usize]);
876
877        self.ty.lim = limits_prime;
878        Ok(())
879    }
880}
881
882pub struct MemInst {
883    #[allow(warnings)]
884    pub ty: MemType,
885    pub mem: LinearMemory,
886}
887impl core::fmt::Debug for MemInst {
888    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
889        f.debug_struct("MemInst")
890            .field("ty", &self.ty)
891            .finish_non_exhaustive()
892    }
893}
894
895impl MemInst {
896    pub fn new(ty: MemType) -> Self {
897        Self {
898            ty,
899            mem: LinearMemory::new_with_initial_pages(ty.limits.min.try_into().unwrap()),
900        }
901    }
902
903    /// <https://webassembly.github.io/spec/core/exec/modules.html#growing-memories>
904    pub fn grow(&mut self, n: u32) -> Result<(), RuntimeError> {
905        // TODO refactor error, the spec Table.grow raises Memory.{SizeOverflow, SizeLimit, OutOfMemory}
906        let len = n + self.mem.pages() as u32;
907        if len > Limits::MAX_MEM_PAGES {
908            return Err(RuntimeError::MemoryAccessOutOfBounds);
909        }
910
911        // roughly matches step 4,5,6
912        // checks limits_prime.valid() for limits_prime := { min: len, max: self.ty.lim.max }
913        // https://webassembly.github.io/spec/core/valid/types.html#limits
914        if self.ty.limits.max.map(|max| len > max).unwrap_or(false) {
915            return Err(RuntimeError::MemoryAccessOutOfBounds);
916        }
917        let limits_prime = Limits {
918            min: len,
919            max: self.ty.limits.max,
920        };
921
922        self.mem.grow(n.try_into().unwrap());
923
924        self.ty.limits = limits_prime;
925        Ok(())
926    }
927
928    /// Can never be bigger than 65,356 pages
929    pub fn size(&self) -> usize {
930        self.mem.len() / (crate::Limits::MEM_PAGE_SIZE as usize)
931    }
932}
933
934// pub struct GlobalInstV2 {
935//     Local(LocalGlobalInst),
936//     Imported(ImportedGlobalInst)
937// }
938
939#[derive(Debug)]
940pub struct GlobalInst {
941    pub ty: GlobalType,
942    /// Must be of the same type as specified in `ty`
943    pub value: Value,
944}
945
946pub struct DataInst {
947    pub data: Vec<u8>,
948}
949
950impl core::fmt::Debug for DataInst {
951    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
952        f.debug_struct("DataInst").finish_non_exhaustive()
953    }
954}
955
956///<https://webassembly.github.io/spec/core/exec/runtime.html#external-values>
957#[derive(Debug, Copy, Clone, PartialEq, Eq)]
958pub enum ExternVal {
959    Func(usize),
960    Table(usize),
961    Mem(usize),
962    Global(usize),
963}
964
965impl ExternVal {
966    /// returns the external type of `self` according to typing relation,
967    /// taking `store` as context S.
968    /// typing fails if this external value does not exist within S.
969    ///<https://webassembly.github.io/spec/core/valid/modules.html#imports>
970    pub fn extern_type(&self, store: &Store) -> CustomResult<ExternType> {
971        // TODO: implement proper errors
972        Ok(match self {
973            // TODO: fix ugly clone in function types
974            ExternVal::Func(func_addr) => ExternType::Func(
975                store
976                    .functions
977                    .get(*func_addr)
978                    .ok_or(Error::InvalidImportType)?
979                    .ty(),
980            ),
981            ExternVal::Table(table_addr) => ExternType::Table(
982                store
983                    .tables
984                    .get(*table_addr)
985                    .ok_or(Error::InvalidImportType)?
986                    .ty,
987            ),
988            ExternVal::Mem(mem_addr) => ExternType::Mem(
989                store
990                    .memories
991                    .get(*mem_addr)
992                    .ok_or(Error::InvalidImportType)?
993                    .ty,
994            ),
995            ExternVal::Global(global_addr) => ExternType::Global(
996                store
997                    .globals
998                    .get(*global_addr)
999                    .ok_or(Error::InvalidImportType)?
1000                    .ty,
1001            ),
1002        })
1003    }
1004}
1005
1006/// common convention functions defined for lists of ExternVals, ExternTypes, Exports
1007/// <https://webassembly.github.io/spec/core/exec/runtime.html#conventions>
1008/// <https://webassembly.github.io/spec/core/syntax/types.html#id3>
1009/// <https://webassembly.github.io/spec/core/syntax/modules.html?highlight=convention#id1>
1010// TODO implement this trait for ExternType lists Export lists
1011pub trait ExternFilterable<T> {
1012    fn funcs(self) -> impl Iterator<Item = T>;
1013    fn tables(self) -> impl Iterator<Item = T>;
1014    fn mems(self) -> impl Iterator<Item = T>;
1015    fn globals(self) -> impl Iterator<Item = T>;
1016}
1017
1018impl<'a, I> ExternFilterable<usize> for I
1019where
1020    I: Iterator<Item = &'a ExternVal>,
1021{
1022    fn funcs(self) -> impl Iterator<Item = usize> {
1023        self.filter_map(|extern_val| {
1024            if let ExternVal::Func(func_addr) = extern_val {
1025                Some(*func_addr)
1026            } else {
1027                None
1028            }
1029        })
1030    }
1031
1032    fn tables(self) -> impl Iterator<Item = usize> {
1033        self.filter_map(|extern_val| {
1034            if let ExternVal::Table(table_addr) = extern_val {
1035                Some(*table_addr)
1036            } else {
1037                None
1038            }
1039        })
1040    }
1041
1042    fn mems(self) -> impl Iterator<Item = usize> {
1043        self.filter_map(|extern_val| {
1044            if let ExternVal::Mem(mem_addr) = extern_val {
1045                Some(*mem_addr)
1046            } else {
1047                None
1048            }
1049        })
1050    }
1051
1052    fn globals(self) -> impl Iterator<Item = usize> {
1053        self.filter_map(|extern_val| {
1054            if let ExternVal::Global(global_addr) = extern_val {
1055                Some(*global_addr)
1056            } else {
1057                None
1058            }
1059        })
1060    }
1061}
1062
1063///<https://webassembly.github.io/spec/core/exec/runtime.html#export-instances>
1064#[derive(Debug)]
1065pub struct ExportInst {
1066    pub name: String,
1067    pub value: ExternVal,
1068}
1069
1070///<https://webassembly.github.io/spec/core/exec/runtime.html#module-instances>
1071#[derive(Debug)]
1072pub struct ModuleInst<'b> {
1073    pub types: Vec<FuncType>,
1074    pub func_addrs: Vec<usize>,
1075    pub table_addrs: Vec<usize>,
1076    pub mem_addrs: Vec<usize>,
1077    pub global_addrs: Vec<usize>,
1078    pub elem_addrs: Vec<usize>,
1079    pub data_addrs: Vec<usize>,
1080    pub exports: Vec<ExportInst>,
1081
1082    // TODO the bytecode is not in the spec, but required for re-parsing
1083    pub wasm_bytecode: &'b [u8],
1084
1085    // sidetable is not in the spec, but required for control flow
1086    pub sidetable: Sidetable,
1087
1088    // TODO name field is not in the spec but used by the testsuite crate, might need to be refactored out
1089    // this data is unfortunately duplicated within store.module_names kv store.
1090    pub name: String,
1091}