wasm/execution/
store.rs

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