1 //! Trap codes describing the reason for a trap.
2 
3 use core::fmt::{self, Display, Formatter};
4 use core::num::NonZeroU8;
5 use core::str::FromStr;
6 #[cfg(feature = "enable-serde")]
7 use serde_derive::{Deserialize, Serialize};
8 
9 /// A trap code describing the reason for a trap.
10 ///
11 /// All trap instructions have an explicit trap code.
12 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
13 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
14 pub struct TrapCode(NonZeroU8);
15 
16 impl TrapCode {
17     /// Number of reserved opcodes for Cranelift itself. This number of traps are
18     /// defined below starting at the high end of the byte space (e.g. 255, 254,
19     /// ...)
20     const RESERVED: u8 = 5;
21     const RESERVED_START: u8 = u8::MAX - Self::RESERVED + 1;
22 
23     /// Internal helper to create new reserved trap codes.
24     const fn reserved(byte: u8) -> TrapCode {
25         if let Some(code) = byte.checked_add(Self::RESERVED_START) {
26             if let Some(nz) = NonZeroU8::new(code) {
27                 return TrapCode(nz);
28             }
29         }
30         panic!("invalid reserved opcode")
31     }
32 
33     /// The current stack space was exhausted.
34     pub const STACK_OVERFLOW: TrapCode = TrapCode::reserved(0);
35     /// An integer arithmetic operation caused an overflow.
36     pub const INTEGER_OVERFLOW: TrapCode = TrapCode::reserved(1);
37     /// A `heap_addr` instruction detected an out-of-bounds error.
38     ///
39     /// Note that not all out-of-bounds heap accesses are reported this way;
40     /// some are detected by a segmentation fault on the heap unmapped or
41     /// offset-guard pages.
42     pub const HEAP_OUT_OF_BOUNDS: TrapCode = TrapCode::reserved(2);
43 
44     /// An integer division by zero.
45     pub const INTEGER_DIVISION_BY_ZERO: TrapCode = TrapCode::reserved(3);
46 
47     /// Failed float-to-int conversion.
48     pub const BAD_CONVERSION_TO_INTEGER: TrapCode = TrapCode::reserved(4);
49 
50     /// Create a user-defined trap code.
51     ///
52     /// Returns `None` if `code` is zero or too large and is reserved by
53     /// Cranelift.
54     pub const fn user(code: u8) -> Option<TrapCode> {
55         if code >= Self::RESERVED_START {
56             return None;
57         }
58         match NonZeroU8::new(code) {
59             Some(nz) => Some(TrapCode(nz)),
60             None => None,
61         }
62     }
63 
64     /// Alias for [`TrapCode::user`] with a panic built-in.
65     pub const fn unwrap_user(code: u8) -> TrapCode {
66         match TrapCode::user(code) {
67             Some(code) => code,
68             None => panic!("invalid user trap code"),
69         }
70     }
71 
72     /// Returns the raw byte representing this trap.
73     pub const fn as_raw(&self) -> NonZeroU8 {
74         self.0
75     }
76 
77     /// Creates a trap code from its raw byte, likely returned by
78     /// [`TrapCode::as_raw`] previously.
79     pub const fn from_raw(byte: NonZeroU8) -> TrapCode {
80         TrapCode(byte)
81     }
82 
83     /// Returns a slice of all traps except `TrapCode::User` traps
84     pub const fn non_user_traps() -> &'static [TrapCode] {
85         &[
86             TrapCode::STACK_OVERFLOW,
87             TrapCode::HEAP_OUT_OF_BOUNDS,
88             TrapCode::INTEGER_OVERFLOW,
89             TrapCode::INTEGER_DIVISION_BY_ZERO,
90             TrapCode::BAD_CONVERSION_TO_INTEGER,
91         ]
92     }
93 }
94 
95 impl Display for TrapCode {
96     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
97         let identifier = match *self {
98             Self::STACK_OVERFLOW => "stk_ovf",
99             Self::HEAP_OUT_OF_BOUNDS => "heap_oob",
100             Self::INTEGER_OVERFLOW => "int_ovf",
101             Self::INTEGER_DIVISION_BY_ZERO => "int_divz",
102             Self::BAD_CONVERSION_TO_INTEGER => "bad_toint",
103             TrapCode(x) => return write!(f, "user{x}"),
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         match s {
114             "stk_ovf" => Ok(Self::STACK_OVERFLOW),
115             "heap_oob" => Ok(Self::HEAP_OUT_OF_BOUNDS),
116             "int_ovf" => Ok(Self::INTEGER_OVERFLOW),
117             "int_divz" => Ok(Self::INTEGER_DIVISION_BY_ZERO),
118             "bad_toint" => Ok(Self::BAD_CONVERSION_TO_INTEGER),
119             _ if s.starts_with("user") => {
120                 let num = s[4..].parse().map_err(|_| ())?;
121                 TrapCode::user(num).ok_or(())
122             }
123             _ => Err(()),
124         }
125     }
126 }
127 
128 #[cfg(test)]
129 mod tests {
130     use super::*;
131     use alloc::string::ToString;
132 
133     #[test]
134     fn display() {
135         for r in TrapCode::non_user_traps() {
136             let tc = *r;
137             assert_eq!(tc.to_string().parse(), Ok(tc));
138         }
139         assert_eq!("bogus".parse::<TrapCode>(), Err(()));
140 
141         assert_eq!(TrapCode::unwrap_user(17).to_string(), "user17");
142         assert_eq!("user22".parse(), Ok(TrapCode::unwrap_user(22)));
143         assert_eq!("user".parse::<TrapCode>(), Err(()));
144         assert_eq!("user-1".parse::<TrapCode>(), Err(()));
145         assert_eq!("users".parse::<TrapCode>(), Err(()));
146     }
147 }
148