wasmi/
error.rs

1use super::errors::{
2    EnforcedLimitsError,
3    FuelError,
4    FuncError,
5    GlobalError,
6    InstantiationError,
7    LinkerError,
8    MemoryError,
9    TableError,
10};
11use crate::{
12    core::{HostError, TrapCode},
13    engine::{ResumableHostError, TranslationError},
14    module::ReadError,
15};
16use core::{fmt, fmt::Display};
17use std::{boxed::Box, string::String};
18use wasmparser::BinaryReaderError as WasmError;
19
20/// The generic Wasmi root error type.
21#[derive(Debug)]
22pub struct Error {
23    /// The underlying kind of the error and its specific information.
24    kind: Box<ErrorKind>,
25}
26
27#[test]
28fn error_size() {
29    use core::mem;
30    assert_eq!(mem::size_of::<Error>(), 8);
31}
32
33impl Error {
34    /// Creates a new [`Error`] from the [`ErrorKind`].
35    fn from_kind(kind: ErrorKind) -> Self {
36        Self {
37            kind: Box::new(kind),
38        }
39    }
40
41    /// Creates a new [`Error`] described by a `message`.
42    #[inline]
43    #[cold]
44    pub fn new<T>(message: T) -> Self
45    where
46        T: Into<String>,
47    {
48        Self::from_kind(ErrorKind::Message(message.into().into_boxed_str()))
49    }
50
51    /// Creates a custom [`HostError`].
52    #[inline]
53    #[cold]
54    pub fn host<E>(host_error: E) -> Self
55    where
56        E: HostError,
57    {
58        Self::from_kind(ErrorKind::Host(Box::new(host_error)))
59    }
60
61    /// Creates a new `Error` representing an explicit program exit with a classic `i32` exit status value.
62    ///
63    /// # Note
64    ///
65    /// This is usually used as return code by WASI applications.
66    #[inline]
67    #[cold]
68    pub fn i32_exit(status: i32) -> Self {
69        Self::from_kind(ErrorKind::I32ExitStatus(status))
70    }
71
72    /// Returns the [`ErrorKind`] of the [`Error`].
73    pub fn kind(&self) -> &ErrorKind {
74        &self.kind
75    }
76
77    /// Returns a reference to [`TrapCode`] if [`Error`] is a [`TrapCode`].
78    pub fn as_trap_code(&self) -> Option<TrapCode> {
79        self.kind().as_trap_code()
80    }
81
82    /// Returns the classic `i32` exit program code of a `Trap` if any.
83    ///
84    /// Otherwise returns `None`.
85    pub fn i32_exit_status(&self) -> Option<i32> {
86        self.kind().as_i32_exit_status()
87    }
88
89    /// Downcasts the [`Error`] into the `T: HostError` if possible.
90    ///
91    /// Returns `None` otherwise.
92    #[inline]
93    pub fn downcast_ref<T>(&self) -> Option<&T>
94    where
95        T: HostError,
96    {
97        self.kind
98            .as_host()
99            .and_then(<(dyn HostError + 'static)>::downcast_ref)
100    }
101
102    /// Downcasts the [`Error`] into the `T: HostError` if possible.
103    ///
104    /// Returns `None` otherwise.
105    #[inline]
106    pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
107    where
108        T: HostError,
109    {
110        self.kind
111            .as_host_mut()
112            .and_then(<(dyn HostError + 'static)>::downcast_mut)
113    }
114
115    /// Consumes `self` to downcast the [`Error`] into the `T: HostError` if possible.
116    ///
117    /// Returns `None` otherwise.
118    #[inline]
119    pub fn downcast<T>(self) -> Option<T>
120    where
121        T: HostError,
122    {
123        self.kind
124            .into_host()
125            .and_then(|error| error.downcast().ok())
126            .map(|boxed| *boxed)
127    }
128
129    pub(crate) fn into_resumable(self) -> Result<ResumableHostError, Error> {
130        if matches!(&*self.kind, ErrorKind::ResumableHost(_)) {
131            let ErrorKind::ResumableHost(error) = *self.kind else {
132                unreachable!("asserted that host error is resumable")
133            };
134            return Ok(error);
135        }
136        Err(self)
137    }
138}
139
140#[cfg(feature = "std")]
141impl std::error::Error for Error {}
142
143impl Display for Error {
144    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145        Display::fmt(&self.kind, f)
146    }
147}
148
149/// An error that may occur upon operating on Wasm modules or module instances.
150#[derive(Debug)]
151#[non_exhaustive]
152pub enum ErrorKind {
153    /// A trap code as defined by the WebAssembly specification.
154    TrapCode(TrapCode),
155    /// A message usually provided by Wasmi users of host function calls.
156    Message(Box<str>),
157    /// An `i32` exit status usually used by WASI applications.
158    I32ExitStatus(i32),
159    /// A trap as defined by the WebAssembly specification.
160    Host(Box<dyn HostError>),
161    /// An error stemming from a host function call with resumable state information.
162    ///
163    /// # Note
164    ///
165    /// This variant is meant for internal uses only in order to store data necessary
166    /// to resume a call after a host function returned an error. This should never
167    /// actually reach user code thus we hide its documentation.
168    #[doc(hidden)]
169    ResumableHost(ResumableHostError),
170    /// A global variable error.
171    Global(GlobalError),
172    /// A linear memory error.
173    Memory(MemoryError),
174    /// A table error.
175    Table(TableError),
176    /// A linker error.
177    Linker(LinkerError),
178    /// A module instantiation error.
179    Instantiation(InstantiationError),
180    /// A fuel error.
181    Fuel(FuelError),
182    /// A function error.
183    Func(FuncError),
184    /// Encountered when there is a problem with the Wasm input stream.
185    Read(ReadError),
186    /// Encountered when there is a Wasm parsing or validation error.
187    Wasm(WasmError),
188    /// Encountered when there is a Wasm to Wasmi translation error.
189    Translation(TranslationError),
190    /// Encountered when an enforced limit is exceeded.
191    Limits(EnforcedLimitsError),
192}
193
194impl ErrorKind {
195    /// Returns a reference to [`TrapCode`] if [`ErrorKind`] is a [`TrapCode`].
196    pub fn as_trap_code(&self) -> Option<TrapCode> {
197        match self {
198            Self::TrapCode(trap_code) => Some(*trap_code),
199            _ => None,
200        }
201    }
202
203    /// Returns a [`i32`] if [`ErrorKind`] is an [`ErrorKind::I32ExitStatus`].
204    pub fn as_i32_exit_status(&self) -> Option<i32> {
205        match self {
206            Self::I32ExitStatus(exit_status) => Some(*exit_status),
207            _ => None,
208        }
209    }
210
211    /// Returns a dynamic reference to [`HostError`] if [`ErrorKind`] is a [`HostError`].
212    pub fn as_host(&self) -> Option<&dyn HostError> {
213        match self {
214            Self::Host(error) => Some(error.as_ref()),
215            _ => None,
216        }
217    }
218
219    /// Returns a dynamic reference to [`HostError`] if [`ErrorKind`] is a [`HostError`].
220    pub fn as_host_mut(&mut self) -> Option<&mut dyn HostError> {
221        match self {
222            Self::Host(error) => Some(error.as_mut()),
223            _ => None,
224        }
225    }
226
227    /// Returns the [`HostError`] if [`ErrorKind`] is a [`HostError`].
228    pub fn into_host(self) -> Option<Box<dyn HostError>> {
229        match self {
230            Self::Host(error) => Some(error),
231            _ => None,
232        }
233    }
234}
235
236#[cfg(feature = "std")]
237impl std::error::Error for ErrorKind {}
238
239impl Display for ErrorKind {
240    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
241        match self {
242            Self::TrapCode(error) => Display::fmt(error, f),
243            Self::I32ExitStatus(status) => writeln!(f, "Exited with i32 exit status {status}"),
244            Self::Message(message) => Display::fmt(message, f),
245            Self::Host(error) => Display::fmt(error, f),
246            Self::Global(error) => Display::fmt(error, f),
247            Self::Memory(error) => Display::fmt(error, f),
248            Self::Table(error) => Display::fmt(error, f),
249            Self::Linker(error) => Display::fmt(error, f),
250            Self::Func(error) => Display::fmt(error, f),
251            Self::Instantiation(error) => Display::fmt(error, f),
252            Self::Fuel(error) => Display::fmt(error, f),
253            Self::Read(error) => Display::fmt(error, f),
254            Self::Wasm(error) => Display::fmt(error, f),
255            Self::Translation(error) => Display::fmt(error, f),
256            Self::Limits(error) => Display::fmt(error, f),
257            Self::ResumableHost(error) => Display::fmt(error, f),
258        }
259    }
260}
261
262macro_rules! impl_from {
263    ( $( impl From<$from:ident> for Error::$name:ident );* $(;)? ) => {
264        $(
265            impl From<$from> for Error {
266                #[inline]
267                #[cold]
268                fn from(error: $from) -> Self {
269                    Self::from_kind(ErrorKind::$name(error))
270                }
271            }
272        )*
273    }
274}
275impl_from! {
276    impl From<TrapCode> for Error::TrapCode;
277    impl From<GlobalError> for Error::Global;
278    impl From<MemoryError> for Error::Memory;
279    impl From<TableError> for Error::Table;
280    impl From<LinkerError> for Error::Linker;
281    impl From<InstantiationError> for Error::Instantiation;
282    impl From<TranslationError> for Error::Translation;
283    impl From<WasmError> for Error::Wasm;
284    impl From<ReadError> for Error::Read;
285    impl From<FuelError> for Error::Fuel;
286    impl From<FuncError> for Error::Func;
287    impl From<EnforcedLimitsError> for Error::Limits;
288    impl From<ResumableHostError> for Error::ResumableHost;
289}
290
291/// An error that can occur upon `memory.grow` or `table.grow`.
292#[derive(Copy, Clone)]
293pub enum EntityGrowError {
294    /// Usually a [`TrapCode::OutOfFuel`] trap.
295    TrapCode(TrapCode),
296    /// Encountered when `memory.grow` or `table.grow` fails.
297    InvalidGrow,
298}
299
300impl EntityGrowError {
301    /// The WebAssembly specification demands to return this value
302    /// if the `memory.grow` or `table.grow` operations fail.
303    pub const ERROR_CODE: u32 = u32::MAX;
304}
305
306impl From<TrapCode> for EntityGrowError {
307    fn from(trap_code: TrapCode) -> Self {
308        Self::TrapCode(trap_code)
309    }
310}