/build/source/src/execution/mod.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use alloc::collections::btree_map::BTreeMap; |
2 | | use alloc::string::{String, ToString}; |
3 | | use alloc::vec; |
4 | | use alloc::vec::Vec; |
5 | | |
6 | | use const_interpreter_loop::{run_const, run_const_span}; |
7 | | use execution_info::ExecutionInfo; |
8 | | use function_ref::FunctionRef; |
9 | | use interpreter_loop::run; |
10 | | use locals::Locals; |
11 | | use lut::Lut; |
12 | | use store::{DataInst, ElemInst, ImportedFuncInst, LocalFuncInst, TableInst}; |
13 | | use value::{ExternAddr, FuncAddr, Ref}; |
14 | | use value_stack::Stack; |
15 | | |
16 | | use crate::core::error::StoreInstantiationError; |
17 | | use crate::core::reader::types::element::{ElemItems, ElemMode}; |
18 | | use crate::core::reader::types::export::ExportDesc; |
19 | | use crate::core::reader::types::import::ImportDesc; |
20 | | use crate::core::reader::WasmReader; |
21 | | use crate::execution::assert_validated::UnwrapValidatedExt; |
22 | | use crate::execution::hooks::{EmptyHookSet, HookSet}; |
23 | | use crate::execution::store::{FuncInst, GlobalInst, MemInst, Store}; |
24 | | use crate::execution::value::Value; |
25 | | use crate::validation::code::read_declared_locals; |
26 | | use crate::value::InteropValueList; |
27 | | use crate::{RefType, Result as CustomResult, RuntimeError, ValType, ValidationInfo}; |
28 | | |
29 | | // TODO |
30 | | pub(crate) mod assert_validated; |
31 | | pub mod const_interpreter_loop; |
32 | | pub(crate) mod execution_info; |
33 | | pub mod function_ref; |
34 | | pub mod hooks; |
35 | | mod interpreter_loop; |
36 | | pub(crate) mod locals; |
37 | | pub(crate) mod lut; |
38 | | pub(crate) mod store; |
39 | | pub mod value; |
40 | | pub mod value_stack; |
41 | | |
42 | | /// The default module name if a [RuntimeInstance] was created using [RuntimeInstance::new]. |
43 | | pub const DEFAULT_MODULE: &str = "__interpreter_default__"; |
44 | | |
45 | | pub struct RuntimeInstance<'b, H = EmptyHookSet> |
46 | | where |
47 | | H: HookSet, |
48 | | { |
49 | | pub modules: Vec<ExecutionInfo<'b>>, |
50 | | module_map: BTreeMap<String, usize>, |
51 | | lut: Option<Lut>, |
52 | | pub hook_set: H, |
53 | | } |
54 | | |
55 | | impl<'b> RuntimeInstance<'b, EmptyHookSet> { |
56 | 270 | pub fn new(validation_info: &'_ ValidationInfo<'b>) -> CustomResult<Self> { |
57 | 270 | Self::new_with_hooks(DEFAULT_MODULE, validation_info, EmptyHookSet) |
58 | 270 | } |
59 | | |
60 | 4 | pub fn new_named( |
61 | 4 | module_name: &str, |
62 | 4 | validation_info: &'_ ValidationInfo<'b>, |
63 | 4 | ) -> CustomResult<Self> { |
64 | 4 | Self::new_with_hooks(module_name, validation_info, EmptyHookSet) |
65 | 4 | } |
66 | | } |
67 | | |
68 | | impl<'b, H> RuntimeInstance<'b, H> |
69 | | where |
70 | | H: HookSet, |
71 | | { |
72 | 274 | pub fn new_with_hooks( |
73 | 274 | module_name: &str, |
74 | 274 | validation_info: &'_ ValidationInfo<'b>, |
75 | 274 | hook_set: H, |
76 | 274 | ) -> CustomResult<Self> { |
77 | 274 | trace!("Starting instantiation of bytecode"); |
78 | | |
79 | 274 | let mut instance = RuntimeInstance { |
80 | 274 | modules: Vec::new(), |
81 | 274 | module_map: BTreeMap::new(), |
82 | 274 | lut: None, |
83 | 274 | hook_set, |
84 | 274 | }; |
85 | 274 | instance.add_module(module_name, validation_info)?0 ; |
86 | | |
87 | | // TODO: how do we handle the start function, if we don't have a LUT yet? |
88 | 274 | if let Some(start1 ) = validation_info.start { |
89 | | // "start" is not always exported, so we need create a non-API exposed function reference. |
90 | | // Note: function name is not important here, as it is not used in the verification process. |
91 | 1 | let start_fn = FunctionRef { |
92 | 1 | module_name: module_name.to_string(), |
93 | 1 | function_name: "start".to_string(), |
94 | 1 | module_index: 0, |
95 | 1 | function_index: start, |
96 | 1 | exported: false, |
97 | 1 | }; |
98 | 1 | instance.invoke::<(), ()>(&start_fn, ())?0 ; |
99 | 273 | } |
100 | | |
101 | 274 | Ok(instance) |
102 | 274 | } |
103 | | |
104 | 251 | pub fn get_function_by_name( |
105 | 251 | &self, |
106 | 251 | module_name: &str, |
107 | 251 | function_name: &str, |
108 | 251 | ) -> Result<FunctionRef, RuntimeError> { |
109 | 251 | let (module_idx, func_idx) = self.get_indicies(module_name, function_name)?0 ; |
110 | | |
111 | 251 | Ok(FunctionRef { |
112 | 251 | module_name: module_name.to_string(), |
113 | 251 | function_name: function_name.to_string(), |
114 | 251 | module_index: module_idx, |
115 | 251 | function_index: func_idx, |
116 | 251 | exported: true, |
117 | 251 | }) |
118 | 251 | } |
119 | | |
120 | 1.32k | pub fn get_function_by_index( |
121 | 1.32k | &self, |
122 | 1.32k | module_idx: usize, |
123 | 1.32k | function_idx: usize, |
124 | 1.32k | ) -> Result<FunctionRef, RuntimeError> { |
125 | 1.32k | let module = self |
126 | 1.32k | .modules |
127 | 1.32k | .get(module_idx) |
128 | 1.32k | .ok_or(RuntimeError::ModuleNotFound)?0 ; |
129 | | |
130 | 1.32k | let function_name = module |
131 | 1.32k | .store |
132 | 1.32k | .exports |
133 | 1.32k | .iter() |
134 | 1.32k | .find(|export| match &export.desc { |
135 | 1.32k | ExportDesc::FuncIdx(idx) => *idx == function_idx, |
136 | 0 | _ => false, |
137 | 1.32k | }) |
138 | 1.32k | .map(|export| export.name.clone()) |
139 | 1.32k | .ok_or(RuntimeError::FunctionNotFound)?0 ; |
140 | | |
141 | 1.32k | Ok(FunctionRef { |
142 | 1.32k | module_name: module.name.clone(), |
143 | 1.32k | function_name, |
144 | 1.32k | module_index: module_idx, |
145 | 1.32k | function_index: function_idx, |
146 | 1.32k | exported: true, |
147 | 1.32k | }) |
148 | 1.32k | } |
149 | | |
150 | 278 | pub fn add_module( |
151 | 278 | &mut self, |
152 | 278 | module_name: &str, |
153 | 278 | validation_info: &'_ ValidationInfo<'b>, |
154 | 278 | ) -> CustomResult<()> { |
155 | 278 | let store = Self::init_store(validation_info)?0 ; |
156 | 278 | let exec_info = ExecutionInfo::new( |
157 | 278 | module_name, |
158 | 278 | validation_info.wasm, |
159 | 278 | validation_info.types.clone(), |
160 | 278 | store, |
161 | 278 | ); |
162 | 278 | |
163 | 278 | self.module_map |
164 | 278 | .insert(module_name.to_string(), self.modules.len()); |
165 | 278 | self.modules.push(exec_info); |
166 | 278 | |
167 | 278 | self.lut = Lut::new(&self.modules, &self.module_map); |
168 | 278 | |
169 | 278 | Ok(()) |
170 | 278 | } |
171 | | |
172 | 6.83k | pub fn invoke<Param: InteropValueList, Returns: InteropValueList>( |
173 | 6.83k | &mut self, |
174 | 6.83k | function_ref: &FunctionRef, |
175 | 6.83k | params: Param, |
176 | 6.83k | ) -> Result<Returns, RuntimeError> { |
177 | | // First, verify that the function reference is valid |
178 | 6.83k | let (module_idx, func_idx) = self.verify_function_ref(function_ref)?0 ; |
179 | | |
180 | | // -=-= Verification =-=- |
181 | 6.83k | trace!("{:?}", self.modules[module_idx].store.funcs); |
182 | | |
183 | 6.83k | let func_inst = self.modules[module_idx] |
184 | 6.83k | .store |
185 | 6.83k | .funcs |
186 | 6.83k | .get(func_idx) |
187 | 6.83k | .ok_or(RuntimeError::FunctionNotFound)?0 |
188 | 6.83k | .try_into_local() |
189 | 6.83k | .ok_or(RuntimeError::FunctionNotFound)?0 ; |
190 | 6.83k | let func_ty = self.modules[module_idx] |
191 | 6.83k | .fn_types |
192 | 6.83k | .get(func_inst.ty) |
193 | 6.83k | .unwrap_validated(); |
194 | 6.83k | |
195 | 6.83k | // Check correct function parameters and return types |
196 | 6.83k | if func_ty.params.valtypes != Param::TYS { |
197 | 0 | panic!("Invalid `Param` generics"); |
198 | 6.83k | } |
199 | 6.83k | if func_ty.returns.valtypes != Returns::TYS { |
200 | 0 | panic!("Invalid `Returns` generics"); |
201 | 6.83k | } |
202 | 6.83k | |
203 | 6.83k | // Prepare a new stack with the locals for the entry function |
204 | 6.83k | let mut stack = Stack::new(); |
205 | 6.83k | let locals = Locals::new( |
206 | 6.83k | params.into_values().into_iter(), |
207 | 6.83k | func_inst.locals.iter().cloned(), |
208 | 6.83k | ); |
209 | 6.83k | |
210 | 6.83k | // setting `usize::MAX` as return address for the outermost function ensures that we |
211 | 6.83k | // observably fail upon errornoeusly continuing execution after that function returns. |
212 | 6.83k | stack.push_stackframe( |
213 | 6.83k | module_idx, |
214 | 6.83k | func_idx, |
215 | 6.83k | func_ty, |
216 | 6.83k | locals, |
217 | 6.83k | usize::MAX, |
218 | 6.83k | usize::MAX, |
219 | 6.83k | ); |
220 | 6.83k | |
221 | 6.83k | let mut current_module_idx = module_idx; |
222 | 6.83k | // Run the interpreter |
223 | 6.83k | run( |
224 | 6.83k | &mut self.modules, |
225 | 6.83k | &mut current_module_idx, |
226 | 6.83k | self.lut.as_ref().ok_or(RuntimeError::UnmetImport)?2 , |
227 | 6.83k | &mut stack, |
228 | 6.83k | EmptyHookSet, |
229 | 667 | )?; |
230 | | |
231 | | // Pop return values from stack |
232 | 6.16k | let return_values = Returns::TYS |
233 | 6.16k | .iter() |
234 | 6.16k | .map(|ty| stack.pop_value(*ty)6.09k ) |
235 | 6.16k | .collect::<Vec<Value>>(); |
236 | 6.16k | |
237 | 6.16k | // Values are reversed because they were popped from stack one-by-one. Now reverse them back |
238 | 6.16k | let reversed_values = return_values.into_iter().rev(); |
239 | 6.16k | let ret: Returns = Returns::from_values(reversed_values); |
240 | 6.16k | debug!("Successfully invoked function"); |
241 | 6.16k | Ok(ret) |
242 | 6.83k | } |
243 | | |
244 | | /// Invokes a function with the given parameters, and return types which are not known at compile time. |
245 | 2 | pub fn invoke_dynamic( |
246 | 2 | &mut self, |
247 | 2 | function_ref: &FunctionRef, |
248 | 2 | params: Vec<Value>, |
249 | 2 | ret_types: &[ValType], |
250 | 2 | ) -> Result<Vec<Value>, RuntimeError> { |
251 | | // First, verify that the function reference is valid |
252 | 2 | let (module_idx, func_idx) = self.verify_function_ref(function_ref)?0 ; |
253 | | |
254 | | // -=-= Verification =-=- |
255 | 2 | let func_inst = self.modules[module_idx] |
256 | 2 | .store |
257 | 2 | .funcs |
258 | 2 | .get(func_idx) |
259 | 2 | .ok_or(RuntimeError::FunctionNotFound)?0 |
260 | 2 | .try_into_local() |
261 | 2 | .ok_or(RuntimeError::FunctionNotFound)?0 ; |
262 | 2 | let func_ty = self.modules[module_idx] |
263 | 2 | .fn_types |
264 | 2 | .get(func_inst.ty) |
265 | 2 | .unwrap_validated(); |
266 | 2 | |
267 | 2 | // Verify that the given parameters match the function parameters |
268 | 4 | let param_types = params.iter().map(|v| v.to_ty()).collect::<Vec<_>>(); |
269 | 2 | |
270 | 2 | if func_ty.params.valtypes != param_types { |
271 | 0 | panic!("Invalid parameters for function"); |
272 | 2 | } |
273 | 2 | |
274 | 2 | // Verify that the given return types match the function return types |
275 | 2 | if func_ty.returns.valtypes != ret_types { |
276 | 0 | panic!("Invalid return types for function"); |
277 | 2 | } |
278 | 2 | |
279 | 2 | // Prepare a new stack with the locals for the entry function |
280 | 2 | let mut stack = Stack::new(); |
281 | 2 | let locals = Locals::new(params.into_iter(), func_inst.locals.iter().cloned()); |
282 | 2 | stack.push_stackframe(module_idx, func_idx, func_ty, locals, 0, 0); |
283 | 2 | |
284 | 2 | let mut currrent_module_idx = module_idx; |
285 | 2 | // Run the interpreter |
286 | 2 | run( |
287 | 2 | &mut self.modules, |
288 | 2 | &mut currrent_module_idx, |
289 | 2 | self.lut.as_ref().ok_or(RuntimeError::UnmetImport)?0 , |
290 | 2 | &mut stack, |
291 | 2 | EmptyHookSet, |
292 | 0 | )?; |
293 | | |
294 | 2 | let func_inst = self.modules[module_idx] |
295 | 2 | .store |
296 | 2 | .funcs |
297 | 2 | .get(func_idx) |
298 | 2 | .ok_or(RuntimeError::FunctionNotFound)?0 |
299 | 2 | .try_into_local() |
300 | 2 | .ok_or(RuntimeError::FunctionNotFound)?0 ; |
301 | 2 | let func_ty = self.modules[module_idx] |
302 | 2 | .fn_types |
303 | 2 | .get(func_inst.ty) |
304 | 2 | .unwrap_validated(); |
305 | 2 | |
306 | 2 | // Pop return values from stack |
307 | 2 | let return_values = func_ty |
308 | 2 | .returns |
309 | 2 | .valtypes |
310 | 2 | .iter() |
311 | 2 | .map(|ty| stack.pop_value(*ty)) |
312 | 2 | .collect::<Vec<Value>>(); |
313 | 2 | |
314 | 2 | // Values are reversed because they were popped from stack one-by-one. Now reverse them back |
315 | 2 | let reversed_values = return_values.into_iter().rev(); |
316 | 2 | let ret = reversed_values.collect(); |
317 | 2 | debug!("Successfully invoked function"); |
318 | 2 | Ok(ret) |
319 | 2 | } |
320 | | |
321 | | /// Get the indicies of a module and function by their names. |
322 | | /// |
323 | | /// # Arguments |
324 | | /// - `module_name`: The module in which to find the function. |
325 | | /// - `function_name`: The name of the function to find inside the module. The function must be a local function and |
326 | | /// not an import. |
327 | | /// |
328 | | /// # Returns |
329 | | /// - `Ok((module_idx, func_idx))`, where `module_idx` is the internal index of the module inside the |
330 | | /// [RuntimeInstance], and `func_idx` is the internal index of the function inside the module. |
331 | | /// - `Err(RuntimeError::ModuleNotFound)`, if the module is not found. |
332 | | /// - `Err(RuntimeError::FunctionNotFound`, if the function is not found within the module. |
333 | 7.09k | fn get_indicies( |
334 | 7.09k | &self, |
335 | 7.09k | module_name: &str, |
336 | 7.09k | function_name: &str, |
337 | 7.09k | ) -> Result<(usize, usize), RuntimeError> { |
338 | 7.09k | let module_idx = *self |
339 | 7.09k | .module_map |
340 | 7.09k | .get(module_name) |
341 | 7.09k | .ok_or(RuntimeError::ModuleNotFound)?0 ; |
342 | | |
343 | 7.09k | let func_idx = self.modules[module_idx] |
344 | 7.09k | .store |
345 | 7.09k | .exports |
346 | 7.09k | .iter() |
347 | 25.5k | .find_map(|export| { |
348 | 25.5k | if export.name == function_name { |
349 | 7.09k | match export.desc { |
350 | 7.09k | ExportDesc::FuncIdx(func_idx) => Some(func_idx), |
351 | 0 | _ => None, |
352 | | } |
353 | | } else { |
354 | 18.4k | None |
355 | | } |
356 | 25.5k | }) |
357 | 7.09k | .ok_or(RuntimeError::FunctionNotFound)?0 ; |
358 | | |
359 | 7.09k | Ok((module_idx, func_idx)) |
360 | 7.09k | } |
361 | | |
362 | | /// Verify that the function reference is still valid. A function reference may be invalid if it created from |
363 | | /// another [RuntimeInstance] or the modules inside the instance have been changed in a way that the indicies inside |
364 | | /// the [FunctionRef] would be invalid. |
365 | | /// |
366 | | /// Note: this function ensures that making an unchecked indexation will not cause a panic. |
367 | | /// |
368 | | /// # Returns |
369 | | /// - `Ok((function_ref.module_idx, function_ref.func_idx))` |
370 | | /// - `Err(RuntimeError::FunctionNotFound)`, or `Err(RuntimeError::ModuleNotFound)` if the function is not valid. |
371 | | /// |
372 | | /// # Implementation details |
373 | | /// For an exported function (i.e. created by the same [RuntimeInstance]), the names are re-resolved using |
374 | | /// [RuntimeInstance::get_indicies], and the indicies are compared with the indicies in the [FunctionRef]. |
375 | | /// |
376 | | /// For a [FunctionRef] with the [export](FunctionRef::exported) flag set to `false`, the indicies are checked to be |
377 | | /// in-bounds, and that the module name matches the module name in the [FunctionRef]. The function name is ignored. |
378 | 6.84k | fn verify_function_ref( |
379 | 6.84k | &self, |
380 | 6.84k | function_ref: &FunctionRef, |
381 | 6.84k | ) -> Result<(usize, usize), RuntimeError> { |
382 | 6.84k | if function_ref.exported { |
383 | 6.83k | let (module_idx, func_idx) = |
384 | 6.83k | self.get_indicies(&function_ref.module_name, &function_ref.function_name)?0 ; |
385 | | |
386 | | // TODO: figure out errors :) |
387 | 6.83k | if module_idx != function_ref.module_index { |
388 | 0 | return Err(RuntimeError::ModuleNotFound); |
389 | 6.83k | } |
390 | 6.83k | if func_idx != function_ref.function_index { |
391 | 0 | return Err(RuntimeError::FunctionNotFound); |
392 | 6.83k | } |
393 | 6.83k | |
394 | 6.83k | Ok((module_idx, func_idx)) |
395 | | } else { |
396 | 1 | let (module_idx, func_idx) = (function_ref.module_index, function_ref.function_index); |
397 | | |
398 | 1 | let module = self |
399 | 1 | .modules |
400 | 1 | .get(module_idx) |
401 | 1 | .ok_or(RuntimeError::ModuleNotFound)?0 ; |
402 | | |
403 | 1 | if module.name != function_ref.module_name { |
404 | 0 | return Err(RuntimeError::ModuleNotFound); |
405 | 1 | } |
406 | 1 | |
407 | 1 | // Sanity check that the function index is at least in the bounds of the store, though this doesn't mean |
408 | 1 | // that it's a valid function. |
409 | 1 | module |
410 | 1 | .store |
411 | 1 | .funcs |
412 | 1 | .get(func_idx) |
413 | 1 | .ok_or(RuntimeError::FunctionNotFound)?0 ; |
414 | | |
415 | 1 | Ok((module_idx, func_idx)) |
416 | | } |
417 | 6.84k | } |
418 | | |
419 | 278 | fn init_store(validation_info: &ValidationInfo) -> CustomResult<Store> { |
420 | 278 | use StoreInstantiationError::*; |
421 | 278 | let function_instances: Vec<FuncInst> = { |
422 | 278 | let mut wasm_reader = WasmReader::new(validation_info.wasm); |
423 | 278 | |
424 | 278 | let functions = validation_info.functions.iter(); |
425 | 278 | let func_blocks = validation_info.func_blocks.iter(); |
426 | 278 | |
427 | 710 | let local_function_inst = functions.zip(func_blocks).map(|(ty, (func, sidetable))| { |
428 | 710 | wasm_reader |
429 | 710 | .move_start_to(*func) |
430 | 710 | .expect("function index to be in the bounds of the WASM binary"); |
431 | 710 | |
432 | 710 | let (locals, bytes_read) = wasm_reader |
433 | 710 | .measure_num_read_bytes(read_declared_locals) |
434 | 710 | .unwrap_validated(); |
435 | 710 | |
436 | 710 | let code_expr = wasm_reader |
437 | 710 | .make_span(func.len() - bytes_read) |
438 | 710 | .expect("TODO remove this expect"); |
439 | 710 | |
440 | 710 | FuncInst::Local(LocalFuncInst { |
441 | 710 | ty: *ty, |
442 | 710 | locals, |
443 | 710 | code_expr, |
444 | 710 | // TODO fix this ugly clone |
445 | 710 | sidetable: sidetable.clone(), |
446 | 710 | }) |
447 | 710 | }); |
448 | 278 | |
449 | 278 | let imported_function_inst = |
450 | 278 | validation_info |
451 | 278 | .imports |
452 | 278 | .iter() |
453 | 278 | .filter_map(|import| match &import.desc8 { |
454 | 7 | ImportDesc::Func(type_idx) => Some(FuncInst::Imported(ImportedFuncInst { |
455 | 7 | ty: *type_idx, |
456 | 7 | module_name: import.module_name.clone(), |
457 | 7 | function_name: import.name.clone(), |
458 | 7 | })), |
459 | 1 | _ => None, |
460 | 278 | }8 ); |
461 | 278 | |
462 | 278 | imported_function_inst.chain(local_function_inst).collect() |
463 | 278 | }; |
464 | 278 | |
465 | 278 | // https://webassembly.github.io/spec/core/exec/modules.html#tables |
466 | 278 | let mut tables: Vec<TableInst> = validation_info |
467 | 278 | .tables |
468 | 278 | .iter() |
469 | 278 | .map(|ty| TableInst::new(*ty)84 ) |
470 | 278 | .collect(); |
471 | 278 | |
472 | 278 | let mut passive_elem_indexes: Vec<usize> = vec![]; |
473 | 278 | // https://webassembly.github.io/spec/core/syntax/modules.html#element-segments |
474 | 278 | let elements: Vec<ElemInst> = validation_info |
475 | 278 | .elements |
476 | 278 | .iter() |
477 | 278 | .enumerate() |
478 | 278 | .filter_map(|(i, elem)| { |
479 | 181 | trace!("Instantiating element {:#?}", elem); |
480 | | |
481 | 181 | let offsets = match &elem.init { |
482 | 123 | ElemItems::Exprs(_ref_type, init_exprs) => init_exprs |
483 | 123 | .iter() |
484 | 330 | .map(|expr| { |
485 | 330 | get_address_offset( |
486 | 330 | run_const_span(validation_info.wasm, expr, ()).unwrap_validated(), |
487 | 330 | ) |
488 | 330 | }) |
489 | 123 | .collect::<Vec<Option<u32>>>(), |
490 | 58 | ElemItems::RefFuncs(indicies) => { |
491 | 58 | // This branch gets taken when the elements are direct function references (i32 values), so we just return the indices |
492 | 58 | indicies |
493 | 58 | .iter() |
494 | 242 | .map(|el| Some(*el)) |
495 | 58 | .collect::<Vec<Option<u32>>>() |
496 | | } |
497 | | }; |
498 | | |
499 | 181 | let references: Vec<Ref> = offsets |
500 | 181 | .iter() |
501 | 572 | .map(|offset| { |
502 | 572 | let offset = offset.as_ref().map(|offset| *offset as usize); |
503 | 572 | match elem.ty() { |
504 | 572 | RefType::FuncRef => Ref::Func(FuncAddr::new(offset)), |
505 | 0 | RefType::ExternRef => Ref::Extern(ExternAddr::new(offset)), |
506 | | } |
507 | 572 | }) |
508 | 181 | .collect(); |
509 | 181 | |
510 | 181 | let instance = ElemInst { |
511 | 181 | ty: elem.ty(), |
512 | 181 | references, |
513 | 181 | }; |
514 | 181 | |
515 | 181 | match &elem.mode { |
516 | | // As per https://webassembly.github.io/spec/core/syntax/modules.html#element-segments |
517 | | // A declarative element segment is not available at runtime but merely serves to forward-declare |
518 | | // references that are formed in code with instructions like `ref.func` |
519 | | |
520 | | // Also, the answer given by Andreas Rossberg (the editor of the WASM Spec - Release 2.0) |
521 | | // Per https://stackoverflow.com/questions/78672934/what-is-the-purpose-of-a-wasm-declarative-element-segment |
522 | | // "[...] The reason Wasm requires this (admittedly ugly) forward declaration is to support streaming compilation [...]" |
523 | 1 | ElemMode::Declarative => None, |
524 | | ElemMode::Passive => { |
525 | 124 | passive_elem_indexes.push(i); |
526 | 124 | Some(instance) |
527 | | } |
528 | 56 | ElemMode::Active(active_elem) => { |
529 | 56 | let table_idx = active_elem.table_idx as usize; |
530 | | |
531 | 56 | let offset = |
532 | 56 | match run_const_span(validation_info.wasm, &active_elem.init_expr, ()) |
533 | 56 | .unwrap_validated() |
534 | | { |
535 | 56 | Value::I32(offset) => offset as usize, |
536 | | // We are already asserting that on top of the stack there is an I32 at validation time |
537 | 0 | _ => unreachable!(), |
538 | | }; |
539 | | |
540 | 56 | let table = &mut tables[table_idx]; |
541 | 56 | // This can't be verified at validation-time because we don't keep track of actual values when validating expressions |
542 | 56 | // we only keep track of the type of the values. As such we can't pop the exact value of an i32 from the validation stack |
543 | 56 | assert!(table.len() >= (offset + instance.len())); |
544 | | |
545 | 56 | table.elem[offset..offset + instance.references.len()] |
546 | 56 | .copy_from_slice(&instance.references); |
547 | 56 | |
548 | 56 | Some(instance) |
549 | | } |
550 | | } |
551 | 278 | }181 ) |
552 | 278 | .collect(); |
553 | 278 | |
554 | 278 | let mut memory_instances: Vec<MemInst> = validation_info |
555 | 278 | .memories |
556 | 278 | .iter() |
557 | 278 | .map(|ty| MemInst::new(*ty)47 ) |
558 | 278 | .collect(); |
559 | | |
560 | 278 | let data_sections: Vec<DataInst> = validation_info |
561 | 278 | .data |
562 | 278 | .iter() |
563 | 278 | .map(|d| {45 |
564 | | use crate::core::reader::types::data::DataMode; |
565 | | use crate::NumType; |
566 | 45 | if let DataMode::Active(active_data33 ) = d.mode.clone() { |
567 | 33 | let mem_idx = active_data.memory_idx; |
568 | 33 | if mem_idx != 0 { |
569 | 0 | todo!("Active data has memory_idx different than 0"); |
570 | 33 | } |
571 | 33 | assert!(memory_instances.len() > mem_idx); |
572 | | |
573 | 33 | let boxed_value = { |
574 | 33 | let mut wasm = WasmReader::new(validation_info.wasm); |
575 | 33 | wasm.move_start_to(active_data.offset).unwrap_validated(); |
576 | 33 | let mut stack = Stack::new(); |
577 | 33 | run_const(wasm, &mut stack, ()); |
578 | 33 | stack.pop_value(ValType::NumType(NumType::I32)) |
579 | | // stack.peek_unknown_value().ok_or(MissingValueOnTheStack)? |
580 | | }; |
581 | | |
582 | | // 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 |
583 | | // TODO: also, do we need to forcefully make it i32? |
584 | 33 | let offset: u32 = match boxed_value { |
585 | 33 | Value::I32(val) => val, |
586 | | // Value::I64(val) => { |
587 | | // if val > u32::MAX as u64 { |
588 | | // return Err(I64ValueOutOfReach("data segment".to_owned())); |
589 | | // } |
590 | | // val as u32 |
591 | | // } |
592 | | // TODO: implement all value types |
593 | 0 | _ => todo!(), |
594 | | }; |
595 | | |
596 | 33 | let mem_inst = memory_instances.get_mut(mem_idx).unwrap(); |
597 | 33 | |
598 | 33 | let len = mem_inst.data.len(); |
599 | 33 | if offset as usize + d.init.len() > len { |
600 | 0 | return Err(ActiveDataWriteOutOfBounds); |
601 | 33 | } |
602 | 33 | let data = mem_inst |
603 | 33 | .data |
604 | 33 | .get_mut(offset as usize..offset as usize + d.init.len()) |
605 | 33 | .unwrap(); |
606 | 33 | data.copy_from_slice(&d.init); |
607 | 12 | } |
608 | 45 | Ok(DataInst { |
609 | 45 | data: d.init.clone(), |
610 | 45 | }) |
611 | 278 | }45 ) |
612 | 278 | .collect::<Result<Vec<DataInst>, StoreInstantiationError>>()?0 ; |
613 | | |
614 | 278 | let global_instances: Vec<GlobalInst> = validation_info |
615 | 278 | .globals |
616 | 278 | .iter() |
617 | 278 | .map({ |
618 | 278 | let mut stack = Stack::new(); |
619 | 278 | move |global| { |
620 | 4 | let mut wasm = WasmReader::new(validation_info.wasm); |
621 | 4 | // The place we are moving the start to should, by all means, be inside the wasm bytecode. |
622 | 4 | wasm.move_start_to(global.init_expr).unwrap_validated(); |
623 | 4 | // We shouldn't need to clear the stack. If validation is correct, it will remain empty after execution. |
624 | 4 | |
625 | 4 | run_const(wasm, &mut stack, ()); |
626 | 4 | let value = stack.pop_value(global.ty.ty); |
627 | 4 | |
628 | 4 | GlobalInst { |
629 | 4 | global: *global, |
630 | 4 | value, |
631 | 4 | } |
632 | 278 | } |
633 | 278 | }) |
634 | 278 | .collect(); |
635 | 278 | |
636 | 278 | let exports = validation_info.exports.clone(); |
637 | 278 | Ok(Store { |
638 | 278 | funcs: function_instances, |
639 | 278 | mems: memory_instances, |
640 | 278 | globals: global_instances, |
641 | 278 | data: data_sections, |
642 | 278 | tables, |
643 | 278 | elements, |
644 | 278 | passive_elem_indexes, |
645 | 278 | exports, |
646 | 278 | }) |
647 | 278 | } |
648 | | } |
649 | | |
650 | | /// Used for getting the offset of an address. |
651 | | /// |
652 | | /// Related to the Active Elements |
653 | | /// |
654 | | /// <https://webassembly.github.io/spec/core/syntax/modules.html#element-segments> |
655 | | /// |
656 | | /// Since active elements need an offset given by a constant expression, in this case |
657 | | /// they can only be an i32 (which can be understood from either a [`Value::I32`] - but |
658 | | /// since we don't unbox the address of the reference, for us also a [`Value::Ref`] - |
659 | | /// or from a Global) |
660 | 330 | fn get_address_offset(value: Value) -> Option<u32> { |
661 | 330 | match value { |
662 | 0 | Value::I32(val) => Some(val), |
663 | 330 | Value::Ref(rref) => match rref { |
664 | 0 | Ref::Extern(_) => todo!("Not yet implemented"), |
665 | | // TODO: fix |
666 | 330 | Ref::Func(func_addr) => func_addr.addr.map(|addr| addr as u32), |
667 | | }, |
668 | | // INFO: from wasmtime - implement only global |
669 | 0 | _ => unreachable!(), |
670 | | } |
671 | 330 | } |