1use core::fmt::{self, Display, Formatter};
7use core::str::FromStr;
8use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
9#[cfg(feature = "enable-serde")]
10use serde::{Deserialize, Serialize};
11use thiserror::Error;
12
13#[derive(
17 Clone,
18 Copy,
19 PartialEq,
20 Eq,
21 Debug,
22 Hash,
23 Error,
24 RkyvSerialize,
25 RkyvDeserialize,
26 Archive,
27 num_enum::TryFromPrimitive,
28)]
29#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
30#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
31#[rkyv(derive(Debug), compare(PartialEq))]
32#[repr(u32)]
33pub enum TrapCode {
34 StackOverflow = 0,
39
40 HeapAccessOutOfBounds = 1,
46
47 HeapMisaligned = 2,
49
50 TableAccessOutOfBounds = 3,
52
53 IndirectCallToNull = 4,
55
56 BadSignature = 5,
58
59 IntegerOverflow = 6,
61
62 IntegerDivisionByZero = 7,
64
65 BadConversionToInteger = 8,
67
68 UnreachableCodeReached = 9,
70
71 UnalignedAtomic = 10,
73
74 UncaughtException = 11,
76
77 UninitializedExnRef = 12,
79
80 YieldOutsideAsyncContext = 13,
83
84 HostInterrupt = 14,
86
87 ReadonlyTableModified = 15,
89}
90
91impl TrapCode {
92 pub fn message(&self) -> &str {
94 match self {
95 Self::StackOverflow => "call stack exhausted",
96 Self::HeapAccessOutOfBounds => "out of bounds memory access",
97 Self::HeapMisaligned => "misaligned heap",
98 Self::TableAccessOutOfBounds => "undefined element: out of bounds table access",
99 Self::IndirectCallToNull => "uninitialized element",
100 Self::BadSignature => "indirect call type mismatch",
101 Self::IntegerOverflow => "integer overflow",
102 Self::IntegerDivisionByZero => "integer divide by zero",
103 Self::BadConversionToInteger => "invalid conversion to integer",
104 Self::UnreachableCodeReached => "unreachable",
105 Self::UnalignedAtomic => "unaligned atomic access",
106 Self::UncaughtException => "uncaught exception",
107 Self::UninitializedExnRef => "uninitialized exnref",
108 Self::YieldOutsideAsyncContext => {
109 "async imported function yielded when not called via `Function::call_async`"
110 }
111 Self::HostInterrupt => "interrupted by host",
112 Self::ReadonlyTableModified => "read-only table modified",
113 }
114 }
115}
116
117impl Display for TrapCode {
118 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
119 let identifier = match *self {
120 Self::StackOverflow => "stk_ovf",
121 Self::HeapAccessOutOfBounds => "heap_get_oob",
122 Self::HeapMisaligned => "heap_misaligned",
123 Self::TableAccessOutOfBounds => "table_get_oob",
124 Self::IndirectCallToNull => "icall_null",
125 Self::BadSignature => "bad_sig",
126 Self::IntegerOverflow => "int_ovf",
127 Self::IntegerDivisionByZero => "int_divz",
128 Self::BadConversionToInteger => "bad_toint",
129 Self::UnreachableCodeReached => "unreachable",
130 Self::UnalignedAtomic => "unalign_atom",
131 Self::UncaughtException => "uncaught_exception",
132 Self::UninitializedExnRef => "uninitialized_exnref",
133 Self::YieldOutsideAsyncContext => "yield_outside_async_context",
134 Self::HostInterrupt => "host_interrupt",
135 Self::ReadonlyTableModified => "readonly_table_modified",
136 };
137 f.write_str(identifier)
138 }
139}
140
141impl FromStr for TrapCode {
142 type Err = ();
143
144 fn from_str(s: &str) -> Result<Self, Self::Err> {
145 match s {
146 "stk_ovf" => Ok(Self::StackOverflow),
147 "heap_get_oob" => Ok(Self::HeapAccessOutOfBounds),
148 "heap_misaligned" => Ok(Self::HeapMisaligned),
149 "table_get_oob" => Ok(Self::TableAccessOutOfBounds),
150 "icall_null" => Ok(Self::IndirectCallToNull),
151 "bad_sig" => Ok(Self::BadSignature),
152 "int_ovf" => Ok(Self::IntegerOverflow),
153 "int_divz" => Ok(Self::IntegerDivisionByZero),
154 "bad_toint" => Ok(Self::BadConversionToInteger),
155 "unreachable" => Ok(Self::UnreachableCodeReached),
156 "unalign_atom" => Ok(Self::UnalignedAtomic),
157 "uncaught_exception" => Ok(Self::UncaughtException),
158 "uninitialized_exnref" => Ok(Self::UninitializedExnRef),
159 "yield_outside_async_context" => Ok(Self::YieldOutsideAsyncContext),
160 "host_interrupt" => Ok(Self::HostInterrupt),
161 _ => Err(()),
162 }
163 }
164}
165
166#[derive(Debug)]
170pub enum OnCalledAction {
171 InvokeAgain,
173 Finish,
175 Trap(Box<dyn std::error::Error + Send + Sync>),
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 const CODES: [TrapCode; 11] = [
185 TrapCode::StackOverflow,
186 TrapCode::HeapAccessOutOfBounds,
187 TrapCode::HeapMisaligned,
188 TrapCode::TableAccessOutOfBounds,
189 TrapCode::IndirectCallToNull,
190 TrapCode::BadSignature,
191 TrapCode::IntegerOverflow,
192 TrapCode::IntegerDivisionByZero,
193 TrapCode::BadConversionToInteger,
194 TrapCode::UnreachableCodeReached,
195 TrapCode::UnalignedAtomic,
196 ];
197
198 #[test]
199 fn display() {
200 for r in &CODES {
201 let tc = *r;
202 assert_eq!(tc.to_string().parse(), Ok(tc));
203 }
204 assert_eq!("bogus".parse::<TrapCode>(), Err(()));
205
206 assert_eq!("user".parse::<TrapCode>(), Err(()));
209 assert_eq!("user-1".parse::<TrapCode>(), Err(()));
210 assert_eq!("users".parse::<TrapCode>(), Err(()));
211 }
212}