From 367f17989bec021d779e3338ec635ceedfaedb05 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 6 Feb 2018 14:14:57 +0300 Subject: [PATCH] Refine errors (#36) * 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 for Trap * Remove redundant clone in prepare_function_args. * Use .into() to convert TrapKind into Trap --- src/func.rs | 23 ++- src/host.rs | 18 +-- src/lib.rs | 75 +++++++--- src/runner.rs | 367 ++++++++++++++++++++++------------------------ src/tests/host.rs | 15 +- src/value.rs | 65 ++++---- 6 files changed, 300 insertions(+), 263 deletions(-) diff --git a/src/func.rs b/src/func.rs index 7662e85..b72107d 100644 --- a/src/func.rs +++ b/src/func.rs @@ -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; @@ -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, @@ -128,20 +127,30 @@ impl FuncInstance { } } - pub(crate) fn invoke( + /// 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( func: &FuncRef, args: &[RuntimeValue], externals: &mut E, ) -> Result, 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()), } } } diff --git a/src/host.rs b/src/host.rs index 6e2385a..a15fd9d 100644 --- a/src/host.rs +++ b/src/host.rs @@ -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)] @@ -18,8 +18,8 @@ impl<'a> RuntimeArgs<'a> { /// # Errors /// /// Returns `Err` if cast is invalid or not enough arguments. - pub fn nth_checked(&self, idx: usize) -> Result where RuntimeValue: TryInto { - Ok(self.nth_value_checked(idx)?.try_into().map_err(|_| Error::Value("Invalid argument cast".to_owned()))?) + pub fn nth_checked(&self, idx: usize) -> Result where RuntimeValue: TryInto { + Ok(self.nth_value_checked(idx)?.try_into().map_err(|_| TrapKind::UnexpectedSignature)?) } /// Extract argument as a [`RuntimeValue`] by index `idx`. @@ -27,9 +27,9 @@ impl<'a> RuntimeArgs<'a> { /// # Errors /// /// Returns `Err` if this list has not enough arguments. - pub fn nth_value_checked(&self, idx: usize) -> Result { + pub fn nth_value_checked(&self, idx: usize) -> Result { if self.0.len() <= idx { - return Err(Error::Value("Invalid argument index".to_owned())); + return Err(TrapKind::UnexpectedSignature.into()); } Ok(self.0[idx]) } @@ -39,7 +39,7 @@ impl<'a> RuntimeArgs<'a> { /// # Panics /// /// Panics if cast is invalid or not enough arguments. - pub fn nth(&self, idx: usize) -> T where RuntimeValue: TryInto { + pub fn nth(&self, idx: usize) -> T where RuntimeValue: TryInto { let value = self.nth_value_checked(idx).expect("Invalid argument index"); value.try_into().expect("Unexpected argument type") } @@ -139,8 +139,8 @@ impl HostError { /// ) -> Result, 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))) @@ -207,7 +207,7 @@ impl Externals for NopExternals { _index: usize, _args: RuntimeArgs, ) -> Result, Trap> { - Err(Trap::Unreachable) + Err(TrapKind::Unreachable.into()) } } diff --git a/src/lib.rs b/src/lib.rs index 9802ff9..7d69caf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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. @@ -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`]. @@ -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. @@ -203,6 +229,27 @@ pub enum Error { Host(Box), } +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 for Error { fn into(self) -> String { match self { @@ -212,7 +259,6 @@ impl Into 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), @@ -229,7 +275,6 @@ 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), @@ -237,8 +282,6 @@ impl fmt::Display for Error { } } - - impl error::Error for Error { fn description(&self) -> &str { match *self { @@ -248,7 +291,6 @@ 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", @@ -256,7 +298,6 @@ impl error::Error for Error { } } - impl From for Error where U: host::HostError + Sized { fn from(e: U) -> Self { Error::Host(Box::new(e)) @@ -265,7 +306,7 @@ impl From for Error where U: host::HostError + Sized { impl From for Trap where U: host::HostError + Sized { fn from(e: U) -> Self { - Trap::Host(Box::new(e)) + Trap::new(TrapKind::Host(Box::new(e))) } } @@ -275,15 +316,15 @@ impl From for Error { } } -impl From for Error { - fn from(e: validation::Error) -> Error { - Error::Validation(e.to_string()) +impl From 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 for Error { + fn from(e: validation::Error) -> Error { + Error::Validation(e.to_string()) } } diff --git a/src/runner.rs b/src/runner.rs index 18cb17e..127d976 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Display}; use std::iter::repeat; use std::collections::{HashMap, VecDeque}; use parity_wasm::elements::{Opcode, BlockType, Local}; -use {Error, Trap, Signature}; +use {Error, Trap, TrapKind, Signature}; use module::ModuleRef; use func::{FuncRef, FuncInstance, FuncInstanceInternal}; use value::{ @@ -78,16 +78,16 @@ impl<'a, E: Externals> Interpreter<'a, E> { if !function_context.is_initialized() { let return_type = function_context.return_type; function_context.initialize(&function_body.locals); - function_context.push_frame(&function_body.labels, BlockFrameType::Function, return_type)?; + function_context.push_frame(&function_body.labels, BlockFrameType::Function, return_type).map_err(Trap::new)?; } - let function_return = self.do_run_function(&mut function_context, function_body.opcodes.elements(), &function_body.labels)?; + let function_return = self.do_run_function(&mut function_context, function_body.opcodes.elements(), &function_body.labels).map_err(Trap::new)?; match function_return { RunResult::Return(return_value) => { match function_stack.back_mut() { Some(caller_context) => if let Some(return_value) = return_value { - caller_context.value_stack_mut().push(return_value)?; + caller_context.value_stack_mut().push(return_value).map_err(Trap::new)?; }, None => return Ok(return_value), } @@ -95,7 +95,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { RunResult::NestedCall(nested_func) => { match *nested_func.as_internal() { FuncInstanceInternal::Internal { .. } => { - let nested_context = function_context.nested(nested_func.clone())?; + let nested_context = function_context.nested(nested_func.clone()).map_err(Trap::new)?; function_stack.push_back(function_context); function_stack.push_back(nested_context); }, @@ -103,7 +103,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { let args = prepare_function_args(signature, &mut function_context.value_stack); let return_val = FuncInstance::invoke(&nested_func, &args, self.externals)?; if let Some(return_val) = return_val { - function_context.value_stack_mut().push(return_val)?; + function_context.value_stack_mut().push(return_val).map_err(Trap::new)?; } function_stack.push_back(function_context); } @@ -113,7 +113,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { } } - fn do_run_function(&mut self, function_context: &mut FunctionContext, function_body: &[Opcode], function_labels: &HashMap) -> Result { + fn do_run_function(&mut self, function_context: &mut FunctionContext, function_body: &[Opcode], function_labels: &HashMap) -> Result { loop { let instruction = &function_body[function_context.position]; @@ -148,15 +148,14 @@ impl<'a, E: Externals> Interpreter<'a, E> { BlockType::Value(_) => { let result = function_context .value_stack_mut() - .pop() - .expect("Due to validation stack shouldn't be empty"); + .pop(); Some(result) }, BlockType::NoResult => None, })) } - fn run_instruction(&mut self, context: &mut FunctionContext, labels: &HashMap, opcode: &Opcode) -> Result { + fn run_instruction(&mut self, context: &mut FunctionContext, labels: &HashMap, opcode: &Opcode) -> Result { match opcode { &Opcode::Unreachable => self.run_unreachable(context), &Opcode::Nop => self.run_nop(context), @@ -350,29 +349,28 @@ impl<'a, E: Externals> Interpreter<'a, E> { } } - fn run_unreachable(&mut self, _context: &mut FunctionContext) -> Result { - Err(Trap::Unreachable) + fn run_unreachable(&mut self, _context: &mut FunctionContext) -> Result { + Err(TrapKind::Unreachable) } - fn run_nop(&mut self, _context: &mut FunctionContext) -> Result { + fn run_nop(&mut self, _context: &mut FunctionContext) -> Result { Ok(InstructionOutcome::RunNextInstruction) } - fn run_block(&mut self, context: &mut FunctionContext, labels: &HashMap, block_type: BlockType) -> Result { + fn run_block(&mut self, context: &mut FunctionContext, labels: &HashMap, block_type: BlockType) -> Result { context.push_frame(labels, BlockFrameType::Block, block_type)?; Ok(InstructionOutcome::RunNextInstruction) } - fn run_loop(&mut self, context: &mut FunctionContext, labels: &HashMap, block_type: BlockType) -> Result { + fn run_loop(&mut self, context: &mut FunctionContext, labels: &HashMap, block_type: BlockType) -> Result { context.push_frame(labels, BlockFrameType::Loop, block_type)?; Ok(InstructionOutcome::RunNextInstruction) } - fn run_if(&mut self, context: &mut FunctionContext, labels: &HashMap, block_type: BlockType) -> Result { + fn run_if(&mut self, context: &mut FunctionContext, labels: &HashMap, block_type: BlockType) -> Result { let condition: bool = context .value_stack_mut() - .pop_as() - .expect("Due to validation stack top should be I32"); + .pop_as(); let block_frame_type = if condition { BlockFrameType::IfTrue } else { let else_pos = labels[&context.position]; if !labels.contains_key(&else_pos) { @@ -388,26 +386,24 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_else(&mut self, context: &mut FunctionContext, labels: &HashMap) -> Result { + fn run_else(&mut self, context: &mut FunctionContext, labels: &HashMap) -> Result { let end_pos = labels[&context.position]; context.pop_frame(false)?; context.position = end_pos; Ok(InstructionOutcome::RunNextInstruction) } - fn run_end(&mut self, context: &mut FunctionContext) -> Result { + fn run_end(&mut self, context: &mut FunctionContext) -> Result { context.pop_frame(false)?; Ok(InstructionOutcome::End) } - fn run_br(&mut self, _context: &mut FunctionContext, label_idx: u32) -> Result { + fn run_br(&mut self, _context: &mut FunctionContext, label_idx: u32) -> Result { Ok(InstructionOutcome::Branch(label_idx as usize)) } - fn run_br_if(&mut self, context: &mut FunctionContext, label_idx: u32) -> Result { - let condition = context.value_stack_mut() - .pop_as() - .expect("Due to validation stack should contain value"); + fn run_br_if(&mut self, context: &mut FunctionContext, label_idx: u32) -> Result { + let condition = context.value_stack_mut().pop_as(); if condition { Ok(InstructionOutcome::Branch(label_idx as usize)) } else { @@ -415,14 +411,13 @@ impl<'a, E: Externals> Interpreter<'a, E> { } } - fn run_br_table(&mut self, context: &mut FunctionContext, table: &[u32], default: u32) -> Result { + fn run_br_table(&mut self, context: &mut FunctionContext, table: &[u32], default: u32) -> Result { let index: u32 = context.value_stack_mut() - .pop_as() - .expect("Due to validation stack should contain value"); + .pop_as(); Ok(InstructionOutcome::Branch(table.get(index as usize).cloned().unwrap_or(default) as usize)) } - fn run_return(&mut self, _context: &mut FunctionContext) -> Result { + fn run_return(&mut self, _context: &mut FunctionContext) -> Result { Ok(InstructionOutcome::Return) } @@ -430,7 +425,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { &mut self, context: &mut FunctionContext, func_idx: u32, - ) -> Result { + ) -> Result { let func = context .module() .func_by_index(func_idx) @@ -442,18 +437,17 @@ impl<'a, E: Externals> Interpreter<'a, E> { &mut self, context: &mut FunctionContext, signature_idx: u32, - ) -> Result { + ) -> Result { let table_func_idx: u32 = context .value_stack_mut() - .pop_as() - .expect("Due to validation stack top should be I32"); + .pop_as(); let table = context .module() .table_by_index(DEFAULT_TABLE_INDEX) .expect("Due to validation table should exists"); let func_ref = table.get(table_func_idx) - .map_err(|_| Trap::TableAccessOutOfBounds)? - .ok_or_else(|| Trap::ElemUninitialized)?; + .map_err(|_| TrapKind::TableAccessOutOfBounds)? + .ok_or_else(|| TrapKind::ElemUninitialized)?; { let actual_function_type = func_ref.signature(); @@ -463,25 +457,24 @@ impl<'a, E: Externals> Interpreter<'a, E> { .expect("Due to validation type should exists"); if &*required_function_type != actual_function_type { - return Err(Trap::ElemSignatureMismatch); + return Err(TrapKind::ElemSignatureMismatch); } } Ok(InstructionOutcome::ExecuteCall(func_ref)) } - fn run_drop(&mut self, context: &mut FunctionContext) -> Result { + fn run_drop(&mut self, context: &mut FunctionContext) -> Result { let _ = context .value_stack_mut() .pop(); Ok(InstructionOutcome::RunNextInstruction) } - fn run_select(&mut self, context: &mut FunctionContext) -> Result { + fn run_select(&mut self, context: &mut FunctionContext) -> Result { let (left, mid, right) = context .value_stack_mut() - .pop_triple() - .expect("Due to validation there should be 3 values on stack"); + .pop_triple(); let condition = right .try_into() @@ -491,26 +484,24 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_get_local(&mut self, context: &mut FunctionContext, index: u32) -> Result { + fn run_get_local(&mut self, context: &mut FunctionContext, index: u32) -> Result { let val = context.get_local(index as usize); context.value_stack_mut().push(val)?; Ok(InstructionOutcome::RunNextInstruction) } - fn run_set_local(&mut self, context: &mut FunctionContext, index: u32) -> Result { + fn run_set_local(&mut self, context: &mut FunctionContext, index: u32) -> Result { let arg = context .value_stack_mut() - .pop() - .expect("Due to vaidation stack should contain value"); + .pop(); context.set_local(index as usize, arg); Ok(InstructionOutcome::RunNextInstruction) } - fn run_tee_local(&mut self, context: &mut FunctionContext, index: u32) -> Result { + fn run_tee_local(&mut self, context: &mut FunctionContext, index: u32) -> Result { let arg = context .value_stack() .top() - .expect("Due to vaidation stack should contain value") .clone(); context.set_local(index as usize, arg); Ok(InstructionOutcome::RunNextInstruction) @@ -520,7 +511,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { &mut self, context: &mut FunctionContext, index: u32, - ) -> Result { + ) -> Result { let global = context .module() .global_by_index(index) @@ -534,11 +525,10 @@ impl<'a, E: Externals> Interpreter<'a, E> { &mut self, context: &mut FunctionContext, index: u32, - ) -> Result { + ) -> Result { let val = context .value_stack_mut() - .pop() - .expect("Due to vaidation stack should contain value"); + .pop(); let global = context .module() .global_by_index(index) @@ -547,12 +537,11 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_load(&mut self, context: &mut FunctionContext, _align: u32, offset: u32) -> Result + fn run_load(&mut self, context: &mut FunctionContext, _align: u32, offset: u32) -> Result where RuntimeValue: From, T: LittleEndianConvert { let raw_address = context .value_stack_mut() - .pop_as() - .expect("Due to vaidation stack should contain value"); + .pop_as(); let address = effective_address( offset, @@ -562,19 +551,18 @@ impl<'a, E: Externals> Interpreter<'a, E> { .memory_by_index(DEFAULT_MEMORY_INDEX) .expect("Due to validation memory should exists"); let b = m.get(address, mem::size_of::()) - .map_err(|_| Trap::MemoryAccessOutOfBounds)?; + .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; let n = T::from_little_endian(&b) .expect("Can't fail since buffer length should be size_of::"); context.value_stack_mut().push(n.into())?; Ok(InstructionOutcome::RunNextInstruction) } - fn run_load_extend(&mut self, context: &mut FunctionContext, _align: u32, offset: u32) -> Result + fn run_load_extend(&mut self, context: &mut FunctionContext, _align: u32, offset: u32) -> Result where T: ExtendInto, RuntimeValue: From, T: LittleEndianConvert { let raw_address = context .value_stack_mut() - .pop_as() - .expect("Due to vaidation stack should contain value"); + .pop_as(); let address = effective_address( offset, @@ -584,7 +572,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { .memory_by_index(DEFAULT_MEMORY_INDEX) .expect("Due to validation memory should exists"); let b = m.get(address, mem::size_of::()) - .map_err(|_| Trap::MemoryAccessOutOfBounds)?; + .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; let v = T::from_little_endian(&b) .expect("Can't fail since buffer length should be size_of::"); let stack_value: U = v.extend_into(); @@ -595,17 +583,15 @@ impl<'a, E: Externals> Interpreter<'a, E> { .map(|_| InstructionOutcome::RunNextInstruction) } - fn run_store(&mut self, context: &mut FunctionContext, _align: u32, offset: u32) -> Result - where RuntimeValue: TryInto, T: LittleEndianConvert { + fn run_store(&mut self, context: &mut FunctionContext, _align: u32, offset: u32) -> Result + where RuntimeValue: TryInto, T: LittleEndianConvert { let stack_value = context .value_stack_mut() .pop_as::() - .expect("Due to vaidation stack should contain value") .into_little_endian(); let raw_address = context .value_stack_mut() - .pop_as::() - .expect("Due to validation stack should contain value"); + .pop_as::(); let address = effective_address( offset, @@ -616,7 +602,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { .memory_by_index(DEFAULT_MEMORY_INDEX) .expect("Due to validation memory should exists"); m.set(address, &stack_value) - .map_err(|_| Trap::MemoryAccessOutOfBounds)?; + .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; Ok(InstructionOutcome::RunNextInstruction) } @@ -625,23 +611,21 @@ impl<'a, E: Externals> Interpreter<'a, E> { context: &mut FunctionContext, _align: u32, offset: u32, - ) -> Result + ) -> Result where - RuntimeValue: TryInto, + RuntimeValue: TryInto, T: WrapInto, U: LittleEndianConvert, { let stack_value: T = context .value_stack_mut() .pop() - .expect("Due to vaidation stack should contain value") .try_into() .expect("Due to validation value should be of proper type"); let stack_value = stack_value.wrap_into().into_little_endian(); let raw_address = context .value_stack_mut() - .pop_as::() - .expect("Due to validation stack should contain value"); + .pop_as::(); let address = effective_address( offset, @@ -651,11 +635,11 @@ impl<'a, E: Externals> Interpreter<'a, E> { .memory_by_index(DEFAULT_MEMORY_INDEX) .expect("Due to validation memory should exists"); m.set(address, &stack_value) - .map_err(|_| Trap::MemoryAccessOutOfBounds)?; + .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; Ok(InstructionOutcome::RunNextInstruction) } - fn run_current_memory(&mut self, context: &mut FunctionContext) -> Result { + fn run_current_memory(&mut self, context: &mut FunctionContext) -> Result { let m = context.module() .memory_by_index(DEFAULT_MEMORY_INDEX) .expect("Due to validation memory should exists"); @@ -666,11 +650,10 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_grow_memory(&mut self, context: &mut FunctionContext) -> Result { + fn run_grow_memory(&mut self, context: &mut FunctionContext) -> Result { let pages: u32 = context .value_stack_mut() - .pop_as() - .expect("Due to validation stack should contain value"); + .pop_as(); let m = context.module() .memory_by_index(DEFAULT_MEMORY_INDEX) .expect("Due to validation memory should exists"); @@ -683,7 +666,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_const(&mut self, context: &mut FunctionContext, val: RuntimeValue) -> Result { + fn run_const(&mut self, context: &mut FunctionContext, val: RuntimeValue) -> Result { context .value_stack_mut() .push(val) @@ -691,9 +674,9 @@ impl<'a, E: Externals> Interpreter<'a, E> { .map(|_| InstructionOutcome::RunNextInstruction) } - fn run_relop(&mut self, context: &mut FunctionContext, f: F) -> Result + fn run_relop(&mut self, context: &mut FunctionContext, f: F) -> Result where - RuntimeValue: TryInto, + RuntimeValue: TryInto, F: FnOnce(T, T) -> bool, { let (left, right) = context @@ -709,79 +692,77 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_eqz(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: TryInto, T: PartialEq + Default { + fn run_eqz(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: TryInto, T: PartialEq + Default { let v = context .value_stack_mut() - .pop_as::() - .expect("Due to vaidation stack should contain value"); + .pop_as::(); let v = RuntimeValue::I32(if v == Default::default() { 1 } else { 0 }); context.value_stack_mut().push(v)?; Ok(InstructionOutcome::RunNextInstruction) } - fn run_eq(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: TryInto, T: PartialEq + fn run_eq(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: TryInto, T: PartialEq { self.run_relop(context, |left, right| left == right) } - fn run_ne(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: TryInto, T: PartialEq { + fn run_ne(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: TryInto, T: PartialEq { self.run_relop(context, |left, right| left != right) } - fn run_lt(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: TryInto, T: PartialOrd + Display { + fn run_lt(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: TryInto, T: PartialOrd + Display { self.run_relop(context, |left, right| left < right) } - fn run_gt(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: TryInto, T: PartialOrd { + fn run_gt(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: TryInto, T: PartialOrd { self.run_relop(context, |left, right| left > right) } - fn run_lte(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: TryInto, T: PartialOrd { + fn run_lte(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: TryInto, T: PartialOrd { self.run_relop(context, |left, right| left <= right) } - fn run_gte(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: TryInto, T: PartialOrd { + fn run_gte(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: TryInto, T: PartialOrd { self.run_relop(context, |left, right| left >= right) } - fn run_unop(&mut self, context: &mut FunctionContext, f: F) -> Result + fn run_unop(&mut self, context: &mut FunctionContext, f: F) -> Result where F: FnOnce(T) -> U, - RuntimeValue: From + TryInto + RuntimeValue: From + TryInto { let v = context .value_stack_mut() - .pop_as::() - .map(|v| f(v)) - .expect("Due to vaidation stack should contain value"); + .pop_as::(); + let v = f(v); context.value_stack_mut().push(v.into())?; Ok(InstructionOutcome::RunNextInstruction) } - fn run_clz(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Integer { + fn run_clz(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Integer { self.run_unop(context, |v| v.leading_zeros()) } - fn run_ctz(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Integer { + fn run_ctz(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Integer { self.run_unop(context, |v| v.trailing_zeros()) } - fn run_popcnt(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Integer { + fn run_popcnt(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Integer { self.run_unop(context, |v| v.count_ones()) } - fn run_add(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: ArithmeticOps { + fn run_add(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: ArithmeticOps { let (left, right) = context .value_stack_mut() .pop_pair_as::() @@ -791,8 +772,8 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_sub(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: ArithmeticOps { + fn run_sub(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: ArithmeticOps { let (left, right) = context .value_stack_mut() .pop_pair_as::() @@ -802,8 +783,8 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_mul(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: ArithmeticOps { + fn run_mul(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: ArithmeticOps { let (left, right) = context .value_stack_mut() .pop_pair_as::() @@ -813,8 +794,8 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_div(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: TransmuteInto + Display, U: ArithmeticOps + TransmuteInto { + fn run_div(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: TransmuteInto + Display, U: ArithmeticOps + TransmuteInto { let (left, right) = context .value_stack_mut() .pop_pair_as::() @@ -826,8 +807,8 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_rem(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: TransmuteInto, U: Integer + TransmuteInto { + fn run_rem(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: TransmuteInto, U: Integer + TransmuteInto { let (left, right) = context .value_stack_mut() .pop_pair_as::() @@ -839,8 +820,8 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_and(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From<::Output> + TryInto, T: ops::BitAnd { + fn run_and(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From<::Output> + TryInto, T: ops::BitAnd { let (left, right) = context .value_stack_mut() .pop_pair_as::() @@ -850,8 +831,8 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_or(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From<::Output> + TryInto, T: ops::BitOr { + fn run_or(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From<::Output> + TryInto, T: ops::BitOr { let (left, right) = context .value_stack_mut() .pop_pair_as::() @@ -861,8 +842,8 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_xor(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From<::Output> + TryInto, T: ops::BitXor { + fn run_xor(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From<::Output> + TryInto, T: ops::BitXor { let (left, right) = context .value_stack_mut() .pop_pair_as::() @@ -872,8 +853,8 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_shl(&mut self, context: &mut FunctionContext, mask: T) -> Result - where RuntimeValue: From<>::Output> + TryInto, T: ops::Shl + ops::BitAnd { + fn run_shl(&mut self, context: &mut FunctionContext, mask: T) -> Result + where RuntimeValue: From<>::Output> + TryInto, T: ops::Shl + ops::BitAnd { let (left, right) = context .value_stack_mut() .pop_pair_as::() @@ -883,8 +864,8 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_shr(&mut self, context: &mut FunctionContext, mask: U) -> Result - where RuntimeValue: From + TryInto, T: TransmuteInto, U: ops::Shr + ops::BitAnd, >::Output: TransmuteInto { + fn run_shr(&mut self, context: &mut FunctionContext, mask: U) -> Result + where RuntimeValue: From + TryInto, T: TransmuteInto, U: ops::Shr + ops::BitAnd, >::Output: TransmuteInto { let (left, right) = context .value_stack_mut() .pop_pair_as::() @@ -896,8 +877,8 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_rotl(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Integer { + fn run_rotl(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Integer { let (left, right) = context .value_stack_mut() .pop_pair_as::() @@ -907,8 +888,8 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_rotr(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Integer + fn run_rotr(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Integer { let (left, right) = context .value_stack_mut() @@ -919,52 +900,52 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_abs(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float + fn run_abs(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { self.run_unop(context, |v| v.abs()) } - fn run_neg(&mut self, context: &mut FunctionContext) -> Result + fn run_neg(&mut self, context: &mut FunctionContext) -> Result where - RuntimeValue: From<::Output> + TryInto, + RuntimeValue: From<::Output> + TryInto, T: ops::Neg { self.run_unop(context, |v| v.neg()) } - fn run_ceil(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float + fn run_ceil(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { self.run_unop(context, |v| v.ceil()) } - fn run_floor(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float + fn run_floor(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { self.run_unop(context, |v| v.floor()) } - fn run_trunc(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float + fn run_trunc(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { self.run_unop(context, |v| v.trunc()) } - fn run_nearest(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float + fn run_nearest(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { self.run_unop(context, |v| v.nearest()) } - fn run_sqrt(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float + fn run_sqrt(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { self.run_unop(context, |v| v.sqrt()) } - fn run_min(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float + fn run_min(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { let (left, right) = context .value_stack_mut() @@ -975,8 +956,8 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_max(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float { + fn run_max(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { let (left, right) = context .value_stack_mut() .pop_pair_as::() @@ -986,8 +967,8 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_copysign(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float { + fn run_copysign(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { let (left, right) = context .value_stack_mut() .pop_pair_as::() @@ -997,17 +978,16 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_wrap(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: WrapInto { + fn run_wrap(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: WrapInto { self.run_unop(context, |v| v.wrap_into()) } - fn run_trunc_to_int(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: TryTruncateInto, U: TransmuteInto, { + fn run_trunc_to_int(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: TryTruncateInto, U: TransmuteInto, { let v = context .value_stack_mut() - .pop_as::() - .expect("Due to vaidation stack should contain value"); + .pop_as::(); v.try_truncate_into() .map(|v| v.transmute_into()) @@ -1015,14 +995,13 @@ impl<'a, E: Externals> Interpreter<'a, E> { .map(|_| InstructionOutcome::RunNextInstruction) } - fn run_extend(&mut self, context: &mut FunctionContext) -> Result + fn run_extend(&mut self, context: &mut FunctionContext) -> Result where - RuntimeValue: From + TryInto, T: ExtendInto, U: TransmuteInto + RuntimeValue: From + TryInto, T: ExtendInto, U: TransmuteInto { let v = context .value_stack_mut() - .pop_as::() - .expect("Due to vaidation stack should contain value"); + .pop_as::(); let v = v.extend_into().transmute_into(); context.value_stack_mut().push(v.into())?; @@ -1030,14 +1009,13 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_reinterpret(&mut self, context: &mut FunctionContext) -> Result + fn run_reinterpret(&mut self, context: &mut FunctionContext) -> Result where - RuntimeValue: From, RuntimeValue: TryInto, T: TransmuteInto + RuntimeValue: From, RuntimeValue: TryInto, T: TransmuteInto { let v = context .value_stack_mut() - .pop_as::() - .expect("Due to vaidation stack should contain value"); + .pop_as::(); let v = v.transmute_into(); context.value_stack_mut().push(v.into())?; @@ -1083,7 +1061,7 @@ impl FunctionContext { } } - pub fn nested(&mut self, function: FuncRef) -> Result { + pub fn nested(&mut self, function: FuncRef) -> Result { let (function_locals, module, function_return_type) = { let module = match *function.as_internal() { FuncInstanceInternal::Internal { ref module, .. } => module.upgrade().expect("module deallocated"), @@ -1149,7 +1127,7 @@ impl FunctionContext { &self.frame_stack } - pub fn push_frame(&mut self, labels: &HashMap, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), Trap> { + pub fn push_frame(&mut self, labels: &HashMap, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), TrapKind> { let begin_position = self.position; let branch_position = match frame_type { BlockFrameType::Function => usize::MAX, @@ -1175,7 +1153,7 @@ impl FunctionContext { branch_position: branch_position, end_position: end_position, value_stack_len: self.value_stack.len(), - }).map_err(|_| Trap::StackOverflow)?; + }).map_err(|_| TrapKind::StackOverflow)?; Ok(()) } @@ -1184,7 +1162,7 @@ impl FunctionContext { let _ = self.frame_stack.pop().expect("Due to validation frame stack shouldn't be empty"); } - pub fn pop_frame(&mut self, is_branch: bool) -> Result<(), Trap> { + pub fn pop_frame(&mut self, is_branch: bool) -> Result<(), TrapKind> { let frame = self.frame_stack .pop() .expect("Due to validation frame stack shouldn't be empty"); @@ -1192,7 +1170,7 @@ impl FunctionContext { let frame_value = match frame.block_type { BlockType::Value(_) if frame.frame_type != BlockFrameType::Loop || !is_branch => - Some(self.value_stack.pop().expect("Due to validation, stack shouldn't be empty")), + Some(self.value_stack.pop()), _ => None, }; self.value_stack.resize(frame.value_stack_len); @@ -1211,17 +1189,22 @@ impl fmt::Debug for FunctionContext { } } -fn effective_address(address: u32, offset: u32) -> Result { +fn effective_address(address: u32, offset: u32) -> Result { match offset.checked_add(address) { - None => Err(Trap::MemoryAccessOutOfBounds), + None => Err(TrapKind::MemoryAccessOutOfBounds), Some(address) => Ok(address), } } -fn prepare_function_args(signature: &Signature, caller_stack: &mut ValueStack) -> Vec { - let mut args = signature.params().iter().cloned().rev().map(|_| { - caller_stack.pop().expect("Due to validation stack shouldn't be empty") - }).collect::>(); +fn prepare_function_args( + signature: &Signature, + caller_stack: &mut ValueStack, +) -> Vec { + let mut args = signature + .params() + .iter() + .map(|_| caller_stack.pop()) + .collect::>(); args.reverse(); check_function_args(signature, &args).expect("Due to validation arguments should match"); args @@ -1262,37 +1245,39 @@ impl ValueStack { } } - fn pop_as(&mut self) -> Result + fn pop_as(&mut self) -> T where - RuntimeValue: TryInto, + RuntimeValue: TryInto, { - let value = self.stack_with_limit.pop()?; - TryInto::try_into(value) + let value = self.stack_with_limit + .pop() + .expect("Due to validation stack shouldn't be empty"); + TryInto::try_into(value).expect("Due to validation stack top's type should match") } fn pop_pair_as(&mut self) -> Result<(T, T), Error> where - RuntimeValue: TryInto, + RuntimeValue: TryInto, { - let right = self.pop_as()?; - let left = self.pop_as()?; + let right = self.pop_as(); + let left = self.pop_as(); Ok((left, right)) } - fn pop_triple(&mut self) -> Result<(RuntimeValue, RuntimeValue, RuntimeValue), Error> { - let right = self.stack_with_limit.pop()?; - let mid = self.stack_with_limit.pop()?; - let left = self.stack_with_limit.pop()?; - Ok((left, mid, right)) + fn pop_triple(&mut self) -> (RuntimeValue, RuntimeValue, RuntimeValue) { + let right = self.stack_with_limit.pop().expect("Due to validation stack shouldn't be empty"); + let mid = self.stack_with_limit.pop().expect("Due to validation stack shouldn't be empty"); + let left = self.stack_with_limit.pop().expect("Due to validation stack shouldn't be empty"); + (left, mid, right) } - fn pop(&mut self) -> Result { - self.stack_with_limit.pop().map_err(|e| Error::Stack(e.to_string())) + fn pop(&mut self) -> RuntimeValue { + self.stack_with_limit.pop().expect("Due to validation stack shouldn't be empty") } - fn push(&mut self, value: RuntimeValue) -> Result<(), Trap> { + fn push(&mut self, value: RuntimeValue) -> Result<(), TrapKind> { self.stack_with_limit.push(value) - .map_err(|_| Trap::StackOverflow) + .map_err(|_| TrapKind::StackOverflow) } fn resize(&mut self, new_len: usize) { @@ -1307,7 +1292,7 @@ impl ValueStack { self.stack_with_limit.limit() } - fn top(&self) -> Result<&RuntimeValue, Error> { - self.stack_with_limit.top().map_err(|e| Error::Stack(e.to_string())) + fn top(&self) -> &RuntimeValue { + self.stack_with_limit.top().expect("Due to validation stack shouldn't be empty") } } diff --git a/src/tests/host.rs b/src/tests/host.rs index 211df56..a6546b9 100644 --- a/src/tests/host.rs +++ b/src/tests/host.rs @@ -1,7 +1,7 @@ use { Error, Signature, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder, MemoryInstance, MemoryRef, TableInstance, TableRef, ModuleImportResolver, ModuleInstance, ModuleRef, - RuntimeValue, RuntimeArgs, TableDescriptor, MemoryDescriptor, Trap, + RuntimeValue, RuntimeArgs, TableDescriptor, MemoryDescriptor, Trap, TrapKind, }; use types::ValueType; use super::parse_wat; @@ -92,7 +92,7 @@ impl Externals for TestHost { ERR_FUNC_INDEX => { let error_code: u32 = args.nth(0); let error = HostErrorWithCode { error_code }; - Err(Trap::Host(Box::new(error))) + Err(TrapKind::Host(Box::new(error)).into()) } INC_MEM_FUNC_INDEX => { let ptr: u32 = args.nth(0); @@ -131,7 +131,7 @@ impl Externals for TestHost { .expect("expected to be Some"); if val.value_type() != result.value_type() { - return Err(Trap::Host(Box::new(HostErrorWithCode { error_code: 123 }))); + return Err(TrapKind::Host(Box::new(HostErrorWithCode { error_code: 123 })).into()); } Ok(Some(result)) } @@ -257,12 +257,7 @@ fn host_err() { "`test` expected to return error", ); - let host_error: Box = match error { - Error::Trap(Trap::Host(err)) => err, - err => panic!("Unexpected error {:?}", err), - }; - - let error_with_code = host_error.downcast_ref::().expect( + let error_with_code = error.as_host_error().expect("Expected host error").downcast_ref::().expect( "Failed to downcast to expected error type", ); assert_eq!(error_with_code.error_code, 228); @@ -657,7 +652,7 @@ fn dynamically_add_host_func() { host_func_index as usize, ); self.table.set(table_index, Some(added_func)) - .map_err(|_| Trap::TableAccessOutOfBounds)?; + .map_err(|_| TrapKind::TableAccessOutOfBounds)?; Ok(Some(RuntimeValue::I32(table_index as i32))) } diff --git a/src/value.rs b/src/value.rs index 4d1bdc6..75035ff 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,9 +1,16 @@ use std::{i32, i64, u32, u64, f32}; use std::io; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use {Error, Trap}; use parity_wasm::elements::ValueType; +use TrapKind; +#[derive(Debug)] +pub enum Error { + UnexpectedType { + expected: ValueType, + }, + InvalidLittleEndianBuffer, +} /// Runtime value. #[derive(Copy, Clone, Debug, PartialEq)] @@ -65,7 +72,7 @@ pub trait ArithmeticOps { /// Multiply two values. fn mul(self, other: T) -> T; /// Divide two values. - fn div(self, other: T) -> Result; + fn div(self, other: T) -> Result; } /// Integer value. @@ -81,7 +88,7 @@ pub trait Integer: ArithmeticOps { /// Get right bit rotation result. fn rotr(self, other: T) -> T; /// Get division remainder. - fn rem(self, other: T) -> Result; + fn rem(self, other: T) -> Result; } /// Float-point value. @@ -180,7 +187,7 @@ impl TryInto for RuntimeValue { fn try_into(self) -> Result { match self { RuntimeValue::I32(val) => Ok(val != 0), - _ => Err(Error::Value("32-bit int value expected".to_owned())), + _ => Err(Error::UnexpectedType { expected: ValueType::I32 }), } } } @@ -189,7 +196,7 @@ impl TryInto for RuntimeValue { fn try_into(self) -> Result { match self { RuntimeValue::I32(val) => Ok(val), - _ => Err(Error::Value("32-bit int value expected".to_owned())), + _ => Err(Error::UnexpectedType { expected: ValueType::I32 }), } } } @@ -198,7 +205,7 @@ impl TryInto for RuntimeValue { fn try_into(self) -> Result { match self { RuntimeValue::I64(val) => Ok(val), - _ => Err(Error::Value("64-bit int value expected".to_owned())), + _ => Err(Error::UnexpectedType { expected: ValueType::I64 }), } } } @@ -207,7 +214,7 @@ impl TryInto for RuntimeValue { fn try_into(self) -> Result { match self { RuntimeValue::F32(val) => Ok(val), - _ => Err(Error::Value("32-bit float value expected".to_owned())), + _ => Err(Error::UnexpectedType { expected: ValueType::F32 }), } } } @@ -216,7 +223,7 @@ impl TryInto for RuntimeValue { fn try_into(self) -> Result { match self { RuntimeValue::F64(val) => Ok(val), - _ => Err(Error::Value("64-bit float value expected".to_owned())), + _ => Err(Error::UnexpectedType { expected: ValueType::F64 }), } } } @@ -225,7 +232,7 @@ impl TryInto for RuntimeValue { fn try_into(self) -> Result { match self { RuntimeValue::I32(val) => Ok(val as u32), - _ => Err(Error::Value("32-bit int value expected".to_owned())), + _ => Err(Error::UnexpectedType { expected: ValueType::I32 }), } } } @@ -234,7 +241,7 @@ impl TryInto for RuntimeValue { fn try_into(self) -> Result { match self { RuntimeValue::I64(val) => Ok(val as u64), - _ => Err(Error::Value("64-bit int value expected".to_owned())), + _ => Err(Error::UnexpectedType { expected: ValueType::I64 }), } } } @@ -263,19 +270,19 @@ impl_wrap_into!(f64, f32); macro_rules! impl_try_truncate_into { ($from: ident, $into: ident) => { - impl TryTruncateInto<$into, Trap> for $from { - fn try_truncate_into(self) -> Result<$into, Trap> { + impl TryTruncateInto<$into, TrapKind> for $from { + fn try_truncate_into(self) -> Result<$into, TrapKind> { // Casting from a float to an integer will round the float towards zero // NOTE: currently this will cause Undefined Behavior if the rounded value cannot be represented by the // target integer type. This includes Inf and NaN. This is a bug and will be fixed. if self.is_nan() || self.is_infinite() { - return Err(Trap::InvalidConversionToInt); + return Err(TrapKind::InvalidConversionToInt); } // range check let result = self as $into; if result as $from != self.trunc() { - return Err(Trap::InvalidConversionToInt); + return Err(TrapKind::InvalidConversionToInt); } Ok(self as $into) @@ -379,7 +386,7 @@ impl LittleEndianConvert for i8 { fn from_little_endian(buffer: &[u8]) -> Result { buffer.get(0) .map(|v| *v as i8) - .ok_or_else(|| Error::Value("invalid little endian buffer".into())) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) } } @@ -391,7 +398,7 @@ impl LittleEndianConvert for u8 { fn from_little_endian(buffer: &[u8]) -> Result { buffer.get(0) .cloned() - .ok_or_else(|| Error::Value("invalid little endian buffer".into())) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) } } @@ -405,7 +412,7 @@ impl LittleEndianConvert for i16 { fn from_little_endian(buffer: &[u8]) -> Result { io::Cursor::new(buffer).read_i16::() - .map_err(|e| Error::Value(e.to_string())) + .map_err(|_| Error::InvalidLittleEndianBuffer) } } @@ -419,7 +426,7 @@ impl LittleEndianConvert for u16 { fn from_little_endian(buffer: &[u8]) -> Result { io::Cursor::new(buffer).read_u16::() - .map_err(|e| Error::Value(e.to_string())) + .map_err(|_| Error::InvalidLittleEndianBuffer) } } @@ -433,7 +440,7 @@ impl LittleEndianConvert for i32 { fn from_little_endian(buffer: &[u8]) -> Result { io::Cursor::new(buffer).read_i32::() - .map_err(|e| Error::Value(e.to_string())) + .map_err(|_| Error::InvalidLittleEndianBuffer) } } @@ -447,7 +454,7 @@ impl LittleEndianConvert for u32 { fn from_little_endian(buffer: &[u8]) -> Result { io::Cursor::new(buffer).read_u32::() - .map_err(|e| Error::Value(e.to_string())) + .map_err(|_| Error::InvalidLittleEndianBuffer) } } @@ -461,7 +468,7 @@ impl LittleEndianConvert for i64 { fn from_little_endian(buffer: &[u8]) -> Result { io::Cursor::new(buffer).read_i64::() - .map_err(|e| Error::Value(e.to_string())) + .map_err(|_| Error::InvalidLittleEndianBuffer) } } @@ -476,7 +483,7 @@ impl LittleEndianConvert for f32 { fn from_little_endian(buffer: &[u8]) -> Result { io::Cursor::new(buffer).read_u32::() .map(f32_from_bits) - .map_err(|e| Error::Value(e.to_string())) + .map_err(|_| Error::InvalidLittleEndianBuffer) } } @@ -491,7 +498,7 @@ impl LittleEndianConvert for f64 { fn from_little_endian(buffer: &[u8]) -> Result { io::Cursor::new(buffer).read_u64::() .map(f64_from_bits) - .map_err(|e| Error::Value(e.to_string())) + .map_err(|_| Error::InvalidLittleEndianBuffer) } } @@ -537,14 +544,14 @@ macro_rules! impl_integer_arithmetic_ops { fn add(self, other: $type) -> $type { self.wrapping_add(other) } fn sub(self, other: $type) -> $type { self.wrapping_sub(other) } fn mul(self, other: $type) -> $type { self.wrapping_mul(other) } - fn div(self, other: $type) -> Result<$type, Trap> { + fn div(self, other: $type) -> Result<$type, TrapKind> { if other == 0 { - Err(Trap::DivisionByZero) + Err(TrapKind::DivisionByZero) } else { let (result, overflow) = self.overflowing_div(other); if overflow { - Err(Trap::InvalidConversionToInt) + Err(TrapKind::InvalidConversionToInt) } else { Ok(result) } @@ -565,7 +572,7 @@ macro_rules! impl_float_arithmetic_ops { fn add(self, other: $type) -> $type { self + other } fn sub(self, other: $type) -> $type { self - other } fn mul(self, other: $type) -> $type { self * other } - fn div(self, other: $type) -> Result<$type, Trap> { Ok(self / other) } + fn div(self, other: $type) -> Result<$type, TrapKind> { Ok(self / other) } } } } @@ -581,8 +588,8 @@ macro_rules! impl_integer { fn count_ones(self) -> $type { self.count_ones() as $type } fn rotl(self, other: $type) -> $type { self.rotate_left(other as u32) } fn rotr(self, other: $type) -> $type { self.rotate_right(other as u32) } - fn rem(self, other: $type) -> Result<$type, Trap> { - if other == 0 { Err(Trap::DivisionByZero) } + fn rem(self, other: $type) -> Result<$type, TrapKind> { + if other == 0 { Err(TrapKind::DivisionByZero) } else { Ok(self.wrapping_rem(other)) } } }