use std::error::Error;
use std::fmt;
use std::sync::Arc;
use wasm_bindgen::convert::FromWasmAbi;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
#[wasm_bindgen]
#[derive(Clone)]
pub struct WasmerRuntimeError {
inner: Arc<RuntimeErrorSource>,
}
pub type RuntimeError = WasmerRuntimeError;
impl PartialEq for RuntimeError {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.inner, &other.inner)
}
}
#[derive(Debug)]
enum RuntimeErrorSource {
Generic(String),
User(Box<dyn Error + Send + Sync>),
Js(JsValue),
}
unsafe impl Send for RuntimeErrorSource {}
unsafe impl Sync for RuntimeErrorSource {}
impl fmt::Display for RuntimeErrorSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Generic(s) => write!(f, "{}", s),
Self::User(s) => write!(f, "{}", s),
Self::Js(s) => write!(f, "{:?}", s),
}
}
}
impl RuntimeError {
pub fn new<I: Into<String>>(message: I) -> Self {
RuntimeError {
inner: Arc::new(RuntimeErrorSource::Generic(message.into())),
}
}
#[deprecated(since = "2.1.1", note = "return a Result from host functions instead")]
pub(crate) fn raise(error: Box<dyn Error + Send + Sync>) -> ! {
let error = Self::user(error);
let js_error: JsValue = error.into();
wasm_bindgen::throw_val(js_error)
}
pub fn user(error: Box<dyn Error + Send + Sync>) -> Self {
match error.downcast::<Self>() {
Ok(runtime_error) => *runtime_error,
Err(error) => RuntimeError {
inner: Arc::new(RuntimeErrorSource::User(error)),
},
}
}
pub fn message(&self) -> String {
format!("{}", self.inner)
}
pub fn downcast<T: Error + 'static>(self) -> Result<T, Self> {
match Arc::try_unwrap(self.inner) {
Ok(RuntimeErrorSource::User(err)) if err.is::<T>() => Ok(*err.downcast::<T>().unwrap()),
Ok(inner) => Err(Self {
inner: Arc::new(inner),
}),
Err(inner) => Err(Self { inner }),
}
}
pub fn is<T: Error + 'static>(&self) -> bool {
match self.inner.as_ref() {
RuntimeErrorSource::User(err) => err.is::<T>(),
_ => false,
}
}
}
impl fmt::Debug for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RuntimeError")
.field("source", &self.inner)
.finish()
}
}
impl fmt::Display for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RuntimeError: {}", self.message())?;
Ok(())
}
}
impl std::error::Error for RuntimeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self.inner.as_ref() {
RuntimeErrorSource::User(err) => Some(&**err),
_ => None,
}
}
}
pub fn generic_of_jsval<T: FromWasmAbi<Abi = u32>>(
js: JsValue,
classname: &str,
) -> Result<T, JsValue> {
use js_sys::{Object, Reflect};
let ctor_name = Object::get_prototype_of(&js).constructor().name();
if ctor_name == classname {
let ptr = Reflect::get(&js, &JsValue::from_str("ptr"))?;
match ptr.as_f64() {
Some(ptr_f64) => {
let foo = unsafe { T::from_abi(ptr_f64 as u32) };
Ok(foo)
}
None => {
Err(js)
}
}
} else {
Err(js)
}
}
impl From<JsValue> for RuntimeError {
fn from(original: JsValue) -> Self {
generic_of_jsval(original, "WasmerRuntimeError").unwrap_or_else(|js| RuntimeError {
inner: Arc::new(RuntimeErrorSource::Js(js)),
})
}
}