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_derive::{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     /// An array access attempted to index beyond its array's bounds.
31     ArrayOutOfBounds,
32 
33     /// Indirect call to a null table entry.
34     IndirectCallToNull,
35 
36     /// Signature mismatch on indirect call.
37     BadSignature,
38 
39     /// An integer arithmetic operation caused an overflow.
40     IntegerOverflow,
41 
42     /// An integer division by zero.
43     IntegerDivisionByZero,
44 
45     /// Failed float-to-int conversion.
46     BadConversionToInteger,
47 
48     /// Code that was supposed to have been unreachable was reached.
49     UnreachableCodeReached,
50 
51     /// Execution has potentially run too long and may be interrupted.
52     Interrupt,
53 
54     /// A user-defined trap code.
55     User(u16),
56 
57     /// A null reference was encountered which was required to be non-null.
58     NullReference,
59 
60     /// A requested memory allocation was too large: beyond implementation
61     /// limits, would trigger overflows, or etc...
62     AllocationTooLarge,
63 }
64 
65 impl TrapCode {
66     /// Returns a slice of all traps except `TrapCode::User` traps
67     pub const fn non_user_traps() -> &'static [TrapCode] {
68         &[
69             TrapCode::StackOverflow,
70             TrapCode::HeapOutOfBounds,
71             TrapCode::HeapMisaligned,
72             TrapCode::TableOutOfBounds,
73             TrapCode::IndirectCallToNull,
74             TrapCode::BadSignature,
75             TrapCode::IntegerOverflow,
76             TrapCode::IntegerDivisionByZero,
77             TrapCode::BadConversionToInteger,
78             TrapCode::UnreachableCodeReached,
79             TrapCode::Interrupt,
80             TrapCode::NullReference,
81         ]
82     }
83 }
84 
85 impl Display for TrapCode {
86     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
87         use self::TrapCode::*;
88         let identifier = match *self {
89             StackOverflow => "stk_ovf",
90             HeapOutOfBounds => "heap_oob",
91             HeapMisaligned => "heap_misaligned",
92             TableOutOfBounds => "table_oob",
93             IndirectCallToNull => "icall_null",
94             BadSignature => "bad_sig",
95             IntegerOverflow => "int_ovf",
96             IntegerDivisionByZero => "int_divz",
97             BadConversionToInteger => "bad_toint",
98             UnreachableCodeReached => "unreachable",
99             Interrupt => "interrupt",
100             User(x) => return write!(f, "user{x}"),
101             NullReference => "null_reference",
102             ArrayOutOfBounds => "array_oob",
103             AllocationTooLarge => "alloc_too_large",
104         };
105         f.write_str(identifier)
106     }
107 }
108 
109 impl FromStr for TrapCode {
110     type Err = ();
111 
112     fn from_str(s: &str) -> Result<Self, Self::Err> {
113         use self::TrapCode::*;
114         match s {
115             "stk_ovf" => Ok(StackOverflow),
116             "heap_oob" => Ok(HeapOutOfBounds),
117             "heap_misaligned" => Ok(HeapMisaligned),
118             "table_oob" => Ok(TableOutOfBounds),
119             "icall_null" => Ok(IndirectCallToNull),
120             "bad_sig" => Ok(BadSignature),
121             "int_ovf" => Ok(IntegerOverflow),
122             "int_divz" => Ok(IntegerDivisionByZero),
123             "bad_toint" => Ok(BadConversionToInteger),
124             "unreachable" => Ok(UnreachableCodeReached),
125             "interrupt" => Ok(Interrupt),
126             "null_reference" => Ok(NullReference),
127             "array_oob" => Ok(ArrayOutOfBounds),
128             "alloc_too_large" => Ok(AllocationTooLarge),
129             _ if s.starts_with("user") => s[4..].parse().map(User).map_err(|_| ()),
130             _ => Err(()),
131         }
132     }
133 }
134 
135 #[cfg(test)]
136 mod tests {
137     use super::*;
138     use alloc::string::ToString;
139 
140     #[test]
141     fn display() {
142         for r in TrapCode::non_user_traps() {
143             let tc = *r;
144             assert_eq!(tc.to_string().parse(), Ok(tc));
145         }
146         assert_eq!("bogus".parse::<TrapCode>(), Err(()));
147 
148         assert_eq!(TrapCode::User(17).to_string(), "user17");
149         assert_eq!("user22".parse(), Ok(TrapCode::User(22)));
150         assert_eq!("user".parse::<TrapCode>(), Err(()));
151         assert_eq!("user-1".parse::<TrapCode>(), Err(()));
152         assert_eq!("users".parse::<TrapCode>(), Err(()));
153     }
154 }
155