From f0c97a1b81fbd948faf5d499fc6a0126d9db3767 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 10 Mar 2020 15:41:49 +0100 Subject: [PATCH 1/6] feat(interface-types) Use better errors. The new `errors` module contains structure to represent errors, instead of using basic strings. The first usage is in the interpreter itself. --- lib/interface-types/src/ast.rs | 2 +- lib/interface-types/src/errors.rs | 219 ++++++++++++++++++ .../src/interpreter/instruction.rs | 2 +- .../interpreter/instructions/argument_get.rs | 15 +- .../src/interpreter/instructions/call_core.rs | 70 +++--- .../instructions/lowering_lifting.rs | 54 +++-- .../instructions/memory_to_string.rs | 97 ++++---- .../src/interpreter/instructions/mod.rs | 21 ++ .../instructions/string_to_memory.rs | 82 ++++--- lib/interface-types/src/interpreter/mod.rs | 89 +++---- .../src/interpreter/wasm/values.rs | 49 ++-- lib/interface-types/src/lib.rs | 1 + lib/interface-types/src/macros.rs | 2 +- 13 files changed, 498 insertions(+), 205 deletions(-) create mode 100644 lib/interface-types/src/errors.rs diff --git a/lib/interface-types/src/ast.rs b/lib/interface-types/src/ast.rs index 21b76020d66..9828043db0d 100644 --- a/lib/interface-types/src/ast.rs +++ b/lib/interface-types/src/ast.rs @@ -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, diff --git a/lib/interface-types/src/errors.rs b/lib/interface-types/src/errors.rs new file mode 100644 index 00000000000..24ca5566a50 --- /dev/null +++ b/lib/interface-types/src/errors.rs @@ -0,0 +1,219 @@ +//! The error module contains all the data structures that represent +//! an error. + +use crate::{ast::InterfaceType, interpreter::Instruction}; +use std::{ + fmt::{self, Display, Formatter}, + result::Result, + string::{self, ToString}, +}; + +/// A type alias for instruction's results. +pub type InstructionResult = Result; + +/// A type alias for the interpreter result. +pub type InterpreterResult = Result; + +/// 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, +} + +/// 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 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, Vec), + + /// The received signature. + received: (Vec, Vec), + }, + + /// 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 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 + ), + } + } +} diff --git a/lib/interface-types/src/interpreter/instruction.rs b/lib/interface-types/src/interpreter/instruction.rs index 109f269b808..425cdad5ca0 100644 --- a/lib/interface-types/src/interpreter/instruction.rs +++ b/lib/interface-types/src/interpreter/instruction.rs @@ -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 { diff --git a/lib/interface-types/src/interpreter/instructions/argument_get.rs b/lib/interface-types/src/interpreter/instructions/argument_get.rs index e530d880a34..dd161e15860 100644 --- a/lib/interface-types/src/interpreter/instructions/argument_get.rs +++ b/lib/interface-types/src/interpreter/instructions/argument_get.rs @@ -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 }, )); } @@ -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" ); } diff --git a/lib/interface-types/src/interpreter/instructions/call_core.rs b/lib/interface-types/src/interpreter/instructions/call_core.rs index 3179c9080ab..dfaf520e463 100644 --- a/lib/interface-types/src/interpreter/instructions/call_core.rs +++ b/lib/interface-types/src/interpreter/instructions/call_core.rs @@ -1,10 +1,14 @@ -use crate::interpreter::wasm::{ - structures::{FunctionIndex, TypedIndex}, - values::InterfaceType, +use crate::{ + errors::{InstructionError, InstructionErrorKind}, + interpreter::wasm::{ + structures::{FunctionIndex, TypedIndex}, + values::InterfaceType, + }, + interpreter::Instruction, }; executable_instruction!( - call_core(function_index: usize, instruction_name: String) -> _ { + call_core(function_index: usize, instruction: Instruction) -> _ { move |runtime| -> _ { let instance = &mut runtime.wasm_instance; let index = FunctionIndex::new(function_index); @@ -21,12 +25,16 @@ executable_instruction!( .collect::>(); if input_types != local_or_import.inputs() { - return Err(format!( - "`{}` cannot call the local or imported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).", - instruction_name, - function_index, - local_or_import.inputs(), - )) + return Err( + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportSignatureMismatch { + function_index: function_index as u32, + expected: (local_or_import.inputs().to_vec(), vec![]), + received: (input_types, vec![]), + } + ) + ) } match local_or_import.call(&inputs) { @@ -37,26 +45,28 @@ executable_instruction!( Ok(()) } - Err(_) => Err(format!( - "`{}` failed when calling the local or imported function `{}`.", - instruction_name, - function_index - )) + Err(_) => Err( + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportCall { function_index: function_index as u32, }, + ) + ) } } - None => Err(format!( - "`{}` cannot call the local or imported function `{}` because there is not enough data on the stack for the arguments (needs {}).", - instruction_name, - function_index, - inputs_cardinality, - )) + None => Err( + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: inputs_cardinality }, + ) + ) } } - None => Err(format!( - "`{}` cannot call the local or imported function `{}` because it doesn't exist.", - instruction_name, - function_index, - )) + None => Err( + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportIsMissing { function_index: function_index as u32, }, + ) + ) } } } @@ -89,7 +99,7 @@ mod tests { InterfaceValue::I32(4), ], instance: Default::default(), - error: r#"`call-core 42` cannot call the local or imported function `42` because it doesn't exist."#, + error: r#"`call-core 42` the local or import function `42` doesn't exist"#, ); test_executable_instruction!( @@ -104,7 +114,7 @@ mod tests { InterfaceValue::I32(4), ], instance: Instance::new(), - error: r#"`call-core 42` cannot call the local or imported function `42` because there is not enough data on the stack for the arguments (needs 2)."#, + error: r#"`call-core 42` needed to read `2` value(s) from the stack, but it doesn't contain enough data"#, ); test_executable_instruction!( @@ -120,7 +130,7 @@ mod tests { // ^^^ mismatch with `42` signature ], instance: Instance::new(), - error: r#"`call-core 42` cannot call the local or imported function `42` because the value types on the stack mismatch the function signature (expects [I32, I32])."#, + error: r#"`call-core 42` the local or import function `42` has the signature `[I32, I32] -> []` but it received values of kind `[I32, I64] -> []`"#, ); test_executable_instruction!( @@ -151,7 +161,7 @@ mod tests { }, ..Default::default() }, - error: r#"`call-core 42` failed when calling the local or imported function `42`."#, + error: r#"`call-core 42` failed while calling the local or import function `42`"#, ); test_executable_instruction!( diff --git a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs index c24fd992bb7..f18c24385f6 100644 --- a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs +++ b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs @@ -1,10 +1,14 @@ -use crate::interpreter::wasm::values::InterfaceValue; +use crate::{ + ast::InterfaceType, + errors::{InstructionError, InstructionErrorKind}, + interpreter::{wasm::values::InterfaceValue, Instruction}, +}; use std::convert::TryInto; macro_rules! lowering_lifting { ($instruction_function_name:ident, $instruction_name:expr, $from_variant:ident, $to_variant:ident) => { executable_instruction!( - $instruction_function_name() -> _ { + $instruction_function_name(instruction: Instruction) -> _ { move |runtime| -> _ { match runtime.stack.pop1() { Some(InterfaceValue::$from_variant(value)) => { @@ -12,39 +16,33 @@ macro_rules! lowering_lifting { .stack .push(InterfaceValue::$to_variant(value.try_into().map_err( |_| { - concat!( - "Failed to cast `", - stringify!($from_variant), - "` to `", - stringify!($to_variant), - "`." - ).to_string() + InstructionError::new( + instruction, + InstructionErrorKind::LoweringLifting { from: InterfaceType::$from_variant, to: InterfaceType::$to_variant }, + ) }, )?)) } Some(wrong_value) => { - return Err(format!( - concat!( - "Instruction `", - $instruction_name, - "` expects a `", - stringify!($from_variant), - "` value on the stack, got `{:?}`.", - ), - wrong_value - + return Err( + InstructionError::new( + instruction, + InstructionErrorKind::InvalidValueOnTheStack { + expected_type: InterfaceType::$from_variant, + received_type: (&wrong_value).into(), + } + ) ) - .to_string()) }, None => { - return Err(concat!( - "Instruction `", - $instruction_name, - "` needs one value on the stack." + return Err( + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 1 }, + ) ) - .to_string()) } } @@ -103,7 +101,7 @@ mod tests { instructions: [Instruction::ArgumentGet { index: 0}, Instruction::I32ToS8], invocation_inputs: [InterfaceValue::I32(128)], instance: Instance::new(), - error: "Failed to cast `I32` to `S8`." + error: "`i32-to-s8` failed to cast `I32` to `S8`" ); test_executable_instruction!( @@ -111,7 +109,7 @@ mod tests { instructions: [Instruction::ArgumentGet { index: 0}, Instruction::I32ToS8], invocation_inputs: [InterfaceValue::I64(42)], instance: Instance::new(), - error: "Instruction `i32-to-s8` expects a `I32` value on the stack, got `I64(42)`." + error: "`i32-to-s8` read a value of type `I64` from the stack, but the type `I32` was expected" ); test_executable_instruction!( @@ -119,7 +117,7 @@ mod tests { instructions: [Instruction::I32ToS8], invocation_inputs: [InterfaceValue::I32(42)], instance: Instance::new(), - error: "Instruction `i32-to-s8` needs one value on the stack." + error: "`i32-to-s8` needed to read `1` value(s) from the stack, but it doesn't contain enough data" ); test_executable_instruction!( diff --git a/lib/interface-types/src/interpreter/instructions/memory_to_string.rs b/lib/interface-types/src/interpreter/instructions/memory_to_string.rs index 678d08d7474..b1b9230356a 100644 --- a/lib/interface-types/src/interpreter/instructions/memory_to_string.rs +++ b/lib/interface-types/src/interpreter/instructions/memory_to_string.rs @@ -1,52 +1,69 @@ -use crate::interpreter::wasm::values::InterfaceValue; -use std::{cell::Cell, convert::TryFrom}; +use super::to_native; +use crate::{ + errors::{InstructionError, InstructionErrorKind}, + interpreter::{wasm::values::InterfaceValue, Instruction}, +}; +use std::cell::Cell; executable_instruction!( - memory_to_string(instruction_name: String) -> _ { + memory_to_string(instruction: Instruction) -> _ { move |runtime| -> _ { match runtime.stack.pop(2) { - Some(inputs) => match runtime.wasm_instance.memory(0) { - Some(memory) => { - let length = i32::try_from(&inputs[0])? as usize; - let pointer = i32::try_from(&inputs[1])? as usize; - let memory_view = memory.view(); + Some(inputs) => { + let memory_index: u32 = 0; - if memory_view.len() < pointer + length { - return Err(format!( - "`{}` failed because it has to read out of the memory bounds (index {} > memory length {}).", - instruction_name, - pointer + length, - memory_view.len() - )); - } + match runtime.wasm_instance.memory(memory_index as usize) { + Some(memory) => { + let length = to_native::(&inputs[0], instruction)? as usize; + let pointer = to_native::(&inputs[1], instruction)? as usize; + let memory_view = memory.view(); + + if memory_view.len() < pointer + length { + return Err( + InstructionError::new( + instruction, + InstructionErrorKind::MemoryOutOfBoundsAccess { + index: pointer + length, + length: memory_view.len(), + } + ), + ) + } - let data: Vec = (&memory_view[pointer..pointer + length]) - .iter() - .map(Cell::get) - .collect(); + let data: Vec = (&memory_view[pointer..pointer + length]) + .iter() + .map(Cell::get) + .collect(); - match String::from_utf8(data) { - Ok(string) => { - runtime.stack.push(InterfaceValue::String(string)); + match String::from_utf8(data) { + Ok(string) => { + runtime.stack.push(InterfaceValue::String(string)); - Ok(()) + Ok(()) + } + Err(utf8_error) => Err( + InstructionError::new( + instruction, + InstructionErrorKind::String(utf8_error) + ), + ) } - Err(utf8_error) => Err(format!( - "`{}` failed because the read string isn't UTF-8 valid ({}).", - instruction_name, - utf8_error, - )) } + None => Err( + InstructionError::new( + instruction, + InstructionErrorKind::MemoryIsMissing { memory_index } + ), + ) } - None => Err(format!( - "`{}` failed because there is no memory to read.", - instruction_name - )) } - None => Err(format!( - "`{}` failed because there is not enough data on the stack (needs 2).", - instruction_name, - )) + + None => Err( + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 2 } + ), + ) } } } @@ -91,7 +108,7 @@ mod tests { memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()), ..Default::default() }, - error: r#"`memory-to-string` failed because it has to read out of the memory bounds (index 13 > memory length 6)."#, + error: r#"`memory-to-string` read out of the memory bounds (index 13 > memory length 6)"#, ); test_executable_instruction!( @@ -111,7 +128,7 @@ mod tests { memory: Memory::new(vec![0, 159, 146, 150].iter().map(|b| Cell::new(*b)).collect::>>()), ..Default::default() }, - error: r#"`memory-to-string` failed because the read string isn't UTF-8 valid (invalid utf-8 sequence of 1 bytes from index 1)."#, + error: r#"`memory-to-string` invalid utf-8 sequence of 1 bytes from index 1"#, ); test_executable_instruction!( @@ -126,6 +143,6 @@ mod tests { InterfaceValue::I32(0), ], instance: Instance::new(), - error: r#"`memory-to-string` failed because there is not enough data on the stack (needs 2)."#, + error: r#"`memory-to-string` needed to read `2` value(s) from the stack, but it doesn't contain enough data"#, ); } diff --git a/lib/interface-types/src/interpreter/instructions/mod.rs b/lib/interface-types/src/interpreter/instructions/mod.rs index 8466b9938d4..4400cf002b4 100644 --- a/lib/interface-types/src/interpreter/instructions/mod.rs +++ b/lib/interface-types/src/interpreter/instructions/mod.rs @@ -4,12 +4,33 @@ mod lowering_lifting; mod memory_to_string; mod string_to_memory; +use crate::{ + errors::{InstructionError, InstructionErrorKind, InstructionResult, WasmValueNativeCastError}, + interpreter::{ + wasm::values::{InterfaceValue, NativeType}, + Instruction, + }, +}; pub(crate) use argument_get::argument_get; pub(crate) use call_core::call_core; pub(crate) use lowering_lifting::*; pub(crate) use memory_to_string::memory_to_string; +use std::convert::TryFrom; pub(crate) use string_to_memory::string_to_memory; +/// Just a short helper to map the error of a cast from an +/// `InterfaceValue` to a native value. +pub(crate) fn to_native<'a, T>( + wit_value: &'a InterfaceValue, + instruction: Instruction, +) -> InstructionResult +where + T: NativeType + TryFrom<&'a InterfaceValue, Error = WasmValueNativeCastError>, +{ + T::try_from(wit_value) + .map_err(|error| InstructionError::new(instruction, InstructionErrorKind::ToNative(error))) +} + #[cfg(test)] pub(crate) mod tests { use crate::interpreter::wasm::{ diff --git a/lib/interface-types/src/interpreter/instructions/string_to_memory.rs b/lib/interface-types/src/interpreter/instructions/string_to_memory.rs index 57414d03f5a..a4341a7c39a 100644 --- a/lib/interface-types/src/interpreter/instructions/string_to_memory.rs +++ b/lib/interface-types/src/interpreter/instructions/string_to_memory.rs @@ -1,60 +1,66 @@ -use crate::interpreter::wasm::{ - structures::{FunctionIndex, TypedIndex}, - values::{InterfaceType, InterfaceValue}, +use super::to_native; +use crate::{ + ast::InterfaceType, + errors::{InstructionError, InstructionErrorKind}, + interpreter::{ + wasm::{ + structures::{FunctionIndex, TypedIndex}, + values::InterfaceValue, + }, + Instruction, + }, }; -use std::convert::TryInto; executable_instruction!( - string_to_memory(allocator_index: u32, instruction_name: String) -> _ { + string_to_memory(allocator_index: u32, instruction: Instruction) -> _ { move |runtime| -> _ { let instance = &mut runtime.wasm_instance; let index = FunctionIndex::new(allocator_index as usize); let allocator = instance.local_or_import(index).ok_or_else(|| { - format!( - "`{}` failed because the function `{}` (the allocator) doesn't exist.", - instruction_name, - allocator_index + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportIsMissing { function_index: allocator_index }, ) })?; if allocator.inputs() != [InterfaceType::I32] || allocator.outputs() != [InterfaceType::I32] { - return Err(format!( - "`{}` failed because the allocator `{}` has an invalid signature (expects [I32] -> [I32]).", - instruction_name, - allocator_index, - )); + return Err(InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportSignatureMismatch { + function_index: allocator_index, + expected: (vec![InterfaceType::I32], vec![InterfaceType::I32]), + received: (allocator.inputs().to_vec(), allocator.outputs().to_vec()) + } + )) } let string = runtime.stack.pop1().ok_or_else(|| { - format!( - "`{}` cannot call the allocator `{}` because there is not enough data on the stack for the arguments (needs {}).", - instruction_name, - allocator_index, - 1 + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 1 } ) })?; - let string: String = (&string).try_into()?; + let string: String = to_native(&string, instruction)?; let string_bytes = string.as_bytes(); - let string_length = (string_bytes.len() as i32) - .try_into() - .map_err(|error| format!("{}", error))?; + let string_length = string_bytes.len() as i32; - let outputs = allocator.call(&[InterfaceValue::I32(string_length)]).map_err(|_| format!( - "`{}` failed when calling the allocator `{}`.", - instruction_name, - allocator_index, - ))?; - - let string_pointer: i32 = (&outputs[0]).try_into()?; + let outputs = allocator.call(&[InterfaceValue::I32(string_length)]).map_err(|_| { + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportCall { function_index: allocator_index }, + ) + })?; + let string_pointer: i32 = to_native(&outputs[0], instruction)?; + let memory_index: u32 = 0; let memory_view = instance - .memory(0) + .memory(memory_index as usize) .ok_or_else(|| { - format!( - "`{}` failed because there is no memory to write into.", - instruction_name + InstructionError::new( + instruction, + InstructionErrorKind::MemoryIsMissing { memory_index } ) })? .view(); @@ -106,7 +112,7 @@ mod tests { instructions: [Instruction::StringToMemory { allocator_index: 43 }], invocation_inputs: [], instance: Instance { ..Default::default() }, - error: r#"`string-to-memory 43` failed because the function `43` (the allocator) doesn't exist."#, + error: r#"`string-to-memory 43` the local or import function `43` doesn't exist"#, ); test_executable_instruction!( @@ -117,7 +123,7 @@ mod tests { ], invocation_inputs: [InterfaceValue::String("Hello, World!".into())], instance: Instance::new(), - error: r#"`string-to-memory 43` cannot call the allocator `43` because there is not enough data on the stack for the arguments (needs 1)."#, + error: r#"`string-to-memory 43` needed to read `1` value(s) from the stack, but it doesn't contain enough data"#, ); test_executable_instruction!( @@ -141,7 +147,7 @@ mod tests { instance }, - error: r#"`string-to-memory 153` failed when calling the allocator `153`."#, + error: r#"`string-to-memory 153` failed while calling the local or import function `153`"#, ); test_executable_instruction!( @@ -164,6 +170,6 @@ mod tests { instance }, - error: r#"`string-to-memory 153` failed because the allocator `153` has an invalid signature (expects [I32] -> [I32])."#, + error: r#"`string-to-memory 153` the local or import function `153` has the signature `[I32] -> [I32]` but it received values of kind `[I32, I32] -> []`"#, ); } diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index bdc939fe22e..122c482d21b 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -5,6 +5,7 @@ mod instructions; pub mod stack; pub mod wasm; +use crate::errors::{InstructionResult, InterpreterResult}; pub use instruction::Instruction; use stack::Stack; use std::{convert::TryFrom, marker::PhantomData}; @@ -38,7 +39,9 @@ where /// Type alias for an executable instruction. It's an implementation /// details, but an instruction is a boxed closure instance. pub(crate) type ExecutableInstruction = Box< - dyn Fn(&mut Runtime) -> Result<(), String>, + dyn Fn( + &mut Runtime, + ) -> InstructionResult<()>, >; /// An interpreter is the central piece of this crate. It is a set of @@ -154,7 +157,7 @@ where &self, invocation_inputs: &[InterfaceValue], wasm_instance: &mut Instance, - ) -> Result, String> { + ) -> InterpreterResult> { let mut runtime = Runtime { invocation_inputs, stack: Stack::new(), @@ -165,7 +168,7 @@ where for executable_instruction in self.iter() { match executable_instruction(&mut runtime) { Ok(_) => continue, - Err(message) => return Err(message), + Err(error) => return Err(error), } } @@ -183,62 +186,64 @@ where MemoryView: wasm::structures::MemoryView, Instance: wasm::structures::Instance, { - type Error = String; + type Error = (); fn try_from(instructions: &Vec) -> Result { let executable_instructions = instructions .iter() .map(|instruction| { - let instruction_name = instruction.to_string(); - match instruction { Instruction::ArgumentGet { index } => { - instructions::argument_get(*index, instruction_name) + instructions::argument_get(*index, *instruction) } Instruction::CallCore { function_index } => { - instructions::call_core(*function_index, instruction_name) + instructions::call_core(*function_index, *instruction) } - Instruction::MemoryToString => instructions::memory_to_string(instruction_name), + Instruction::MemoryToString => instructions::memory_to_string(*instruction), Instruction::StringToMemory { allocator_index } => { - instructions::string_to_memory(*allocator_index, instruction_name) + instructions::string_to_memory(*allocator_index, *instruction) } - Instruction::I32ToS8 => instructions::i32_to_s8(), + Instruction::I32ToS8 => instructions::i32_to_s8(*instruction), //Instruction::I32ToS8X - Instruction::I32ToU8 => instructions::i32_to_u8(), - Instruction::I32ToS16 => instructions::i32_to_s16(), + Instruction::I32ToU8 => instructions::i32_to_u8(*instruction), + Instruction::I32ToS16 => instructions::i32_to_s16(*instruction), //Instruction::I32ToS16X - Instruction::I32ToU16 => instructions::i32_to_u16(), - Instruction::I32ToS32 => instructions::i32_to_s32(), - Instruction::I32ToU32 => instructions::i32_to_u32(), - Instruction::I32ToS64 => instructions::i32_to_s64(), - Instruction::I32ToU64 => instructions::i32_to_u64(), - Instruction::I64ToS8 => instructions::i64_to_s8(), + Instruction::I32ToU16 => instructions::i32_to_u16(*instruction), + Instruction::I32ToS32 => instructions::i32_to_s32(*instruction), + Instruction::I32ToU32 => instructions::i32_to_u32(*instruction), + Instruction::I32ToS64 => instructions::i32_to_s64(*instruction), + Instruction::I32ToU64 => instructions::i32_to_u64(*instruction), + Instruction::I64ToS8 => instructions::i64_to_s8(*instruction), //Instruction::I64ToS8X - Instruction::I64ToU8 => instructions::i64_to_u8(), - Instruction::I64ToS16 => instructions::i64_to_s16(), + Instruction::I64ToU8 => instructions::i64_to_u8(*instruction), + Instruction::I64ToS16 => instructions::i64_to_s16(*instruction), //Instruction::I64ToS16X - Instruction::I64ToU16 => instructions::i64_to_u16(), - Instruction::I64ToS32 => instructions::i64_to_s32(), - Instruction::I64ToU32 => instructions::i64_to_u32(), - Instruction::I64ToS64 => instructions::i64_to_s64(), - Instruction::I64ToU64 => instructions::i64_to_u64(), - Instruction::S8ToI32 => instructions::s8_to_i32(), - Instruction::U8ToI32 => instructions::u8_to_i32(), - Instruction::S16ToI32 => instructions::s16_to_i32(), - Instruction::U16ToI32 => instructions::u16_to_i32(), - Instruction::S32ToI32 => instructions::s32_to_i32(), - Instruction::U32ToI32 => instructions::u32_to_i32(), - Instruction::S64ToI32 | Instruction::S64ToI32X => instructions::s64_to_i32(), - Instruction::U64ToI32 | Instruction::U64ToI32X => instructions::u64_to_i32(), - Instruction::S8ToI64 => instructions::s8_to_i64(), - Instruction::U8ToI64 => instructions::u8_to_i64(), - Instruction::S16ToI64 => instructions::s16_to_i64(), - Instruction::U16ToI64 => instructions::u16_to_i64(), - Instruction::S32ToI64 => instructions::s32_to_i64(), - Instruction::U32ToI64 => instructions::u32_to_i64(), - Instruction::S64ToI64 => instructions::s64_to_i64(), - Instruction::U64ToI64 => instructions::u64_to_i64(), + Instruction::I64ToU16 => instructions::i64_to_u16(*instruction), + Instruction::I64ToS32 => instructions::i64_to_s32(*instruction), + Instruction::I64ToU32 => instructions::i64_to_u32(*instruction), + Instruction::I64ToS64 => instructions::i64_to_s64(*instruction), + Instruction::I64ToU64 => instructions::i64_to_u64(*instruction), + Instruction::S8ToI32 => instructions::s8_to_i32(*instruction), + Instruction::U8ToI32 => instructions::u8_to_i32(*instruction), + Instruction::S16ToI32 => instructions::s16_to_i32(*instruction), + Instruction::U16ToI32 => instructions::u16_to_i32(*instruction), + Instruction::S32ToI32 => instructions::s32_to_i32(*instruction), + Instruction::U32ToI32 => instructions::u32_to_i32(*instruction), + Instruction::S64ToI32 | Instruction::S64ToI32X => { + instructions::s64_to_i32(*instruction) + } + Instruction::U64ToI32 | Instruction::U64ToI32X => { + instructions::u64_to_i32(*instruction) + } + Instruction::S8ToI64 => instructions::s8_to_i64(*instruction), + Instruction::U8ToI64 => instructions::u8_to_i64(*instruction), + Instruction::S16ToI64 => instructions::s16_to_i64(*instruction), + Instruction::U16ToI64 => instructions::u16_to_i64(*instruction), + Instruction::S32ToI64 => instructions::s32_to_i64(*instruction), + Instruction::U32ToI64 => instructions::u32_to_i64(*instruction), + Instruction::S64ToI64 => instructions::s64_to_i64(*instruction), + Instruction::U64ToI64 => instructions::u64_to_i64(*instruction), _ => unimplemented!(), } }) diff --git a/lib/interface-types/src/interpreter/wasm/values.rs b/lib/interface-types/src/interpreter/wasm/values.rs index 1e0976cfe42..484fa1dae58 100644 --- a/lib/interface-types/src/interpreter/wasm/values.rs +++ b/lib/interface-types/src/interpreter/wasm/values.rs @@ -1,8 +1,8 @@ #![allow(missing_docs)] -use std::convert::TryFrom; - pub use crate::ast::InterfaceType; +use crate::errors::WasmValueNativeCastError; +use std::convert::TryFrom; #[derive(Debug, Clone, PartialEq)] pub enum InterfaceValue { @@ -49,35 +49,46 @@ impl Default for InterfaceValue { } } -macro_rules! from_x_for_interface_value { - ($native_type:ty, $value_variant:ident) => { +pub trait NativeType { + const INTERFACE_TYPE: InterfaceType; +} + +macro_rules! native { + ($native_type:ty, $variant:ident) => { + impl NativeType for $native_type { + const INTERFACE_TYPE: InterfaceType = InterfaceType::$variant; + } + impl From<$native_type> for InterfaceValue { fn from(n: $native_type) -> Self { - Self::$value_variant(n) + Self::$variant(n) } } impl TryFrom<&InterfaceValue> for $native_type { - type Error = &'static str; + type Error = WasmValueNativeCastError; fn try_from(w: &InterfaceValue) -> Result { match w { - InterfaceValue::$value_variant(n) => Ok(n.clone()), - _ => Err("Invalid cast."), + InterfaceValue::$variant(n) => Ok(n.clone()), + _ => Err(WasmValueNativeCastError { + from: w.into(), + to: <$native_type>::INTERFACE_TYPE, + }), } } } }; } -from_x_for_interface_value!(i8, S8); -from_x_for_interface_value!(i16, S16); -from_x_for_interface_value!(u8, U8); -from_x_for_interface_value!(u16, U16); -from_x_for_interface_value!(u32, U32); -from_x_for_interface_value!(u64, U64); -from_x_for_interface_value!(f32, F32); -from_x_for_interface_value!(f64, F64); -from_x_for_interface_value!(String, String); -from_x_for_interface_value!(i32, I32); -from_x_for_interface_value!(i64, I64); +native!(i8, S8); +native!(i16, S16); +native!(u8, U8); +native!(u16, U16); +native!(u32, U32); +native!(u64, U64); +native!(f32, F32); +native!(f64, F64); +native!(String, String); +native!(i32, I32); +native!(i64, I64); diff --git a/lib/interface-types/src/lib.rs b/lib/interface-types/src/lib.rs index 95545943800..9e88cac281b 100644 --- a/lib/interface-types/src/lib.rs +++ b/lib/interface-types/src/lib.rs @@ -55,4 +55,5 @@ pub mod ast; mod macros; pub mod decoders; pub mod encoders; +pub mod errors; pub mod interpreter; diff --git a/lib/interface-types/src/macros.rs b/lib/interface-types/src/macros.rs index 5e2217e29e0..b2e2cfa807d 100644 --- a/lib/interface-types/src/macros.rs +++ b/lib/interface-types/src/macros.rs @@ -122,7 +122,7 @@ macro_rules! test_executable_instruction { assert!(run.is_err()); - let error = run.unwrap_err(); + let error = run.unwrap_err().to_string(); assert_eq!(error, String::from($error)); } From 7d6bc577b724a2055a5b4b07df96dfd7f5be813f Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 10 Mar 2020 15:53:46 +0100 Subject: [PATCH 2/6] feat(interface-types) Reformat the instructions. --- .../src/interpreter/instructions/call_core.rs | 93 +++++++++---------- .../instructions/lowering_lifting.rs | 31 +++---- .../instructions/memory_to_string.rs | 89 ++++++++---------- 3 files changed, 96 insertions(+), 117 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/call_core.rs b/lib/interface-types/src/interpreter/instructions/call_core.rs index dfaf520e463..77486498f6c 100644 --- a/lib/interface-types/src/interpreter/instructions/call_core.rs +++ b/lib/interface-types/src/interpreter/instructions/call_core.rs @@ -13,61 +13,54 @@ executable_instruction!( let instance = &mut runtime.wasm_instance; let index = FunctionIndex::new(function_index); - match instance.local_or_import(index) { - Some(local_or_import) => { - let inputs_cardinality = local_or_import.inputs_cardinality(); - - match runtime.stack.pop(inputs_cardinality) { - Some(inputs) => { - let input_types = inputs - .iter() - .map(Into::into) - .collect::>(); + let local_or_import = instance.local_or_import(index).ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportIsMissing { + function_index: function_index as u32, + }, + ) + })?; + let inputs_cardinality = local_or_import.inputs_cardinality(); - if input_types != local_or_import.inputs() { - return Err( - InstructionError::new( - instruction, - InstructionErrorKind::LocalOrImportSignatureMismatch { - function_index: function_index as u32, - expected: (local_or_import.inputs().to_vec(), vec![]), - received: (input_types, vec![]), - } - ) - ) - } + let inputs = runtime.stack.pop(inputs_cardinality).ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { + needed: inputs_cardinality, + }, + ) + })?; + let input_types = inputs + .iter() + .map(Into::into) + .collect::>(); - match local_or_import.call(&inputs) { - Ok(outputs) => { - for output in outputs.iter() { - runtime.stack.push(output.clone()); - } + if input_types != local_or_import.inputs() { + return Err(InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportSignatureMismatch { + function_index: function_index as u32, + expected: (local_or_import.inputs().to_vec(), vec![]), + received: (input_types, vec![]), + }, + )); + } - Ok(()) - } - Err(_) => Err( - InstructionError::new( - instruction, - InstructionErrorKind::LocalOrImportCall { function_index: function_index as u32, }, - ) - ) - } - } - None => Err( - InstructionError::new( - instruction, - InstructionErrorKind::StackIsTooSmall { needed: inputs_cardinality }, - ) - ) - } - } - None => Err( - InstructionError::new( - instruction, - InstructionErrorKind::LocalOrImportIsMissing { function_index: function_index as u32, }, - ) + let outputs = local_or_import.call(&inputs).map_err(|_| { + InstructionError::new( + instruction, + InstructionErrorKind::LocalOrImportCall { + function_index: function_index as u32, + }, ) + })?; + + for output in outputs.iter() { + runtime.stack.push(output.clone()); } + + Ok(()) } } ); diff --git a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs index f18c24385f6..d3903e6604a 100644 --- a/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs +++ b/lib/interface-types/src/interpreter/instructions/lowering_lifting.rs @@ -18,31 +18,30 @@ macro_rules! lowering_lifting { |_| { InstructionError::new( instruction, - InstructionErrorKind::LoweringLifting { from: InterfaceType::$from_variant, to: InterfaceType::$to_variant }, + InstructionErrorKind::LoweringLifting { + from: InterfaceType::$from_variant, + to: InterfaceType::$to_variant + }, ) }, )?)) } Some(wrong_value) => { - return Err( - InstructionError::new( - instruction, - InstructionErrorKind::InvalidValueOnTheStack { - expected_type: InterfaceType::$from_variant, - received_type: (&wrong_value).into(), - } - ) - ) + return Err(InstructionError::new( + instruction, + InstructionErrorKind::InvalidValueOnTheStack { + expected_type: InterfaceType::$from_variant, + received_type: (&wrong_value).into(), + } + )) }, None => { - return Err( - InstructionError::new( - instruction, - InstructionErrorKind::StackIsTooSmall { needed: 1 }, - ) - ) + return Err(InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 1 }, + )) } } diff --git a/lib/interface-types/src/interpreter/instructions/memory_to_string.rs b/lib/interface-types/src/interpreter/instructions/memory_to_string.rs index b1b9230356a..f96c47f22f6 100644 --- a/lib/interface-types/src/interpreter/instructions/memory_to_string.rs +++ b/lib/interface-types/src/interpreter/instructions/memory_to_string.rs @@ -8,63 +8,50 @@ use std::cell::Cell; executable_instruction!( memory_to_string(instruction: Instruction) -> _ { move |runtime| -> _ { - match runtime.stack.pop(2) { - Some(inputs) => { - let memory_index: u32 = 0; + let inputs = runtime.stack.pop(2).ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::StackIsTooSmall { needed: 2 }, + ) + })?; - match runtime.wasm_instance.memory(memory_index as usize) { - Some(memory) => { - let length = to_native::(&inputs[0], instruction)? as usize; - let pointer = to_native::(&inputs[1], instruction)? as usize; - let memory_view = memory.view(); + let memory_index: u32 = 0; - if memory_view.len() < pointer + length { - return Err( - InstructionError::new( - instruction, - InstructionErrorKind::MemoryOutOfBoundsAccess { - index: pointer + length, - length: memory_view.len(), - } - ), - ) - } + let memory = runtime + .wasm_instance + .memory(memory_index as usize) + .ok_or_else(|| { + InstructionError::new( + instruction, + InstructionErrorKind::MemoryIsMissing { memory_index }, + ) + })?; - let data: Vec = (&memory_view[pointer..pointer + length]) - .iter() - .map(Cell::get) - .collect(); + let length = to_native::(&inputs[0], instruction)? as usize; + let pointer = to_native::(&inputs[1], instruction)? as usize; + let memory_view = memory.view(); - match String::from_utf8(data) { - Ok(string) => { - runtime.stack.push(InterfaceValue::String(string)); + if memory_view.len() < pointer + length { + return Err(InstructionError::new( + instruction, + InstructionErrorKind::MemoryOutOfBoundsAccess { + index: pointer + length, + length: memory_view.len(), + }, + )); + } - Ok(()) - } - Err(utf8_error) => Err( - InstructionError::new( - instruction, - InstructionErrorKind::String(utf8_error) - ), - ) - } - } - None => Err( - InstructionError::new( - instruction, - InstructionErrorKind::MemoryIsMissing { memory_index } - ), - ) - } - } + let data: Vec = (&memory_view[pointer..pointer + length]) + .iter() + .map(Cell::get) + .collect(); - None => Err( - InstructionError::new( - instruction, - InstructionErrorKind::StackIsTooSmall { needed: 2 } - ), - ) - } + let string = String::from_utf8(data) + .map_err(|error| InstructionError::new(instruction, InstructionErrorKind::String(error)))?; + + runtime.stack.push(InterfaceValue::String(string)); + + Ok(()) } } ); From 4ffb158f1435bb21a058a3c10c21a815dec3c4c1 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Tue, 10 Mar 2020 16:07:07 +0100 Subject: [PATCH 3/6] doc(changelog) Add #1285. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f90c879971a..4a8de268716 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## **[Unreleased]** +- [#1285](https://github.com/wasmerio/wasmer/pull/1285) Greatly improve errors in `wasmer-interface-types` - [#1284](https://github.com/wasmerio/wasmer/pull/1284) Implement string and memory instructions in `wasmer-interface-types` - [#1272](https://github.com/wasmerio/wasmer/pull/1272) Fix off-by-one error bug when accessing memory with a `WasmPtr` that contains the last valid byte of memory. Also changes the behavior of `WasmPtr` with a length of 0 and `WasmPtr` where `std::mem::size_of::()` is 0 to always return `None` From 64729aa8f4e920a96256566ec2658d8fe0f2db61 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 12 Mar 2020 14:39:15 +0100 Subject: [PATCH 4/6] feat(interface-types) Use include ranges to read the memory. --- .../instructions/memory_to_string.rs | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/interface-types/src/interpreter/instructions/memory_to_string.rs b/lib/interface-types/src/interpreter/instructions/memory_to_string.rs index f96c47f22f6..e30b70ce0b1 100644 --- a/lib/interface-types/src/interpreter/instructions/memory_to_string.rs +++ b/lib/interface-types/src/interpreter/instructions/memory_to_string.rs @@ -31,7 +31,13 @@ executable_instruction!( let pointer = to_native::(&inputs[1], instruction)? as usize; let memory_view = memory.view(); - if memory_view.len() < pointer + length { + if length == 0 { + runtime.stack.push(InterfaceValue::String("".into())); + + return Ok(()) + } + + if memory_view.len() <= pointer + length - 1 { return Err(InstructionError::new( instruction, InstructionErrorKind::MemoryOutOfBoundsAccess { @@ -41,7 +47,7 @@ executable_instruction!( )); } - let data: Vec = (&memory_view[pointer..pointer + length]) + let data: Vec = (&memory_view[pointer..=pointer + length - 1]) .iter() .map(Cell::get) .collect(); @@ -78,6 +84,24 @@ mod tests { stack: [InterfaceValue::String("Hello, World!".into())], ); + test_executable_instruction!( + test_memory_to_string__empty_string = + instructions: [ + Instruction::ArgumentGet { index: 1 }, + Instruction::ArgumentGet { index: 0 }, + Instruction::MemoryToString, + ], + invocation_inputs: [ + InterfaceValue::I32(0), + InterfaceValue::I32(0), + ], + instance: Instance { + memory: Memory::new(vec![]), + ..Default::default() + }, + stack: [InterfaceValue::String("".into())], + ); + test_executable_instruction!( test_memory_to_string__read_out_of_memory = instructions: [ From f46099c67b7ab3e531096154167f989e857d492d Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 12 Mar 2020 14:49:43 +0100 Subject: [PATCH 5/6] feat(interface-types) Implement `Error` on errors. --- lib/interface-types/src/errors.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/interface-types/src/errors.rs b/lib/interface-types/src/errors.rs index 24ca5566a50..5df5c49e391 100644 --- a/lib/interface-types/src/errors.rs +++ b/lib/interface-types/src/errors.rs @@ -3,6 +3,7 @@ use crate::{ast::InterfaceType, interpreter::Instruction}; use std::{ + error::Error, fmt::{self, Display, Formatter}, result::Result, string::{self, ToString}, @@ -28,6 +29,14 @@ pub struct WasmValueNativeCastError { 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 { @@ -47,6 +56,8 @@ impl InstructionError { } } +impl Error for InstructionError {} + impl Display for InstructionError { fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { write!( @@ -140,6 +151,8 @@ pub enum InstructionErrorKind { String(string::FromUtf8Error), } +impl Error for InstructionErrorKind {} + impl Display for InstructionErrorKind { fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { match self { From b78a6f47abac055e32a00b54e8ba427062c66e9b Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 12 Mar 2020 14:51:18 +0100 Subject: [PATCH 6/6] chore(interface-types) Simplify code. --- lib/interface-types/src/interpreter/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/interface-types/src/interpreter/mod.rs b/lib/interface-types/src/interpreter/mod.rs index 122c482d21b..d5f01056b16 100644 --- a/lib/interface-types/src/interpreter/mod.rs +++ b/lib/interface-types/src/interpreter/mod.rs @@ -166,10 +166,7 @@ where }; for executable_instruction in self.iter() { - match executable_instruction(&mut runtime) { - Ok(_) => continue, - Err(error) => return Err(error), - } + executable_instruction(&mut runtime)?; } Ok(runtime.stack)