use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ByteOffset(pub usize);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecodeContext {
Magic,
Version,
SectionHeader,
SectionBody { id: u8 },
Leb128,
TypeSection,
ImportSection,
FunctionSection,
TableSection,
GlobalSection,
MemorySection,
ExportSection,
StartSection,
ElementSection,
DataSection,
DataCountSection,
CodeSection,
}
impl fmt::Display for DecodeContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DecodeContext::Magic => write!(f, "WASM magic number"),
DecodeContext::Version => write!(f, "WASM version"),
DecodeContext::SectionHeader => write!(f, "section header"),
DecodeContext::SectionBody { id } => write!(f, "section body (id={id})"),
DecodeContext::Leb128 => write!(f, "LEB128 value"),
DecodeContext::TypeSection => write!(f, "type section"),
DecodeContext::ImportSection => write!(f, "import section"),
DecodeContext::FunctionSection => write!(f, "function section"),
DecodeContext::TableSection => write!(f, "table section"),
DecodeContext::GlobalSection => write!(f, "global section"),
DecodeContext::MemorySection => write!(f, "memory section"),
DecodeContext::ExportSection => write!(f, "export section"),
DecodeContext::StartSection => write!(f, "start section"),
DecodeContext::ElementSection => write!(f, "element section"),
DecodeContext::DataSection => write!(f, "data section"),
DecodeContext::DataCountSection => write!(f, "data count section"),
DecodeContext::CodeSection => write!(f, "code section"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DecodeError {
pub offset: ByteOffset,
pub context: DecodeContext,
pub kind: DecodeErrorKind,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DecodeErrorKind {
UnexpectedEof,
InvalidMagic,
UnsupportedVersion { found: u32 },
Leb128TooLong,
Leb128Overflow,
UnknownSectionId { id: u8 },
SectionOverflow,
SectionOutOfOrder { prev: u8, current: u8 },
DuplicateSection { id: u8 },
UnknownValType { byte: u8 },
UnknownRefType { byte: u8 },
UnknownImportDesc { byte: u8 },
UnknownExportDesc { byte: u8 },
InvalidMutability { byte: u8 },
InvalidUtf8,
FunctionCodeLengthMismatch { functions: u32, codes: u32 },
TooManyLocals,
UnknownOpcode { byte: u8 },
UnknownSimdOpcode { opcode: u32 },
UnexpectedByte { expected: u8, found: u8 },
SectionSizeMismatch { expected: u32, consumed: u32 },
}
impl fmt::Display for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"decode error at byte {}: {}: {}",
self.offset.0, self.context, self.kind
)
}
}
impl fmt::Display for DecodeErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DecodeErrorKind::UnexpectedEof => write!(f, "unexpected end of input"),
DecodeErrorKind::InvalidMagic => write!(f, "invalid magic number (expected \\0asm)"),
DecodeErrorKind::UnsupportedVersion { found } => {
write!(f, "unsupported WASM version {found} (expected 1)")
}
DecodeErrorKind::Leb128TooLong => write!(f, "LEB128 encoding too long"),
DecodeErrorKind::TooManyLocals => write!(f, "too many locals"),
DecodeErrorKind::Leb128Overflow => write!(f, "LEB128 overflow (unused bits set)"),
DecodeErrorKind::UnknownSectionId { id } => {
write!(f, "unknown section ID {id:#04x}")
}
DecodeErrorKind::SectionOverflow => {
write!(f, "section extends beyond end of binary")
}
DecodeErrorKind::SectionOutOfOrder { prev, current } => {
write!(
f,
"section {current} appears after section {prev} (out of order)"
)
}
DecodeErrorKind::DuplicateSection { id } => {
write!(f, "duplicate section (id={id})")
}
DecodeErrorKind::UnknownValType { byte } => {
write!(f, "unknown value type {byte:#04x}")
}
DecodeErrorKind::UnknownRefType { byte } => {
write!(f, "unknown reference type {byte:#04x}")
}
DecodeErrorKind::UnknownImportDesc { byte } => {
write!(f, "unknown import descriptor {byte:#04x}")
}
DecodeErrorKind::UnknownExportDesc { byte } => {
write!(f, "unknown export descriptor {byte:#04x}")
}
DecodeErrorKind::InvalidMutability { byte } => {
write!(f, "invalid mutability {byte:#04x}")
}
DecodeErrorKind::InvalidUtf8 => write!(f, "invalid UTF-8 string"),
DecodeErrorKind::FunctionCodeLengthMismatch { functions, codes } => {
write!(
f,
"function/code section length mismatch: {functions} declarations, {codes} bodies"
)
}
DecodeErrorKind::UnknownOpcode { byte } => {
write!(f, "unknown opcode {byte:#04x}")
}
DecodeErrorKind::UnknownSimdOpcode { opcode } => {
write!(f, "unknown SIMD opcode {opcode:#04x}")
}
DecodeErrorKind::UnexpectedByte { expected, found } => {
write!(f, "expected {expected:#04x}, found {found:#04x}")
}
DecodeErrorKind::SectionSizeMismatch { expected, consumed } => {
write!(
f,
"section size mismatch: declared {expected} bytes, consumed {consumed}"
)
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for DecodeError {}
#[cfg(not(feature = "std"))]
impl core::error::Error for DecodeError {}