1747ad3c4Slazypassion //! Trap codes describing the reason for a trap.
2747ad3c4Slazypassion 
3747ad3c4Slazypassion use core::fmt::{self, Display, Formatter};
4*9fc41baeSAlex Crichton use core::num::NonZeroU8;
5747ad3c4Slazypassion use core::str::FromStr;
61431ab52SArtur Jamro #[cfg(feature = "enable-serde")]
79ec02f9dSChristopher Serr use serde_derive::{Deserialize, Serialize};
8747ad3c4Slazypassion 
9747ad3c4Slazypassion /// A trap code describing the reason for a trap.
10747ad3c4Slazypassion ///
11747ad3c4Slazypassion /// All trap instructions have an explicit trap code.
12747ad3c4Slazypassion #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
131431ab52SArtur Jamro #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
14*9fc41baeSAlex Crichton pub struct TrapCode(NonZeroU8);
15747ad3c4Slazypassion 
16*9fc41baeSAlex Crichton impl TrapCode {
17*9fc41baeSAlex Crichton     /// Number of reserved opcodes for Cranelift itself. This number of traps are
18*9fc41baeSAlex Crichton     /// defined below starting at the high end of the byte space (e.g. 255, 254,
19*9fc41baeSAlex Crichton     /// ...)
20*9fc41baeSAlex Crichton     const RESERVED: u8 = 5;
21*9fc41baeSAlex Crichton     const RESERVED_START: u8 = u8::MAX - Self::RESERVED + 1;
22*9fc41baeSAlex Crichton 
23*9fc41baeSAlex Crichton     /// Internal helper to create new reserved trap codes.
reserved(byte: u8) -> TrapCode24*9fc41baeSAlex Crichton     const fn reserved(byte: u8) -> TrapCode {
25*9fc41baeSAlex Crichton         if let Some(code) = byte.checked_add(Self::RESERVED_START) {
26*9fc41baeSAlex Crichton             if let Some(nz) = NonZeroU8::new(code) {
27*9fc41baeSAlex Crichton                 return TrapCode(nz);
28*9fc41baeSAlex Crichton             }
29*9fc41baeSAlex Crichton         }
30*9fc41baeSAlex Crichton         panic!("invalid reserved opcode")
31*9fc41baeSAlex Crichton     }
32*9fc41baeSAlex Crichton 
33*9fc41baeSAlex Crichton     /// The current stack space was exhausted.
34*9fc41baeSAlex Crichton     pub const STACK_OVERFLOW: TrapCode = TrapCode::reserved(0);
35*9fc41baeSAlex Crichton     /// An integer arithmetic operation caused an overflow.
36*9fc41baeSAlex Crichton     pub const INTEGER_OVERFLOW: TrapCode = TrapCode::reserved(1);
37747ad3c4Slazypassion     /// A `heap_addr` instruction detected an out-of-bounds error.
38747ad3c4Slazypassion     ///
39747ad3c4Slazypassion     /// Note that not all out-of-bounds heap accesses are reported this way;
40747ad3c4Slazypassion     /// some are detected by a segmentation fault on the heap unmapped or
41747ad3c4Slazypassion     /// offset-guard pages.
42*9fc41baeSAlex Crichton     pub const HEAP_OUT_OF_BOUNDS: TrapCode = TrapCode::reserved(2);
43747ad3c4Slazypassion 
44747ad3c4Slazypassion     /// An integer division by zero.
45*9fc41baeSAlex Crichton     pub const INTEGER_DIVISION_BY_ZERO: TrapCode = TrapCode::reserved(3);
46747ad3c4Slazypassion 
47747ad3c4Slazypassion     /// Failed float-to-int conversion.
48*9fc41baeSAlex Crichton     pub const BAD_CONVERSION_TO_INTEGER: TrapCode = TrapCode::reserved(4);
49747ad3c4Slazypassion 
50*9fc41baeSAlex Crichton     /// Create a user-defined trap code.
51*9fc41baeSAlex Crichton     ///
52*9fc41baeSAlex Crichton     /// Returns `None` if `code` is zero or too large and is reserved by
53*9fc41baeSAlex Crichton     /// Cranelift.
user(code: u8) -> Option<TrapCode>54*9fc41baeSAlex Crichton     pub const fn user(code: u8) -> Option<TrapCode> {
55*9fc41baeSAlex Crichton         if code >= Self::RESERVED_START {
56*9fc41baeSAlex Crichton             return None;
57*9fc41baeSAlex Crichton         }
58*9fc41baeSAlex Crichton         match NonZeroU8::new(code) {
59*9fc41baeSAlex Crichton             Some(nz) => Some(TrapCode(nz)),
60*9fc41baeSAlex Crichton             None => None,
61*9fc41baeSAlex Crichton         }
62747ad3c4Slazypassion     }
63747ad3c4Slazypassion 
64*9fc41baeSAlex Crichton     /// Alias for [`TrapCode::user`] with a panic built-in.
unwrap_user(code: u8) -> TrapCode65*9fc41baeSAlex Crichton     pub const fn unwrap_user(code: u8) -> TrapCode {
66*9fc41baeSAlex Crichton         match TrapCode::user(code) {
67*9fc41baeSAlex Crichton             Some(code) => code,
68*9fc41baeSAlex Crichton             None => panic!("invalid user trap code"),
69*9fc41baeSAlex Crichton         }
70*9fc41baeSAlex Crichton     }
71*9fc41baeSAlex Crichton 
72*9fc41baeSAlex Crichton     /// Returns the raw byte representing this trap.
as_raw(&self) -> NonZeroU873*9fc41baeSAlex Crichton     pub const fn as_raw(&self) -> NonZeroU8 {
74*9fc41baeSAlex Crichton         self.0
75*9fc41baeSAlex Crichton     }
76*9fc41baeSAlex Crichton 
77*9fc41baeSAlex Crichton     /// Creates a trap code from its raw byte, likely returned by
78*9fc41baeSAlex Crichton     /// [`TrapCode::as_raw`] previously.
from_raw(byte: NonZeroU8) -> TrapCode79*9fc41baeSAlex Crichton     pub const fn from_raw(byte: NonZeroU8) -> TrapCode {
80*9fc41baeSAlex Crichton         TrapCode(byte)
81*9fc41baeSAlex Crichton     }
82*9fc41baeSAlex Crichton 
8365a3af72SAfonso Bordado     /// Returns a slice of all traps except `TrapCode::User` traps
non_user_traps() -> &'static [TrapCode]8465a3af72SAfonso Bordado     pub const fn non_user_traps() -> &'static [TrapCode] {
8565a3af72SAfonso Bordado         &[
86*9fc41baeSAlex Crichton             TrapCode::STACK_OVERFLOW,
87*9fc41baeSAlex Crichton             TrapCode::HEAP_OUT_OF_BOUNDS,
88*9fc41baeSAlex Crichton             TrapCode::INTEGER_OVERFLOW,
89*9fc41baeSAlex Crichton             TrapCode::INTEGER_DIVISION_BY_ZERO,
90*9fc41baeSAlex Crichton             TrapCode::BAD_CONVERSION_TO_INTEGER,
9165a3af72SAfonso Bordado         ]
9265a3af72SAfonso Bordado     }
9365a3af72SAfonso Bordado }
9465a3af72SAfonso Bordado 
95747ad3c4Slazypassion impl Display for TrapCode {
fmt(&self, f: &mut Formatter) -> fmt::Result96747ad3c4Slazypassion     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
97747ad3c4Slazypassion         let identifier = match *self {
98*9fc41baeSAlex Crichton             Self::STACK_OVERFLOW => "stk_ovf",
99*9fc41baeSAlex Crichton             Self::HEAP_OUT_OF_BOUNDS => "heap_oob",
100*9fc41baeSAlex Crichton             Self::INTEGER_OVERFLOW => "int_ovf",
101*9fc41baeSAlex Crichton             Self::INTEGER_DIVISION_BY_ZERO => "int_divz",
102*9fc41baeSAlex Crichton             Self::BAD_CONVERSION_TO_INTEGER => "bad_toint",
103*9fc41baeSAlex Crichton             TrapCode(x) => return write!(f, "user{x}"),
104747ad3c4Slazypassion         };
105747ad3c4Slazypassion         f.write_str(identifier)
106747ad3c4Slazypassion     }
107747ad3c4Slazypassion }
108747ad3c4Slazypassion 
109747ad3c4Slazypassion impl FromStr for TrapCode {
110747ad3c4Slazypassion     type Err = ();
111747ad3c4Slazypassion 
from_str(s: &str) -> Result<Self, Self::Err>112747ad3c4Slazypassion     fn from_str(s: &str) -> Result<Self, Self::Err> {
113747ad3c4Slazypassion         match s {
114*9fc41baeSAlex Crichton             "stk_ovf" => Ok(Self::STACK_OVERFLOW),
115*9fc41baeSAlex Crichton             "heap_oob" => Ok(Self::HEAP_OUT_OF_BOUNDS),
116*9fc41baeSAlex Crichton             "int_ovf" => Ok(Self::INTEGER_OVERFLOW),
117*9fc41baeSAlex Crichton             "int_divz" => Ok(Self::INTEGER_DIVISION_BY_ZERO),
118*9fc41baeSAlex Crichton             "bad_toint" => Ok(Self::BAD_CONVERSION_TO_INTEGER),
119*9fc41baeSAlex Crichton             _ if s.starts_with("user") => {
120*9fc41baeSAlex Crichton                 let num = s[4..].parse().map_err(|_| ())?;
121*9fc41baeSAlex Crichton                 TrapCode::user(num).ok_or(())
122*9fc41baeSAlex Crichton             }
123747ad3c4Slazypassion             _ => Err(()),
124747ad3c4Slazypassion         }
125747ad3c4Slazypassion     }
126747ad3c4Slazypassion }
127747ad3c4Slazypassion 
128747ad3c4Slazypassion #[cfg(test)]
129747ad3c4Slazypassion mod tests {
130747ad3c4Slazypassion     use super::*;
13110e226f9Sbjorn3     use alloc::string::ToString;
132747ad3c4Slazypassion 
133747ad3c4Slazypassion     #[test]
display()134747ad3c4Slazypassion     fn display() {
13565a3af72SAfonso Bordado         for r in TrapCode::non_user_traps() {
136747ad3c4Slazypassion             let tc = *r;
137747ad3c4Slazypassion             assert_eq!(tc.to_string().parse(), Ok(tc));
138747ad3c4Slazypassion         }
139747ad3c4Slazypassion         assert_eq!("bogus".parse::<TrapCode>(), Err(()));
140747ad3c4Slazypassion 
141*9fc41baeSAlex Crichton         assert_eq!(TrapCode::unwrap_user(17).to_string(), "user17");
142*9fc41baeSAlex Crichton         assert_eq!("user22".parse(), Ok(TrapCode::unwrap_user(22)));
143747ad3c4Slazypassion         assert_eq!("user".parse::<TrapCode>(), Err(()));
144747ad3c4Slazypassion         assert_eq!("user-1".parse::<TrapCode>(), Err(()));
145747ad3c4Slazypassion         assert_eq!("users".parse::<TrapCode>(), Err(()));
146747ad3c4Slazypassion     }
147747ad3c4Slazypassion }
148