Skip to content

Commit

Permalink
Merge #1285
Browse files Browse the repository at this point in the history
1285: feat(interface-types) Greatly improve errors r=Hywan a=Hywan

~This PR is build on top of #1284. It must be merged first. [View the exact diff](https://github.com/Hywan/wasmer/compare/feat-interface-types-instructions-string-and-memory...Hywan:feat-interface-types-better-error?expand=1).~

Errors in `wasmer-interface-types` were just `&'static str`, which isn't quite great 😉. This PR introduces the `errors` module, with structures that represent errors. A `Display` implementation maps the errors to strings.

Co-authored-by: Ivan Enderlin <ivan.enderlin@hoa-project.net>
  • Loading branch information
bors[bot] and Hywan authored Mar 12, 2020
2 parents 18168fc + 03910a5 commit f77df9b
Show file tree
Hide file tree
Showing 14 changed files with 548 additions and 241 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## **[Unreleased]**

- [#1285](https://github.com/wasmerio/wasmer/pull/1285) Greatly improve errors in `wasmer-interface-types`
- [#1283](https://github.com/wasmerio/wasmer/pull/1283) Workaround for floating point arguments and return values in `DynamicFunc`s.

## 0.16.2 - 2020-03-11
Expand Down
2 changes: 1 addition & 1 deletion lib/interface-types/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::interpreter::Instruction;
use std::str;

/// Represents the types supported by WIT.
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Debug, Clone)]
pub enum InterfaceType {
/// A 8-bits signed integer.
S8,
Expand Down
232 changes: 232 additions & 0 deletions lib/interface-types/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
//! The error module contains all the data structures that represent
//! an error.

use crate::{ast::InterfaceType, interpreter::Instruction};
use std::{
error::Error,
fmt::{self, Display, Formatter},
result::Result,
string::{self, ToString},
};

/// A type alias for instruction's results.
pub type InstructionResult<T> = Result<T, InstructionError>;

/// A type alias for the interpreter result.
pub type InterpreterResult<T> = Result<T, InstructionError>;

/// Structure to represent errors when casting from an `InterfaceType`
/// to a native value.
#[derive(Debug)]
pub struct WasmValueNativeCastError {
/// The initial type.
pub from: InterfaceType,

/// The targeted type.
///
/// `InterfaceType` is used to represent the native type by
/// associativity.
pub to: InterfaceType,
}

impl Error for WasmValueNativeCastError {}

impl Display for WasmValueNativeCastError {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter, "{:?}", self)
}
}

/// Structure to represent the errors for instructions.
#[derive(Debug)]
pub struct InstructionError {
/// The instruction that raises the error.
pub instruction: Instruction,

/// The error kind.
pub error_kind: InstructionErrorKind,
}

impl InstructionError {
pub(crate) fn new(instruction: Instruction, error_kind: InstructionErrorKind) -> Self {
Self {
instruction,
error_kind,
}
}
}

impl Error for InstructionError {}

impl Display for InstructionError {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(
formatter,
"`{}` {}",
(&self.instruction).to_string(),
self.error_kind
)
}
}

/// The kind of instruction errors.
#[derive(Debug)]
pub enum InstructionErrorKind {
/// The instruction needs to read an invocation input at index `index`, but it's missing.
InvocationInputIsMissing {
/// The invocation input index.
index: u32,
},

/// Failed to cast from a WIT value to a native value.
ToNative(WasmValueNativeCastError),

/// Failed to cast from `from` to `to`.
LoweringLifting {
/// The initial type.
from: InterfaceType,

/// The targeted type.
to: InterfaceType,
},

/// Read a value from the stack, but it doesn't have the expected
/// type.
InvalidValueOnTheStack {
/// The expected type.
expected_type: InterfaceType,

/// The received type.
received_type: InterfaceType,
},

/// Need to read some values from the stack, but it doesn't
/// contain enough data.
StackIsTooSmall {
/// The number of values that were needed.
needed: usize,
},

/// The local or import function doesn't exist.
LocalOrImportIsMissing {
/// The local or import function index.
function_index: u32,
},

/// Values given to a local or import function doesn't match the
/// function signature.
LocalOrImportSignatureMismatch {
/// The local or import function index.
function_index: u32,

/// The expected signature.
expected: (Vec<InterfaceType>, Vec<InterfaceType>),

/// The received signature.
received: (Vec<InterfaceType>, Vec<InterfaceType>),
},

/// Failed to call a local or import function.
LocalOrImportCall {
/// The local or import function index that has been called.
function_index: u32,
},

/// The memory doesn't exist.
MemoryIsMissing {
/// The memory indeX.
memory_index: u32,
},

/// Tried to read out of bounds of the memory.
MemoryOutOfBoundsAccess {
/// The access index.
index: usize,

/// The memory length.
length: usize,
},

/// The string contains invalid UTF-8 encoding.
String(string::FromUtf8Error),
}

