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 #[deprecated(since = "2.1.1", note = "return a Result from host functions instead")]
111 pub fn raise(error: Box<dyn Error + Send + Sync>) -> ! {
112 unsafe { raise_user_trap(error) }
113 }
114
115 pub fn user(error: Box<dyn Error + Send + Sync>) -> Self {
120 match error.downcast::<Self>() {
121 Ok(runtime_error) => *runtime_error,
123 Err(error) => {
124 let info = FRAME_INFO.read().unwrap();
125 Self::new_with_trace(
126 &info,
127 None,
128 RuntimeErrorSource::User(error),
129 Backtrace::new_unresolved(),
130 )
131 }
132 }
133 }
134
135 fn new_with_trace(
136 info: &GlobalFrameInfo,
137 trap_pc: Option<usize>,
138 source: RuntimeErrorSource,
139 native_trace: Backtrace,
140 ) -> Self {
141 let frames: Vec<usize> = native_trace
142 .frames()
143 .iter()
144 .filter_map(|frame| {
145 let pc = frame.ip() as usize;
146 if pc == 0 {
147 None
148 } else {
149 let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 };
160 Some(pc_to_lookup)
161 }
162 })
163 .collect();
164
165 let wasm_trace = frames
167 .into_iter()
168 .filter_map(|pc| info.lookup_frame_info(pc))
169 .collect::<Vec<_>>();
170
171 Self {
172 inner: Arc::new(RuntimeErrorInner {
173 source,
174 wasm_trace,
175 native_trace,
176 }),
177 }
178 }
179
180 pub fn message(&self) -> String {
182 self.inner.source.to_string()
183 }
184
185 pub fn trace(&self) -> &[FrameInfo] {
188 &self.inner.wasm_trace
189 }
190
191 pub fn downcast<T: Error + 'static>(self) -> Result<T, Self> {
193 match Arc::try_unwrap(self.inner) {
194 Ok(RuntimeErrorInner {
196 source: RuntimeErrorSource::User(err),
197 ..
198 }) if err.is::<T>() => Ok(*err.downcast::<T>().unwrap()),
199 Ok(inner) => Err(Self {
200 inner: Arc::new(inner),
201 }),
202 Err(inner) => Err(Self { inner }),
203 }
204 }
205
206 pub fn to_trap(self) -> Option<TrapCode> {
208 if let RuntimeErrorSource::Trap(trap_code) = self.inner.source {
209 Some(trap_code)
210 } else {
211 None
212 }
213 }
214
215 pub fn is<T: Error + 'static>(&self) -> bool {
217 match &self.inner.source {
218 RuntimeErrorSource::User(err) => err.is::<T>(),
219 _ => false,
220 }
221 }
222}
223
224impl fmt::Debug for RuntimeError {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 f.debug_struct("RuntimeError")
227 .field("source", &self.inner.source)
228 .field("wasm_trace", &self.inner.wasm_trace)
229 .field("native_trace", &self.inner.native_trace)
230 .finish()
231 }
232}
233
234impl fmt::Display for RuntimeError {
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 write!(f, "RuntimeError: {}", self.message())?;
237 let trace = self.trace();
238 if trace.is_empty() {
239 return Ok(());
240 }
241 for frame in self.trace().iter() {
242 let name = frame.module_name();
243 let func_index = frame.func_index();
244 writeln!(f)?;
245 write!(f, " at ")?;
246 match frame.function_name() {
247 Some(name) => match rustc_demangle::try_demangle(name) {
248 Ok(name) => write!(f, "{}", name)?,
249 Err(_) => write!(f, "{}", name)?,
250 },
251 None => write!(f, "<unnamed>")?,
252 }
253 write!(
254 f,
255 " ({}[{}]:0x{:x})",
256 name,
257 func_index,
258 frame.module_offset()
259 )?;
260 }
261 Ok(())
262 }
263}
264
265impl std::error::Error for RuntimeError {
266 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
267 match &self.inner.source {
268 RuntimeErrorSource::User(err) => Some(&**err),
269 RuntimeErrorSource::Trap(err) => Some(err),
270 _ => None,
271 }
272 }
273}
274
275impl From<Trap> for RuntimeError {
276 fn from(trap: Trap) -> Self {
277 Self::from_trap(trap)
278 }
279}