linera_wasmer/
errors.rs

1#[cfg(feature = "js")]
2use crate::js::trap::Trap;
3#[cfg(feature = "jsc")]
4use crate::jsc::trap::Trap;
5use std::fmt;
6use std::sync::Arc;
7use thiserror::Error;
8use wasmer_types::{FrameInfo, TrapCode};
9#[cfg(feature = "sys")]
10use wasmer_vm::Trap;
11
12use wasmer_types::ImportError;
13
14/// The WebAssembly.LinkError object indicates an error during
15/// module instantiation (besides traps from the start function).
16///
17/// This is based on the [link error][link-error] API.
18///
19/// [link-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/LinkError
20#[derive(Debug, Clone)]
21#[cfg_attr(feature = "std", derive(Error))]
22#[cfg_attr(feature = "std", error("Link error: {0}"))]
23pub enum LinkError {
24    /// An error occurred when checking the import types.
25    #[cfg_attr(feature = "std", error("Error while importing {0:?}.{1:?}: {2}"))]
26    Import(String, String, ImportError),
27
28    /// A trap ocurred during linking.
29    #[cfg_attr(feature = "std", error("RuntimeError occurred during linking: {0}"))]
30    Trap(#[source] RuntimeError),
31    /// Insufficient resources available for linking.
32    #[cfg_attr(feature = "std", error("Insufficient resources: {0}"))]
33    Resource(String),
34}
35
36/// An error while instantiating a module.
37///
38/// This is not a common WebAssembly error, however
39/// we need to differentiate from a `LinkError` (an error
40/// that happens while linking, on instantiation), a
41/// Trap that occurs when calling the WebAssembly module
42/// start function, and an error when initializing the user's
43/// host environments.
44#[derive(Debug, Clone)]
45#[cfg_attr(feature = "std", derive(Error))]
46pub enum InstantiationError {
47    /// A linking ocurred during instantiation.
48    #[cfg_attr(feature = "std", error(transparent))]
49    Link(LinkError),
50
51    /// A runtime error occured while invoking the start function
52    #[cfg_attr(feature = "std", error(transparent))]
53    Start(RuntimeError),
54
55    /// The module was compiled with a CPU feature that is not available on
56    /// the current host.
57    #[cfg_attr(feature = "std", error("missing required CPU features: {0:?}"))]
58    CpuFeature(String),
59
60    /// Import from a different [`Store`][super::Store].
61    /// This error occurs when an import from a different store is used.
62    #[cfg_attr(feature = "std", error("cannot mix imports from different stores"))]
63    DifferentStores,
64
65    /// Import from a different Store.
66    /// This error occurs when an import from a different store is used.
67    #[cfg_attr(feature = "std", error("incorrect OS or architecture"))]
68    DifferentArchOS,
69}
70
71/// A struct representing an aborted instruction execution, with a message
72/// indicating the cause.
73#[derive(Clone)]
74pub struct RuntimeError {
75    pub(crate) inner: Arc<RuntimeErrorInner>,
76}
77
78#[derive(Debug)]
79struct RuntimeStringError {
80    details: String,
81}
82
83impl RuntimeStringError {
84    fn new(msg: String) -> Self {
85        Self { details: msg }
86    }
87}
88
89impl fmt::Display for RuntimeStringError {
90    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91        write!(f, "{}", self.details)
92    }
93}
94
95impl std::error::Error for RuntimeStringError {
96    fn description(&self) -> &str {
97        &self.details
98    }
99}
100
101pub(crate) struct RuntimeErrorInner {
102    /// The source error
103    pub(crate) source: Trap,
104    /// The trap code (if any)
105    trap_code: Option<TrapCode>,
106    /// The reconstructed Wasm trace (from the native trace and the `GlobalFrameInfo`).
107    wasm_trace: Vec<FrameInfo>,
108}
109
110impl RuntimeError {
111    /// Creates a new generic `RuntimeError` with the given `message`.
112    ///
113    /// # Example
114    /// ```
115    /// let trap = wasmer::RuntimeError::new("unexpected error");
116    /// assert_eq!("unexpected error", trap.message());
117    /// ```
118    pub fn new<I: Into<String>>(message: I) -> Self {
119        let msg = message.into();
120        let source = RuntimeStringError::new(msg);
121        Self::user(Box::new(source))
122    }
123
124    /// Creates `RuntimeError` from an error and a WasmTrace
125    ///
126    /// # Example
127    /// ```ignore
128    /// let wasm_trace = vec![wasmer_types::FrameInfo::new(
129    ///   "my_module".to_string(),
130    ///   0,
131    ///   Some("my_function".to_string()),
132    ///   0.into(),
133    ///   2.into()
134    /// )];
135    /// let trap = wasmer::RuntimeError::new_from_source(my_error, wasm_trace, None);
136    /// assert_eq!("unexpected error", trap.message());
137    /// ```
138    pub fn new_from_source(
139        source: Trap,
140        wasm_trace: Vec<FrameInfo>,
141        trap_code: Option<TrapCode>,
142    ) -> Self {
143        Self {
144            inner: Arc::new(RuntimeErrorInner {
145                source,
146                wasm_trace,
147                trap_code,
148            }),
149        }
150    }
151
152    /// Creates a custom user Error.
153    ///
154    /// This error object can be passed through Wasm frames and later retrieved
155    /// using the `downcast` method.
156    pub fn user(error: Box<dyn std::error::Error + Send + Sync>) -> Self {
157        match error.downcast::<Self>() {
158            Ok(err) => *err,
159            Err(error) => error.into(),
160        }
161    }
162
163    /// Returns a reference the `message` stored in `Trap`.
164    pub fn message(&self) -> String {
165        if let Some(trap_code) = self.inner.trap_code {
166            trap_code.message().to_string()
167        } else {
168            self.inner.source.to_string()
169        }
170    }
171
172    /// Returns a list of function frames in WebAssembly code that led to this
173    /// trap happening.
174    pub fn trace(&self) -> &[FrameInfo] {
175        &self.inner.wasm_trace
176    }
177
178    /// Returns trap code, if it's a Trap
179    pub fn to_trap(self) -> Option<TrapCode> {
180        self.inner.trap_code
181    }
182
183    // /// Returns trap code, if it's a Trap
184    // pub fn to_source(self) -> &'static Trap {
185    //     &self.inner.as_ref().source
186    // }
187
188    /// Attempts to downcast the `RuntimeError` to a concrete type.
189    pub fn downcast<T: std::error::Error + 'static>(self) -> Result<T, Self> {
190        match Arc::try_unwrap(self.inner) {
191            Ok(inner) if inner.source.is::<T>() => Ok(inner.source.downcast::<T>().unwrap()),
192            Ok(inner) => Err(Self {
193                inner: Arc::new(inner),
194            }),
195            Err(inner) => Err(Self { inner }),
196        }
197    }
198
199    /// Attempts to downcast the `RuntimeError` to a concrete type.
200    pub fn downcast_ref<T: std::error::Error + 'static>(&self) -> Option<&T> {
201        self.inner.as_ref().source.downcast_ref::<T>()
202    }
203
204    /// Returns true if the `RuntimeError` is the same as T
205    pub fn is<T: std::error::Error + 'static>(&self) -> bool {
206        self.inner.source.is::<T>()
207    }
208}
209
210impl fmt::Debug for RuntimeError {
211    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212        f.debug_struct("RuntimeError")
213            .field("source", &self.inner.source)
214            .field("wasm_trace", &self.inner.wasm_trace)
215            .finish()
216    }
217}
218
219impl fmt::Display for RuntimeError {
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        write!(f, "RuntimeError: {}", self.message())?;
222        let trace = self.trace();
223        if trace.is_empty() {
224            return Ok(());
225        }
226        for frame in self.trace().iter() {
227            let name = frame.module_name();
228            let func_index = frame.func_index();
229            writeln!(f)?;
230            write!(f, "    at ")?;
231            match frame.function_name() {
232                Some(name) => match rustc_demangle::try_demangle(name) {
233                    Ok(name) => write!(f, "{}", name)?,
234                    Err(_) => write!(f, "{}", name)?,
235                },
236                None => write!(f, "<unnamed>")?,
237            }
238            write!(
239                f,
240                " ({}[{}]:0x{:x})",
241                name,
242                func_index,
243                frame.module_offset()
244            )?;
245        }
246        Ok(())
247    }
248}
249
250impl std::error::Error for RuntimeError {
251    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
252        self.inner.source.source()
253    }
254}
255
256impl From<Box<dyn std::error::Error + Send + Sync>> for RuntimeError {
257    fn from(error: Box<dyn std::error::Error + Send + Sync>) -> Self {
258        match error.downcast::<Self>() {
259            // The error is already a RuntimeError, we return it directly
260            Ok(runtime_error) => *runtime_error,
261            Err(error) => Trap::user(error).into(),
262        }
263    }
264}
265
266/// Error that can occur during atomic operations. (notify/wait)
267// Non-exhaustive to allow for future variants without breaking changes!
268#[derive(PartialEq, Eq, Debug, Error)]
269#[non_exhaustive]
270pub enum AtomicsError {
271    /// Atomic operations are not supported by this memory.
272    Unimplemented,
273    /// To many waiter for address.
274    TooManyWaiters,
275    /// Atomic operations are disabled.
276    AtomicsDisabled,
277}
278
279impl std::fmt::Display for AtomicsError {
280    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281        match self {
282            Self::Unimplemented => write!(f, "Atomic operations are not supported"),
283            Self::TooManyWaiters => write!(f, "Too many waiters for address"),
284            Self::AtomicsDisabled => write!(f, "Atomic operations are disabled"),
285        }
286    }
287}