use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::fmt::{Debug, Display};
use tinywasm_types::FuncType;
use tinywasm_types::archive::TwasmError;
#[cfg(feature = "parser")]
pub use tinywasm_parser::ParseError;
#[non_exhaustive]
pub enum Error {
Trap(Trap),
Linker(LinkingError),
UnsupportedFeature(&'static str),
Other(String),
InvalidHostFnReturn {
expected: Arc<FuncType>,
actual: Vec<tinywasm_types::WasmValue>,
},
InvalidLabelType,
#[cfg(feature = "std")]
Io(crate::std::io::Error),
#[cfg(feature = "parser")]
Parser(ParseError),
Twasm(TwasmError),
}
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Trap(a), Self::Trap(b)) => a == b,
(Self::Linker(a), Self::Linker(b)) => a == b,
(Self::UnsupportedFeature(a), Self::UnsupportedFeature(b)) => a == b,
(Self::Other(a), Self::Other(b)) => a == b,
#[cfg(feature = "std")]
(Self::Io(a), Self::Io(b)) => a.kind() == b.kind(),
#[cfg(feature = "parser")]
(Self::Parser(a), Self::Parser(b)) => a == b,
(Self::Twasm(a), Self::Twasm(b)) => a == b,
_ => false,
}
}
}
#[non_exhaustive]
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(PartialEq, Eq)]
pub enum LinkingError {
UnknownImport {
module: String,
name: String,
},
IncompatibleImportType {
module: String,
name: String,
},
}
impl LinkingError {
pub(crate) fn incompatible_import_type(import: &tinywasm_types::Import) -> Self {
Self::IncompatibleImportType { module: import.module.to_string(), name: import.name.to_string() }
}
pub(crate) fn unknown_import(import: &tinywasm_types::Import) -> Self {
Self::UnknownImport { module: import.module.to_string(), name: import.name.to_string() }
}
}
#[non_exhaustive]
#[cfg_attr(feature = "debug", derive(Debug))]
pub enum Trap {
Unreachable,
HostFunction(Box<dyn core::error::Error + Send + Sync>),
MemoryOutOfBounds {
offset: usize,
len: usize,
max: usize,
},
TableOutOfBounds {
offset: usize,
len: usize,
max: usize,
},
DivisionByZero,
InvalidConversionToInt,
InvalidStore,
IntegerOverflow,
CallStackOverflow,
ValueStackOverflow,
OutOfMemory,
UndefinedElement {
index: usize,
},
UninitializedElement {
index: usize,
},
IndirectCallTypeMismatch {
expected: Arc<FuncType>,
actual: Arc<FuncType>,
},
Other(&'static str),
}
impl Trap {
pub fn message(&self) -> &'static str {
match self {
Self::Unreachable => "unreachable",
Self::MemoryOutOfBounds { .. } => "out of bounds memory access",
Self::TableOutOfBounds { .. } => "out of bounds table access",
Self::DivisionByZero => "integer divide by zero",
Self::InvalidConversionToInt => "invalid conversion to integer",
Self::IntegerOverflow => "integer overflow",
Self::CallStackOverflow => "call stack exhausted",
Self::ValueStackOverflow => "value stack exhausted",
Self::OutOfMemory => "out of memory",
Self::UndefinedElement { .. } => "undefined element",
Self::UninitializedElement { .. } => "uninitialized element",
Self::IndirectCallTypeMismatch { .. } => "indirect call type mismatch",
Self::HostFunction(_) => "host function trap",
Self::InvalidStore => "invalid store",
Self::Other(message) => message,
}
}
}
impl PartialEq for Trap {
fn eq(&self, other: &Self) -> bool {
self.message() == other.message()
}
}
impl LinkingError {
pub fn message(&self) -> &'static str {
match self {
Self::UnknownImport { .. } => "unknown import",
Self::IncompatibleImportType { .. } => "incompatible import type",
}
}
}
impl From<LinkingError> for Error {
fn from(value: LinkingError) -> Self {
Self::Linker(value)
}
}
impl From<TwasmError> for Error {
fn from(value: TwasmError) -> Self {
Self::Twasm(value)
}
}
impl From<Trap> for Error {
fn from(value: Trap) -> Self {
Self::Trap(value)
}
}
impl Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
#[cfg(feature = "parser")]
Self::Parser(err) => write!(f, "error parsing module: {err:?}"),
#[cfg(feature = "std")]
Self::Io(err) => write!(f, "I/O error: {err}"),
Self::Twasm(err) => write!(f, "serialization error: {err}"),
Self::Trap(trap) => write!(f, "trap: {trap}"),
Self::Linker(err) => write!(f, "linking error: {err}"),
Self::InvalidLabelType => write!(f, "invalid label type"),
Self::Other(message) => write!(f, "unknown error: {message}"),
Self::UnsupportedFeature(feature) => write!(f, "unsupported feature: {feature}"),
#[cfg(feature = "debug")]
Self::InvalidHostFnReturn { expected, actual } => {
write!(f, "invalid host function return: expected={expected:?}, actual={actual:?}")
}
#[cfg(not(feature = "debug"))]
Self::InvalidHostFnReturn { .. } => write!(f, "invalid host function return"),
}
}
}
impl Display for LinkingError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::UnknownImport { module, name } => write!(f, "unknown import: {module}.{name}"),
Self::IncompatibleImportType { module, name } => {
write!(f, "incompatible import type: {module}.{name}")
}
}
}
}
impl Display for Trap {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Other(message) => write!(f, "{message}"),
Self::HostFunction(message) => write!(f, "host function trap: {message}"),
Self::Unreachable => write!(f, "unreachable"),
Self::MemoryOutOfBounds { offset, len, max } => {
write!(f, "out of bounds memory access: offset={offset}, len={len}, max={max}")
}
Self::TableOutOfBounds { offset, len, max } => {
write!(f, "out of bounds table access: offset={offset}, len={len}, max={max}")
}
Self::DivisionByZero => write!(f, "integer divide by zero"),
Self::InvalidConversionToInt => write!(f, "invalid conversion to integer"),
Self::IntegerOverflow => write!(f, "integer overflow"),
Self::CallStackOverflow => write!(f, "call stack exhausted"),
Self::ValueStackOverflow => write!(f, "value stack exhausted"),
Self::OutOfMemory => write!(f, "out of memory"),
Self::UndefinedElement { index } => write!(f, "undefined element: index={index}"),
Self::UninitializedElement { index } => {
write!(f, "uninitialized element: index={index}")
}
Self::InvalidStore => write!(f, "invalid store"),
#[cfg(feature = "debug")]
Self::IndirectCallTypeMismatch { expected, actual } => {
write!(f, "indirect call type mismatch: expected={expected:?}, actual={actual:?}")
}
#[cfg(not(feature = "debug"))]
Self::IndirectCallTypeMismatch { .. } => write!(f, "indirect call type mismatch"),
}
}
}
impl Debug for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self)
}
}
impl core::error::Error for Error {}
#[cfg(feature = "std")]
impl From<Error> for crate::std::io::Error {
fn from(value: Error) -> Self {
match value {
Error::Io(err) => err,
other => Self::other(other.to_string()),
}
}
}
#[cfg(feature = "parser")]
impl From<tinywasm_parser::ParseError> for Error {
fn from(value: tinywasm_parser::ParseError) -> Self {
Self::Parser(value)
}
}
pub type Result<T, E = Error> = crate::std::result::Result<T, E>;