1use std::sync::Arc;
2use thiserror::Error;
3use wasmer_types::{FrameInfo, ImportError, TrapCode};
4
5use crate::{AsStoreMut, AsStoreRef, BackendTrap as Trap, Exception, Value};
6
7#[derive(Debug, Clone)]
14#[cfg_attr(feature = "std", derive(Error))]
15#[cfg_attr(feature = "std", error("Link error: {0}"))]
16pub enum LinkError {
17 #[cfg_attr(feature = "std", error("Error while importing {0:?}.{1:?}: {2}"))]
19 Import(String, String, ImportError),
20
21 #[cfg_attr(feature = "std", error("RuntimeError occurred during linking: {0}"))]
23 Trap(#[cfg_attr(feature = "std", source)] RuntimeError),
24 #[cfg_attr(feature = "std", error("Insufficient resources: {0}"))]
26 Resource(String),
27}
28
29#[derive(Debug, Clone)]
38#[cfg_attr(feature = "std", derive(Error))]
39pub enum InstantiationError {
40 #[cfg_attr(feature = "std", error(transparent))]
42 Link(LinkError),
43
44 #[cfg_attr(feature = "std", error(transparent))]
46 Start(RuntimeError),
47
48 #[cfg_attr(feature = "std", error("missing required CPU features: {0:?}"))]
51 CpuFeature(String),
52
53 #[cfg_attr(feature = "std", error("cannot mix imports from different stores"))]
56 DifferentStores,
57
58 #[cfg_attr(feature = "std", error("incorrect OS or architecture"))]
61 DifferentArchOS,
62}
63
64#[derive(Clone)]
67pub struct RuntimeError {
68 pub(crate) inner: Arc<RuntimeErrorInner>,
69}
70
71#[derive(Debug)]
72struct RuntimeStringError {
73 details: String,
74}
75
76impl RuntimeStringError {
77 fn new(msg: String) -> Self {
78 Self { details: msg }
79 }
80}
81
82impl std::fmt::Display for RuntimeStringError {
83 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
84 write!(f, "{}", self.details)
85 }
86}
87
88impl std::error::Error for RuntimeStringError {
89 fn description(&self) -> &str {
90 &self.details
91 }
92}
93
94pub(crate) struct RuntimeErrorInner {
95 pub(crate) source: Trap,
97 trap_code: Option<TrapCode>,
99 wasm_trace: Vec<FrameInfo>,
101}
102
103impl RuntimeError {
104 pub fn new<I: Into<String>>(message: I) -> Self {
112 let msg = message.into();
113 let source = RuntimeStringError::new(msg);
114 Self::user(Box::new(source))
115 }
116
117 pub fn new_from_source(
132 source: Trap,
133 wasm_trace: Vec<FrameInfo>,
134 trap_code: Option<TrapCode>,
135 ) -> Self {
136 Self {
137 inner: Arc::new(RuntimeErrorInner {
138 source,
139 wasm_trace,
140 trap_code,
141 }),
142 }
143 }
144
145 pub fn user(error: Box<dyn std::error::Error + Send + Sync>) -> Self {
150 match error.downcast::<Self>() {
151 Ok(err) => *err,
152 Err(error) => error.into(),
153 }
154 }
155
156 pub fn exception(ctx: &impl AsStoreRef, exception: Exception) -> Self {
161 let exnref = exception.vm_exceptionref();
162 let store = ctx.as_store_ref();
163 match store.inner.objects {
164 #[cfg(feature = "sys")]
165 crate::StoreObjects::Sys(ref store_objects) => {
166 crate::backend::sys::vm::Trap::uncaught_exception(
167 exnref.as_sys().clone(),
168 store_objects,
169 )
170 .into()
171 }
172 _ => panic!("exceptions are only supported in the `sys` backend"),
173 }
174 }
175
176 pub fn message(&self) -> String {
178 if let Some(trap_code) = self.inner.trap_code {
179 trap_code.message().to_string()
180 } else {
181 self.inner.source.to_string()
182 }
183 }
184
185 pub fn trace(&self) -> &[FrameInfo] {
188 &self.inner.wasm_trace
189 }
190
191 pub fn to_trap(self) -> Option<TrapCode> {
193 self.inner.trap_code
194 }
195
196 pub fn downcast<T: std::error::Error + 'static>(self) -> Result<T, Self> {
203 match Arc::try_unwrap(self.inner) {
204 Ok(inner) if inner.source.is::<T>() => Ok(inner.source.downcast::<T>().unwrap()),
205 Ok(inner) => Err(Self {
206 inner: Arc::new(inner),
207 }),
208 Err(inner) => Err(Self { inner }),
209 }
210 }
211
212 pub fn downcast_ref<T: std::error::Error + 'static>(&self) -> Option<&T> {
214 self.inner.as_ref().source.downcast_ref::<T>()
215 }
216
217 pub fn is<T: std::error::Error + 'static>(&self) -> bool {
219 self.inner.source.is::<T>()
220 }
221
222 pub fn is_exception(&self) -> bool {
224 self.inner.source.is_exception()
225 }
226
227 pub fn to_exception(&self) -> Option<Exception> {
229 self.inner.source.to_exception()
230 }
231
232 pub fn display<'a>(&'a self, store: &'a mut impl AsStoreMut) -> RuntimeErrorDisplay<'a> {
234 if let Some(exception) = self.to_exception() {
235 RuntimeErrorDisplay::Exception(exception.payload(store), self.trace())
236 } else {
237 RuntimeErrorDisplay::Other(self)
238 }
239 }
240
241 pub fn write_trace(trace: &[FrameInfo], f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 if trace.is_empty() {
244 return Ok(());
245 }
246 for frame in trace.iter() {
247 let name = frame.module_name();
248 let func_index = frame.func_index();
249 writeln!(f)?;
250 write!(f, " at ")?;
251 match frame.function_name() {
252 Some(name) => match rustc_demangle::try_demangle(name) {
253 Ok(name) => write!(f, "{name}")?,
254 Err(_) => write!(f, "{name}")?,
255 },
256 None => write!(f, "<unnamed>")?,
257 }
258 write!(
259 f,
260 " ({}[{}]:0x{:x})",
261 name,
262 func_index,
263 frame.module_offset()
264 )?;
265 }
266 Ok(())
267 }
268
269 pub(crate) fn from_dyn(err: Box<dyn std::error::Error + Send + Sync>) -> Self {
270 match err.downcast::<Self>() {
271 Ok(runtime_error) => *runtime_error,
272 Err(error) => Trap::user(error),
273 }
274 }
275}
276
277impl std::fmt::Debug for RuntimeError {
278 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
279 f.debug_struct("RuntimeError")
280 .field("source", &self.inner.source)
281 .field("wasm_trace", &self.inner.wasm_trace)
282 .finish()
283 }
284}
285
286impl std::fmt::Display for RuntimeError {
287 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288 write!(f, "RuntimeError: {}", self.message())?;
289 Self::write_trace(self.trace(), f)
290 }
291}
292
293pub enum RuntimeErrorDisplay<'a> {
295 Exception(Vec<Value>, &'a [FrameInfo]),
297 Other(&'a RuntimeError),
299}
300
301impl std::fmt::Display for RuntimeErrorDisplay<'_> {
302 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
303 match self {
304 RuntimeErrorDisplay::Exception(payload, trace) => {
305 write!(f, "Uncaught exception")?;
306 if !payload.is_empty() {
307 write!(f, " with payload: {payload:?}")?;
308 }
309 RuntimeError::write_trace(trace, f)
310 }
311 RuntimeErrorDisplay::Other(err) => write!(f, "{err}"),
312 }
313 }
314}
315
316impl std::error::Error for RuntimeError {
317 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
318 self.inner.source.source()
319 }
320}
321
322impl From<Box<dyn std::error::Error + Send + Sync>> for RuntimeError {
323 fn from(error: Box<dyn std::error::Error + Send + Sync>) -> Self {
324 match error.downcast::<Self>() {
325 Ok(runtime_error) => *runtime_error,
327 Err(error) => Trap::user(error),
328 }
329 }
330}
331
332#[derive(PartialEq, Eq, Debug, Error)]
335#[non_exhaustive]
336pub enum AtomicsError {
337 Unimplemented,
339 TooManyWaiters,
341 AtomicsDisabled,
343}
344
345impl std::fmt::Display for AtomicsError {
346 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
347 match self {
348 Self::Unimplemented => write!(f, "Atomic operations are not supported"),
349 Self::TooManyWaiters => write!(f, "Too many waiters for address"),
350 Self::AtomicsDisabled => write!(f, "Atomic operations are disabled"),
351 }
352 }
353}