wasm/execution/store/mod.rs
1use core::convert::Infallible;
2
3use crate::addrs::{
4 AddrVec, DataAddr, ElemAddr, FuncAddr, GlobalAddr, MemAddr, ModuleAddr, TableAddr,
5};
6use crate::config::Config;
7use crate::core::indices::{ElemIdx, IdxVec, TypeIdx};
8use crate::core::reader::span::Span;
9use crate::core::reader::types::data::{DataModeActive, DataSegment};
10use crate::core::reader::types::element::{ActiveElem, ElemItems, ElemMode, ElemType};
11use crate::core::reader::types::export::ExportDesc;
12use crate::core::reader::types::global::GlobalType;
13use crate::core::reader::types::{ExternType, FuncType, ImportSubTypeRelation, MemType, TableType};
14use crate::core::reader::WasmReader;
15use crate::core::utils::ToUsizeExt;
16use crate::execution::interpreter_loop::{self, memory_init, table_init, InterpreterLoopOutcome};
17use crate::execution::value::{Ref, Value};
18use crate::execution::{run_const_span, Stack};
19use crate::resumable::{HostCall, HostResumable, Resumable, RunState, WasmResumable};
20use crate::{RefType, RuntimeError, ValidationInfo};
21use alloc::borrow::ToOwned;
22use alloc::collections::btree_map::BTreeMap;
23use alloc::string::String;
24use alloc::vec;
25use alloc::vec::Vec;
26use instances::{
27 DataInst, ElemInst, FuncInst, GlobalInst, HostFuncInst, MemInst, ModuleInst, TableInst,
28 WasmFuncInst,
29};
30use linear_memory::LinearMemory;
31
32use super::interpreter_loop::{data_drop, elem_drop};
33use super::UnwrapValidatedExt;
34
35pub mod addrs;
36pub(crate) mod instances;
37pub(crate) mod linear_memory;
38
39/// The store represents all global state that can be manipulated by WebAssembly programs. It
40/// consists of the runtime representation of all instances of functions, tables, memories, and
41/// globals, element segments, and data segments that have been allocated during the life time of
42/// the abstract machine.
43/// <https://webassembly.github.io/spec/core/exec/runtime.html#store>
44///
45/// # Safety
46///
47/// All addresses contained in a store must be valid for their associated
48/// address vectors in the same store.
49pub struct Store<'b, T: Config> {
50 pub(crate) functions: AddrVec<FuncAddr, FuncInst>,
51 pub(crate) tables: AddrVec<TableAddr, TableInst>,
52 pub(crate) memories: AddrVec<MemAddr, MemInst>,
53 pub(crate) globals: AddrVec<GlobalAddr, GlobalInst>,
54 pub(crate) elements: AddrVec<ElemAddr, ElemInst>,
55 pub(crate) data: AddrVec<DataAddr, DataInst>,
56
57 // fields outside of the spec but are convenient are below
58 /// An address space of modules instantiated within the context of this [`Store`].
59 ///
60 /// Although the WebAssembly Specification 2.0 does not specify module instances
61 /// to be part of the [`Store`], in reality they can be managed very similar to
62 /// other instance types. Therefore, we extend the [`Store`] by a module address
63 /// space along with a `ModuleAddr` index type.
64 pub(crate) modules: AddrVec<ModuleAddr, ModuleInst<'b>>,
65
66 pub user_data: T,
67}
68
69impl<'b, T: Config> Store<'b, T> {
70 /// Creates a new empty store with some user data
71 ///
72 /// See: WebAssembly Specification 2.0 - 7.1.4 - store_init
73 pub fn new(user_data: T) -> Self {
74 // 1. Return the empty store.
75 // For us the store is empty except for the user data, which we do not have control over.
76 Self {
77 functions: AddrVec::default(),
78 tables: AddrVec::default(),
79 memories: AddrVec::default(),
80 globals: AddrVec::default(),
81 elements: AddrVec::default(),
82 data: AddrVec::default(),
83 modules: AddrVec::default(),
84 user_data,
85 }
86 }
87
88 /// Instantiate a new module instance from a [`ValidationInfo`] in this [`Store`].
89 ///
90 /// Note that if this returns an `Err(_)`, the store might be left in an ill-defined state. This might cause further
91 /// operations to have unexpected results.
92 ///
93 /// See: WebAssembly Specification 2.0 - 7.1.5 - module_instantiate
94 ///
95 /// # Safety
96 ///
97 /// The caller has to guarantee that any address values contained in the
98 /// [`ExternVal`]s came from the current [`Store`] object.
99 pub unsafe fn module_instantiate(
100 &mut self,
101 validation_info: &ValidationInfo<'b>,
102 extern_vals: Vec<ExternVal>,
103 maybe_fuel: Option<u64>,
104 ) -> Result<InstantiationOutcome, RuntimeError> {
105 // instantiation: step 1
106 // The module is guaranteed to be valid, because only validation can
107 // produce `ValidationInfo`s.
108
109 // instantiation: step 3
110 if validation_info.imports.len() != extern_vals.len() {
111 return Err(RuntimeError::ExternValsLenMismatch);
112 }
113
114 // instantiation: step 4
115 let imports_as_extern_types = validation_info.imports.iter().map(|import| {
116 // SAFETY: `import` is part of the same `validation_info` and
117 // therefore it was created as part of the same `validation_info`.
118 unsafe { import.desc.extern_type(validation_info) }
119 });
120 for (extern_val, import_as_extern_type) in extern_vals.iter().zip(imports_as_extern_types) {
121 // instantiation: step 4a
122 // check that extern_val is valid in this Store, which should be guaranteed by the caller through a safety constraint in the future.
123 // TODO document this instantiation step properly
124
125 // instantiation: step 4b
126 let extern_type = extern_val.extern_type(self);
127
128 // instantiation: step 4c
129 if !extern_type.is_subtype_of(&import_as_extern_type) {
130 return Err(RuntimeError::InvalidImportType);
131 }
132 }
133
134 // instantiation: step 5
135 // module_inst_init is unfortunately circularly defined from parts of module_inst that would be defined in step 11, which uses module_inst_init again implicitly.
136 // therefore I am mimicking the reference interpreter code here, I will allocate functions in the store in this step instead of step 11.
137 // https://github.com/WebAssembly/spec/blob/8d6792e3d6709e8d3e90828f9c8468253287f7ed/interpreter/exec/eval.ml#L789
138 let module_inst = ModuleInst {
139 types: validation_info.types.clone(),
140 func_addrs: IdxVec::default(),
141 table_addrs: IdxVec::default(),
142 mem_addrs: IdxVec::default(),
143 // TODO This is weird hack with soundness holes. Wasm defines a
144 // special `moduleinst_init`. Here, we want to use the `IdxVec` type
145 // safety, but at the same time only the imports can be populated at
146 // this point.
147 global_addrs: IdxVec::new(extern_vals.iter().globals().collect())
148 .expect(
149 "that the number of imports and therefore also the number of imported globals is <= u32::MAX",
150 ),
151 elem_addrs: IdxVec::default(),
152 data_addrs: IdxVec::default(),
153 exports: BTreeMap::new(),
154 wasm_bytecode: validation_info.wasm,
155 sidetable: validation_info.sidetable.clone(),
156 };
157 let module_addr = self.modules.insert(module_inst);
158
159 let imported_functions = extern_vals.iter().funcs();
160 let local_func_addrs = validation_info
161 .functions
162 .iter_local_definitions()
163 .zip(validation_info.func_blocks_stps.iter())
164 .map(|(ty_idx, (span, stp))| {
165 // SAFETY: The module address is valid for the current store,
166 // because it was just created and the type index is valid for
167 // that same module because it came from that module's
168 // `ValidationInfo`.
169 unsafe { self.alloc_func((*ty_idx, (*span, *stp)), module_addr) }
170 });
171
172 let func_addrs = validation_info
173 .functions
174 .map(imported_functions.collect(), local_func_addrs.collect())
175 .expect(
176 "that the numbers of imported and local functions always \
177 match the respective numbers in the validation info. Step 3 and 4 \
178 check if the number of imported functions is correct and the number \
179 of local functions is a direct one-to-one mapping of \
180 `validation_info.func_blocks_stps`",
181 )
182 .into_inner();
183
184 // SAFETY: The module with this module address was just inserted into
185 // this `AddrVec`
186 let module_inst = unsafe { self.modules.get_mut(module_addr) };
187 module_inst.func_addrs = func_addrs;
188
189 // instantiation: this roughly matches step 6,7,8
190 // validation guarantees these will evaluate without errors.
191 let local_globals_init_vals: Vec<Value> = validation_info
192 .globals
193 .iter_local_definitions()
194 .map(|global| {
195 // SAFETY: All requirements are met:
196 // 1. Validation guarantees that the constant expression for
197 // this global is valid.
198 // 2. The module with this module address was just inserted into
199 // this store's `AddrVec`.
200 let const_expr_result = unsafe {
201 run_const_span(validation_info.wasm, &global.init_expr, module_addr, self)
202 };
203 const_expr_result.transpose().unwrap_validated()
204 })
205 .collect::<Result<Vec<Value>, _>>()?;
206
207 // instantiation: this roughly matches step 9,10 and performs allocation
208 // step 6,12 already
209 let elem_addrs: IdxVec<ElemIdx, ElemAddr> = validation_info.elements.map(|elem| {
210 let refs = match &elem.init {
211 // shortcut of evaluation of "ref.func <func_idx>; end;"
212 // validation guarantees corresponding func_idx's existence
213 ElemItems::RefFuncs(ref_funcs) => {
214 ref_funcs
215 .iter()
216 .map(|func_idx| {
217 // SAFETY: The module with this module address was
218 // just inserted into this `AddrVec`
219 let module = unsafe { self.modules.get(module_addr) };
220 // SAFETY: Both the function index and the module
221 // instance's `func_addrs` come from the same
222 // `ValidationInfo`, i.e. the one passed into this
223 // function.
224 let func_addr = unsafe { module.func_addrs.get(*func_idx) };
225
226 Ref::Func(*func_addr)
227 })
228 .collect()
229 }
230 ElemItems::Exprs(_, exprs) => exprs
231 .iter()
232 .map(|expr| {
233 // SAFETY: All requirements are met:
234 // 1. Validation guarantees that all constant expressions
235 // for elements, including this one, are valid.
236 // 2. The module with this module address was just inserted into
237 // this store's `AddrVec`.
238 let const_expr_result = unsafe {
239 run_const_span(validation_info.wasm, expr, module_addr, self)
240 };
241 const_expr_result
242 .map(|res| res.unwrap_validated().try_into().unwrap_validated())
243 })
244 .collect::<Result<Vec<Ref>, RuntimeError>>()?,
245 };
246
247 // SAFETY: The initial values were retrieved by (1) resolving
248 // function indices or (2) running constant expressions in the
249 // context of the current store. Therefore, their results are also
250 // valid in the current store.
251 let elem = unsafe { self.alloc_elem(elem.ty(), refs) };
252 Ok::<_, RuntimeError>(elem)
253 })?;
254
255 // instantiation: step 11 - module allocation (except function allocation - which was made in step 5)
256 // https://webassembly.github.io/spec/core/exec/modules.html#alloc-module
257
258 // allocation: begin
259
260 // allocation: step 1
261 let module = validation_info;
262
263 // allocation: skip step 2 & 8 as it was done in instantiation step 5
264
265 // allocation: step 3, 9
266 let table_addrs_local: Vec<TableAddr> = module
267 .tables
268 .iter_local_definitions()
269 .map(|table_type| {
270 // SAFETY: The initial table value is null, which is not an
271 // address type and therefore not invalid in the current store.
272 unsafe { self.alloc_table(*table_type, Ref::Null(table_type.et)) }
273 })
274 .collect();
275 // allocation: step 4, 10
276 let mem_addrs_local: Vec<MemAddr> = module
277 .memories
278 .iter_local_definitions()
279 .map(|mem_type| self.alloc_mem(*mem_type))
280 .collect();
281 // allocation: step 5, 11
282 let global_addrs_local: Vec<GlobalAddr> = module
283 .globals
284 .iter_local_definitions()
285 .zip(local_globals_init_vals)
286 .map(|(global, val)| {
287 // SAFETY: The initial values were retrieved by running constant
288 // expressions within the current store context. Therefore,
289 // their results are also valid in the current store.
290 unsafe { self.alloc_global(global.ty, val) }
291 })
292 .collect();
293 // allocation: skip step 6, 12 as it was done in instantiation step 9, 10
294
295 // allocation: step 7, 13
296 let data_addrs = module
297 .data
298 .map::<DataAddr, Infallible>(|data_segment| Ok(self.alloc_data(&data_segment.init)))
299 .expect("infallible error type to never be constructed");
300
301 // allocation: skip step 14 as it was done in instantiation step 5
302
303 // allocation: step 15
304 let table_addrs = validation_info
305 .tables
306 .map(extern_vals.iter().tables().collect(), table_addrs_local)
307 .expect(
308 "that the numbers of imported and local tables always \
309 match the respective numbers in the validation info. Step 3 and 4 \
310 check if the number of imported tables is correct and the number \
311 of local tables is produced by iterating through all table \
312 definitions and performing one-to-one mapping on each one.",
313 )
314 .into_inner();
315
316 // allocation: step 16
317 let mem_addrs = validation_info
318 .memories
319 .map(extern_vals.iter().mems().collect(), mem_addrs_local)
320 .expect(
321 "that the number of imported and local memories always \
322 match the respective numbers in the validation info. Step 3 and 4 \
323 check if the number of imported memories is correct and the number \
324 of local memories is produced by iterating through all memory \
325 definitions and performing one-to-one mapping on each one.",
326 )
327 .into_inner();
328
329 // allocation step 17
330 let global_addrs = validation_info
331 .globals
332 .map(extern_vals.iter().globals().collect(), global_addrs_local)
333 .expect(
334 "that the number of imported and local globals always \
335 match the respective numbers in the validation info. Step 3 and 4 \
336 check if the number of imported globals is correct and the number \
337 of local globals is produced by iterating through all global \
338 definitions and performing one-to-one mapping on each one.",
339 )
340 .into_inner();
341
342 // allocation: step 18,19
343 let export_insts: BTreeMap<String, ExternVal> = module
344 .exports
345 .iter()
346 .map(|export| {
347 // SAFETY: The module with this module address was just inserted
348 // into this `AddrVec`
349 let module_inst = unsafe { self.modules.get(module_addr) };
350 let value = match export.desc {
351 ExportDesc::Func(func_idx) => {
352 // SAFETY: Both the function index and the functions
353 // `IdxVec` come from the same module instance.
354 // Because all indices are valid in their specific
355 // module instance, this is sound.
356 let func_addr = unsafe { module_inst.func_addrs.get(func_idx) };
357 ExternVal::Func(*func_addr)
358 }
359 ExportDesc::Table(table_idx) => {
360 // SAFETY: Both the table index and the tables `IdxVec`
361 // come from the same module instance. Because all
362 // indices are valid in their specific module instance,
363 // this is sound.
364 let table_addr = unsafe { table_addrs.get(table_idx) };
365 ExternVal::Table(*table_addr)
366 }
367 ExportDesc::Mem(mem_idx) => {
368 // SAFETY: Both the memory index and the memories
369 // `IdxVec` come from the same module instance. Because
370 // all indices are valid in their specific module
371 // instance, this is sound.
372 let mem_addr = unsafe { mem_addrs.get(mem_idx) };
373
374 ExternVal::Mem(*mem_addr)
375 }
376 ExportDesc::Global(global_idx) => {
377 // SAFETY: Both the global index and the globals
378 // `ExtendedIdxVec` come from the same module instance.
379 // Because all indices are valid in their specific
380 // module instance, this is sound.
381 let global_addr = unsafe { global_addrs.get(global_idx) };
382
383 ExternVal::Global(*global_addr)
384 }
385 };
386 (export.name.to_owned(), value)
387 })
388 .collect();
389
390 // allocation: step 20,21 initialize module (except functions to instantiation step 5, allocation step 14)
391
392 // SAFETY: The module with this module address was
393 // just inserted into this `AddrVec`
394 let module_inst = unsafe { self.modules.get_mut(module_addr) };
395 module_inst.table_addrs = table_addrs;
396 module_inst.mem_addrs = mem_addrs;
397 module_inst.global_addrs = global_addrs;
398 module_inst.elem_addrs = elem_addrs;
399 module_inst.data_addrs = data_addrs;
400 module_inst.exports = export_insts;
401
402 // allocation: end
403
404 // instantiation step 11 end: module_inst properly allocated after this point.
405
406 // instantiation: step 12-15
407 // TODO have to stray away from the spec a bit since our codebase does not lend itself well to freely executing instructions by themselves
408 for (
409 element_idx,
410 ElemType {
411 init: elem_items,
412 mode,
413 },
414 ) in validation_info.elements.iter_enumerated()
415 {
416 match mode {
417 ElemMode::Active(ActiveElem {
418 table_idx: table_idx_i,
419 init_expr: einstr_i,
420 }) => {
421 let n = elem_items.len() as u32;
422 // equivalent to init.len() in spec
423 // instantiation step 14:
424 // TODO (for now, we are doing hopefully what is equivalent to it)
425 // execute:
426 // einstr_i
427 // i32.const 0
428 // i32.const n
429 // table.init table_idx_i i
430 // elem.drop i
431
432 // SAFETY: The module with this module address was just
433 // inserted into this `AddrVec`. Furthermore, the span comes
434 // from an element contained in the same validation info the
435 // Wasm bytecode is from. Therefore, the constant expression
436 // in that span must be validated already.
437 let const_expr_result = unsafe {
438 run_const_span(validation_info.wasm, einstr_i, module_addr, self)?
439 };
440 let d: i32 = const_expr_result
441 .unwrap_validated() // there is a return value
442 .try_into()
443 .unwrap_validated(); // return value has correct type
444
445 let s = 0;
446 // SAFETY: All requirements are met:
447 // 1. The module address was just retrieved by inserting a
448 // new module instance into the current store. Therefore, it
449 // is valid in the current store.
450 // 2. The table index is valid for the current module
451 // instance because it came from the validation info of the
452 // same module.
453 // 3./5. The table and element addresses are valid because
454 // they come from a module instance that is part of the
455 // current store itself.
456 // 4. The element index is valid for the current module
457 // instance because it came from the validation info of the
458 // same module.
459 unsafe {
460 table_init(
461 &self.modules,
462 &mut self.tables,
463 &self.elements,
464 module_addr,
465 element_idx,
466 *table_idx_i,
467 n,
468 s,
469 d,
470 )?
471 };
472 // SAFETY: All requirements are met:
473 // 1. The module address was just retrieved by inserting a
474 // new module instance into the current store. Therefore, it
475 // is valid in the current store.
476 // 2. The element index is valid for the current module
477 // instance because it came from the validation info of the
478 // same module.
479 // 3. The element address is valid because it comes from a
480 // module instance that is part of the current store itself.
481 unsafe {
482 elem_drop(&self.modules, &mut self.elements, module_addr, element_idx);
483 }
484 }
485 ElemMode::Declarative => {
486 // instantiation step 15:
487 // TODO (for now, we are doing hopefully what is equivalent to it)
488 // execute:
489 // elem.drop i
490
491 // SAFETY: The passed element index comes from the
492 // validation info, that was just used to allocate a new
493 // module instance with address `module_addr` in
494 // `self.modules`. Therefore, it must be safe to use for
495 // accessing the referenced element.
496 // SAFETY: All requirements are met:
497 // 1. The module address was just retrieved by inserting a
498 // new module instance into the current store. Therefore, it
499 // is valid in the current store.
500 // 2. The element index is valid for the current module
501 // instance because it came from the validation info of the
502 // same module.
503 // 3. The element address is valid because it comes from a
504 // module instance that is part of the current store itself.
505 unsafe {
506 elem_drop(&self.modules, &mut self.elements, module_addr, element_idx);
507 }
508 }
509 ElemMode::Passive => (),
510 }
511 }
512
513 // instantiation: step 16
514 // TODO have to stray away from the spec a bit since our codebase does not lend itself well to freely executing instructions by themselves
515 for (i, DataSegment { init, mode }) in validation_info.data.iter_enumerated() {
516 match mode {
517 crate::core::reader::types::data::DataMode::Active(DataModeActive {
518 memory_idx,
519 offset: dinstr_i,
520 }) => {
521 let n = init.len() as u32;
522
523 // TODO (for now, we are doing hopefully what is equivalent to it)
524 // execute:
525 // dinstr_i
526 // i32.const 0
527 // i32.const n
528 // memory.init i
529 // data.drop i
530 // SAFETY: The module with this module address was just
531 // inserted into this `AddrVec`. Furthermore, the span comes
532 // from a data segment contained in the same validation info
533 // the Wasm bytecode is from. Therefore, the constant
534 // expression in that span must be validated already.
535 let const_expr_result = unsafe {
536 run_const_span(validation_info.wasm, dinstr_i, module_addr, self)?
537 };
538 let d: u32 = const_expr_result
539 .unwrap_validated() // there is a return value
540 .try_into()
541 .unwrap_validated(); // return value has the correct type
542
543 let s = 0;
544 // SAFETY: All requirements are met:
545 // 1. The module address was just retrieved by inserting a
546 // new module instance into the current store. Therefore, it
547 // is valid in the current store.
548 // 2. The memory index is valid for the current module
549 // instance because it came from the validation info of the
550 // same module.
551 // 3./5. The memory and data addresses are valid because
552 // they come from a module instance that is part of the
553 // current store itself.
554 // 4. The data index is valid for the current module
555 // instance because it came from the validation info of the
556 // same module.
557 unsafe {
558 memory_init(
559 &self.modules,
560 &mut self.memories,
561 &self.data,
562 module_addr,
563 i,
564 *memory_idx,
565 n,
566 s,
567 d,
568 )
569 }?;
570
571 // SAFETY: All requirements are met:
572 // 1. The module address was just retrieved by inserting a
573 // new module instance into the current store. Therefore, it
574 // is valid in the current store.
575 // 2. The data index is valid for the current module
576 // instance because it came from the validation info of the
577 // same module.
578 // 3. The data address is valid because it comes from a
579 // module instance that is part of the current store itself.
580 unsafe { data_drop(&self.modules, &mut self.data, module_addr, i) };
581 }
582 crate::core::reader::types::data::DataMode::Passive => (),
583 }
584 }
585
586 // instantiation: step 17
587 let maybe_remaining_fuel = if let Some(func_idx) = validation_info.start {
588 // TODO (for now, we are doing hopefully what is equivalent to it)
589 // execute
590 // call func_ifx
591
592 // SAFETY: The module with this module address was just inserted
593 // into this `AddrVec`
594 let module = unsafe { self.modules.get(module_addr) };
595 // SAFETY: The function index comes from the passed `ValidationInfo`
596 // and the `IdxVec<FuncIdx, FuncAddr>` comes from the module
597 // instance that originated from that same `ValidationInfo`.
598 // Therefore, this is sound.
599 let func_addr = unsafe { module.func_addrs.get(func_idx) };
600
601 // SAFETY: The function address just came from the current module
602 // and is therefore valid in the current store. Furthermore, there
603 // are no function arguments and thus also no other address types
604 // can be invalid.
605 let create_resumable_outcome =
606 unsafe { self.create_resumable(*func_addr, Vec::new(), maybe_fuel) }?;
607
608 let Resumable::Wasm(resumable) = create_resumable_outcome else {
609 todo!("calling host functions from the start function");
610 };
611
612 // SAFETY: The resumable just came from the current store.
613 // Therefore, it is always valid in the current store.
614 match unsafe { self.resume_wasm(resumable) }? {
615 RunState::Finished {
616 maybe_remaining_fuel,
617 ..
618 } => maybe_remaining_fuel,
619 RunState::Resumable { .. } => return Err(RuntimeError::OutOfFuel),
620 RunState::HostCalled { .. } => {
621 return Err(RuntimeError::UnsupportedHostCallDuringInstantiation);
622 }
623 }
624 } else {
625 maybe_fuel
626 };
627
628 Ok(InstantiationOutcome {
629 module_addr,
630 maybe_remaining_fuel,
631 })
632 }
633
634 /// Gets an export of a specific module instance by its name
635 ///
636 /// See: WebAssembly Specification 2.0 - 7.1.6 - instance_export
637 ///
638 /// # Safety
639 ///
640 /// The caller has to guarantee that the [`ModuleAddr`] came from the
641 /// current [`Store`] object.
642 pub unsafe fn instance_export(
643 &self,
644 module_addr: ModuleAddr,
645 name: &str,
646 ) -> Result<ExternVal, RuntimeError> {
647 // Fetch the module instance because we store them in the [`Store`]
648 // SAFETY: The caller ensures the module address to be valid in the
649 // current store.
650 let module_inst = unsafe { self.modules.get(module_addr) };
651
652 // 1. Assert: due to validity of the module instance `moduleinst`, all its export names are different
653
654 // 2. If there exists an `exportinst_i` in `moduleinst.exports` such that name `exportinst_i.name` equals `name`, then:
655 // a. Return the external value `exportinst_i.value`.
656 // 3. Else return `error`.
657 module_inst
658 .exports
659 .get(name)
660 .copied()
661 .ok_or(RuntimeError::UnknownExport)
662 }
663
664 /// Allocates a new function with some host code.
665 ///
666 /// This type of function is also called a host function.
667 ///
668 /// # Panics & Unexpected Behavior
669 /// The specification states that:
670 ///
671 /// > This operation must make sure that the provided host function satisfies the pre-
672 /// > and post-conditions required for a function instance with type `functype`.
673 ///
674 /// Therefore, all "invalid" host functions (e.g. those which return incorrect return values)
675 /// can cause the interpreter to panic or behave unexpectedly.
676 ///
677 /// See: <https://webassembly.github.io/spec/core/exec/modules.html#host-functions>
678 /// See: WebAssembly Specification 2.0 - 7.1.7 - func_alloc
679 pub fn func_alloc(&mut self, func_type: FuncType, hostcode: Hostcode) -> FuncAddr {
680 // 1. Pre-condition: `functype` is valid.
681
682 // 2. Let `funcaddr` be the result of allocating a host function in `store` with
683 // function type `functype` and host function code `hostfunc`.
684 // 3. Return the new store paired with `funcaddr`.
685 //
686 // Note: Returning the new store is a noop for us because we mutate the store instead.
687 self.functions.insert(FuncInst::HostFunc(HostFuncInst {
688 function_type: func_type,
689 hostcode,
690 }))
691 }
692
693 /// Gets the type of a function by its addr.
694 ///
695 /// See: WebAssembly Specification 2.0 - 7.1.7 - func_type
696 ///
697 /// # Safety
698 ///
699 /// The caller has to guarantee that the [`FuncAddr`] came from the current
700 /// [`Store`] object.
701 pub unsafe fn func_type(&self, func_addr: FuncAddr) -> FuncType {
702 // 1. Return `S.funcs[a].type`.
703 // SAFETY: The caller ensures this function address to be valid for the
704 // current store.
705 let function = unsafe { self.functions.get(func_addr) };
706 function.ty().clone()
707
708 // 2. Post-condition: the returned function type is valid.
709 }
710
711 /// See: WebAssembly Specification 2.0 - 7.1.7 - func_invoke
712 ///
713 /// # Safety
714 ///
715 /// The caller has to guarantee that the given [`FuncAddr`] and any
716 /// [`FuncAddr`] or [`ExternAddr`](crate::execution::value::ExternAddr)
717 /// values contained in the parameter values came from the current [`Store`]
718 /// object.
719 pub unsafe fn invoke(
720 &mut self,
721 func_addr: FuncAddr,
722 params: Vec<Value>,
723 maybe_fuel: Option<u64>,
724 ) -> Result<RunState, RuntimeError> {
725 // SAFETY: The caller ensures that the function address and any function
726 // addresses or extern addresses contained in the parameter values are
727 // valid in the current store.
728 let resumable = unsafe { self.create_resumable(func_addr, params, maybe_fuel)? };
729 // SAFETY: The resumable just came from the current store. Therefore, it
730 // must be valid in the current store.
731 unsafe { self.resume(resumable) }
732 }
733
734 /// Allocates a new table with some table type and an initialization value `ref` and returns its table address.
735 ///
736 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_alloc
737 ///
738 /// # Safety
739 ///
740 /// The caller has to guarantee that any [`FuncAddr`] or [`ExternAddr`](crate::execution::value::ExternAddr)
741 /// values contained in `r#ref` came from the current [`Store`] object.
742 pub unsafe fn table_alloc(
743 &mut self,
744 table_type: TableType,
745 r#ref: Ref,
746 ) -> Result<TableAddr, RuntimeError> {
747 // Check pre-condition: ref has correct type
748 if table_type.et != r#ref.ty() {
749 return Err(RuntimeError::TableTypeMismatch);
750 }
751
752 // 1. Pre-condition: `tabletype` is valid
753
754 // 2. Let `tableaddr` be the result of allocating a table in `store` with table type `tabletype`
755 // and initialization value `ref`.
756 // SAFETY: The caller ensures that the reference is valid in the current
757 // store.
758 let table_addr = unsafe { self.alloc_table(table_type, r#ref) };
759
760 // 3. Return the new store paired with `tableaddr`.
761 //
762 // Note: Returning the new store is a noop for us because we mutate the store instead.
763 Ok(table_addr)
764 }
765
766 /// Gets the type of some table by its addr.
767 ///
768 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_type
769 ///
770 /// # Safety
771 ///
772 /// The caller has to guarantee that the given [`TableAddr`] came from
773 /// the current [`Store`] object.
774 pub unsafe fn table_type(&self, table_addr: TableAddr) -> TableType {
775 // 1. Return `S.tables[a].type`.
776 // SAFETY: The caller ensures that the given table address is valid in
777 // the current store.
778 let table = unsafe { self.tables.get(table_addr) };
779 table.ty
780
781 // 2. Post-condition: the returned table type is valid.
782 }
783
784 /// Reads a single reference from a table by its table address and an index into the table.
785 ///
786 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_read
787 ///
788 /// # Safety
789 ///
790 /// The caller has to guarantee that the given [`TableAddr`] must come from
791 /// the current [`Store`] object.
792 pub unsafe fn table_read(&self, table_addr: TableAddr, i: u32) -> Result<Ref, RuntimeError> {
793 // Convert `i` to usize for indexing
794 let i = i.into_usize();
795
796 // 1. Let `ti` be the table instance `store.tables[tableaddr]`
797 // SAFETY: The caller ensures that the given table address is valid in
798 // the current store.
799 let ti = unsafe { self.tables.get(table_addr) };
800
801 // 2. If `i` is larger than or equal to the length of `ti.elem`, then return `error`.
802 // 3. Else, return the reference value `ti.elem[i]`.
803 ti.elem
804 .get(i)
805 .copied()
806 .ok_or(RuntimeError::TableAccessOutOfBounds)
807 }
808
809 /// Writes a single reference into a table by its table address and an index into the table.
810 ///
811 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_write
812 ///
813 /// # Safety
814 ///
815 /// The caller has to guarantee that the given [`TableAddr`] and any
816 /// [`FuncAddr`] or [`ExternAddr`](crate::execution::value::ExternAddr)
817 /// values contained in the [`Ref`] must come from the current [`Store`]
818 /// object.
819 pub unsafe fn table_write(
820 &mut self,
821 table_addr: TableAddr,
822 i: u32,
823 r#ref: Ref,
824 ) -> Result<(), RuntimeError> {
825 // Convert `i` to usize for indexing
826 let i = i.into_usize();
827
828 // 1. Let `ti` be the table instance `store.tables[tableaddr]`.
829 // SAFETY: The caller ensures that the given table address is valid in
830 // the current store.
831 let ti = unsafe { self.tables.get_mut(table_addr) };
832
833 // Check pre-condition: ref has correct type
834 if ti.ty.et != r#ref.ty() {
835 return Err(RuntimeError::TableTypeMismatch);
836 }
837
838 // 2. If `i` is larger than or equal to the length of `ti.elem`, then return `error`.
839 // 3. Replace `ti.elem[i]` with the reference value `ref`
840 *ti.elem
841 .get_mut(i)
842 .ok_or(RuntimeError::TableAccessOutOfBounds)? = r#ref;
843
844 // 4. Return the updated store.
845 //
846 // Note: Returning the new store is a noop for us because we mutate the store instead.
847 Ok(())
848 }
849
850 /// Gets the current size of a table by its table address.
851 ///
852 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_size
853 ///
854 /// # Safety
855 ///
856 /// The caller has to guarantee that the given [`TableAddr`] must come from
857 /// the current [`Store`] object.
858 pub unsafe fn table_size(&self, table_addr: TableAddr) -> u32 {
859 // 1. Return the length of `store.tables[tableaddr].elem`.
860 // SAFETY: The caller ensures that the table address is valid in the
861 // current store.
862 let table = unsafe { self.tables.get(table_addr) };
863 let len = table.elem.len();
864
865 // In addition we have to convert the length back to a `u32`
866 u32::try_from(len).expect(
867 "the maximum table length to be u32::MAX because thats what the specification allows for indexing",
868 )
869 }
870
871 /// Grows a table referenced by its table address by `n` elements.
872 ///
873 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_grow
874 ///
875 /// # Safety
876 ///
877 /// The caller has to guarantee that the given [`TableAddr`] and any
878 /// [`FuncAddr`] or [`ExternAddr`](crate::execution::value::ExternAddr)
879 /// values contained in the [`Ref`] must come from the current [`Store`]
880 /// object.
881 pub unsafe fn table_grow(
882 &mut self,
883 table_addr: TableAddr,
884 n: u32,
885 r#ref: Ref,
886 ) -> Result<(), RuntimeError> {
887 // 1. Try growing the table instance `store.tables[tableaddr] by `n` elements with initialization value `ref`:
888 // a. If it succeeds, return the updated store.
889 // b. Else, return `error`.
890 //
891 // Note: Returning the new store is a noop for us because we mutate the store instead.
892 // SAFETY: The caller ensures that the given table address is valid in
893 // the current store.
894 let table = unsafe { self.tables.get_mut(table_addr) };
895 table.grow(n, r#ref)
896 }
897
898 /// Allocates a new linear memory and returns its memory address.
899 ///
900 /// See: WebAssembly Specification 2.0 - 7.1.9 - mem_alloc
901 pub fn mem_alloc(&mut self, mem_type: MemType) -> MemAddr {
902 // 1. Pre-condition: `memtype` is valid.
903
904 // 2. Let `memaddr` be the result of allocating a memory in `store` with memory type `memtype`.
905 // 3. Return the new store paired with `memaddr`.
906 //
907 // Note: Returning the new store is a noop for us because we mutate the store instead.
908 self.alloc_mem(mem_type)
909 }
910
911 /// Gets the memory type of some memory by its memory address
912 ///
913 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_type
914 ///
915 /// # Safety
916 ///
917 /// The caller has to guarantee that the given [`MemAddr`] came from the
918 /// current [`Store`] object.
919 pub unsafe fn mem_type(&self, mem_addr: MemAddr) -> MemType {
920 // 1. Return `S.mems[a].type`.
921 // SAFETY: The caller ensures that the given memory address is valid in
922 // the current store.
923 let memory = unsafe { self.memories.get(mem_addr) };
924 memory.ty
925
926 // 2. Post-condition: the returned memory type is valid.
927 }
928
929 /// Reads a byte from some memory by its memory address and an index into the memory
930 ///
931 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_read
932 ///
933 /// # Safety
934 ///
935 /// The caller has to guarantee that the given [`MemAddr`] came from the
936 /// current [`Store`] object.
937 pub unsafe fn mem_read(&self, mem_addr: MemAddr, i: u32) -> Result<u8, RuntimeError> {
938 // Convert the index type
939 let i = i.into_usize();
940
941 // 1. Let `mi` be the memory instance `store.mems[memaddr]`.
942 // SAFETY: The caller ensures that the given memory address is valid in
943 // the current store.
944 let mi = unsafe { self.memories.get(mem_addr) };
945
946 // 2. If `i` is larger than or equal to the length of `mi.data`, then return `error`.
947 // 3. Else, return the byte `mi.data[i]`.
948 mi.mem.load(i)
949 }
950
951 /// Writes a byte into some memory by its memory address and an index into the memory
952 ///
953 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_write
954 ///
955 /// # Safety
956 ///
957 /// The caller has to guarantee that the given [`MemAddr`] came from the
958 /// current [`Store`] object.
959 pub unsafe fn mem_write(
960 &self,
961 mem_addr: MemAddr,
962 i: u32,
963 byte: u8,
964 ) -> Result<(), RuntimeError> {
965 // Convert the index type
966 let i = i.into_usize();
967
968 // 1. Let `mi` be the memory instance `store.mems[memaddr]`.
969 // SAFETY: The caller ensures that the given memory address is valid in
970 // the current store.
971 let mi = unsafe { self.memories.get(mem_addr) };
972
973 mi.mem.store(i, byte)
974 }
975
976 /// Gets the size of some memory by its memory address in pages.
977 ///
978 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_size
979 ///
980 /// # Safety
981 ///
982 /// The caller has to guarantee that the given [`MemAddr`] came from the
983 /// current [`Store`] object.
984 pub unsafe fn mem_size(&self, mem_addr: MemAddr) -> u32 {
985 // 1. Return the length of `store.mems[memaddr].data` divided by the page size.
986 // SAFETY: The caller ensures that the given memory address is valid in
987 // the current store.
988 let memory = unsafe { self.memories.get(mem_addr) };
989 let length = memory.size();
990
991 // In addition we have to convert the length back to a `u32`
992 length.try_into().expect(
993 "the maximum memory length to be smaller than u32::MAX because thats what the specification allows for indexing into the memory. Also the memory size is measured in pages, not bytes.")
994 }
995
996 /// Grows some memory by its memory address by `n` pages.
997 ///
998 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_grow
999 ///
1000 /// # Safety
1001 ///
1002 /// The caller has to guarantee that the given [`MemAddr`] came from the
1003 /// current [`Store`] object.
1004 pub unsafe fn mem_grow(&mut self, mem_addr: MemAddr, n: u32) -> Result<(), RuntimeError> {
1005 // 1. Try growing the memory instance `store.mems[memaddr]` by `n` pages:
1006 // a. If it succeeds, then return the updated store.
1007 // b. Else, return `error`.
1008 //
1009 // Note: Returning the new store is a noop for us because we mutate the store instead.
1010 // SAFETY: The caller ensures that the given memory address is valid in
1011 // the current store.
1012 let memory = unsafe { self.memories.get_mut(mem_addr) };
1013 memory.grow(n)
1014 }
1015
1016 /// Allocates a new global and returns its global address.
1017 ///
1018 /// See: WebAssemblySpecification 2.0 - 7.1.10 - global_alloc
1019 ///
1020 /// # Safety
1021 ///
1022 /// The caller has to guarantee that any [`FuncAddr`] or
1023 /// [`ExternAddr`](crate::execution::value::ExternAddr) values contained in
1024 /// the [`Value`] came from the current [`Store`] object.
1025 pub unsafe fn global_alloc(
1026 &mut self,
1027 global_type: GlobalType,
1028 val: Value,
1029 ) -> Result<GlobalAddr, RuntimeError> {
1030 // Check pre-condition: val has correct type
1031 if global_type.ty != val.to_ty() {
1032 return Err(RuntimeError::GlobalTypeMismatch);
1033 }
1034
1035 // 1. Pre-condition: `globaltype` is valid.
1036
1037 // 2. Let `globaladdr` be the result of allocating a global with global type `globaltype` and initialization value `val`.
1038 // SAFETY: The caller ensures that any address types contained in the
1039 // initial value are valid in the current store.
1040 let global_addr = unsafe { self.alloc_global(global_type, val) };
1041
1042 // 3. Return the new store paired with `globaladdr`.
1043 //
1044 // Note: Returning the new store is a noop for us because we mutate the store instead.
1045 Ok(global_addr)
1046 }
1047
1048 /// Returns the global type of some global instance by its addr.
1049 ///
1050 /// See: WebAssembly Specification 2.0 - 7.1.10 - global_type
1051 ///
1052 /// # Safety
1053 ///
1054 /// The caller has to guarantee that the given [`GlobalAddr`] came from the
1055 /// current [`Store`] object.
1056 pub unsafe fn global_type(&self, global_addr: GlobalAddr) -> GlobalType {
1057 // 1. Return `S.globals[a].type`.
1058 // SAFETY: The caller ensures that the given global address is valid in
1059 // the current store.
1060 let global = unsafe { self.globals.get(global_addr) };
1061 global.ty
1062 // 2. Post-condition: the returned global type is valid
1063 }
1064
1065 /// Returns the current value of some global instance by its addr.
1066 ///
1067 /// See: WebAssembly Specification 2.0 - 7.1.10 - global_read
1068 ///
1069 /// # Safety
1070 ///
1071 /// The caller has to guarantee that the given [`GlobalAddr`] came from the
1072 /// current [`Store`] object.
1073 pub unsafe fn global_read(&self, global_addr: GlobalAddr) -> Value {
1074 // 1. Let `gi` be the global instance `store.globals[globaladdr].
1075 // SAFETY: The caller ensures that the given global address is valid in
1076 // the current store.
1077 let gi = unsafe { self.globals.get(global_addr) };
1078
1079 // 2. Return the value `gi.value`.
1080 gi.value
1081 }
1082
1083 /// Sets a new value of some global instance by its addr.
1084 ///
1085 /// # Errors
1086 /// - [` RuntimeError::WriteOnImmutableGlobal`]
1087 /// - [` RuntimeError::GlobalTypeMismatch`]
1088 ///
1089 /// See: WebAssembly Specification 2.0 - 7.1.10 - global_write
1090 ///
1091 /// # Safety
1092 ///
1093 /// The caller has to guarantee that the given [`GlobalAddr`] and any
1094 /// [`FuncAddr`] or [`ExternAddr`](crate::execution::value::ExternAddr)
1095 /// values contained in the [`Value`] came from the current [`Store`]
1096 /// object.
1097 pub unsafe fn global_write(
1098 &mut self,
1099 global_addr: GlobalAddr,
1100 val: Value,
1101 ) -> Result<(), RuntimeError> {
1102 // 1. Let `gi` be the global instance `store.globals[globaladdr]`.
1103 // SAFETY: The caller ensures that the given global address is valid in the current module.
1104 let gi = unsafe { self.globals.get_mut(global_addr) };
1105
1106 // 2. Let `mut t` be the structure of the global type `gi.type`.
1107 let r#mut = gi.ty.is_mut;
1108 let t = gi.ty.ty;
1109
1110 // 3. If `mut` is not `var`, then return error.
1111 if !r#mut {
1112 return Err(RuntimeError::WriteOnImmutableGlobal);
1113 }
1114
1115 // Check invariant:
1116 // It is an invariant of the semantics that the value has a type equal to the value type of `globaltype`.
1117 // See: WebAssembly Specification 2.0 - 4.2.9
1118 if t != val.to_ty() {
1119 return Err(RuntimeError::GlobalTypeMismatch);
1120 }
1121
1122 // 4. Replace `gi.value` with the value `val`.
1123 gi.value = val;
1124
1125 // 5. Return the updated store.
1126 // This is a noop for us, as our store `self` is mutable.
1127
1128 Ok(())
1129 }
1130
1131 /// roughly matches <https://webassembly.github.io/spec/core/exec/modules.html#functions> with the addition of sidetable pointer to the input signature
1132 ///
1133 /// # Safety
1134 ///
1135 /// The caller has to guarantee that
1136 /// - the given [`ModuleAddr`] came from the current [`Store`] object.
1137 /// - the given [`TypeIdx`] is valid in the module for the given [`ModuleAddr`].
1138 // TODO refactor the type of func
1139 unsafe fn alloc_func(
1140 &mut self,
1141 func: (TypeIdx, (Span, usize)),
1142 module_addr: ModuleAddr,
1143 ) -> FuncAddr {
1144 let (ty, (span, stp)) = func;
1145
1146 // TODO rewrite this huge chunk of parsing after generic way to re-parse(?) structs lands
1147 // SAFETY: The caller ensures that the given module address is valid in
1148 // the current store.
1149 let module = unsafe { self.modules.get(module_addr) };
1150 let mut wasm_reader = WasmReader::new(module.wasm_bytecode);
1151 wasm_reader.move_start_to(span).unwrap_validated();
1152
1153 let (locals, bytes_read) = wasm_reader
1154 .measure_num_read_bytes(crate::code::read_declared_locals)
1155 .unwrap_validated();
1156
1157 let code_expr = wasm_reader
1158 .make_span(span.len() - bytes_read)
1159 .unwrap_validated();
1160
1161 // core of the method below
1162
1163 // validation guarantees func_ty_idx exists within module_inst.types
1164 // TODO fix clone
1165 // SAFETY: The caller guarantees that the given type index is valid for
1166 // this module.
1167 let function_type = unsafe { module.types.get(ty).clone() };
1168 let func_inst = FuncInst::WasmFunc(WasmFuncInst {
1169 function_type,
1170 _ty: ty,
1171 locals,
1172 code_expr,
1173 stp,
1174 module_addr,
1175 });
1176 self.functions.insert(func_inst)
1177 }
1178
1179 /// <https://webassembly.github.io/spec/core/exec/modules.html#tables>
1180 ///
1181 /// # Safety
1182 ///
1183 /// The caller has to guarantee that any [`FuncAddr`] or
1184 /// [`ExternAddr`](crate::execution::value::ExternAddr) values contained in
1185 /// the [`Ref`] came from the current [`Store`] object.
1186 unsafe fn alloc_table(&mut self, table_type: TableType, reff: Ref) -> TableAddr {
1187 let table_inst = TableInst {
1188 ty: table_type,
1189 elem: vec![reff; table_type.lim.min.into_usize()],
1190 };
1191
1192 self.tables.insert(table_inst)
1193 }
1194
1195 /// <https://webassembly.github.io/spec/core/exec/modules.html#memories>
1196 fn alloc_mem(&mut self, mem_type: MemType) -> MemAddr {
1197 let mem_inst = MemInst {
1198 ty: mem_type,
1199 mem: LinearMemory::new_with_initial_pages(
1200 mem_type.limits.min.try_into().unwrap_validated(),
1201 ),
1202 };
1203
1204 self.memories.insert(mem_inst)
1205 }
1206
1207 /// <https://webassembly.github.io/spec/core/exec/modules.html#globals>
1208 ///
1209 /// # Safety
1210 ///
1211 /// The caller has to guarantee that any [`FuncAddr`] or
1212 /// [`ExternAddr`](crate::execution::value::ExternAddr) values contained in
1213 /// the [`Value`] came from the current [`Store`] object.
1214 unsafe fn alloc_global(&mut self, global_type: GlobalType, val: Value) -> GlobalAddr {
1215 let global_inst = GlobalInst {
1216 ty: global_type,
1217 value: val,
1218 };
1219
1220 self.globals.insert(global_inst)
1221 }
1222
1223 /// <https://webassembly.github.io/spec/core/exec/modules.html#element-segments>
1224 ///
1225 /// # Safety
1226 ///
1227 /// The caller has to guarantee that any [`FuncAddr`] or
1228 /// [`ExternAddr`](crate::execution::value::ExternAddr) values contained in
1229 /// the [`Ref`]s came from the current [`Store`] object.
1230 unsafe fn alloc_elem(&mut self, ref_type: RefType, refs: Vec<Ref>) -> ElemAddr {
1231 let elem_inst = ElemInst {
1232 _ty: ref_type,
1233 references: refs,
1234 };
1235
1236 self.elements.insert(elem_inst)
1237 }
1238
1239 /// <https://webassembly.github.io/spec/core/exec/modules.html#data-segments>
1240 fn alloc_data(&mut self, bytes: &[u8]) -> DataAddr {
1241 let data_inst = DataInst {
1242 data: Vec::from(bytes),
1243 };
1244
1245 self.data.insert(data_inst)
1246 }
1247
1248 /// Creates a new resumable, which when resumed for the first time invokes the function `function_ref` is associated
1249 /// to, with the arguments `params`. The newly created resumable initially stores `fuel` units of fuel. Returns a
1250 /// `[ResumableRef]` associated to the newly created resumable on success.
1251 ///
1252 /// # Safety
1253 ///
1254 /// The caller has to guarantee that the [`FuncAddr`] and any [`FuncAddr`]
1255 /// or [`ExternAddr`](crate::execution::value::ExternAddr) values contained
1256 /// in the parameter values came from the current [`Store`] object.
1257 pub unsafe fn create_resumable(
1258 &self,
1259 func_addr: FuncAddr,
1260 params: Vec<Value>,
1261 maybe_fuel: Option<u64>,
1262 ) -> Result<Resumable, RuntimeError> {
1263 // SAFETY: The caller ensures that this function address is valid in the
1264 // current store.
1265 let func_inst = unsafe { self.functions.get(func_addr) };
1266
1267 let func_ty = func_inst.ty();
1268
1269 // Verify that the given parameter types match the function parameter types
1270 if func_ty.params.valtypes.len() != params.len() {
1271 return Err(RuntimeError::FunctionInvocationSignatureMismatch);
1272 }
1273
1274 let type_mismatch = func_ty
1275 .params
1276 .valtypes
1277 .iter()
1278 .zip(¶ms)
1279 .any(|(func_val_ty, param_val)| func_val_ty != ¶m_val.to_ty());
1280
1281 if type_mismatch {
1282 return Err(RuntimeError::FunctionInvocationSignatureMismatch);
1283 };
1284
1285 let resumable = match func_inst {
1286 FuncInst::WasmFunc(wasm_func_inst) => {
1287 // Prepare a new stack with the locals for the entry function
1288 let stack = Stack::new::<T>(
1289 params,
1290 &wasm_func_inst.function_type,
1291 &wasm_func_inst.locals,
1292 )?;
1293
1294 Resumable::Wasm(WasmResumable {
1295 current_func_addr: func_addr,
1296 stack,
1297 pc: wasm_func_inst.code_expr.from,
1298 stp: wasm_func_inst.stp,
1299 maybe_fuel,
1300 })
1301 }
1302 FuncInst::HostFunc(host_func_inst) => Resumable::Host {
1303 host_call: HostCall {
1304 params,
1305 hostcode: host_func_inst.hostcode,
1306 },
1307 host_resumable: HostResumable {
1308 host_func_addr: func_addr,
1309 inner_resumable: None,
1310 maybe_fuel: Some(maybe_fuel),
1311 },
1312 },
1313 };
1314
1315 Ok(resumable)
1316 }
1317
1318 /// Resumes execution of a [`Resumable`], regardless of its inner representation.
1319 ///
1320 /// Note: The [`Resumable`] may also be deconstructed by the user and its
1321 /// contents used with [`Store::resume_wasm`] or
1322 /// [`Store::finish_host_call`].
1323 ///
1324 /// # Safety
1325 ///
1326 /// The caller has to guarantee that the [`Resumable`] came from the current
1327 /// [`Store`] object.
1328 pub unsafe fn resume(&mut self, resumable: Resumable) -> Result<RunState, RuntimeError> {
1329 match resumable {
1330 // SAFETY: The caller ensures that this `WasmResumable` came from
1331 // the current store.
1332 Resumable::Wasm(wasm_resumable) => unsafe { self.resume_wasm(wasm_resumable) },
1333 Resumable::Host {
1334 host_call,
1335 host_resumable,
1336 } => Ok(RunState::HostCalled {
1337 host_call,
1338 resumable: host_resumable,
1339 }),
1340 }
1341 }
1342
1343 /// Resumes the given [`WasmResumable`]. Returns a [`RunState`] that may contain
1344 /// a new [`Resumable`] depending on whether execution ran out of fuel or
1345 /// finished normally.
1346 ///
1347 /// # Safety
1348 ///
1349 /// The caller has to guarantee that the [`Resumable`] came from the current
1350 /// [`Store`] object.
1351 pub unsafe fn resume_wasm(
1352 &mut self,
1353 mut resumable: WasmResumable,
1354 ) -> Result<RunState, RuntimeError> {
1355 let result = interpreter_loop::run(&mut resumable, self)?;
1356
1357 let run_state = match result {
1358 InterpreterLoopOutcome::ExecutionReturned => RunState::Finished {
1359 values: resumable.stack.into_values(),
1360 maybe_remaining_fuel: resumable.maybe_fuel,
1361 },
1362 InterpreterLoopOutcome::OutOfFuel { required_fuel } => RunState::Resumable {
1363 resumable,
1364 required_fuel: Some(required_fuel),
1365 },
1366 InterpreterLoopOutcome::HostCalled {
1367 func_addr,
1368 params,
1369 hostcode,
1370 } => RunState::HostCalled {
1371 host_call: HostCall { params, hostcode },
1372 resumable: HostResumable {
1373 host_func_addr: func_addr,
1374 inner_resumable: Some(resumable),
1375 maybe_fuel: None,
1376 },
1377 },
1378 };
1379
1380 Ok(run_state)
1381 }
1382
1383 /// To be executed after executing a [`HostCall`].
1384 ///
1385 /// # Safety
1386 ///
1387 /// The caller has to guarantee that the [`HostResumable`] and all
1388 /// addresses in the return values came from the current [`Store`] object.
1389 pub unsafe fn finish_host_call(
1390 &mut self,
1391 host_resumable: HostResumable,
1392 host_call_return_values: Vec<Value>,
1393 ) -> Result<RunState, RuntimeError> {
1394 // Verify that the return parameters match the host function parameters
1395 // since we have no validation guarantees for host functions
1396
1397 // SAFETY: The caller ensures that the `HostResumable`, and thus also
1398 // the function address in it, is valid in the current store.
1399 let function = unsafe { self.functions.get(host_resumable.host_func_addr) };
1400
1401 let FuncInst::HostFunc(host_func_inst) = function else {
1402 unreachable!("expected function to be a host function instance")
1403 };
1404
1405 let return_types = host_call_return_values
1406 .iter()
1407 .map(|v| v.to_ty())
1408 .collect::<Vec<_>>();
1409
1410 if host_func_inst.function_type.returns.valtypes != return_types {
1411 return Err(RuntimeError::HostFunctionSignatureMismatch);
1412 }
1413
1414 if let Some(mut wasm_resumable) = host_resumable.inner_resumable {
1415 for return_value in host_call_return_values {
1416 wasm_resumable.stack.push_value::<T>(return_value)?;
1417 }
1418
1419 Ok(RunState::Resumable {
1420 resumable: wasm_resumable,
1421 required_fuel: None,
1422 })
1423 } else {
1424 Ok(RunState::Finished {
1425 values: host_call_return_values,
1426 maybe_remaining_fuel: host_resumable
1427 .maybe_fuel
1428 .expect("this to be set if the inner WasmResumable is None"),
1429 })
1430 }
1431 }
1432
1433 /// Invokes a function without support for fuel or host functions.
1434 ///
1435 /// This function wraps [`Store::invoke`].
1436 ///
1437 /// # Safety
1438 ///
1439 /// The caller has to guarantee that the given [`FuncAddr`] and any
1440 /// [`FuncAddr`] or [`ExternAddr`](crate::execution::value::ExternAddr)
1441 /// values contained in the parameter values came from the current [`Store`]
1442 /// object.
1443 pub unsafe fn invoke_simple(
1444 &mut self,
1445 function: FuncAddr,
1446 params: Vec<Value>,
1447 ) -> Result<Vec<Value>, RuntimeError> {
1448 // SAFETY: The caller ensures that the given function address and all
1449 // address types contained in the parameters are valid in the current
1450 // store.
1451 let run_state = unsafe { self.invoke(function, params, None) }?;
1452
1453 match run_state {
1454 RunState::Finished {
1455 values,
1456 maybe_remaining_fuel: _,
1457 } => Ok(values),
1458 RunState::Resumable { .. } => unreachable!("fuel is disabled"),
1459 RunState::HostCalled { .. } => Err(RuntimeError::UnexpectedHostCall),
1460 }
1461 }
1462
1463 /// Allows a given closure to temporarily access the entire memory as a
1464 /// `&mut [u8]`.
1465 ///
1466 /// # Safety
1467 ///
1468 /// The caller has to guarantee that the given [`MemAddr`] came from the
1469 /// current [`Store`] object.
1470 pub unsafe fn mem_access_mut_slice<R>(
1471 &self,
1472 memory: MemAddr,
1473 accessor: impl FnOnce(&mut [u8]) -> R,
1474 ) -> R {
1475 // SAFETY: The caller ensures that the given memory address is valid in
1476 // the current store.
1477 let memory = unsafe { self.memories.get(memory) };
1478 memory.mem.access_mut_slice(accessor)
1479 }
1480
1481 /// Returns all exports of a module instance by its module address.
1482 ///
1483 /// To get a single import by its known name, use
1484 /// [`Store::instance_export`].
1485 ///
1486 /// # Safety
1487 ///
1488 /// The caller has to guarantee that the given [`ModuleAddr`] came from the
1489 /// current [`Store`] object.
1490 pub unsafe fn instance_exports(&self, module_addr: ModuleAddr) -> Vec<(String, ExternVal)> {
1491 // SAFETY: The caller ensures that the given module address is valid in
1492 // the current store.
1493 let module = unsafe { self.modules.get(module_addr) };
1494
1495 module
1496 .exports
1497 .iter()
1498 .map(|(name, externval)| (name.clone(), *externval))
1499 .collect()
1500 }
1501}
1502
1503///<https://webassembly.github.io/spec/core/exec/runtime.html#external-values>
1504#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1505pub enum ExternVal {
1506 Func(FuncAddr),
1507 Table(TableAddr),
1508 Mem(MemAddr),
1509 Global(GlobalAddr),
1510}
1511
1512impl ExternVal {
1513 /// returns the external type of `self` according to typing relation,
1514 /// taking `store` as context S.
1515 ///
1516 /// # Safety
1517 /// The caller has to guarantee that `self` came from the same [`Store`] which
1518 /// is passed now as a reference.
1519 pub fn extern_type<T: Config>(&self, store: &Store<T>) -> ExternType {
1520 match self {
1521 // TODO: fix ugly clone in function types
1522 ExternVal::Func(func_addr) => {
1523 // SAFETY: The caller ensures that self including the function
1524 // address in self is valid in the given store.
1525 let function = unsafe { store.functions.get(*func_addr) };
1526 ExternType::Func(function.ty().clone())
1527 }
1528 ExternVal::Table(table_addr) => {
1529 // SAFETY: The caller ensures that self including the table
1530 // address in self is valid in the given store.
1531 let table = unsafe { store.tables.get(*table_addr) };
1532 ExternType::Table(table.ty)
1533 }
1534 ExternVal::Mem(mem_addr) => {
1535 // SAFETY: The caller ensures that self including the memory
1536 // address in self is valid in the given store.
1537 let memory = unsafe { store.memories.get(*mem_addr) };
1538 ExternType::Mem(memory.ty)
1539 }
1540 ExternVal::Global(global_addr) => {
1541 // SAFETY: The caller ensures that self including the global
1542 // address in self is valid in the given store.
1543 let global = unsafe { store.globals.get(*global_addr) };
1544 ExternType::Global(global.ty)
1545 }
1546 }
1547 }
1548}
1549
1550impl ExternVal {
1551 pub fn as_func(self) -> Option<FuncAddr> {
1552 match self {
1553 ExternVal::Func(func_addr) => Some(func_addr),
1554 _ => None,
1555 }
1556 }
1557
1558 pub fn as_table(self) -> Option<TableAddr> {
1559 match self {
1560 ExternVal::Table(table_addr) => Some(table_addr),
1561 _ => None,
1562 }
1563 }
1564
1565 pub fn as_mem(self) -> Option<MemAddr> {
1566 match self {
1567 ExternVal::Mem(mem_addr) => Some(mem_addr),
1568 _ => None,
1569 }
1570 }
1571
1572 pub fn as_global(self) -> Option<GlobalAddr> {
1573 match self {
1574 ExternVal::Global(global_addr) => Some(global_addr),
1575 _ => None,
1576 }
1577 }
1578}
1579
1580/// common convention functions defined for lists of ExternVals, ExternTypes, Exports
1581/// <https://webassembly.github.io/spec/core/exec/runtime.html#conventions>
1582/// <https://webassembly.github.io/spec/core/syntax/types.html#id3>
1583/// <https://webassembly.github.io/spec/core/syntax/modules.html?highlight=convention#id1>
1584// TODO implement this trait for ExternType lists Export lists
1585pub trait ExternFilterable {
1586 fn funcs(self) -> impl Iterator<Item = FuncAddr>;
1587 fn tables(self) -> impl Iterator<Item = TableAddr>;
1588 fn mems(self) -> impl Iterator<Item = MemAddr>;
1589 fn globals(self) -> impl Iterator<Item = GlobalAddr>;
1590}
1591
1592impl<'a, I> ExternFilterable for I
1593where
1594 I: Iterator<Item = &'a ExternVal>,
1595{
1596 fn funcs(self) -> impl Iterator<Item = FuncAddr> {
1597 self.filter_map(|extern_val| extern_val.as_func())
1598 }
1599
1600 fn tables(self) -> impl Iterator<Item = TableAddr> {
1601 self.filter_map(|extern_val| extern_val.as_table())
1602 }
1603
1604 fn mems(self) -> impl Iterator<Item = MemAddr> {
1605 self.filter_map(|extern_val| extern_val.as_mem())
1606 }
1607
1608 fn globals(self) -> impl Iterator<Item = GlobalAddr> {
1609 self.filter_map(|extern_val| extern_val.as_global())
1610 }
1611}
1612
1613/// Represents a successful, possibly fueled instantiation of a module.
1614pub struct InstantiationOutcome {
1615 /// contains the store address of the module that has successfully instantiated.
1616 pub module_addr: ModuleAddr,
1617 /// contains `Some(remaining_fuel)` if instantiation was fuel-metered and `None` otherwise.
1618 pub maybe_remaining_fuel: Option<u64>,
1619}
1620
1621pub type Hostcode = usize;