tinywasm/
error.rs

1use alloc::string::{String, ToString};
2use core::{fmt::Display, ops::ControlFlow};
3use tinywasm_types::FuncType;
4
5#[cfg(feature = "parser")]
6pub use tinywasm_parser::ParseError;
7
8/// Errors that can occur for `TinyWasm` operations
9#[derive(Debug)]
10pub enum Error {
11    /// A WebAssembly trap occurred
12    Trap(Trap),
13
14    /// A linking error occurred
15    Linker(LinkingError),
16
17    /// A WebAssembly feature is not supported
18    UnsupportedFeature(String),
19
20    /// An unknown error occurred
21    Other(String),
22
23    /// A function did not return a value
24    FuncDidNotReturn,
25
26    /// An invalid label type was encountered
27    InvalidLabelType,
28
29    /// The store is not the one that the module instance was instantiated in
30    InvalidStore,
31
32    #[cfg(feature = "std")]
33    /// An I/O error occurred
34    Io(crate::std::io::Error),
35
36    #[cfg(feature = "parser")]
37    /// A parsing error occurred
38    ParseError(ParseError),
39}
40
41#[derive(Debug)]
42/// Errors that can occur when linking a WebAssembly module
43pub enum LinkingError {
44    /// An unknown import was encountered
45    UnknownImport {
46        /// The module name
47        module: String,
48        /// The import name
49        name: String,
50    },
51
52    /// A mismatched import type was encountered
53    IncompatibleImportType {
54        /// The module name
55        module: String,
56        /// The import name
57        name: String,
58    },
59}
60
61impl LinkingError {
62    pub(crate) fn incompatible_import_type(import: &tinywasm_types::Import) -> Self {
63        Self::IncompatibleImportType { module: import.module.to_string(), name: import.name.to_string() }
64    }
65
66    pub(crate) fn unknown_import(import: &tinywasm_types::Import) -> Self {
67        Self::UnknownImport { module: import.module.to_string(), name: import.name.to_string() }
68    }
69}
70
71#[derive(Debug)]
72/// A WebAssembly trap
73///
74/// See <https://webassembly.github.io/spec/core/intro/overview.html#trap>
75pub enum Trap {
76    /// An unreachable instruction was executed
77    Unreachable,
78
79    /// An out-of-bounds memory access occurred
80    MemoryOutOfBounds {
81        /// The offset of the access
82        offset: usize,
83        /// The size of the access
84        len: usize,
85        /// The maximum size of the memory
86        max: usize,
87    },
88
89    /// An out-of-bounds table access occurred
90    TableOutOfBounds {
91        /// The offset of the access
92        offset: usize,
93        /// The size of the access
94        len: usize,
95        /// The maximum size of the memory
96        max: usize,
97    },
98
99    /// A division by zero occurred
100    DivisionByZero,
101
102    /// Invalid Integer Conversion
103    InvalidConversionToInt,
104
105    /// Integer Overflow
106    IntegerOverflow,
107
108    /// Call stack overflow
109    CallStackOverflow,
110
111    /// An undefined element was encountered
112    UndefinedElement {
113        /// The element index
114        index: usize,
115    },
116
117    /// An uninitialized element was encountered
118    UninitializedElement {
119        /// The element index
120        index: usize,
121    },
122
123    /// Indirect call type mismatch
124    IndirectCallTypeMismatch {
125        /// The expected type
126        expected: FuncType,
127        /// The actual type
128        actual: FuncType,
129    },
130}
131
132impl Trap {
133    /// Get the message of the trap
134    pub fn message(&self) -> &'static str {
135        match self {
136            Self::Unreachable => "unreachable",
137            Self::MemoryOutOfBounds { .. } => "out of bounds memory access",
138            Self::TableOutOfBounds { .. } => "out of bounds table access",
139            Self::DivisionByZero => "integer divide by zero",
140            Self::InvalidConversionToInt => "invalid conversion to integer",
141            Self::IntegerOverflow => "integer overflow",
142            Self::CallStackOverflow => "call stack exhausted",
143            Self::UndefinedElement { .. } => "undefined element",
144            Self::UninitializedElement { .. } => "uninitialized element",
145            Self::IndirectCallTypeMismatch { .. } => "indirect call type mismatch",
146        }
147    }
148}
149
150impl LinkingError {
151    /// Get the message of the linking error
152    pub fn message(&self) -> &'static str {
153        match self {
154            Self::UnknownImport { .. } => "unknown import",
155            Self::IncompatibleImportType { .. } => "incompatible import type",
156        }
157    }
158}
159
160impl From<LinkingError> for Error {
161    fn from(value: LinkingError) -> Self {
162        Self::Linker(value)
163    }
164}
165
166impl From<Trap> for Error {
167    fn from(value: Trap) -> Self {
168        Self::Trap(value)
169    }
170}
171
172impl Display for Error {
173    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
174        match self {
175            #[cfg(feature = "parser")]
176            Self::ParseError(err) => write!(f, "error parsing module: {err:?}"),
177
178            #[cfg(feature = "std")]
179            Self::Io(err) => write!(f, "I/O error: {err}"),
180
181            Self::Trap(trap) => write!(f, "trap: {trap}"),
182            Self::Linker(err) => write!(f, "linking error: {err}"),
183            Self::InvalidLabelType => write!(f, "invalid label type"),
184            Self::Other(message) => write!(f, "unknown error: {message}"),
185            Self::UnsupportedFeature(feature) => write!(f, "unsupported feature: {feature}"),
186            Self::FuncDidNotReturn => write!(f, "function did not return"),
187            Self::InvalidStore => write!(f, "invalid store"),
188        }
189    }
190}
191
192impl Display for LinkingError {
193    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
194        match self {
195            Self::UnknownImport { module, name } => write!(f, "unknown import: {}.{}", module, name),
196            Self::IncompatibleImportType { module, name } => {
197                write!(f, "incompatible import type: {}.{}", module, name)
198            }
199        }
200    }
201}
202
203impl Display for Trap {
204    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
205        match self {
206            Self::Unreachable => write!(f, "unreachable"),
207            Self::MemoryOutOfBounds { offset, len, max } => {
208                write!(f, "out of bounds memory access: offset={offset}, len={len}, max={max}")
209            }
210            Self::TableOutOfBounds { offset, len, max } => {
211                write!(f, "out of bounds table access: offset={offset}, len={len}, max={max}")
212            }
213            Self::DivisionByZero => write!(f, "integer divide by zero"),
214            Self::InvalidConversionToInt => write!(f, "invalid conversion to integer"),
215            Self::IntegerOverflow => write!(f, "integer overflow"),
216            Self::CallStackOverflow => write!(f, "call stack exhausted"),
217            Self::UndefinedElement { index } => write!(f, "undefined element: index={index}"),
218            Self::UninitializedElement { index } => {
219                write!(f, "uninitialized element: index={index}")
220            }
221            Self::IndirectCallTypeMismatch { expected, actual } => {
222                write!(f, "indirect call type mismatch: expected={expected:?}, actual={actual:?}")
223            }
224        }
225    }
226}
227
228#[cfg(any(feature = "std", all(not(feature = "std"), feature = "nightly")))]
229impl crate::std::error::Error for Error {}
230
231#[cfg(feature = "parser")]
232impl From<tinywasm_parser::ParseError> for Error {
233    fn from(value: tinywasm_parser::ParseError) -> Self {
234        Self::ParseError(value)
235    }
236}
237
238/// A wrapper around [`core::result::Result`] for tinywasm operations
239pub type Result<T, E = Error> = crate::std::result::Result<T, E>;
240
241pub(crate) trait Controlify<T> {
242    fn to_cf(self) -> ControlFlow<Option<Error>, T>;
243}
244
245impl<T> Controlify<T> for Result<T, Error> {
246    fn to_cf(self) -> ControlFlow<Option<Error>, T> {
247        match self {
248            Ok(value) => ControlFlow::Continue(value),
249            Err(err) => ControlFlow::Break(Some(err)),
250        }
251    }
252}