Skip to content

Commit

Permalink
Merge pull request #3165 from wasmerio/make-wasmer-js-work-on-core
Browse files Browse the repository at this point in the history
Initial port of make test-js-core (port wasmer API to core)
  • Loading branch information
syrusakbary authored Sep 2, 2022
2 parents fd9113a + c915967 commit aedbdcc
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 11 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/test-js.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,6 @@ jobs:
- name: Compile Wasmer to WebAssembly and test with a JavaScript host
run: make test-js

- name: Compile Wasmer to WebAssembly and test with a JavaScript host (no-std)
run: make test-js-core
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,9 @@ test-packages:

test-js: test-js-api test-js-wasi

test-js-core:
cd lib/api && wasm-pack test --node -- --no-default-features --features js,core,wasm-types-polyfill,wat

test-js-api:
cd lib/api && wasm-pack test --node -- --no-default-features --features js-default,wat

Expand Down
20 changes: 12 additions & 8 deletions lib/api/src/js/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#[cfg(feature = "core")]
use crate::alloc::borrow::Cow;
use crate::js::lib::std::string::String;
use crate::js::trap::RuntimeError;
#[cfg(feature = "std")]
Expand Down Expand Up @@ -116,7 +118,7 @@ impl From<wasm_bindgen::JsValue> for WasmError {
pub enum SerializeError {
/// An IO error
#[cfg_attr(feature = "std", error(transparent))]
Io(#[from] std::io::Error),
Io(#[cfg_attr(feature = "std", from)] std::io::Error),
/// A generic serialization error
#[cfg_attr(feature = "std", error("{0}"))]
Generic(String),
Expand All @@ -125,11 +127,12 @@ pub enum SerializeError {
/// The Deserialize error can occur when loading a
/// compiled Module from a binary.
/// Copied from wasmer_compiler::DeSerializeError
#[derive(Error, Debug)]
#[derive(Debug)]
#[cfg_attr(feature = "std", derive(Error))]
pub enum DeserializeError {
/// An IO error
#[cfg_attr(feature = "std", error(transparent))]
Io(#[from] std::io::Error),
Io(#[cfg_attr(feature = "std", from)] std::io::Error),
/// A generic deserialization error
#[cfg_attr(feature = "std", error("{0}"))]
Generic(String),
Expand All @@ -151,19 +154,20 @@ pub enum DeserializeError {
/// This is based on the [link error][link-error] API.
///
/// [link-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/LinkError
#[derive(Error, Debug)]
#[error("Link error: {0}")]
#[derive(Debug)]
#[cfg_attr(feature = "std", derive(Error))]
#[cfg_attr(feature = "std", error("Link error: {0}"))]
pub enum LinkError {
/// An error occurred when checking the import types.
#[error("Error while importing {0:?}.{1:?}: {2}")]
#[cfg_attr(feature = "std", error("Error while importing {0:?}.{1:?}: {2}"))]
Import(String, String, ImportError),

#[cfg(not(target_arch = "wasm32"))]
/// A trap ocurred during linking.
#[error("RuntimeError occurred during linking: {0}")]
#[cfg_attr(feature = "std", error("RuntimeError occurred during linking: {0}"))]
Trap(#[source] RuntimeError),
/// Insufficient resources available for linking.
#[error("Insufficient resources: {0}")]
#[cfg_attr(feature = "std", error("Insufficient resources: {0}"))]
Resource(String),
}

Expand Down
8 changes: 8 additions & 0 deletions lib/api/src/js/externals/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,10 @@ mod inner {
match result {
Ok(Ok(result)) => return result.into_c_struct(&mut store),
#[allow(deprecated)]
#[cfg(feature = "std")]
Ok(Err(trap)) => RuntimeError::raise(Box::new(trap)),
#[cfg(feature = "core")]
#[allow(deprecated)]
Ok(Err(trap)) => RuntimeError::raise(Box::new(trap)),
Err(_panic) => unimplemented!(),
}
Expand Down Expand Up @@ -1191,6 +1195,10 @@ mod inner {

match result {
Ok(Ok(result)) => return result.into_c_struct(&mut store),
#[cfg(feature = "std")]
#[allow(deprecated)]
Ok(Err(trap)) => RuntimeError::raise(Box::new(trap)),
#[cfg(feature = "core")]
#[allow(deprecated)]
Ok(Err(trap)) => RuntimeError::raise(Box::new(trap)),
Err(_panic) => unimplemented!(),
Expand Down
6 changes: 3 additions & 3 deletions lib/api/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ compile_error!(
compile_error!("Both the `std` and `core` features are disabled. Please enable one of them.");

#[cfg(feature = "core")]
extern crate alloc;
pub(crate) extern crate alloc;

mod lib {
#[cfg(feature = "core")]
pub mod std {
pub use alloc::{borrow, boxed, str, string, sync, vec};
pub use crate::alloc::{borrow, boxed, str, string, sync, vec};
pub use core::fmt;
pub use hashbrown as collections;
}
Expand All @@ -23,7 +23,7 @@ mod lib {
}
}

mod error;
pub(crate) mod error;
mod export;
mod exports;
mod externals;
Expand Down
117 changes: 117 additions & 0 deletions lib/api/src/js/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,89 @@ use wasm_bindgen::convert::FromWasmAbi;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;

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 {
/// Returns `true` if the inner type is the same as `T`.
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 {
/// Returns `true` if the inner type is the same as `T`.
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]
/// Attempts to downcast the box to a concrete type.
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 {
// Reapply the `Send` marker.
core::mem::transmute::<Box<dyn CoreError>, Box<dyn CoreError + Send>>(s)
})
}
}

impl dyn CoreError + Send + Sync {
#[inline]
/// Attempts to downcast the box to a concrete type.
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 {
// Reapply the `Send + Sync` marker.
core::mem::transmute::<Box<dyn CoreError>, Box<dyn CoreError + Send + Sync>>(s)
})
}
}

impl dyn CoreError {
#[inline]
/// Attempts to downcast the box to a concrete type.
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)
}
}
}

/// A struct representing an aborted instruction execution, with a message
/// indicating the cause.
#[wasm_bindgen]
Expand All @@ -30,7 +113,10 @@ impl PartialEq for RuntimeError {
#[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),
}

Expand Down Expand Up @@ -64,16 +150,27 @@ impl RuntimeError {

/// Raises a custom user Error
#[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)
}

/// Raises a custom user 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)
}

/// Creates a custom user Error.
///
/// This error object can be passed through Wasm frames and later retrieved
/// using the `downcast` method.
#[cfg(feature = "std")]
pub fn user(error: Box<dyn Error + Send + Sync>) -> Self {
match error.downcast::<Self>() {
// The error is already a RuntimeError, we return it directly
Expand All @@ -84,6 +181,17 @@ impl RuntimeError {
}
}

#[cfg(feature = "core")]
pub fn user(error: Box<dyn CoreError + Send + Sync>) -> Self {
match error.downcast_core::<Self>() {
// The error is already a RuntimeError, we return it directly
Ok(runtime_error) => *runtime_error,
Err(error) => RuntimeError {
inner: Arc::new(RuntimeErrorSource::User(error)),
},
}
}

/// Returns a reference the `message` stored in `Trap`.
pub fn message(&self) -> String {
format!("{}", self.inner)
Expand All @@ -93,7 +201,12 @@ impl RuntimeError {
pub fn downcast<T: Error + 'static>(self) -> Result<T, Self> {
match Arc::try_unwrap(self.inner) {
// We only try to downcast user errors
#[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),
}),
Expand All @@ -104,7 +217,10 @@ impl RuntimeError {
/// Returns true if the `RuntimeError` is the same as T
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,
}
}
Expand All @@ -125,6 +241,7 @@ impl fmt::Display for RuntimeError {
}
}

#[cfg(feature = "std")]
impl std::error::Error for RuntimeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self.inner.as_ref() {
Expand Down
29 changes: 29 additions & 0 deletions lib/compiler/src/engine/trap/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,19 @@ pub struct RuntimeError {
inner: Arc<RuntimeErrorInner>,
}

pub trait CoreError: fmt::Debug + fmt::Display + core::any::Any {}

impl<T: fmt::Debug + fmt::Display + core::any::Any> CoreError for T {}

/// The source of the `RuntimeError`.
#[derive(Debug)]
enum RuntimeErrorSource {
Generic(String),
OutOfMemory,
#[cfg(feature = "std")]
User(Box<dyn Error + Send + Sync>),
#[cfg(feature = "core")]
User(Box<dyn CoreError + Send + Sync>),
Trap(TrapCode),
}

Expand Down Expand Up @@ -110,6 +117,7 @@ impl RuntimeError {
///
/// This error object can be passed through Wasm frames and later retrieved
/// using the `downcast` method.
#[cfg(feature = "std")]
pub fn user(error: Box<dyn Error + Send + Sync>) -> Self {
match error.downcast::<Self>() {
// The error is already a RuntimeError, we return it directly
Expand All @@ -126,6 +134,27 @@ impl RuntimeError {
}
}

/// Creates a custom user Error.
///
/// This error object can be passed through Wasm frames and later retrieved
/// using the `downcast` method.
#[cfg(feature = "core")]
pub fn user(error: Box<dyn CoreError + Send + Sync>) -> Self {
match error.downcast::<Self>() {
// The error is already a RuntimeError, we return it directly
Ok(runtime_error) => *runtime_error,
Err(error) => {
let info = FRAME_INFO.read().unwrap();
Self::new_with_trace(
&info,
None,
RuntimeErrorSource::User(error),
Backtrace::new_unresolved(),
)
}
}
}

fn new_with_trace(
info: &GlobalFrameInfo,
trap_pc: Option<usize>,
Expand Down

0 comments on commit aedbdcc

Please sign in to comment.