1use super::frame_info::{FrameInfo, GlobalFrameInfo, FRAME_INFO};
2use backtrace::Backtrace;
3use std::error::Error;
4use std::fmt;
5use std::sync::Arc;
6use wasmer_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 {
90 pc,
91 signal_trap,
92 backtrace,
93 } => {
94 let code = info
95 .lookup_trap_info(pc)
96 .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| {
97 info.trap_code
98 });
99 Self::new_with_trace(&info, Some(pc), RuntimeErrorSource::Trap(code), backtrace)
100 }
101 Trap::Lib {
103 trap_code,
104 backtrace,
105 } => Self::new_with_trace(&info, None, RuntimeErrorSource::Trap(trap_code), backtrace),
106 }
107 }
108
109 pub fn raise(error: Box<dyn Error + Send + Sync>) -> ! {
111 unsafe { raise_user_trap(error) }
112 }
113
114 fn new_with_trace(
115 info: &GlobalFrameInfo,
116 trap_pc: Option<usize>,
117 source: RuntimeErrorSource,
118 native_trace: Backtrace,
119 ) -> Self {
120 let frames: Vec<usize> = native_trace
121 .frames()
122 .iter()
123 .filter_map(|frame| {
124 let pc = frame.ip() as usize;
125 if pc == 0 {
126 None
127 } else {
128 let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 };
139 Some(pc_to_lookup)
140 }
141 })
142 .collect();
143
144 let wasm_trace = frames
146 .into_iter()
147 .filter_map(|pc| info.lookup_frame_info(pc))
148 .collect::<Vec<_>>();
149
150 Self {
151 inner: Arc::new(RuntimeErrorInner {
152 source,
153 wasm_trace,
154 native_trace,
155 }),
156 }
157 }
158
159 pub fn message(&self) -> String {
161 self.inner.source.to_string()
162 }
163
164 pub fn trace(&self) -> &[FrameInfo] {
167 &self.inner.wasm_trace
168 }
169
170 pub fn downcast<T: Error + 'static>(self) -> Result<T, Self> {
172 match Arc::try_unwrap(self.inner) {
173 Ok(RuntimeErrorInner {
175 source: RuntimeErrorSource::User(err),
176 ..
177 }) if err.is::<T>() => Ok(*err.downcast::<T>().unwrap()),
178 Ok(inner) => Err(Self {
179 inner: Arc::new(inner),
180 }),
181 Err(inner) => Err(Self { inner }),
182 }
183 }
184
185 pub fn to_trap(self) -> Option<TrapCode> {
187 if let RuntimeErrorSource::Trap(trap_code) = self.inner.source {
188 Some(trap_code)
189 } else {
190 None
191 }
192 }
193
194 pub fn is<T: Error + 'static>(&self) -> bool {
196 match &self.inner.source {
197 RuntimeErrorSource::User(err) => err.is::<T>(),
198 _ => false,
199 }
200 }
201}
202
203impl fmt::Debug for RuntimeError {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205 f.debug_struct("RuntimeError")
206 .field("source", &self.inner.source)
207 .field("wasm_trace", &self.inner.wasm_trace)
208 .field("native_trace", &self.inner.native_trace)
209 .finish()
210 }
211}
212
213impl fmt::Display for RuntimeError {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 write!(f, "RuntimeError: {}", self.message())?;
216 let trace = self.trace();
217 if trace.is_empty() {
218 return Ok(());
219 }
220 for frame in self.trace().iter() {
221 let name = frame.module_name();
222 let func_index = frame.func_index();
223 writeln!(f)?;
224 write!(f, " at ")?;
225 match frame.function_name() {
226 Some(name) => match rustc_demangle::try_demangle(name) {
227 Ok(name) => write!(f, "{}", name)?,
228 Err(_) => write!(f, "{}", name)?,
229 },
230 None => write!(f, "<unnamed>")?,
231 }
232 write!(
233 f,
234 " ({}[{}]:0x{:x})",
235 name,
236 func_index,
237 frame.module_offset()
238 )?;
239 }
240 Ok(())
241 }
242}
243
244impl std::error::Error for RuntimeError {
245 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
246 match &self.inner.source {
247 RuntimeErrorSource::User(err) => Some(&**err),
248 RuntimeErrorSource::Trap(err) => Some(err),
249 _ => None,
250 }
251 }
252}
253
254impl From<Trap> for RuntimeError {
255 fn from(trap: Trap) -> Self {
256 Self::from_trap(trap)
257 }
258}