use std::{
error::Error,
fmt::{self, Display},
};
use js_sys::Reflect;
use wasm_bindgen::{prelude::*, JsValue};
use crate::RuntimeError;
#[derive(Debug)]
enum InnerTrap {
User(Box<dyn Error + Send + Sync>),
Js(JsTrap),
}
#[wasm_bindgen(skip_typescript)]
#[derive(Debug)]
pub struct Trap {
inner: InnerTrap,
}
impl Trap {
pub fn user(error: Box<dyn Error + Send + Sync>) -> Self {
Self {
inner: InnerTrap::User(error),
}
}
pub fn downcast<T: Error + 'static>(self) -> Result<T, Self> {
match self.inner {
InnerTrap::User(err) if err.is::<T>() => Ok(*err.downcast::<T>().unwrap()),
_ => Err(self),
}
}
pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
match &self.inner {
InnerTrap::User(err) if err.is::<T>() => err.downcast_ref::<T>(),
_ => None,
}
}
pub fn is<T: Error + 'static>(&self) -> bool {
match &self.inner {
InnerTrap::User(err) => err.is::<T>(),
_ => false,
}
}
}
#[wasm_bindgen]
impl Trap {
pub fn __wbg_wasmer_trap() {}
}
impl std::error::Error for Trap {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.inner {
InnerTrap::User(err) => Some(&**err),
_ => None,
}
}
}
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}"),
}
}
}
impl From<JsValue> for RuntimeError {
fn from(value: JsValue) -> Self {
if let Some(obj) = value.dyn_ref() {
if let Some(trap) = downcast_from_ptr(obj) {
return trap.into();
}
}
RuntimeError::from(Trap {
inner: InnerTrap::Js(value.into()),
})
}
}
fn downcast_from_ptr(value: &JsValue) -> Option<Trap> {
if !value.is_object() {
return None;
}
let prototype = &Reflect::get_prototype_of(value).ok()?;
let class = prototype.constructor();
let key = JsValue::from_str("__wbg_wasmer_trap");
let marker_func: Option<js_sys::Function> = Reflect::get(&class, &key)
.and_then(|v: JsValue| v.dyn_into())
.ok();
if marker_func.is_none() {
return None;
}
unsafe {
let key = JsValue::from_str("__destroy_into_raw");
let ptr = Reflect::get(value, &key)
.ok()
.and_then(|v| v.dyn_into::<js_sys::Function>().ok())
.and_then(|destroy_into_raw| destroy_into_raw.call0(value).ok())
.and_then(|ret| ret.as_f64())?;
Some(<Trap as wasm_bindgen::convert::FromWasmAbi>::from_abi(
ptr as u32,
))
}
}
#[derive(Debug)]
enum JsTrap {
Message(String),
Unknown,
}
impl From<JsValue> for JsTrap {
fn from(value: JsValue) -> Self {
if let Some(error) = value.dyn_ref::<js_sys::Error>() {
return JsTrap::Message(error.message().into());
}
if let Some(s) = value.as_string() {
return JsTrap::Message(s);
}
if let Some(obj) = value.dyn_ref::<js_sys::Object>() {
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"),
}
}
}