Coverage Report

Created: 2024-11-19 11:03

/build/source/src/execution/mod.rs
Line
Count
Source (jump to first uncovered line)
1
use alloc::string::ToString;
2
use alloc::vec::Vec;
3
4
use const_interpreter_loop::run_const;
5
use function_ref::FunctionRef;
6
use interpreter_loop::run;
7
use locals::Locals;
8
use store::DataInst;
9
use value_stack::Stack;
10
11
use crate::core::reader::types::export::{Export, ExportDesc};
12
use crate::core::reader::types::FuncType;
13
use crate::core::reader::WasmReader;
14
use crate::execution::assert_validated::UnwrapValidatedExt;
15
use crate::execution::hooks::{EmptyHookSet, HookSet};
16
use crate::execution::store::{FuncInst, GlobalInst, MemInst, Store};
17
use crate::execution::value::Value;
18
use crate::validation::code::read_declared_locals;
19
use crate::value::InteropValueList;
20
use crate::{RuntimeError, ValType, ValidationInfo};
21
22
// TODO
23
pub(crate) mod assert_validated;
24
pub mod const_interpreter_loop;
25
pub mod function_ref;
26
pub mod hooks;
27
mod interpreter_loop;
28
pub(crate) mod locals;
29
pub(crate) mod store;
30
pub mod value;
31
pub mod value_stack;
32
33
/// The default module name if a [RuntimeInstance] was created using [RuntimeInstance::new].
34
pub const DEFAULT_MODULE: &str = "__interpreter_default__";
35
36
pub struct RuntimeInstance<'b, H = EmptyHookSet>
37
where
38
    H: HookSet,
39
{
40
    pub wasm_bytecode: &'b [u8],
41
    types: Vec<FuncType>,
42
    exports: Vec<Export>,
43
    pub store: Store,
44
    pub hook_set: H,
45
}
46
47
impl<'b> RuntimeInstance<'b, EmptyHookSet> {
48
1.26k
    pub fn new(validation_info: &'_ ValidationInfo<'b>) -> Result<Self, RuntimeError> {
49
1.26k
        Self::new_with_hooks(DEFAULT_MODULE, validation_info, EmptyHookSet)
50
1.26k
    }
51
52
0
    pub fn new_named(
53
0
        module_name: &str,
54
0
        validation_info: &'_ ValidationInfo<'b>,
55
0
    ) -> Result<Self, RuntimeError> {
56
0
        Self::new_with_hooks(module_name, validation_info, EmptyHookSet)
57
0
    }
58
}
59
60
impl<'b, H> RuntimeInstance<'b, H>
61
where
62
    H: HookSet,
