From bfb6814f23e4c03d37a4e2f903055df63ac2968d Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 23 Apr 2020 12:40:35 -0700 Subject: [PATCH 1/3] Make runtime and trap errors well defined (WIP) --- lib/clif-backend/src/signal/mod.rs | 31 +++++++++++--- lib/clif-backend/src/signal/unix.rs | 25 +++++++---- lib/runtime-core/src/backend.rs | 4 +- lib/runtime-core/src/error.rs | 65 ++++++++++++++++++++++++++++- lib/runtime-core/src/instance.rs | 15 +++---- lib/runtime-core/src/typed_func.rs | 20 ++++----- 6 files changed, 125 insertions(+), 35 deletions(-) diff --git a/lib/clif-backend/src/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index cb6e147b1a7..6ce156c9642 100644 --- a/lib/clif-backend/src/signal/mod.rs +++ b/lib/clif-backend/src/signal/mod.rs @@ -1,12 +1,13 @@ use crate::{ - relocation::{TrapData, TrapSink}, + relocation::{TrapData, TrapSink, TrapCode}, resolver::FuncResolver, trampoline::Trampolines, }; use libc::c_void; use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc}; use wasmer_runtime_core::{ - backend::RunnableModule, + backend::{RunnableModule, ExceptionCode}, + error::{InvokeError, RuntimeError}, module::ModuleInfo, typed_func::{Trampoline, Wasm}, types::{LocalFuncIndex, SigIndex}, @@ -26,10 +27,25 @@ pub use self::unix::*; pub use self::windows::*; thread_local! { - pub static TRAP_EARLY_DATA: Cell>> = Cell::new(None); + pub static TRAP_EARLY_DATA: Cell> = Cell::new(None); } -pub struct CallProtError(pub Box); +pub enum CallProtError { + UnknownTrap { + address: usize, + signal: &'static str, + }, + TrapCode { + code: ExceptionCode, + srcloc: u32, + }, + UnknownTrapCode { + trap_code: TrapCode, + srcloc: u32, + }, + EarlyTrap(RuntimeError), + Misc(Box), +} pub struct Caller { handler_data: HandlerData, @@ -63,7 +79,7 @@ impl RunnableModule for Caller { func: NonNull, args: *const u64, rets: *mut u64, - error_out: *mut Option>, + error_out: *mut Option, invoke_env: Option>, ) -> bool { let handler_data = &*invoke_env.unwrap().cast().as_ptr(); @@ -80,6 +96,9 @@ impl RunnableModule for Caller { match res { Err(err) => { + // probably makes the most sense to actually do a translation here to a + // a generic type defined in runtime-core + // TODO: figure out _this_ error return story *error_out = Some(err.0); false } @@ -101,7 +120,7 @@ impl RunnableModule for Caller { }) } - unsafe fn do_early_trap(&self, data: Box) -> ! { + unsafe fn do_early_trap(&self, data: RuntimeError) -> ! { TRAP_EARLY_DATA.with(|cell| cell.set(Some(data))); trigger_trap() } diff --git a/lib/clif-backend/src/signal/unix.rs b/lib/clif-backend/src/signal/unix.rs index 98571cbbff3..8b517dac4e8 100644 --- a/lib/clif-backend/src/signal/unix.rs +++ b/lib/clif-backend/src/signal/unix.rs @@ -79,16 +79,16 @@ pub fn call_protected( *jmp_buf = prev_jmp_buf; if let Some(data) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { - Err(CallProtError(data)) + Err(CallProtError::EarlyTrap(data)) } else { let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get()); if let Some(TrapData { trapcode, - srcloc: _, + srcloc, }) = handler_data.lookup(inst_ptr) { - Err(CallProtError(Box::new(match Signal::from_c_int(signum) { + let code = match Signal::from_c_int(signum) { Ok(SIGILL) => match trapcode { TrapCode::StackOverflow => ExceptionCode::MemoryOutOfBounds, TrapCode::HeapOutOfBounds => ExceptionCode::MemoryOutOfBounds, @@ -101,9 +101,10 @@ pub fn call_protected( TrapCode::BadConversionToInteger => ExceptionCode::IllegalArithmetic, TrapCode::UnreachableCodeReached => ExceptionCode::Unreachable, _ => { - return Err(CallProtError(Box::new( - "unknown clif trap code".to_string(), - ))) + return Err(CallProtError::UnknownTrapCode { + trap_code: trapcode, + srcloc, + }) } }, Ok(SIGSEGV) | Ok(SIGBUS) => ExceptionCode::MemoryOutOfBounds, @@ -112,7 +113,11 @@ pub fn call_protected( "ExceptionCode::Unknown signal:{:?}", Signal::from_c_int(signum) ), - }))) + }; + Err(CallProtError::TrapCode { + srcloc, + code, + }) } else { let signal = match Signal::from_c_int(signum) { Ok(SIGFPE) => "floating-point exception", @@ -123,8 +128,10 @@ pub fn call_protected( _ => "unknown trapped signal", }; // When the trap-handler is fully implemented, this will return more information. - let s = format!("unknown trap at {:p} - {}", faulting_addr, signal); - Err(CallProtError(Box::new(s))) + Err(CallProtError::UnknownTrap { + address: faulting_addr as usize, + signal, + }) } } } else { diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index c6235475d8d..cbef1db3bc0 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -1,5 +1,5 @@ use crate::{ - error::CompileResult, + error::{CompileResult, RuntimeError}, module::ModuleInner, state::ModuleStateMap, typed_func::Wasm, @@ -282,7 +282,7 @@ pub trait RunnableModule: Send + Sync { fn get_trampoline(&self, info: &ModuleInfo, sig_index: SigIndex) -> Option; /// Trap an error. - unsafe fn do_early_trap(&self, data: Box) -> !; + unsafe fn do_early_trap(&self, data: RuntimeError) -> !; /// Returns the machine code associated with this module. fn get_code(&self) -> Option<&[u8]> { diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index a685a8238f2..c705e5213d3 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -1,6 +1,6 @@ //! The error module contains the data structures and helper functions used to implement errors that //! are produced and returned from the wasmer runtime core. -use crate::backend::ExceptionCode; +//use crate::backend::ExceptionCode; use crate::types::{FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type}; use core::borrow::Borrow; use std::any::Any; @@ -173,6 +173,7 @@ impl std::fmt::Display for LinkError { impl std::error::Error for LinkError {} +/* /// This is the error type returned when calling /// a WebAssembly function. /// @@ -210,6 +211,68 @@ impl std::fmt::Debug for RuntimeError { impl std::error::Error for RuntimeError {} +*/ + +/// An `InternalError` is an error that happened inside of Wasmer and is a +/// catch-all for errors that would otherwise be returned as +/// `RuntimeError(Box::new())`. +/// +/// This type provides greater visibility into the kinds of things that may fail +/// and improves the ability of users to handle them, though these errors may be +/// extremely rare and impossible to handle. +#[derive(Debug)] +pub enum RuntimeError { + /// When an invoke returns an error (this is where exception codes come from?) + InvokeError(InvokeError), + /// A user triggered error value. + /// + /// An error returned from a host function. + User(Box) +} + +/// TODO: +#[derive(Debug)] +pub enum InvokeError { + /// not yet handled error cases, ideally we should be able to handle them all + Misc(Box), + /// Indicates an exceptional circumstance such as a bug that should be reported or + /// a hardware failure. + FailedWithNoError, +} + +impl std::error::Error for RuntimeError {} + +impl std::fmt::Display for RuntimeError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + // TODO: ideally improve the error type of invoke + RuntimeError::InvokeError(_) => write!(f, "Error when calling invoke"), + RuntimeError::User(user_error) => { + write!(f, "User supplied error: ")?; + if let Some(s) = user_error.downcast_ref::() { + write!(f, "\"{}\"", s) + } else if let Some(s) = user_error.downcast_ref::<&str>() { + write!(f, "\"{}\"", s) + } else if let Some(n) = user_error.downcast_ref::() { + write!(f, "{}", n) + } else { + write!(f, "unknown error type") + } + }, + } + } +} + +/* +impl From for RuntimeError { + fn from(other: InternalError) -> Self { + RuntimeError(Box::new(other)) + } +} + */ + + + /// This error type is produced by resolving a wasm function /// given its name. /// diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 48e59d4ec65..4eb43d71f70 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -5,7 +5,7 @@ use crate::{ backend::RunnableModule, backing::{ImportBacking, LocalBacking}, - error::{CallResult, ResolveError, ResolveResult, Result, RuntimeError}, + error::{CallResult, ResolveError, ResolveResult, Result, RuntimeError, InvokeError}, export::{Context, Export, ExportIter, Exportable, FuncPointer}, global::Global, import::{ImportObject, LikeNamespace}, @@ -584,10 +584,10 @@ pub(crate) fn call_func_with_index_inner( invoke_env, } = wasm; - let run_wasm = |result_space: *mut u64| unsafe { + let run_wasm = |result_space: *mut u64| -> CallResult<()> { let mut error_out = None; - let success = invoke( + let success = unsafe { invoke( trampoline, ctx_ptr, func_ptr, @@ -595,14 +595,15 @@ pub(crate) fn call_func_with_index_inner( result_space, &mut error_out, invoke_env, - ); + )}; if success { Ok(()) } else { - Err(error_out - .map(RuntimeError) - .unwrap_or_else(|| RuntimeError(Box::new("invoke(): Unknown error".to_string())))) + let error: RuntimeError = error_out + .map(RuntimeError::InvokeError) + .unwrap_or_else(|| RuntimeError::InvokeError(InvokeError::FailedWithNoError)); + Err(error.into()) } }; diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index a6337bc5888..1daa5831971 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -1,7 +1,7 @@ //! The typed func module implements a way of representing a wasm function //! with the correct types from rust. Function calls using a typed func have a low overhead. use crate::{ - error::RuntimeError, + error::{RuntimeError, InvokeError}, export::{Context, Export, FuncPointer}, import::IsExport, types::{FuncSig, NativeWasmType, Type, WasmExternType}, @@ -37,7 +37,7 @@ pub type Invoke = unsafe extern "C" fn( func: NonNull, args: *const u64, rets: *mut u64, - error_out: *mut Option>, + error_out: *mut Option, extra: Option>, ) -> bool; @@ -340,7 +340,7 @@ impl<'a> DynamicFunc<'a> { Err(e) => { // At this point, there is an error that needs to be trapped. drop(args); // Release the Vec which will leak otherwise. - (&*vmctx.module).runnable_module.do_early_trap(e) + (&*vmctx.module).runnable_module.do_early_trap(RuntimeError::User(e)) } } } @@ -588,9 +588,7 @@ macro_rules! impl_traits { ) { Ok(Rets::from_ret_array(rets)) } else { - Err(error_out.map(RuntimeError).unwrap_or_else(|| { - RuntimeError(Box::new("invoke(): Unknown error".to_string())) - })) + Err(error_out.map_or_else(|| RuntimeError::InvokeError(InvokeError::FailedWithNoError), RuntimeError::InvokeError)) } } } @@ -678,9 +676,10 @@ macro_rules! impl_traits { Ok(Ok(returns)) => return returns.into_c_struct(), Ok(Err(err)) => { let b: Box<_> = err.into(); - b as Box + RuntimeError::User(b as Box) }, - Err(err) => err, + // TODO(blocking): this line is wrong! + Err(err) => RuntimeError::User(err), }; // At this point, there is an error that needs to @@ -791,9 +790,10 @@ macro_rules! impl_traits { Ok(Ok(returns)) => return returns.into_c_struct(), Ok(Err(err)) => { let b: Box<_> = err.into(); - b as Box + RuntimeError::User(b as Box) }, - Err(err) => err, + // TODO(blocking): this line is wrong! + Err(err) => RuntimeError::User(err), }; // At this point, there is an error that needs to From b9ec8f98450e5891c8897bdbaf4835039f42134a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 24 Apr 2020 13:21:45 -0700 Subject: [PATCH 2/3] Define runtime error values explicitly in Singlepass --- lib/clif-backend/src/signal/mod.rs | 25 ++---------- lib/clif-backend/src/signal/unix.rs | 24 ++++------- lib/llvm-backend/src/backend.rs | 38 ++++++++++------- lib/middleware-common/src/metering.rs | 3 +- lib/runtime-core/src/codegen.rs | 5 +-- lib/runtime-core/src/error.rs | 50 ++++++++++++++++++----- lib/runtime-core/src/fault.rs | 36 ++++++++-------- lib/runtime-core/src/instance.rs | 22 +++++----- lib/runtime-core/src/state.rs | 4 +- lib/runtime-core/src/typed_func.rs | 6 ++- lib/singlepass-backend/src/codegen_x64.rs | 10 ++--- src/commands/run.rs | 8 +++- 12 files changed, 129 insertions(+), 102 deletions(-) diff --git a/lib/clif-backend/src/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index 6ce156c9642..cf3d1eb021f 100644 --- a/lib/clif-backend/src/signal/mod.rs +++ b/lib/clif-backend/src/signal/mod.rs @@ -1,12 +1,12 @@ use crate::{ - relocation::{TrapData, TrapSink, TrapCode}, + relocation::{TrapData, TrapSink}, resolver::FuncResolver, trampoline::Trampolines, }; use libc::c_void; -use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc}; +use std::{cell::Cell, ptr::NonNull, sync::Arc}; use wasmer_runtime_core::{ - backend::{RunnableModule, ExceptionCode}, + backend::RunnableModule, error::{InvokeError, RuntimeError}, module::ModuleInfo, typed_func::{Trampoline, Wasm}, @@ -30,23 +30,6 @@ thread_local! { pub static TRAP_EARLY_DATA: Cell> = Cell::new(None); } -pub enum CallProtError { - UnknownTrap { - address: usize, - signal: &'static str, - }, - TrapCode { - code: ExceptionCode, - srcloc: u32, - }, - UnknownTrapCode { - trap_code: TrapCode, - srcloc: u32, - }, - EarlyTrap(RuntimeError), - Misc(Box), -} - pub struct Caller { handler_data: HandlerData, trampolines: Arc, @@ -99,7 +82,7 @@ impl RunnableModule for Caller { // probably makes the most sense to actually do a translation here to a // a generic type defined in runtime-core // TODO: figure out _this_ error return story - *error_out = Some(err.0); + *error_out = Some(err); false } Ok(()) => true, diff --git a/lib/clif-backend/src/signal/unix.rs b/lib/clif-backend/src/signal/unix.rs index 8b517dac4e8..965359e354f 100644 --- a/lib/clif-backend/src/signal/unix.rs +++ b/lib/clif-backend/src/signal/unix.rs @@ -10,7 +10,7 @@ //! unless you have memory unsafety elsewhere in your code. //! use crate::relocation::{TrapCode, TrapData}; -use crate::signal::{CallProtError, HandlerData}; +use crate::signal::HandlerData; use libc::{c_int, c_void, siginfo_t}; use nix::sys::signal::{ sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV, @@ -19,6 +19,7 @@ use std::cell::{Cell, UnsafeCell}; use std::ptr; use std::sync::Once; use wasmer_runtime_core::backend::ExceptionCode; +use wasmer_runtime_core::error::InvokeError; extern "C" fn signal_trap_handler( signum: ::nix::libc::c_int, @@ -65,7 +66,7 @@ pub unsafe fn trigger_trap() -> ! { pub fn call_protected( handler_data: &HandlerData, f: impl FnOnce() -> T, -) -> Result { +) -> Result { unsafe { let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); let prev_jmp_buf = *jmp_buf; @@ -79,15 +80,11 @@ pub fn call_protected( *jmp_buf = prev_jmp_buf; if let Some(data) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { - Err(CallProtError::EarlyTrap(data)) + Err(InvokeError::EarlyTrap(Box::new(data))) } else { let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get()); - if let Some(TrapData { - trapcode, - srcloc, - }) = handler_data.lookup(inst_ptr) - { + if let Some(TrapData { trapcode, srcloc }) = handler_data.lookup(inst_ptr) { let code = match Signal::from_c_int(signum) { Ok(SIGILL) => match trapcode { TrapCode::StackOverflow => ExceptionCode::MemoryOutOfBounds, @@ -101,8 +98,8 @@ pub fn call_protected( TrapCode::BadConversionToInteger => ExceptionCode::IllegalArithmetic, TrapCode::UnreachableCodeReached => ExceptionCode::Unreachable, _ => { - return Err(CallProtError::UnknownTrapCode { - trap_code: trapcode, + return Err(InvokeError::UnknownTrapCode { + trap_code: format!("{:?}", trapcode), srcloc, }) } @@ -114,10 +111,7 @@ pub fn call_protected( Signal::from_c_int(signum) ), }; - Err(CallProtError::TrapCode { - srcloc, - code, - }) + Err(InvokeError::TrapCode { srcloc, code }) } else { let signal = match Signal::from_c_int(signum) { Ok(SIGFPE) => "floating-point exception", @@ -128,7 +122,7 @@ pub fn call_protected( _ => "unknown trapped signal", }; // When the trap-handler is fully implemented, this will return more information. - Err(CallProtError::UnknownTrap { + Err(InvokeError::UnknownTrap { address: faulting_addr as usize, signal, }) diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs index a2ac15a940c..c3922b6bed9 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -11,7 +11,6 @@ use inkwell::{ }; use libc::c_char; use std::{ - any::Any, cell::RefCell, ffi::{c_void, CString}, mem, @@ -27,6 +26,7 @@ use wasmer_runtime_core::{ CacheGen, ExceptionCode, RunnableModule, }, cache::Error as CacheError, + error::{InvokeError, RuntimeError}, module::ModuleInfo, state::ModuleStateMap, structures::TypedIndex, @@ -56,7 +56,7 @@ extern "C" { /// but this is cleaner, I think? #[cfg_attr(nightly, unwind(allowed))] #[allow(improper_ctypes)] - fn throw_any(data: *mut dyn Any) -> !; + fn throw_runtime_error(data: RuntimeError) -> !; #[allow(improper_ctypes)] fn cxx_invoke_trampoline( @@ -66,7 +66,7 @@ extern "C" { params: *const u64, results: *mut u64, trap_out: *mut i32, - error_out: *mut Option>, + error_out: *mut Option, invoke_env: Option>, ) -> bool; } @@ -79,7 +79,7 @@ unsafe extern "C" fn invoke_trampoline( func_ptr: NonNull, params: *const u64, results: *mut u64, - error_out: *mut Option>, + error_out: *mut Option, invoke_env: Option>, ) -> bool { let mut trap_out: i32 = -1; @@ -95,15 +95,22 @@ unsafe extern "C" fn invoke_trampoline( ); // Translate trap code if an error occurred. if !ret && (*error_out).is_none() && trap_out != -1 { - *error_out = Some(Box::new(match trap_out { - 0 => ExceptionCode::Unreachable, - 1 => ExceptionCode::IncorrectCallIndirectSignature, - 2 => ExceptionCode::MemoryOutOfBounds, - 3 => ExceptionCode::CallIndirectOOB, - 4 => ExceptionCode::IllegalArithmetic, - 5 => ExceptionCode::MisalignedAtomicAccess, - _ => return ret, - })); + *error_out = { + let exception_code = match trap_out { + 0 => ExceptionCode::Unreachable, + 1 => ExceptionCode::IncorrectCallIndirectSignature, + 2 => ExceptionCode::MemoryOutOfBounds, + 3 => ExceptionCode::CallIndirectOOB, + 4 => ExceptionCode::IllegalArithmetic, + 5 => ExceptionCode::MisalignedAtomicAccess, + _ => return ret, + }; + Some(InvokeError::TrapCode { + code: exception_code, + // TODO: + srcloc: 0, + }) + }; } ret } @@ -467,8 +474,9 @@ impl RunnableModule for LLVMBackend { self.msm.clone() } - unsafe fn do_early_trap(&self, data: Box) -> ! { - throw_any(Box::leak(data)) + unsafe fn do_early_trap(&self, data: RuntimeError) -> ! { + // maybe need to box leak it? + throw_runtime_error(data) } } diff --git a/lib/middleware-common/src/metering.rs b/lib/middleware-common/src/metering.rs index 409035d4ba7..a201403d266 100644 --- a/lib/middleware-common/src/metering.rs +++ b/lib/middleware-common/src/metering.rs @@ -1,5 +1,6 @@ use wasmer_runtime_core::{ codegen::{Event, EventSink, FunctionMiddleware, InternalEvent}, + error::RuntimeError, module::ModuleInfo, vm::{Ctx, InternalField}, wasmparser::{Operator, Type as WpType, TypeOrFuncType as WpTypeOrFuncType}, @@ -96,7 +97,7 @@ impl FunctionMiddleware for Metering { ty: WpTypeOrFuncType::Type(WpType::EmptyBlockType), })); sink.push(Event::Internal(InternalEvent::Breakpoint(Box::new(|_| { - Err(Box::new(ExecutionLimitExceededError)) + Err(RuntimeError::Metering(Box::new(ExecutionLimitExceededError))) })))); sink.push(Event::WasmOwned(Operator::End)); } diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index 4e2bf74a4a2..6ff5c596c03 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -6,13 +6,12 @@ use crate::{ backend::RunnableModule, backend::{CacheGen, Compiler, CompilerConfig, Features, Token}, cache::{Artifact, Error as CacheError}, - error::{CompileError, CompileResult}, + error::{CompileError, CompileResult, RuntimeError}, module::{ModuleInfo, ModuleInner}, structures::Map, types::{FuncIndex, FuncSig, SigIndex}, }; use smallvec::SmallVec; -use std::any::Any; use std::collections::HashMap; use std::fmt; use std::fmt::Debug; @@ -23,7 +22,7 @@ use wasmparser::{Operator, Type as WpType}; /// A type that defines a function pointer, which is called when breakpoints occur. pub type BreakpointHandler = - Box Result<(), Box> + Send + Sync + 'static>; + Box Result<(), RuntimeError> + Send + Sync + 'static>; /// Maps instruction pointers to their breakpoint handlers. pub type BreakpointMap = Arc>; diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index c705e5213d3..b501c6067ad 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -1,6 +1,6 @@ //! The error module contains the data structures and helper functions used to implement errors that //! are produced and returned from the wasmer runtime core. -//use crate::backend::ExceptionCode; +use crate::backend::ExceptionCode; use crate::types::{FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type}; use core::borrow::Borrow; use std::any::Any; @@ -222,31 +222,63 @@ impl std::error::Error for RuntimeError {} /// extremely rare and impossible to handle. #[derive(Debug)] pub enum RuntimeError { - /// When an invoke returns an error (this is where exception codes come from?) + /// When an invoke returns an error InvokeError(InvokeError), + /// A metering triggered error value. + /// + /// An error of this type indicates that it was returned by the metering system. + Metering(Box), /// A user triggered error value. /// /// An error returned from a host function. - User(Box) + User(Box), } /// TODO: #[derive(Debug)] pub enum InvokeError { - /// not yet handled error cases, ideally we should be able to handle them all - Misc(Box), /// Indicates an exceptional circumstance such as a bug that should be reported or /// a hardware failure. FailedWithNoError, + + /// TODO: + UnknownTrap { + /// TODO: + address: usize, + /// TODO: + signal: &'static str, + }, + /// TODO: + TrapCode { + /// TODO: + code: ExceptionCode, + /// TODO: + srcloc: u32, + }, + /// TODO: + UnknownTrapCode { + /// TODO: + trap_code: String, + /// TODO: + srcloc: u32, + }, + /// extra TODO: investigate if this can be a `Box` instead (looks like probably no) + /// TODO: + EarlyTrap(Box), + /// Indicates an error that ocurred related to breakpoints. (currently Singlepass only) + Breakpoint(Box), } +//impl std::error::Error for InvokeError {} + impl std::error::Error for RuntimeError {} impl std::fmt::Display for RuntimeError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - // TODO: ideally improve the error type of invoke + // TODO: update invoke error formatting RuntimeError::InvokeError(_) => write!(f, "Error when calling invoke"), + RuntimeError::Metering(_) => write!(f, "unknown metering error type"), RuntimeError::User(user_error) => { write!(f, "User supplied error: ")?; if let Some(s) = user_error.downcast_ref::() { @@ -256,9 +288,9 @@ impl std::fmt::Display for RuntimeError { } else if let Some(n) = user_error.downcast_ref::() { write!(f, "{}", n) } else { - write!(f, "unknown error type") + write!(f, "unknown user error type") } - }, + } } } } @@ -270,8 +302,6 @@ impl From for RuntimeError { } } */ - - /// This error type is produced by resolving a wasm function /// given its name. diff --git a/lib/runtime-core/src/fault.rs b/lib/runtime-core/src/fault.rs index d8c985e09b9..cdd5078b501 100644 --- a/lib/runtime-core/src/fault.rs +++ b/lib/runtime-core/src/fault.rs @@ -29,14 +29,14 @@ pub mod raw { use crate::codegen::{BreakpointInfo, BreakpointMap}; use crate::state::x64::{build_instance_image, read_stack, X64Register, GPR}; -use crate::state::{CodeVersion, ExecutionStateImage}; +use crate::state::{CodeVersion, ExecutionStateImage, InstanceImage}; +use crate::error::{RuntimeError, InvokeError}; use crate::vm; use libc::{mmap, mprotect, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; use nix::sys::signal::{ sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTRAP, }; -use std::any::Any; use std::cell::{Cell, RefCell, UnsafeCell}; use std::ffi::c_void; use std::process; @@ -61,7 +61,7 @@ type SetJmpBuffer = [i32; SETJMP_BUFFER_LEN]; struct UnwindInfo { jmpbuf: SetJmpBuffer, // in breakpoints: Option, - payload: Option>, // out + payload: Option, // out } /// A store for boundary register preservation. @@ -182,7 +182,7 @@ pub unsafe fn clear_wasm_interrupt() { pub unsafe fn catch_unsafe_unwind R>( f: F, breakpoints: Option, -) -> Result> { +) -> Result { let unwind = UNWIND.with(|x| x.get()); let old = (*unwind).take(); *unwind = Some(UnwindInfo { @@ -205,7 +205,7 @@ pub unsafe fn catch_unsafe_unwind R>( } /// Begins an unsafe unwind. -pub unsafe fn begin_unsafe_unwind(e: Box) -> ! { +pub unsafe fn begin_unsafe_unwind(e: RuntimeError) -> ! { let unwind = UNWIND.with(|x| x.get()); let inner = (*unwind) .as_mut() @@ -279,7 +279,7 @@ extern "C" fn signal_trap_handler( static ARCH: Architecture = Architecture::Aarch64; let mut should_unwind = false; - let mut unwind_result: Box = Box::new(()); + let mut unwind_result: Option> = None; unsafe { let fault = get_fault_info(siginfo as _, ucontext); @@ -302,7 +302,7 @@ extern "C" fn signal_trap_handler( ) { match ib.ty { InlineBreakpointType::Middleware => { - let out: Option>> = + let out: Option> = with_breakpoint_map(|bkpt_map| { bkpt_map.and_then(|x| x.get(&ip)).map(|x| { x(BreakpointInfo { @@ -313,7 +313,7 @@ extern "C" fn signal_trap_handler( if let Some(Ok(())) = out { } else if let Some(Err(e)) = out { should_unwind = true; - unwind_result = e; + unwind_result = Some(Err(e)); } } } @@ -328,7 +328,7 @@ extern "C" fn signal_trap_handler( }) }); if should_unwind { - begin_unsafe_unwind(unwind_result); + begin_unsafe_unwind(unwind_result.unwrap().unwrap_err()); } if early_return { return; @@ -342,9 +342,9 @@ extern "C" fn signal_trap_handler( match Signal::from_c_int(signum) { Ok(SIGTRAP) => { // breakpoint - let out: Option>> = - with_breakpoint_map(|bkpt_map| { - bkpt_map.and_then(|x| x.get(&(fault.ip.get()))).map(|x| { + let out: Option> = + with_breakpoint_map(|bkpt_map| -> Option> { + bkpt_map.and_then(|x| x.get(&(fault.ip.get()))).map(|x| -> Result<(), RuntimeError> { x(BreakpointInfo { fault: Some(&fault), }) @@ -355,7 +355,7 @@ extern "C" fn signal_trap_handler( return false; } Some(Err(e)) => { - unwind_result = e; + unwind_result = Some(Err(e)); return true; } None => {} @@ -387,7 +387,7 @@ extern "C" fn signal_trap_handler( if is_suspend_signal { // If this is a suspend signal, we parse the runtime state and return the resulting image. let image = build_instance_image(ctx, es_image); - unwind_result = Box::new(image); + unwind_result = Some(Ok(image)); } else { // Otherwise, this is a real exception and we just throw it to the caller. if !es_image.frames.is_empty() { @@ -415,7 +415,11 @@ extern "C" fn signal_trap_handler( None }); if let Some(code) = exc_code { - unwind_result = Box::new(code); + unwind_result = Some(Err(RuntimeError::InvokeError(InvokeError::TrapCode { + code, + // TODO: + srcloc: 0, + }))); } } @@ -423,7 +427,7 @@ extern "C" fn signal_trap_handler( }); if should_unwind { - begin_unsafe_unwind(unwind_result); + begin_unsafe_unwind(unwind_result.unwrap().unwrap_err()); } } } diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 4eb43d71f70..7dd176b657c 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -5,7 +5,7 @@ use crate::{ backend::RunnableModule, backing::{ImportBacking, LocalBacking}, - error::{CallResult, ResolveError, ResolveResult, Result, RuntimeError, InvokeError}, + error::{CallResult, InvokeError, ResolveError, ResolveResult, Result, RuntimeError}, export::{Context, Export, ExportIter, Exportable, FuncPointer}, global::Global, import::{ImportObject, LikeNamespace}, @@ -587,15 +587,17 @@ pub(crate) fn call_func_with_index_inner( let run_wasm = |result_space: *mut u64| -> CallResult<()> { let mut error_out = None; - let success = unsafe { invoke( - trampoline, - ctx_ptr, - func_ptr, - raw_args.as_ptr(), - result_space, - &mut error_out, - invoke_env, - )}; + let success = unsafe { + invoke( + trampoline, + ctx_ptr, + func_ptr, + raw_args.as_ptr(), + result_space, + &mut error_out, + invoke_env, + ) + }; if success { Ok(()) diff --git a/lib/runtime-core/src/state.rs b/lib/runtime-core/src/state.rs index 55809e965d7..fb3f5d2b769 100644 --- a/lib/runtime-core/src/state.rs +++ b/lib/runtime-core/src/state.rs @@ -705,7 +705,7 @@ pub mod x64 { use crate::structures::TypedIndex; use crate::types::LocalGlobalIndex; use crate::vm::Ctx; - use std::any::Any; + use crate::error::RuntimeError; #[allow(clippy::cast_ptr_alignment)] unsafe fn compute_vmctx_deref(vmctx: *const Ctx, seq: &[usize]) -> u64 { @@ -738,7 +738,7 @@ pub mod x64 { image: InstanceImage, vmctx: &mut Ctx, breakpoints: Option, - ) -> Result> { + ) -> Result { let mut stack: Vec = vec![0; 1048576 * 8 / 8]; // 8MB stack let mut stack_offset: usize = stack.len(); diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 1daa5831971..4594bcd4f80 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -1,7 +1,7 @@ //! The typed func module implements a way of representing a wasm function //! with the correct types from rust. Function calls using a typed func have a low overhead. use crate::{ - error::{RuntimeError, InvokeError}, + error::{InvokeError, RuntimeError}, export::{Context, Export, FuncPointer}, import::IsExport, types::{FuncSig, NativeWasmType, Type, WasmExternType}, @@ -340,7 +340,9 @@ impl<'a> DynamicFunc<'a> { Err(e) => { // At this point, there is an error that needs to be trapped. drop(args); // Release the Vec which will leak otherwise. - (&*vmctx.module).runnable_module.do_early_trap(RuntimeError::User(e)) + (&*vmctx.module) + .runnable_module + .do_early_trap(RuntimeError::User(e)) } } } diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index e51db5b7c5b..f51af7c5862 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -10,7 +10,6 @@ use dynasmrt::x64::Assembler; use dynasmrt::{AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi}; use smallvec::SmallVec; use std::{ - any::Any, collections::{BTreeMap, HashMap}, ffi::c_void, iter, mem, @@ -29,6 +28,7 @@ use wasmer_runtime_core::{ codegen::*, fault::{self, raw::register_preservation_trampoline}, loader::CodeMemory, + error::{InvokeError, RuntimeError}, memory::MemoryType, module::{ModuleInfo, ModuleInner}, state::{ @@ -214,7 +214,7 @@ pub struct X64FunctionCode { breakpoints: Option< HashMap< AssemblyOffset, - Box Result<(), Box> + Send + Sync + 'static>, + Box Result<(), RuntimeError> + Send + Sync + 'static>, >, >, returns: SmallVec<[WpType; 1]>, @@ -507,7 +507,7 @@ impl RunnableModule for X64ExecutionContext { func: NonNull, args: *const u64, rets: *mut u64, - error_out: *mut Option>, + error_out: *mut Option, num_params_plus_one: Option>, ) -> bool { let rm: &Box = &(&*(*ctx).module).runnable_module; @@ -655,7 +655,7 @@ impl RunnableModule for X64ExecutionContext { true } Err(err) => { - *error_out = Some(err); + *error_out = Some(InvokeError::Breakpoint(Box::new(err))); false } }; @@ -680,7 +680,7 @@ impl RunnableModule for X64ExecutionContext { }) } - unsafe fn do_early_trap(&self, data: Box) -> ! { + unsafe fn do_early_trap(&self, data: RuntimeError) -> ! { fault::begin_unsafe_unwind(data); } diff --git a/src/commands/run.rs b/src/commands/run.rs index b58075fd777..43223793762 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -20,6 +20,7 @@ use wasmer_runtime::{ }; use wasmer_runtime_core::{ self, + error::RuntimeError, backend::{Compiler, CompilerConfig, MemoryBoundCheckMode}, loader::{Instance as LoadedInstance, LocalLoader}, Module, @@ -437,9 +438,12 @@ fn execute_wasi( } if let Err(ref err) = result { - if let Some(error_code) = err.0.downcast_ref::() { - std::process::exit(error_code.code as i32) + if let RuntimeError::User(user_error) = err { + if let Some(error_code) = user_error.downcast_ref::() { + std::process::exit(error_code.code as i32) + } } + return Err(format!("error: {:?}", err)); } } From 197f2f859ee4b885a318ba42816c40b421803144 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 24 Apr 2020 14:30:14 -0700 Subject: [PATCH 3/3] Fix tests for the Cranelift backend --- lib/llvm-backend/cpp/object_loader.hh | 20 ++++++++++---------- lib/llvm-backend/src/backend.rs | 6 +++--- lib/llvm-backend/src/code.rs | 5 +++-- lib/runtime-core/src/error.rs | 17 ++++++++++++++++- lib/runtime-core/src/instance.rs | 24 +++++++++++------------- lib/runtime-core/src/typed_func.rs | 2 +- tests/high_level_api.rs | 2 +- tests/imports.rs | 14 +++++++------- tests/middleware_common.rs | 13 +++++++++---- 9 files changed, 61 insertions(+), 42 deletions(-) diff --git a/lib/llvm-backend/cpp/object_loader.hh b/lib/llvm-backend/cpp/object_loader.hh index 53bd2b0df85..045502dd8d9 100644 --- a/lib/llvm-backend/cpp/object_loader.hh +++ b/lib/llvm-backend/cpp/object_loader.hh @@ -50,8 +50,8 @@ typedef struct { } callbacks_t; typedef struct { - size_t data, vtable; -} box_any_t; + size_t data; +} runtime_error_t; enum WasmTrapType { Unreachable = 0, @@ -121,7 +121,7 @@ private: struct WasmErrorSink { WasmTrapType *trap_out; - box_any_t *user_error; + runtime_error_t *user_error; }; struct WasmException : std::exception { @@ -149,14 +149,14 @@ public: struct UserException : UncatchableException { public: - UserException(size_t data, size_t vtable) : error_data({data, vtable}) {} + UserException(size_t data) : error_data({data}) {} virtual std::string description() const noexcept override { return "user exception"; } - // The parts of a `Box`. - box_any_t error_data; + // The pointer to `RuntimeError`. + runtime_error_t error_data; virtual void write_error(WasmErrorSink &out) const noexcept override { *out.user_error = error_data; @@ -274,10 +274,10 @@ result_t module_load(const uint8_t *mem_ptr, size_t mem_size, void module_delete(WasmModule *module) { delete module; } -// Throw a fat pointer that's assumed to be `*mut dyn Any` on the rust +// Throw a pointer that's assumed to be `*mut RuntimeError` on the rust // side. -[[noreturn]] void throw_any(size_t data, size_t vtable) { - unsafe_unwind(new UserException(data, vtable)); +[[noreturn]] void throw_runtime_error(size_t data) { + unsafe_unwind(new UserException(data)); } // Throw a pointer that's assumed to be codegen::BreakpointHandler on the @@ -288,7 +288,7 @@ void module_delete(WasmModule *module) { delete module; } bool cxx_invoke_trampoline(trampoline_t trampoline, void *ctx, void *func, void *params, void *results, WasmTrapType *trap_out, - box_any_t *user_error, void *invoke_env) noexcept { + runtime_error_t *user_error, void *invoke_env) noexcept { try { catch_unwind([trampoline, ctx, func, params, results]() { trampoline(ctx, func, params, results); diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs index c3922b6bed9..16f80ac84c0 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -56,7 +56,8 @@ extern "C" { /// but this is cleaner, I think? #[cfg_attr(nightly, unwind(allowed))] #[allow(improper_ctypes)] - fn throw_runtime_error(data: RuntimeError) -> !; + // POINTER!!!!! TODOARINO + fn throw_runtime_error(data: *mut RuntimeError) -> !; #[allow(improper_ctypes)] fn cxx_invoke_trampoline( @@ -475,8 +476,7 @@ impl RunnableModule for LLVMBackend { } unsafe fn do_early_trap(&self, data: RuntimeError) -> ! { - // maybe need to box leak it? - throw_runtime_error(data) + throw_runtime_error(Box::into_raw(Box::new(data))) } } diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 554628e254b..0ed4f3ce0da 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -34,6 +34,7 @@ use std::{ use wasmer_runtime_core::{ backend::{CacheGen, CompilerConfig, Token}, cache::{Artifact, Error as CacheError}, + error::RuntimeError, codegen::*, memory::MemoryType, module::{ModuleInfo, ModuleInner}, @@ -940,11 +941,11 @@ pub struct CodegenError { // prevents unused function elimination. #[no_mangle] pub unsafe extern "C" fn callback_trampoline( - b: *mut Option>, + b: *mut Option, callback: *mut BreakpointHandler, ) { let callback = Box::from_raw(callback); - let result: Result<(), Box> = + let result: Result<(), RuntimeError> = callback(BreakpointInfo { fault: None }); match result { Ok(()) => *b = None, diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index b501c6067ad..44c12ad6eaf 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -265,10 +265,25 @@ pub enum InvokeError { /// extra TODO: investigate if this can be a `Box` instead (looks like probably no) /// TODO: EarlyTrap(Box), - /// Indicates an error that ocurred related to breakpoints. (currently Singlepass only) + /// Indicates an error that ocurred related to breakpoints. Breakpoint(Box), } +impl From for RuntimeError { + fn from(other: InvokeError) -> RuntimeError { + match other { + InvokeError::EarlyTrap(re) | InvokeError::Breakpoint(re) => *re, + _ => RuntimeError::InvokeError(other), + } + } +} + +impl PartialEq for RuntimeError { + fn eq(&self, _other: &RuntimeError) -> bool { + false + } +} + //impl std::error::Error for InvokeError {} impl std::error::Error for RuntimeError {} diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 7dd176b657c..274904fb346 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -5,7 +5,7 @@ use crate::{ backend::RunnableModule, backing::{ImportBacking, LocalBacking}, - error::{CallResult, InvokeError, ResolveError, ResolveResult, Result, RuntimeError}, + error::{CallResult, ResolveError, ResolveResult, Result, RuntimeError, InvokeError}, export::{Context, Export, ExportIter, Exportable, FuncPointer}, global::Global, import::{ImportObject, LikeNamespace}, @@ -587,23 +587,21 @@ pub(crate) fn call_func_with_index_inner( let run_wasm = |result_space: *mut u64| -> CallResult<()> { let mut error_out = None; - let success = unsafe { - invoke( - trampoline, - ctx_ptr, - func_ptr, - raw_args.as_ptr(), - result_space, - &mut error_out, - invoke_env, - ) - }; + let success = unsafe { invoke( + trampoline, + ctx_ptr, + func_ptr, + raw_args.as_ptr(), + result_space, + &mut error_out, + invoke_env, + )}; if success { Ok(()) } else { let error: RuntimeError = error_out - .map(RuntimeError::InvokeError) + .map(Into::into) .unwrap_or_else(|| RuntimeError::InvokeError(InvokeError::FailedWithNoError)); Err(error.into()) } diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index 4594bcd4f80..b4db0fbacfa 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -590,7 +590,7 @@ macro_rules! impl_traits { ) { Ok(Rets::from_ret_array(rets)) } else { - Err(error_out.map_or_else(|| RuntimeError::InvokeError(InvokeError::FailedWithNoError), RuntimeError::InvokeError)) + Err(error_out.map_or_else(|| RuntimeError::InvokeError(InvokeError::FailedWithNoError), Into::into)) } } } diff --git a/tests/high_level_api.rs b/tests/high_level_api.rs index 389e20c411b..5d9ae70fb03 100644 --- a/tests/high_level_api.rs +++ b/tests/high_level_api.rs @@ -268,7 +268,7 @@ wasmer_backends! { let result = foo.call(); - if let Err(RuntimeError(e)) = result { + if let Err(RuntimeError::User(e)) = result { let exit_code = e.downcast::().unwrap(); assert_eq!(exit_code.code, 42); } else { diff --git a/tests/imports.rs b/tests/imports.rs index e4ce076719c..ed1a2da81d7 100644 --- a/tests/imports.rs +++ b/tests/imports.rs @@ -52,7 +52,7 @@ macro_rules! call_and_assert { expected_value, concat!("Expected right when calling `", stringify!($function), "`.") ), - (Err(RuntimeError(data)), Err(RuntimeError(expected_data))) => { + (Err(RuntimeError::User(data)), Err(RuntimeError::User(expected_data))) => { if let (Some(data), Some(expected_data)) = ( data.downcast_ref::<&str>(), expected_data.downcast_ref::<&str>(), @@ -406,7 +406,7 @@ wasmer_backends! { test!( test_fn, function_fn(i32) -> i32, (1) == Ok(2)); test!( test_closure, function_closure(i32) -> i32, (1) == Ok(2)); test!( test_fn_dynamic, function_fn_dynamic(i32) -> i32, (1) == Ok(2)); - test!( test_fn_dynamic_panic, function_fn_dynamic_panic(i32) -> i32, (1) == Err(RuntimeError(Box::new("test")))); + test!( test_fn_dynamic_panic, function_fn_dynamic_panic(i32) -> i32, (1) == Err(RuntimeError::User(Box::new("test")))); test!( test_closure_dynamic_0, @@ -460,31 +460,31 @@ wasmer_backends! { test_fn_trap, function_fn_trap(i32) -> i32, - (1) == Err(RuntimeError(Box::new(format!("foo {}", 2)))) + (1) == Err(RuntimeError::User(Box::new(format!("foo {}", 2)))) ); test!( test_closure_trap, function_closure_trap(i32) -> i32, - (1) == Err(RuntimeError(Box::new(format!("bar {}", 2)))) + (1) == Err(RuntimeError::User(Box::new(format!("bar {}", 2)))) ); test!( test_fn_trap_with_vmctx, function_fn_trap_with_vmctx(i32) -> i32, - (1) == Err(RuntimeError(Box::new(format!("baz {}", 2 + SHIFT)))) + (1) == Err(RuntimeError::User(Box::new(format!("baz {}", 2 + SHIFT)))) ); test!( test_closure_trap_with_vmctx, function_closure_trap_with_vmctx(i32) -> i32, - (1) == Err(RuntimeError(Box::new(format!("qux {}", 2 + SHIFT)))) + (1) == Err(RuntimeError::User(Box::new(format!("qux {}", 2 + SHIFT)))) ); test!( test_closure_trap_with_vmctx_and_env, function_closure_trap_with_vmctx_and_env(i32) -> i32, - (1) == Err(RuntimeError(Box::new(format!("! {}", 2 + shift + SHIFT)))) + (1) == Err(RuntimeError::User(Box::new(format!("! {}", 2 + shift + SHIFT)))) ); #[test] diff --git a/tests/middleware_common.rs b/tests/middleware_common.rs index 90516af0c30..9f139a4ad79 100644 --- a/tests/middleware_common.rs +++ b/tests/middleware_common.rs @@ -7,6 +7,7 @@ use wasmer_middleware_common::metering::*; use wasmer_runtime_core::codegen::ModuleCodeGenerator; use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler}; use wasmer_runtime_core::fault::{pop_code_version, push_code_version}; +use wasmer_runtime_core::error::RuntimeError; use wasmer_runtime_core::state::CodeVersion; // Assemblyscript @@ -179,10 +180,14 @@ fn middleware_test_traps_after_costly_call(backend: &'static str, compiler: impl } let err = result.unwrap_err(); - assert!(err - .0 - .downcast_ref::() - .is_some()); + if let RuntimeError::Metering(metering_err) = err { + assert!(metering_err + .downcast_ref::() + .is_some()); + } else { + assert!(false, "metering error not found"); + } + // verify it used the correct number of points assert_eq!(get_points_used(&instance), 109); // Used points will be slightly more than `limit` because of the way we do gas checking.