wasm/execution/store/mod.rs
1use core::mem;
2
3use crate::addrs::{
4 AddrVec, DataAddr, ElemAddr, FuncAddr, GlobalAddr, MemAddr, ModuleAddr, TableAddr,
5};
6use crate::config::Config;
7use crate::core::indices::TypeIdx;
8use crate::core::reader::span::Span;
9use crate::core::reader::types::data::{DataModeActive, DataSegment};
10use crate::core::reader::types::element::{ActiveElem, ElemItems, ElemMode, ElemType};
11use crate::core::reader::types::export::{Export, ExportDesc};
12use crate::core::reader::types::global::{Global, GlobalType};
13use crate::core::reader::types::import::Import;
14use crate::core::reader::types::{ExternType, FuncType, ImportSubTypeRelation, MemType, TableType};
15use crate::core::reader::WasmReader;
16use crate::execution::interpreter_loop::{self, memory_init, table_init};
17use crate::execution::value::{Ref, Value};
18use crate::execution::{run_const_span, Stack};
19use crate::registry::Registry;
20use crate::resumable::{
21 Dormitory, FreshResumableRef, InvokedResumableRef, Resumable, ResumableRef, RunState,
22};
23use crate::{RefType, RuntimeError, ValidationInfo};
24use alloc::borrow::ToOwned;
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::interpreter_loop::{data_drop, elem_drop};
37use super::UnwrapValidatedExt;
38
39pub mod addrs;
40pub(crate) mod instances;
41pub(crate) mod linear_memory;
42
43/// The store represents all global state that can be manipulated by WebAssembly programs. It
44/// consists of the runtime representation of all instances of functions, tables, memories, and
45/// globals, element segments, and data segments that have been allocated during the life time of
46/// the abstract machine.
47/// <https://webassembly.github.io/spec/core/exec/runtime.html#store>
48pub struct Store<'b, T: Config> {
49 pub(crate) functions: AddrVec<FuncAddr, FuncInst<T>>,
50 pub(crate) tables: AddrVec<TableAddr, TableInst>,
51 pub(crate) memories: AddrVec<MemAddr, MemInst>,
52 pub(crate) globals: AddrVec<GlobalAddr, GlobalInst>,
53 pub(crate) elements: AddrVec<ElemAddr, ElemInst>,
54 pub(crate) data: AddrVec<DataAddr, DataInst>,
55
56 // fields outside of the spec but are convenient are below
57 /// An address space of modules instantiated within the context of this [`Store`].
58 ///
59 /// Although the WebAssembly Specification 2.0 does not specify module instances
60 /// to be part of the [`Store`], in reality they can be managed very similar to
61 /// other instance types. Therefore, we extend the [`Store`] by a module address
62 /// space along with a `ModuleAddr` index type.
63 pub(crate) modules: AddrVec<ModuleAddr, ModuleInst<'b>>,
64
65 // all visible exports and entities added by hand or module instantiation by the interpreter
66 // currently, all of the exports of an instantiated module is made visible (this is outside of spec)
67 pub registry: Registry,
68 pub user_data: T,
69
70 // data structure holding all resumable objects that belong to this store
71 pub(crate) dormitory: Dormitory,
72}
73
74impl<'b, T: Config> Store<'b, T> {
75 /// Creates a new empty store with some user data
76 ///
77 /// See: WebAssembly Specification 2.0 - 7.1.4 - store_init
78 pub fn new(user_data: T) -> Self {
79 // 1. Return the empty store.
80 // For us the store is empty except for the user data, which we do not have control over.
81 Self {
82 functions: AddrVec::default(),
83 tables: AddrVec::default(),
84 memories: AddrVec::default(),
85 globals: AddrVec::default(),
86 elements: AddrVec::default(),
87 data: AddrVec::default(),
88 modules: AddrVec::default(),
89 registry: Registry::default(),
90 dormitory: Dormitory::default(),
91 user_data,
92 }
93 }
94
95 /// instantiates a validated module with `validation_info` as validation evidence with name `name`
96 /// with the steps in <https://webassembly.github.io/spec/core/exec/modules.html#instantiation>
97 /// this method roughly matches the suggested embedder function`module_instantiate`
98 /// <https://webassembly.github.io/spec/core/appendix/embedding.html#modules>
99 /// except external values for module instantiation are retrieved from `self`.
100 /// Returns the module addr of the new module instance
101 pub fn add_module(
102 &mut self,
103 name: &str,
104 validation_info: &ValidationInfo<'b>,
105 maybe_fuel: Option<u32>,
106 ) -> Result<ModuleAddr, RuntimeError> {
107 // instantiation step -1: collect extern_vals, this section basically acts as a linker between modules
108 // best attempt at trying to match the spec implementation in terms of errors
109 debug!("adding module with name {:?}", name);
110 let mut extern_vals = Vec::new();
111
112 for Import {
113 module_name: exporting_module_name,
114 name: import_name,
115 desc: import_desc,
116 } in &validation_info.imports
117 {
118 trace!(
119 "trying to import from exporting module instance named {:?}, the entity with name {:?} with desc: {:?}",
120 exporting_module_name,
121 import_name,
122 import_desc
123 );
124 let import_extern_type = import_desc.extern_type(validation_info);
125 let export_extern_val_candidate = *self.registry.lookup(
126 exporting_module_name.clone().into(),
127 import_name.clone().into(),
128 )?;
129 trace!("export candidate found: {:?}", export_extern_val_candidate);
130 if !export_extern_val_candidate
131 .extern_type(self)
132 .is_subtype_of(&import_extern_type)
133 {
134 return Err(RuntimeError::InvalidImportType);
135 }
136 trace!("import and export matches. Adding to externvals");
137 extern_vals.push(export_extern_val_candidate)
138 }
139
140 // instantiation: step 5
141 // 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.
142 // therefore I am mimicking the reference interpreter code here, I will allocate functions in the store in this step instead of step 11.
143 // https://github.com/WebAssembly/spec/blob/8d6792e3d6709e8d3e90828f9c8468253287f7ed/interpreter/exec/eval.ml#L789
144 let module_inst = ModuleInst {
145 types: validation_info.types.clone(),
146 func_addrs: extern_vals.iter().funcs().collect(),
147 table_addrs: Vec::new(),
148 mem_addrs: Vec::new(),
149 global_addrs: extern_vals.iter().globals().collect(),
150 elem_addrs: Vec::new(),
151 data_addrs: Vec::new(),
152 exports: BTreeMap::new(),
153 wasm_bytecode: validation_info.wasm,
154 sidetable: validation_info.sidetable.clone(),
155 };
156 let module_addr = self.modules.insert(module_inst);
157
158 // TODO rewrite this part
159 // <https://webassembly.github.io/spec/core/exec/modules.html#functions>
160 let func_addrs: Vec<FuncAddr> = validation_info
161 .functions
162 .iter()
163 .zip(validation_info.func_blocks_stps.iter())
164 .map(|(ty_idx, (span, stp))| self.alloc_func((*ty_idx, (*span, *stp)), module_addr))
165 .collect();
166
167 self.modules
168 .get_mut(module_addr)
169 .func_addrs
170 .extend(func_addrs);
171
172 // instantiation: this roughly matches step 6,7,8
173 // validation guarantees these will evaluate without errors.
174 let maybe_global_init_vals: Result<Vec<Value>, _> = validation_info
175 .globals
176 .iter()
177 .map(|global| {
178 run_const_span(validation_info.wasm, &global.init_expr, module_addr, self)
179 .transpose()
180 .unwrap_validated()
181 })
182 .collect();
183 let global_init_vals = maybe_global_init_vals?;
184
185 // instantiation: this roughly matches step 9,10
186
187 let mut element_init_ref_lists: Vec<Vec<Ref>> =
188 Vec::with_capacity(validation_info.elements.len());
189
190 for elem in &validation_info.elements {
191 let mut new_list = Vec::new();
192 match &elem.init {
193 // shortcut of evaluation of "ref.func <func_idx>; end;"
194 // validation guarantees corresponding func_idx's existence
195 ElemItems::RefFuncs(ref_funcs) => {
196 for func_idx in ref_funcs {
197 let func_addr = *self
198 .modules
199 .get(module_addr)
200 .func_addrs
201 .get(*func_idx as usize)
202 .unwrap_validated();
203
204 new_list.push(Ref::Func(func_addr));
205 }
206 }
207 ElemItems::Exprs(_, exprs) => {
208 for expr in exprs {
209 new_list.push(
210 run_const_span(validation_info.wasm, expr, module_addr, self)?
211 .unwrap_validated() // there is a return value
212 .try_into()
213 .unwrap_validated(), // return value has the correct type
214 )
215 }
216 }
217 }
218 element_init_ref_lists.push(new_list);
219 }
220
221 // instantiation: step 11 - module allocation (except function allocation - which was made in step 5)
222 // https://webassembly.github.io/spec/core/exec/modules.html#alloc-module
223
224 // allocation: begin
225
226 // allocation: step 1
227 let module = validation_info;
228
229 let extern_vals = extern_vals;
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 // register module exports, this is outside of the spec
319 self.registry
320 .register_module(name.to_owned().into(), module_inst)?;
321
322 // instantiation step 11 end: module_inst properly allocated after this point.
323
324 // instantiation: step 12-15
325 // TODO have to stray away from the spec a bit since our codebase does not lend itself well to freely executing instructions by themselves
326 for (
327 i,
328 ElemType {
329 init: elem_items,
330 mode,
331 },
332 ) in validation_info.elements.iter().enumerate()
333 {
334 match mode {
335 ElemMode::Active(ActiveElem {
336 table_idx: table_idx_i,
337 init_expr: einstr_i,
338 }) => {
339 let n = elem_items.len() as i32;
340 // equivalent to init.len() in spec
341 // instantiation step 14:
342 // TODO (for now, we are doing hopefully what is equivalent to it)
343 // execute:
344 // einstr_i
345 // i32.const 0
346 // i32.const n
347 // table.init table_idx_i i
348 // elem.drop i
349 let d: i32 = run_const_span(validation_info.wasm, einstr_i, module_addr, self)?
350 .unwrap_validated() // there is a return value
351 .try_into()
352 .unwrap_validated(); // return value has correct type
353
354 let s = 0;
355 table_init(
356 &self.modules,
357 &mut self.tables,
358 &self.elements,
359 module_addr,
360 i,
361 *table_idx_i as usize,
362 n,
363 s,
364 d,
365 )?;
366 elem_drop(&self.modules, &mut self.elements, module_addr, i)?;
367 }
368 ElemMode::Declarative => {
369 // instantiation step 15:
370 // TODO (for now, we are doing hopefully what is equivalent to it)
371 // execute:
372 // elem.drop i
373 elem_drop(&self.modules, &mut self.elements, module_addr, i)?;
374 }
375 ElemMode::Passive => (),
376 }
377 }
378
379 // instantiation: step 16
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 (i, DataSegment { init, mode }) in validation_info.data.iter().enumerate() {
382 match mode {
383 crate::core::reader::types::data::DataMode::Active(DataModeActive {
384 memory_idx,
385 offset: dinstr_i,
386 }) => {
387 let n = init.len() as i32;
388 // assert: mem_idx is 0
389 if *memory_idx != 0 {
390 // TODO fix error
391 return Err(RuntimeError::MoreThanOneMemory);
392 }
393
394 // TODO (for now, we are doing hopefully what is equivalent to it)
395 // execute:
396 // dinstr_i
397 // i32.const 0
398 // i32.const n
399 // memory.init i
400 // data.drop i
401 let d: i32 = run_const_span(validation_info.wasm, dinstr_i, module_addr, self)?
402 .unwrap_validated() // there is a return value
403 .try_into()
404 .unwrap_validated(); // return value has the correct type
405
406 let s = 0;
407 memory_init(
408 &self.modules,
409 &mut self.memories,
410 &self.data,
411 module_addr,
412 i,
413 0,
414 n,
415 s,
416 d,
417 )?;
418 data_drop(&self.modules, &mut self.data, module_addr, i)?;
419 }
420 crate::core::reader::types::data::DataMode::Passive => (),
421 }
422 }
423
424 // instantiation: step 17
425 if let Some(func_idx) = validation_info.start {
426 // TODO (for now, we are doing hopefully what is equivalent to it)
427 // execute
428 // call func_ifx
429 let func_addr = self.modules.get(module_addr).func_addrs[func_idx];
430 self.invoke(func_addr, Vec::new(), maybe_fuel)?;
431 };
432
433 Ok(module_addr)
434 }
435
436 /// Gets an export of a specific module instance by its name
437 ///
438 /// See: WebAssembly Specification 2.0 - 7.1.6 - instance_export
439 pub fn instance_export(
440 &self,
441 module_addr: ModuleAddr,
442 name: &str,
443 ) -> Result<ExternVal, RuntimeError> {
444 // Fetch the module instance because we store them in the [`Store`]
445 let module_inst = self.modules.get(module_addr);
446
447 // 1. Assert: due to validity of the module instance `moduleinst`, all its export names are different
448
449 // 2. If there exists an `exportinst_i` in `moduleinst.exports` such that name `exportinst_i.name` equals `name`, then:
450 // a. Return the external value `exportinst_i.value`.
451 // 3. Else return `error`.
452 module_inst
453 .exports
454 .get(name)
455 .copied()
456 .ok_or(RuntimeError::UnknownExport)
457 }
458
459 /// Allocates a new function with some host code.
460 ///
461 /// This type of function is also called a host function.
462 ///
463 /// # Panics & Unexpected Behavior
464 /// The specification states that:
465 ///
466 /// > This operation must make sure that the provided host function satisfies the pre-
467 /// > and post-conditions required for a function instance with type `functype`.
468 ///
469 /// Therefore, all "invalid" host functions (e.g. those which return incorrect return values)
470 /// can cause the interpreter to panic or behave unexpectedly.
471 ///
472 /// See: <https://webassembly.github.io/spec/core/exec/modules.html#host-functions>
473 /// See: WebAssembly Specification 2.0 - 7.1.7 - func_alloc
474 pub fn func_alloc(
475 &mut self,
476 func_type: FuncType,
477 host_func: fn(&mut T, Vec<Value>) -> Result<Vec<Value>, HaltExecutionError>,
478 ) -> FuncAddr {
479 // 1. Pre-condition: `functype` is valid.
480
481 // 2. Let `funcaddr` be the result of allocating a host function in `store` with
482 // function type `functype` and host function code `hostfunc`.
483 // 3. Return the new store paired with `funcaddr`.
484 //
485 // Note: Returning the new store is a noop for us because we mutate the store instead.
486 self.functions.insert(FuncInst::HostFunc(HostFuncInst {
487 function_type: func_type,
488 hostcode: host_func,
489 }))
490 }
491
492 /// Gets the type of a function by its addr.
493 ///
494 /// See: WebAssembly Specification 2.0 - 7.1.7 - func_type
495 pub fn func_type(&self, func_addr: FuncAddr) -> FuncType {
496 // 1. Return `S.funcs[a].type`.
497 self.functions.get(func_addr).ty()
498
499 // 2. Post-condition: the returned function type is valid.
500 }
501
502 /// See: WebAssembly Specification 2.0 - 7.1.7 - func_invoke
503 pub fn invoke(
504 &mut self,
505 func_addr: FuncAddr,
506 params: Vec<Value>,
507 maybe_fuel: Option<u32>,
508 ) -> Result<RunState, RuntimeError> {
509 self.resume(self.create_resumable(func_addr, params, maybe_fuel)?)
510 }
511
512 /// Allocates a new table with some table type and an initialization value `ref` and returns its table address.
513 ///
514 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_alloc
515 pub fn table_alloc(
516 &mut self,
517 table_type: TableType,
518 r#ref: Ref,
519 ) -> Result<TableAddr, RuntimeError> {
520 // Check pre-condition: ref has correct type
521 if table_type.et != r#ref.ty() {
522 return Err(RuntimeError::TableTypeMismatch);
523 }
524
525 // 1. Pre-condition: `tabletype` is valid
526
527 // 2. Let `tableaddr` be the result of allocating a table in `store` with table type `tabletype`
528 // and initialization value `ref`.
529 let table_addr = self.alloc_table(table_type, r#ref);
530
531 // 3. Return the new store paired with `tableaddr`.
532 //
533 // Note: Returning the new store is a noop for us because we mutate the store instead.
534 Ok(table_addr)
535 }
536
537 /// Gets the type of some table by its addr.
538 ///
539 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_type
540 pub fn table_type(&self, table_addr: TableAddr) -> TableType {
541 // 1. Return `S.tables[a].type`.
542 self.tables.get(table_addr).ty
543
544 // 2. Post-condition: the returned table type is valid.
545 }
546
547 /// Reads a single reference from a table by its table address and an index into the table.
548 ///
549 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_read
550 pub fn table_read(&self, table_addr: TableAddr, i: u32) -> Result<Ref, RuntimeError> {
551 // Convert `i` to usize for indexing
552 let i = usize::try_from(i).expect("the architecture to be at least 32-bit");
553
554 // 1. Let `ti` be the table instance `store.tables[tableaddr]`
555 let ti = self.tables.get(table_addr);
556
557 // 2. If `i` is larger than or equal to the length of `ti.elem`, then return `error`.
558 // 3. Else, return the reference value `ti.elem[i]`.
559 ti.elem
560 .get(i)
561 .copied()
562 .ok_or(RuntimeError::TableAccessOutOfBounds)
563 }
564
565 /// Writes a single reference into a table by its table address and an index into the table.
566 ///
567 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_write
568 pub fn table_write(
569 &mut self,
570 table_addr: TableAddr,
571 i: u32,
572 r#ref: Ref,
573 ) -> Result<(), RuntimeError> {
574 // Convert `i` to usize for indexing
575 let i = usize::try_from(i).expect("the architecture to be at least 32-bit");
576
577 // 1. Let `ti` be the table instance `store.tables[tableaddr]`.
578 let ti = self.tables.get_mut(table_addr);
579
580 // Check pre-condition: ref has correct type
581 if ti.ty.et != r#ref.ty() {
582 return Err(RuntimeError::TableTypeMismatch);
583 }
584
585 // 2. If `i` is larger than or equal to the length of `ti.elem`, then return `error`.
586 // 3. Replace `ti.elem[i]` with the reference value `ref`
587 *ti.elem
588 .get_mut(i)
589 .ok_or(RuntimeError::TableAccessOutOfBounds)? = r#ref;
590
591 // 4. Return the updated store.
592 //
593 // Note: Returning the new store is a noop for us because we mutate the store instead.
594 Ok(())
595 }
596
597 /// Gets the current size of a table by its table address.
598 ///
599 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_size
600 pub fn table_size(&self, table_addr: TableAddr) -> u32 {
601 // 1. Return the length of `store.tables[tableaddr].elem`.
602 let len = self.tables.get(table_addr).elem.len();
603
604 // In addition we have to convert the length back to a `u32`
605 u32::try_from(len).expect(
606 "the maximum table length to be u32::MAX because thats what the specification allows for indexing",
607 )
608 }
609
610 /// Grows a table referenced by its table address by `n` elements.
611 ///
612 /// See: WebAssembly Specification 2.0 - 7.1.8 - table_grow
613 pub fn table_grow(
614 &mut self,
615 table_addr: TableAddr,
616 n: u32,
617 r#ref: Ref,
618 ) -> Result<(), RuntimeError> {
619 // 1. Try growing the table instance `store.tables[tableaddr] by `n` elements with initialization value `ref`:
620 // a. If it succeeds, return the updated store.
621 // b. Else, return `error`.
622 //
623 // Note: Returning the new store is a noop for us because we mutate the store instead.
624 self.tables.get_mut(table_addr).grow(n, r#ref)
625 }
626
627 /// Allocates a new linear memory and returns its memory address.
628 ///
629 /// See: WebAssembly Specification 2.0 - 7.1.9 - mem_alloc
630 pub fn mem_alloc(&mut self, mem_type: MemType) -> MemAddr {
631 // 1. Pre-condition: `memtype` is valid.
632
633 // 2. Let `memaddr` be the result of allocating a memory in `store` with memory type `memtype`.
634 // 3. Return the new store paired with `memaddr`.
635 //
636 // Note: Returning the new store is a noop for us because we mutate the store instead.
637 self.alloc_mem(mem_type)
638 }
639
640 /// Gets the memory type of some memory by its memory address
641 ///
642 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_type
643 pub fn mem_type(&self, mem_addr: MemAddr) -> MemType {
644 // 1. Return `S.mems[a].type`.
645 self.memories.get(mem_addr).ty
646
647 // 2. Post-condition: the returned memory type is valid.
648 }
649
650 /// Reads a byte from some memory by its memory address and an index into the memory
651 ///
652 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_read
653 pub fn mem_read(&self, mem_addr: MemAddr, i: u32) -> Result<u8, RuntimeError> {
654 // Convert the index type
655 let i = usize::try_from(i).expect("the architecture to be at least 32-bit");
656
657 // 1. Let `mi` be the memory instance `store.mems[memaddr]`.
658 let mi = self.memories.get(mem_addr);
659
660 // 2. If `i` is larger than or equal to the length of `mi.data`, then return `error`.
661 // 3. Else, return the byte `mi.data[i]`.
662 mi.mem.load(i)
663 }
664
665 /// Writes a byte into some memory by its memory address and an index into the memory
666 ///
667 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_write
668 pub fn mem_write(&self, mem_addr: MemAddr, i: u32, byte: u8) -> Result<(), RuntimeError> {
669 // Convert the index type
670 let i = usize::try_from(i).expect("the architecture to be at least 32-bit");
671
672 // 1. Let `mi` be the memory instance `store.mems[memaddr]`.
673 let mi = self.memories.get(mem_addr);
674
675 mi.mem.store(i, byte)
676 }
677
678 /// Gets the size of some memory by its memory address in pages.
679 ///
680 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_size
681 pub fn mem_size(&self, mem_addr: MemAddr) -> u32 {
682 // 1. Return the length of `store.mems[memaddr].data` divided by the page size.
683 let length = self.memories.get(mem_addr).size();
684
685 // In addition we have to convert the length back to a `u32`
686 length.try_into().expect(
687 "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.")
688 }
689
690 /// Grows some memory by its memory address by `n` pages.
691 ///
692 /// See: WebAssemblySpecification 2.0 - 7.1.9 - mem_grow
693 pub fn mem_grow(&mut self, mem_addr: MemAddr, n: u32) -> Result<(), RuntimeError> {
694 // 1. Try growing the memory instance `store.mems[memaddr]` by `n` pages:
695 // a. If it succeeds, then return the updated store.
696 // b. Else, return `error`.
697 //
698 // Note: Returning the new store is a noop for us because we mutate the store instead.
699 self.memories.get_mut(mem_addr).grow(n)
700 }
701
702 /// Allocates a new global and returns its global address.
703 ///
704 /// See: WebAssemblySpecification 2.0 - 7.1.10 - global_alloc
705 pub fn global_alloc(
706 &mut self,
707 global_type: GlobalType,
708 val: Value,
709 ) -> Result<GlobalAddr, RuntimeError> {
710 // Check pre-condition: val has correct type
711 if global_type.ty != val.to_ty() {
712 return Err(RuntimeError::GlobalTypeMismatch);
713 }
714
715 // 1. Pre-condition: `globaltype` is valid.
716
717 // 2. Let `globaladdr` be the result of allocating a global with global type `globaltype` and initialization value `val`.
718 let global_addr = self.alloc_global(global_type, val);
719
720 // 3. Return the new store paired with `globaladdr`.
721 //
722 // Note: Returning the new store is a noop for us because we mutate the store instead.
723 Ok(global_addr)
724 }
725
726 /// Returns the global type of some global instance by its addr.
727 ///
728 /// See: WebAssembly Specification 2.0 - 7.1.10 - global_type
729 pub fn global_type(&self, global_addr: GlobalAddr) -> GlobalType {
730 // 1. Return `S.globals[a].type`.
731 self.globals.get(global_addr).ty
732 // 2. Post-condition: the returned global type is valid
733 }
734
735 /// Returns the current value of some global instance by its addr.
736 ///
737 /// See: WebAssembly Specification 2.0 - 7.1.10 - global_read
738 pub fn global_read(&self, global_addr: GlobalAddr) -> Value {
739 // 1. Let `gi` be the global instance `store.globals[globaladdr].
740 let gi = self.globals.get(global_addr);
741
742 // 2. Return the value `gi.value`.
743 gi.value
744 }
745
746 /// Sets a new value of some global instance by its addr.
747 ///
748 /// # Errors
749 /// - [` RuntimeError::WriteOnImmutableGlobal`]
750 /// - [` RuntimeError::GlobalTypeMismatch`]
751 ///
752 /// See: WebAssembly Specification 2.0 - 7.1.10 - global_write
753 pub fn global_write(
754 &mut self,
755 global_addr: GlobalAddr,
756 val: Value,
757 ) -> Result<(), RuntimeError> {
758 // 1. Let `gi` be the global instance `store.globals[globaladdr]`.
759 let gi = self.globals.get_mut(global_addr);
760
761 // 2. Let `mut t` be the structure of the global type `gi.type`.
762 let r#mut = gi.ty.is_mut;
763 let t = gi.ty.ty;
764
765 // 3. If `mut` is not `var`, then return error.
766 if !r#mut {
767 return Err(RuntimeError::WriteOnImmutableGlobal);
768 }
769
770 // Check invariant:
771 // It is an invariant of the semantics that the value has a type equal to the value type of `globaltype`.
772 // See: WebAssembly Specification 2.0 - 4.2.9
773 if t != val.to_ty() {
774 return Err(RuntimeError::GlobalTypeMismatch);
775 }
776
777 // 4. Replace `gi.value` with the value `val`.
778 gi.value = val;
779
780 // 5. Return the updated store.
781 // This is a noop for us, as our store `self` is mutable.
782
783 Ok(())
784 }
785
786 /// roughly matches <https://webassembly.github.io/spec/core/exec/modules.html#functions> with the addition of sidetable pointer to the input signature
787 // TODO refactor the type of func
788 // TODO module_addr
789 fn alloc_func(&mut self, func: (TypeIdx, (Span, usize)), module_addr: ModuleAddr) -> FuncAddr {
790 let (ty, (span, stp)) = func;
791
792 // TODO rewrite this huge chunk of parsing after generic way to re-parse(?) structs lands
793 let mut wasm_reader = WasmReader::new(self.modules.get(module_addr).wasm_bytecode);
794 wasm_reader.move_start_to(span).unwrap_validated();
795
796 let (locals, bytes_read) = wasm_reader
797 .measure_num_read_bytes(crate::code::read_declared_locals)
798 .unwrap_validated();
799
800 let code_expr = wasm_reader
801 .make_span(span.len() - bytes_read)
802 .unwrap_validated();
803
804 // core of the method below
805
806 // validation guarantees func_ty_idx exists within module_inst.types
807 // TODO fix clone
808 let func_inst = FuncInst::WasmFunc(WasmFuncInst {
809 function_type: self.modules.get(module_addr).types[ty].clone(),
810 _ty: ty,
811 locals,
812 code_expr,
813 stp,
814 module_addr,
815 });
816 self.functions.insert(func_inst)
817 }
818
819 /// <https://webassembly.github.io/spec/core/exec/modules.html#tables>
820 fn alloc_table(&mut self, table_type: TableType, reff: Ref) -> TableAddr {
821 let table_inst = TableInst {
822 ty: table_type,
823 elem: vec![reff; table_type.lim.min as usize],
824 };
825
826 self.tables.insert(table_inst)
827 }
828
829 /// <https://webassembly.github.io/spec/core/exec/modules.html#memories>
830 fn alloc_mem(&mut self, mem_type: MemType) -> MemAddr {
831 let mem_inst = MemInst {
832 ty: mem_type,
833 mem: LinearMemory::new_with_initial_pages(
834 mem_type.limits.min.try_into().unwrap_validated(),
835 ),
836 };
837
838 self.memories.insert(mem_inst)
839 }
840
841 /// <https://webassembly.github.io/spec/core/exec/modules.html#globals>
842 fn alloc_global(&mut self, global_type: GlobalType, val: Value) -> GlobalAddr {
843 let global_inst = GlobalInst {
844 ty: global_type,
845 value: val,
846 };
847
848 self.globals.insert(global_inst)
849 }
850
851 /// <https://webassembly.github.io/spec/core/exec/modules.html#element-segments>
852 fn alloc_elem(&mut self, ref_type: RefType, refs: Vec<Ref>) -> ElemAddr {
853 let elem_inst = ElemInst {
854 _ty: ref_type,
855 references: refs,
856 };
857
858 self.elements.insert(elem_inst)
859 }
860
861 /// <https://webassembly.github.io/spec/core/exec/modules.html#data-segments>
862 fn alloc_data(&mut self, bytes: &[u8]) -> DataAddr {
863 let data_inst = DataInst {
864 data: Vec::from(bytes),
865 };
866
867 self.data.insert(data_inst)
868 }
869
870 /// This function allows an already instantiated module to be reregistered
871 /// under a different name. All previous registers of this module are not
872 /// affected.
873 ///
874 /// Note: This method exists as a temporary solution because our suboptimal registry
875 /// design. Because [`Store::add_module`] automatically registers all
876 /// modules directly after instantiation, we still need to provide some way
877 /// for only registering a module.
878 pub fn reregister_module(
879 &mut self,
880 module_addr: ModuleAddr,
881 name: &str,
882 ) -> Result<(), RuntimeError> {
883 self.registry
884 .register_module(name.to_owned().into(), self.modules.get(module_addr))
885 }
886
887 pub fn create_resumable(
888 &self,
889 func_addr: FuncAddr,
890 params: Vec<Value>,
891 maybe_fuel: Option<u32>,
892 ) -> Result<ResumableRef, RuntimeError> {
893 let func_inst = self.functions.get(func_addr);
894
895 let func_ty = func_inst.ty();
896
897 // Verify that the given parameters match the function parameters
898 let param_types = params.iter().map(|v| v.to_ty()).collect::<Vec<_>>();
899
900 if func_ty.params.valtypes != param_types {
901 trace!(
902 "Func param types len: {}; Given args len: {}",
903 func_ty.params.valtypes.len(),
904 param_types.len()
905 );
906 panic!("Invalid parameters for function");
907 }
908
909 Ok(ResumableRef::Fresh(FreshResumableRef {
910 func_addr,
911 params,
912 maybe_fuel,
913 }))
914 }
915
916 pub fn resume(&mut self, mut resumable_ref: ResumableRef) -> Result<RunState, RuntimeError> {
917 match resumable_ref {
918 ResumableRef::Fresh(FreshResumableRef {
919 func_addr,
920 params,
921 maybe_fuel,
922 }) => {
923 let func_inst = self.functions.get(func_addr);
924
925 match func_inst {
926 FuncInst::HostFunc(host_func_inst) => {
927 let returns = (host_func_inst.hostcode)(&mut self.user_data, params);
928
929 debug!("Successfully invoked function");
930
931 let returns = returns.map_err(|HaltExecutionError| {
932 RuntimeError::HostFunctionHaltedExecution
933 })?;
934
935 // Verify that the return parameters match the host function parameters
936 // since we have no validation guarantees for host functions
937
938 let return_types = returns.iter().map(|v| v.to_ty()).collect::<Vec<_>>();
939 if host_func_inst.function_type.returns.valtypes != return_types {
940 trace!(
941 "Func return types len: {}; returned args len: {}",
942 host_func_inst.function_type.returns.valtypes.len(),
943 return_types.len()
944 );
945 return Err(RuntimeError::HostFunctionSignatureMismatch);
946 }
947
948 Ok(RunState::Finished(returns))
949 }
950 FuncInst::WasmFunc(wasm_func_inst) => {
951 // Prepare a new stack with the locals for the entry function
952 let mut stack = Stack::new_with_values(params);
953
954 stack.push_call_frame::<T>(
955 FuncAddr::INVALID, // TODO using a default value like this is dangerous
956 &wasm_func_inst.function_type,
957 &wasm_func_inst.locals,
958 usize::MAX,
959 usize::MAX,
960 )?;
961
962 let mut resumable = Resumable {
963 current_func_addr: func_addr,
964 stack,
965 pc: wasm_func_inst.code_expr.from,
966 stp: wasm_func_inst.stp,
967 maybe_fuel,
968 };
969
970 // Run the interpreter
971 let result = interpreter_loop::run(&mut resumable, self)?;
972
973 match result {
974 None => {
975 debug!("Successfully invoked function");
976 Ok(RunState::Finished(resumable.stack.into_values()))
977 }
978 Some(required_fuel) => {
979 debug!("Successfully invoked function, but ran out of fuel");
980 Ok(RunState::Resumable {
981 resumable_ref: ResumableRef::Invoked(
982 self.dormitory.insert(resumable),
983 ),
984 required_fuel,
985 })
986 }
987 }
988 }
989 }
990 }
991 ResumableRef::Invoked(InvokedResumableRef {
992 dormitory: ref mut dormitory_weak,
993 ref key,
994 }) => {
995 // Resuming requires `self`'s dormitory to still be alive
996 let Some(dormitory) = dormitory_weak.upgrade() else {
997 return Err(RuntimeError::ResumableNotFound);
998 };
999
1000 // Check the given `RuntimeInstance` is the same one used to create `self`
1001 if !Arc::ptr_eq(&dormitory, &self.dormitory.0) {
1002 return Err(RuntimeError::ResumableNotFound);
1003 }
1004
1005 // Obtain a write lock to the `Dormitory`
1006 let mut dormitory = dormitory.write();
1007
1008 // TODO We might want to remove the `Resumable` here already and later reinsert it.
1009 // This would prevent holding the lock across the interpreter loop.
1010 let resumable = dormitory
1011 .get_mut(key)
1012 .expect("the key to always be valid as self was not dropped yet");
1013
1014 // Resume execution
1015 let result = interpreter_loop::run(resumable, self)?;
1016
1017 match result {
1018 None => {
1019 let resumable = dormitory.remove(key)
1020 .expect("that the resumable could not have been removed already, because then this self could not exist");
1021
1022 // Take the `Weak` pointing to the dormitory out of `self` and replace it with a default `Weak`.
1023 // This causes the `Drop` impl of `self` to directly quit preventing it from unnecessarily locking the dormitory.
1024 let _dormitory = mem::take(dormitory_weak);
1025
1026 Ok(RunState::Finished(resumable.stack.into_values()))
1027 }
1028 Some(required_fuel) => Ok(RunState::Resumable {
1029 resumable_ref,
1030 required_fuel,
1031 }),
1032 }
1033 }
1034 }
1035 }
1036
1037 pub fn access_fuel_mut<R>(
1038 &mut self,
1039 resumable_ref: &mut ResumableRef,
1040 f: impl FnOnce(&mut Option<u32>) -> R,
1041 ) -> Result<R, RuntimeError> {
1042 match resumable_ref {
1043 ResumableRef::Fresh(FreshResumableRef { maybe_fuel, .. }) => Ok(f(maybe_fuel)),
1044 ResumableRef::Invoked(resumable_ref) => {
1045 // Resuming requires `self`'s dormitory to still be alive
1046 let Some(dormitory) = resumable_ref.dormitory.upgrade() else {
1047 return Err(RuntimeError::ResumableNotFound);
1048 };
1049
1050 // Check the given `RuntimeInstance` is the same one used to create `self`
1051 if !Arc::ptr_eq(&dormitory, &self.dormitory.0) {
1052 return Err(RuntimeError::ResumableNotFound);
1053 }
1054
1055 let mut dormitory = dormitory.write();
1056
1057 let resumable = dormitory
1058 .get_mut(&resumable_ref.key)
1059 .expect("the key to always be valid as self was not dropped yet");
1060
1061 Ok(f(&mut resumable.maybe_fuel))
1062 }
1063 }
1064 }
1065}
1066
1067/// A marker error for host functions to return, in case they want execution to be halted.
1068pub struct HaltExecutionError;
1069
1070///<https://webassembly.github.io/spec/core/exec/runtime.html#external-values>
1071#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1072pub enum ExternVal {
1073 Func(FuncAddr),
1074 Table(TableAddr),
1075 Mem(MemAddr),
1076 Global(GlobalAddr),
1077}
1078
1079impl ExternVal {
1080 /// returns the external type of `self` according to typing relation,
1081 /// taking `store` as context S.
1082 ///
1083 /// Note: This method may panic if self does not come from the given [`Store`].
1084 ///<https://webassembly.github.io/spec/core/valid/modules.html#imports>
1085 pub fn extern_type<T: Config>(&self, store: &Store<T>) -> ExternType {
1086 match self {
1087 // TODO: fix ugly clone in function types
1088 ExternVal::Func(func_addr) => ExternType::Func(store.functions.get(*func_addr).ty()),
1089 ExternVal::Table(table_addr) => ExternType::Table(store.tables.get(*table_addr).ty),
1090 ExternVal::Mem(mem_addr) => ExternType::Mem(store.memories.get(*mem_addr).ty),
1091 ExternVal::Global(global_addr) => {
1092 ExternType::Global(store.globals.get(*global_addr).ty)
1093 }
1094 }
1095 }
1096}
1097
1098/// common convention functions defined for lists of ExternVals, ExternTypes, Exports
1099/// <https://webassembly.github.io/spec/core/exec/runtime.html#conventions>
1100/// <https://webassembly.github.io/spec/core/syntax/types.html#id3>
1101/// <https://webassembly.github.io/spec/core/syntax/modules.html?highlight=convention#id1>
1102// TODO implement this trait for ExternType lists Export lists
1103pub trait ExternFilterable {
1104 fn funcs(self) -> impl Iterator<Item = FuncAddr>;
1105 fn tables(self) -> impl Iterator<Item = TableAddr>;
1106 fn mems(self) -> impl Iterator<Item = MemAddr>;
1107 fn globals(self) -> impl Iterator<Item = GlobalAddr>;
1108}
1109
1110impl<'a, I> ExternFilterable for I
1111where
1112 I: Iterator<Item = &'a ExternVal>,
1113{
1114 fn funcs(self) -> impl Iterator<Item = FuncAddr> {
1115 self.filter_map(|extern_val| {
1116 if let ExternVal::Func(func_addr) = extern_val {
1117 Some(*func_addr)
1118 } else {
1119 None
1120 }
1121 })
1122 }
1123
1124 fn tables(self) -> impl Iterator<Item = TableAddr> {
1125 self.filter_map(|extern_val| {
1126 if let ExternVal::Table(table_addr) = extern_val {
1127 Some(*table_addr)
1128 } else {
1129 None
1130 }
1131 })
1132 }
1133
1134 fn mems(self) -> impl Iterator<Item = MemAddr> {
1135 self.filter_map(|extern_val| {
1136 if let ExternVal::Mem(mem_addr) = extern_val {
1137 Some(*mem_addr)
1138 } else {
1139 None
1140 }
1141 })
1142 }
1143
1144 fn globals(self) -> impl Iterator<Item = GlobalAddr> {
1145 self.filter_map(|extern_val| {
1146 if let ExternVal::Global(global_addr) = extern_val {
1147 Some(*global_addr)
1148 } else {
1149 None
1150 }
1151 })
1152 }
1153}