1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use crate::Error;
use alloc::vec::Vec;
use parity_wasm::elements::{
    BlockType,
    FunctionType,
    GlobalType,
    MemoryType,
    TableType,
    ValueType,
};

#[derive(Default, Debug)]
pub struct ModuleContext {
    pub memories: Vec<MemoryType>,
    pub tables: Vec<TableType>,
    pub globals: Vec<GlobalType>,
    pub types: Vec<FunctionType>,
    pub func_type_indexes: Vec<u32>,
}

impl ModuleContext {
    pub fn memories(&self) -> &[MemoryType] {
        &self.memories
    }

    pub fn tables(&self) -> &[TableType] {
        &self.tables
    }

    pub fn globals(&self) -> &[GlobalType] {
        &self.globals
    }

    pub fn types(&self) -> &[FunctionType] {
        &self.types
    }

    pub fn func_type_indexes(&self) -> &[u32] {
        &self.func_type_indexes
    }

    pub fn require_memory(&self, idx: u32) -> Result<(), Error> {
        if self.memories().get(idx as usize).is_none() {
            return Err(Error(format!("Memory at index {} doesn't exists", idx)));
        }
        Ok(())
    }

    pub fn require_table(&self, idx: u32) -> Result<&TableType, Error> {
        self.tables()
            .get(idx as usize)
            .ok_or_else(|| Error(format!("Table at index {} doesn't exists", idx)))
    }

    pub fn require_function(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> {
        let ty_idx = self
            .func_type_indexes()
            .get(idx as usize)
            .ok_or_else(|| Error(format!("Function at index {} doesn't exists", idx)))?;
        self.require_function_type(*ty_idx)
    }

    pub fn require_function_type(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> {
        let ty = self
            .types()
            .get(idx as usize)
            .ok_or_else(|| Error(format!("Type at index {} doesn't exists", idx)))?;

        let params = ty.params();
        let return_ty = ty
            .results()
            .first()
            .map(|vty| BlockType::Value(*vty))
            .unwrap_or(BlockType::NoResult);
        Ok((params, return_ty))
    }

    pub fn require_global(&self, idx: u32, mutability: Option<bool>) -> Result<&GlobalType, Error> {
        let global = self
            .globals()
            .get(idx as usize)
            .ok_or_else(|| Error(format!("Global at index {} doesn't exists", idx)))?;

        if let Some(expected_mutable) = mutability {
            if expected_mutable && !global.is_mutable() {
                return Err(Error(format!("Expected global {} to be mutable", idx)));
            }
            if !expected_mutable && global.is_mutable() {
                return Err(Error(format!("Expected global {} to be immutable", idx)));
            }
        }
        Ok(global)
    }
}

#[derive(Default)]
pub struct ModuleContextBuilder {
    memories: Vec<MemoryType>,
    tables: Vec<TableType>,
    globals: Vec<GlobalType>,
    types: Vec<FunctionType>,
    func_type_indexes: Vec<u32>,
}

impl ModuleContextBuilder {
    pub fn new() -> ModuleContextBuilder {
        ModuleContextBuilder::default()
    }

    pub fn push_memory(&mut self, memory: MemoryType) {
        self.memories.push(memory);
    }

    pub fn push_table(&mut self, table: TableType) {
        self.tables.push(table);
    }

    pub fn push_global(&mut self, global: GlobalType) {
        self.globals.push(global);
    }

    pub fn set_types(&mut self, types: Vec<FunctionType>) {
        self.types = types;
    }

    pub fn push_func_type_index(&mut self, func_type_index: u32) {
        self.func_type_indexes.push(func_type_index);
    }

    pub fn build(self) -> ModuleContext {
        let ModuleContextBuilder {
            memories,
            tables,
            globals,
            types,
            func_type_indexes,
        } = self;

        ModuleContext {
            memories,
            tables,
            globals,
            types,
            func_type_indexes,
        }
    }
}