Skip to content

Commit

Permalink
Refine errors (bytecodealliance#36)
Browse files Browse the repository at this point in the history
* Get rid of Stack error

* Add UnexpectedSignature error and remove Value err

* Publish FuncInstance::invoke

* Rename Trap to TrapKind

* Replace Trap with struct. Enum is now TrapKind

* Fixes

* Update value.rs

* Avoid reversing parameter types iter.

* Add impl From<TrapKind> for Trap

* Remove redundant clone in prepare_function_args.

* Use .into() to convert TrapKind into Trap
  • Loading branch information
pepyakin authored and NikVolf committed Feb 6, 2018
1 parent eda4882 commit 367f179
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 263 deletions.
23 changes: 16 additions & 7 deletions src/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::rc::{Rc, Weak};
use std::fmt;
use std::collections::HashMap;
use parity_wasm::elements::{Local, Opcodes};
use {Trap, Signature};
use {Trap, TrapKind, Signature};
use host::Externals;
use runner::{check_function_args, Interpreter};
use value::RuntimeValue;
Expand Down Expand Up @@ -75,7 +75,6 @@ impl fmt::Debug for FuncInstance {
}

impl FuncInstance {

/// Allocate a function instance for a host function.
///
/// When this function instance will be called by the wasm code,
Expand Down Expand Up @@ -128,20 +127,30 @@ impl FuncInstance {
}
}

pub(crate) fn invoke<E: Externals>(
/// Invoke this function.
///
/// # Errors
///
/// Returns `Err` if `args` types is not match function [`signature`] or
/// if [`Trap`] at execution time occured.
///
/// [`signature`]: #method.signature
/// [`Trap`]: #enum.Trap.html
pub fn invoke<E: Externals>(
func: &FuncRef,
args: &[RuntimeValue],
externals: &mut E,
) -> Result<Option<RuntimeValue>, Trap> {
debug_assert!(check_function_args(func.signature(), &args).is_ok());
check_function_args(func.signature(), &args).map_err(|_| TrapKind::UnexpectedSignature)?;
match *func.as_internal() {
FuncInstanceInternal::Internal { .. } => {
let mut interpreter = Interpreter::new(externals);
interpreter.start_execution(func, args)
}
FuncInstanceInternal::Host { ref host_func_index, .. } => {
externals.invoke_index(*host_func_index, args.into())
}
FuncInstanceInternal::Host {
ref host_func_index,
..
} => externals.invoke_index(*host_func_index, args.into()),
}
}
}
Expand Down
18 changes: 9 additions & 9 deletions src/host.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::any::TypeId;
use value::{RuntimeValue, TryInto};
use {Error, Trap};
use {TrapKind, Trap};

/// Safe wrapper for list of arguments.
#[derive(Debug)]
Expand All @@ -18,18 +18,18 @@ impl<'a> RuntimeArgs<'a> {
/// # Errors
///
/// Returns `Err` if cast is invalid or not enough arguments.
pub fn nth_checked<T>(&self, idx: usize) -> Result<T, Error> where RuntimeValue: TryInto<T, Error> {
Ok(self.nth_value_checked(idx)?.try_into().map_err(|_| Error::Value("Invalid argument cast".to_owned()))?)
pub fn nth_checked<T>(&self, idx: usize) -> Result<T, Trap> where RuntimeValue: TryInto<T, ::value::Error> {
Ok(self.nth_value_checked(idx)?.try_into().map_err(|_| TrapKind::UnexpectedSignature)?)
}

/// Extract argument as a [`RuntimeValue`] by index `idx`.
///
/// # Errors
///
/// Returns `Err` if this list has not enough arguments.
pub fn nth_value_checked(&self, idx: usize) -> Result<RuntimeValue, Error> {
pub fn nth_value_checked(&self, idx: usize) -> Result<RuntimeValue, Trap> {
if self.0.len() <= idx {
return Err(Error::Value("Invalid argument index".to_owned()));
return Err(TrapKind::UnexpectedSignature.into());
}
Ok(self.0[idx])
}
Expand All @@ -39,7 +39,7 @@ impl<'a> RuntimeArgs<'a> {
/// # Panics
///
/// Panics if cast is invalid or not enough arguments.
pub fn nth<T>(&self, idx: usize) -> T where RuntimeValue: TryInto<T, Error> {
pub fn nth<T>(&self, idx: usize) -> T where RuntimeValue: TryInto<T, ::value::Error> {
let value = self.nth_value_checked(idx).expect("Invalid argument index");
value.try_into().expect("Unexpected argument type")
}
Expand Down Expand Up @@ -139,8 +139,8 @@ impl HostError {
/// ) -> Result<Option<RuntimeValue>, Trap> {
/// match index {
/// ADD_FUNC_INDEX => {
/// let a: u32 = args.nth(0);
/// let b: u32 = args.nth(1);
/// let a: u32 = args.nth_checked(0)?;
/// let b: u32 = args.nth_checked(1)?;
/// let result = a + b;
///
/// Ok(Some(RuntimeValue::I32(result as i32)))
Expand Down Expand Up @@ -207,7 +207,7 @@ impl Externals for NopExternals {
_index: usize,
_args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Trap> {
Err(Trap::Unreachable)
Err(TrapKind::Unreachable.into())
}
}

Expand Down
75 changes: 58 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,34 @@ use std::fmt;
use std::error;
use std::collections::HashMap;

/// Error type which can happen at execution time.
/// Error type which can thrown by wasm code or by host environment.
///
/// Under some conditions, wasm execution may produce a `Trap`, which immediately aborts execution.
/// Traps can't be handled by WebAssembly code, but are reported to the embedder.
#[derive(Debug)]
pub enum Trap {
pub struct Trap {
kind: TrapKind,
}

impl Trap {
/// Create new trap.
pub fn new(kind: TrapKind) -> Trap {
Trap { kind }
}

/// Returns kind of this trap.
pub fn kind(&self) -> &TrapKind {
&self.kind
}
}

/// Error type which can thrown by wasm code or by host environment.
///
/// See [`Trap`] for details.
///
/// [`Trap`]: struct.Trap.html
#[derive(Debug)]
pub enum TrapKind {
/// Wasm code executed `unreachable` opcode.
///
/// `unreachable` is a special opcode which always traps upon execution.
Expand Down Expand Up @@ -169,6 +191,12 @@ pub enum Trap {
/// Extensive inlining might also be the cause of stack overflow.
StackOverflow,

/// Unexpected signature provided.
///
/// This can happen if [`FuncInstance`] was invoked
/// with mismatching signature.
UnexpectedSignature,

/// Error specified by the host.
///
/// Typically returned from an implementation of [`Externals`].
Expand All @@ -193,8 +221,6 @@ pub enum Error {
Memory(String),
/// Global-level error.
Global(String),
/// Stack-level error.
Stack(String),
/// Value-level error.
Value(String),
/// Trap.
Expand All @@ -203,6 +229,27 @@ pub enum Error {
Host(Box<host::HostError>),
}

impl Error {
/// Returns [`HostError`] if this `Error` represents some host error.
///
/// I.e. if this error have variant [`Host`] or [`Trap`][`Trap`] with [host][`TrapKind::Host`] error.
///
/// [`HostError`]: trait.HostError.html
/// [`Host`]: enum.Error.html#variant.Host
/// [`Trap`]: enum.Error.html#variant.Trap
/// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host
pub fn as_host_error(&self) -> Option<&host::HostError> {
match *self {
Error::Host(ref host_err) => Some(&**host_err),
Error::Trap(ref trap) => match *trap.kind() {
TrapKind::Host(ref host_err) => Some(&**host_err),
_ => None,
}
_ => None,
}
}
}

impl Into<String> for Error {
fn into(self) -> String {
match self {
Expand All @@ -212,7 +259,6 @@ impl Into<String> for Error {
Error::Table(s) => s,
Error::Memory(s) => s,
Error::Global(s) => s,
Error::Stack(s) => s,
Error::Value(s) => s,
Error::Trap(s) => format!("trap: {:?}", s),
Error::Host(e) => format!("user: {}", e),
Expand All @@ -229,16 +275,13 @@ impl fmt::Display for Error {
Error::Table(ref s) => write!(f, "Table: {}", s),
Error::Memory(ref s) => write!(f, "Memory: {}", s),
Error::Global(ref s) => write!(f, "Global: {}", s),
Error::Stack(ref s) => write!(f, "Stack: {}", s),
Error::Value(ref s) => write!(f, "Value: {}", s),
Error::Trap(ref s) => write!(f, "Trap: {:?}", s),
Error::Host(ref e) => write!(f, "User: {}", e),
}
}
}



impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Expand All @@ -248,15 +291,13 @@ impl error::Error for Error {
Error::Table(ref s) => s,
Error::Memory(ref s) => s,
Error::Global(ref s) => s,
Error::Stack(ref s) => s,
Error::Value(ref s) => s,
Error::Trap(_) => "Trap",
Error::Host(_) => "Host error",
}
}
}


impl<U> From<U> for Error where U: host::HostError + Sized {
fn from(e: U) -> Self {
Error::Host(Box::new(e))
Expand All @@ -265,7 +306,7 @@ impl<U> From<U> for Error where U: host::HostError + Sized {

impl<U> From<U> for Trap where U: host::HostError + Sized {
fn from(e: U) -> Self {
Trap::Host(Box::new(e))
Trap::new(TrapKind::Host(Box::new(e)))
}
}

Expand All @@ -275,15 +316,15 @@ impl From<Trap> for Error {
}
}

impl From<validation::Error> for Error {
fn from(e: validation::Error) -> Error {
Error::Validation(e.to_string())
impl From<TrapKind> for Trap {
fn from(e: TrapKind) -> Trap {
Trap::new(e)
}
}

impl From<::common::stack::Error> for Error {
fn from(e: ::common::stack::Error) -> Self {
Error::Stack(e.to_string())
impl From<validation::Error> for Error {
fn from(e: validation::Error) -> Error {
Error::Validation(e.to_string())
}
}

Expand Down
Loading

0 comments on commit 367f179

Please sign in to comment.