Line | Count | Source |
1 | | //! An in-place interpreter for WebAssembly 2.0 |
2 | | //! |
3 | | //! # General Usage |
4 | | //! |
5 | | //! WebAssembly (Wasm) modules must first be decoded and validated through [`decode_and_validate`], |
6 | | //! producing a [`Module`]. This module can then be instantiated in a [`Store`] via |
7 | | //! [`Store::module_instantiate`], creating a module instance and returning its module address, |
8 | | //! uniquely identifying this module instance within that store. |
9 | | //! |
10 | | //! When a [`Store`] is initially created through [`Store::new`], it is empty. This store exposes |
11 | | //! many other functions besides module instantiation to interact with it and objects allocated |
12 | | //! within it. Most notably, the [`Store::invoke_simple`] and [`Store::invoke`] methods are used to |
13 | | //! interpret Wasm code. Refer to the examples in `examples/` for more information. |
14 | | //! |
15 | | //! # Example |
16 | | //! |
17 | | //! This is an example for how to run a function exposed from a simple Wasm module (full code in |
18 | | //! `examples/function_invocation.rs`): |
19 | | //! |
20 | | //! ``` |
21 | | //! # use dlr_wasm_interpreter::{ExternVal, FuncAddr, InstantiationOutcome, Module, Store, Value, decode_and_validate}; |
22 | | //! # fn main() -> Result<(), Box<dyn std::error::Error>> { |
23 | | //! const WAT_CODE: &str = r#" |
24 | | //! (module |
25 | | //! (func (export "add_one") (param $n i32) (result i32) |
26 | | //! local.get $n |
27 | | //! i32.const 1 |
28 | | //! i32.add)) |
29 | | //! "#; |
30 | | //! |
31 | | //! // Use the `wat` crate to convert from the text format to bytecode |
32 | | //! let wasm_bytecode = wat::parse_str(WAT_CODE)?; |
33 | | //! |
34 | | //! // Decode and validate the module |
35 | | //! let module = decode_and_validate(&wasm_bytecode, &mut ())?; |
36 | | //! |
37 | | //! // Create a new empty store |
38 | | //! let mut store = Store::new(()); |
39 | | //! |
40 | | //! // Instantiate the module to create a module instance, returning its address |
41 | | //! // SAFETY: There are no extern values. |
42 | | //! let module_addr = unsafe { store.module_instantiate(&module, vec![], None) }?.module_addr; |
43 | | //! |
44 | | //! // Get the function address of the exported add_one function |
45 | | //! // SAFETY: The module address was returned from the same store. |
46 | | //! let add_one_extern = unsafe { store.instance_export(module_addr, "add_one") }?; |
47 | | //! let add_one = add_one_extern.as_func().ok_or("add_one is not a function")?; |
48 | | //! |
49 | | //! // Invoke the function |
50 | | //! // SAFETY: The function address was returned from the same store. There are also no address |
51 | | //! // type parameters. |
52 | | //! let return_values = unsafe { store.invoke_simple(add_one, vec![Value::I32(16)]) }?; |
53 | | //! assert_eq!(*return_values, [Value::I32(17)]); |
54 | | //! # Ok(()) |
55 | | //! # } |
56 | | //! ``` |
57 | | //! |
58 | | //! # Optional Features |
59 | | //! |
60 | | //! - `log`: Enables logging (enables `log` dependency). |
61 | | |
62 | | #![no_std] |
63 | | |
64 | | extern crate alloc; |
65 | | |
66 | | pub use crate::{ |
67 | | core::{ |
68 | | decoding::{error::DecodingError, modules::custom_section::CustomSection}, |
69 | | rw_spinlock, |
70 | | structure::instructions, |
71 | | structure::types::{ |
72 | | ExternType, FuncType, GlobalType, Limits, MemType, NumType, RefType, ResultType, |
73 | | TableType, ValType, |
74 | | }, |
75 | | }, |
76 | | execution::{ |
77 | | config::Config, |
78 | | error::{RuntimeError, TrapError}, |
79 | | resumable::*, |
80 | | runtime_structure::{ |
81 | | addresses::*, |
82 | | external_values::ExternVal, |
83 | | store::{Hostcode, InstantiationOutcome, Store}, |
84 | | values::{ExternAddr, Ref, Value, ValueTypeMismatchError, F32, F64}, |
85 | | }, |
86 | | }, |
87 | | validation::{config::ValidationConfig, decode_and_validate, error::ValidationError, Module}, |
88 | | }; |
89 | | |
90 | | pub(crate) mod core; |
91 | | pub(crate) mod execution; |
92 | | pub(crate) mod validation; |
93 | | |
94 | | /// A definition for a [`Result`] using the optional [`Error`] type. |
95 | | pub type Result<T> = ::core::result::Result<T, Error>; |
96 | | |
97 | | /// An opt-in error type useful for merging all error types of this crate into a single type. |
98 | | /// |
99 | | /// Note: This crate does not use this type in any public interfaces, making it optional for downstream users. |
100 | | #[derive(Debug, PartialEq, Eq)] |
101 | | pub enum Error { |
102 | | Validation(ValidationError), |
103 | | RuntimeError(RuntimeError), |
104 | | } |
105 | | |
106 | | impl From<ValidationError> for Error { |
107 | 1 | fn from(value: ValidationError) -> Self { |
108 | 1 | Self::Validation(value) |
109 | 1 | } |
110 | | } |
111 | | |
112 | | impl From<RuntimeError> for Error { |
113 | 1 | fn from(value: RuntimeError) -> Self { |
114 | 1 | Self::RuntimeError(value) |
115 | 1 | } |
116 | | } |
117 | | |
118 | | #[cfg(test)] |
119 | | mod test { |
120 | | use crate::{core::decoding::error::DecodingError, Error, RuntimeError, ValidationError}; |
121 | | |
122 | | #[test] |
123 | 1 | fn error_conversion_validation_error() { |
124 | 1 | let validation_error = ValidationError::Decoding(DecodingError::InvalidMagic); |
125 | 1 | let error: Error = validation_error.into(); |
126 | | |
127 | 1 | assert_eq!( |
128 | | error, |
129 | | Error::Validation(ValidationError::Decoding(DecodingError::InvalidMagic)) |
130 | | ) |
131 | 1 | } |
132 | | |
133 | | #[test] |
134 | 1 | fn error_conversion_runtime_error() { |
135 | 1 | let runtime_error = RuntimeError::ModuleNotFound; |
136 | 1 | let error: Error = runtime_error.into(); |
137 | | |
138 | 1 | assert_eq!(error, Error::RuntimeError(RuntimeError::ModuleNotFound)) |
139 | 1 | } |
140 | | } |