/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 | | } |