blob: dec97f6eabdbd6d8f466f7edb482b0318f347861 [file] [log] [blame]
use std::{fmt, error};
use nan_preserving_float::{F32, F64};
#[derive(Debug, Serialize, Deserialize)]
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, Serialize, Deserialize)]
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,
/// Attempt to make a conversion to an int failed.
///
/// 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.
/// - 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<host::HostError>),
}
/// Internal interpreter error.
#[derive(Debug, Serialize, Deserialize)]
pub enum Error {
/// Module validation error. Might occur only at load time.
Validation(String),
/// Error while instantiating a module. Might occur when provided
/// with incorrect exports (i.e. linkage failure).
Instantiation(String),
/// Function-level error.
Function(String),
/// Table-level error.
Table(String),
/// Memory-level error.
Memory(String),
/// Global-level error.
Global(String),
/// Value-level error.
Value(String),
/// Trap.
Trap(Trap),
// Custom embedder error.
//Host(Box<host::HostError>),
}
impl Into<String> for Error {
fn into(self) -> String {
match self {
Error::Validation(s) => s,
Error::Instantiation(s) => s,
Error::Function(s) => s,
Error::Table(s) => s,
Error::Memory(s) => s,
Error::Global(s) => s,
Error::Value(s) => s,
Error::Trap(s) => format!("trap: {:?}", s),
// Error::Host(e) => format!("user: {}", e),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Validation(ref s) => write!(f, "Validation: {}", s),
Error::Instantiation(ref s) => write!(f, "Instantiation: {}", s),
Error::Function(ref s) => write!(f, "Function: {}", s),
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::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 {
Error::Validation(ref s) => s,
Error::Instantiation(ref s) => s,
Error::Function(ref s) => s,
Error::Table(ref s) => s,
Error::Memory(ref s) => s,
Error::Global(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))
// }
//}
//
//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)
}
}
/// Type of a value.
///
/// See [`RuntimeValue`] for details.
///
/// [`RuntimeValue`]: enum.RuntimeValue.html
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ValueType {
/// 32-bit signed or unsigned integer.
I32,
/// 64-bit signed or unsigned integer.
I64,
/// 32-bit IEEE 754-2008 floating point number.
F32,
/// 64-bit IEEE 754-2008 floating point number.
F64,
/// 128-bit SIMD register
V128,
}
/// Runtime representation of a value.
///
/// Wasm code manipulate values of the four basic value types:
/// integers and floating-point (IEEE 754-2008) data of 32 or 64 bit width each, respectively.
///
/// There is no distinction between signed and unsigned integer types. Instead, integers are
/// interpreted by respective operations as either unsigned or signed in two’s complement representation.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum RuntimeValue {
/// Value of 32-bit signed or unsigned integer.
I32(i32),
/// Value of 64-bit signed or unsigned integer.
I64(i64),
/// Value of 32-bit IEEE 754-2008 floating point number.
F32(F32),
/// Value of 64-bit IEEE 754-2008 floating point number.
F64(F64),
/// 128-bit SIMD register
V128(u128),
}
impl RuntimeValue {
pub fn value_type(&self) -> ValueType {
match *self {
RuntimeValue::I32(_) => ValueType::I32,
RuntimeValue::I64(_) => ValueType::I64,
RuntimeValue::F32(_) => ValueType::F32,
RuntimeValue::F64(_) => ValueType::F64,
RuntimeValue::V128(_) => ValueType::V128,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Value {
/// Value of 32-bit signed or unsigned integer.
I32(i32),
/// Value of 64-bit signed or unsigned integer.
I64(i64),
/// Value of 32-bit IEEE 754-2008 floating point number.
F32(f32),
/// Value of 64-bit IEEE 754-2008 floating point number.
F64(f64),
/// 128-bit SIMD register
V128(u128),
}