wasm/execution/store/mod.rs
1use core::mem;
2use core::sync::atomic::{AtomicU64, Ordering};
3
4use crate::addrs::{
5 AddrVec, DataAddr, ElemAddr, FuncAddr, GlobalAddr, MemAddr, ModuleAddr, TableAddr,
6};
7use crate::config::Config;
8use crate::core::indices::TypeIdx;
9use crate::core::reader::span::Span;
10use crate::core::reader::types::data::{DataModeActive, DataSegment};
11use crate::core::reader::types::element::{ActiveElem, ElemItems, ElemMode, ElemType};
12use crate::core::reader::types::export::{Export, ExportDesc};
13use crate::core::reader::types::global::{Global, GlobalType};
14use crate::core::reader::types::{
15 ExternType, FuncType, ImportSubTypeRelation, MemType, ResultType, TableType,
16};
17use crate::core::reader::WasmReader;
18use crate::execution::interpreter_loop::{self, memory_init, table_init};
19use crate::execution::value::{Ref, Value};
20use crate::execution::{run_const_span, Stack};
21use crate::resumable::{
22 Dormitory, FreshResumableRef, InvokedResumableRef, Resumable, ResumableRef, RunState,
23};
24use crate::{RefType, RuntimeError, ValidationInfo};
25use alloc::collections::btree_map::BTreeMap;
26use alloc::string::String;
27use alloc::sync::Arc;
28use alloc::vec;
29use alloc::vec::Vec;
30use instances::{
31 DataInst, ElemInst, FuncInst, GlobalInst, HostFuncInst, MemInst, ModuleInst, TableInst,
32 WasmFuncInst,
33};
34use linear_memory::LinearMemory;
35
36use super::interop::InteropValueList;
37use super::interpreter_loop::{data_drop, elem_drop};
38use super::value::ValueTypeMismatchError;
39use super::UnwrapValidatedExt;
40
41pub mod addrs;
42pub(crate) mod instances;
43pub(crate) mod linear_memory;
44
45/// The store represents all global state that can be manipulated by WebAssembly programs. It
46/// consists of the runtime representation of all instances of functions, tables, memories, and
47/// globals, element segments, and data segments that have been allocated during the life time of
48/// the abstract machine.
49/// <https://webassembly.github.io/spec/core/exec/runtime.html#store>
50pub struct Store<'b, T: Config> {
51 pub(crate) functions: AddrVec<FuncAddr, FuncInst<T>>,
52 pub(crate) tables: AddrVec<TableAddr, TableInst>,
53 pub(crate) memories: AddrVec<MemAddr, MemInst>,
54 pub(crate) globals: AddrVec<GlobalAddr, GlobalInst>,
55 pub(crate) elements: AddrVec<ElemAddr, ElemInst>,
56 pub(crate) data: AddrVec<DataAddr, DataInst>,
57
58 // fields outside of the spec but are convenient are below
59 /// An address space of modules instantiated within the context of this [`Store`].
60 ///
61 /// Although the WebAssembly Specification 2.0 does not specify module instances
62 /// to be part of the [`Store`], in reality they can be managed very similar to
63 /// other instance types. Therefore, we extend the [`Store`] by a module address
64 /// space along with a `ModuleAddr` index type.
65 pub(crate) modules: AddrVec<ModuleAddr, ModuleInst<'b>>,
66
67 /// A unique identifier for this store. This is used to verify that
68 /// stored objects belong to the current [`Store`].
69 pub(crate) id: StoreId,
70 pub user_data: T,
71
72 // data structure holding all resumable objects that belong to this store
73 pub(crate) dormitory: Dormitory,
74}
75
76impl<'b, T: Config> Store<'b, T> {
77 /// Creates a new empty store with some user data
78 ///
79 /// See: WebAssembly Specification 2.0 - 7.1.4 - store_init
80 pub fn new(user_data: T) -> Self {
81 // 1. Return the empty store.
82 // For us the store is empty except for the user data, which we do not have control over.
83 Self {
84 functions: AddrVec::default(),
85 tables: AddrVec::default(),
86 memories: AddrVec::default(),
87 globals: AddrVec::default(),
88 elements: AddrVec::default(),
89 data: AddrVec::default(),
90 modules: AddrVec::default(),
91 id: StoreId::new(),
92 dormitory: Dormitory::default(),
93 user_data,
94 }
95 }
96
97 /// Instantiate a new module instance from a [`ValidationInfo`] in this [`Store`].
98 ///
99 /// Note that if this returns an `Err(_)`, the store might be left in an ill-defined state. This might cause further
100 /// operations to have unexpected results.
101 ///
102 /// See: WebAssembly Specification 2.0 - 7.1.5 - module_instantiate
103 ///
104 /// # Safety
105 /// The caller has to guarantee that any address values contained in the
106 /// [`ExternVal`]s came from the current [`Store`] object.
107 pub fn module_instantiate_unchecked(
108 &mut self,
109 validation_info: &ValidationInfo<'b>,
110 extern_vals: Vec<ExternVal>,
111 maybe_fuel: Option<u32>,
112 ) -> Result<InstantiationOutcome, RuntimeError> {
113 // instantiation: step 1
114 // The module is guaranteed to be valid, because only validation can
115 // produce `ValidationInfo`s.
116
117 // instantiation: step 3
118 if validation_info.imports.len() != extern_vals.len() {
119 return Err(RuntimeError::ExternValsLenMismatch);
120 }
121
122 // instantiation: step 4
123 let imports_as_extern_types = validation_info
124 .imports
125 .iter()
126 .map(|import| import.desc.extern_type(validation_info));
127 for (extern_val, import_as_extern_type) in extern_vals.iter().zip(imports_as_extern_types) {
128 // instantiation: step 4a
129 // check that extern_val is valid in this Store, which should be guaranteed by the caller through a safety constraint in the future.
130 // TODO document this instantiation step properly
131
132 // instantiation: step 4b
133 let extern_type = extern_val.extern_type(self);
134
135 // instantiation: step 4c
136 if !extern_type.is_subtype_of(&import_as_extern_type) {
137 return Err(RuntimeError::InvalidImportType);
138 }
139 }
140
141 // instantiation: step 5
142 // 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.
143 // therefore I am mimicking the reference interpreter code here, I will allocate functions in the store in this step instead of step 11.
144 // https://github.com/WebAssembly/spec/blob/8d6792e3d6709e8d3e90828f9c8468253287f7ed/interpreter/exec/eval.ml#L789
145 let module_inst = ModuleInst {
146 types: validation_info.types.clone(),
147 func_addrs: extern_vals.iter().funcs().collect(),
148 table_addrs: Vec::new(),
149 mem_addrs: Vec::new(),
150 global_addrs: extern_vals.iter().globals().collect(),
151 elem_addrs: Vec::new(),
152 data_addrs: Vec::new(),
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 // TODO rewrite this part
160 // <https://webassembly.github.io/spec/core/exec/modules.html#functions>
161 let func_addrs: Vec<FuncAddr> = validation_info
162 .functions
163 .iter()
164 .zip(validation_info.func_blocks_stps.iter())
165 .map(|(ty_idx, (span, stp))| self.alloc_func((*ty_idx, (*span, *stp)), module_addr))
166 .collect();
167
168 self.modules
169 .get_mut(module_addr)
170 .func_addrs
171 .extend(func_addrs);
172
173 // instantiation: this roughly matches step 6,7,8
174 // validation guarantees these will evaluate without errors.
175 let maybe_global_init_vals: Result<Vec<Value>, _> = validation_info
176 .globals
177 .iter()
178 .map(|global| {
179 run_const_span(validation_info.wasm, &global.init_expr, module_addr, self)
180 .transpose()
181 .unwrap_validated()
182 })
183 .collect();
184 let global_init_vals = maybe_global_init_vals?;
185
186 // instantiation: this roughly matches step 9,10
187
188 let mut element_init_ref_lists: Vec<Vec<Ref>> =
189 Vec::with_capacity(validation_info.elements.len());
190
191 for elem in &validation_info.elements {
192 let mut new_list = Vec::new();
193 match &elem.init {
194 // shortcut of evaluation of "ref.func <func_idx>; end;"
195 // validation guarantees corresponding func_idx's existence
196 ElemItems::RefFuncs(ref_funcs) => {
197 for func_idx in ref_funcs {
198 let func_addr = *self
199 .modules
200 .get(module_addr)
201 .func_addrs
202 .get(*func_idx as usize)
203 .unwrap_validated();
204
205 new_list.push(Ref::Func(func_addr));
206 }
207 }
208 ElemItems::Exprs(_, exprs) => {
209 for expr in exprs {
210 new_list.push(
211 run_const_span(validation_info.wasm, expr, module_addr, self)?
212 .unwrap_validated() // there is a return value
213 .try_into()
214 .unwrap_validated(), // return value has the correct type
215 )
216 }
217 }
218 }
219 element_init_ref_lists.push(new_list);
220 }
221
222 // instantiation: step 11 - module allocation (except function allocation - which was made in step 5)
223 // https://webassembly.github.io/spec/core/exec/modules.html#alloc-module
224
225 // allocation: begin
226
227 // allocation: step 1
228 let module = validation_info;
229
230 let vals = global_init_vals;
231 let ref_lists = element_init_ref_lists;
232
233 // allocation: skip step 2 as it was done in instantiation step 5
234
235 // allocation: step 3-13
236 let table_addrs: Vec<TableAddr> = module
237 .tables
238 .iter()
239 .map(|table_type| self.alloc_table(*table_type, Ref::Null(table_type.et)))
240 .collect();
241 let mem_addrs: Vec<MemAddr> = module
242 .memories
243 .iter()
244 .map(|mem_type| self.alloc_mem(*mem_type))
245 .collect();
246 let global_addrs: Vec<GlobalAddr> = module
247 .globals
248 .iter()
249 .zip(vals)
250 .map(
251 |(
252 Global {
253 ty: global_type, ..
254 },
255 val,
256 )| self.alloc_global(*global_type, val),
257 )
258 .collect();
259 let elem_addrs = module
260 .elements
261 .iter()
262 .zip(ref_lists)
263 .map(|(elem, refs)| self.alloc_elem(elem.ty(), refs))
264 .collect();
265 let data_addrs = module
266 .data
267 .iter()
268 .map(|DataSegment { init: bytes, .. }| self.alloc_data(bytes))
269 .collect();
270
271 // allocation: skip step 14 as it was done in instantiation step 5
272
273 // allocation: step 15,16
274 let mut table_addrs_mod: Vec<TableAddr> = extern_vals.iter().tables().collect();
275 table_addrs_mod.extend(table_addrs);
276
277 let mut mem_addrs_mod: Vec<MemAddr> = extern_vals.iter().mems().collect();
278 mem_addrs_mod.extend(mem_addrs);
279
280 // skipping step 17 partially as it was partially done in instantiation step
281 self.modules
282 .get_mut(module_addr)
283 .global_addrs
284 .extend(global_addrs);
285
286 // allocation: step 18,19
287 let export_insts: BTreeMap<String, ExternVal> = module
288 .exports
289 .iter()
290 .map(|Export { name, desc }| {
291 let module_inst = self.modules.get(module_addr);
292 let value = match desc {
293 ExportDesc::FuncIdx(func_idx) => {
294 ExternVal::Func(module_inst.func_addrs[*func_idx])
295 }
296 ExportDesc::TableIdx(table_idx) => {
297 ExternVal::Table(table_addrs_mod[*table_idx])
298 }
299 ExportDesc::MemIdx(mem_idx) => ExternVal::Mem(mem_addrs_mod[*mem_idx]),
300 ExportDesc::GlobalIdx(global_idx) => {
301 ExternVal::Global(module_inst.global_addrs[*global_idx])
302 }
303 };
304 (String::from(name), value)
305 })
306 .collect();
307
308 // allocation: step 20,21 initialize module (except functions and globals due to instantiation step 5, allocation step 14,17)
309 let module_inst = self.modules.get_mut(module_addr);
310 module_inst.table_addrs = table_addrs_mod;
311 module_inst.mem_addrs = mem_addrs_mod;
312 module_inst.elem_addrs = elem_addrs;
313 module_inst.data_addrs = data_addrs;
314 module_inst.exports = export_insts;
315
316 // allocation: end
317
318 // instantiation step 11 end: module_inst properly allocated after this point.
319
320 // instantiation: step 12-15
321 // TODO have to stray away from the spec a bit since our codebase does not lend itself well to freely executing instructions by themselves
322 for (
323 i,
324 ElemType {
325 init: elem_items,
326 mode,
327 },
328 ) in validation_info.elements.iter().enumerate()
329 {
330 match mode {
331 ElemMode::Active(ActiveElem {
332 table_idx: table_idx_i,
333 init_expr: einstr_i,
334 }) => {
335 let n = elem_items.len() as u32;
336 // equivalent to init.len() in spec
337 // instantiation step 14:
338 // TODO (for now, we are doing hopefully what is equivalent to it)
339 // execute:
340 // einstr_i
341 // i32.const 0
342 // i32.const n
343 // table.init table_idx_i i
344 // elem.drop i
345 let d: i32 = run_const_span(validation_info.wasm, einstr_i, module_addr, self)?
346 .unwrap_validated() // there is a return value
347 .try_into()
348 .unwrap_validated(); // return value has correct type
349
350 let s = 0;
351 table_init(
352 &self.modules,
353 &mut self.tables,
354 &self.elements,
355 module_addr,
356 i,
357 *table_idx_i as usize,
358 n,
359 s,
360 d,
361 )?;
362 elem_drop(&self.modules, &mut self.elements, module_addr, i)?;
363 }
364 ElemMode::Declarative => {
365 // instantiation step 15:
366 // TODO (for now, we are doing hopefully what is equivalent to it)
367 // execute:
368 // elem.drop i
369 elem_drop(&self.modules, &mut self.elements, module_addr, i)?;
370 }
371 ElemMode::Passive => (),
372 }
373 }
374
375 // instantiation: step 16
376 // TODO have to stray away from the spec a bit since our codebase does not lend itself well to freely executing instructions by themselves
377 for (i, DataSegment { init, mode }) in validation_info.data.iter().enumerate() {
378 match mode {
379 crate::core::reader::types::data::DataMode::Active(DataModeActive {
380 memory_idx,
381 offset: dinstr_i,
382 }) => {
383 let n = init.len() as u32;
384 // assert: mem_idx is 0
385 if *memory_idx != 0 {
386 // TODO fix error
387 return Err(RuntimeError::MoreThanOneMemory);
388 }
389
390 // TODO (for now, we are doing hopefully what is equivalent to it)
391 // execute:
392 // dinstr_i
393 // i32.const 0
394 // i32.const n
395 // memory.init i
396 // data.drop i
397 let d: i32 = run_const_span(validation_info.wasm, dinstr_i, module_addr, self)?
398 .unwrap_validated() // there is a return value
399 .try_into()
400 .unwrap_validated(); // return value has the correct type
401
402 let s = 0;
403 memory_init(
404 &self.modules,
405 &mut self.memories,
406 &self.data,
407 module_addr,
408 i,
409 0,
410 n,
411 s,
412 d,
413 )?;
414 data_drop(&self.modules, &mut self.data, module_addr, i)?;
415 }
416 crate::core::reader::types::data::DataMode::Passive => (),
417 }
418 }
419
420 // instantiation: step 17
421 let maybe_remaining_fuel = if let Some(func_idx) = validation_info.start {
422 // TODO (for now, we are doing hopefully what is equivalent to it)
423 // execute
424 // call func_ifx
425 let func_addr = self.modules.get(module_addr).func_addrs[func_idx];
426 let RunState::Finished {
427 maybe_remaining_fuel,
428 ..
429 } = self.invoke_unchecked(func_addr, Vec::new(), maybe_fuel)?
430 else {
431 return Err(RuntimeError::OutOfFuel);
432 };
433 maybe_remaining_fuel
434 } else {
435 maybe_fuel
436 };
437
438 Ok(InstantiationOutcome {
439 module_addr,
440 maybe_remaining_fuel,
441 })
442 }
443
444 /// Gets an export of a specific module instance by its name
445 ///
446 /// See: WebAssembly Specification 2.0 - 7.1.6 - instance_export
447 ///
448 /// # Safety
449 /// The caller has to guarantee that the [`ModuleAddr`] came from the
450 /// current [`Store`] object.
451 pub fn instance_export_unchecked(
452 &self,
453 module_addr: ModuleAddr,
454 name: &str,
455 ) -> Result<ExternVal, RuntimeError> {
456 // Fetch the module instance because we store them in the [`Store`]
457 let module_inst = self.modules.get(module_addr);
458
459 // 1. Assert: due to validity of the module instance `moduleinst`, all its export names are different
460
461 // 2. If there exists an `exportinst_i` in `moduleinst.exports` such that name `exportinst_i.name` equals `name`, then:
462 // a. Return the external value `exportinst_i.value`.
463 // 3. Else return `error`.
464 module_inst
465 .exports
466 .get(name)
467 .copied()
468 .ok_or(RuntimeError::UnknownExport)
469 }
470
471 /// Allocates a new function with some host code.
472 ///
473 /// This type of function is also called a host function.
474 ///
475 /// # Panics & Unexpected Behavior
476 /// The specification states that:
477 ///
478 /// > This operation must make sure that the provided host function satisfies the pre-
479 /// > and post-conditions required for a function instance with type `functype`.
480 ///
481 /// Therefore, all "invalid" host functions (e.g. those which return incorrect return values)
482 /// can cause the interpreter to panic or behave unexpectedly.
483 ///
484 /// See: <https://webassembly.github.io/spec/core/exec/modules.html#host-functions>
485 /// See: WebAssembly Specification 2.0 - 7.1.7 - func_alloc
486 ///
487 /// # Safety
488 /// The caller has to guarantee that if the [`Value`]s returned from the
489 /// given host function are references, their addresses came either from the
490 /// host function arguments or from the current [`Store`] object.
491 pub fn func_alloc_unchecked(
492 &mut self,
493 func_type: FuncType,
494 host_func: fn(&mut T, Vec<Value>) -> Result<Vec<Value>, HaltExecutionError>,
495 ) -> FuncAddr {
496 // 1. Pre-condition: `functype` is valid.
497
498 // 2. Let `funcaddr` be the result of allocating a host function in `store` with
499 // function type `functype` and host function code `hostfunc`.
500 // 3. Return the new store paired with `funcaddr`.
501 //
502 // Note: Returning the new store is a noop for us because we mutate the store instead.
503 self.functions.insert(FuncInst::HostFunc(HostFuncInst {
504 function_type: func_type,
505 hostcode: host_func,
506 }))
507 }
508
509 /// Gets the type of a function by its addr.
510 ///
511 /// See: WebAssembly Specification 2.0 - 7.1.7 - func_type
512 ///
513 /// # Safety
514 /// The caller has to guarantee that the [`FuncAddr`] came from the current
515 /// [`Store`] object.
516 pub fn func_type_unchecked(&self, func_addr: FuncAddr) -> FuncType {
517 // 1. Return `S.funcs[a].type`.
518 self.functions.get(func_addr).ty()
519
520 // 2. Post-condition: the returned function type is valid.
521 }
522
523 /// See: WebAssembly Specification 2.0 - 7.1.7 - func_invoke
524 ///
525 /// # Safety
526 /// The caller has to guarantee that the given [`FuncAddr`] or any
527 /// [`FuncAddr`] or [`ExternAddr`](crate::execution::value::ExternAddr) values contained in the parameter values
528 /// came from the current [`Store`] object.
529 pub fn invoke_unchecked(
530 &mut self,
531 func_addr: FuncAddr,
532 params: Vec<Value>,
533 maybe_fuel: Option<u32>,
534 ) -> Result<RunState, RuntimeError> {
535 self.resume_unchecked(self.create_resumable_unchecked(func_addr, params, maybe_fuel)?)
536 }
537
538 /// Allocates a new table with some table type and an initialization value `ref` and returns its table address.
539 ///
540 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_alloc
541 ///
542 /// # Safety
543 /// The caller has to guarantee that any [`FuncAddr`] or [`ExternAddr`](crate::execution::value::ExternAddr)
544 /// values contained in `r#ref` came from the current [`Store`] object.
545 pub fn table_alloc_unchecked(
546 &mut self,
547 table_type: TableType,
548 r#ref: Ref,
549 ) -> Result<TableAddr, RuntimeError> {
550 // Check pre-condition: ref has correct type
551 if table_type.et != r#ref.ty() {
552 return Err(RuntimeError::TableTypeMismatch);
553 }
554
555 // 1. Pre-condition: `tabletype` is valid
556
557 // 2. Let `tableaddr` be the result of allocating a table in `store` with table type `tabletype`
558 // and initialization value `ref`.
559 let table_addr = self.alloc_table(table_type, r#ref);
560
561 // 3. Return the new store paired with `tableaddr`.
562 //
563 // Note: Returning the new store is a noop for us because we mutate the store instead.
564 Ok(table_addr)
565 }
566
567 /// Gets the type of some table by its addr.
568 ///
569 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_type
570 ///
571 /// # Safety
572 /// The caller has to guarantee that the given [`TableAddr`] came from
573 /// the current [`Store`] object.
574 pub fn table_type_unchecked(&self, table_addr: TableAddr) -> TableType {
575 // 1. Return `S.tables[a].type`.
576 self.tables.get(table_addr).ty
577
578 // 2. Post-condition: the returned table type is valid.
579 }
580
581 /// Reads a single reference from a table by its table address and an index into the table.
582 ///
583 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_read
584 ///
585 /// # Safety
586 /// The caller has to guarantee that the given [`TableAddr`] must come from
587 /// the current [`Store`] object.
588 pub fn table_read_unchecked(&self, table_addr: TableAddr, i: u32) -> Result<Ref, RuntimeError> {
589 // Convert `i` to usize for indexing
590 let i = usize::try_from(i).expect("the architecture to be at least 32-bit");
591
592 // 1. Let `ti` be the table instance `store.tables[tableaddr]`
593 let ti = self.tables.get(table_addr);
594
595 // 2. If `i` is larger than or equal to the length of `ti.elem`, then return `error`.
596 // 3. Else, return the reference value `ti.elem[i]`.
597 ti.elem
598 .get(i)
599 .copied()
600 .ok_or(RuntimeError::TableAccessOutOfBounds)
601 }
602
603 /// Writes a single reference into a table by its table address and an index into the table.
604 ///
605 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_write
606 ///
607 /// # Safety
608 /// The caller has to guarantee that the given [`TableAddr`] and any
609 /// [`FuncAddr`] or [`ExternAddr`](crate::execution::value::ExternAddr)
610 /// values contained in the [`Ref`] must come from the current [`Store`]
611 /// object.
612 pub fn table_write_unchecked(
613 &mut self,
614 table_addr: TableAddr,
615 i: u32,
616 r#ref: Ref,
617 ) -> Result<(), RuntimeError> {
618 // Convert `i` to usize for indexing
619 let i = usize::try_from(i).expect("the architecture to be at least 32-bit");
620
621 // 1. Let `ti` be the table instance `store.tables[tableaddr]`.
622 let ti = self.tables.get_mut(table_addr);
623
624 // Check pre-condition: ref has correct type
625 if ti.ty.et != r#ref.ty() {
626 return Err(RuntimeError::TableTypeMismatch);
627 }
628
629 // 2. If `i` is larger than or equal to the length of `ti.elem`, then return `error`.
630 // 3. Replace `ti.elem[i]` with the reference value `ref`
631 *ti.elem
632 .get_mut(i)
633 .ok_or(RuntimeError::TableAccessOutOfBounds)? = r#ref;
634
635 // 4. Return the updated store.
636 //
637 // Note: Returning the new store is a noop for us because we mutate the store instead.
638 Ok(())
639 }
640
641 /// Gets the current size of a table by its table address.
642 ///
643 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_size
644 ///
645 /// # Safety
646 /// The caller has to guarantee that the given [`TableAddr`] must come from
647 /// the current [`Store`] object.
648 pub fn table_size_unchecked(&self, table_addr: TableAddr) -> u32 {
649 // 1. Return the length of `store.tables[tableaddr].elem`.
650 let len = self.tables.get(table_addr).elem.len();
651
652 // In addition we have to convert the length back to a `u32`
653 u32::try_from(len).expect(
654 "the maximum table length to be u32::MAX because thats what the specification allows for indexing",
655 )
656 }
657
658 /// Grows a table referenced by its table address by `n` elements.
659 ///
660 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_grow
661 ///
662 /// # Safety
663 /// The caller has to guarantee that the given [`TableAddr`] and any
664 /// [`FuncAddr`] or [`ExternAddr`](crate::execution::value::ExternAddr)
665 /// values contained in the [`Ref`] must come from the current [`Store`]
666 /// object.
667 pub fn table_grow_unchecked(
668 &mut self,
669 table_addr: TableAddr,
670 n: u32,
671 r#ref: Ref,
672 ) -> Result<(), RuntimeError> {
673 // 1. Try growing the table instance `store.tables[tableaddr] by `n` elements with initialization value `ref`:
674 // a. If it succeeds, return the updated store.
675 // b. Else, return `error`.
676 //
677 // Note: Returning the new store is a noop for us because we mutate the store instead.
678 self.tables.get_mut(table_addr).grow(n, r#ref)
679 }
680
681 /// Allocates a new linear memory and returns its memory address.
682 ///
683 /// See: WebAssembly Specification 2.0 - 7.1.9 - mem_alloc
684 ///
685 /// # A Note About Safety
686 ///
687 /// This method is always safe. However it returns a [`MemAddr`], which can
688 /// only be used with other unchecked methods. Consider using the safe and
689 /// stored [`Store::mem_alloc`] variant instead, which returns a
690 /// [`Stored<MemAddr>`](crate::execution::checked::Stored).
691 pub fn mem_alloc_unchecked(&mut self, mem_type: MemType) -> MemAddr {
692 // 1. Pre-condition: `memtype` is valid.
693
694 // 2. Let `memaddr` be the result of allocating a memory in `store` with memory type `memtype`.
695 // 3. Return the new store paired with `memaddr`.
696 //
697 // Note: Returning the new store is a noop for us because we mutate the store instead.
698 self.alloc_mem(mem_type)
699 }
700
701 /// Gets the memory type of some memory by its memory address
702 ///
703 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_type
704 ///
705 /// # Safety
706 /// The caller has to guarantee that the given [`MemAddr`] came from the
707 /// current [`Store`] object.
708 pub fn mem_type_unchecked(&self, mem_addr: MemAddr) -> MemType {
709 // 1. Return `S.mems[a].type`.
710 self.memories.get(mem_addr).ty
711
712 // 2. Post-condition: the returned memory type is valid.
713 }
714
715 /// Reads a byte from some memory by its memory address and an index into the memory
716 ///
717 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_read
718 ///
719 /// # Safety
720 /// The caller has to guarantee that the given [`MemAddr`] came from the
721 /// current [`Store`] object.
722 pub fn mem_read_unchecked(&self, mem_addr: MemAddr, i: u32) -> Result<u8, RuntimeError> {
723 // Convert the index type
724 let i = usize::try_from(i).expect("the architecture to be at least 32-bit");
725
726 // 1. Let `mi` be the memory instance `store.mems[memaddr]`.
727 let mi = self.memories.get(mem_addr);
728
729 // 2. If `i` is larger than or equal to the length of `mi.data`, then return `error`.
730 // 3. Else, return the byte `mi.data[i]`.
731 mi.mem.load(i)
732 }
733
734 /// Writes a byte into some memory by its memory address and an index into the memory
735 ///
736 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_write
737 ///
738 /// # Safety
739 /// The caller has to guarantee that the given [`MemAddr`] came from the
740 /// current [`Store`] object.
741 pub fn mem_write_unchecked(
742 &self,
743 mem_addr: MemAddr,
744 i: u32,
745 byte: u8,
746 ) -> Result<(), RuntimeError> {
747 // Convert the index type
748 let i = usize::try_from(i).expect("the architecture to be at least 32-bit");
749
750 // 1. Let `mi` be the memory instance `store.mems[memaddr]`.
751 let mi = self.memories.get(mem_addr);
752
753 mi.mem.store(i, byte)
754 }
755
756 /// Gets the size of some memory by its memory address in pages.
757 ///
758 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_size
759 ///
760 /// # Safety
761 /// The caller has to guarantee that the given [`MemAddr`] came from the
762 /// current [`Store`] object.
763 pub fn mem_size_unchecked(&self, mem_addr: MemAddr) -> u32 {
764 // 1. Return the length of `store.mems[memaddr].data` divided by the page size.
765 let length = self.memories.get(mem_addr).size();
766
767 // In addition we have to convert the length back to a `u32`
768 length.try_into().expect(
769 "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.")
770 }
771
772 /// Grows some memory by its memory address by `n` pages.
773 ///
774 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_grow
775 ///
776 /// # Safety
777 /// The caller has to guarantee that the given [`MemAddr`] came from the
778 /// current [`Store`] object.
779 pub fn mem_grow_unchecked(&mut self, mem_addr: MemAddr, n: u32) -> Result<(), RuntimeError> {
780 // 1. Try growing the memory instance `store.mems[memaddr]` by `n` pages:
781 // a. If it succeeds, then return the updated store.
782 // b. Else, return `error`.
783 //
784 // Note: Returning the new store is a noop for us because we mutate the store instead.
785 self.memories.get_mut(mem_addr).grow(n)
786 }
787
788 /// Allocates a new global and returns its global address.
789 ///
790 /// See: WebAssemblySpecification 2.0 - 7.1.10 - global_alloc
791 ///
792 /// # Safety
793 /// The caller has to guarantee that any [`FuncAddr`] or
794 /// [`ExternAddr`](crate::execution::value::ExternAddr) values contained in
795 /// the [`Value`] came from the current [`Store`] object.
796 pub fn global_alloc_unchecked(
797 &mut self,
798 global_type: GlobalType,
799 val: Value,
800 ) -> Result<GlobalAddr, RuntimeError> {
801 // Check pre-condition: val has correct type
802 if global_type.ty != val.to_ty() {
803 return Err(RuntimeError::GlobalTypeMismatch);
804 }
805
806 // 1. Pre-condition: `globaltype` is valid.
807
808 // 2. Let `globaladdr` be the result of allocating a global with global type `globaltype` and initialization value `val`.
809 let global_addr = self.alloc_global(global_type, val);
810
811 // 3. Return the new store paired with `globaladdr`.
812 //
813 // Note: Returning the new store is a noop for us because we mutate the store instead.
814 Ok(global_addr)
815 }
816
817 /// Returns the global type of some global instance by its addr.
818 ///
819 /// See: WebAssembly Specification 2.0 - 7.1.10 - global_type
820 ///
821 /// # Safety
822 /// The caller has to guarantee that the given [`GlobalAddr`] came from the
823 /// current [`Store`] object.
824 pub fn global_type_unchecked(&self, global_addr: GlobalAddr) -> GlobalType {
825 // 1. Return `S.globals[a].type`.
826 self.globals.get(global_addr).ty
827 // 2. Post-condition: the returned global type is valid
828 }
829
830 /// Returns the current value of some global instance by its addr.
831 ///
832 /// See: WebAssembly Specification 2.0 - 7.1.10 - global_read
833 ///
834 /// # Safety
835 /// The caller has to guarantee that the given [`GlobalAddr`] came from the
836 /// current [`Store`] object.
837 pub fn global_read_unchecked(&self, global_addr: GlobalAddr) -> Value {
838 // 1. Let `gi` be the global instance `store.globals[globaladdr].
839 let gi = self.globals.get(global_addr);
840
841 // 2. Return the value `gi.value`.
842 gi.value
843 }
844
845 /// Sets a new value of some global instance by its addr.
846 ///
847 /// # Errors
848 /// - [` RuntimeError::WriteOnImmutableGlobal`]
849 /// - [` RuntimeError::GlobalTypeMismatch`]
850 ///
851 /// See: WebAssembly Specification 2.0 - 7.1.10 - global_write
852 ///
853 /// # Safety
854 /// The caller has to guarantee that the given [`GlobalAddr`] and any
855 /// [`FuncAddr`] or [`ExternAddr`](crate::execution::value::ExternAddr)
856 /// values contained in the [`Value`] came from the current [`Store`]
857 /// object.
858 pub fn global_write_unchecked(
859 &mut self,
860 global_addr: GlobalAddr,
861 val: Value,
862 ) -> Result<(), RuntimeError> {
863 // 1. Let `gi` be the global instance `store.globals[globaladdr]`.
864 let gi = self.globals.get_mut(global_addr);
865
866 // 2. Let `mut t` be the structure of the global type `gi.type`.
867 let r#mut = gi.ty.is_mut;
868 let t = gi.ty.ty;
869
870 // 3. If `mut` is not `var`, then return error.
871 if !r#mut {
872 return Err(RuntimeError::WriteOnImmutableGlobal);
873 }
874
875 // Check invariant:
876 // It is an invariant of the semantics that the value has a type equal to the value type of `globaltype`.
877 // See: WebAssembly Specification 2.0 - 4.2.9
878 if t != val.to_ty() {
879 return Err(RuntimeError::GlobalTypeMismatch);
880 }
881
882 // 4. Replace `gi.value` with the value `val`.
883 gi.value = val;
884
885 // 5. Return the updated store.
886 // This is a noop for us, as our store `self` is mutable.
887
888 Ok(())
889 }
890
891 /// roughly matches <https://webassembly.github.io/spec/core/exec/modules.html#functions> with the addition of sidetable pointer to the input signature
892 ///
893 /// # Safety
894 /// The caller has to guarantee that the given [`ModuleAddr`] came from the
895 /// current [`Store`] object.
896 // TODO refactor the type of func
897 fn alloc_func(&mut self, func: (TypeIdx, (Span, usize)), module_addr: ModuleAddr) -> FuncAddr {
898 let (ty, (span, stp)) = func;
899
900 // TODO rewrite this huge chunk of parsing after generic way to re-parse(?) structs lands
901 let mut wasm_reader = WasmReader::new(self.modules.get(module_addr).wasm_bytecode);
902 wasm_reader.move_start_to(span).unwrap_validated();
903
904 let (locals, bytes_read) = wasm_reader
905 .measure_num_read_bytes(crate::code::read_declared_locals)
906 .unwrap_validated();
907
908 let code_expr = wasm_reader
909 .make_span(span.len() - bytes_read)
910 .unwrap_validated();
911
912 // core of the method below
913
914 // validation guarantees func_ty_idx exists within module_inst.types
915 // TODO fix clone
916 let func_inst = FuncInst::WasmFunc(WasmFuncInst {
917 function_type: self.modules.get(module_addr).types[ty].clone(),
918 _ty: ty,
919 locals,
920 code_expr,
921 stp,
922 module_addr,
923 });
924 self.functions.insert(func_inst)
925 }
926
927 /// <https://webassembly.github.io/spec/core/exec/modules.html#tables>
928 ///
929 /// # Safety
930 /// The caller has to guarantee that any [`FuncAddr`] or
931 /// [`ExternAddr`](crate::execution::value::ExternAddr) values contained in
932 /// the [`Ref`] came from the current [`Store`] object.
933 fn alloc_table(&mut self, table_type: TableType, reff: Ref) -> TableAddr {
934 let table_inst = TableInst {
935 ty: table_type,
936 elem: vec![reff; table_type.lim.min as usize],
937 };
938
939 self.tables.insert(table_inst)
940 }
941
942 /// <https://webassembly.github.io/spec/core/exec/modules.html#memories>
943 fn alloc_mem(&mut self, mem_type: MemType) -> MemAddr {
944 let mem_inst = MemInst {
945 ty: mem_type,
946 mem: LinearMemory::new_with_initial_pages(
947 mem_type.limits.min.try_into().unwrap_validated(),
948 ),
949 };
950
951 self.memories.insert(mem_inst)
952 }
953
954 /// <https://webassembly.github.io/spec/core/exec/modules.html#globals>
955 ///
956 /// # Safety
957 /// The caller has to guarantee that any [`FuncAddr`] or
958 /// [`ExternAddr`](crate::execution::value::ExternAddr) values contained in
959 /// the [`Value`] came from the current [`Store`] object.
960 fn alloc_global(&mut self, global_type: GlobalType, val: Value) -> GlobalAddr {
961 let global_inst = GlobalInst {
962 ty: global_type,
963 value: val,
964 };
965
966 self.globals.insert(global_inst)
967 }
968
969 /// <https://webassembly.github.io/spec/core/exec/modules.html#element-segments>
970 ///
971 /// # Safety
972 /// The caller has to guarantee that any [`FuncAddr`] or
973 /// [`ExternAddr`](crate::execution::value::ExternAddr) values contained in
974 /// the [`Ref`]s came from the current [`Store`] object.
975 fn alloc_elem(&mut self, ref_type: RefType, refs: Vec<Ref>) -> ElemAddr {
976 let elem_inst = ElemInst {
977 _ty: ref_type,
978 references: refs,
979 };
980
981 self.elements.insert(elem_inst)
982 }
983
984 /// <https://webassembly.github.io/spec/core/exec/modules.html#data-segments>
985 fn alloc_data(&mut self, bytes: &[u8]) -> DataAddr {
986 let data_inst = DataInst {
987 data: Vec::from(bytes),
988 };
989
990 self.data.insert(data_inst)
991 }
992
993 /// Creates a new resumable, which when resumed for the first time invokes the function `function_ref` is associated
994 /// to, with the arguments `params`. The newly created resumable initially stores `fuel` units of fuel. Returns a
995 /// `[ResumableRef]` associated to the newly created resumable on success.
996 ///
997 /// # Safety
998 /// The caller has to guarantee that the [`FuncAddr`] and any [`FuncAddr`]
999 /// or [`ExternAddr`](crate::execution::value::ExternAddr) values contained
1000 /// in the parameter values came from the current [`Store`] object.
1001 pub fn create_resumable_unchecked(
1002 &self,
1003 func_addr: FuncAddr,
1004 params: Vec<Value>,
1005 maybe_fuel: Option<u32>,
1006 ) -> Result<ResumableRef, RuntimeError> {
1007 let func_inst = self.functions.get(func_addr);
1008
1009 let func_ty = func_inst.ty();
1010
1011 // Verify that the given parameters match the function parameters
1012 let param_types = params.iter().map(|v| v.to_ty()).collect::<Vec<_>>();
1013
1014 if func_ty.params.valtypes != param_types {
1015 trace!(
1016 "Func param types len: {}; Given args len: {}",
1017 func_ty.params.valtypes.len(),
1018 param_types.len()
1019 );
1020 return Err(RuntimeError::FunctionInvocationSignatureMismatch);
1021 }
1022
1023 Ok(ResumableRef::Fresh(FreshResumableRef {
1024 func_addr,
1025 params,
1026 maybe_fuel,
1027 }))
1028 }
1029
1030 /// resumes the resumable associated to `resumable_ref`. Returns a [`RunState`] associated to this resumable if the
1031 /// resumable ran out of fuel or completely executed.
1032 ///
1033 /// # Safety
1034 /// The caller has to guarantee that the [`ResumableRef`] came from the
1035 /// current [`Store`] object.
1036 pub fn resume_unchecked(
1037 &mut self,
1038 mut resumable_ref: ResumableRef,
1039 ) -> Result<RunState, RuntimeError> {
1040 match resumable_ref {
1041 ResumableRef::Fresh(FreshResumableRef {
1042 func_addr,
1043 params,
1044 maybe_fuel,
1045 }) => {
1046 let func_inst = self.functions.get(func_addr);
1047
1048 match func_inst {
1049 FuncInst::HostFunc(host_func_inst) => {
1050 let returns = (host_func_inst.hostcode)(&mut self.user_data, params);
1051
1052 debug!("Successfully invoked function");
1053
1054 let returns = returns.map_err(|HaltExecutionError| {
1055 RuntimeError::HostFunctionHaltedExecution
1056 })?;
1057
1058 // Verify that the return parameters match the host function parameters
1059 // since we have no validation guarantees for host functions
1060
1061 let return_types = returns.iter().map(|v| v.to_ty()).collect::<Vec<_>>();
1062 if host_func_inst.function_type.returns.valtypes != return_types {
1063 trace!(
1064 "Func return types len: {}; returned args len: {}",
1065 host_func_inst.function_type.returns.valtypes.len(),
1066 return_types.len()
1067 );
1068 return Err(RuntimeError::HostFunctionSignatureMismatch);
1069 }
1070
1071 Ok(RunState::Finished {
1072 values: returns,
1073 maybe_remaining_fuel: maybe_fuel,
1074 })
1075 }
1076 FuncInst::WasmFunc(wasm_func_inst) => {
1077 // Prepare a new stack with the locals for the entry function
1078 let mut stack = Stack::new_with_values(params);
1079
1080 stack.push_call_frame::<T>(
1081 FuncAddr::INVALID, // TODO using a default value like this is dangerous
1082 &wasm_func_inst.function_type,
1083 &wasm_func_inst.locals,
1084 usize::MAX,
1085 usize::MAX,
1086 )?;
1087
1088 let mut resumable = Resumable {
1089 current_func_addr: func_addr,
1090 stack,
1091 pc: wasm_func_inst.code_expr.from,
1092 stp: wasm_func_inst.stp,
1093 maybe_fuel,
1094 };
1095
1096 // Run the interpreter
1097 let result = interpreter_loop::run(&mut resumable, self)?;
1098
1099 match result {
1100 None => {
1101 debug!("Successfully invoked function");
1102 let maybe_remaining_fuel = resumable.maybe_fuel;
1103 let values = resumable.stack.into_values();
1104 Ok(RunState::Finished {
1105 values,
1106 maybe_remaining_fuel,
1107 })
1108 }
1109 Some(required_fuel) => {
1110 debug!("Successfully invoked function, but ran out of fuel");
1111 Ok(RunState::Resumable {
1112 resumable_ref: ResumableRef::Invoked(
1113 self.dormitory.insert(resumable),
1114 ),
1115 required_fuel,
1116 })
1117 }
1118 }
1119 }
1120 }
1121 }
1122 ResumableRef::Invoked(InvokedResumableRef {
1123 dormitory: ref mut dormitory_weak,
1124 ref key,
1125 }) => {
1126 // Resuming requires `self`'s dormitory to still be alive
1127 let Some(dormitory) = dormitory_weak.upgrade() else {
1128 return Err(RuntimeError::ResumableNotFound);
1129 };
1130
1131 // Check the given `RuntimeInstance` is the same one used to create `self`
1132 if !Arc::ptr_eq(&dormitory, &self.dormitory.0) {
1133 return Err(RuntimeError::ResumableNotFound);
1134 }
1135
1136 // Obtain a write lock to the `Dormitory`
1137 let mut dormitory = dormitory.write();
1138
1139 // TODO We might want to remove the `Resumable` here already and later reinsert it.
1140 // This would prevent holding the lock across the interpreter loop.
1141 let resumable = dormitory
1142 .get_mut(key)
1143 .expect("the key to always be valid as self was not dropped yet");
1144
1145 // Resume execution
1146 let result = interpreter_loop::run(resumable, self)?;
1147
1148 match result {
1149 None => {
1150 let resumable = dormitory.remove(key)
1151 .expect("that the resumable could not have been removed already, because then this self could not exist");
1152
1153 // Take the `Weak` pointing to the dormitory out of `self` and replace it with a default `Weak`.
1154 // This causes the `Drop` impl of `self` to directly quit preventing it from unnecessarily locking the dormitory.
1155 let _dormitory = mem::take(dormitory_weak);
1156 let maybe_remaining_fuel = resumable.maybe_fuel;
1157 let values = resumable.stack.into_values();
1158 Ok(RunState::Finished {
1159 values,
1160 maybe_remaining_fuel,
1161 })
1162 }
1163 Some(required_fuel) => Ok(RunState::Resumable {
1164 resumable_ref,
1165 required_fuel,
1166 }),
1167 }
1168 }
1169 }
1170 }
1171
1172 /// Calls its argument `f` with a mutable reference of the fuel of the
1173 /// respective [`ResumableRef`].
1174 ///
1175 /// Fuel is stored as an [`Option<u32>`], where `None` means that fuel is
1176 /// disabled and `Some(x)` means that `x` units of fuel is left. A
1177 /// ubiquitious use of this method would be using `f` to read or mutate the
1178 /// current fuel amount of the respective [`ResumableRef`].
1179 ///
1180 /// # Example
1181 ///
1182 /// ```
1183 /// use wasm::{resumable::RunState, validate, Store};
1184 /// // a simple module with a single function looping forever
1185 /// let wasm = [ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
1186 /// 0x01, 0x04, 0x01, 0x60, 0x00, 0x00, 0x03, 0x02,
1187 /// 0x01, 0x00, 0x07, 0x09, 0x01, 0x05, 0x6c, 0x6f,
1188 /// 0x6f, 0x70, 0x73, 0x00, 0x00, 0x0a, 0x09, 0x01,
1189 /// 0x07, 0x00, 0x03, 0x40, 0x0c, 0x00, 0x0b, 0x0b ];
1190 /// let validation_info = validate(&wasm).unwrap();
1191 ///
1192 /// let mut store = Store::new(());
1193 /// let module = store.module_instantiate_unchecked(&validation_info, Vec::new(), None).unwrap().module_addr;
1194 /// let func_addr = store.instance_export_unchecked(module, "loops").unwrap().as_func().unwrap();
1195 /// let mut resumable_ref = store.create_resumable_unchecked(func_addr, Vec::new(), Some(0)).unwrap();
1196 /// store.access_fuel_mut_unchecked(&mut resumable_ref, |x| { assert_eq!(*x, Some(0)); *x = None; }).unwrap();
1197 /// ```
1198 ///
1199 /// # Safety
1200 /// The caller has to guarantee that the [`ResumableRef`] came from the
1201 /// current [`Store`] object.
1202 pub fn access_fuel_mut_unchecked<R>(
1203 &mut self,
1204 resumable_ref: &mut ResumableRef,
1205 f: impl FnOnce(&mut Option<u32>) -> R,
1206 ) -> Result<R, RuntimeError> {
1207 match resumable_ref {
1208 ResumableRef::Fresh(FreshResumableRef { maybe_fuel, .. }) => Ok(f(maybe_fuel)),
1209 ResumableRef::Invoked(resumable_ref) => {
1210 // Resuming requires `self`'s dormitory to still be alive
1211 let Some(dormitory) = resumable_ref.dormitory.upgrade() else {
1212 return Err(RuntimeError::ResumableNotFound);
1213 };
1214
1215 // Check the given `RuntimeInstance` is the same one used to create `self`
1216 if !Arc::ptr_eq(&dormitory, &self.dormitory.0) {
1217 return Err(RuntimeError::ResumableNotFound);
1218 }
1219
1220 let mut dormitory = dormitory.write();
1221
1222 let resumable = dormitory
1223 .get_mut(&resumable_ref.key)
1224 .expect("the key to always be valid as self was not dropped yet");
1225
1226 Ok(f(&mut resumable.maybe_fuel))
1227 }
1228 }
1229 }
1230
1231 /// Allocates a new function with a statically known type signature with some host code.
1232 ///
1233 /// This function is simply syntactic sugar for calling
1234 /// [`Store::func_alloc_unchecked`] with statically know types.
1235 ///
1236 /// # Panics & Unexpected Behavior
1237 /// Same as [`Store::func_alloc_unchecked`].
1238 pub fn func_alloc_typed_unchecked<Params: InteropValueList, Returns: InteropValueList>(
1239 &mut self,
1240 host_func: fn(&mut T, Vec<Value>) -> Result<Vec<Value>, HaltExecutionError>,
1241 ) -> FuncAddr {
1242 let func_type = FuncType {
1243 params: ResultType {
1244 valtypes: Vec::from(Params::TYS),
1245 },
1246 returns: ResultType {
1247 valtypes: Vec::from(Returns::TYS),
1248 },
1249 };
1250 self.func_alloc_unchecked(func_type, host_func)
1251 }
1252
1253 /// Invokes a function without fuel.
1254 ///
1255 /// This function is simply syntactic sugar for calling
1256 /// [`Store::invoke_unchecked`] without any fuel and destructuring the
1257 /// resulting [`RunState`].
1258 ///
1259 /// # Safety
1260 /// The caller has to guarantee that the given [`FuncAddr`] or any
1261 /// [`FuncAddr`] or [`ExternAddr`](crate::execution::value::ExternAddr)
1262 /// values contained in the parameter values came from the current [`Store`]
1263 /// object.
1264 pub fn invoke_without_fuel_unchecked(
1265 &mut self,
1266 function: FuncAddr,
1267 params: Vec<Value>,
1268 ) -> Result<Vec<Value>, RuntimeError> {
1269 self.invoke_unchecked(function, params, None)
1270 .map(|run_state| match run_state {
1271 RunState::Finished {
1272 values,
1273 maybe_remaining_fuel: _,
1274 } => values,
1275 RunState::Resumable { .. } => unreachable!("fuel is disabled"),
1276 })
1277 }
1278
1279 /// Invokes a function with a statically known type signature without fuel.
1280 ///
1281 /// This function is simply syntactic sugar for calling
1282 /// [`Store::invoke_unchecked`] without any fuel and destructuring the
1283 /// resulting [`RunState`] with statically known types.
1284 ///
1285 /// # Safety
1286 /// The caller has to guarantee that the given [`FuncAddr`] or any
1287 /// [`FuncAddr`] or [`ExternAddr`](crate::execution::value::ExternAddr)
1288 /// values contained in the parameter values came from the current [`Store`]
1289 /// object.
1290 pub fn invoke_typed_without_fuel_unchecked<
1291 Params: InteropValueList,
1292 Returns: InteropValueList,
1293 >(
1294 &mut self,
1295 function: FuncAddr,
1296 params: Params,
1297 ) -> Result<Returns, RuntimeError> {
1298 self.invoke_without_fuel_unchecked(function, params.into_values())
1299 .and_then(|values| {
1300 Returns::try_from_values(values.into_iter()).map_err(|ValueTypeMismatchError| {
1301 RuntimeError::FunctionInvocationSignatureMismatch
1302 })
1303 })
1304 }
1305}
1306
1307/// A unique identifier for a specific [`Store`]
1308#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1309pub struct StoreId(u64);
1310
1311impl StoreId {
1312 /// Creates a new unique [`StoreId`]
1313 #[allow(clippy::new_without_default)] // reason = "StoreId::default() might be misunderstood to be some
1314 // default value. However, a default value does not exist in that
1315 // sense because every newly created StoreId must be unique. Also
1316 // we don't want to allow the user to create new instances of
1317 // this object."
1318 pub(crate) fn new() -> Self {
1319 static NEXT_STORE_ID: AtomicU64 = AtomicU64::new(0);
1320
1321 // TODO find a fix for the default wrapping behavior of `fetch_add`.
1322 // Maybe we could return a `RuntimeError` here?
1323 Self(NEXT_STORE_ID.fetch_add(1, Ordering::SeqCst))
1324 }
1325}
1326
1327/// A marker error for host functions to return, in case they want execution to be halted.
1328pub struct HaltExecutionError;
1329
1330///<https://webassembly.github.io/spec/core/exec/runtime.html#external-values>
1331#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1332pub enum ExternVal {
1333 Func(FuncAddr),
1334 Table(TableAddr),
1335 Mem(MemAddr),
1336 Global(GlobalAddr),
1337}
1338
1339impl ExternVal {
1340 /// returns the external type of `self` according to typing relation,
1341 /// taking `store` as context S.
1342 ///
1343 /// # Safety
1344 /// The caller has to guarantee that `self` came from the same [`Store`] which
1345 /// is passed now as a reference.
1346 pub fn extern_type<T: Config>(&self, store: &Store<T>) -> ExternType {
1347 match self {
1348 // TODO: fix ugly clone in function types
1349 ExternVal::Func(func_addr) => ExternType::Func(store.functions.get(*func_addr).ty()),
1350 ExternVal::Table(table_addr) => ExternType::Table(store.tables.get(*table_addr).ty),
1351 ExternVal::Mem(mem_addr) => ExternType::Mem(store.memories.get(*mem_addr).ty),
1352 ExternVal::Global(global_addr) => {
1353 ExternType::Global(store.globals.get(*global_addr).ty)
1354 }
1355 }
1356 }
1357}
1358
1359impl ExternVal {
1360 pub fn as_func(self) -> Option<FuncAddr> {
1361 match self {
1362 ExternVal::Func(func_addr) => Some(func_addr),
1363 _ => None,
1364 }
1365 }
1366
1367 pub fn as_table(self) -> Option<TableAddr> {
1368 match self {
1369 ExternVal::Table(table_addr) => Some(table_addr),
1370 _ => None,
1371 }
1372 }
1373
1374 pub fn as_mem(self) -> Option<MemAddr> {
1375 match self {
1376 ExternVal::Mem(mem_addr) => Some(mem_addr),
1377 _ => None,
1378 }
1379 }
1380
1381 pub fn as_global(self) -> Option<GlobalAddr> {
1382 match self {
1383 ExternVal::Global(global_addr) => Some(global_addr),
1384 _ => None,
1385 }
1386 }
1387}
1388
1389/// common convention functions defined for lists of ExternVals, ExternTypes, Exports
1390/// <https://webassembly.github.io/spec/core/exec/runtime.html#conventions>
1391/// <https://webassembly.github.io/spec/core/syntax/types.html#id3>
1392/// <https://webassembly.github.io/spec/core/syntax/modules.html?highlight=convention#id1>
1393// TODO implement this trait for ExternType lists Export lists
1394pub trait ExternFilterable {
1395 fn funcs(self) -> impl Iterator<Item = FuncAddr>;
1396 fn tables(self) -> impl Iterator<Item = TableAddr>;
1397 fn mems(self) -> impl Iterator<Item = MemAddr>;
1398 fn globals(self) -> impl Iterator<Item = GlobalAddr>;
1399}
1400
1401impl<'a, I> ExternFilterable for I
1402where
1403 I: Iterator<Item = &'a ExternVal>,
1404{
1405 fn funcs(self) -> impl Iterator<Item = FuncAddr> {
1406 self.filter_map(|extern_val| extern_val.as_func())
1407 }
1408
1409 fn tables(self) -> impl Iterator<Item = TableAddr> {
1410 self.filter_map(|extern_val| extern_val.as_table())
1411 }
1412
1413 fn mems(self) -> impl Iterator<Item = MemAddr> {
1414 self.filter_map(|extern_val| extern_val.as_mem())
1415 }
1416
1417 fn globals(self) -> impl Iterator<Item = GlobalAddr> {
1418 self.filter_map(|extern_val| extern_val.as_global())
1419 }
1420}
1421
1422/// Represents a successful, possibly fueled instantiation of a module.
1423pub struct InstantiationOutcome {
1424 /// contains the store address of the module that has successfully instantiated.
1425 pub module_addr: ModuleAddr,
1426 /// contains `Some(remaining_fuel)` if instantiation was fuel-metered and `None` otherwise.
1427 pub maybe_remaining_fuel: Option<u32>,
1428}