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