use std::error::Error;
use std::fmt;
use std::sync::Arc;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
use wasm_bindgen_downcast::DowncastJS;
pub trait CoreError: fmt::Debug + fmt::Display {
fn source(&self) -> Option<&(dyn CoreError + 'static)> {
None
}
fn type_id(&self) -> core::any::TypeId
where
Self: 'static,
{
core::any::TypeId::of::<Self>()
}
fn description(&self) -> &str {
"description() is deprecated; use Display"
}
fn cause(&self) -> Option<&dyn CoreError> {
self.source()
}
}
impl<T: fmt::Debug + fmt::Display> CoreError for T {}
impl dyn CoreError + 'static {
#[allow(dead_code)]
pub fn core_is_equal<T: CoreError + 'static>(&self) -> bool {
let t = core::any::TypeId::of::<T>();
let concrete = self.type_id();
t == concrete
}
}
impl dyn CoreError + Send + Sync + 'static {
#[allow(dead_code)]
pub fn core_is_equal<T: CoreError + 'static>(&self) -> bool {
let t = core::any::TypeId::of::<T>();
let concrete = self.type_id();
t == concrete
}
}
impl dyn CoreError + Send {
#[inline]
#[allow(dead_code)]
pub fn downcast_core<T: CoreError + 'static>(
self: Box<Self>,
) -> Result<Box<T>, Box<dyn CoreError + Send>> {
let err: Box<dyn CoreError> = self;
<dyn CoreError>::downcast_core(err).map_err(|s| unsafe {
core::mem::transmute::<Box<dyn CoreError>, Box<dyn CoreError + Send>>(s)
})
}
}
impl dyn CoreError + Send + Sync {
#[inline]
#[allow(dead_code)]
pub fn downcast_core<T: CoreError + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
let err: Box<dyn CoreError> = self;
<dyn CoreError>::downcast_core(err).map_err(|s| unsafe {
core::mem::transmute::<Box<dyn CoreError>, Box<dyn CoreError + Send + Sync>>(s)
})
}
}
impl dyn CoreError {
#[inline]
#[allow(dead_code)]
pub fn downcast_core<T: CoreError + 'static>(
self: Box<Self>,
) -> Result<Box<T>, Box<dyn CoreError>> {
if self.core_is_equal::<T>() {
unsafe {
let raw: *mut dyn CoreError = Box::into_raw(self);
Ok(Box::from_raw(raw as *mut T))
}
} else {
Err(self)
}
}
}
#[wasm_bindgen]
#[derive(Clone, DowncastJS)]
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),
#[cfg(feature = "std")]
User(Box<dyn Error + Send + Sync>),
#[cfg(feature = "core")]
User(Box<dyn CoreError + 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")]
#[cfg(feature = "std")]
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)
}
#[deprecated(since = "2.1.1", note = "return a Result from host functions instead")]
#[cfg(feature = "core")]
pub(crate) fn raise(error: Box<dyn CoreError + Send + Sync>) -> ! {
let error = Self::user(error);
let js_error: JsValue = error.into();
wasm_bindgen::throw_val(js_error)
}
#[cfg(feature = "std")]
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)),
},
}
}
#[cfg(feature = "core")]
pub fn user(error: Box<dyn CoreError + Send + Sync>) -> Self {
match error.downcast_core::<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) {
#[cfg(feature = "std")]
Ok(RuntimeErrorSource::User(err)) if err.is::<T>() => Ok(*err.downcast::<T>().unwrap()),
#[cfg(feature = "core")]
Ok(RuntimeErrorSource::User(err)) if (*err).core_is_equal::<T>() => {
Ok(*err.downcast_core::<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() {
#[cfg(feature = "std")]
RuntimeErrorSource::User(err) => err.is::<T>(),
#[cfg(feature = "core")]
RuntimeErrorSource::User(err) => (*err).core_is_equal::<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(())
}
}
#[cfg(feature = "std")]
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,
}
}
}
impl From<JsValue> for RuntimeError {
fn from(original: JsValue) -> Self {
WasmerRuntimeError::downcast_js(original).unwrap_or_else(|js| RuntimeError {
inner: Arc::new(RuntimeErrorSource::Js(js)),
})
}
}