diff --git a/examples/tictactoe.rs b/examples/tictactoe.rs index 138de9c..86d7781 100644 --- a/examples/tictactoe.rs +++ b/examples/tictactoe.rs @@ -8,7 +8,7 @@ use wasmi::{ Error as InterpreterError, ModuleInstance, ModuleRef, Externals, RuntimeValue, FuncRef, ModuleImportResolver, FuncInstance, HostError, ImportsBuilder, Signature, ValueType, - RuntimeArgs, + RuntimeArgs, Trap, }; #[derive(Debug)] @@ -149,15 +149,15 @@ impl<'a> Externals for Runtime<'a> { &mut self, index: usize, args: RuntimeArgs, - ) -> Result, InterpreterError> { + ) -> Result, Trap> { match index { SET_FUNC_INDEX => { - let idx: i32 = args.nth(0)?; + let idx: i32 = args.nth(0); self.game.set(idx, self.player)?; Ok(None) } GET_FUNC_INDEX => { - let idx: i32 = args.nth(0)?; + let idx: i32 = args.nth(0); let val: i32 = tictactoe::Player::into_i32(self.game.get(idx)?); Ok(Some(val.into())) } diff --git a/spec/src/run.rs b/spec/src/run.rs index 14afa25..7f56ce7 100644 --- a/spec/src/run.rs +++ b/spec/src/run.rs @@ -13,13 +13,14 @@ use wasmi::{ GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryInstance, MemoryRef, ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeValue, TableInstance, TableRef, ValueType, - Module, Signature, MemoryDescriptor, + Module, Signature, MemoryDescriptor, Trap, TableDescriptor, GlobalDescriptor, FuncInstance, RuntimeArgs, }; #[derive(Debug)] enum Error { Load(String), + Start(Trap), Interpreter(InterpreterError), } @@ -58,7 +59,7 @@ impl Externals for SpecModule { &mut self, index: usize, args: RuntimeArgs, - ) -> Result, InterpreterError> { + ) -> Result, Trap> { match index { PRINT_FUNC_INDEX => { println!("print: {:?}", args); @@ -243,7 +244,9 @@ fn try_load( ) -> Result<(), Error> { let module = try_load_module(base_dir, module_path)?; let instance = ModuleInstance::new(&module, &ImportsBuilder::default())?; - instance.run_start(spec_driver.spec_module())?; + instance + .run_start(spec_driver.spec_module()) + .map_err(|trap| Error::Start(trap))?; Ok(()) } diff --git a/src/common/stack.rs b/src/common/stack.rs index b59e77a..d4d1bf3 100644 --- a/src/common/stack.rs +++ b/src/common/stack.rs @@ -28,13 +28,6 @@ pub struct StackWithLimit where T: Clone { } impl StackWithLimit where T: Clone { - pub fn with_data>(data: D, limit: usize) -> Self { - StackWithLimit { - values: data.into_iter().collect(), - limit: limit - } - } - pub fn with_limit(limit: usize) -> Self { StackWithLimit { values: VecDeque::new(), diff --git a/src/func.rs b/src/func.rs index bd1649a..7662e85 100644 --- a/src/func.rs +++ b/src/func.rs @@ -2,13 +2,11 @@ use std::rc::{Rc, Weak}; use std::fmt; use std::collections::HashMap; use parity_wasm::elements::{Local, Opcodes}; -use {Error, Signature}; +use {Trap, Signature}; use host::Externals; -use runner::{prepare_function_args, FunctionContext, Interpreter}; +use runner::{check_function_args, Interpreter}; use value::RuntimeValue; use module::ModuleInstance; -use common::stack::StackWithLimit; -use common::{DEFAULT_FRAME_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT}; /// Reference to a function (See [`FuncInstance`] for details). /// @@ -134,37 +132,16 @@ impl FuncInstance { func: &FuncRef, args: &[RuntimeValue], externals: &mut E, - ) -> Result, Error> { - enum InvokeKind<'a> { - Internal(FunctionContext), - Host(usize, &'a [RuntimeValue]), - } - - let result = match *func.as_internal() { - FuncInstanceInternal::Internal { ref signature, .. } => { - let mut stack = - StackWithLimit::with_data(args.into_iter().cloned(), DEFAULT_VALUE_STACK_LIMIT); - let args = prepare_function_args(signature, &mut stack)?; - let context = FunctionContext::new( - func.clone(), - DEFAULT_VALUE_STACK_LIMIT, - DEFAULT_FRAME_STACK_LIMIT, - signature, - args, - ); - InvokeKind::Internal(context) + ) -> Result, Trap> { + debug_assert!(check_function_args(func.signature(), &args).is_ok()); + match *func.as_internal() { + FuncInstanceInternal::Internal { .. } => { + let mut interpreter = Interpreter::new(externals); + interpreter.start_execution(func, args) } FuncInstanceInternal::Host { ref host_func_index, .. } => { - InvokeKind::Host(*host_func_index, &*args) + externals.invoke_index(*host_func_index, args.into()) } - }; - - match result { - InvokeKind::Internal(ctx) => { - let mut interpreter = Interpreter::new(externals); - interpreter.run_function(ctx) - } - InvokeKind::Host(host_func, args) => externals.invoke_index(host_func, args.into()), } } } diff --git a/src/host.rs b/src/host.rs index a58abdb..4fd46b7 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,6 +1,6 @@ use std::any::TypeId; use value::{RuntimeValue, TryInto}; -use Error; +use {Error, Trap}; /// Safe wrapper for list of arguments. #[derive(Debug)] @@ -13,19 +13,37 @@ impl<'a> From<&'a [RuntimeValue]> for RuntimeArgs<'a> { } impl<'a> RuntimeArgs<'a> { - /// Extract argument by index `idx` returning error if cast is invalid or not enough arguments - pub fn nth(&self, idx: usize) -> Result where RuntimeValue: TryInto { - Ok(self.nth_value(idx)?.try_into().map_err(|_| Error::Value("Invalid argument cast".to_owned()))?) + /// Extract argument by index `idx`. + /// + /// # 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()))?) } - /// Extract argument as a runtime value by index `idx` returning error is not enough arguments - pub fn nth_value(&self, idx: usize) -> Result { + /// Extract argument as a [`RuntimeValue`] by index `idx`. + /// + /// # Errors + /// + /// Returns `Err` if this list has not enough arguments. + pub fn nth_value_checked(&self, idx: usize) -> Result { if self.0.len() <= idx { return Err(Error::Value("Invalid argument index".to_owned())); } Ok(self.0[idx]) } + /// Extract argument by index `idx`. + /// + /// # Panics + /// + /// Panics if cast is invalid or not enough arguments. + 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") + } + /// Total number of arguments pub fn len(&self) -> usize { self.0.len() @@ -104,7 +122,7 @@ impl HostError { /// ```rust /// use wasmi::{ /// Externals, RuntimeValue, RuntimeArgs, Error, ModuleImportResolver, -/// FuncRef, ValueType, Signature, FuncInstance +/// FuncRef, ValueType, Signature, FuncInstance, Trap, /// }; /// /// struct HostExternals { @@ -118,11 +136,11 @@ impl HostError { /// &mut self, /// index: usize, /// args: RuntimeArgs, -/// ) -> Result, Error> { +/// ) -> Result, Trap> { /// match index { /// ADD_FUNC_INDEX => { -/// let a: u32 = args.nth(0).unwrap(); -/// let b: u32 = args.nth(1).unwrap(); +/// let a: u32 = args.nth(0); +/// let b: u32 = args.nth(1); /// let result = a + b; /// /// Ok(Some(RuntimeValue::I32(result as i32))) @@ -173,7 +191,7 @@ pub trait Externals { &mut self, index: usize, args: RuntimeArgs, - ) -> Result, Error>; + ) -> Result, Trap>; } /// Implementation of [`Externals`] that just traps on [`invoke_index`]. @@ -187,8 +205,8 @@ impl Externals for NopExternals { &mut self, _index: usize, _args: RuntimeArgs, - ) -> Result, Error> { - Err(Error::Trap("invoke index on no-op externals".into())) + ) -> Result, Trap> { + Err(Trap::Unreachable) } } @@ -201,14 +219,14 @@ mod tests { #[test] fn i32_runtime_args() { let args: RuntimeArgs = (&[RuntimeValue::I32(0)][..]).into(); - let val: i32 = args.nth(0).unwrap(); + let val: i32 = args.nth_checked(0).unwrap(); assert_eq!(val, 0); } #[test] fn i64_invalid_arg_cast() { let args: RuntimeArgs = (&[RuntimeValue::I64(90534534545322)][..]).into(); - assert!(args.nth::(0).is_err()); + assert!(args.nth_checked::(0).is_err()); } // Tests that `HostError` trait is object safe. diff --git a/src/lib.rs b/src/lib.rs index 98b04a4..4f919e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,6 +106,78 @@ use std::fmt; use std::error; use std::collections::HashMap; +/// Error type which can happen at execution time. +/// +/// 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 { + /// Wasm code executed `unreachable` opcode. + /// + /// `unreachable` is a special opcode which always traps upon execution. + /// This opcode have a similar purpose as `ud2` in x86. + Unreachable, + + /// Attempt to load or store at the address which + /// lies outside of bounds of the memory. + /// + /// Since addresses are interpreted as unsigned integers, out of bounds access + /// can't happen with negative addresses (i.e. they will always wrap). + MemoryAccessOutOfBounds, + + /// Attempt to access table element at index which + /// lies outside of bounds. + /// + /// This typically can happen when `call_indirect` is executed + /// with index that lies out of bounds. + /// + /// Since indexes are interpreted as unsinged integers, out of bounds access + /// can't happen with negative indexes (i.e. they will always wrap). + TableAccessOutOfBounds, + + /// Attempt to access table element which is uninitialized (i.e. `None`). + /// + /// This typically can happen when `call_indirect` is executed. + ElemUninitialized, + + /// Attempt to `call_indirect` function with mismatched [signature][`Signature`]. + /// + /// `call_indirect` always specifies the expected signature of function. + /// If `call_indirect` is executed with index that points on function with + /// signature different that is expected by this `call_indirect`, this trap is raised. + /// + /// [`Signature`]: struct.Signature.html + ElemSignatureMismatch, + + /// Attempt to divide by zero. + /// + /// This trap typically can happen if `div` or `rem` is executed with + /// zero as divider. + DivisionByZero, + + /// Attempt to make a conversion to an int failed. + /// + /// This can happen when: + /// + /// - trying to do signed division (or get the remainder) -2N-1 over -1. This is + /// because the result +2N-1 isn't representable as a N-bit signed integer. + /// - trying to truncate NaNs, infinity, or value for which the result is out of range into an integer. + InvalidConversionToInt, + + /// Stack overflow. + /// + /// This is likely caused by some infinite or very deep recursion. + /// Extensive inlining might also be the cause of stack overflow. + StackOverflow, + + /// Error specified by the host. + /// + /// Typically returned from an implementation of [`Externals`]. + /// + /// [`Externals`]: trait.Externals.html + Host(Box), +} + /// Internal interpreter error. #[derive(Debug)] pub enum Error { @@ -127,7 +199,7 @@ pub enum Error { /// Value-level error. Value(String), /// Trap. - Trap(String), + Trap(Trap), /// Custom embedder error. Host(Box), } @@ -143,7 +215,7 @@ impl Into for Error { Error::Global(s) => s, Error::Stack(s) => s, Error::Value(s) => s, - Error::Trap(s) => format!("trap: {}", s), + Error::Trap(s) => format!("trap: {:?}", s), Error::Host(e) => format!("user: {}", e), } } @@ -160,7 +232,7 @@ impl fmt::Display for Error { 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::Trap(ref s) => write!(f, "Trap: {:?}", s), Error::Host(ref e) => write!(f, "User: {}", e), } } @@ -179,7 +251,7 @@ impl error::Error for Error { Error::Global(ref s) => s, Error::Stack(ref s) => s, Error::Value(ref s) => s, - Error::Trap(ref s) => s, + Error::Trap(_) => "Trap", Error::Host(_) => "Host error", } } @@ -192,6 +264,18 @@ 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)) + } +} + +impl From for Error { + fn from(e: Trap) -> Error { + Error::Trap(e) + } +} + impl From for Error { fn from(e: validation::Error) -> Error { Error::Validation(e.to_string()) diff --git a/src/module.rs b/src/module.rs index 0d10d25..5419a76 100644 --- a/src/module.rs +++ b/src/module.rs @@ -1,3 +1,5 @@ +use runner::check_function_args; +use Trap; use std::rc::Rc; use std::cell::RefCell; use std::fmt; @@ -575,7 +577,9 @@ impl ModuleInstance { } }; + check_function_args(func_instance.signature(), &args)?; FuncInstance::invoke(&func_instance, args, externals) + .map_err(|t| Error::Trap(t)) } /// Find export by a name. @@ -608,7 +612,7 @@ impl<'a> NotStartedModuleRef<'a> { &self.instance } - pub fn run_start(self, state: &mut E) -> Result { + pub fn run_start(self, state: &mut E) -> Result { if let Some(start_fn_idx) = self.loaded_module.module().start_section() { let start_func = self.instance.func_by_index(start_fn_idx).expect( "Due to validation start function should exists", diff --git a/src/runner.rs b/src/runner.rs index e2c2e7c..18cb17e 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, Signature}; +use {Error, Trap, Signature}; use module::ModuleRef; use func::{FuncRef, FuncInstance, FuncInstanceInternal}; use value::{ @@ -15,31 +15,13 @@ use value::{ use host::Externals; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, BlockFrame, BlockFrameType}; use common::stack::StackWithLimit; +use common::{DEFAULT_FRAME_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT}; /// Function interpreter. pub struct Interpreter<'a, E: Externals + 'a> { externals: &'a mut E, } -/// Function execution context. -pub struct FunctionContext { - /// Is context initialized. - pub is_initialized: bool, - /// Internal function reference. - pub function: FuncRef, - pub module: ModuleRef, - /// Function return type. - pub return_type: BlockType, - /// Local variables. - pub locals: Vec, - /// Values stack. - pub value_stack: StackWithLimit, - /// Blocks frames stack. - pub frame_stack: StackWithLimit, - /// Current instruction position. - pub position: usize, -} - /// Interpreter action to execute after executing instruction. pub enum InstructionOutcome { /// Continue with next instruction. @@ -69,10 +51,22 @@ impl<'a, E: Externals> Interpreter<'a, E> { } } - pub fn run_function(&mut self, function_context: FunctionContext) -> Result, Error> { - let mut function_stack = VecDeque::new(); - function_stack.push_back(function_context); + pub fn start_execution(&mut self, func: &FuncRef, args: &[RuntimeValue]) -> Result, Trap> { + let context = FunctionContext::new( + func.clone(), + DEFAULT_VALUE_STACK_LIMIT, + DEFAULT_FRAME_STACK_LIMIT, + func.signature(), + args.into_iter().cloned().collect(), + ); + let mut function_stack = VecDeque::new(); + function_stack.push_back(context); + + self.run_interpreter_loop(&mut function_stack) + } + + fn run_interpreter_loop(&mut self, function_stack: &mut VecDeque) -> Result, Trap> { loop { let mut function_context = function_stack.pop_back().expect("on loop entry - not empty; on loop continue - checking for emptiness; qed"); let function_ref = function_context.function.clone(); @@ -106,7 +100,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { function_stack.push_back(nested_context); }, FuncInstanceInternal::Host { ref signature, .. } => { - let args = prepare_function_args(signature, &mut function_context.value_stack)?; + 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)?; @@ -119,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]; @@ -128,7 +122,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { InstructionOutcome::Branch(mut index) => { // discard index - 1 blocks while index >= 1 { - function_context.discard_frame()?; + function_context.discard_frame(); index -= 1; } @@ -151,12 +145,18 @@ impl<'a, E: Externals> Interpreter<'a, E> { } Ok(RunResult::Return(match function_context.return_type { - BlockType::Value(_) => Some(function_context.value_stack_mut().pop()?), + BlockType::Value(_) => { + let result = function_context + .value_stack_mut() + .pop() + .expect("Due to validation stack shouldn't be empty"); + 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,27 +350,30 @@ impl<'a, E: Externals> Interpreter<'a, E> { } } - fn run_unreachable(&mut self, _context: &mut FunctionContext) -> Result { - Err(Error::Trap("programmatic".into())) + fn run_unreachable(&mut self, _context: &mut FunctionContext) -> Result { + Err(Trap::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 { - let branch = context.value_stack_mut().pop_as()?; - let block_frame_type = if branch { BlockFrameType::IfTrue } else { + 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"); + let block_frame_type = if condition { BlockFrameType::IfTrue } else { let else_pos = labels[&context.position]; if !labels.contains_key(&else_pos) { context.position = else_pos; @@ -380,39 +383,46 @@ impl<'a, E: Externals> Interpreter<'a, E> { context.position = else_pos; BlockFrameType::IfFalse }; - context.push_frame(labels, block_frame_type, block_type).map(|_| InstructionOutcome::RunNextInstruction) + context.push_frame(labels, block_frame_type, block_type)?; + + 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 { - if context.value_stack_mut().pop_as()? { + 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"); + if condition { Ok(InstructionOutcome::Branch(label_idx as usize)) } else { Ok(InstructionOutcome::RunNextInstruction) } } - fn run_br_table(&mut self, context: &mut FunctionContext, table: &[u32], default: u32) -> Result { - let index: u32 = context.value_stack_mut().pop_as()?; + 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"); 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) } @@ -420,7 +430,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) @@ -432,13 +442,18 @@ impl<'a, E: Externals> Interpreter<'a, E> { &mut self, context: &mut FunctionContext, signature_idx: u32, - ) -> Result { - let table_func_idx: u32 = context.value_stack_mut().pop_as()?; + ) -> Result { + let table_func_idx: u32 = context + .value_stack_mut() + .pop_as() + .expect("Due to validation stack top should be I32"); 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)?; + let func_ref = table.get(table_func_idx) + .map_err(|_| Trap::TableAccessOutOfBounds)? + .ok_or_else(|| Trap::ElemUninitialized)?; { let actual_function_type = func_ref.signature(); @@ -448,66 +463,64 @@ impl<'a, E: Externals> Interpreter<'a, E> { .expect("Due to validation type should exists"); if &*required_function_type != actual_function_type { - return Err(Error::Function(format!( - "expected function with signature ({:?}) -> {:?} when got with ({:?}) -> {:?}", - required_function_type.params(), - required_function_type.return_type(), - actual_function_type.params(), - actual_function_type.return_type() - ))); + return Err(Trap::ElemSignatureMismatch); } } Ok(InstructionOutcome::ExecuteCall(func_ref)) } - fn run_drop(&mut self, context: &mut FunctionContext) -> Result { - context + fn run_drop(&mut self, context: &mut FunctionContext) -> Result { + let _ = context .value_stack_mut() - .pop() - .map_err(Into::into) - .map(|_| InstructionOutcome::RunNextInstruction) + .pop(); + Ok(InstructionOutcome::RunNextInstruction) } - fn run_select(&mut self, context: &mut FunctionContext) -> Result { - context + fn run_select(&mut self, context: &mut FunctionContext) -> Result { + let (left, mid, right) = context .value_stack_mut() .pop_triple() - .and_then(|(left, mid, right)| { - let right: Result<_, Error> = right.try_into(); - match (left, mid, right) { - (left, mid, Ok(condition)) => Ok((left, mid, condition)), - _ => Err(Error::Stack("expected to get int value from stack".into())) - } - }) - .map(|(left, mid, condition)| if condition { left } else { mid }) - .map(|val| context.value_stack_mut().push(val)) - .map(|_| InstructionOutcome::RunNextInstruction) + .expect("Due to validation there should be 3 values on stack"); + + let condition = right + .try_into() + .expect("Due to validation stack top should be I32"); + let val = if condition { left } else { mid }; + context.value_stack_mut().push(val)?; + Ok(InstructionOutcome::RunNextInstruction) } - fn run_get_local(&mut self, context: &mut FunctionContext, index: u32) -> Result { - context.get_local(index as usize) - .map(|value| context.value_stack_mut().push(value)) - .map(|_| InstructionOutcome::RunNextInstruction) + 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 { - let arg = context.value_stack_mut().pop()?; - context.set_local(index as usize, arg) - .map(|_| InstructionOutcome::RunNextInstruction) + 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"); + context.set_local(index as usize, arg); + Ok(InstructionOutcome::RunNextInstruction) } - fn run_tee_local(&mut self, context: &mut FunctionContext, index: u32) -> Result { - let arg = context.value_stack().top()?.clone(); - context.set_local(index as usize, arg) - .map(|_| InstructionOutcome::RunNextInstruction) + 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) } fn run_get_global( &mut self, context: &mut FunctionContext, index: u32, - ) -> Result { + ) -> Result { let global = context .module() .global_by_index(index) @@ -521,9 +534,11 @@ impl<'a, E: Externals> Interpreter<'a, E> { &mut self, context: &mut FunctionContext, index: u32, - ) -> Result { - let val = context.value_stack_mut().pop()?; - + ) -> Result { + let val = context + .value_stack_mut() + .pop() + .expect("Due to vaidation stack should contain value"); let global = context .module() .global_by_index(index) @@ -532,26 +547,46 @@ 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 address = effective_address(offset, context.value_stack_mut().pop_as()?)?; + let raw_address = context + .value_stack_mut() + .pop_as() + .expect("Due to vaidation stack should contain value"); + let address = + effective_address( + offset, + raw_address, + )?; let m = context.module() .memory_by_index(DEFAULT_MEMORY_INDEX) .expect("Due to validation memory should exists"); - let b = m.get(address, mem::size_of::())?; - let n = T::from_little_endian(b)?; + let b = m.get(address, mem::size_of::()) + .map_err(|_| Trap::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 address = effective_address(offset, context.value_stack_mut().pop_as()?)?; + let raw_address = context + .value_stack_mut() + .pop_as() + .expect("Due to vaidation stack should contain value"); + let address = + effective_address( + offset, + raw_address, + )?; let m = context.module() .memory_by_index(DEFAULT_MEMORY_INDEX) .expect("Due to validation memory should exists"); - let b = m.get(address, mem::size_of::())?; - let v = T::from_little_endian(b)?; + let b = m.get(address, mem::size_of::()) + .map_err(|_| Trap::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(); context .value_stack_mut() @@ -560,18 +595,28 @@ impl<'a, E: Externals> Interpreter<'a, E> { .map(|_| InstructionOutcome::RunNextInstruction) } - fn run_store(&mut self, context: &mut FunctionContext, _align: u32, offset: u32) -> Result + 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::() - .map(|n| n.into_little_endian())?; - let address = effective_address(offset, 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"); + let address = + effective_address( + offset, + raw_address, + )?; let m = context.module() .memory_by_index(DEFAULT_MEMORY_INDEX) .expect("Due to validation memory should exists"); - m.set(address, &stack_value)?; + m.set(address, &stack_value) + .map_err(|_| Trap::MemoryAccessOutOfBounds)?; Ok(InstructionOutcome::RunNextInstruction) } @@ -580,7 +625,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { context: &mut FunctionContext, _align: u32, offset: u32, - ) -> Result + ) -> Result where RuntimeValue: TryInto, T: WrapInto, @@ -589,18 +634,28 @@ impl<'a, E: Externals> Interpreter<'a, E> { let stack_value: T = context .value_stack_mut() .pop() - .map_err(Into::into) - .and_then(|v| v.try_into())?; + .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 address = effective_address(offset, context.value_stack_mut().pop_as::()?)?; + let raw_address = context + .value_stack_mut() + .pop_as::() + .expect("Due to validation stack should contain value"); + let address = + effective_address( + offset, + raw_address, + )?; let m = context.module() .memory_by_index(DEFAULT_MEMORY_INDEX) .expect("Due to validation memory should exists"); - m.set(address, &stack_value)?; + m.set(address, &stack_value) + .map_err(|_| Trap::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"); @@ -611,8 +666,11 @@ impl<'a, E: Externals> Interpreter<'a, E> { Ok(InstructionOutcome::RunNextInstruction) } - fn run_grow_memory(&mut self, context: &mut FunctionContext) -> Result { - let pages: u32 = context.value_stack_mut().pop_as()?; + 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"); let m = context.module() .memory_by_index(DEFAULT_MEMORY_INDEX) .expect("Due to validation memory should exists"); @@ -625,7 +683,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) @@ -633,374 +691,378 @@ impl<'a, E: Externals> Interpreter<'a, E> { .map(|_| InstructionOutcome::RunNextInstruction) } - fn run_eqz(&mut self, context: &mut FunctionContext) -> Result + fn run_relop(&mut self, context: &mut FunctionContext, f: F) -> Result + where + RuntimeValue: TryInto, + F: FnOnce(T, T) -> bool, + { + let (left, right) = context + .value_stack_mut() + .pop_pair_as::() + .expect("Due to validation stack should contain pair of values"); + let v = if f(left, right) { + RuntimeValue::I32(1) + } else { + RuntimeValue::I32(0) + }; + context.value_stack_mut().push(v)?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_eqz(&mut self, context: &mut FunctionContext) -> Result where RuntimeValue: TryInto, T: PartialEq + Default { - context + let v = context .value_stack_mut() .pop_as::() - .map(|v| RuntimeValue::I32(if v == Default::default() { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) + .expect("Due to vaidation stack should contain value"); + 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 + 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 { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| RuntimeValue::I32(if left == right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) + self.run_relop(context, |left, right| left != right) } - fn run_ne(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: TryInto, T: PartialEq { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| RuntimeValue::I32(if left != right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_lt(&mut self, context: &mut FunctionContext) -> Result + fn run_lt(&mut self, context: &mut FunctionContext) -> Result where RuntimeValue: TryInto, T: PartialOrd + Display { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| RuntimeValue::I32(if left < right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) + self.run_relop(context, |left, right| left < right) } - fn run_gt(&mut self, context: &mut FunctionContext) -> Result + fn run_gt(&mut self, context: &mut FunctionContext) -> Result where RuntimeValue: TryInto, T: PartialOrd { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| RuntimeValue::I32(if left > right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) + self.run_relop(context, |left, right| left > right) } - fn run_lte(&mut self, context: &mut FunctionContext) -> Result + fn run_lte(&mut self, context: &mut FunctionContext) -> Result where RuntimeValue: TryInto, T: PartialOrd { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| RuntimeValue::I32(if left <= right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) + self.run_relop(context, |left, right| left <= right) } - fn run_gte(&mut self, context: &mut FunctionContext) -> Result + fn run_gte(&mut self, context: &mut FunctionContext) -> Result where RuntimeValue: TryInto, T: PartialOrd { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| RuntimeValue::I32(if left >= right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) + self.run_relop(context, |left, right| left >= right) } - fn run_clz(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Integer { - context + fn run_unop(&mut self, context: &mut FunctionContext, f: F) -> Result + where + F: FnOnce(T) -> U, + RuntimeValue: From + TryInto + { + let v = context .value_stack_mut() .pop_as::() - .map(|v| v.leading_zeros()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) + .map(|v| f(v)) + .expect("Due to vaidation stack should contain value"); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) } - fn run_ctz(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Integer { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.trailing_zeros()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) + 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_popcnt(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Integer { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.count_ones()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) + 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_add(&mut self, context: &mut FunctionContext) -> Result + 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 { - context + let (left, right) = context .value_stack_mut() .pop_pair_as::() - .map(|(left, right)| left.add(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) + .expect("Due to validation stack should contain pair of values"); + let v = left.add(right); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) } - fn run_sub(&mut self, context: &mut FunctionContext) -> Result + fn run_sub(&mut self, context: &mut FunctionContext) -> Result where RuntimeValue: From + TryInto, T: ArithmeticOps { - context + let (left, right) = context .value_stack_mut() .pop_pair_as::() - .map(|(left, right)| left.sub(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) + .expect("Due to validation stack should contain pair of values"); + let v = left.sub(right); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) } - fn run_mul(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: ArithmeticOps { - context + 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::() - .map(|(left, right)| left.mul(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) + .expect("Due to validation stack should contain pair of values"); + let v = left.mul(right); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) } - fn run_div(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: TransmuteInto + Display, U: ArithmeticOps + TransmuteInto { - context + 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::() - .map(|(left, right)| (left.transmute_into(), right.transmute_into())) - .map(|(left, right)| left.div(right))? + .expect("Due to validation stack should contain pair of values"); + let (left, right) = (left.transmute_into(), right.transmute_into()); + let v = left.div(right)?; + let v = v.transmute_into(); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + 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::() + .expect("Due to validation stack should contain pair of values"); + let (left, right) = (left.transmute_into(), right.transmute_into()); + let v = left.rem(right)?; + let v = v.transmute_into(); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + 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::() + .expect("Due to validation stack should contain pair of values"); + let v = left.bitand(right); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + 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::() + .expect("Due to validation stack should contain pair of values"); + let v = left.bitor(right); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + 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::() + .expect("Due to validation stack should contain pair of values"); + let v = left.bitxor(right); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + 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::() + .expect("Due to validation stack should contain pair of values"); + let v = left.shl(right & mask); + context.value_stack_mut().push(v.into())?; + 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 { + let (left, right) = context + .value_stack_mut() + .pop_pair_as::() + .expect("Due to validation stack should contain pair of values"); + let (left, right) = (left.transmute_into(), right.transmute_into()); + let v = left.shr(right & mask); + let v = v.transmute_into(); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + 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::() + .expect("Due to validation stack should contain pair of values"); + let v = left.rotl(right); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_rotr(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Integer + { + let (left, right) = context + .value_stack_mut() + .pop_pair_as::() + .expect("Due to validation stack should contain pair of values"); + let v = left.rotr(right); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + 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 + where + 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 + { + self.run_unop(context, |v| v.ceil()) + } + + 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 + { + self.run_unop(context, |v| v.trunc()) + } + + 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 + { + self.run_unop(context, |v| v.sqrt()) + } + + fn run_min(&mut self, context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float + { + let (left, right) = context + .value_stack_mut() + .pop_pair_as::() + .expect("Due to validation stack should contain pair of values"); + let v = left.min(right); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + 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::() + .expect("Due to validation stack should contain pair of values"); + let v = left.max(right); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + 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::() + .expect("Due to validation stack should contain pair of values"); + let v = left.copysign(right); + context.value_stack_mut().push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + 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, { + let v = context + .value_stack_mut() + .pop_as::() + .expect("Due to vaidation stack should contain value"); + + v.try_truncate_into() .map(|v| v.transmute_into()) .map(|v| context.value_stack_mut().push(v.into())) .map(|_| InstructionOutcome::RunNextInstruction) } - fn run_rem(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: TransmuteInto, U: Integer + TransmuteInto { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| (left.transmute_into(), right.transmute_into())) - .map(|(left, right)| left.rem(right))? - .map(|v| v.transmute_into()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_and(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From<::Output> + TryInto, T: ops::BitAnd { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.bitand(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_or(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From<::Output> + TryInto, T: ops::BitOr { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.bitor(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_xor(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From<::Output> + TryInto, T: ops::BitXor { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.bitxor(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_shl(&mut self, context: &mut FunctionContext, mask: T) -> Result - where RuntimeValue: From<>::Output> + TryInto, T: ops::Shl + ops::BitAnd { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.shl(right & mask)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| 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 { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| (left.transmute_into(), right.transmute_into())) - .map(|(left, right)| left.shr(right & mask)) - .map(|v| v.transmute_into()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_rotl(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Integer { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.rotl(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_rotr(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Integer { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.rotr(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_abs(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float { - context + fn run_extend(&mut self, context: &mut FunctionContext) -> Result + where + RuntimeValue: From + TryInto, T: ExtendInto, U: TransmuteInto + { + let v = context .value_stack_mut() .pop_as::() - .map(|v| v.abs()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) + .expect("Due to vaidation stack should contain value"); + + let v = v.extend_into().transmute_into(); + context.value_stack_mut().push(v.into())?; + + Ok(InstructionOutcome::RunNextInstruction) } - fn run_neg(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From<::Output> + TryInto, T: ops::Neg { - context + fn run_reinterpret(&mut self, context: &mut FunctionContext) -> Result + where + RuntimeValue: From, RuntimeValue: TryInto, T: TransmuteInto + { + let v = context .value_stack_mut() .pop_as::() - .map(|v| v.neg()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } + .expect("Due to vaidation stack should contain value"); - fn run_ceil(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.ceil()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } + let v = v.transmute_into(); + context.value_stack_mut().push(v.into())?; - fn run_floor(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.floor()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) + Ok(InstructionOutcome::RunNextInstruction) } +} - fn run_trunc(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.trunc()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_nearest(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.nearest()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_sqrt(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.sqrt()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_min(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.min(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_max(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.max(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_copysign(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.copysign(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_wrap(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: WrapInto { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.wrap_into()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_trunc_to_int(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: TryTruncateInto, U: TransmuteInto, { - context - .value_stack_mut() - .pop_as::() - .and_then(|v| v.try_truncate_into()) - .map(|v| v.transmute_into()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_extend(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From + TryInto, T: ExtendInto, U: TransmuteInto { - context - .value_stack_mut() - .pop_as::() - .map_err(Error::into) - .map(|v| v.extend_into()) - .map(|v| v.transmute_into()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_reinterpret(&mut self, context: &mut FunctionContext) -> Result - where RuntimeValue: From, RuntimeValue: TryInto, T: TransmuteInto { - context - .value_stack_mut() - .pop_as::() - .map(TransmuteInto::transmute_into) - .and_then(|val| context.value_stack_mut().push(val.into()).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) - } +/// Function execution context. +struct FunctionContext { + /// Is context initialized. + pub is_initialized: bool, + /// Internal function reference. + pub function: FuncRef, + pub module: ModuleRef, + /// Function return type. + pub return_type: BlockType, + /// Local variables. + pub locals: Vec, + /// Values stack. + pub value_stack: ValueStack, + /// Blocks frames stack. + pub frame_stack: StackWithLimit, + /// Current instruction position. + pub position: usize, } impl FunctionContext { @@ -1014,14 +1076,14 @@ impl FunctionContext { function: function, module: ModuleRef(module), return_type: signature.return_type().map(|vt| BlockType::Value(vt.into_elements())).unwrap_or(BlockType::NoResult), - value_stack: StackWithLimit::with_limit(value_stack_limit), + value_stack: ValueStack::with_limit(value_stack_limit), frame_stack: StackWithLimit::with_limit(frame_stack_limit), locals: args, position: 0, } } - 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"), @@ -1029,7 +1091,7 @@ impl FunctionContext { }; let function_type = function.signature(); let function_return_type = function_type.return_type().map(|vt| BlockType::Value(vt.into_elements())).unwrap_or(BlockType::NoResult); - let function_locals = prepare_function_args(function_type, &mut self.value_stack)?; + let function_locals = prepare_function_args(function_type, &mut self.value_stack); (function_locals, module, function_return_type) }; @@ -1038,7 +1100,7 @@ impl FunctionContext { function: function, module: ModuleRef(module), return_type: function_return_type, - value_stack: StackWithLimit::with_limit(self.value_stack.limit() - self.value_stack.len()), + value_stack: ValueStack::with_limit(self.value_stack.limit() - self.value_stack.len()), frame_stack: StackWithLimit::with_limit(self.frame_stack.limit() - self.frame_stack.len()), locals: function_locals, position: 0, @@ -1064,24 +1126,22 @@ impl FunctionContext { self.module.clone() } - pub fn set_local(&mut self, index: usize, value: RuntimeValue) -> Result { + pub fn set_local(&mut self, index: usize, value: RuntimeValue) { let l = self.locals.get_mut(index).expect("Due to validation local should exists"); - *l = value; - Ok(InstructionOutcome::RunNextInstruction) } - pub fn get_local(&mut self, index: usize) -> Result { - Ok(self.locals.get(index) + pub fn get_local(&mut self, index: usize) -> RuntimeValue { + self.locals.get(index) .cloned() - .expect("Due to validation local should exists")) + .expect("Due to validation local should exists") } - pub fn value_stack(&self) -> &StackWithLimit { + pub fn value_stack(&self) -> &ValueStack { &self.value_stack } - pub fn value_stack_mut(&mut self) -> &mut StackWithLimit { + pub fn value_stack_mut(&mut self) -> &mut ValueStack { &mut self.value_stack } @@ -1089,7 +1149,7 @@ impl FunctionContext { &self.frame_stack } - pub fn push_frame(&mut self, labels: &HashMap, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), Error> { + pub fn push_frame(&mut self, labels: &HashMap, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), Trap> { let begin_position = self.position; let branch_position = match frame_type { BlockFrameType::Function => usize::MAX, @@ -1107,31 +1167,35 @@ impl FunctionContext { BlockFrameType::Function => usize::MAX, _ => labels[&begin_position] + 1, }; - Ok(self.frame_stack.push(BlockFrame { + + self.frame_stack.push(BlockFrame { frame_type: frame_type, block_type: block_type, begin_position: begin_position, branch_position: branch_position, end_position: end_position, value_stack_len: self.value_stack.len(), - })?) + }).map_err(|_| Trap::StackOverflow)?; + + Ok(()) } - pub fn discard_frame(&mut self) -> Result<(), Error> { - Ok(self.frame_stack.pop().map(|_| ())?) + pub fn discard_frame(&mut self) { + 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<(), Error> { - let frame = self.frame_stack.pop()?; - if frame.value_stack_len > self.value_stack.len() { - return Err(Error::Stack("invalid stack len".into())); - } + pub fn pop_frame(&mut self, is_branch: bool) -> Result<(), Trap> { + let frame = self.frame_stack + .pop() + .expect("Due to validation frame stack shouldn't be empty"); + assert!(frame.value_stack_len <= self.value_stack.len(), "invalid stack len"); let frame_value = match frame.block_type { - BlockType::Value(_) if frame.frame_type != BlockFrameType::Loop || !is_branch => Some(self.value_stack.pop()?), + BlockType::Value(_) if frame.frame_type != BlockFrameType::Loop || !is_branch => + Some(self.value_stack.pop().expect("Due to validation, stack shouldn't be empty")), _ => None, }; - self.value_stack.resize(frame.value_stack_len, RuntimeValue::I32(0)); + self.value_stack.resize(frame.value_stack_len); self.position = if is_branch { frame.branch_position } else { frame.end_position }; if let Some(frame_value) = frame_value { self.value_stack.push(frame_value)?; @@ -1147,33 +1211,62 @@ 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(Error::Memory(format!("invalid memory access: {} + {}", offset, address))), + None => Err(Trap::MemoryAccessOutOfBounds), Some(address) => Ok(address), } } -pub fn prepare_function_args(signature: &Signature, caller_stack: &mut StackWithLimit) -> Result, Error> { - let mut args = signature.params().iter().cloned().rev().map(|expected_type| { - let param_value = caller_stack.pop()?; +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::>(); + args.reverse(); + check_function_args(signature, &args).expect("Due to validation arguments should match"); + args +} + +pub fn check_function_args(signature: &Signature, args: &[RuntimeValue]) -> Result<(), Error> { + if signature.params().len() != args.len() { + return Err( + Error::Function( + format!( + "not enough arguments, given {} but expected: {}", + args.len(), + signature.params().len(), + ) + ) + ); + } + + signature.params().iter().cloned().zip(args).map(|(expected_type, param_value)| { let actual_type = param_value.value_type(); if actual_type != expected_type { return Err(Error::Function(format!("invalid parameter type {:?} when expected {:?}", actual_type, expected_type))); } - - Ok(param_value) + Ok(()) }).collect::, _>>()?; - args.reverse(); - Ok(args) + + Ok(()) } -impl StackWithLimit { +struct ValueStack { + stack_with_limit: StackWithLimit, +} + +impl ValueStack { + fn with_limit(limit: usize) -> ValueStack { + ValueStack { + stack_with_limit: StackWithLimit::with_limit(limit), + } + } + fn pop_as(&mut self) -> Result where RuntimeValue: TryInto, { - let value = self.pop()?; + let value = self.stack_with_limit.pop()?; TryInto::try_into(value) } @@ -1187,9 +1280,34 @@ impl StackWithLimit { } fn pop_triple(&mut self) -> Result<(RuntimeValue, RuntimeValue, RuntimeValue), Error> { - let right = self.pop()?; - let mid = self.pop()?; - let left = self.pop()?; + 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(&mut self) -> Result { + self.stack_with_limit.pop().map_err(|e| Error::Stack(e.to_string())) + } + + fn push(&mut self, value: RuntimeValue) -> Result<(), Trap> { + self.stack_with_limit.push(value) + .map_err(|_| Trap::StackOverflow) + } + + fn resize(&mut self, new_len: usize) { + self.stack_with_limit.resize(new_len, RuntimeValue::I32(0)); + } + + fn len(&self) -> usize { + self.stack_with_limit.len() + } + + fn limit(&self) -> usize { + self.stack_with_limit.limit() + } + + fn top(&self) -> Result<&RuntimeValue, Error> { + self.stack_with_limit.top().map_err(|e| Error::Stack(e.to_string())) + } } diff --git a/src/table.rs b/src/table.rs index eac4b0b..4c47da3 100644 --- a/src/table.rs +++ b/src/table.rs @@ -107,7 +107,7 @@ impl TableInstance { } /// Get the specific value in the table - pub fn get(&self, offset: u32) -> Result { + pub fn get(&self, offset: u32) -> Result, Error> { let buffer = self.buffer.borrow(); let buffer_len = buffer.len(); let table_elem = buffer.get(offset as usize).cloned().ok_or_else(|| @@ -117,10 +117,7 @@ impl TableInstance { buffer_len )), )?; - Ok(table_elem.ok_or_else(|| Error::Table(format!( - "trying to read uninitialized element on index {}", - offset - )))?) + Ok(table_elem) } /// Set the table element to the specified function. diff --git a/src/tests/host.rs b/src/tests/host.rs index 27f7a72..02cb86b 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, Module, TableDescriptor, MemoryDescriptor, + RuntimeValue, RuntimeArgs, Module, TableDescriptor, MemoryDescriptor, Trap, }; use types::ValueType; use wabt::wat2wasm; @@ -79,23 +79,23 @@ impl Externals for TestHost { &mut self, index: usize, args: RuntimeArgs, - ) -> Result, Error> { + ) -> Result, Trap> { match index { SUB_FUNC_INDEX => { - let a: i32 = args.nth(0)?; - let b: i32 = args.nth(1)?; + let a: i32 = args.nth(0); + let b: i32 = args.nth(1); let result: RuntimeValue = (a - b).into(); Ok(Some(result)) } ERR_FUNC_INDEX => { - let error_code: u32 = args.nth(0)?; + let error_code: u32 = args.nth(0); let error = HostErrorWithCode { error_code }; - Err(Error::Host(Box::new(error))) + Err(Trap::Host(Box::new(error))) } INC_MEM_FUNC_INDEX => { - let ptr: u32 = args.nth(0)?; + let ptr: u32 = args.nth(0); let memory = self.memory.as_ref().expect( "Function 'inc_mem' expects attached memory", @@ -108,7 +108,7 @@ impl Externals for TestHost { Ok(None) } GET_MEM_FUNC_INDEX => { - let ptr: u32 = args.nth(0)?; + let ptr: u32 = args.nth(0); let memory = self.memory.as_ref().expect( "Function 'get_mem' expects attached memory", @@ -119,7 +119,7 @@ impl Externals for TestHost { Ok(Some(RuntimeValue::I32(buf[0] as i32))) } RECURSE_FUNC_INDEX => { - let val = args.nth_value(0)?; + let val = args.nth_value_checked(0).expect("Exactly one argument expected"); let instance = self.instance .as_ref() @@ -131,7 +131,7 @@ impl Externals for TestHost { .expect("expected to be Some"); if val.value_type() != result.value_type() { - return Err(Error::Host(Box::new(HostErrorWithCode { error_code: 123 }))); + return Err(Trap::Host(Box::new(HostErrorWithCode { error_code: 123 }))); } Ok(Some(result)) } @@ -263,7 +263,7 @@ fn host_err() { ); let host_error: Box = match error { - Error::Host(err) => err, + Error::Trap(Trap::Host(err)) => err, err => panic!("Unexpected error {:?}", err), }; @@ -464,10 +464,10 @@ fn defer_providing_externals() { &mut self, index: usize, args: RuntimeArgs, - ) -> Result, Error> { + ) -> Result, Trap> { match index { INC_FUNC_INDEX => { - let a = args.nth::(0)?; + let a = args.nth::(0); *self.acc += a; Ok(None) } @@ -528,7 +528,7 @@ fn two_envs_one_externals() { &mut self, index: usize, _args: RuntimeArgs, - ) -> Result, Error> { + ) -> Result, Trap> { match index { PRIVILEGED_FUNC_INDEX => { println!("privileged!"); @@ -648,7 +648,7 @@ fn dynamically_add_host_func() { &mut self, index: usize, _args: RuntimeArgs, - ) -> Result, Error> { + ) -> Result, Trap> { match index { ADD_FUNC_FUNC_INDEX => { // Allocate indicies for the new function. @@ -661,7 +661,8 @@ fn dynamically_add_host_func() { Signature::new(&[][..], Some(ValueType::I32)), host_func_index as usize, ); - self.table.set(table_index, Some(added_func))?; + self.table.set(table_index, Some(added_func)) + .map_err(|_| Trap::TableAccessOutOfBounds)?; Ok(Some(RuntimeValue::I32(table_index as i32))) } diff --git a/src/value.rs b/src/value.rs index 49dd702..4d1bdc6 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,7 +1,7 @@ use std::{i32, i64, u32, u64, f32}; use std::io; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use Error; +use {Error, Trap}; use parity_wasm::elements::ValueType; @@ -53,7 +53,7 @@ pub trait LittleEndianConvert where Self: Sized { /// Convert to little endian buffer. fn into_little_endian(self) -> Vec; /// Convert from little endian buffer. - fn from_little_endian(buffer: Vec) -> Result; + fn from_little_endian(buffer: &[u8]) -> Result; } /// Arithmetic operations. @@ -65,7 +65,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 +81,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. @@ -263,19 +263,19 @@ impl_wrap_into!(f64, f32); macro_rules! impl_try_truncate_into { ($from: ident, $into: ident) => { - impl TryTruncateInto<$into, Error> for $from { - fn try_truncate_into(self) -> Result<$into, Error> { + impl TryTruncateInto<$into, Trap> for $from { + fn try_truncate_into(self) -> Result<$into, Trap> { // 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(Error::Value("invalid float value for this operation".into())); + return Err(Trap::InvalidConversionToInt); } // range check let result = self as $into; if result as $from != self.trunc() { - return Err(Error::Value("invalid float value for this operation".into())); + return Err(Trap::InvalidConversionToInt); } Ok(self as $into) @@ -376,7 +376,7 @@ impl LittleEndianConvert for i8 { vec![self as u8] } - fn from_little_endian(buffer: Vec) -> Result { + 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())) @@ -388,7 +388,7 @@ impl LittleEndianConvert for u8 { vec![self] } - fn from_little_endian(buffer: Vec) -> Result { + fn from_little_endian(buffer: &[u8]) -> Result { buffer.get(0) .cloned() .ok_or_else(|| Error::Value("invalid little endian buffer".into())) @@ -403,7 +403,7 @@ impl LittleEndianConvert for i16 { vec } - fn from_little_endian(buffer: Vec) -> Result { + fn from_little_endian(buffer: &[u8]) -> Result { io::Cursor::new(buffer).read_i16::() .map_err(|e| Error::Value(e.to_string())) } @@ -417,7 +417,7 @@ impl LittleEndianConvert for u16 { vec } - fn from_little_endian(buffer: Vec) -> Result { + fn from_little_endian(buffer: &[u8]) -> Result { io::Cursor::new(buffer).read_u16::() .map_err(|e| Error::Value(e.to_string())) } @@ -431,7 +431,7 @@ impl LittleEndianConvert for i32 { vec } - fn from_little_endian(buffer: Vec) -> Result { + fn from_little_endian(buffer: &[u8]) -> Result { io::Cursor::new(buffer).read_i32::() .map_err(|e| Error::Value(e.to_string())) } @@ -445,7 +445,7 @@ impl LittleEndianConvert for u32 { vec } - fn from_little_endian(buffer: Vec) -> Result { + fn from_little_endian(buffer: &[u8]) -> Result { io::Cursor::new(buffer).read_u32::() .map_err(|e| Error::Value(e.to_string())) } @@ -459,7 +459,7 @@ impl LittleEndianConvert for i64 { vec } - fn from_little_endian(buffer: Vec) -> Result { + fn from_little_endian(buffer: &[u8]) -> Result { io::Cursor::new(buffer).read_i64::() .map_err(|e| Error::Value(e.to_string())) } @@ -473,7 +473,7 @@ impl LittleEndianConvert for f32 { vec } - fn from_little_endian(buffer: Vec) -> Result { + 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())) @@ -488,7 +488,7 @@ impl LittleEndianConvert for f64 { vec } - fn from_little_endian(buffer: Vec) -> Result { + 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())) @@ -537,12 +537,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, Error> { - if other == 0 { Err(Error::Value("Division by zero".to_owned())) } + fn div(self, other: $type) -> Result<$type, Trap> { + if other == 0 { + Err(Trap::DivisionByZero) + } else { let (result, overflow) = self.overflowing_div(other); if overflow { - return Err(Error::Value("Attempt to divide with overflow".to_owned())); + Err(Trap::InvalidConversionToInt) } else { Ok(result) } @@ -563,7 +565,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, Error> { Ok(self / other) } + fn div(self, other: $type) -> Result<$type, Trap> { Ok(self / other) } } } } @@ -579,8 +581,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, Error> { - if other == 0 { Err(Error::Value("Division by zero".to_owned())) } + fn rem(self, other: $type) -> Result<$type, Trap> { + if other == 0 { Err(Trap::DivisionByZero) } else { Ok(self.wrapping_rem(other)) } } }