Skip to content

Commit

Permalink
Refactor trap (#318)
Browse files Browse the repository at this point in the history
* rename TrapKind to TrapCode

* move extern crate to other extern crate definitions

* add new Trap implementation in trap.rs

* move to new trap implementation

* fix no_std build

* update some trap docs

* apply rustfmt

* fix test code

* use TrapCode instead of Trap where possible in executor

* move From impl into trap.rs module where it belongs

* move From impl to other From impl

* apply rustfmt

* use Trap in exec_context again to improve performance

* apply clippy suggestion

* fix bench code

* remove duplicated no_std libm import
  • Loading branch information
Robbepop authored Jan 11, 2022
1 parent 0793ada commit cd59462
Show file tree
Hide file tree
Showing 13 changed files with 391 additions and 402 deletions.
6 changes: 3 additions & 3 deletions src/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,11 +327,11 @@ impl<'args> FuncInvocation<'args> {
return_val: Option<Value>,
externals: &'externals mut E,
) -> Result<Option<Value>, ResumableError> {
use crate::TrapKind;
use crate::TrapCode;

if return_val.map(|v| v.value_type()) != self.resumable_value_type() {
return Err(ResumableError::Trap(Trap::new(
TrapKind::UnexpectedSignature,
return Err(ResumableError::Trap(Trap::from(
TrapCode::UnexpectedSignature,
)));
}

Expand Down
8 changes: 4 additions & 4 deletions src/host.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
value::{FromValue, Value},
Trap,
TrapKind,
TrapCode,
};

use downcast_rs::{impl_downcast, DowncastSync};
Expand Down Expand Up @@ -37,7 +37,7 @@ impl<'a> RuntimeArgs<'a> {
{
self.nth_value_checked(idx)?
.try_into()
.ok_or(TrapKind::UnexpectedSignature)
.ok_or(TrapCode::UnexpectedSignature)
.map_err(Into::into)
}

Expand All @@ -50,7 +50,7 @@ impl<'a> RuntimeArgs<'a> {
/// [`Value`]: enum.Value.html
pub fn nth_value_checked(&self, idx: usize) -> Result<Value, Trap> {
if self.0.len() <= idx {
return Err(TrapKind::UnexpectedSignature.into());
return Err(TrapCode::UnexpectedSignature.into());
}
Ok(self.0[idx])
}
Expand Down Expand Up @@ -220,7 +220,7 @@ pub struct NopExternals;

impl Externals for NopExternals {
fn invoke_index(&mut self, _index: usize, _args: RuntimeArgs) -> Result<Option<Value>, Trap> {
Err(TrapKind::Unreachable.into())
Err(TrapCode::Unreachable.into())
}
}

Expand Down
201 changes: 12 additions & 189 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,180 +105,22 @@ extern crate alloc;
#[cfg(feature = "std")]
extern crate std as alloc;

#[cfg(not(feature = "std"))]
extern crate libm;

use alloc::{
boxed::Box,
string::{String, ToString},
vec::Vec,
};
use core::fmt::{self, Display};
use core::fmt;
#[cfg(feature = "std")]
use std::error;

#[cfg(not(feature = "std"))]
extern crate libm;

#[doc(inline)]
#[deprecated(note = "use `Value` instead")]
pub use self::value::Value as RuntimeValue;

/// Error type which can be 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 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
}

/// Converts into kind of this trap.
pub fn into_kind(self) -> TrapKind {
self.kind
}
}

impl fmt::Display for Trap {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.kind())
}
}

#[cfg(feature = "std")]
impl error::Error for Trap {
fn description(&self) -> &str {
self.kind().trap_message()
}
}

/// Error type which can be 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.
/// This opcode have a similar purpose as `ud2` in x86.
Unreachable,

/// Attempt to load or store at the address which
/// lies outside of bounds of the memory.
///
/// Since addresses are interpreted as unsigned integers, out of bounds access
/// can't happen with negative addresses (i.e. they will always wrap).
MemoryAccessOutOfBounds,

/// Attempt to access table element at index which
/// lies outside of bounds.
///
/// This typically can happen when `call_indirect` is executed
/// with index that lies out of bounds.
///
/// Since indexes are interpreted as unsinged integers, out of bounds access
/// can't happen with negative indexes (i.e. they will always wrap).
TableAccessOutOfBounds,

/// Attempt to access table element which is uninitialized (i.e. `None`).
///
/// This typically can happen when `call_indirect` is executed.
ElemUninitialized,

/// Attempt to divide by zero.
///
/// This trap typically can happen if `div` or `rem` is executed with
/// zero as divider.
DivisionByZero,

/// An integer arithmetic operation caused an overflow.
///
/// This can happen when:
///
/// - Trying to do signed division (or get the remainder) -2<sup>N-1</sup> over -1. This is
/// because the result +2<sup>N-1</sup> isn't representable as a N-bit signed integer.
IntegerOverflow,

/// Attempt to make a conversion to an int failed.
///
/// This can happen when:
///
/// - Trying to truncate NaNs, infinity, or value for which the result is out of range into an integer.
InvalidConversionToInt,

/// Stack overflow.
///
/// This is likely caused by some infinite or very deep recursion.
/// Extensive inlining might also be the cause of stack overflow.
StackOverflow,

/// Attempt to invoke a function with mismatching signature.
///
/// This can happen if [`FuncInstance`] was invoked
/// with mismatching [signature][`Signature`].
///
/// This can always happen with indirect calls. `call_indirect` instruction always
/// specifies the expected signature of function. If `call_indirect` is executed
/// with index that points on function with signature different that is
/// expected by this `call_indirect`, this trap is raised.
///
/// [`Signature`]: struct.Signature.html
UnexpectedSignature,

/// Error specified by the host.
///
/// Typically returned from an implementation of [`Externals`].
///
/// [`Externals`]: trait.Externals.html
Host(Box<dyn host::HostError>),
}

impl TrapKind {
/// Whether this trap is specified by the host.
pub fn is_host(&self) -> bool {
matches!(self, TrapKind::Host(_))
}

/// Returns the trap message as specified by the WebAssembly specification.
///
/// # Note
///
/// This API is primarily useful for the Wasm spec testsuite but might have
/// other uses since it avoid heap memory allocation in certain cases.
pub fn trap_message(&self) -> &'static str {
match self {
TrapKind::Unreachable => "unreachable",
TrapKind::MemoryAccessOutOfBounds => "out of bounds memory access",
TrapKind::TableAccessOutOfBounds => "undefined element",
TrapKind::ElemUninitialized => "uninitialized element",
TrapKind::DivisionByZero => "integer divide by zero",
TrapKind::IntegerOverflow => "integer overflow",
TrapKind::InvalidConversionToInt => "invalid conversion to integer",
TrapKind::StackOverflow => "call stack exhausted",
TrapKind::UnexpectedSignature => "indirect call type mismatch",

// Note: The below trap message is not further specified by the Wasm spec.
TrapKind::Host(_) => "host trap",
}
}
}

