From a19ffa27695619c73ba2334f09827e04bb0c0d0c Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Fri, 21 Apr 2023 12:46:35 -0700 Subject: [PATCH 01/13] Moved FrameInfo into wasmer types --- lib/api/src/js/trap.rs | 8 ++ lib/api/src/lib.rs | 8 +- lib/api/src/sys/mod.rs | 2 +- .../wasmer-capi-examples-runner/src/lib.rs | 10 +- .../tests/wasmer-c-api-test-runner/src/lib.rs | 12 +-- lib/compiler/src/engine/trap/error.rs | 7 +- lib/compiler/src/engine/trap/frame_info.rs | 99 +---------------- lib/compiler/src/engine/trap/mod.rs | 3 +- lib/types/src/compilation/address_map.rs | 2 +- lib/types/src/compilation/function.rs | 2 +- lib/types/src/compilation/mod.rs | 2 - lib/types/src/lib.rs | 4 +- lib/types/src/stack/frame.rs | 100 ++++++++++++++++++ lib/types/src/stack/mod.rs | 9 ++ .../src/{compilation => stack}/sourceloc.rs | 0 lib/types/src/{compilation => stack}/trap.rs | 0 16 files changed, 143 insertions(+), 125 deletions(-) create mode 100644 lib/types/src/stack/frame.rs create mode 100644 lib/types/src/stack/mod.rs rename lib/types/src/{compilation => stack}/sourceloc.rs (100%) rename lib/types/src/{compilation => stack}/trap.rs (100%) diff --git a/lib/api/src/js/trap.rs b/lib/api/src/js/trap.rs index e6221109609..f5551be51c5 100644 --- a/lib/api/src/js/trap.rs +++ b/lib/api/src/js/trap.rs @@ -3,6 +3,7 @@ use std::fmt; use std::sync::Arc; use wasm_bindgen::{prelude::*, JsValue}; use wasm_bindgen_downcast::DowncastJS; +use wasmer_types::FrameInfo; pub trait CoreError: fmt::Debug + fmt::Display { fn source(&self) -> Option<&(dyn CoreError + 'static)> { @@ -169,6 +170,13 @@ impl RuntimeError { wasm_bindgen::throw_val(js_error) } + /// Returns a list of function frames in WebAssembly code that led to this + /// trap happening. + pub fn trace(&self) -> &[FrameInfo] { + // unimplemented!(); + return &[]; + } + /// Creates a custom user Error. /// /// This error object can be passed through Wasm frames and later retrieved diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index 56ea197f604..ec46693dce6 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -470,10 +470,10 @@ pub use wasmer_derive::ValueType; // TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 pub use wasmer_types::{ is_wasm, Bytes, CompileError, CpuFeature, DeserializeError, ExportIndex, ExportType, - ExternType, FunctionType, GlobalInit, GlobalType, ImportType, LocalFunctionIndex, MemoryError, - MemoryType, MiddlewareError, Mutability, OnCalledAction, Pages, ParseCpuFeatureError, - SerializeError, TableType, Target, Type, ValueType, WasmError, WasmResult, WASM_MAX_PAGES, - WASM_MIN_PAGES, WASM_PAGE_SIZE, + ExternType, FrameInfo, FunctionType, GlobalInit, GlobalType, ImportType, LocalFunctionIndex, + MemoryError, MemoryType, MiddlewareError, Mutability, OnCalledAction, Pages, + ParseCpuFeatureError, SerializeError, TableType, Target, Type, ValueType, WasmError, + WasmResult, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, }; #[cfg(feature = "wat")] pub use wat::parse_bytes as wat2wasm; diff --git a/lib/api/src/sys/mod.rs b/lib/api/src/sys/mod.rs index 485b87f55b6..4bf975c994f 100644 --- a/lib/api/src/sys/mod.rs +++ b/lib/api/src/sys/mod.rs @@ -14,7 +14,7 @@ pub use target_lexicon::{Architecture, CallingConvention, OperatingSystem, Tripl pub use wasmer_compiler::{ wasmparser, CompilerConfig, FunctionMiddleware, MiddlewareReaderState, ModuleMiddleware, }; -pub use wasmer_compiler::{Artifact, EngineBuilder, Features, FrameInfo, Tunables}; +pub use wasmer_compiler::{Artifact, EngineBuilder, Features, Tunables}; #[cfg(feature = "cranelift")] pub use wasmer_compiler_cranelift::{Cranelift, CraneliftOptLevel}; #[cfg(feature = "llvm")] diff --git a/lib/c-api/examples/wasmer-capi-examples-runner/src/lib.rs b/lib/c-api/examples/wasmer-capi-examples-runner/src/lib.rs index 71f936b556e..7874adf7cbb 100644 --- a/lib/c-api/examples/wasmer-capi-examples-runner/src/lib.rs +++ b/lib/c-api/examples/wasmer-capi-examples-runner/src/lib.rs @@ -254,7 +254,7 @@ fn test_run() { println!("outputting batch to {}", path.display()); std::fs::write(&path, vcvars_modified).unwrap(); - print_wasmer_root_to_stdout(&config); + // print_wasmer_root_to_stdout(&config); let mut vcvars = std::process::Command::new("cmd"); vcvars.arg("/C"); @@ -270,7 +270,7 @@ fn test_run() { if !output.status.success() { println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stdout: {}", String::from_utf8_lossy(&output.stderr)); - print_wasmer_root_to_stdout(&config); + // print_wasmer_root_to_stdout(&config); panic!("failed to compile {test}"); } @@ -296,7 +296,7 @@ fn test_run() { println!("{output:#?}"); println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - print_wasmer_root_to_stdout(&config); + // print_wasmer_root_to_stdout(&config); panic!("failed to execute {test}"); } } else { @@ -353,7 +353,7 @@ fn test_run() { if !output.status.success() { println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - print_wasmer_root_to_stdout(&config); + // print_wasmer_root_to_stdout(&config); panic!("failed to compile {test}: {command:#?}"); } @@ -368,7 +368,7 @@ fn test_run() { if !output.status.success() { println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stdout: {}", String::from_utf8_lossy(&output.stderr)); - print_wasmer_root_to_stdout(&config); + // print_wasmer_root_to_stdout(&config); panic!("failed to execute {test} executable"); } } diff --git a/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs b/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs index f75456008bd..ea1234e3fd8 100644 --- a/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs +++ b/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs @@ -161,7 +161,7 @@ fn test_ok() { println!(); println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - print_wasmer_root_to_stdout(&config); + // print_wasmer_root_to_stdout(&config); panic!("failed to invoke vcvars64.bat {test}"); } @@ -203,7 +203,7 @@ fn test_ok() { println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); println!("output: {:#?}", output); - print_wasmer_root_to_stdout(&config); + // print_wasmer_root_to_stdout(&config); panic!("failed to compile {test}"); } @@ -225,7 +225,7 @@ fn test_ok() { println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); println!("output: {:#?}", output); - print_wasmer_root_to_stdout(&config); + // print_wasmer_root_to_stdout(&config); panic!("failed to execute {test}"); } @@ -275,7 +275,7 @@ fn test_ok() { command.arg("-o"); command.arg(&format!("{manifest_dir}/../{test}")); - print_wasmer_root_to_stdout(&config); + // print_wasmer_root_to_stdout(&config); println!("compile: {command:#?}"); // compile @@ -288,7 +288,7 @@ fn test_ok() { if !output.status.success() { println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - print_wasmer_root_to_stdout(&config); + // print_wasmer_root_to_stdout(&config); panic!("failed to compile {test}: {command:#?}"); } @@ -303,7 +303,7 @@ fn test_ok() { if !output.status.success() { println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - print_wasmer_root_to_stdout(&config); + // print_wasmer_root_to_stdout(&config); panic!("failed to execute {test}: {command:#?}"); } } diff --git a/lib/compiler/src/engine/trap/error.rs b/lib/compiler/src/engine/trap/error.rs index 80a590690d4..211dd036a26 100644 --- a/lib/compiler/src/engine/trap/error.rs +++ b/lib/compiler/src/engine/trap/error.rs @@ -1,8 +1,9 @@ -use super::frame_info::{FrameInfo, GlobalFrameInfo, FRAME_INFO}; +use super::frame_info::{GlobalFrameInfo, FRAME_INFO}; use backtrace::Backtrace; use std::error::Error; use std::fmt; use std::sync::Arc; +use wasmer_types::FrameInfo; use wasmer_vm::{Trap, TrapCode}; /// A struct representing an aborted instruction execution, with a message @@ -44,8 +45,6 @@ struct RuntimeErrorInner { source: RuntimeErrorSource, /// The reconstructed Wasm trace (from the native trace and the `GlobalFrameInfo`). wasm_trace: Vec, - /// The native backtrace - native_trace: Option, } fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) { @@ -191,7 +190,6 @@ impl RuntimeError { inner: Arc::new(RuntimeErrorInner { source, wasm_trace, - native_trace: Some(native_trace), }), } } @@ -257,7 +255,6 @@ impl fmt::Debug for RuntimeError { f.debug_struct("RuntimeError") .field("source", &self.inner.source) .field("wasm_trace", &self.inner.wasm_trace) - .field("native_trace", &self.inner.native_trace) .finish() } } diff --git a/lib/compiler/src/engine/trap/frame_info.rs b/lib/compiler/src/engine/trap/frame_info.rs index a23f7607f94..91ca9b3285e 100644 --- a/lib/compiler/src/engine/trap/frame_info.rs +++ b/lib/compiler/src/engine/trap/frame_info.rs @@ -15,8 +15,9 @@ use std::cmp; use std::collections::BTreeMap; use std::sync::{Arc, RwLock}; use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap}; -use wasmer_types::{CompiledFunctionFrameInfo, SourceLoc, TrapInformation}; -use wasmer_types::{LocalFunctionIndex, ModuleInfo}; +use wasmer_types::{ + CompiledFunctionFrameInfo, FrameInfo, LocalFunctionIndex, ModuleInfo, TrapInformation, +}; use wasmer_vm::FunctionBodyPtr; lazy_static::lazy_static! { @@ -240,97 +241,3 @@ pub fn register( assert!(prev.is_none()); Some(GlobalFrameInfoRegistration { key: max }) } - -/// Description of a frame in a backtrace for a [`RuntimeError::trace`](crate::RuntimeError::trace). -/// -/// Whenever a WebAssembly trap occurs an instance of [`RuntimeError`] -/// is created. Each [`RuntimeError`] has a backtrace of the -/// WebAssembly frames that led to the trap, and each frame is -/// described by this structure. -/// -/// [`RuntimeError`]: crate::RuntimeError -#[derive(Debug, Clone)] -pub struct FrameInfo { - module_name: String, - func_index: u32, - function_name: Option, - func_start: SourceLoc, - instr: SourceLoc, -} - -impl FrameInfo { - /// Creates a new [FrameInfo], useful for testing. - pub fn new( - module_name: String, - func_index: u32, - function_name: Option, - func_start: SourceLoc, - instr: SourceLoc, - ) -> Self { - Self { - module_name, - func_index, - function_name, - func_start, - instr, - } - } - - /// Returns the WebAssembly function index for this frame. - /// - /// This function index is the index in the function index space of the - /// WebAssembly module that this frame comes from. - pub fn func_index(&self) -> u32 { - self.func_index - } - - /// Returns the identifer of the module that this frame is for. - /// - /// ModuleInfo identifiers are present in the `name` section of a WebAssembly - /// binary, but this may not return the exact item in the `name` section. - /// ModuleInfo names can be overwritten at construction time or perhaps inferred - /// from file names. The primary purpose of this function is to assist in - /// debugging and therefore may be tweaked over time. - /// - /// This function returns `None` when no name can be found or inferred. - pub fn module_name(&self) -> &str { - &self.module_name - } - - /// Returns a descriptive name of the function for this frame, if one is - /// available. - /// - /// The name of this function may come from the `name` section of the - /// WebAssembly binary, or wasmer may try to infer a better name for it if - /// not available, for example the name of the export if it's exported. - /// - /// This return value is primarily used for debugging and human-readable - /// purposes for things like traps. Note that the exact return value may be - /// tweaked over time here and isn't guaranteed to be something in - /// particular about a wasm module due to its primary purpose of assisting - /// in debugging. - /// - /// This function returns `None` when no name could be inferred. - pub fn function_name(&self) -> Option<&str> { - self.function_name.as_deref() - } - - /// Returns the offset within the original wasm module this frame's program - /// counter was at. - /// - /// The offset here is the offset from the beginning of the original wasm - /// module to the instruction that this frame points to. - pub fn module_offset(&self) -> usize { - self.instr.bits() as usize - } - - /// Returns the offset from the original wasm module's function to this - /// frame's program counter. - /// - /// The offset here is the offset from the beginning of the defining - /// function of this frame (within the wasm module) to the instruction this - /// frame points to. - pub fn func_offset(&self) -> usize { - (self.instr.bits() - self.func_start.bits()) as usize - } -} diff --git a/lib/compiler/src/engine/trap/mod.rs b/lib/compiler/src/engine/trap/mod.rs index d1eac6b420b..3d286f9ae03 100644 --- a/lib/compiler/src/engine/trap/mod.rs +++ b/lib/compiler/src/engine/trap/mod.rs @@ -2,6 +2,5 @@ mod error; mod frame_info; pub use error::RuntimeError; pub use frame_info::{ - register as register_frame_info, FrameInfo, FunctionExtent, GlobalFrameInfoRegistration, - FRAME_INFO, + register as register_frame_info, FunctionExtent, GlobalFrameInfoRegistration, FRAME_INFO, }; diff --git a/lib/types/src/compilation/address_map.rs b/lib/types/src/compilation/address_map.rs index e9db7f467cd..2f46624b891 100644 --- a/lib/types/src/compilation/address_map.rs +++ b/lib/types/src/compilation/address_map.rs @@ -1,8 +1,8 @@ //! Data structures to provide transformation of the source // addresses of a WebAssembly module into the native code. -use super::sourceloc::SourceLoc; use crate::lib::std::vec::Vec; +use crate::SourceLoc; use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; diff --git a/lib/types/src/compilation/function.rs b/lib/types/src/compilation/function.rs index 2becb5c4652..e57476f491c 100644 --- a/lib/types/src/compilation/function.rs +++ b/lib/types/src/compilation/function.rs @@ -4,9 +4,9 @@ //! A `Compilation` contains the compiled function bodies for a WebAssembly //! module (`CompiledFunction`). -use super::trap::TrapInformation; use crate::entity::PrimaryMap; use crate::lib::std::vec::Vec; +use crate::TrapInformation; use crate::{CompiledFunctionUnwindInfo, FunctionAddressMap}; use crate::{ CustomSection, FunctionIndex, LocalFunctionIndex, Relocation, SectionIndex, SignatureIndex, diff --git a/lib/types/src/compilation/mod.rs b/lib/types/src/compilation/mod.rs index 87ad57b4dc8..624cd2de15a 100644 --- a/lib/types/src/compilation/mod.rs +++ b/lib/types/src/compilation/mod.rs @@ -5,8 +5,6 @@ pub mod function; pub mod module; pub mod relocation; pub mod section; -pub mod sourceloc; pub mod symbols; pub mod target; -pub mod trap; pub mod unwind; diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 268c59b5ce4..3f8e4993afb 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -62,6 +62,7 @@ mod libcalls; mod memory; mod module; mod serialize; +mod stack; mod store_id; mod table; mod trapcode; @@ -125,11 +126,10 @@ pub use crate::compilation::function::{ Functions, }; pub use crate::compilation::module::CompileModuleInfo; -pub use crate::compilation::sourceloc::SourceLoc; pub use crate::compilation::symbols::{Symbol, SymbolRegistry}; -pub use crate::compilation::trap::TrapInformation; pub use crate::compilation::unwind::CompiledFunctionUnwindInfo; +pub use crate::stack::{FrameInfo, SourceLoc, TrapInformation}; pub use crate::store_id::StoreId; /// Offset in bytes from the beginning of the function. diff --git a/lib/types/src/stack/frame.rs b/lib/types/src/stack/frame.rs new file mode 100644 index 00000000000..10e69610b9b --- /dev/null +++ b/lib/types/src/stack/frame.rs @@ -0,0 +1,100 @@ +use crate::SourceLoc; + +/// Description of a frame in a backtrace for a [`RuntimeError::trace`](crate::RuntimeError::trace). +/// +/// Whenever a WebAssembly trap occurs an instance of [`RuntimeError`] +/// is created. Each [`RuntimeError`] has a backtrace of the +/// WebAssembly frames that led to the trap, and each frame is +/// described by this structure. +/// +/// [`RuntimeError`]: crate::RuntimeError +#[derive(Debug, Clone)] +pub struct FrameInfo { + /// The name of the module + pub module_name: String, + /// The index of the function in the module + pub func_index: u32, + /// The function name, if one is available. + pub function_name: Option, + /// The source location of the function + pub func_start: SourceLoc, + /// The source location of the instruction + pub instr: SourceLoc, +} + +impl FrameInfo { + /// Creates a new [FrameInfo], useful for testing. + pub fn new( + module_name: String, + func_index: u32, + function_name: Option, + func_start: SourceLoc, + instr: SourceLoc, + ) -> Self { + Self { + module_name, + func_index, + function_name, + func_start, + instr, + } + } + + /// Returns the WebAssembly function index for this frame. + /// + /// This function index is the index in the function index space of the + /// WebAssembly module that this frame comes from. + pub fn func_index(&self) -> u32 { + self.func_index + } + + /// Returns the identifer of the module that this frame is for. + /// + /// ModuleInfo identifiers are present in the `name` section of a WebAssembly + /// binary, but this may not return the exact item in the `name` section. + /// ModuleInfo names can be overwritten at construction time or perhaps inferred + /// from file names. The primary purpose of this function is to assist in + /// debugging and therefore may be tweaked over time. + /// + /// This function returns `None` when no name can be found or inferred. + pub fn module_name(&self) -> &str { + &self.module_name + } + + /// Returns a descriptive name of the function for this frame, if one is + /// available. + /// + /// The name of this function may come from the `name` section of the + /// WebAssembly binary, or wasmer may try to infer a better name for it if + /// not available, for example the name of the export if it's exported. + /// + /// This return value is primarily used for debugging and human-readable + /// purposes for things like traps. Note that the exact return value may be + /// tweaked over time here and isn't guaranteed to be something in + /// particular about a wasm module due to its primary purpose of assisting + /// in debugging. + /// + /// This function returns `None` when no name could be inferred. + pub fn function_name(&self) -> Option<&str> { + self.function_name.as_deref() + } + + /// Returns the offset within the original wasm module this frame's program + /// counter was at. + /// + /// The offset here is the offset from the beginning of the original wasm + /// module to the instruction that this frame points to. + pub fn module_offset(&self) -> usize { + self.instr.bits() as usize + } + + /// Returns the offset from the original wasm module's function to this + /// frame's program counter. + /// + /// The offset here is the offset from the beginning of the defining + /// function of this frame (within the wasm module) to the instruction this + /// frame points to. + pub fn func_offset(&self) -> usize { + (self.instr.bits() - self.func_start.bits()) as usize + } +} diff --git a/lib/types/src/stack/mod.rs b/lib/types/src/stack/mod.rs new file mode 100644 index 00000000000..0e36b9f495e --- /dev/null +++ b/lib/types/src/stack/mod.rs @@ -0,0 +1,9 @@ +//! Types for the stack tracing / frames. + +mod frame; +mod sourceloc; +mod trap; + +pub use frame::FrameInfo; +pub use sourceloc::SourceLoc; +pub use trap::TrapInformation; diff --git a/lib/types/src/compilation/sourceloc.rs b/lib/types/src/stack/sourceloc.rs similarity index 100% rename from lib/types/src/compilation/sourceloc.rs rename to lib/types/src/stack/sourceloc.rs diff --git a/lib/types/src/compilation/trap.rs b/lib/types/src/stack/trap.rs similarity index 100% rename from lib/types/src/compilation/trap.rs rename to lib/types/src/stack/trap.rs From 2b3442f4fcab4b76dea136bbb0e014f97ab30afa Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Fri, 21 Apr 2023 20:56:01 -0700 Subject: [PATCH 02/13] Improved a bit the API --- lib/api/src/sys/externals/function.rs | 2 +- lib/api/src/sys/externals/table.rs | 4 +- lib/compiler/src/engine/artifact.rs | 6 +- lib/compiler/src/engine/trap/error.rs | 86 ++++++++++++--------------- 4 files changed, 45 insertions(+), 53 deletions(-) diff --git a/lib/api/src/sys/externals/function.rs b/lib/api/src/sys/externals/function.rs index adfbbc9cbc7..1b773ae2ea4 100644 --- a/lib/api/src/sys/externals/function.rs +++ b/lib/api/src/sys/externals/function.rs @@ -301,7 +301,7 @@ impl Function { r }; if let Err(error) = result { - return Err(RuntimeError::from_trap(error)); + return Err(error.into()); } // Load the return values out of `values_vec`. diff --git a/lib/api/src/sys/externals/table.rs b/lib/api/src/sys/externals/table.rs index 3067094c91b..6aea055be7b 100644 --- a/lib/api/src/sys/externals/table.rs +++ b/lib/api/src/sys/externals/table.rs @@ -2,7 +2,7 @@ use crate::store::{AsStoreMut, AsStoreRef}; use crate::TableType; use crate::Value; use crate::{vm::VMExternTable, ExternRef, Function, RuntimeError}; -use wasmer_vm::{StoreHandle, TableElement, VMExtern, VMTable}; +use wasmer_vm::{StoreHandle, TableElement, Trap, VMExtern, VMTable}; #[derive(Debug, Clone)] pub struct Table { @@ -130,7 +130,7 @@ impl Table { ); VMTable::copy(dst_table, src_table, dst_index, src_index, len) } - .map_err(RuntimeError::from_trap)?; + .map_err(Into::::into)?; Ok(()) } diff --git a/lib/compiler/src/engine/artifact.rs b/lib/compiler/src/engine/artifact.rs index 78bcbc1a845..8fdb8e162d0 100644 --- a/lib/compiler/src/engine/artifact.rs +++ b/lib/compiler/src/engine/artifact.rs @@ -8,7 +8,7 @@ use crate::Features; use crate::ModuleEnvironment; use crate::{ register_frame_info, resolve_imports, FunctionExtent, GlobalFrameInfoRegistration, - InstantiationError, RuntimeError, Tunables, + InstantiationError, Tunables, }; #[cfg(feature = "static-artifact-create")] use crate::{Compiler, FunctionBodyData, ModuleTranslationState}; @@ -526,7 +526,7 @@ impl Artifact { imports, self.signatures().clone(), ) - .map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap)))?; + .map_err(|trap| InstantiationError::Start(trap.into()))?; Ok(handle) } @@ -551,7 +551,7 @@ impl Artifact { .collect::>(); handle .finish_instantiation(trap_handler, &data_initializers) - .map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap))) + .map_err(|trap| InstantiationError::Start(trap.into())) } #[allow(clippy::type_complexity)] diff --git a/lib/compiler/src/engine/trap/error.rs b/lib/compiler/src/engine/trap/error.rs index 211dd036a26..6bd6e59a431 100644 --- a/lib/compiler/src/engine/trap/error.rs +++ b/lib/compiler/src/engine/trap/error.rs @@ -70,48 +70,6 @@ impl RuntimeError { ) } - /// Create a new RuntimeError from a Trap. - pub fn from_trap(trap: Trap) -> Self { - let info = FRAME_INFO.read().unwrap(); - match trap { - // A user error - Trap::User(error) => { - match error.downcast::() { - // The error is already a RuntimeError, we return it directly - Ok(runtime_error) => *runtime_error, - Err(e) => Self::new_with_trace( - &info, - None, - RuntimeErrorSource::User(e), - Backtrace::new_unresolved(), - ), - } - } - // A trap caused by the VM being Out of Memory - Trap::OOM { backtrace } => { - Self::new_with_trace(&info, None, RuntimeErrorSource::OutOfMemory, backtrace) - } - // A trap caused by an error on the generated machine code for a Wasm function - Trap::Wasm { - pc, - signal_trap, - backtrace, - } => { - let code = info - .lookup_trap_info(pc) - .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| { - info.trap_code - }); - Self::new_with_trace(&info, Some(pc), RuntimeErrorSource::Trap(code), backtrace) - } - // A trap triggered manually from the Wasmer runtime - Trap::Lib { - trap_code, - backtrace, - } => Self::new_with_trace(&info, None, RuntimeErrorSource::Trap(trap_code), backtrace), - } - } - /// Creates a custom user Error. /// /// This error object can be passed through Wasm frames and later retrieved @@ -187,10 +145,7 @@ impl RuntimeError { .collect::>(); Self { - inner: Arc::new(RuntimeErrorInner { - source, - wasm_trace, - }), + inner: Arc::new(RuntimeErrorInner { source, wasm_trace }), } } @@ -302,6 +257,43 @@ impl std::error::Error for RuntimeError { impl From for RuntimeError { fn from(trap: Trap) -> Self { - Self::from_trap(trap) + let info = FRAME_INFO.read().unwrap(); + match trap { + // A user error + Trap::User(error) => { + match error.downcast::() { + // The error is already a RuntimeError, we return it directly + Ok(runtime_error) => *runtime_error, + Err(e) => Self::new_with_trace( + &info, + None, + RuntimeErrorSource::User(e), + Backtrace::new_unresolved(), + ), + } + } + // A trap caused by the VM being Out of Memory + Trap::OOM { backtrace } => { + Self::new_with_trace(&info, None, RuntimeErrorSource::OutOfMemory, backtrace) + } + // A trap caused by an error on the generated machine code for a Wasm function + Trap::Wasm { + pc, + signal_trap, + backtrace, + } => { + let code = info + .lookup_trap_info(pc) + .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| { + info.trap_code + }); + Self::new_with_trace(&info, Some(pc), RuntimeErrorSource::Trap(code), backtrace) + } + // A trap triggered manually from the Wasmer runtime + Trap::Lib { + trap_code, + backtrace, + } => Self::new_with_trace(&info, None, RuntimeErrorSource::Trap(trap_code), backtrace), + } } } From 25d415d8b7bdfe9111c9d10d37d3084e8ce9adf2 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Fri, 21 Apr 2023 21:14:18 -0700 Subject: [PATCH 03/13] Improved error --- lib/compiler/src/engine/trap/error.rs | 28 ++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/compiler/src/engine/trap/error.rs b/lib/compiler/src/engine/trap/error.rs index 6bd6e59a431..101a83709ab 100644 --- a/lib/compiler/src/engine/trap/error.rs +++ b/lib/compiler/src/engine/trap/error.rs @@ -17,10 +17,32 @@ pub trait CoreError: fmt::Debug + fmt::Display + core::any::Any {} impl CoreError for T {} +#[derive(Debug)] +struct RuntimeStringError { + details: String, +} + +impl RuntimeStringError { + fn new(msg: String) -> RuntimeStringError { + RuntimeStringError { details: msg } + } +} + +impl fmt::Display for RuntimeStringError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.details) + } +} + +impl Error for RuntimeStringError { + fn description(&self) -> &str { + &self.details + } +} + /// The source of the `RuntimeError`. #[derive(Debug)] enum RuntimeErrorSource { - Generic(String), OutOfMemory, #[cfg(feature = "std")] User(Box), @@ -32,7 +54,6 @@ enum 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::OutOfMemory => write!(f, "Wasmer VM out of memory"), Self::Trap(s) => write!(f, "{}", s.message()), @@ -62,10 +83,11 @@ impl RuntimeError { pub fn new>(message: I) -> Self { let info = FRAME_INFO.read().unwrap(); let msg = message.into(); + let source = RuntimeStringError::new(msg); Self::new_with_trace( &info, None, - RuntimeErrorSource::Generic(msg), + RuntimeErrorSource::User(Box::new(source)), Backtrace::new_unresolved(), ) } From 013fb1417f0f4583a625d28191b3c79812aaa3e7 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Fri, 21 Apr 2023 22:09:55 -0700 Subject: [PATCH 04/13] Trying to simplify RuntimeError --- lib/compiler/src/engine/trap/error.rs | 168 +++++++++++--------------- lib/vm/src/trap/trap.rs | 168 +++++++++++++++----------- 2 files changed, 165 insertions(+), 171 deletions(-) diff --git a/lib/compiler/src/engine/trap/error.rs b/lib/compiler/src/engine/trap/error.rs index 101a83709ab..e970d1ae78b 100644 --- a/lib/compiler/src/engine/trap/error.rs +++ b/lib/compiler/src/engine/trap/error.rs @@ -13,10 +13,6 @@ pub struct RuntimeError { inner: Arc, } -pub trait CoreError: fmt::Debug + fmt::Display + core::any::Any {} - -impl CoreError for T {} - #[derive(Debug)] struct RuntimeStringError { details: String, @@ -40,30 +36,9 @@ impl Error for RuntimeStringError { } } -/// The source of the `RuntimeError`. -#[derive(Debug)] -enum RuntimeErrorSource { - OutOfMemory, - #[cfg(feature = "std")] - User(Box), - #[cfg(feature = "core")] - User(Box), - Trap(TrapCode), -} - -impl fmt::Display for RuntimeErrorSource { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::User(s) => write!(f, "{}", s), - Self::OutOfMemory => write!(f, "Wasmer VM out of memory"), - Self::Trap(s) => write!(f, "{}", s.message()), - } - } -} - struct RuntimeErrorInner { /// The source error (this can be a custom user `Error` or a [`TrapCode`]) - source: RuntimeErrorSource, + source: Trap, /// The reconstructed Wasm trace (from the native trace and the `GlobalFrameInfo`). wasm_trace: Vec, } @@ -84,12 +59,27 @@ impl RuntimeError { let info = FRAME_INFO.read().unwrap(); let msg = message.into(); let source = RuntimeStringError::new(msg); - Self::new_with_trace( - &info, - None, - RuntimeErrorSource::User(Box::new(source)), - Backtrace::new_unresolved(), - ) + Self::new_with_trace(&info, Trap::user(Box::new(source))) + } + + /// Creates `RuntimeError` from an error and a WasmTrace + /// + /// # Example + /// ``` + /// let wasm_trace = vec![wasmer_types::FrameInfo { + /// module_name: "my_module".to_string(), + /// func_index: 0, + /// function_name: Some("my_function".to_string()), + /// func_start: 0.into(), + /// instr: 2.into() + /// }]; + /// let trap = wasmer_compiler::RuntimeError::new(my_error, wasm_trace); + /// assert_eq!("unexpected error", trap.message()); + /// ``` + pub fn new_from_source(source: Trap, wasm_trace: Vec) -> Self { + Self { + inner: Arc::new(RuntimeErrorInner { source, wasm_trace }), + } } /// Creates a custom user Error. @@ -103,12 +93,7 @@ impl RuntimeError { 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(), - ) + Self::new_with_trace(&info, Trap::user(error)) } } } @@ -124,24 +109,18 @@ impl RuntimeError { 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(), - ) + Self::new_with_trace(&info, Trap::user(error)) } } } - fn new_with_trace( + fn wasm_trace( info: &GlobalFrameInfo, trap_pc: Option, - source: RuntimeErrorSource, - native_trace: Backtrace, - ) -> Self { + backtrace: &Backtrace, + ) -> Vec { // Let's construct the trace - let wasm_trace = native_trace + backtrace .frames() .iter() .filter_map(|frame| { @@ -164,11 +143,29 @@ impl RuntimeError { } }) .filter_map(|pc| info.lookup_frame_info(pc)) - .collect::>(); + .collect::>() + } - Self { - inner: Arc::new(RuntimeErrorInner { source, wasm_trace }), - } + fn new_with_trace(info: &GlobalFrameInfo, source: Trap) -> Self { + let wasm_trace: Vec = match &source { + // A user error + Trap::User(_) => Self::wasm_trace(info, None, &Backtrace::new_unresolved()), + // A trap caused by the VM being Out of Memory + Trap::OOM { backtrace } => Self::wasm_trace(info, None, backtrace), + // A trap caused by an error on the generated machine code for a Wasm function + Trap::Wasm { + pc, + signal_trap: _, + backtrace, + } => Self::wasm_trace(info, Some(*pc), backtrace), + // A trap triggered manually from the Wasmer runtime + Trap::Lib { + trap_code: _, + backtrace, + } => Self::wasm_trace(info, None, backtrace), + }; + + Self::new_from_source(source, wasm_trace) } /// Returns a reference the `message` stored in `Trap`. @@ -187,7 +184,7 @@ impl RuntimeError { match Arc::try_unwrap(self.inner) { // We only try to downcast user errors Ok(RuntimeErrorInner { - source: RuntimeErrorSource::User(err), + source: Trap::User(err), .. }) if err.is::() => Ok(*err.downcast::().unwrap()), Ok(inner) => Err(Self { @@ -202,7 +199,7 @@ impl RuntimeError { match self.inner.as_ref() { // We only try to downcast user errors RuntimeErrorInner { - source: RuntimeErrorSource::User(err), + source: Trap::User(err), .. } if err.is::() => err.downcast_ref::(), _ => None, @@ -211,17 +208,26 @@ impl RuntimeError { /// Returns trap code, if it's a Trap pub fn to_trap(self) -> Option { - if let RuntimeErrorSource::Trap(trap_code) = self.inner.source { - Some(trap_code) - } else { - None + match self.inner.source { + Trap::Wasm { + pc, signal_trap, .. + } => { + let info = FRAME_INFO.read().unwrap(); + let code: TrapCode = info + .lookup_trap_info(pc) + .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| { + info.trap_code + }); + Some(code) + } + _ => None, } } /// Returns true if the `RuntimeError` is the same as T pub fn is(&self) -> bool { match &self.inner.source { - RuntimeErrorSource::User(err) => err.is::(), + Trap::User(err) => err.is::(), _ => false, } } @@ -270,8 +276,8 @@ impl fmt::Display for RuntimeError { impl std::error::Error for RuntimeError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self.inner.source { - RuntimeErrorSource::User(err) => Some(&**err), - RuntimeErrorSource::Trap(err) => Some(err), + Trap::User(err) => Some(&**err), + // RuntimeErrorSource::Trap(err) => Some(err), _ => None, } } @@ -280,42 +286,6 @@ impl std::error::Error for RuntimeError { impl From for RuntimeError { fn from(trap: Trap) -> Self { let info = FRAME_INFO.read().unwrap(); - match trap { - // A user error - Trap::User(error) => { - match error.downcast::() { - // The error is already a RuntimeError, we return it directly - Ok(runtime_error) => *runtime_error, - Err(e) => Self::new_with_trace( - &info, - None, - RuntimeErrorSource::User(e), - Backtrace::new_unresolved(), - ), - } - } - // A trap caused by the VM being Out of Memory - Trap::OOM { backtrace } => { - Self::new_with_trace(&info, None, RuntimeErrorSource::OutOfMemory, backtrace) - } - // A trap caused by an error on the generated machine code for a Wasm function - Trap::Wasm { - pc, - signal_trap, - backtrace, - } => { - let code = info - .lookup_trap_info(pc) - .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| { - info.trap_code - }); - Self::new_with_trace(&info, Some(pc), RuntimeErrorSource::Trap(code), backtrace) - } - // A trap triggered manually from the Wasmer runtime - Trap::Lib { - trap_code, - backtrace, - } => Self::new_with_trace(&info, None, RuntimeErrorSource::Trap(trap_code), backtrace), - } + Self::new_with_trace(&info, trap) } } diff --git a/lib/vm/src/trap/trap.rs b/lib/vm/src/trap/trap.rs index 0934364b024..2bba0e7d831 100644 --- a/lib/vm/src/trap/trap.rs +++ b/lib/vm/src/trap/trap.rs @@ -1,72 +1,96 @@ -use backtrace::Backtrace; -use std::error::Error; -use wasmer_types::TrapCode; - -/// Stores trace message with backtrace. -#[derive(Debug)] -pub enum Trap { - /// A user-raised trap through `raise_user_trap`. - User(Box), - - /// A trap raised from the Wasm generated code - /// - /// Note: this trap is deterministic (assuming a deterministic host implementation) - Wasm { - /// The program counter in generated code where this trap happened. - pc: usize, - /// Native stack backtrace at the time the trap occurred - backtrace: Backtrace, - /// Optional trapcode associated to the signal that caused the trap - signal_trap: Option, - }, - - /// A trap raised from a wasm libcall - /// - /// Note: this trap is deterministic (assuming a deterministic host implementation) - Lib { - /// Code of the trap. - trap_code: TrapCode, - /// Native stack backtrace at the time the trap occurred - backtrace: Backtrace, - }, - - /// A trap indicating that the runtime was unable to allocate sufficient memory. - /// - /// Note: this trap is nondeterministic, since it depends on the host system. - OOM { - /// Native stack backtrace at the time the OOM occurred - backtrace: Backtrace, - }, -} - -impl Trap { - /// Construct a new Wasm trap with the given source location and backtrace. - /// - /// Internally saves a backtrace when constructed. - pub fn wasm(pc: usize, backtrace: Backtrace, signal_trap: Option) -> Self { - Self::Wasm { - pc, - backtrace, - signal_trap, - } - } - - /// Construct a new Wasm trap with the given trap code. - /// - /// Internally saves a backtrace when constructed. - pub fn lib(trap_code: TrapCode) -> Self { - let backtrace = Backtrace::new_unresolved(); - Self::Lib { - trap_code, - backtrace, - } - } - - /// Construct a new OOM trap with the given source location and trap code. - /// - /// Internally saves a backtrace when constructed. - pub fn oom() -> Self { - let backtrace = Backtrace::new_unresolved(); - Self::OOM { backtrace } - } -} +use backtrace::Backtrace; +use std::error::Error; +use std::fmt; +use wasmer_types::TrapCode; + +/// Stores trace message with backtrace. +#[derive(Debug)] +pub enum Trap { + /// A user-raised trap through `raise_user_trap`. + User(Box), + + /// A trap raised from the Wasm generated code + /// + /// Note: this trap is deterministic (assuming a deterministic host implementation) + Wasm { + /// The program counter in generated code where this trap happened. + pc: usize, + /// Native stack backtrace at the time the trap occurred + backtrace: Backtrace, + /// Optional trapcode associated to the signal that caused the trap + signal_trap: Option, + }, + + /// A trap raised from a wasm libcall + /// + /// Note: this trap is deterministic (assuming a deterministic host implementation) + Lib { + /// Code of the trap. + trap_code: TrapCode, + /// Native stack backtrace at the time the trap occurred + backtrace: Backtrace, + }, + + /// A trap indicating that the runtime was unable to allocate sufficient memory. + /// + /// Note: this trap is nondeterministic, since it depends on the host system. + OOM { + /// Native stack backtrace at the time the OOM occurred + backtrace: Backtrace, + }, +} + +impl Trap { + /// Construct a new Error with the given a user error. + /// + /// Internally saves a backtrace when constructed. + pub fn user(err: Box) -> Self { + Self::User(err) + } + + /// Construct a new Wasm trap with the given source location and backtrace. + /// + /// Internally saves a backtrace when constructed. + pub fn wasm(pc: usize, backtrace: Backtrace, signal_trap: Option) -> Self { + Self::Wasm { + pc, + backtrace, + signal_trap, + } + } + + /// Returns trap code, if it's a Trap + pub fn to_trap(self) -> Option { + unimplemented!() + } + + /// Construct a new Wasm trap with the given trap code. + /// + /// Internally saves a backtrace when constructed. + pub fn lib(trap_code: TrapCode) -> Self { + let backtrace = Backtrace::new_unresolved(); + Self::Lib { + trap_code, + backtrace, + } + } + + /// Construct a new OOM trap with the given source location and trap code. + /// + /// Internally saves a backtrace when constructed. + pub fn oom() -> Self { + let backtrace = Backtrace::new_unresolved(); + Self::OOM { backtrace } + } +} + +impl fmt::Display for Trap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::User(e) => write!(f, "{}", e), + Self::Lib { .. } => write!(f, "lib"), + Self::Wasm { .. } => write!(f, "wasm"), + Self::OOM { .. } => write!(f, "Wasmer VM out of memory"), + } + } +} From 45441bf3adae2a26d39be8b905cf7fc28708c989 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Fri, 21 Apr 2023 22:33:13 -0700 Subject: [PATCH 05/13] Improved error implementation --- lib/compiler/src/engine/trap/error.rs | 53 ++++++++++----------------- lib/vm/src/trap/trap.rs | 26 +++++++++++++ 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/lib/compiler/src/engine/trap/error.rs b/lib/compiler/src/engine/trap/error.rs index e970d1ae78b..ee60d289ed0 100644 --- a/lib/compiler/src/engine/trap/error.rs +++ b/lib/compiler/src/engine/trap/error.rs @@ -56,16 +56,15 @@ impl RuntimeError { /// assert_eq!("unexpected error", trap.message()); /// ``` pub fn new>(message: I) -> Self { - let info = FRAME_INFO.read().unwrap(); let msg = message.into(); let source = RuntimeStringError::new(msg); - Self::new_with_trace(&info, Trap::user(Box::new(source))) + Self::user(Box::new(source)) } /// Creates `RuntimeError` from an error and a WasmTrace /// /// # Example - /// ``` + /// ```ignore /// let wasm_trace = vec![wasmer_types::FrameInfo { /// module_name: "my_module".to_string(), /// func_index: 0, @@ -179,33 +178,6 @@ impl RuntimeError { &self.inner.wasm_trace } - /// Attempts to downcast the `RuntimeError` to a concrete type. - pub fn downcast(self) -> Result { - match Arc::try_unwrap(self.inner) { - // We only try to downcast user errors - Ok(RuntimeErrorInner { - source: Trap::User(err), - .. - }) if err.is::() => Ok(*err.downcast::().unwrap()), - Ok(inner) => Err(Self { - inner: Arc::new(inner), - }), - Err(inner) => Err(Self { inner }), - } - } - - /// Attempts to downcast the `RuntimeError` to a concrete type. - pub fn downcast_ref(&self) -> Option<&T> { - match self.inner.as_ref() { - // We only try to downcast user errors - RuntimeErrorInner { - source: Trap::User(err), - .. - } if err.is::() => err.downcast_ref::(), - _ => None, - } - } - /// Returns trap code, if it's a Trap pub fn to_trap(self) -> Option { match self.inner.source { @@ -224,12 +196,25 @@ impl RuntimeError { } } + /// Attempts to downcast the `RuntimeError` to a concrete type. + pub fn downcast(self) -> Result { + match Arc::try_unwrap(self.inner) { + Ok(inner) if inner.source.is::() => Ok(inner.source.downcast::().unwrap()), + Ok(inner) => Err(Self { + inner: Arc::new(inner), + }), + Err(inner) => Err(Self { inner }), + } + } + + /// Attempts to downcast the `RuntimeError` to a concrete type. + pub fn downcast_ref(&self) -> Option<&T> { + self.inner.as_ref().source.downcast_ref::() + } + /// Returns true if the `RuntimeError` is the same as T pub fn is(&self) -> bool { - match &self.inner.source { - Trap::User(err) => err.is::(), - _ => false, - } + self.inner.source.is::() } } diff --git a/lib/vm/src/trap/trap.rs b/lib/vm/src/trap/trap.rs index 2bba0e7d831..4f390ac41fd 100644 --- a/lib/vm/src/trap/trap.rs +++ b/lib/vm/src/trap/trap.rs @@ -82,6 +82,32 @@ impl Trap { let backtrace = Backtrace::new_unresolved(); Self::OOM { backtrace } } + + /// Attempts to downcast the `Trap` to a concrete type. + pub fn downcast(self) -> Result { + match self { + // We only try to downcast user errors + Trap::User(err) if err.is::() => Ok(*err.downcast::().unwrap()), + _ => Err(self), + } + } + + /// Attempts to downcast the `Trap` to a concrete type. + pub fn downcast_ref(&self) -> Option<&T> { + match &self { + // We only try to downcast user errors + Trap::User(err) if err.is::() => err.downcast_ref::(), + _ => None, + } + } + + /// Returns true if the `RuntimeError` is the same as T + pub fn is(&self) -> bool { + match self { + Trap::User(err) => err.is::(), + _ => false, + } + } } impl fmt::Display for Trap { From f8848422255cb8523c5708238a9b0dc47440b008 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Fri, 21 Apr 2023 23:42:47 -0700 Subject: [PATCH 06/13] Improved error --- lib/compiler/src/engine/trap/error.rs | 217 ++++++++++++++------------ 1 file changed, 121 insertions(+), 96 deletions(-) diff --git a/lib/compiler/src/engine/trap/error.rs b/lib/compiler/src/engine/trap/error.rs index ee60d289ed0..4555191b6c4 100644 --- a/lib/compiler/src/engine/trap/error.rs +++ b/lib/compiler/src/engine/trap/error.rs @@ -1,10 +1,11 @@ use super::frame_info::{GlobalFrameInfo, FRAME_INFO}; use backtrace::Backtrace; +use std::convert::TryInto; use std::error::Error; use std::fmt; use std::sync::Arc; -use wasmer_types::FrameInfo; -use wasmer_vm::{Trap, TrapCode}; +use wasmer_types::{FrameInfo, TrapCode}; +use wasmer_vm::Trap; /// A struct representing an aborted instruction execution, with a message /// indicating the cause. @@ -76,6 +77,7 @@ impl RuntimeError { /// assert_eq!("unexpected error", trap.message()); /// ``` pub fn new_from_source(source: Trap, wasm_trace: Vec) -> Self { + println!("CREATING ERROR FROM TRAP {}", source); Self { inner: Arc::new(RuntimeErrorInner { source, wasm_trace }), } @@ -85,101 +87,15 @@ 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 - Ok(runtime_error) => *runtime_error, - Err(error) => { - let info = FRAME_INFO.read().unwrap(); - Self::new_with_trace(&info, Trap::user(error)) - } - } - } - - /// 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, Trap::user(error)) - } - } - } - - fn wasm_trace( - info: &GlobalFrameInfo, - trap_pc: Option, - backtrace: &Backtrace, - ) -> Vec { - // Let's construct the trace - backtrace - .frames() - .iter() - .filter_map(|frame| { - let pc = frame.ip() as usize; - if pc == 0 { - None - } else { - // Note that we need to be careful about the pc we pass in here to - // lookup frame information. This program counter is used to - // translate back to an original source location in the origin wasm - // module. If this pc is the exact pc that the trap happened at, - // then we look up that pc precisely. Otherwise backtrace - // information typically points at the pc *after* the call - // instruction (because otherwise it's likely a call instruction on - // the stack). In that case we want to lookup information for the - // previous instruction (the call instruction) so we subtract one as - // the lookup. - let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 }; - Some(pc_to_lookup) - } - }) - .filter_map(|pc| info.lookup_frame_info(pc)) - .collect::>() - } - - fn new_with_trace(info: &GlobalFrameInfo, source: Trap) -> Self { - let wasm_trace: Vec = match &source { - // A user error - Trap::User(_) => Self::wasm_trace(info, None, &Backtrace::new_unresolved()), - // A trap caused by the VM being Out of Memory - Trap::OOM { backtrace } => Self::wasm_trace(info, None, backtrace), - // A trap caused by an error on the generated machine code for a Wasm function - Trap::Wasm { - pc, - signal_trap: _, - backtrace, - } => Self::wasm_trace(info, Some(*pc), backtrace), - // A trap triggered manually from the Wasmer runtime - Trap::Lib { - trap_code: _, - backtrace, - } => Self::wasm_trace(info, None, backtrace), - }; - - Self::new_from_source(source, wasm_trace) + // if error.is::() { + // return *error.downcast::().unwrap(); + // } + error.into() } /// Returns a reference the `message` stored in `Trap`. pub fn message(&self) -> String { - self.inner.source.to_string() - } - - /// Returns a list of function frames in WebAssembly code that led to this - /// trap happening. - pub fn trace(&self) -> &[FrameInfo] { - &self.inner.wasm_trace - } - - /// Returns trap code, if it's a Trap - pub fn to_trap(self) -> Option { match self.inner.source { Trap::Wasm { pc, signal_trap, .. @@ -190,10 +106,23 @@ impl RuntimeError { .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| { info.trap_code }); - Some(code) + code.message().to_string() } - _ => None, + Trap::Lib { trap_code, .. } => trap_code.message().to_string(), + _ => self.inner.source.to_string(), } + // self.inner.source.to_string() + } + + /// Returns a list of function frames in WebAssembly code that led to this + /// trap happening. + pub fn trace(&self) -> &[FrameInfo] { + &self.inner.wasm_trace + } + + /// Returns trap code, if it's a Trap + pub fn to_trap(self) -> Option { + self.try_into().ok() } /// Attempts to downcast the `RuntimeError` to a concrete type. @@ -262,15 +191,111 @@ impl std::error::Error for RuntimeError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &self.inner.source { Trap::User(err) => Some(&**err), - // RuntimeErrorSource::Trap(err) => Some(err), _ => None, } } } +#[cfg(feature = "std")] +impl From> for RuntimeError { + fn from(error: Box) -> Self { + match error.downcast::() { + // The error is already a RuntimeError, we return it directly + Ok(runtime_error) => *runtime_error, + Err(error) => Trap::user(error).into(), + } + } +} + +#[cfg(feature = "core")] +impl From> for RuntimeError { + fn from(error: Box) -> Self { + match error.downcast::() { + // The error is already a RuntimeError, we return it directly + Ok(runtime_error) => *runtime_error, + Err(error) => Trap::user(error).into(), + } + } +} + +// ------ + +impl TryInto for RuntimeError { + type Error = Self; + fn try_into(self) -> Result { + match self.inner.source { + Trap::Wasm { + pc, signal_trap, .. + } => { + let info = FRAME_INFO.read().unwrap(); + let code: TrapCode = info + .lookup_trap_info(pc) + .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| { + info.trap_code + }); + Ok(code) + } + _ => Err(self), + } + } +} + impl From for RuntimeError { fn from(trap: Trap) -> Self { + if trap.is::() { + return trap.downcast::().unwrap(); + } let info = FRAME_INFO.read().unwrap(); - Self::new_with_trace(&info, trap) + let wasm_trace: Vec = match &trap { + // A user error + Trap::User(_err) => wasm_trace(&info, None, &Backtrace::new_unresolved()), + // A trap caused by the VM being Out of Memory + Trap::OOM { backtrace } => wasm_trace(&info, None, backtrace), + // A trap caused by an error on the generated machine code for a Wasm function + Trap::Wasm { + pc, + signal_trap: _, + backtrace, + } => wasm_trace(&info, Some(*pc), backtrace), + // A trap triggered manually from the Wasmer runtime + Trap::Lib { + trap_code: _, + backtrace, + } => wasm_trace(&info, None, backtrace), + }; + + RuntimeError::new_from_source(trap, wasm_trace) } } + +fn wasm_trace( + info: &GlobalFrameInfo, + trap_pc: Option, + backtrace: &Backtrace, +) -> Vec { + // Let's construct the trace + backtrace + .frames() + .iter() + .filter_map(|frame| { + let pc = frame.ip() as usize; + if pc == 0 { + None + } else { + // Note that we need to be careful about the pc we pass in here to + // lookup frame information. This program counter is used to + // translate back to an original source location in the origin wasm + // module. If this pc is the exact pc that the trap happened at, + // then we look up that pc precisely. Otherwise backtrace + // information typically points at the pc *after* the call + // instruction (because otherwise it's likely a call instruction on + // the stack). In that case we want to lookup information for the + // previous instruction (the call instruction) so we subtract one as + // the lookup. + let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 }; + Some(pc_to_lookup) + } + }) + .filter_map(|pc| info.lookup_frame_info(pc)) + .collect::>() +} From 096f33b4d0a2e452bd6bfbbce53094c8149b519c Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sat, 22 Apr 2023 01:20:39 -0700 Subject: [PATCH 07/13] Trying to improve the errors --- lib/compiler/src/engine/trap/error.rs | 11 ++++++----- tests/compilers/issues.rs | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/lib/compiler/src/engine/trap/error.rs b/lib/compiler/src/engine/trap/error.rs index 4555191b6c4..673318b3879 100644 --- a/lib/compiler/src/engine/trap/error.rs +++ b/lib/compiler/src/engine/trap/error.rs @@ -77,7 +77,7 @@ impl RuntimeError { /// assert_eq!("unexpected error", trap.message()); /// ``` pub fn new_from_source(source: Trap, wasm_trace: Vec) -> Self { - println!("CREATING ERROR FROM TRAP {}", source); + // println!("CREATING ERROR FROM TRAP {}", source); Self { inner: Arc::new(RuntimeErrorInner { source, wasm_trace }), } @@ -88,10 +88,10 @@ impl RuntimeError { /// This error object can be passed through Wasm frames and later retrieved /// using the `downcast` method. pub fn user(error: Box) -> Self { - // if error.is::() { - // return *error.downcast::().unwrap(); - // } - error.into() + match error.downcast::() { + Ok(err) => *err, + Err(error) => error.into(), + } } /// Returns a reference the `message` stored in `Trap`. @@ -235,6 +235,7 @@ impl TryInto for RuntimeError { }); Ok(code) } + Trap::Lib { trap_code, .. } => Ok(trap_code), _ => Err(self), } } diff --git a/tests/compilers/issues.rs b/tests/compilers/issues.rs index 8ce799687fe..532ba8b8fae 100644 --- a/tests/compilers/issues.rs +++ b/tests/compilers/issues.rs @@ -264,6 +264,27 @@ fn regression_gpr_exhaustion_for_calls(mut config: crate::Config) -> Result<()> Ok(()) } +#[compiler_test(issues)] +fn test_start(mut config: crate::Config) -> Result<()> { + let mut store = config.store(); + let mut env = FunctionEnv::new(&mut store, ()); + let imports: Imports = imports! {}; + let wat = r#" + (module (func $main (unreachable)) (start $main)) + "#; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&mut store, &module, &imports); + assert!(instance.is_err()); + if let InstantiationError::Start(err) = instance.unwrap_err() { + assert_eq!(err.message(), "unreachable"); + } + else { + panic!("_start should have failed with an unreachable error") + } + + Ok(()) +} + #[compiler_test(issues)] fn test_popcnt(mut config: crate::Config) -> Result<()> { let mut store = config.store(); From 44a7e8419b5cf80e64147f641d8aaa40989a9a86 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sat, 22 Apr 2023 01:48:56 -0700 Subject: [PATCH 08/13] Fixed errors --- lib/compiler/src/engine/trap/error.rs | 82 +++++++++++---------------- tests/compilers/issues.rs | 5 +- 2 files changed, 36 insertions(+), 51 deletions(-) diff --git a/lib/compiler/src/engine/trap/error.rs b/lib/compiler/src/engine/trap/error.rs index 673318b3879..c2e2cb2af7f 100644 --- a/lib/compiler/src/engine/trap/error.rs +++ b/lib/compiler/src/engine/trap/error.rs @@ -38,8 +38,10 @@ impl Error for RuntimeStringError { } struct RuntimeErrorInner { - /// The source error (this can be a custom user `Error` or a [`TrapCode`]) + /// The source error source: Trap, + /// The trap code (if any) + trap_code: Option, /// The reconstructed Wasm trace (from the native trace and the `GlobalFrameInfo`). wasm_trace: Vec, } @@ -76,10 +78,18 @@ impl RuntimeError { /// let trap = wasmer_compiler::RuntimeError::new(my_error, wasm_trace); /// assert_eq!("unexpected error", trap.message()); /// ``` - pub fn new_from_source(source: Trap, wasm_trace: Vec) -> Self { + pub fn new_from_source( + source: Trap, + wasm_trace: Vec, + trap_code: Option, + ) -> Self { // println!("CREATING ERROR FROM TRAP {}", source); Self { - inner: Arc::new(RuntimeErrorInner { source, wasm_trace }), + inner: Arc::new(RuntimeErrorInner { + source, + wasm_trace, + trap_code, + }), } } @@ -96,22 +106,11 @@ impl RuntimeError { /// Returns a reference the `message` stored in `Trap`. pub fn message(&self) -> String { - match self.inner.source { - Trap::Wasm { - pc, signal_trap, .. - } => { - let info = FRAME_INFO.read().unwrap(); - let code: TrapCode = info - .lookup_trap_info(pc) - .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| { - info.trap_code - }); - code.message().to_string() - } - Trap::Lib { trap_code, .. } => trap_code.message().to_string(), - _ => self.inner.source.to_string(), + if let Some(trap_code) = self.inner.trap_code { + return trap_code.message().to_string(); + } else { + return self.inner.source.to_string(); } - // self.inner.source.to_string() } /// Returns a list of function frames in WebAssembly code that led to this @@ -122,7 +121,7 @@ impl RuntimeError { /// Returns trap code, if it's a Trap pub fn to_trap(self) -> Option { - self.try_into().ok() + self.inner.trap_code } /// Attempts to downcast the `RuntimeError` to a concrete type. @@ -220,52 +219,39 @@ impl From> for RuntimeError { // ------ -impl TryInto for RuntimeError { - type Error = Self; - fn try_into(self) -> Result { - match self.inner.source { - Trap::Wasm { - pc, signal_trap, .. - } => { - let info = FRAME_INFO.read().unwrap(); - let code: TrapCode = info - .lookup_trap_info(pc) - .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| { - info.trap_code - }); - Ok(code) - } - Trap::Lib { trap_code, .. } => Ok(trap_code), - _ => Err(self), - } - } -} - impl From for RuntimeError { fn from(trap: Trap) -> Self { if trap.is::() { return trap.downcast::().unwrap(); } let info = FRAME_INFO.read().unwrap(); - let wasm_trace: Vec = match &trap { + let (wasm_trace, trap_code) = match &trap { // A user error - Trap::User(_err) => wasm_trace(&info, None, &Backtrace::new_unresolved()), + Trap::User(_err) => (wasm_trace(&info, None, &Backtrace::new_unresolved()), None), // A trap caused by the VM being Out of Memory - Trap::OOM { backtrace } => wasm_trace(&info, None, backtrace), + Trap::OOM { backtrace } => (wasm_trace(&info, None, backtrace), None), // A trap caused by an error on the generated machine code for a Wasm function Trap::Wasm { pc, - signal_trap: _, + signal_trap, backtrace, - } => wasm_trace(&info, Some(*pc), backtrace), + } => { + let trap_code = info + .lookup_trap_info(*pc) + .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| { + info.trap_code + }); + + (wasm_trace(&info, Some(*pc), backtrace), Some(trap_code)) + } // A trap triggered manually from the Wasmer runtime Trap::Lib { - trap_code: _, + trap_code, backtrace, - } => wasm_trace(&info, None, backtrace), + } => (wasm_trace(&info, None, backtrace), Some(trap_code.clone())), }; - RuntimeError::new_from_source(trap, wasm_trace) + RuntimeError::new_from_source(trap, wasm_trace, trap_code) } } diff --git a/tests/compilers/issues.rs b/tests/compilers/issues.rs index 532ba8b8fae..471e465c30c 100644 --- a/tests/compilers/issues.rs +++ b/tests/compilers/issues.rs @@ -277,11 +277,10 @@ fn test_start(mut config: crate::Config) -> Result<()> { assert!(instance.is_err()); if let InstantiationError::Start(err) = instance.unwrap_err() { assert_eq!(err.message(), "unreachable"); - } - else { + } else { panic!("_start should have failed with an unreachable error") } - + Ok(()) } From a18fd0b73ad40025e35e47322bb390150a979cb8 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sat, 22 Apr 2023 01:52:11 -0700 Subject: [PATCH 09/13] Multiple fixes --- lib/compiler/src/engine/trap/error.rs | 15 +++++++-------- lib/compiler/src/engine/trap/frame_info.rs | 12 ++++++------ lib/types/src/stack/frame.rs | 10 +++++----- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/lib/compiler/src/engine/trap/error.rs b/lib/compiler/src/engine/trap/error.rs index c2e2cb2af7f..6c073d77d78 100644 --- a/lib/compiler/src/engine/trap/error.rs +++ b/lib/compiler/src/engine/trap/error.rs @@ -1,6 +1,5 @@ use super::frame_info::{GlobalFrameInfo, FRAME_INFO}; use backtrace::Backtrace; -use std::convert::TryInto; use std::error::Error; use std::fmt; use std::sync::Arc; @@ -68,13 +67,13 @@ impl RuntimeError { /// /// # Example /// ```ignore - /// let wasm_trace = vec![wasmer_types::FrameInfo { - /// module_name: "my_module".to_string(), - /// func_index: 0, - /// function_name: Some("my_function".to_string()), - /// func_start: 0.into(), - /// instr: 2.into() - /// }]; + /// let wasm_trace = vec![wasmer_types::FrameInfo::new( + /// "my_module".to_string(), + /// 0, + /// Some("my_function".to_string()), + /// 0.into(), + /// 2.into() + /// )]; /// let trap = wasmer_compiler::RuntimeError::new(my_error, wasm_trace); /// assert_eq!("unexpected error", trap.message()); /// ``` diff --git a/lib/compiler/src/engine/trap/frame_info.rs b/lib/compiler/src/engine/trap/frame_info.rs index 91ca9b3285e..4222acc1ad1 100644 --- a/lib/compiler/src/engine/trap/frame_info.rs +++ b/lib/compiler/src/engine/trap/frame_info.rs @@ -130,13 +130,13 @@ impl GlobalFrameInfo { None => instr_map.start_srcloc, }; let func_index = module.module.func_index(func.local_index); - Some(FrameInfo { - module_name: module.module.name(), - func_index: func_index.index() as u32, - function_name: module.module.function_names.get(&func_index).cloned(), + Some(FrameInfo::new( + module.module.name(), + func_index.index() as u32, + module.module.function_names.get(&func_index).cloned(), + instr_map.start_srcloc, instr, - func_start: instr_map.start_srcloc, - }) + )) } /// Fetches trap information about a program counter in a backtrace. diff --git a/lib/types/src/stack/frame.rs b/lib/types/src/stack/frame.rs index 10e69610b9b..dfd4a42c391 100644 --- a/lib/types/src/stack/frame.rs +++ b/lib/types/src/stack/frame.rs @@ -11,15 +11,15 @@ use crate::SourceLoc; #[derive(Debug, Clone)] pub struct FrameInfo { /// The name of the module - pub module_name: String, + module_name: String, /// The index of the function in the module - pub func_index: u32, + func_index: u32, /// The function name, if one is available. - pub function_name: Option, + function_name: Option, /// The source location of the function - pub func_start: SourceLoc, + func_start: SourceLoc, /// The source location of the instruction - pub instr: SourceLoc, + instr: SourceLoc, } impl FrameInfo { From faaecc5c02a3371c29a08313a1bc070d7be973ac Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sat, 22 Apr 2023 02:27:38 -0700 Subject: [PATCH 10/13] Move RuntimeError logic into sys --- Cargo.lock | 2 +- lib/api/Cargo.toml | 1 + lib/api/src/errors.rs | 237 ++++++++++++++++++++- lib/api/src/sys/errors.rs | 24 +++ lib/api/src/sys/instance.rs | 4 +- lib/api/src/sys/mod.rs | 1 + lib/compiler/Cargo.toml | 1 - lib/compiler/src/engine/error.rs | 10 +- lib/compiler/src/engine/trap/error.rs | 287 -------------------------- lib/compiler/src/engine/trap/mod.rs | 4 +- lib/compiler/src/engine/trap/stack.rs | 66 ++++++ lib/vm/src/trap/trap.rs | 13 ++ 12 files changed, 349 insertions(+), 301 deletions(-) create mode 100644 lib/api/src/sys/errors.rs delete mode 100644 lib/compiler/src/engine/trap/error.rs create mode 100644 lib/compiler/src/engine/trap/stack.rs diff --git a/Cargo.lock b/Cargo.lock index 33ed37bc27c..0bc637862f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5174,6 +5174,7 @@ dependencies = [ "js-sys", "macro-wasmer-universal-test", "more-asserts", + "rustc-demangle", "serde", "serde-wasm-bindgen", "target-lexicon 0.12.6", @@ -5353,7 +5354,6 @@ dependencies = [ "memmap2", "more-asserts", "region", - "rustc-demangle", "serde", "serde_bytes", "smallvec", diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index 810e7079c6a..6c7d3a6aa3b 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -31,6 +31,7 @@ bytes = "1" # - Optional shared dependencies. wat = { version = "1.0", optional = true } tracing = { version = "0.1", optional = true } +rustc-demangle = "0.1" # Dependencies and Development Dependencies for `sys`. [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/lib/api/src/errors.rs b/lib/api/src/errors.rs index a216cdda459..2bc0eef13f8 100644 --- a/lib/api/src/errors.rs +++ b/lib/api/src/errors.rs @@ -1,8 +1,33 @@ -#[cfg(feature = "js")] -pub use crate::js::errors::{LinkError, RuntimeError}; +use std::fmt; +use std::sync::Arc; use thiserror::Error; +use wasmer_types::{FrameInfo, TrapCode}; #[cfg(feature = "sys")] -pub use wasmer_compiler::{LinkError, RuntimeError}; +pub use wasmer_vm::Trap; + +use wasmer_types::ImportError; + +/// The WebAssembly.LinkError object indicates an error during +/// module instantiation (besides traps from the start function). +/// +/// 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(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. + #[cfg_attr(feature = "std", error("Error while importing {0:?}.{1:?}: {2}"))] + Import(String, String, ImportError), + + /// A trap ocurred during linking. + #[cfg_attr(feature = "std", error("RuntimeError occurred during linking: {0}"))] + Trap(#[source] RuntimeError), + /// Insufficient resources available for linking. + #[cfg_attr(feature = "std", error("Insufficient resources: {0}"))] + Resource(String), +} /// An error while instantiating a module. /// @@ -38,3 +63,209 @@ pub enum InstantiationError { #[cfg_attr(feature = "std", error("incorrect OS or architecture"))] DifferentArchOS, } + +/// A struct representing an aborted instruction execution, with a message +/// indicating the cause. +#[derive(Clone)] +pub struct RuntimeError { + inner: Arc, +} + +#[derive(Debug)] +struct RuntimeStringError { + details: String, +} + +impl RuntimeStringError { + fn new(msg: String) -> RuntimeStringError { + RuntimeStringError { 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 + } +} + +struct RuntimeErrorInner { + /// The source error + source: Trap, + /// The trap code (if any) + trap_code: Option, + /// The reconstructed Wasm trace (from the native trace and the `GlobalFrameInfo`). + wasm_trace: Vec, +} + +impl RuntimeError { + /// Creates a new generic `RuntimeError` with the given `message`. + /// + /// # Example + /// ``` + /// let trap = wasmer_compiler::RuntimeError::new("unexpected error"); + /// assert_eq!("unexpected error", trap.message()); + /// ``` + pub fn new>(message: I) -> Self { + let msg = message.into(); + let source = RuntimeStringError::new(msg); + Self::user(Box::new(source)) + } + + /// Creates `RuntimeError` from an error and a WasmTrace + /// + /// # Example + /// ```ignore + /// let wasm_trace = vec![wasmer_types::FrameInfo::new( + /// "my_module".to_string(), + /// 0, + /// Some("my_function".to_string()), + /// 0.into(), + /// 2.into() + /// )]; + /// let trap = wasmer_compiler::RuntimeError::new(my_error, wasm_trace); + /// assert_eq!("unexpected error", trap.message()); + /// ``` + pub fn new_from_source( + source: Trap, + wasm_trace: Vec, + trap_code: Option, + ) -> Self { + // println!("CREATING ERROR FROM TRAP {}", source); + Self { + inner: Arc::new(RuntimeErrorInner { + source, + wasm_trace, + trap_code, + }), + } + } + + /// Creates a custom user Error. + /// + /// This error object can be passed through Wasm frames and later retrieved + /// using the `downcast` method. + pub fn user(error: Box) -> Self { + match error.downcast::() { + Ok(err) => *err, + Err(error) => error.into(), + } + } + + /// Returns a reference the `message` stored in `Trap`. + pub fn message(&self) -> String { + if let Some(trap_code) = self.inner.trap_code { + return trap_code.message().to_string(); + } else { + return self.inner.source.to_string(); + } + } + + /// Returns a list of function frames in WebAssembly code that led to this + /// trap happening. + pub fn trace(&self) -> &[FrameInfo] { + &self.inner.wasm_trace + } + + /// Returns trap code, if it's a Trap + pub fn to_trap(self) -> Option { + self.inner.trap_code + } + + /// Attempts to downcast the `RuntimeError` to a concrete type. + pub fn downcast(self) -> Result { + match Arc::try_unwrap(self.inner) { + Ok(inner) if inner.source.is::() => Ok(inner.source.downcast::().unwrap()), + Ok(inner) => Err(Self { + inner: Arc::new(inner), + }), + Err(inner) => Err(Self { inner }), + } + } + + /// Attempts to downcast the `RuntimeError` to a concrete type. + pub fn downcast_ref(&self) -> Option<&T> { + self.inner.as_ref().source.downcast_ref::() + } + + /// Returns true if the `RuntimeError` is the same as T + pub fn is(&self) -> bool { + self.inner.source.is::() + } +} + +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, "")?, + } + 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)> { + match &self.inner.source { + Trap::User(err) => Some(&**err), + _ => None, + } + } +} + +#[cfg(feature = "std")] +impl From> for RuntimeError { + fn from(error: Box) -> Self { + match error.downcast::() { + // The error is already a RuntimeError, we return it directly + Ok(runtime_error) => *runtime_error, + Err(error) => Trap::user(error).into(), + } + } +} + +#[cfg(feature = "core")] +impl From> for RuntimeError { + fn from(error: Box) -> Self { + match error.downcast::() { + // The error is already a RuntimeError, we return it directly + Ok(runtime_error) => *runtime_error, + Err(error) => Trap::user(error).into(), + } + } +} diff --git a/lib/api/src/sys/errors.rs b/lib/api/src/sys/errors.rs new file mode 100644 index 00000000000..0ab9622d44c --- /dev/null +++ b/lib/api/src/sys/errors.rs @@ -0,0 +1,24 @@ +use crate::{LinkError, RuntimeError}; +use wasmer_vm::Trap; + +impl From for LinkError { + fn from(other: wasmer_compiler::LinkError) -> Self { + match other { + wasmer_compiler::LinkError::Import(namespace, name, error) => { + Self::Import(namespace, name, error) + } + wasmer_compiler::LinkError::Trap(e) => Self::Trap(e.into()), + wasmer_compiler::LinkError::Resource(e) => Self::Resource(e), + } + } +} + +impl From for RuntimeError { + fn from(trap: Trap) -> Self { + if trap.is::() { + return trap.downcast::().unwrap(); + } + let (wasm_trace, trap_code) = wasmer_compiler::get_trace_and_trapcode(&trap); + RuntimeError::new_from_source(trap, wasm_trace, trap_code) + } +} diff --git a/lib/api/src/sys/instance.rs b/lib/api/src/sys/instance.rs index 0b5060a2853..5119dfdfc09 100644 --- a/lib/api/src/sys/instance.rs +++ b/lib/api/src/sys/instance.rs @@ -29,8 +29,8 @@ mod send_test { impl From for InstantiationError { fn from(other: wasmer_compiler::InstantiationError) -> Self { match other { - wasmer_compiler::InstantiationError::Link(e) => Self::Link(e), - wasmer_compiler::InstantiationError::Start(e) => Self::Start(e), + wasmer_compiler::InstantiationError::Link(e) => Self::Link(e.into()), + wasmer_compiler::InstantiationError::Start(e) => Self::Start(e.into()), wasmer_compiler::InstantiationError::CpuFeature(e) => Self::CpuFeature(e), } } diff --git a/lib/api/src/sys/mod.rs b/lib/api/src/sys/mod.rs index 4bf975c994f..39dbe9bcb2b 100644 --- a/lib/api/src/sys/mod.rs +++ b/lib/api/src/sys/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod engine; +pub(crate) mod errors; pub(crate) mod extern_ref; pub(crate) mod externals; pub(crate) mod instance; diff --git a/lib/compiler/Cargo.toml b/lib/compiler/Cargo.toml index bbb1a11ec89..d1a53a26855 100644 --- a/lib/compiler/Cargo.toml +++ b/lib/compiler/Cargo.toml @@ -22,7 +22,6 @@ serde_bytes = { version = "0.11", optional = true } smallvec = "1.6" backtrace = "0.3" -rustc-demangle = "0.1" memmap2 = "0.5" more-asserts = "0.2" lazy_static = "1.4" diff --git a/lib/compiler/src/engine/error.rs b/lib/compiler/src/engine/error.rs index 3ee5860646d..10c5e3d440d 100644 --- a/lib/compiler/src/engine/error.rs +++ b/lib/compiler/src/engine/error.rs @@ -1,8 +1,8 @@ //! The WebAssembly possible errors -#[cfg(not(target_arch = "wasm32"))] -use crate::engine::trap::RuntimeError; use thiserror::Error; pub use wasmer_types::{DeserializeError, ImportError, SerializeError}; +#[cfg(not(target_arch = "wasm32"))] +use wasmer_vm::Trap; /// The WebAssembly.LinkError object indicates an error during /// module instantiation (besides traps from the start function). @@ -19,8 +19,8 @@ pub enum LinkError { #[cfg(not(target_arch = "wasm32"))] /// A trap ocurred during linking. - #[error("RuntimeError occurred during linking: {0}")] - Trap(#[source] RuntimeError), + #[error("Trap occurred during linking: {0}")] + Trap(#[source] Trap), /// Insufficient resources available for linking. #[error("Insufficient resources: {0}")] @@ -48,5 +48,5 @@ pub enum InstantiationError { /// A runtime error occured while invoking the start function #[cfg(not(target_arch = "wasm32"))] #[error(transparent)] - Start(RuntimeError), + Start(Trap), } diff --git a/lib/compiler/src/engine/trap/error.rs b/lib/compiler/src/engine/trap/error.rs deleted file mode 100644 index 6c073d77d78..00000000000 --- a/lib/compiler/src/engine/trap/error.rs +++ /dev/null @@ -1,287 +0,0 @@ -use super::frame_info::{GlobalFrameInfo, FRAME_INFO}; -use backtrace::Backtrace; -use std::error::Error; -use std::fmt; -use std::sync::Arc; -use wasmer_types::{FrameInfo, TrapCode}; -use wasmer_vm::Trap; - -/// A struct representing an aborted instruction execution, with a message -/// indicating the cause. -#[derive(Clone)] -pub struct RuntimeError { - inner: Arc, -} - -#[derive(Debug)] -struct RuntimeStringError { - details: String, -} - -impl RuntimeStringError { - fn new(msg: String) -> RuntimeStringError { - RuntimeStringError { details: msg } - } -} - -impl fmt::Display for RuntimeStringError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.details) - } -} - -impl Error for RuntimeStringError { - fn description(&self) -> &str { - &self.details - } -} - -struct RuntimeErrorInner { - /// The source error - source: Trap, - /// The trap code (if any) - trap_code: Option, - /// The reconstructed Wasm trace (from the native trace and the `GlobalFrameInfo`). - wasm_trace: Vec, -} - -fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) { - (t, t) -} - -impl RuntimeError { - /// Creates a new generic `RuntimeError` with the given `message`. - /// - /// # Example - /// ``` - /// let trap = wasmer_compiler::RuntimeError::new("unexpected error"); - /// assert_eq!("unexpected error", trap.message()); - /// ``` - pub fn new>(message: I) -> Self { - let msg = message.into(); - let source = RuntimeStringError::new(msg); - Self::user(Box::new(source)) - } - - /// Creates `RuntimeError` from an error and a WasmTrace - /// - /// # Example - /// ```ignore - /// let wasm_trace = vec![wasmer_types::FrameInfo::new( - /// "my_module".to_string(), - /// 0, - /// Some("my_function".to_string()), - /// 0.into(), - /// 2.into() - /// )]; - /// let trap = wasmer_compiler::RuntimeError::new(my_error, wasm_trace); - /// assert_eq!("unexpected error", trap.message()); - /// ``` - pub fn new_from_source( - source: Trap, - wasm_trace: Vec, - trap_code: Option, - ) -> Self { - // println!("CREATING ERROR FROM TRAP {}", source); - Self { - inner: Arc::new(RuntimeErrorInner { - source, - wasm_trace, - trap_code, - }), - } - } - - /// Creates a custom user Error. - /// - /// This error object can be passed through Wasm frames and later retrieved - /// using the `downcast` method. - pub fn user(error: Box) -> Self { - match error.downcast::() { - Ok(err) => *err, - Err(error) => error.into(), - } - } - - /// Returns a reference the `message` stored in `Trap`. - pub fn message(&self) -> String { - if let Some(trap_code) = self.inner.trap_code { - return trap_code.message().to_string(); - } else { - return self.inner.source.to_string(); - } - } - - /// Returns a list of function frames in WebAssembly code that led to this - /// trap happening. - pub fn trace(&self) -> &[FrameInfo] { - &self.inner.wasm_trace - } - - /// Returns trap code, if it's a Trap - pub fn to_trap(self) -> Option { - self.inner.trap_code - } - - /// Attempts to downcast the `RuntimeError` to a concrete type. - pub fn downcast(self) -> Result { - match Arc::try_unwrap(self.inner) { - Ok(inner) if inner.source.is::() => Ok(inner.source.downcast::().unwrap()), - Ok(inner) => Err(Self { - inner: Arc::new(inner), - }), - Err(inner) => Err(Self { inner }), - } - } - - /// Attempts to downcast the `RuntimeError` to a concrete type. - pub fn downcast_ref(&self) -> Option<&T> { - self.inner.as_ref().source.downcast_ref::() - } - - /// Returns true if the `RuntimeError` is the same as T - pub fn is(&self) -> bool { - self.inner.source.is::() - } -} - -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, "")?, - } - 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)> { - match &self.inner.source { - Trap::User(err) => Some(&**err), - _ => None, - } - } -} - -#[cfg(feature = "std")] -impl From> for RuntimeError { - fn from(error: Box) -> Self { - match error.downcast::() { - // The error is already a RuntimeError, we return it directly - Ok(runtime_error) => *runtime_error, - Err(error) => Trap::user(error).into(), - } - } -} - -#[cfg(feature = "core")] -impl From> for RuntimeError { - fn from(error: Box) -> Self { - match error.downcast::() { - // The error is already a RuntimeError, we return it directly - Ok(runtime_error) => *runtime_error, - Err(error) => Trap::user(error).into(), - } - } -} - -// ------ - -impl From for RuntimeError { - fn from(trap: Trap) -> Self { - if trap.is::() { - return trap.downcast::().unwrap(); - } - let info = FRAME_INFO.read().unwrap(); - let (wasm_trace, trap_code) = match &trap { - // A user error - Trap::User(_err) => (wasm_trace(&info, None, &Backtrace::new_unresolved()), None), - // A trap caused by the VM being Out of Memory - Trap::OOM { backtrace } => (wasm_trace(&info, None, backtrace), None), - // A trap caused by an error on the generated machine code for a Wasm function - Trap::Wasm { - pc, - signal_trap, - backtrace, - } => { - let trap_code = info - .lookup_trap_info(*pc) - .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| { - info.trap_code - }); - - (wasm_trace(&info, Some(*pc), backtrace), Some(trap_code)) - } - // A trap triggered manually from the Wasmer runtime - Trap::Lib { - trap_code, - backtrace, - } => (wasm_trace(&info, None, backtrace), Some(trap_code.clone())), - }; - - RuntimeError::new_from_source(trap, wasm_trace, trap_code) - } -} - -fn wasm_trace( - info: &GlobalFrameInfo, - trap_pc: Option, - backtrace: &Backtrace, -) -> Vec { - // Let's construct the trace - backtrace - .frames() - .iter() - .filter_map(|frame| { - let pc = frame.ip() as usize; - if pc == 0 { - None - } else { - // Note that we need to be careful about the pc we pass in here to - // lookup frame information. This program counter is used to - // translate back to an original source location in the origin wasm - // module. If this pc is the exact pc that the trap happened at, - // then we look up that pc precisely. Otherwise backtrace - // information typically points at the pc *after* the call - // instruction (because otherwise it's likely a call instruction on - // the stack). In that case we want to lookup information for the - // previous instruction (the call instruction) so we subtract one as - // the lookup. - let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 }; - Some(pc_to_lookup) - } - }) - .filter_map(|pc| info.lookup_frame_info(pc)) - .collect::>() -} diff --git a/lib/compiler/src/engine/trap/mod.rs b/lib/compiler/src/engine/trap/mod.rs index 3d286f9ae03..511a35e20aa 100644 --- a/lib/compiler/src/engine/trap/mod.rs +++ b/lib/compiler/src/engine/trap/mod.rs @@ -1,6 +1,6 @@ -mod error; mod frame_info; -pub use error::RuntimeError; +mod stack; pub use frame_info::{ register as register_frame_info, FunctionExtent, GlobalFrameInfoRegistration, FRAME_INFO, }; +pub use stack::get_trace_and_trapcode; diff --git a/lib/compiler/src/engine/trap/stack.rs b/lib/compiler/src/engine/trap/stack.rs new file mode 100644 index 00000000000..ca9622edb41 --- /dev/null +++ b/lib/compiler/src/engine/trap/stack.rs @@ -0,0 +1,66 @@ +use super::frame_info::{GlobalFrameInfo, FRAME_INFO}; +use backtrace::Backtrace; +use wasmer_types::{FrameInfo, TrapCode}; +use wasmer_vm::Trap; + +/// Given a `Trap`, this function returns the Wasm trace and the trap code. +pub fn get_trace_and_trapcode(trap: &Trap) -> (Vec, Option) { + let info = FRAME_INFO.read().unwrap(); + match &trap { + // A user error + Trap::User(_err) => (wasm_trace(&info, None, &Backtrace::new_unresolved()), None), + // A trap caused by the VM being Out of Memory + Trap::OOM { backtrace } => (wasm_trace(&info, None, backtrace), None), + // A trap caused by an error on the generated machine code for a Wasm function + Trap::Wasm { + pc, + signal_trap, + backtrace, + } => { + let trap_code = info + .lookup_trap_info(*pc) + .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| { + info.trap_code + }); + + (wasm_trace(&info, Some(*pc), backtrace), Some(trap_code)) + } + // A trap triggered manually from the Wasmer runtime + Trap::Lib { + trap_code, + backtrace, + } => (wasm_trace(&info, None, backtrace), Some(trap_code.clone())), + } +} + +fn wasm_trace( + info: &GlobalFrameInfo, + trap_pc: Option, + backtrace: &Backtrace, +) -> Vec { + // Let's construct the trace + backtrace + .frames() + .iter() + .filter_map(|frame| { + let pc = frame.ip() as usize; + if pc == 0 { + None + } else { + // Note that we need to be careful about the pc we pass in here to + // lookup frame information. This program counter is used to + // translate back to an original source location in the origin wasm + // module. If this pc is the exact pc that the trap happened at, + // then we look up that pc precisely. Otherwise backtrace + // information typically points at the pc *after* the call + // instruction (because otherwise it's likely a call instruction on + // the stack). In that case we want to lookup information for the + // previous instruction (the call instruction) so we subtract one as + // the lookup. + let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 }; + Some(pc_to_lookup) + } + }) + .filter_map(|pc| info.lookup_frame_info(pc)) + .collect::>() +} diff --git a/lib/vm/src/trap/trap.rs b/lib/vm/src/trap/trap.rs index 4f390ac41fd..e624debb1c3 100644 --- a/lib/vm/src/trap/trap.rs +++ b/lib/vm/src/trap/trap.rs @@ -40,6 +40,10 @@ pub enum Trap { }, } +fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) { + (t, t) +} + impl Trap { /// Construct a new Error with the given a user error. /// @@ -110,6 +114,15 @@ impl Trap { } } +impl std::error::Error for Trap { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Trap::User(err) => Some(&**err), + _ => None, + } + } +} + impl fmt::Display for Trap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { From a7abbb7dd20de53f2d9fac6bd00af824c3a30fe7 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sat, 22 Apr 2023 03:06:58 -0700 Subject: [PATCH 11/13] Make js work --- lib/api/src/errors.rs | 20 +- lib/api/src/js/errors.rs | 49 ++--- lib/api/src/js/externals/function.rs | 8 +- lib/api/src/js/mod.rs | 2 +- lib/api/src/js/trap.rs | 272 ++++----------------------- lib/vm/src/trap/trap.rs | 2 +- 6 files changed, 80 insertions(+), 273 deletions(-) diff --git a/lib/api/src/errors.rs b/lib/api/src/errors.rs index 2bc0eef13f8..de61a0e0317 100644 --- a/lib/api/src/errors.rs +++ b/lib/api/src/errors.rs @@ -1,9 +1,11 @@ +#[cfg(feature = "js")] +use crate::js::trap::Trap; use std::fmt; use std::sync::Arc; use thiserror::Error; use wasmer_types::{FrameInfo, TrapCode}; #[cfg(feature = "sys")] -pub use wasmer_vm::Trap; +use wasmer_vm::Trap; use wasmer_types::ImportError; @@ -68,7 +70,7 @@ pub enum InstantiationError { /// indicating the cause. #[derive(Clone)] pub struct RuntimeError { - inner: Arc, + pub(crate) inner: Arc, } #[derive(Debug)] @@ -94,9 +96,9 @@ impl std::error::Error for RuntimeStringError { } } -struct RuntimeErrorInner { +pub(crate) struct RuntimeErrorInner { /// The source error - source: Trap, + pub(crate) source: Trap, /// The trap code (if any) trap_code: Option, /// The reconstructed Wasm trace (from the native trace and the `GlobalFrameInfo`). @@ -177,6 +179,11 @@ impl RuntimeError { self.inner.trap_code } + // /// Returns trap code, if it's a Trap + // pub fn to_source(self) -> &'static Trap { + // &self.inner.as_ref().source + // } + /// Attempts to downcast the `RuntimeError` to a concrete type. pub fn downcast(self) -> Result { match Arc::try_unwrap(self.inner) { @@ -241,10 +248,7 @@ impl fmt::Display for RuntimeError { impl std::error::Error for RuntimeError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match &self.inner.source { - Trap::User(err) => Some(&**err), - _ => None, - } + self.inner.source.source() } } diff --git a/lib/api/src/js/errors.rs b/lib/api/src/js/errors.rs index 2f93a80b119..b4f6e02049f 100644 --- a/lib/api/src/js/errors.rs +++ b/lib/api/src/js/errors.rs @@ -1,27 +1,28 @@ -use crate::js::lib::std::string::String; -pub use crate::js::trap::RuntimeError; -#[cfg(feature = "std")] -use thiserror::Error; -use wasmer_types::ImportError; +use crate::js::trap::Trap; +use crate::RuntimeError; +use wasm_bindgen::prelude::*; -/// The WebAssembly.LinkError object indicates an error during -/// module instantiation (besides traps from the start function). -/// -/// 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(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. - #[cfg_attr(feature = "std", error("Error while importing {0:?}.{1:?}: {2}"))] - Import(String, String, ImportError), +impl From for RuntimeError { + fn from(trap: Trap) -> Self { + if trap.is::() { + return trap.downcast::().unwrap(); + } + let wasm_trace = vec![]; + let trap_code = None; + // let (wasm_trace, trap_code) = wasmer_compiler::get_trace_and_trapcode(&trap); + RuntimeError::new_from_source(trap, wasm_trace, trap_code) + } +} + +impl From for JsValue { + fn from(_err: RuntimeError) -> Self { + // err.inner.source.into() + unimplemented!(); + } +} - /// A trap ocurred during linking. - #[cfg_attr(feature = "std", error("RuntimeError occurred during linking: {0}"))] - Trap(#[source] RuntimeError), - /// Insufficient resources available for linking. - #[cfg_attr(feature = "std", error("Insufficient resources: {0}"))] - Resource(String), +pub(crate) fn raise(error: Box) -> ! { + let error = Trap::user(error); + let js_error: JsValue = error.into(); + wasm_bindgen::throw_val(js_error) } diff --git a/lib/api/src/js/externals/function.rs b/lib/api/src/js/externals/function.rs index 8ed64561849..46157e419a5 100644 --- a/lib/api/src/js/externals/function.rs +++ b/lib/api/src/js/externals/function.rs @@ -394,10 +394,10 @@ macro_rules! impl_host_function { Ok(Ok(result)) => return result.into_c_struct(&mut store), #[allow(deprecated)] #[cfg(feature = "std")] - Ok(Err(trap)) => RuntimeError::raise(Box::new(trap)), + Ok(Err(trap)) => crate::js::errors::raise(Box::new(trap)), #[cfg(feature = "core")] #[allow(deprecated)] - Ok(Err(trap)) => RuntimeError::raise(Box::new(trap)), + Ok(Err(trap)) => crate::js::errors::raise(Box::new(trap)), Err(_panic) => unimplemented!(), } } @@ -442,10 +442,10 @@ macro_rules! impl_host_function { Ok(Ok(result)) => return result.into_c_struct(&mut store), #[cfg(feature = "std")] #[allow(deprecated)] - Ok(Err(trap)) => RuntimeError::raise(Box::new(trap)), + Ok(Err(trap)) => crate::js::errors::raise(Box::new(trap)), #[cfg(feature = "core")] #[allow(deprecated)] - Ok(Err(trap)) => RuntimeError::raise(Box::new(trap)), + Ok(Err(trap)) => crate::js::errors::raise(Box::new(trap)), Err(_panic) => unimplemented!(), } } diff --git a/lib/api/src/js/mod.rs b/lib/api/src/js/mod.rs index d8c82b7d28b..fe10e935af0 100644 --- a/lib/api/src/js/mod.rs +++ b/lib/api/src/js/mod.rs @@ -34,7 +34,7 @@ pub(crate) mod module; #[cfg(feature = "wasm-types-polyfill")] mod module_info_polyfill; pub(crate) mod store; -mod trap; +pub(crate) mod trap; pub(crate) mod typed_function; pub(crate) mod vm; mod wasm_bindgen_polyfill; diff --git a/lib/api/src/js/trap.rs b/lib/api/src/js/trap.rs index f5551be51c5..8a0ef2d940b 100644 --- a/lib/api/src/js/trap.rs +++ b/lib/api/src/js/trap.rs @@ -1,273 +1,74 @@ +use crate::RuntimeError; use std::error::Error; use std::fmt; -use std::sync::Arc; + use wasm_bindgen::{prelude::*, JsValue}; use wasm_bindgen_downcast::DowncastJS; -use wasmer_types::FrameInfo; - -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::() - } - - 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`. - #[allow(dead_code)] - pub fn core_is_equal(&self) -> bool { - let t = core::any::TypeId::of::(); - let concrete = self.type_id(); - t == concrete - } -} - -impl dyn CoreError + Send + Sync + 'static { - /// Returns `true` if the inner type is the same as `T`. - #[allow(dead_code)] - 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. - #[allow(dead_code)] - 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. - #[allow(dead_code)] - 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. - #[allow(dead_code)] - 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. -#[wasm_bindgen] -#[derive(Clone, DowncastJS)] -pub struct WasmerRuntimeError { - inner: Arc, -} - -/// This type is the same as `WasmerRuntimeError`. -/// -/// We use the `WasmerRuntimeError` name to not collide with the -/// `RuntimeError` in JS. -pub type RuntimeError = WasmerRuntimeError; -impl PartialEq for RuntimeError { - fn eq(&self, other: &Self) -> bool { - Arc::ptr_eq(&self.inner, &other.inner) - } -} - -/// The source of the `RuntimeError`. #[derive(Debug)] -enum RuntimeErrorSource { - Generic(String), - #[cfg(feature = "std")] +enum InnerTrap { User(Box), - #[cfg(feature = "core")] - User(Box), Js(JsValue), } -/// This is a hack to ensure the error type is Send+Sync -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), - } - } +/// A struct representing a Trap +#[wasm_bindgen] +#[derive(Debug, DowncastJS)] +pub struct Trap { + inner: InnerTrap, } -impl RuntimeError { - /// Creates a new generic `RuntimeError` with the given `message`. - /// - /// # Example - /// ``` - /// let trap = wasmer_compiler::RuntimeError::new("unexpected error"); - /// assert_eq!("unexpected error", trap.message()); - /// ``` - pub fn new>(message: I) -> Self { - RuntimeError { - inner: Arc::new(RuntimeErrorSource::Generic(message.into())), - } - } - - /// 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) - } - - /// Returns a list of function frames in WebAssembly code that led to this - /// trap happening. - pub fn trace(&self) -> &[FrameInfo] { - // unimplemented!(); - return &[]; - } +unsafe impl Send for Trap {} +unsafe impl Sync for Trap {} - /// Creates a custom user Error. - /// - /// This error object can be passed through Wasm frames and later retrieved - /// using the `downcast` method. - #[cfg(feature = "std")] +impl Trap { 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)), - }, - } - } - - #[cfg(feature = "core")] - pub fn user(error: Box) -> Self { - match error.downcast_core::() { - // The error is already a RuntimeError, we return it directly - Ok(runtime_error) => *runtime_error, - Err(error) => RuntimeError { - inner: Arc::new(RuntimeErrorSource::User(error)), - }, + Self { + inner: InnerTrap::User(error), } } - /// Returns a reference the `message` stored in `Trap`. - pub fn message(&self) -> String { - format!("{}", self.inner) - } - - /// Attempts to downcast the `RuntimeError` to a concrete type. + /// Attempts to downcast the `Trap` to a concrete type. pub fn downcast(self) -> Result { - match Arc::try_unwrap(self.inner) { + match 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), - }), - Err(inner) => Err(Self { inner }), + InnerTrap::User(err) if err.is::() => Ok(*err.downcast::().unwrap()), + _ => Err(self), } } - /// Attempts to downcast the `RuntimeError` to a concrete type. + /// Attempts to downcast the `Trap` to a concrete type. pub fn downcast_ref(&self) -> Option<&T> { - match self.inner.as_ref() { + match &self.inner { // We only try to downcast user errors - #[cfg(feature = "std")] - RuntimeErrorSource::User(err) => err.downcast_ref::(), + InnerTrap::User(err) if err.is::() => err.downcast_ref::(), _ => None, } } - /// Returns true if the `RuntimeError` is the same as T + /// Returns true if the `Trap` 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::(), + match &self.inner { + InnerTrap::User(err) => err.is::(), _ => 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 std::error::Error for Trap { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self.inner { + InnerTrap::User(err) => Some(&**err), + _ => None, + } } } -impl fmt::Display for RuntimeError { +impl fmt::Display for Trap { 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, + match &self.inner { + InnerTrap::User(e) => write!(f, "user: {}", e), + InnerTrap::Js(value) => write!(f, "js: {:?}", value.as_string()), } } } @@ -277,8 +78,9 @@ impl From for RuntimeError { // We try to downcast the error and see if it's // an instance of RuntimeError instead, so we don't need // to re-wrap it. - WasmerRuntimeError::downcast_js(original).unwrap_or_else(|js| RuntimeError { - inner: Arc::new(RuntimeErrorSource::Js(js)), - }) + let trap = Trap::downcast_js(original).unwrap_or_else(|o| Trap { + inner: InnerTrap::Js(o), + }); + trap.into() } } diff --git a/lib/vm/src/trap/trap.rs b/lib/vm/src/trap/trap.rs index e624debb1c3..54239fcd924 100644 --- a/lib/vm/src/trap/trap.rs +++ b/lib/vm/src/trap/trap.rs @@ -105,7 +105,7 @@ impl Trap { } } - /// Returns true if the `RuntimeError` is the same as T + /// Returns true if the `Trap` is the same as T pub fn is(&self) -> bool { match self { Trap::User(err) => err.is::(), From 06ee28a3b42d0ac8a2220c442ebbfd47c191cc64 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sat, 22 Apr 2023 03:11:51 -0700 Subject: [PATCH 12/13] Fixed lint --- lib/api/src/errors.rs | 4 ++-- lib/compiler/src/engine/artifact.rs | 4 ++-- lib/compiler/src/engine/trap/stack.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/api/src/errors.rs b/lib/api/src/errors.rs index de61a0e0317..a1956754206 100644 --- a/lib/api/src/errors.rs +++ b/lib/api/src/errors.rs @@ -162,9 +162,9 @@ impl RuntimeError { /// Returns a reference the `message` stored in `Trap`. pub fn message(&self) -> String { if let Some(trap_code) = self.inner.trap_code { - return trap_code.message().to_string(); + trap_code.message().to_string() } else { - return self.inner.source.to_string(); + self.inner.source.to_string() } } diff --git a/lib/compiler/src/engine/artifact.rs b/lib/compiler/src/engine/artifact.rs index 8fdb8e162d0..054e76c79c5 100644 --- a/lib/compiler/src/engine/artifact.rs +++ b/lib/compiler/src/engine/artifact.rs @@ -526,7 +526,7 @@ impl Artifact { imports, self.signatures().clone(), ) - .map_err(|trap| InstantiationError::Start(trap.into()))?; + .map_err(InstantiationError::Start)?; Ok(handle) } @@ -551,7 +551,7 @@ impl Artifact { .collect::>(); handle .finish_instantiation(trap_handler, &data_initializers) - .map_err(|trap| InstantiationError::Start(trap.into())) + .map_err(InstantiationError::Start) } #[allow(clippy::type_complexity)] diff --git a/lib/compiler/src/engine/trap/stack.rs b/lib/compiler/src/engine/trap/stack.rs index ca9622edb41..aa07f93a981 100644 --- a/lib/compiler/src/engine/trap/stack.rs +++ b/lib/compiler/src/engine/trap/stack.rs @@ -29,7 +29,7 @@ pub fn get_trace_and_trapcode(trap: &Trap) -> (Vec, Option) Trap::Lib { trap_code, backtrace, - } => (wasm_trace(&info, None, backtrace), Some(trap_code.clone())), + } => (wasm_trace(&info, None, backtrace), Some(*trap_code)), } } From 88f712aebe83baa036f52eb0b8663ecac9d79d5b Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sat, 22 Apr 2023 10:00:26 -0700 Subject: [PATCH 13/13] Multiple doc fixes --- lib/api/src/errors.rs | 16 ++-------------- lib/types/src/compilation/symbols.rs | 2 +- lib/types/src/error.rs | 2 +- lib/types/src/stack/frame.rs | 2 +- lib/types/src/vmoffsets.rs | 2 +- 5 files changed, 6 insertions(+), 18 deletions(-) diff --git a/lib/api/src/errors.rs b/lib/api/src/errors.rs index a1956754206..76771beb802 100644 --- a/lib/api/src/errors.rs +++ b/lib/api/src/errors.rs @@ -110,7 +110,7 @@ impl RuntimeError { /// /// # Example /// ``` - /// let trap = wasmer_compiler::RuntimeError::new("unexpected error"); + /// let trap = wasmer::RuntimeError::new("unexpected error"); /// assert_eq!("unexpected error", trap.message()); /// ``` pub fn new>(message: I) -> Self { @@ -130,7 +130,7 @@ impl RuntimeError { /// 0.into(), /// 2.into() /// )]; - /// let trap = wasmer_compiler::RuntimeError::new(my_error, wasm_trace); + /// let trap = wasmer::RuntimeError::new_from_source(my_error, wasm_trace, None); /// assert_eq!("unexpected error", trap.message()); /// ``` pub fn new_from_source( @@ -252,7 +252,6 @@ impl std::error::Error for RuntimeError { } } -#[cfg(feature = "std")] impl From> for RuntimeError { fn from(error: Box) -> Self { match error.downcast::() { @@ -262,14 +261,3 @@ impl From> for RuntimeError { } } } - -#[cfg(feature = "core")] -impl From> for RuntimeError { - fn from(error: Box) -> Self { - match error.downcast::() { - // The error is already a RuntimeError, we return it directly - Ok(runtime_error) => *runtime_error, - Err(error) => Trap::user(error).into(), - } - } -} diff --git a/lib/types/src/compilation/symbols.rs b/lib/types/src/compilation/symbols.rs index 8cfbe50d937..1e91e44d14b 100644 --- a/lib/types/src/compilation/symbols.rs +++ b/lib/types/src/compilation/symbols.rs @@ -60,7 +60,7 @@ pub struct ModuleMetadata { pub data_initializers: Box<[OwnedDataInitializer]>, /// The function body lengths (used to find function by address) pub function_body_lengths: PrimaryMap, - /// CPU features used (See [`CpuFeature`]) + /// CPU features used (See [`CpuFeature`](crate::CpuFeature)) pub cpu_features: u64, } diff --git a/lib/types/src/error.rs b/lib/types/src/error.rs index 66f5ce765cc..9bebab026d8 100644 --- a/lib/types/src/error.rs +++ b/lib/types/src/error.rs @@ -235,7 +235,7 @@ impl From for WasmError { } /// The error that can happen while parsing a `str` -/// to retrieve a [`CpuFeature`](crate::target::CpuFeature). +/// to retrieve a [`CpuFeature`](crate::CpuFeature). #[derive(Debug)] #[cfg_attr(feature = "std", derive(Error))] pub enum ParseCpuFeatureError { diff --git a/lib/types/src/stack/frame.rs b/lib/types/src/stack/frame.rs index dfd4a42c391..e6cb53062a4 100644 --- a/lib/types/src/stack/frame.rs +++ b/lib/types/src/stack/frame.rs @@ -1,6 +1,6 @@ use crate::SourceLoc; -/// Description of a frame in a backtrace for a [`RuntimeError::trace`](crate::RuntimeError::trace). +/// Description of a frame in a backtrace for a [`RuntimeError`](crate::RuntimeError). /// /// Whenever a WebAssembly trap occurs an instance of [`RuntimeError`] /// is created. Each [`RuntimeError`] has a backtrace of the diff --git a/lib/types/src/vmoffsets.rs b/lib/types/src/vmoffsets.rs index 729adc1069d..4c387f737e3 100644 --- a/lib/types/src/vmoffsets.rs +++ b/lib/types/src/vmoffsets.rs @@ -4,7 +4,7 @@ //! Offsets and sizes of various structs in wasmer-vm's vmcontext //! module. -#![deny(broken_intra_doc_links)] +#![deny(rustdoc::broken_intra_doc_links)] use crate::{ FunctionIndex, GlobalIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex,