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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
use crate::HostError;
use alloc::boxed::Box;
use core::fmt::{self, Display};

#[cfg(feature = "std")]
use std::error::Error as StdError;

/// Error type which can be thrown by wasm code or by host environment.
///
/// Under some conditions, wasm execution may produce a `Trap`, which immediately aborts execution.
/// Traps can't be handled by WebAssembly code, but are reported to the embedder.
#[derive(Debug)]
pub enum Trap {
    /// Traps during Wasm execution.
    Code(TrapCode),
    /// Traps and errors during host execution.
    Host(Box<dyn HostError>),
}

impl Trap {
    /// Wraps the host error in a [`Trap`].
    #[inline]
    pub fn host<U>(host_error: U) -> Self
    where
        U: HostError + Sized,
    {
        Self::Host(Box::new(host_error))
    }

    /// Returns `true` if `self` trap originating from host code.
    #[inline]
    pub fn is_host(&self) -> bool {
        matches!(self, Self::Host(_))
    }

    /// Returns the [`TrapCode`] traps originating from Wasm execution.
    #[inline]
    pub fn code(&self) -> Option<TrapCode> {
        if let Self::Code(trap_code) = self {
            return Some(*trap_code);
        }
        None
    }
}

impl From<TrapCode> for Trap {
    #[inline]
    fn from(error: TrapCode) -> Self {
        Self::Code(error)
    }
}

impl<U> From<U> for Trap
where
    U: HostError + Sized,
{
    #[inline]
    fn from(e: U) -> Self {
        Trap::host(e)
    }
}

impl Display for Trap {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Trap::Code(trap_code) => Display::fmt(trap_code, f),
            Trap::Host(host_error) => Display::fmt(host_error, f),
        }
    }
}

#[cfg(feature = "std")]
impl StdError for Trap {
    fn description(&self) -> &str {
        self.code().map(|code| code.trap_message()).unwrap_or("")
    }
}

/// Error type which can be thrown by wasm code or by host environment.
///
/// See [`Trap`] for details.
///
/// [`Trap`]: struct.Trap.html
#[derive(Debug, Copy, Clone)]
pub enum TrapCode {
    /// Wasm code executed `unreachable` opcode.
    ///
    /// `unreachable` is a special opcode which always traps upon execution.
    /// This opcode have a similar purpose as `ud2` in x86.
    Unreachable,

    /// Attempt to load or store at the address which
    /// lies outside of bounds of the memory.
    ///
    /// Since addresses are interpreted as unsigned integers, out of bounds access
    /// can't happen with negative addresses (i.e. they will always wrap).
    MemoryAccessOutOfBounds,

    /// Attempt to access table element at index which
    /// lies outside of bounds.
    ///
    /// This typically can happen when `call_indirect` is executed
    /// with index that lies out of bounds.
    ///
    /// Since indexes are interpreted as unsinged integers, out of bounds access
    /// can't happen with negative indexes (i.e. they will always wrap).
    TableAccessOutOfBounds,

    /// Attempt to access table element which is uninitialized (i.e. `None`).
    ///
    /// This typically can happen when `call_indirect` is executed.
    ElemUninitialized,

    /// Attempt to divide by zero.
    ///
    /// This trap typically can happen if `div` or `rem` is executed with
    /// zero as divider.
    DivisionByZero,

    /// An integer arithmetic operation caused an overflow.
    ///
    /// This can happen when:
    ///
    /// - Trying to do signed division (or get the remainder) -2<sup>N-1</sup> over -1. This is
    ///   because the result +2<sup>N-1</sup> isn't representable as a N-bit signed integer.
    IntegerOverflow,

    /// Attempt to make a conversion to an int failed.
    ///
    /// This can happen when:
    ///
    /// - Trying to truncate NaNs, infinity, or value for which the result is out of range into an integer.
    InvalidConversionToInt,

    /// Stack overflow.
    ///
    /// This is likely caused by some infinite or very deep recursion.
    /// Extensive inlining might also be the cause of stack overflow.
    StackOverflow,

    /// Attempt to invoke a function with mismatching signature.
    ///
    /// This can happen if a Wasm or host function was invoked
    /// with mismatching parameters or result values.
    ///
    /// This can always happen with indirect calls as they always
    /// specify the expected signature of function. If an indirect call is executed
    /// with an index that points to a function with signature different of what is
    /// expected by this indirect call, this trap is raised.
    UnexpectedSignature,
}

impl TrapCode {
    /// Returns the trap message as specified by the WebAssembly specification.
    ///
    /// # Note
    ///
    /// This API is primarily useful for the Wasm spec testsuite but might have
    /// other uses since it avoid heap memory allocation in certain cases.
    pub fn trap_message(&self) -> &'static str {
        match self {
            TrapCode::Unreachable => "unreachable",
            TrapCode::MemoryAccessOutOfBounds => "out of bounds memory access",
            TrapCode::TableAccessOutOfBounds => "undefined element",
            TrapCode::ElemUninitialized => "uninitialized element",
            TrapCode::DivisionByZero => "integer divide by zero",
            TrapCode::IntegerOverflow => "integer overflow",
            TrapCode::InvalidConversionToInt => "invalid conversion to integer",
            TrapCode::StackOverflow => "call stack exhausted",
            TrapCode::UnexpectedSignature => "indirect call type mismatch",
        }
    }
}

impl Display for TrapCode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.trap_message())
    }
}