unc_vm_engine/trap/
error.rs1use super::frame_info::{FrameInfo, GlobalFrameInfo, FRAME_INFO};
2use backtrace::Backtrace;
3use std::error::Error;
4use std::fmt;
5use std::sync::Arc;
6use unc_vm_vm::{raise_user_trap, Trap, TrapCode};
7
8#[derive(Clone)]
11pub struct RuntimeError {
12 inner: Arc<RuntimeErrorInner>,
13}
14
15#[derive(Debug)]
17enum RuntimeErrorSource {
18 Generic(String),
19 OOM,
20 User(Box<dyn Error + Send + Sync>),
21 Trap(TrapCode),
22}
23
24impl fmt::Display for RuntimeErrorSource {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match self {
27 Self::Generic(s) => write!(f, "{}", s),
28 Self::User(s) => write!(f, "{}", s),
29 Self::OOM => write!(f, "Wasmer VM out of memory"),
30 Self::Trap(s) => write!(f, "{}", s.message()),
31 }
32 }
33}
34
35struct RuntimeErrorInner {
36 source: RuntimeErrorSource,
38 wasm_trace: Vec<FrameInfo>,
40 native_trace: Backtrace,
42}
43
44fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) {
45 (t, t)
46}
47
48impl RuntimeError {
49 pub fn new<I: Into<String>>(message: I) -> Self {
57 let info = FRAME_INFO.read().unwrap();
58 let msg = message.into();
59 Self::new_with_trace(
60 &info,
61 None,
62 RuntimeErrorSource::Generic(msg),
63 Backtrace::new_unresolved(),
64 )
65 }
66
67 pub fn from_trap(trap: Trap) -> Self {
69 let info = FRAME_INFO.read().unwrap();
70 match trap {
71 Trap::User(error) => {
73 match error.downcast::<Self>() {
74 Ok(runtime_error) => *runtime_error,
76 Err(e) => Self::new_with_trace(
77 &info,
78 None,
79 RuntimeErrorSource::User(e),
80 Backtrace::new_unresolved(),
81 ),
82 }
83 }
84 Trap::OOM { backtrace } => {
86 Self::new_with_trace(&info, None, RuntimeErrorSource::OOM, backtrace)
87 }
88 Trap::Wasm { pc, signal_trap, backtrace } => {
90 let code = info
91 .lookup_trap_info(pc)
92 .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| info.trap_code);
93 Self::new_with_trace(&info, Some(pc), RuntimeErrorSource::Trap(code), backtrace)
94 }
95 Trap::Lib { trap_code, backtrace } => {
97 Self::new_with_trace(&info, None, RuntimeErrorSource::Trap(trap_code), backtrace)
98 }
99 }
100 }
101
102 pub fn raise(error: Box<dyn Error + Send + Sync>) -> ! {
104 unsafe { raise_user_trap(error) }
105 }
106
107 fn new_with_trace(
108 info: &GlobalFrameInfo,
109 trap_pc: Option<usize>,
110 source: RuntimeErrorSource,
111 native_trace: Backtrace,
112 ) -> Self {
113 let frames: Vec<usize> = native_trace
114 .frames()
115 .iter()
116 .filter_map(|frame| {
117 let pc = frame.ip() as usize;
118 if pc == 0 {
119 None
120 } else {
121 let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 };
132 Some(pc_to_lookup)
133 }
134 })
135 .collect();
136
137 let wasm_trace =
139 frames.into_iter().filter_map(|pc| info.lookup_frame_info(pc)).collect::<Vec<_>>();
140
141 Self { inner: Arc::new(RuntimeErrorInner { source, wasm_trace, native_trace }) }
142 }
143
144 pub fn message(&self) -> String {
146 self.inner.source.to_string()
147 }
148
149 pub fn trace(&self) -> &[FrameInfo] {
152 &self.inner.wasm_trace
153 }
154
155 pub fn downcast<T: Error + 'static>(self) -> Result<T, Self> {
157 match Arc::try_unwrap(self.inner) {
158 Ok(RuntimeErrorInner { source: RuntimeErrorSource::User(err), .. })
160 if err.is::<T>() =>
161 {
162 Ok(*err.downcast::<T>().unwrap())
163 }
164 Ok(inner) => Err(Self { inner: Arc::new(inner) }),
165 Err(inner) => Err(Self { inner }),
166 }
167 }
168
169 pub fn to_trap(self) -> Option<TrapCode> {
171 if let RuntimeErrorSource::Trap(trap_code) = self.inner.source {
172 Some(trap_code)
173 } else {
174 None
175 }
176 }
177
178 pub fn is<T: Error + 'static>(&self) -> bool {
180 match &self.inner.source {
181 RuntimeErrorSource::User(err) => err.is::<T>(),
182 _ => false,
183 }
184 }
185}
186
187impl fmt::Debug for RuntimeError {
188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189 f.debug_struct("RuntimeError")
190 .field("source", &self.inner.source)
191 .field("wasm_trace", &self.inner.wasm_trace)
192 .field("native_trace", &self.inner.native_trace)
193 .finish()
194 }
195}
196
197impl fmt::Display for RuntimeError {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 write!(f, "RuntimeError: {}", self.message())?;
200 let trace = self.trace();
201 if trace.is_empty() {
202 return Ok(());
203 }
204 for frame in self.trace().iter() {
205 let name = frame.module_name();
206 let func_index = frame.func_index();
207 writeln!(f)?;
208 write!(f, " at ")?;
209 match frame.function_name() {
210 Some(name) => match rustc_demangle::try_demangle(name) {
211 Ok(name) => write!(f, "{}", name)?,
212 Err(_) => write!(f, "{}", name)?,
213 },
214 None => write!(f, "<unnamed>")?,
215 }
216 write!(f, " ({}[{}]:0x{:x})", name, func_index, frame.module_offset())?;
217 }
218 Ok(())
219 }
220}
221
222impl std::error::Error for RuntimeError {
223 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
224 match &self.inner.source {
225 RuntimeErrorSource::User(err) => Some(&**err),
226 RuntimeErrorSource::Trap(err) => Some(err),
227 _ => None,
228 }
229 }
230}
231
232impl From<Trap> for RuntimeError {
233 fn from(trap: Trap) -> Self {
234 Self::from_trap(trap)
235 }
236}