Coverage Report

Created: 2024-09-10 12:50

/build/source/src/execution/mod.rs
Line
Count
Source (jump to first uncovered line)
1
use alloc::vec::Vec;
2
3
use const_interpreter_loop::run_const;
4
use interpreter_loop::run;
5
use locals::Locals;
6
use value_stack::Stack;
7
8
use crate::core::indices::FuncIdx;
9
use crate::core::reader::types::export::{Export, ExportDesc};
10
use crate::core::reader::types::{FuncType, ValType};
11
use crate::core::reader::WasmReader;
12
use crate::execution::assert_validated::UnwrapValidatedExt;
13
use crate::execution::hooks::{EmptyHookSet, HookSet};
14
use crate::execution::store::{FuncInst, GlobalInst, MemInst, Store};
15
use crate::execution::value::Value;
16
use crate::validation::code::read_declared_locals;
17
use crate::value::InteropValueList;
18
use crate::{RuntimeError, ValidationInfo};
19
20
// TODO
21
pub(crate) mod assert_validated;
22
mod const_interpreter_loop;
23
pub mod hooks;
24
mod interpreter_loop;
25
pub(crate) mod locals;
26
pub(crate) mod store;
27
pub mod value;
28
pub mod value_stack;
29
30
pub struct RuntimeInstance<'b, H = EmptyHookSet>
31
where
32
    H: HookSet,
33
{
34
    pub wasm_bytecode: &'b [u8],
35
    types: Vec<FuncType>,
36
    exports: Vec<Export>,
37
    store: Store,
38
    pub hook_set: H,
39
}
40
41
impl<'b> RuntimeInstance<'b, EmptyHookSet> {
42
146
    pub fn new(validation_info: &'_ ValidationInfo<'b>) -> Result<Self, RuntimeError> {
43
146
        Self::new_with_hooks(validation_info, EmptyHookSet)
44
146
    }
45
}
46
47
impl<'b, H> RuntimeInstance<'b, H>
48
where
49
    H: HookSet,