impl Error for InstructionErrorKind {}

impl Display for InstructionErrorKind {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
match self {
Self::InvocationInputIsMissing { index } => write!(
formatter,
"cannot access invocation inputs #{} because it doesn't exist",
index
),

Self::ToNative(WasmValueNativeCastError { from, .. }) => write!(
formatter,
"failed to cast the WIT value `{:?}` to its native type",
from,
),

Self::LoweringLifting { from, to } => {
write!(formatter, "failed to cast `{:?}` to `{:?}`", from, to)
}

Self::InvalidValueOnTheStack {
expected_type,
received_type,
} => write!(
formatter,
"read a value of type `{:?}` from the stack, but the type `{:?}` was expected",
received_type, expected_type,
),

Self::StackIsTooSmall { needed } => write!(
formatter,
"needed to read `{}` value(s) from the stack, but it doesn't contain enough data",
needed
),

Self::LocalOrImportIsMissing { function_index } => write!(
formatter,
"the local or import function `{}` doesn't exist",
function_index
),

Self::LocalOrImportSignatureMismatch { function_index, expected, received } => write!(
formatter,
"the local or import function `{}` has the signature `{:?} -> {:?}` but it received values of kind `{:?} -> {:?}`",
function_index,
expected.0,
expected.1,
received.0,
received.1,
),

Self::LocalOrImportCall { function_index } => write!(
formatter,
"failed while calling the local or import function `{}`",
function_index
),

Self::MemoryIsMissing { memory_index } => write!(
formatter,
"memory `{}` does not exist",
memory_index,
),

Self::MemoryOutOfBoundsAccess { index, length } => write!(
formatter,
"read out of the memory bounds (index {} > memory length {})",
index,
length,
),

Self::String(error) => write!(
formatter,
"{}",
error
),
}
}
}
2 changes: 1 addition & 1 deletion lib/interface-types/src/interpreter/instruction.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//use crate::ast::InterfaceType;

/// Represents all the possible WIT instructions.
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum Instruction {
/// The `arg.get` instruction.
ArgumentGet {
Expand Down
15 changes: 10 additions & 5 deletions lib/interface-types/src/interpreter/instructions/argument_get.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
use crate::{
errors::{InstructionError, InstructionErrorKind},
interpreter::Instruction,
};

executable_instruction!(
argument_get(index: u32, instruction_name: String) -> _ {
argument_get(index: u32, instruction: Instruction) -> _ {
move |runtime| -> _ {
let invocation_inputs = runtime.invocation_inputs;

if index >= (invocation_inputs.len() as u32) {
return Err(format!(
"`{}` cannot access argument #{} because it doesn't exist.",
instruction_name, index
return Err(InstructionError::new(
instruction,
InstructionErrorKind::InvocationInputIsMissing { index },
));
}

Expand Down Expand Up @@ -49,6 +54,6 @@ mod tests {
instructions: [Instruction::ArgumentGet { index: 1 }],
invocation_inputs: [InterfaceValue::I32(42)],
instance: Instance::new(),
error: "`arg.get 1` cannot access argument #1 because it doesn't exist."
error: "`arg.get 1` cannot access invocation inputs #1 because it doesn't exist"
);
}
Loading

0 comments on commit f77df9b

Please sign in to comment.