wasmi/
error.rs

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