impl Display for TrapKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.trap_message())
}
}

/// Internal interpreter error.
#[derive(Debug)]
pub enum Error {
Expand Down Expand Up @@ -314,10 +156,8 @@ impl Error {
/// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host
pub fn as_host_error(&self) -> Option<&dyn host::HostError> {
match self {
Error::Host(host_err) => Some(&**host_err),
Error::Trap(Trap {
kind: TrapKind::Host(host_err),
}) => Some(&**host_err),
Self::Host(host_error) => Some(&**host_error),
Self::Trap(Trap::Host(host_error)) => Some(&**host_error),
_ => None,
}
}
Expand All @@ -332,10 +172,8 @@ impl Error {
/// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host
pub fn into_host_error(self) -> Option<Box<dyn host::HostError>> {
match self {
Error::Host(host_err) => Some(host_err),
Error::Trap(Trap {
kind: TrapKind::Host(host_err),
}) => Some(host_err),
Error::Host(host_error) => Some(host_error),
Self::Trap(Trap::Host(host_error)) => Some(host_error),
_ => None,
}
}
Expand All @@ -350,10 +188,8 @@ impl Error {
/// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host
pub fn try_into_host_error(self) -> Result<Box<dyn host::HostError>, Self> {
match self {
Error::Host(host_err) => Ok(host_err),
Error::Trap(Trap {
kind: TrapKind::Host(host_err),
}) => Ok(host_err),
Error::Host(host_error) => Ok(host_error),
Self::Trap(Trap::Host(host_error)) => Ok(host_error),
other => Err(other),
}
}
Expand Down Expand Up @@ -418,27 +254,12 @@ where
}
}

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

impl From<Trap> for Error {
fn from(e: Trap) -> Error {
Error::Trap(e)
}
}

impl From<TrapKind> for Trap {
fn from(e: TrapKind) -> Trap {
Trap::new(e)
}
}

impl From<validation::Error> for Error {
fn from(e: validation::Error) -> Error {
Error::Validation(e.to_string())
Expand All @@ -456,6 +277,7 @@ pub mod nan_preserving_float;
mod prepare;
mod runner;
mod table;
mod trap;
mod types;
pub mod v1;
mod value;
Expand All @@ -472,6 +294,7 @@ pub use self::{
module::{ExternVal, ModuleInstance, ModuleRef, NotStartedModuleRef},
runner::{StackRecycler, DEFAULT_CALL_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT},
table::{TableInstance, TableRef},
trap::{Trap, TrapCode},
types::{GlobalDescriptor, MemoryDescriptor, Signature, TableDescriptor, ValueType},
value::{FromValue, LittleEndianConvert, Value},
};
Expand Down
Loading

0 comments on commit cd59462

Please sign in to comment.