#[cfg(feature = "js")]
use crate::js::trap::Trap;
#[cfg(feature = "jsc")]
use crate::jsc::trap::Trap;
use std::fmt;
use std::sync::Arc;
use thiserror::Error;
use wasmer_types::{FrameInfo, TrapCode};
#[cfg(feature = "sys")]
use wasmer_vm::Trap;
use wasmer_types::ImportError;
#[derive(Debug)]
#[cfg_attr(feature = "std", derive(Error))]
#[cfg_attr(feature = "std", error("Link error: {0}"))]
pub enum LinkError {
#[cfg_attr(feature = "std", error("Error while importing {0:?}.{1:?}: {2}"))]
Import(String, String, ImportError),
#[cfg_attr(feature = "std", error("RuntimeError occurred during linking: {0}"))]
Trap(#[source] RuntimeError),
#[cfg_attr(feature = "std", error("Insufficient resources: {0}"))]
Resource(String),
}
#[derive(Debug)]
#[cfg_attr(feature = "std", derive(Error))]
pub enum InstantiationError {
#[cfg_attr(feature = "std", error(transparent))]
Link(LinkError),
#[cfg_attr(feature = "std", error(transparent))]
Start(RuntimeError),
#[cfg_attr(feature = "std", error("missing required CPU features: {0:?}"))]
CpuFeature(String),
#[cfg_attr(feature = "std", error("cannot mix imports from different stores"))]
DifferentStores,
#[cfg_attr(feature = "std", error("incorrect OS or architecture"))]
DifferentArchOS,
}
#[derive(Clone)]
pub struct RuntimeError {
pub(crate) inner: Arc<RuntimeErrorInner>,
}
#[derive(Debug)]
struct RuntimeStringError {
details: String,
}
impl RuntimeStringError {
fn new(msg: String) -> Self {
Self { details: msg }
}
}
impl fmt::Display for RuntimeStringError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.details)
}
}
impl std::error::Error for RuntimeStringError {
fn description(&self) -> &str {
&self.details
}
}
pub(crate) struct RuntimeErrorInner {
pub(crate) source: Trap,
trap_code: Option<TrapCode>,
wasm_trace: Vec<FrameInfo>,
}
impl RuntimeError {
pub fn new<I: Into<String>>(message: I) -> Self {
let msg = message.into();
let source = RuntimeStringError::new(msg);
Self::user(Box::new(source))
}
pub fn new_from_source(
source: Trap,
wasm_trace: Vec<FrameInfo>,
trap_code: Option<TrapCode>,
) -> Self {
Self {
inner: Arc::new(RuntimeErrorInner {
source,
wasm_trace,
trap_code,
}),
}
}
pub fn user(error: Box<dyn std::error::Error + Send + Sync>) -> Self {
match error.downcast::<Self>() {
Ok(err) => *err,
Err(error) => error.into(),
}
}
pub fn message(&self) -> String {
if let Some(trap_code) = self.inner.trap_code {
trap_code.message().to_string()
} else {
self.inner.source.to_string()
}
}
pub fn trace(&self) -> &[FrameInfo] {
&self.inner.wasm_trace
}
pub fn to_trap(self) -> Option<TrapCode> {
self.inner.trap_code
}
pub fn downcast<T: std::error::Error + 'static>(self) -> Result<T, Self> {
match Arc::try_unwrap(self.inner) {
Ok(inner) if inner.source.is::<T>() => Ok(inner.source.downcast::<T>().unwrap()),
Ok(inner) => Err(Self {
inner: Arc::new(inner),
}),
Err(inner) => Err(Self { inner }),
}
}
pub fn downcast_ref<T: std::error::Error + 'static>(&self) -> Option<&T> {
self.inner.as_ref().source.downcast_ref::<T>()
}
pub fn is<T: std::error::Error + 'static>(&self) -> bool {
self.inner.source.is::<T>()
}
}
impl fmt::Debug for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RuntimeError")
.field("source", &self.inner.source)
.field("wasm_trace", &self.inner.wasm_trace)
.finish()
}
}
impl fmt::Display for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RuntimeError: {}", self.message())?;
let trace = self.trace();
if trace.is_empty() {
return Ok(());
}
for frame in self.trace().iter() {
let name = frame.module_name();
let func_index = frame.func_index();
writeln!(f)?;
write!(f, " at ")?;
match frame.function_name() {
Some(name) => match rustc_demangle::try_demangle(name) {
Ok(name) => write!(f, "{}", name)?,
Err(_) => write!(f, "{}", name)?,
},
None => write!(f, "<unnamed>")?,
}
write!(
f,
" ({}[{}]:0x{:x})",
name,
func_index,
frame.module_offset()
)?;
}
Ok(())
}
}
impl std::error::Error for RuntimeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.inner.source.source()
}
}
impl From<Box<dyn std::error::Error + Send + Sync>> for RuntimeError {
fn from(error: Box<dyn std::error::Error + Send + Sync>) -> Self {
match error.downcast::<Self>() {
Ok(runtime_error) => *runtime_error,
Err(error) => Trap::user(error).into(),
}
}
}