From eb58e62f9cb538efc9d3d5d93cc789928dffbb4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Wed, 31 Aug 2022 15:13:07 +0200 Subject: [PATCH 1/7] Initial port of make test-js-core (port wasmer API to core) --- Makefile | 3 +++ lib/api/src/js/error.rs | 9 ++++++--- lib/api/src/js/mod.rs | 6 +++--- lib/api/src/js/trap.rs | 19 ++++++++++++++++++ lib/compiler/src/engine/trap/error.rs | 29 +++++++++++++++++++++++++++ 5 files changed, 60 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 8355918c5a3..7ad96636e9b 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/lib/api/src/js/error.rs b/lib/api/src/js/error.rs index f334b4a1cec..0bd656af7d0 100644 --- a/lib/api/src/js/error.rs +++ b/lib/api/src/js/error.rs @@ -2,6 +2,8 @@ use crate::js::lib::std::string::String; use crate::js::trap::RuntimeError; #[cfg(feature = "std")] use std::borrow::Cow; +#[cfg(feature = "core")] +use crate::alloc::borrow::Cow; #[cfg(feature = "std")] use thiserror::Error; @@ -115,7 +117,7 @@ impl From 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), @@ -124,11 +126,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), diff --git a/lib/api/src/js/mod.rs b/lib/api/src/js/mod.rs index a642e14b5b9..99aa2c22b8b 100644 --- a/lib/api/src/js/mod.rs +++ b/lib/api/src/js/mod.rs @@ -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; } @@ -23,7 +23,7 @@ mod lib { } } -mod error; +pub(crate) mod error; mod export; mod exports; mod externals; diff --git a/lib/api/src/js/trap.rs b/lib/api/src/js/trap.rs index 0d5ca531860..1273851a30b 100644 --- a/lib/api/src/js/trap.rs +++ b/lib/api/src/js/trap.rs @@ -6,6 +6,10 @@ use wasm_bindgen::convert::FromWasmAbi; use wasm_bindgen::prelude::*; use wasm_bindgen::JsValue; +pub trait CoreError: fmt::Debug + fmt::Display + core::any::Any { } + +impl CoreError for T { } + /// A struct representing an aborted instruction execution, with a message /// indicating the cause. #[wasm_bindgen] @@ -30,7 +34,10 @@ impl PartialEq for RuntimeError { #[derive(Debug)] enum RuntimeErrorSource { Generic(String), + #[cfg(feature = "std")] User(Box), + #[cfg(feature = "core")] + User(Box), Js(JsValue), } @@ -74,6 +81,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) -> Self { match error.downcast::() { // The error is already a RuntimeError, we return it directly @@ -84,6 +92,17 @@ impl RuntimeError { } } + #[cfg(feature = "core")] + pub fn user(error: Box) -> Self { + match error.downcast::() { + // 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) diff --git a/lib/compiler/src/engine/trap/error.rs b/lib/compiler/src/engine/trap/error.rs index 76ae212fb41..60e0339854a 100644 --- a/lib/compiler/src/engine/trap/error.rs +++ b/lib/compiler/src/engine/trap/error.rs @@ -12,12 +12,19 @@ pub struct RuntimeError { inner: Arc, } +pub trait CoreError: fmt::Debug + fmt::Display + core::any::Any { } + +impl CoreError for T { } + /// The source of the `RuntimeError`. #[derive(Debug)] enum RuntimeErrorSource { Generic(String), OutOfMemory, + #[cfg(feature = "std")] User(Box), + #[cfg(feature = "core")] + User(Box), Trap(TrapCode), } @@ -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) -> Self { match error.downcast::() { // The error is already a RuntimeError, we return it directly @@ -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) -> Self { + match error.downcast::() { + // 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, From 898988b440f0f6f3a2df2359e956ebea4c9b8c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 2 Sep 2022 11:20:04 +0200 Subject: [PATCH 2/7] Implement CoreError --- lib/api/src/js/externals/function.rs | 8 +++ lib/api/src/js/trap.rs | 89 +++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/lib/api/src/js/externals/function.rs b/lib/api/src/js/externals/function.rs index 5e1d169eab4..770c462ae60 100644 --- a/lib/api/src/js/externals/function.rs +++ b/lib/api/src/js/externals/function.rs @@ -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!(), } @@ -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!(), diff --git a/lib/api/src/js/trap.rs b/lib/api/src/js/trap.rs index 1273851a30b..c7453e5280a 100644 --- a/lib/api/src/js/trap.rs +++ b/lib/api/src/js/trap.rs @@ -6,9 +6,75 @@ use wasm_bindgen::convert::FromWasmAbi; use wasm_bindgen::prelude::*; use wasm_bindgen::JsValue; -pub trait CoreError: fmt::Debug + fmt::Display + core::any::Any { } +pub trait CoreError: fmt::Debug + fmt::Display { + fn source(&self) -> Option<&(dyn CoreError + 'static)> { + None + } -impl CoreError for T { } + fn type_id(&self) -> core::any::TypeId + where + Self: 'static, + { + core::any::TypeId::of::() + } + + fn description(&self) -> &str { + "description() is deprecated; use Display" + } + fn cause(&self) -> Option<&dyn CoreError> { + self.source() + } +} + +impl CoreError for T { } + +impl dyn CoreError + 'static { + /// Returns `true` if the inner type is the same as `T`. + pub fn core_is_equal(&self) -> bool { + let t = core::any::TypeId::of::(); + 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(self: Box) -> Result, Box> { + let err: Box = self; + ::downcast_core(err).map_err(|s| unsafe { + // Reapply the `Send` marker. + core::mem::transmute::, Box>(s) + }) + } +} + +impl dyn CoreError + Send + Sync { + #[inline] + /// Attempts to downcast the box to a concrete type. + pub fn downcast_core(self: Box) -> Result, Box> { + let err: Box = self; + ::downcast_core(err).map_err(|s| unsafe { + // Reapply the `Send + Sync` marker. + core::mem::transmute::, Box>(s) + }) + } +} + +impl dyn CoreError { + #[inline] + /// Attempts to downcast the box to a concrete type. + pub fn downcast_core(self: Box) -> Result, Box> { + if self.core_is_equal::() { + 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. @@ -71,12 +137,22 @@ 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) -> ! { 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) -> ! { + 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 @@ -94,7 +170,7 @@ impl RuntimeError { #[cfg(feature = "core")] pub fn user(error: Box) -> Self { - match error.downcast::() { + match error.downcast_core::() { // The error is already a RuntimeError, we return it directly Ok(runtime_error) => *runtime_error, Err(error) => RuntimeError { @@ -112,7 +188,10 @@ impl RuntimeError { pub fn downcast(self) -> Result { match Arc::try_unwrap(self.inner) { // We only try to downcast user errors + #[cfg(feature = "std")] Ok(RuntimeErrorSource::User(err)) if err.is::() => Ok(*err.downcast::().unwrap()), + #[cfg(feature = "core")] + Ok(RuntimeErrorSource::User(err)) if (*err).core_is_equal::() => Ok(*err.downcast_core::().unwrap()), Ok(inner) => Err(Self { inner: Arc::new(inner), }), @@ -123,7 +202,10 @@ impl RuntimeError { /// Returns true if the `RuntimeError` is the same as T pub fn is(&self) -> bool { match self.inner.as_ref() { + #[cfg(feature = "std")] RuntimeErrorSource::User(err) => err.is::(), + #[cfg(feature = "core")] + RuntimeErrorSource::User(err) => (*err).core_is_equal::(), _ => false, } } @@ -144,6 +226,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() { From de9361f034cde26a78c0164edc12cc57fae57bed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 2 Sep 2022 11:22:23 +0200 Subject: [PATCH 3/7] Implement core_is_equal for Send + Sync type --- lib/api/src/js/trap.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/api/src/js/trap.rs b/lib/api/src/js/trap.rs index c7453e5280a..3626c2313e8 100644 --- a/lib/api/src/js/trap.rs +++ b/lib/api/src/js/trap.rs @@ -37,6 +37,15 @@ impl dyn CoreError + 'static { } } +impl dyn CoreError + Send + Sync + 'static { + /// Returns `true` if the inner type is the same as `T`. + pub fn core_is_equal(&self) -> bool { + let t = core::any::TypeId::of::(); + let concrete = self.type_id(); + t == concrete + } +} + impl dyn CoreError + Send { #[inline] /// Attempts to downcast the box to a concrete type. From 6ac06760adc7fc84f6a9f54f7d3c3193fa461a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 2 Sep 2022 11:23:19 +0200 Subject: [PATCH 4/7] cargo fmt --- lib/api/src/js/error.rs | 4 ++-- lib/api/src/js/trap.rs | 14 ++++++++++---- lib/compiler/src/engine/trap/error.rs | 4 ++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/api/src/js/error.rs b/lib/api/src/js/error.rs index e11c6f4a564..47fe3335a78 100644 --- a/lib/api/src/js/error.rs +++ b/lib/api/src/js/error.rs @@ -1,9 +1,9 @@ +#[cfg(feature = "core")] +use crate::alloc::borrow::Cow; use crate::js::lib::std::string::String; use crate::js::trap::RuntimeError; #[cfg(feature = "std")] use std::borrow::Cow; -#[cfg(feature = "core")] -use crate::alloc::borrow::Cow; #[cfg(feature = "std")] use thiserror::Error; use wasmer_types::ImportError; diff --git a/lib/api/src/js/trap.rs b/lib/api/src/js/trap.rs index 3626c2313e8..c9d1ac3f389 100644 --- a/lib/api/src/js/trap.rs +++ b/lib/api/src/js/trap.rs @@ -26,7 +26,7 @@ pub trait CoreError: fmt::Debug + fmt::Display { } } -impl CoreError for T { } +impl CoreError for T {} impl dyn CoreError + 'static { /// Returns `true` if the inner type is the same as `T`. @@ -49,7 +49,9 @@ impl dyn CoreError + Send + Sync + 'static { impl dyn CoreError + Send { #[inline] /// Attempts to downcast the box to a concrete type. - pub fn downcast_core(self: Box) -> Result, Box> { + pub fn downcast_core( + self: Box, + ) -> Result, Box> { let err: Box = self; ::downcast_core(err).map_err(|s| unsafe { // Reapply the `Send` marker. @@ -73,7 +75,9 @@ impl dyn CoreError + Send + Sync { impl dyn CoreError { #[inline] /// Attempts to downcast the box to a concrete type. - pub fn downcast_core(self: Box) -> Result, Box> { + pub fn downcast_core( + self: Box, + ) -> Result, Box> { if self.core_is_equal::() { unsafe { let raw: *mut dyn CoreError = Box::into_raw(self); @@ -200,7 +204,9 @@ impl RuntimeError { #[cfg(feature = "std")] Ok(RuntimeErrorSource::User(err)) if err.is::() => Ok(*err.downcast::().unwrap()), #[cfg(feature = "core")] - Ok(RuntimeErrorSource::User(err)) if (*err).core_is_equal::() => Ok(*err.downcast_core::().unwrap()), + Ok(RuntimeErrorSource::User(err)) if (*err).core_is_equal::() => { + Ok(*err.downcast_core::().unwrap()) + } Ok(inner) => Err(Self { inner: Arc::new(inner), }), diff --git a/lib/compiler/src/engine/trap/error.rs b/lib/compiler/src/engine/trap/error.rs index 60e0339854a..e1c0dab5e62 100644 --- a/lib/compiler/src/engine/trap/error.rs +++ b/lib/compiler/src/engine/trap/error.rs @@ -12,9 +12,9 @@ pub struct RuntimeError { inner: Arc, } -pub trait CoreError: fmt::Debug + fmt::Display + core::any::Any { } +pub trait CoreError: fmt::Debug + fmt::Display + core::any::Any {} -impl CoreError for T { } +impl CoreError for T {} /// The source of the `RuntimeError`. #[derive(Debug)] From b9496c36c588ab1f815dba05ee42779c228b8443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 2 Sep 2022 11:24:40 +0200 Subject: [PATCH 5/7] Add no-std JS tests to CI --- .github/workflows/test-js.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test-js.yaml b/.github/workflows/test-js.yaml index 486eac08bc9..f83064f488c 100644 --- a/.github/workflows/test-js.yaml +++ b/.github/workflows/test-js.yaml @@ -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 \ No newline at end of file From 018eaf171e0d170b70775e9d266675b94b8d1a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 2 Sep 2022 11:33:00 +0200 Subject: [PATCH 6/7] Fix CI errors --- lib/api/src/js/error.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/api/src/js/error.rs b/lib/api/src/js/error.rs index 47fe3335a78..c664b7514e2 100644 --- a/lib/api/src/js/error.rs +++ b/lib/api/src/js/error.rs @@ -154,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)] +#[derive(Debug)] +#[cfg_attr(feature = "std", derive(Error))] #[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), } From c915967d9f47153d92d360ec812f12305f8deb27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 2 Sep 2022 16:36:48 +0200 Subject: [PATCH 7/7] Fix errors on CI again --- lib/api/src/js/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/src/js/error.rs b/lib/api/src/js/error.rs index c664b7514e2..ffc9eeff8b3 100644 --- a/lib/api/src/js/error.rs +++ b/lib/api/src/js/error.rs @@ -156,7 +156,7 @@ pub enum DeserializeError { /// [link-error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/LinkError #[derive(Debug)] #[cfg_attr(feature = "std", derive(Error))] -#[error("Link error: {0}")] +#[cfg_attr(feature = "std", error("Link error: {0}"))] pub enum LinkError { /// An error occurred when checking the import types. #[cfg_attr(feature = "std", error("Error while importing {0:?}.{1:?}: {2}"))]