63
{
64
1.26k
    pub fn new_with_hooks(
65
1.26k
        module_name: &str,
66
1.26k
        validation_info: &'_ ValidationInfo<'b>,
67
1.26k
        hook_set: H,
68
1.26k
    ) -> Result<Self, RuntimeError> {
69
1.26k
        trace!("Starting instantiation of bytecode");
70
71
1.26k
        let store = Self::init_store(validation_info);
72
1.26k
73
1.26k
        let mut instance = RuntimeInstance {
74
1.26k
            wasm_bytecode: validation_info.wasm,
75
1.26k
            types: validation_info.types.clone(),
76
1.26k
            exports: validation_info.exports.clone(),
77
1.26k
            store,
78
1.26k
            hook_set,
79
1.26k
        };
80
81
1.26k
        if let Some(
start3
) = validation_info.start {
82
            // "start" is not always exported, so we need create a non-API exposed function reference.
83
            // Note: function name is not important here, as it is not used in the verification process.
84
3
            let start_fn = FunctionRef {
85
3
                module_name: module_name.to_string(),
86
3
                function_name: "start".to_string(),
87
3
                module_index: 0,
88
3
                function_index: start,
89
3
                exported: false,
90
3
            };
91
3
            instance.invoke::<(), ()>(&start_fn, ())
?0
;
92
1.26k
        }
93
94
1.26k
        Ok(instance)
95
1.26k
    }
96
97
19.8k
    pub fn get_function_by_name(
98
19.8k
        &self,
99
19.8k
        module_name: &str,
100
19.8k
        function_name: &str,
101
19.8k
    ) -> Result<FunctionRef, RuntimeError> {
102
19.8k
        let (
module_idx, func_idx19.8k
) = self.get_indicies(module_name, function_name)
?4
;
103
104
19.8k
        Ok(FunctionRef {
105
19.8k
            module_name: module_name.to_string(),
106
19.8k
            function_name: function_name.to_string(),
107
19.8k
            module_index: module_idx,
108
19.8k
            function_index: func_idx,
109
19.8k
            exported: true,
110
19.8k
        })
111
19.8k
    }
112
113
1.30k
    pub fn get_function_by_index(
114
1.30k
        &self,
115
1.30k
        module_idx: usize,
116
1.30k
        function_idx: usize,
117
1.30k
    ) -> Result<FunctionRef, RuntimeError> {
118
        // TODO: Module resolution
119
1.30k
        let function_name = self
120
1.30k
            .exports
121
1.30k
            .iter()
122
1.30k
            .find(|export| match &export.desc {
123
1.30k
                ExportDesc::FuncIdx(idx) => *idx == function_idx,
124
0
                _ => false,
125
1.30k
            })
126
1.30k
            .map(|export| export.name.clone())
127
1.30k
            .ok_or(RuntimeError::FunctionNotFound)
?0
;
128
129
1.30k
        Ok(FunctionRef {
130
1.30k
            // TODO: get the module name from the module index
131
1.30k
            module_name: DEFAULT_MODULE.to_string(),
132
1.30k
            function_name,
133
1.30k
            module_index: module_idx,
134
1.30k
            function_index: function_idx,
135
1.30k
            exported: true,
136
1.30k
        })
137
1.30k
    }
138
139
    // TODO: remove this annotation when implementing the function
140
    #[allow(clippy::result_unit_err)]
141
0
    pub fn add_module(
142
0
        &mut self,
143
0
        _module_name: &str,
144
0
        _validation_info: &'_ ValidationInfo<'b>,
145
0
    ) -> Result<(), ()> {
146
0
        todo!("Implement module linking");
147
    }
148
149
6.00k
    pub fn invoke<Param: InteropValueList, Returns: InteropValueList>(
150
6.00k
        &mut self,
151
6.00k
        function_ref: &FunctionRef,
152
6.00k
        params: Param,
153
6.00k
    ) -> Result<Returns, RuntimeError> {
154
        // First, verify that the function reference is valid
155
6.00k
        let (_module_idx, func_idx) = self.verify_function_ref(function_ref)
?0
;
156
157
        // -=-= Verification =-=-
158
6.00k
        let func_inst = self.store.funcs.get(func_idx).expect("valid FuncIdx");
159
6.00k
        let func_ty = self.types.get(func_inst.ty).unwrap_validated();
160
6.00k
161
6.00k
        // Check correct function parameters and return types
162
6.00k
        if func_ty.params.valtypes != Param::TYS {
163
0
            panic!("Invalid `Param` generics");
164
6.00k
        }
165
6.00k
        if func_ty.returns.valtypes != Returns::TYS {
166
0
            panic!("Invalid `Returns` generics");
167
6.00k
        }
168
6.00k
169
6.00k
        // Prepare a new stack with the locals for the entry function
170
6.00k
        let mut stack = Stack::new();
171
6.00k
        let locals = Locals::new(
172
6.00k
            params.into_values().into_iter(),
173
6.00k
            func_inst.locals.iter().cloned(),
174
6.00k
        );
175
6.00k
176
6.00k
        // setting `usize::MAX` as return address for the outermost function ensures that we
177
6.00k
        // observably fail upon errornoeusly continuing execution after that function returns.
178
6.00k
        stack.push_stackframe(func_idx, func_ty, locals, usize::MAX);
179
6.00k
180
6.00k
        // Run the interpreter
181
6.00k
        run(
182
6.00k
            self.wasm_bytecode,
183
6.00k
            &self.types,
184
6.00k
            &mut self.store,
185
6.00k
            &mut stack,
186
6.00k
            EmptyHookSet,
187
6.00k
        )
?89
;
188
189
        // Pop return values from stack
190
5.91k
        let return_values = Returns::TYS
191
5.91k
            .iter()
192
5.91k
            .map(|ty| 
stack.pop_value(*ty)5.87k
)
193
5.91k
            .collect::<Vec<Value>>();
194
5.91k
195
5.91k
        // Values are reversed because they were popped from stack one-by-one. Now reverse them back
196
5.91k
        let reversed_values = return_values.into_iter().rev();
197
5.91k
        let ret: Returns = Returns::from_values(reversed_values);
198
5.91k
        debug!("Successfully invoked function");
199
5.91k
        Ok(ret)
200
6.00k
    }
201
202
    /// Invokes a function with the given parameters, and return types which are not known at compile time.
203
19.6k
    pub fn invoke_dynamic(
204
19.6k
        &mut self,
205
19.6k
        function_ref: &FunctionRef,
206
19.6k
        params: Vec<Value>,
207
19.6k
        ret_types: &[ValType],
208
19.6k
    ) -> Result<Vec<Value>, RuntimeError> {
209
        // First, verify that the function reference is valid
210
19.6k
        let (_module_idx, func_idx) = self.verify_function_ref(function_ref)
?0
;
211
212
        // -=-= Verification =-=-
213
19.6k
        let func_inst = self.store.funcs.get(func_idx).expect("valid FuncIdx");
214
19.6k
        let func_ty = self.types.get(func_inst.ty).unwrap_validated();
215
19.6k
216
19.6k
        // Verify that the given parameters match the function parameters
217
28.1k
        let param_types = params.iter().map(|v| v.to_ty()).collect::<Vec<_>>();
218
19.6k
219
19.6k
        if func_ty.params.valtypes != param_types {
220
0
            panic!("Invalid parameters for function");
221
19.6k
        }
222
19.6k
223
19.6k
        // Verify that the given return types match the function return types
224
19.6k
        if func_ty.returns.valtypes != ret_types {
225
0
            panic!("Invalid return types for function");
226
19.6k
        }
227
19.6k
228
19.6k
        // Prepare a new stack with the locals for the entry function
229
19.6k
        let mut stack = Stack::new();
230
19.6k
        let locals = Locals::new(params.into_iter(), func_inst.locals.iter().cloned());
231
19.6k
        stack.push_stackframe(func_idx, func_ty, locals, 0);
232
19.6k
233
19.6k
        // Run the interpreter
234
19.6k
        run(
235
19.6k
            self.wasm_bytecode,
236
19.6k
            &self.types,
237
19.6k
            &mut self.store,
238
19.6k
            &mut stack,
239
19.6k
            EmptyHookSet,
240
19.6k
        )
?57
;
241
242
19.5k
        let func_inst = self.store.funcs.get(func_idx).expect("valid FuncIdx");
243
19.5k
        let func_ty = self.types.get(func_inst.ty).unwrap_validated();
244
19.5k
245
19.5k
        // Pop return values from stack
246
19.5k
        let return_values = func_ty
247
19.5k
            .returns
248
19.5k
            .valtypes
249
19.5k
            .iter()
250
19.5k
            .map(|ty| 
stack.pop_value(*ty)19.5k
)
251
19.5k
            .collect::<Vec<Value>>();
252
19.5k
253
19.5k
        // Values are reversed because they were popped from stack one-by-one. Now reverse them back
254
19.5k
        let reversed_values = return_values.into_iter().rev();
255
19.5k
        let ret = reversed_values.collect();
256
19.5k
        debug!(
"Successfully invoked function"19.5k
);
257
19.5k
        Ok(ret)
258
19.6k
    }
259
260
    // TODO: replace this with the lookup table when implmenting the linker
261
45.4k
    fn get_indicies(
262
45.4k
        &self,
263
45.4k
        _module_name: &str,
264
45.4k
        function_name: &str,
265
45.4k
    ) -> Result<(usize, usize), RuntimeError> {
266
45.4k
        let 
func_idx45.4k
= self
267
45.4k
            .exports
268
45.4k
            .iter()
269
185k
            .find_map(|export| {
270
185k
                if export.name == function_name {
271
45.4k
                    match export.desc {
272
45.4k
                        ExportDesc::FuncIdx(func_idx) => Some(func_idx),
273
0
                        _ => None,
274
                    }
275
                } else {
276
140k
                    None
277
                }
278
185k
            })
279
45.4k
            .ok_or(RuntimeError::FunctionNotFound)
?4
;
280
281
45.4k
        Ok((0, func_idx))
282
45.4k
    }
283
284
25.6k
    fn verify_function_ref(
285
25.6k
        &self,
286
25.6k
        function_ref: &FunctionRef,
287
25.6k
    ) -> Result<(usize, usize), RuntimeError> {
288
25.6k
        if function_ref.exported {
289
25.6k
            let (module_idx, func_idx) =
290
25.6k
                self.get_indicies(&function_ref.module_name, &function_ref.function_name)
?0
;
291
292
25.6k
            if module_idx != function_ref.module_index || func_idx != function_ref.function_index {
293
                // TODO: should we return a different error here?
294
0
                return Err(RuntimeError::FunctionNotFound);
295
25.6k
            }
296
25.6k
297
25.6k
            Ok((module_idx, func_idx))
298
        } else {
299
3
            let (module_idx, func_idx) = (function_ref.module_index, function_ref.function_index);
300
3
301
3
            // TODO: verify module named - index mapping.
302
3
303
3
            // Sanity check that the function index is at least in the bounds of the store, though this doesn't mean
304
3
            // that it's a valid function.
305
3
            self.store
306
3
                .funcs
307
3
                .get(func_idx)
308
3
                .ok_or(RuntimeError::FunctionNotFound)
?0
;
309
310
3
            Ok((module_idx, func_idx))
311
        }
312
25.6k
    }
313
314
1.26k
    fn init_store(validation_info: &ValidationInfo) -> Store {
315
1.26k
        let function_instances: Vec<FuncInst> = {
316
1.26k
            let mut wasm_reader = WasmReader::new(validation_info.wasm);
317
1.26k
318
1.26k
            let functions = validation_info.functions.iter();
319
1.26k
            let func_blocks = validation_info.func_blocks.iter();
320
1.26k
321
1.26k
            functions
322
1.26k
                .zip(func_blocks)
323
1.68k
                .map(|(ty, func)| {
324
1.68k
                    wasm_reader
325
1.68k
                        .move_start_to(*func)
326
1.68k
                        .expect("function index to be in the bounds of the WASM binary");
327
1.68k
328
1.68k
                    let (locals, bytes_read) = wasm_reader
329
1.68k
                        .measure_num_read_bytes(read_declared_locals)
330
1.68k
                        .unwrap_validated();
331
1.68k
332
1.68k
                    let code_expr = wasm_reader
333
1.68k
                        .make_span(func.len() - bytes_read)
334
1.68k
                        .expect("TODO remove this expect");
335
1.68k
336
1.68k
                    FuncInst {
337
1.68k
                        ty: *ty,
338
1.68k
                        locals,
339
1.68k
                        code_expr,
340
1.68k
                    }
341
1.68k
                })
342
1.26k
                .collect()
343
1.26k
        };
344
1.26k
345
1.26k
        let mut memory_instances: Vec<MemInst> = validation_info
346
1.26k
            .memories
347
1.26k
            .iter()
348
1.26k
            .map(|ty| 
MemInst::new(*ty)351
)
349
1.26k
            .collect();
350
1.26k
351
1.26k
        let data_sections: Vec<DataInst> = validation_info
352
1.26k
            .data
353
1.26k
            .iter()
354
1.26k
            .map(|d| 
{337
355
                use crate::core::reader::types::data::DataMode;
356
337
                if let DataMode::Active(
active_data284
) = d.mode.clone() {
357
284
                    let mem_idx = active_data.memory_idx;
358
284
                    if mem_idx != 0 {
359
0
                        todo!("Active data has memory_idx different than 0");
360
284
                    }
361
284
                    assert!(memory_instances.len() > mem_idx);
362
363
284
                    let value = {
364
284
                        let mut wasm = WasmReader::new(validation_info.wasm);
365
284
                        wasm.move_start_to(active_data.offset).unwrap_validated();
366
284
                        let mut stack = Stack::new();
367
284
                        run_const(wasm, &mut stack, ());
368
284
                        let value = stack.peek_unknown_value();
369
284
                        if value.is_none() {
370
0
                            panic!("No value on the stack for data segment offset");
371
284
                        }
372
284
                        value.unwrap()
373
                    };
374
375
                    // TODO: this shouldn't be a simple value, should it? I mean it can't be, but it can also be any type of ValType
376
                    // TODO: also, do we need to forcefully make it i32?
377
284
                    let offset: u32 = match value {
378
284
                        Value::I32(val) => val,
379
0
                        Value::I64(val) => {
380
0
                            if val > u32::MAX as u64 {
381
0
                                panic!("i64 value for data segment offset is out of reach")
382
0
                            }
383
0
                            val as u32
384
                        }
385
                        // TODO: implement all value types
386
0
                        _ => unimplemented!(),
387
                    };
388
389
284
                    let mem_inst = memory_instances.get_mut(mem_idx).unwrap();
390
284
391
284
                    let len = mem_inst.data.len();
392
284
                    if offset as usize + d.init.len() > len {
393
0
                        panic!("Active data writing in memory, out of bounds");
394
284
                    }
395
284
                    let data = mem_inst
396
284
                        .data
397
284
                        .get_mut(offset as usize..offset as usize + d.init.len())
398
284
                        .unwrap();
399
284
                    data.copy_from_slice(&d.init);
400
53
                }
401
337
                DataInst {
402
337
                    data: d.init.clone(),
403
337
                }
404
1.26k
            })
405
1.26k
            .collect();
406
1.26k
407
1.26k
        let global_instances: Vec<GlobalInst> = validation_info
408
1.26k
            .globals
409
1.26k
            .iter()
410
1.26k
            .map({
411
1.26k
                let mut stack = Stack::new();
412
1.26k
                move |global| {
413
48
                    let mut wasm = WasmReader::new(validation_info.wasm);
414
48
                    // The place we are moving the start to should, by all means, be inside the wasm bytecode.
415
48
                    wasm.move_start_to(global.init_expr).unwrap_validated();
416
48
                    // We shouldn't need to clear the stack. If validation is correct, it will remain empty after execution.
417
48
418
48
                    run_const(wasm, &mut stack, ());
419
48
                    let value = stack.pop_value(global.ty.ty);
420
48
421
48
                    GlobalInst {
422
48
                        global: *global,
423
48
                        value,
424
48
                    }
425
1.26k
                }
426
1.26k
            })
427
1.26k
            .collect();
428
1.26k
429
1.26k
        Store {
430
1.26k
            funcs: function_instances,
431
1.26k
            mems: memory_instances,
432
1.26k
            globals: global_instances,
433
1.26k
            data: data_sections,
434
1.26k
        }
435
1.26k
    }
436
}