diff --git a/lib/api/src/js/trap.rs b/lib/api/src/js/trap.rs index 8a0ef2d940b..9d62e2c7753 100644 --- a/lib/api/src/js/trap.rs +++ b/lib/api/src/js/trap.rs @@ -1,14 +1,17 @@ -use crate::RuntimeError; -use std::error::Error; -use std::fmt; +use std::{ + error::Error, + fmt::{self, Display}, +}; use wasm_bindgen::{prelude::*, JsValue}; use wasm_bindgen_downcast::DowncastJS; +use crate::RuntimeError; + #[derive(Debug)] enum InnerTrap { User(Box), - Js(JsValue), + Js(JsTrap), } /// A struct representing a Trap @@ -18,9 +21,6 @@ pub struct Trap { inner: InnerTrap, } -unsafe impl Send for Trap {} -unsafe impl Sync for Trap {} - impl Trap { pub fn user(error: Box) -> Self { Self { @@ -67,8 +67,8 @@ impl std::error::Error for Trap { impl fmt::Display for Trap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.inner { - InnerTrap::User(e) => write!(f, "user: {}", e), - InnerTrap::Js(value) => write!(f, "js: {:?}", value.as_string()), + InnerTrap::User(e) => write!(f, "user: {e}"), + InnerTrap::Js(value) => write!(f, "js: {value}"), } } } @@ -78,9 +78,51 @@ impl From for RuntimeError { // We try to downcast the error and see if it's // an instance of RuntimeError instead, so we don't need // to re-wrap it. - let trap = Trap::downcast_js(original).unwrap_or_else(|o| Trap { - inner: InnerTrap::Js(o), - }); + let trap: Trap = match Trap::downcast_js(original) { + Ok(trap) => trap, + Err(other) => Trap { + inner: InnerTrap::Js(JsTrap::from(other)), + }, + }; + trap.into() } } + +/// A `Send+Sync` version of a JavaScript error. +#[derive(Debug)] +enum JsTrap { + /// An error message. + Message(String), + /// Unable to determine the underlying error. + Unknown, +} + +impl From for JsTrap { + fn from(value: JsValue) -> Self { + // Let's try some easy special cases first + if let Some(error) = value.dyn_ref::() { + return JsTrap::Message(error.message().into()); + } + + if let Some(s) = value.as_string() { + return JsTrap::Message(s); + } + + // Otherwise, we'll try to stringify the error and hope for the best + if let Some(obj) = value.dyn_ref::() { + return JsTrap::Message(obj.to_string().into()); + } + + JsTrap::Unknown + } +} + +impl Display for JsTrap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + JsTrap::Message(m) => write!(f, "{m}"), + JsTrap::Unknown => write!(f, "unknown"), + } + } +}