1 //! Trap codes describing the reason for a trap.
2 
3 use core::fmt::{self, Display, Formatter};
4 use core::str::FromStr;
5 #[cfg(feature = "enable-serde")]
6 use serde::{Deserialize, Serialize};
7 
8 /// A trap code describing the reason for a trap.
9 ///
10 /// All trap instructions have an explicit trap code.
11 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
12 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
13 pub enum TrapCode {
14     /// The current stack space was exhausted.
15     StackOverflow,
16 
17     /// A `heap_addr` instruction detected an out-of-bounds error.
18     ///
19     /// Note that not all out-of-bounds heap accesses are reported this way;
20     /// some are detected by a segmentation fault on the heap unmapped or
21     /// offset-guard pages.
22     HeapOutOfBounds,
23 
24     /// A wasm atomic operation was presented with a not-naturally-aligned linear-memory address.
25     HeapMisaligned,
26 
27     /// A `table_addr` instruction detected an out-of-bounds error.
28     TableOutOfBounds,
29 
30     /// Indirect call to a null table entry.
31     IndirectCallToNull,
32 
33     /// Signature mismatch on indirect call.
34     BadSignature,
35 
36     /// An integer arithmetic operation caused an overflow.
37     IntegerOverflow,
38 
39     /// An integer division by zero.
40     IntegerDivisionByZero,
41 
42     /// Failed float-to-int conversion.
43     BadConversionToInteger,
44 
45     /// Code that was supposed to have been unreachable was reached.
46     UnreachableCodeReached,
47 
48     /// Execution has potentially run too long and may be interrupted.
49     /// This trap is resumable.
50     Interrupt,
51 
52     /// A user-defined trap code.
53     User(u16),
54 }
55 
56 impl TrapCode {
57     /// Returns a slice of all traps except `TrapCode::User` traps
58     pub const fn non_user_traps() -> &'static [TrapCode] {
59         &[
60             TrapCode::StackOverflow,
61             TrapCode::HeapOutOfBounds,
62             TrapCode::HeapMisaligned,
63             TrapCode::TableOutOfBounds,
64             TrapCode::IndirectCallToNull,
65             TrapCode::BadSignature,
66             TrapCode::IntegerOverflow,
67             TrapCode::IntegerDivisionByZero,
68             TrapCode::BadConversionToInteger,
69             TrapCode::UnreachableCodeReached,
70             TrapCode::Interrupt,
71         ]
72     }
73 }
74 
75 impl Display for TrapCode {
76     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
77         use self::TrapCode::*;
78         let identifier = match *self {
79             StackOverflow => "stk_ovf",
80             HeapOutOfBounds => "heap_oob",
81             HeapMisaligned => "heap_misaligned",
82             TableOutOfBounds => "table_oob",
83             IndirectCallToNull => "icall_null",
84             BadSignature => "bad_sig",
85             IntegerOverflow => "int_ovf",
86             IntegerDivisionByZero => "int_divz",
87             BadConversionToInteger => "bad_toint",
88             UnreachableCodeReached => "unreachable",
89             Interrupt => "interrupt",
90             User(x) => return write!(f, "user{}", x),
91         };
92         f.write_str(identifier)
93     }
94 }
95 
96 impl FromStr for TrapCode {
97     type Err = ();
98 
99     fn from_str(s: &str) -> Result<Self, Self::Err> {
100         use self::TrapCode::*;
101         match s {
102             "stk_ovf" => Ok(StackOverflow),
103             "heap_oob" => Ok(HeapOutOfBounds),
104             "heap_misaligned" => Ok(HeapMisaligned),
105             "table_oob" => Ok(TableOutOfBounds),
106             "icall_null" => Ok(IndirectCallToNull),
107             "bad_sig" => Ok(BadSignature),
108             "int_ovf" => Ok(IntegerOverflow),
109             "int_divz" => Ok(IntegerDivisionByZero),
110             "bad_toint" => Ok(BadConversionToInteger),
111             "unreachable" => Ok(UnreachableCodeReached),
112             "interrupt" => Ok(Interrupt),
113             _ if s.starts_with("user") => s[4..].parse().map(User).map_err(|_| ()),
114             _ => Err(()),
115         }
116     }
117 }
118 
119 #[cfg(test)]
120 mod tests {
121     use super::*;
122     use alloc::string::ToString;
123 
124     #[test]
125     fn display() {
126         for r in TrapCode::non_user_traps() {
127             let tc = *r;
128             assert_eq!(tc.to_string().parse(), Ok(tc));
129         }
130         assert_eq!("bogus".parse::<TrapCode>(), Err(()));
131 
132         assert_eq!(TrapCode::User(17).to_string(), "user17");
133         assert_eq!("user22".parse(), Ok(TrapCode::User(22)));
134         assert_eq!("user".parse::<TrapCode>(), Err(()));
135         assert_eq!("user-1".parse::<TrapCode>(), Err(()));
136         assert_eq!("users".parse::<TrapCode>(), Err(()));
137     }
138 }
139