1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use crate::core::indices::GlobalIdx;
use crate::validation_stack::LabelKind;
use core::fmt::{Display, Formatter};
use core::str::Utf8Error;

use crate::core::reader::section_header::SectionTy;
use crate::core::reader::types::ValType;

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum RuntimeError {
    DivideBy0,
    UnrepresentableResult,
    FunctionNotFound,
    StackSmash,
    // https://github.com/wasmi-labs/wasmi/blob/37d1449524a322817c55026eb21eb97dd693b9ce/crates/core/src/trap.rs#L265C5-L265C27
    BadConversionToInteger,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Error {
    /// The magic number at the very start of the given WASM file is invalid.
    InvalidMagic,
    InvalidVersion,
    MalformedUtf8String(Utf8Error),
    Eof,
    InvalidSectionType(u8),
    SectionOutOfOrder(SectionTy),
    InvalidNumType,
    InvalidVecType,
    InvalidFuncType,
    InvalidRefType,
    InvalidValType,
    InvalidExportDesc(u8),
    InvalidImportDesc(u8),
    ExprMissingEnd,
    InvalidInstr(u8),
    InvalidMultiByteInstr(u8, u8),
    EndInvalidValueStack,
    InvalidLocalIdx,
    InvalidValidationStackValType(Option<ValType>),
    InvalidLimitsType(u8),
    InvalidMutType(u8),
    MoreThanOneMemory,
    InvalidGlobalIdx(GlobalIdx),
    GlobalIsConst,
    RuntimeError(RuntimeError),
    FoundLabel(LabelKind),
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        match self {
            Error::InvalidMagic => {
                f.write_str("The magic number at the very start of the given WASM file is invalid.")
            }
            Error::InvalidVersion => f.write_str("The version in the WASM file header is invalid"),
            Error::MalformedUtf8String(err) => f.write_fmt(format_args!(
                "A name could not be parsed as it was invalid UTF8: {err}"
            )),
            Error::Eof => f.write_str(
                "A value was expected in the WASM binary but the end of file was reached instead",
            ),
            Error::InvalidSectionType(ty) => f.write_fmt(format_args!(
                "An invalid section type id was found in a section header: {ty}"
            )),
            Error::SectionOutOfOrder(ty) => {
                f.write_fmt(format_args!("The section {ty:?} is out of order"))
            }
            Error::InvalidNumType => {
                f.write_str("An invalid byte was read where a numtype was expected")
            }
            Error::InvalidVecType => {
                f.write_str("An invalid byte was read where a vectype was expected")
            }
            Error::InvalidFuncType => {
                f.write_str("An invalid byte was read where a functype was expected")
            }
            Error::InvalidRefType => {
                f.write_str("An invalid byte was read where a reftype was expected")
            }
            Error::InvalidValType => {
                f.write_str("An invalid byte was read where a valtype was expected")
            }
            Error::InvalidExportDesc(byte) => f.write_fmt(format_args!(
                "An invalid byte `{byte:#x?}` was read where an exportdesc was expected"
            )),
            Error::InvalidImportDesc(byte) => f.write_fmt(format_args!(
                "An invalid byte `{byte:#x?}` was read where an importdesc was expected"
            )),
            Error::ExprMissingEnd => f.write_str("An expr is missing an end byte"),
            Error::InvalidInstr(byte) => f.write_fmt(format_args!(
                "An invalid instruction opcode was found: `{byte:#x?}`"
            )),
            Error::InvalidMultiByteInstr(byte1, byte2) => f.write_fmt(format_args!(
                "An invalid multi-byte instruction opcode was found: `{byte1:#x?} {byte2:#x?}`"
            )),
            Error::EndInvalidValueStack => f.write_str(
                "Different value stack types were expected at the end of a block/function.",
            ),
            Error::InvalidLocalIdx => f.write_str("An invalid localidx was used"),
            Error::InvalidValidationStackValType(ty) => f.write_fmt(format_args!(
                "An unexpected type was found on the stack when trying to pop another: `{ty:?}`"
            )),
            Error::InvalidLimitsType(ty) => {
                f.write_fmt(format_args!("An invalid limits type was found: {ty:#x?}"))
            }
            Error::InvalidMutType(byte) => f.write_fmt(format_args!(
                "An invalid mut/const byte was found: {byte:#x?}"
            )),
            Error::MoreThanOneMemory => {
                f.write_str("As of not only one memory is allowed per module.")
            }
            Error::InvalidGlobalIdx(idx) => f.write_fmt(format_args!(
                "An invalid global index `{idx}` was specified"
            )),
            Error::GlobalIsConst => f.write_str("A const global cannot be written to"),
            Error::RuntimeError(err) => err.fmt(f),
            Error::FoundLabel(lk) => f.write_fmt(format_args!(
                "Expecting a ValType, a Label was found: {lk:?}"
            )),
        }
    }
}

impl Display for RuntimeError {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        match self {
            RuntimeError::DivideBy0 => f.write_str("Divide by zero is not permitted"),
            RuntimeError::UnrepresentableResult => f.write_str("Result is unrepresentable"),
            RuntimeError::FunctionNotFound => f.write_str("Function not found"),
            RuntimeError::StackSmash => f.write_str("Stack smashed"),
            RuntimeError::BadConversionToInteger => f.write_str("Bad conversion to integer"),
        }
    }
}

pub type Result<T> = core::result::Result<T, Error>;

impl From<RuntimeError> for Error {
    fn from(value: RuntimeError) -> Self {
        Self::RuntimeError(value)
    }
}