50
{
51
146
    pub fn new_with_hooks(
52
146
        validation_info: &'_ ValidationInfo<'b>,
53
146
        hook_set: H,
54
146
    ) -> Result<Self, RuntimeError> {
55
146
        trace!("Starting instantiation of bytecode");
56
57
146
        let store = Self::init_store(validation_info);
58
146
59
146
        let mut instance = RuntimeInstance {
60
146
            wasm_bytecode: validation_info.wasm,
61
146
            types: validation_info.types.clone(),
62
146
            exports: validation_info.exports.clone(),
63
146
            store,
64
146
            hook_set,
65
146
        };
66
67
146
        if let Some(
start1
) = validation_info.start {
68
1
            let result = instance.invoke_func::<(), ()>(start, ());
69
1
            result
?0
;
70
145
        }
71
72
146
        Ok(instance)
73
146
    }
74
75
66
    pub fn invoke_named<Param: InteropValueList, Returns: InteropValueList>(
76
66
        &mut self,
77
66
        func_name: &str,
78
66
        param: Param,
79
66
    ) -> Result<Returns, RuntimeError> {
80
66
        // TODO: Optimize this search for better than linear-time. Pre-processing will likely be required
81
72
        let func_idx = self.exports.iter().find_map(|export| {
82
72
            if export.name == func_name {
83
66
                match export.desc {
84
66
                    ExportDesc::FuncIdx(idx) => Some(idx),
85
0
                    _ => None,
86
                }
87
            } else {
88
6
                None
89
            }
90
72
        }
)66
;
91
92
66
        if let Some(func_idx) = func_idx {
93
66
            self.invoke_func(func_idx, param)
94
        } else {
95
0
            Err(RuntimeError::FunctionNotFound)
96
        }
97
66
    }
98
99
    /// Can only invoke functions with signature `[t1] -> [t2]` as of now.
100
1.20k
    pub fn invoke_func<Param: InteropValueList, Returns: InteropValueList>(
101
1.20k
        &mut self,
102
1.20k
        func_idx: FuncIdx,
103
1.20k
        params: Param,
104
1.20k
    ) -> Result<Returns, RuntimeError> {
105
1.20k
        // -=-= Verification =-=-
106
1.20k
        let func_inst = self.store.funcs.get(func_idx).expect("valid FuncIdx");
107
1.20k
        let func_ty = self.types.get(func_inst.ty).unwrap_validated();
108
1.20k
109
1.20k
        // Check correct function parameters and return types
110
1.20k
        if func_ty.params.valtypes != Param::TYS {
111
0
            panic!("Invalid `Param` generics");
112
1.20k
        }
113
1.20k
        if func_ty.returns.valtypes != Returns::TYS {
114
0
            panic!("Invalid `Returns` generics");
115
1.20k
        }
116
1.20k
117
1.20k
        // Prepare a new stack with the locals for the entry function
118
1.20k
        let mut stack = Stack::new();
119
1.20k
        let locals = Locals::new(
120
1.20k
            params.into_values().into_iter(),
121
1.20k
            func_inst.locals.iter().cloned(),
122
1.20k
        );
123
1.20k
124
1.20k
        // setting `usize::MAX` as return address for the outermost function ensures that we
125
1.20k
        // observably fail upon errornoeusly continuing execution after that function returns.
126
1.20k
        stack.push_stackframe(func_idx, func_ty, locals, usize::MAX);
127
1.20k
128
1.20k
        // Run the interpreter
129
1.20k
        run(
130
1.20k
            self.wasm_bytecode,
131
1.20k
            &self.types,
132
1.20k
            &mut self.store,
133
1.20k
            &mut stack,
134
1.20k
            EmptyHookSet,
135
1.20k
        )
?58
;
136
137
        // Pop return values from stack
138
1.14k
        let return_values = Returns::TYS
139
1.14k
            .iter()
140
1.14k
            .map(|ty| 
stack.pop_value(*ty)1.14k
)
141
1.14k
            .collect::<Vec<Value>>();
142
1.14k
143
1.14k
        // Values are reversed because they were popped from stack one-by-one. Now reverse them back
144
1.14k
        let reversed_values = return_values.into_iter().rev();
145
1.14k
        let ret: Returns = Returns::from_values(reversed_values);
146
1.14k
        debug!("Successfully invoked function");
147
1.14k
        Ok(ret)
148
1.20k
    }
149
150
    /// Invokes a function with the given parameters, and return types which are not known at compile time.
151
2
    pub fn invoke_dynamic(
152
2
        &mut self,
153
2
        func_idx: FuncIdx,
154
2
        params: Vec<Value>,
155
2
        ret_types: &[ValType],
156
2
    ) -> Result<Vec<Value>, RuntimeError> {
157
2
        // -=-= Verification =-=-
158
2
        let func_inst = self.store.funcs.get(func_idx).expect("valid FuncIdx");
159
2
        let func_ty = self.types.get(func_inst.ty).unwrap_validated();
160
2
161
2
        // Verify that the given parameters match the function parameters
162
4
        let param_types = params.iter().map(|v| v.to_ty()).collect::<Vec<_>>();
163
2
164
2
        if func_ty.params.valtypes != param_types {
165
0
            panic!("Invalid parameters for function");
166
2
        }
167
2
168
2
        // Verify that the given return types match the function return types
169
2
        if func_ty.returns.valtypes != ret_types {
170
0
            panic!("Invalid return types for function");
171
2
        }
172
2
173
2
        // Prepare a new stack with the locals for the entry function
174
2
        let mut stack = Stack::new();
175
2
        let locals = Locals::new(params.into_iter(), func_inst.locals.iter().cloned());
176
2
        stack.push_stackframe(func_idx, func_ty, locals, 0);
177
2
178
2
        // Run the interpreter
179
2
        run(
180
2
            self.wasm_bytecode,
181
2
            &self.types,
182
2
            &mut self.store,
183
2
            &mut stack,
184
2
            EmptyHookSet,
185
2
        )
?0
;
186
187
2
        let func_inst = self.store.funcs.get(func_idx).expect("valid FuncIdx");
188
2
        let func_ty = self.types.get(func_inst.ty).unwrap_validated();
189
2
190
2
        // Pop return values from stack
191
2
        let return_values = func_ty
192
2
            .returns
193
2
            .valtypes
194
2
            .iter()
195
2
            .map(|ty| stack.pop_value(*ty))
196
2
            .collect::<Vec<Value>>();
197
2
198
2
        // Values are reversed because they were popped from stack one-by-one. Now reverse them back
199
2
        let reversed_values = return_values.into_iter().rev();
200
2
        let ret = reversed_values.collect();
201
2
        debug!("Successfully invoked function");
202
2
        Ok(ret)
203
2
    }
204
205
146
    fn init_store(validation_info: &ValidationInfo) -> Store {
206
146
        let function_instances: Vec<FuncInst> = {
207
146
            let mut wasm_reader = WasmReader::new(validation_info.wasm);
208
146
209
146
            let functions = validation_info.functions.iter();
210
146
            let func_blocks = validation_info.func_blocks.iter();
211
146
212
146
            functions
213
146
                .zip(func_blocks)
214
153
                .map(|(ty, func)| {
215
153
                    wasm_reader
216
153
                        .move_start_to(*func)
217
153
                        .expect("function index to be in the bounds of the WASM binary");
218
153
219
153
                    let (locals, bytes_read) = wasm_reader
220
153
                        .measure_num_read_bytes(read_declared_locals)
221
153
                        .unwrap_validated();
222
153
223
153
                    let code_expr = wasm_reader
224
153
                        .make_span(func.len() - bytes_read)
225
153
                        .expect("TODO remove this expect");
226
153
227
153
                    FuncInst {
228
153
                        ty: *ty,
229
153
                        locals,
230
153
                        code_expr,
231
153
                    }
232
153
                })
233
146
                .collect()
234
146
        };
235
146
236
146
        let memory_instances: Vec<MemInst> = validation_info
237
146
            .memories
238
146
            .iter()
239
146
            .map(|ty| 
MemInst::new(*ty)3
)
240
146
            .collect();
241
146
242
146
        let global_instances: Vec<GlobalInst> = validation_info
243
146
            .globals
244
146
            .iter()
245
146
            .map({
246
146
                let mut stack = Stack::new();
247
146
                move |global| {
248
1
                    let mut wasm = WasmReader::new(validation_info.wasm);
249
1
                    // The place we are moving the start to should, by all means, be inside the wasm bytecode.
250
1
                    wasm.move_start_to(global.init_expr).unwrap_validated();
251
1
                    // We shouldn't need to clear the stack. If validation is correct, it will remain empty after execution.
252
1
253
1
                    run_const(wasm, &mut stack, ());
254
1
                    let value = stack.pop_value(global.ty.ty);
255
1
256
1
                    GlobalInst {
257
1
                        global: *global,
258
1
                        value,
259
1
                    }
260
146
                }
261
146
            })
262
146
            .collect();
263
146
264
146
        Store {
265
146
            funcs: function_instances,
266
146
            mems: memory_instances,
267
146
            globals: global_instances,
268
146
        }
269
146
    }
270
}