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