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