wasm/execution/
const_interpreter_loop.rs

1use crate::{
2    addrs::ModuleAddr,
3    assert_validated::UnwrapValidatedExt,
4    config::Config,
5    core::{
6        indices::GlobalIdx,
7        reader::{span::Span, WasmReadable, WasmReader},
8    },
9    unreachable_validated,
10    value::{self, Ref},
11    value_stack::Stack,
12    RefType, RuntimeError, Store, Value,
13};
14
15// TODO update this documentation
16/// Execute a previosly-validated constant expression. These type of expressions are used for initializing global
17/// variables, data and element segments.
18///
19/// # Arguments
20/// TODO
21///
22/// # Safety
23/// This function assumes that the expression has been validated. Passing unvalidated code will likely result in a
24/// panic, or undefined behaviour.
25// TODO this signature might change to support hooks or match the spec better
26pub(crate) fn run_const<T: Config>(
27    wasm: &mut WasmReader,
28    stack: &mut Stack,
29    module: ModuleAddr,
30    store: &Store<T>,
31) -> Result<(), RuntimeError> {
32    use crate::core::reader::types::opcode::*;
33    loop {
34        let first_instr_byte = wasm.read_u8().unwrap_validated();
35
36        #[cfg(debug_assertions)]
37        crate::core::utils::print_beautiful_instruction_name_1_byte(first_instr_byte, wasm.pc);
38
39        #[cfg(not(debug_assertions))]
40        trace!("Read instruction byte {first_instr_byte:#04X?} ({first_instr_byte}) at wasm_binary[{}]", wasm.pc);
41
42        match first_instr_byte {
43            END => {
44                trace!("Constant instruction: END");
45                break;
46            }
47            GLOBAL_GET => {
48                let global_idx = wasm.read_var_u32().unwrap_validated() as GlobalIdx;
49
50                //TODO replace double indirection
51                let global = store
52                    .globals
53                    .get(store.modules.get(module).global_addrs[global_idx]);
54
55                trace!(
56                    "Constant instruction: global.get [{global_idx}] -> [{:?}]",
57                    global
58                );
59                stack.push_value::<T>(global.value)?;
60            }
61            I32_CONST => {
62                let constant = wasm.read_var_i32().unwrap_validated();
63                trace!("Constant instruction: i32.const [] -> [{constant}]");
64                stack.push_value::<T>(constant.into())?;
65            }
66            F32_CONST => {
67                let constant = value::F32::from_bits(wasm.read_f32().unwrap_validated());
68                trace!("Constanting instruction: f32.const [] -> [{constant}]");
69                stack.push_value::<T>(constant.into())?;
70            }
71            F64_CONST => {
72                let constant = value::F64::from_bits(wasm.read_f64().unwrap_validated());
73                trace!("Constanting instruction: f64.const [] -> [{constant}]");
74                stack.push_value::<T>(constant.into())?;
75            }
76            I64_CONST => {
77                let constant = wasm.read_var_i64().unwrap_validated();
78                trace!("Constant instruction: i64.const [] -> [{constant}]");
79                stack.push_value::<T>(constant.into())?;
80            }
81            REF_NULL => {
82                let reftype = RefType::read(wasm).unwrap_validated();
83
84                stack.push_value::<T>(Value::Ref(Ref::Null(reftype)))?;
85                trace!("Instruction: ref.null '{:?}' -> [{:?}]", reftype, reftype);
86            }
87            REF_FUNC => {
88                // we already checked for the func_idx to be in bounds during validation
89                let func_idx = wasm.read_var_u32().unwrap_validated() as usize;
90                let func_addr = *store
91                    .modules
92                    .get(module)
93                    .func_addrs
94                    .get(func_idx)
95                    .unwrap_validated();
96                stack.push_value::<T>(Value::Ref(Ref::Func(func_addr)))?;
97            }
98
99            FD_EXTENSIONS => {
100                use crate::core::reader::types::opcode::fd_extensions::*;
101
102                match wasm.read_var_u32().unwrap_validated() {
103                    V128_CONST => {
104                        let mut data = [0; 16];
105                        for byte_ref in &mut data {
106                            *byte_ref = wasm.read_u8().unwrap_validated();
107                        }
108
109                        stack.push_value::<T>(Value::V128(data))?;
110                    }
111                    0x00..=0x0B | 0x0D.. => unreachable_validated!(),
112                }
113            }
114
115            0x00..=0x0A
116            | 0x0C..=0x22
117            | 0x24..=0x40
118            | 0x45..=0xBF
119            | 0xC0..=0xCF
120            | 0xD1
121            | 0xD3..=0xFC
122            | 0xFE..=0xFF => {
123                unreachable_validated!();
124            }
125        }
126    }
127    Ok(())
128}
129
130pub(crate) fn run_const_span<T: Config>(
131    wasm: &[u8],
132    span: &Span,
133    module: ModuleAddr,
134    store: &Store<T>,
135) -> Result<Option<Value>, RuntimeError> {
136    let mut wasm = WasmReader::new(wasm);
137
138    wasm.move_start_to(*span).unwrap_validated();
139
140    let mut stack = Stack::new();
141    run_const(&mut wasm, &mut stack, module, store)?;
142
143    Ok(stack.peek_value())
144}