Fail with Trap in Instance::new() instead of Error (#683)

This commit is contained in:
Yury Delendik
2019-12-30 16:25:16 -06:00
committed by GitHub
parent 51f3ac0c45
commit 681445b18b
18 changed files with 172 additions and 86 deletions

View File

@@ -1,7 +1,7 @@
use crate::r#ref::HostRef;
use crate::runtime::Store;
use crate::trampoline::{generate_func_export, take_api_trap};
use crate::trap::Trap;
use crate::trap::{Trap, TrapInfo};
use crate::types::FuncType;
use crate::values::Val;
use std::rc::Rc;
@@ -18,7 +18,7 @@ use wasmtime_runtime::Export;
/// struct TimesTwo;
///
/// impl wasmtime::Callable for TimesTwo {
/// fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<wasmtime::Trap>> {
/// fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), wasmtime::Trap> {
/// let mut value = params[0].unwrap_i32();
/// value *= 2;
/// results[0] = value.into();
@@ -75,7 +75,7 @@ use wasmtime_runtime::Export;
/// let results = run_function
/// .borrow()
/// .call(&[original.into()])
/// .map_err(|trap| trap.borrow().to_string())?;
/// .map_err(|trap| trap.to_string())?;
///
/// // Compare that the results returned matches what we expect.
/// assert_eq!(original * 2, results[0].unwrap_i32());
@@ -87,11 +87,11 @@ pub trait Callable {
/// `params` is an immutable list of parameters provided to the function.
/// `results` is mutable list of results to be potentially set by your
/// function. Produces a `Trap` if the function encounters any errors.
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>>;
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap>;
}
pub(crate) trait WrappedCallable {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>>;
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap>;
fn signature(&self) -> &ir::Signature {
match self.wasmtime_export() {
Export::Function { signature, .. } => signature,
@@ -119,7 +119,7 @@ impl WasmtimeFn {
}
impl WrappedCallable for WasmtimeFn {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>> {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap> {
use std::cmp::max;
use std::mem;
@@ -150,7 +150,7 @@ impl WrappedCallable for WasmtimeFn {
.context()
.compiler()
.get_published_trampoline(body, &signature, value_size)
.map_err(|_| HostRef::new(Trap::fake()))?; //was ActionError::Setup)?;
.map_err(|e| Trap::new(format!("trampoline error: {:?}", e)))?;
// Call the trampoline.
if let Err(message) = unsafe {
@@ -160,8 +160,9 @@ impl WrappedCallable for WasmtimeFn {
values_vec.as_mut_ptr() as *mut u8,
)
} {
let trap = take_api_trap().unwrap_or_else(|| HostRef::new(Trap::new(message)));
return Err(trap);
let trap = take_api_trap()
.unwrap_or_else(|| HostRef::new(TrapInfo::new(format!("call error: {}", message))));
return Err(trap.into());
}
// Load the return values out of `values_vec`.
@@ -206,7 +207,7 @@ impl NativeCallable {
}
impl WrappedCallable for NativeCallable {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>> {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap> {
self.callable.call(params, results)
}
fn wasmtime_handle(&self) -> &InstanceHandle {

View File

@@ -147,7 +147,7 @@ impl Func {
self.r#type.results().len()
}
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, HostRef<Trap>> {
pub fn call(&self, params: &[Val]) -> Result<Box<[Val]>, Trap> {
let mut results = vec![Val::null(); self.result_arity()];
self.callable.call(params, &mut results)?;
Ok(results.into_boxed_slice())

View File

@@ -4,8 +4,9 @@ use crate::module::Module;
use crate::r#ref::HostRef;
use crate::runtime::Store;
use crate::trampoline::take_api_trap;
use crate::trap::Trap;
use crate::types::{ExportType, ExternType};
use anyhow::Result;
use anyhow::{Error, Result};
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
@@ -31,7 +32,7 @@ pub fn instantiate_in_context(
imports: Vec<(String, String, Extern)>,
mut context: Context,
exports: Rc<RefCell<HashMap<String, Option<wasmtime_runtime::Export>>>>,
) -> Result<(InstanceHandle, HashSet<Context>)> {
) -> Result<(InstanceHandle, HashSet<Context>), Error> {
let mut contexts = HashSet::new();
let debug_info = context.debug_info();
let mut resolver = SimpleResolver { imports };
@@ -42,10 +43,12 @@ pub fn instantiate_in_context(
exports,
debug_info,
)
.map_err(|e| {
// TODO wrap HostRef<Trap> into Error
drop(take_api_trap());
e
.map_err(|e| -> Error {
if let Some(trap) = take_api_trap() {
Trap::from(trap).into()
} else {
e.into()
}
})?;
contexts.insert(context);
Ok((instance, contexts))
@@ -68,7 +71,7 @@ impl Instance {
store: &HostRef<Store>,
module: &HostRef<Module>,
externs: &[Extern],
) -> Result<Instance> {
) -> Result<Instance, Error> {
let context = store.borrow_mut().context().clone();
let exports = store.borrow_mut().global_exports().clone();
let imports = module

View File

@@ -28,6 +28,6 @@ pub use crate::instance::Instance;
pub use crate::module::Module;
pub use crate::r#ref::{AnyRef, HostInfo, HostRef};
pub use crate::runtime::{Config, Engine, Store};
pub use crate::trap::Trap;
pub use crate::trap::{FrameInfo, Trap, TrapInfo};
pub use crate::types::*;
pub use crate::values::*;

View File

@@ -96,6 +96,7 @@ unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, call_id: u32, values_vec: *m
0
}
Err(trap) => {
let trap = trap.trap_info_unchecked();
record_api_trap(trap);
1
}

View File

@@ -1,7 +1,7 @@
use std::cell::Cell;
use crate::r#ref::HostRef;
use crate::Trap;
use crate::TrapInfo;
use wasmtime_environ::ir::{SourceLoc, TrapCode};
use wasmtime_environ::TrapInformation;
use wasmtime_jit::trampoline::binemit;
@@ -10,10 +10,10 @@ use wasmtime_jit::trampoline::binemit;
pub const API_TRAP_CODE: TrapCode = TrapCode::User(13);
thread_local! {
static RECORDED_API_TRAP: Cell<Option<HostRef<Trap>>> = Cell::new(None);
static RECORDED_API_TRAP: Cell<Option<HostRef<TrapInfo>>> = Cell::new(None);
}
pub fn record_api_trap(trap: HostRef<Trap>) {
pub fn record_api_trap(trap: HostRef<TrapInfo>) {
RECORDED_API_TRAP.with(|data| {
let trap = Cell::new(Some(trap));
data.swap(&trap);
@@ -24,7 +24,7 @@ pub fn record_api_trap(trap: HostRef<Trap>) {
});
}
pub fn take_api_trap() -> Option<HostRef<Trap>> {
pub fn take_api_trap() -> Option<HostRef<TrapInfo>> {
RECORDED_API_TRAP.with(|data| data.take())
}

View File

@@ -1,11 +1,88 @@
use crate::instance::Instance;
use crate::r#ref::HostRef;
use std::fmt;
use std::sync::{Arc, Mutex};
use thiserror::Error;
#[derive(Debug)]
pub struct FrameInfo;
impl FrameInfo {
pub fn instance(&self) -> *const Instance {
unimplemented!("FrameInfo::instance");
}
pub fn func_index() -> usize {
unimplemented!("FrameInfo::func_index");
}
pub fn func_offset() -> usize {
unimplemented!("FrameInfo::func_offset");
}
pub fn module_offset() -> usize {
unimplemented!("FrameInfo::module_offset");
}
}
#[derive(Debug)]
pub struct TrapInfo {
message: String,
trace: Vec<FrameInfo>,
}
impl TrapInfo {
pub fn new<I: Into<String>>(message: I) -> Self {
Self {
message: message.into(),
trace: vec![],
}
}
/// Returns a reference the `message` stored in `Trap`.
pub fn message(&self) -> &str {
&self.message
}
pub fn origin(&self) -> Option<&FrameInfo> {
self.trace.first()
}
pub fn trace(&self) -> &[FrameInfo] {
&self.trace
}
}
/// A struct to hold unsafe TrapInfo host reference, designed
/// to be Send-able. The only access for it provided via the
/// Trap::trap_info_unchecked() method.
struct UnsafeTrapInfo(HostRef<TrapInfo>);
impl UnsafeTrapInfo {
fn trap_info(&self) -> HostRef<TrapInfo> {
self.0.clone()
}
}
unsafe impl Send for UnsafeTrapInfo {}
impl fmt::Debug for UnsafeTrapInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "UnsafeTrapInfo")
}
}
/// A struct representing an aborted instruction execution, with a message
/// indicating the cause.
#[derive(Error, Debug)]
#[error("Wasm trap: {message}")]
pub struct Trap {
message: String,
info: Arc<Mutex<UnsafeTrapInfo>>,
}
fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) {
(t, t)
}
impl Trap {
@@ -15,20 +92,26 @@ impl Trap {
/// let trap = wasmtime::Trap::new("unexpected error");
/// assert_eq!("unexpected error", trap.message());
/// ```
pub fn new<I: Into<String>>(message: I) -> Trap {
Self {
message: message.into(),
}
}
/// Create a `Trap` without defining a message for the trap. Mostly useful
/// for prototypes and tests.
pub fn fake() -> Trap {
Self::new("TODO trap")
pub fn new<I: Into<String>>(message: I) -> Self {
Trap::from(HostRef::new(TrapInfo::new(message)))
}
/// Returns a reference the `message` stored in `Trap`.
pub fn message(&self) -> &str {
&self.message
}
/// Returns inner TrapInfo assotiated with the Trap.
/// The method is unsafe: obtained TrapInfo is not thread safe.
pub(crate) unsafe fn trap_info_unchecked(&self) -> HostRef<TrapInfo> {
self.info.lock().unwrap().trap_info()
}
}
impl From<HostRef<TrapInfo>> for Trap {
fn from(trap_info: HostRef<TrapInfo>) -> Self {
let message = trap_info.borrow().message().to_string();
let info = Arc::new(Mutex::new(UnsafeTrapInfo(trap_info)));
Trap { message, info }
}
}

View File

@@ -8,7 +8,7 @@
use super::{
AnyRef, Callable, Engine, ExportType, Extern, ExternType, Func, FuncType, Global, GlobalType,
HostInfo, HostRef, ImportType, Instance, Limits, Memory, MemoryType, Module, Store, Table,
TableType, Trap, Val, ValType,
TableType, Trap, TrapInfo, Val, ValType,
};
use std::rc::Rc;
use std::{mem, ptr, slice};
@@ -317,7 +317,7 @@ pub type wasm_message_t = wasm_name_t;
#[repr(C)]
#[derive(Clone)]
pub struct wasm_trap_t {
trap: HostRef<Trap>,
trap_info: HostRef<TrapInfo>,
}
#[repr(C)]
#[derive(Clone)]
@@ -469,7 +469,8 @@ pub unsafe extern "C" fn wasm_func_call(
ptr::null_mut()
}
Err(trap) => {
let trap = Box::new(wasm_trap_t { trap });
let trap_info = trap.trap_info_unchecked();
let trap = Box::new(wasm_trap_t { trap_info });
Box::into_raw(trap)
}
}
@@ -539,7 +540,7 @@ impl wasm_val_t {
}
impl Callable for wasm_func_callback_t {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>> {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap> {
let params = params
.iter()
.map(|p| wasm_val_t::from_val(p))
@@ -549,7 +550,8 @@ impl Callable for wasm_func_callback_t {
let out = unsafe { func(params.as_ptr(), out_results.as_mut_ptr()) };
if !out.is_null() {
let trap: Box<wasm_trap_t> = unsafe { Box::from_raw(out) };
return Err((*trap).into());
let trap_info: HostRef<TrapInfo> = (*trap).into();
return Err(Trap::from(trap_info));
}
for i in 0..results.len() {
results[i] = out_results[i].val();
@@ -558,9 +560,9 @@ impl Callable for wasm_func_callback_t {
}
}
impl Into<HostRef<Trap>> for wasm_trap_t {
fn into(self) -> HostRef<Trap> {
self.trap
impl Into<HostRef<TrapInfo>> for wasm_trap_t {
fn into(self) -> HostRef<TrapInfo> {
self.trap_info
}
}
@@ -571,7 +573,7 @@ struct CallbackWithEnv {
}
impl Callable for CallbackWithEnv {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), HostRef<Trap>> {
fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap> {
let params = params
.iter()
.map(|p| wasm_val_t::from_val(p))
@@ -581,7 +583,8 @@ impl Callable for CallbackWithEnv {
let out = unsafe { func(self.env, params.as_ptr(), out_results.as_mut_ptr()) };
if !out.is_null() {
let trap: Box<wasm_trap_t> = unsafe { Box::from_raw(out) };
return Err((*trap).into());
let trap_info: HostRef<TrapInfo> = (*trap).into();
return Err(Trap::from(trap_info));
}
for i in 0..results.len() {
results[i] = out_results[i].val();
@@ -677,12 +680,13 @@ pub unsafe extern "C" fn wasm_instance_new(
}
Box::into_raw(instance)
}
Err(_) => {
Err(trap) => {
if !result.is_null() {
// TODO Unwrap trap from error
let trap = Box::new(wasm_trap_t {
trap: HostRef::new(Trap::new("trap during instantiation".to_string())),
});
let trap_info = match trap.downcast::<Trap>() {
Ok(trap) => trap.trap_info_unchecked(),
Err(_) => HostRef::new(TrapInfo::new("instance error".to_string())),
};
let trap = Box::new(wasm_trap_t { trap_info });
(*result) = Box::into_raw(trap);
}
ptr::null_mut()
@@ -925,7 +929,7 @@ pub unsafe extern "C" fn wasm_trap_new(
}
let message = String::from_utf8_lossy(message).to_string();
let trap = Box::new(wasm_trap_t {
trap: HostRef::new(Trap::new(message)),
trap_info: HostRef::new(TrapInfo::new(message)),
});
Box::into_raw(trap)
}
@@ -933,7 +937,7 @@ pub unsafe extern "C" fn wasm_trap_new(
#[no_mangle]
pub unsafe extern "C" fn wasm_trap_message(trap: *const wasm_trap_t, out: *mut wasm_message_t) {
let mut buffer = Vec::new();
buffer.extend_from_slice((*trap).trap.borrow().message().as_bytes());
buffer.extend_from_slice((*trap).trap_info.borrow().message().as_bytes());
buffer.reserve_exact(1);
buffer.push(0);
(*out).set_buffer(buffer);