use super::errors::{
EnforcedLimitsError,
FuncError,
GlobalError,
InstantiationError,
IrError,
LinkerError,
};
use crate::{
TrapCode,
engine::{ResumableHostTrapError, ResumableOutOfFuelError, TranslationError},
module::ReadError,
};
use alloc::{boxed::Box, string::String};
use core::{fmt, fmt::Display};
use wasmi_core::{FuelError, HostError, MemoryError, TableError};
use wasmparser::BinaryReaderError as WasmError;
#[cfg(feature = "wat")]
use wat::Error as WatError;
#[derive(Debug)]
pub struct Error {
kind: Box<ErrorKind>,
}
#[test]
fn error_size() {
use core::mem;
assert_eq!(mem::size_of::<Error>(), 8);
}
impl Error {
fn from_kind(kind: ErrorKind) -> Self {
Self {
kind: Box::new(kind),
}
}
#[inline]
#[cold]
pub fn new<T>(message: T) -> Self
where
T: Into<String>,
{
Self::from_kind(ErrorKind::Message(message.into().into_boxed_str()))
}
#[inline]
#[cold]
pub fn host<E>(host_error: E) -> Self
where
E: HostError,
{
Self::from_kind(ErrorKind::Host(Box::new(host_error)))
}
#[inline]
#[cold]
pub fn i32_exit(status: i32) -> Self {
Self::from_kind(ErrorKind::I32ExitStatus(status))
}
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
pub fn as_trap_code(&self) -> Option<TrapCode> {
self.kind().as_trap_code()
}
pub fn i32_exit_status(&self) -> Option<i32> {
self.kind().as_i32_exit_status()
}
#[inline]
pub fn downcast_ref<T>(&self) -> Option<&T>
where
T: HostError,
{
self.kind
.as_host()
.and_then(<dyn HostError + 'static>::downcast_ref)
}
#[inline]
pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
where
T: HostError,
{
self.kind
.as_host_mut()
.and_then(<dyn HostError + 'static>::downcast_mut)
}
#[inline]
pub fn downcast<T>(self) -> Option<T>
where
T: HostError,
{
self.kind
.into_host()
.and_then(|error| error.downcast().ok())
.map(|boxed| *boxed)
}
#[expect(unused)] pub(crate) fn is_out_of_fuel(&self) -> bool {
matches!(
self.kind(),
ErrorKind::TrapCode(TrapCode::OutOfFuel)
| ErrorKind::ResumableOutOfFuel(_)
| ErrorKind::Memory(MemoryError::OutOfFuel { .. })
| ErrorKind::Table(TableError::OutOfFuel { .. })
| ErrorKind::Fuel(FuelError::OutOfFuel { .. })
)
}
}
impl core::error::Error for Error {}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.kind, f)
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum ErrorKind {
TrapCode(TrapCode),
Message(Box<str>),
I32ExitStatus(i32),
Host(Box<dyn HostError>),
#[doc(hidden)]
ResumableHostTrap(ResumableHostTrapError),
#[doc(hidden)]
ResumableOutOfFuel(ResumableOutOfFuelError),
Global(GlobalError),
Memory(MemoryError),
Table(TableError),
Linker(LinkerError),
Instantiation(InstantiationError),
Fuel(FuelError),
Func(FuncError),
Read(ReadError),
Wasm(WasmError),
Translation(TranslationError),
Limits(EnforcedLimitsError),
Ir(IrError),
#[cfg(feature = "wat")]
Wat(WatError),
}
impl ErrorKind {
pub fn as_trap_code(&self) -> Option<TrapCode> {
let trap_code = match self {
| Self::TrapCode(trap_code) => *trap_code,
| Self::ResumableOutOfFuel(_)
| Self::Fuel(FuelError::OutOfFuel { .. })
| Self::Table(TableError::OutOfFuel { .. })
| Self::Memory(MemoryError::OutOfFuel { .. }) => TrapCode::OutOfFuel,
| Self::Memory(MemoryError::OutOfBoundsAccess)
| Self::Memory(MemoryError::OutOfBoundsGrowth) => TrapCode::MemoryOutOfBounds,
| Self::Table(TableError::ElementTypeMismatch) => TrapCode::BadSignature,
| Self::Table(TableError::SetOutOfBounds)
| Self::Table(TableError::FillOutOfBounds)
| Self::Table(TableError::GrowOutOfBounds)
| Self::Table(TableError::InitOutOfBounds) => TrapCode::TableOutOfBounds,
_ => return None,
};
Some(trap_code)
}
pub fn as_i32_exit_status(&self) -> Option<i32> {
match self {
Self::I32ExitStatus(exit_status) => Some(*exit_status),
_ => None,
}
}
pub fn as_host(&self) -> Option<&dyn HostError> {
match self {
Self::Host(error) => Some(error.as_ref()),
_ => None,
}
}
pub fn as_host_mut(&mut self) -> Option<&mut dyn HostError> {
match self {
Self::Host(error) => Some(error.as_mut()),
_ => None,
}
}
pub fn into_host(self) -> Option<Box<dyn HostError>> {
match self {
Self::Host(error) => Some(error),
_ => None,
}
}
}
impl core::error::Error for ErrorKind {}
impl Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::TrapCode(error) => Display::fmt(error, f),
Self::I32ExitStatus(status) => writeln!(f, "Exited with i32 exit status {status}"),
Self::Message(message) => Display::fmt(message, f),
Self::Host(error) => Display::fmt(error, f),
Self::Global(error) => Display::fmt(error, f),
Self::Memory(error) => Display::fmt(error, f),
Self::Table(error) => Display::fmt(error, f),
Self::Linker(error) => Display::fmt(error, f),
Self::Func(error) => Display::fmt(error, f),
Self::Instantiation(error) => Display::fmt(error, f),
Self::Fuel(error) => Display::fmt(error, f),
Self::Read(error) => Display::fmt(error, f),
Self::Wasm(error) => Display::fmt(error, f),
Self::Translation(error) => Display::fmt(error, f),
Self::Limits(error) => Display::fmt(error, f),
Self::ResumableHostTrap(error) => Display::fmt(error, f),
Self::ResumableOutOfFuel(error) => Display::fmt(error, f),
Self::Ir(error) => Display::fmt(error, f),
#[cfg(feature = "wat")]
Self::Wat(error) => Display::fmt(error, f),
}
}
}
macro_rules! impl_from {
( $( impl From<$from:ident> for Error::$name:ident );* $(;)? ) => {
$(
impl From<$from> for Error {
#[inline]
#[cold]
fn from(error: $from) -> Self {
Self::from_kind(ErrorKind::$name(error))
}
}
)*
}
}
impl_from! {
impl From<TrapCode> for Error::TrapCode;
impl From<GlobalError> for Error::Global;
impl From<MemoryError> for Error::Memory;
impl From<TableError> for Error::Table;
impl From<LinkerError> for Error::Linker;
impl From<InstantiationError> for Error::Instantiation;
impl From<TranslationError> for Error::Translation;
impl From<WasmError> for Error::Wasm;
impl From<ReadError> for Error::Read;
impl From<FuelError> for Error::Fuel;
impl From<FuncError> for Error::Func;
impl From<EnforcedLimitsError> for Error::Limits;
impl From<ResumableHostTrapError> for Error::ResumableHostTrap;
impl From<ResumableOutOfFuelError> for Error::ResumableOutOfFuel;
impl From<IrError> for Error::Ir;
}
#[cfg(feature = "wat")]
impl_from! {
impl From<WatError> for Error::Wat;
}