From 2b80a98b7fca5d50535d4d33d77b550c6e38c408 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Mon, 5 Feb 2018 19:05:08 +0300 Subject: [PATCH] Replace Trap with struct. Enum is now TrapKind --- examples/tictactoe.rs | 4 +-- spec/src/run.rs | 8 +++--- spec/src/test.rs | 2 +- src/func.rs | 6 ++--- src/host.rs | 24 ++++++++--------- src/lib.rs | 63 +++++++++++++++++++++++++++++++++++-------- src/module.rs | 6 ++--- src/runner.rs | 16 +++++------ src/tests/host.rs | 23 +++++++--------- 9 files changed, 94 insertions(+), 58 deletions(-) diff --git a/examples/tictactoe.rs b/examples/tictactoe.rs index 1fc561f..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, TrapKind, + RuntimeArgs, Trap, }; #[derive(Debug)] @@ -149,7 +149,7 @@ impl<'a> Externals for Runtime<'a> { &mut self, index: usize, args: RuntimeArgs, - ) -> Result, TrapKind> { + ) -> Result, Trap> { match index { SET_FUNC_INDEX => { let idx: i32 = args.nth(0); diff --git a/spec/src/run.rs b/spec/src/run.rs index 04eebe7..7f56ce7 100644 --- a/spec/src/run.rs +++ b/spec/src/run.rs @@ -13,14 +13,14 @@ use wasmi::{ GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryInstance, MemoryRef, ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeValue, TableInstance, TableRef, ValueType, - Module, Signature, MemoryDescriptor, TrapKind, + Module, Signature, MemoryDescriptor, Trap, TableDescriptor, GlobalDescriptor, FuncInstance, RuntimeArgs, }; #[derive(Debug)] enum Error { Load(String), - Start(TrapKind), + Start(Trap), Interpreter(InterpreterError), } @@ -59,7 +59,7 @@ impl Externals for SpecModule { &mut self, index: usize, args: RuntimeArgs, - ) -> Result, TrapKind> { + ) -> Result, Trap> { match index { PRINT_FUNC_INDEX => { println!("print: {:?}", args); @@ -495,7 +495,7 @@ pub fn spec(name: &str) { Err(e) => println!("assert_exhaustion at line {} - success ({:?})", line, e), } } - &test::Command::AssertTrapKind { + &test::Command::AssertTrap { line, ref action, .. } => { let result = run_action(&mut spec_driver, action); diff --git a/spec/src/test.rs b/spec/src/test.rs index a1d60fc..8c4a2b3 100644 --- a/spec/src/test.rs +++ b/spec/src/test.rs @@ -49,7 +49,7 @@ pub enum Command { action: Action, }, #[serde(rename = "assert_trap")] - AssertTrapKind { + AssertTrap { line: u64, action: Action, text: String, diff --git a/src/func.rs b/src/func.rs index aef0505..90566f8 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 {TrapKind, Signature}; +use {Trap, TrapKind, Signature}; use host::Externals; use runner::{check_function_args, Interpreter}; use value::RuntimeValue; @@ -140,8 +140,8 @@ impl FuncInstance { func: &FuncRef, args: &[RuntimeValue], externals: &mut E, - ) -> Result, TrapKind> { - check_function_args(func.signature(), &args).map_err(|_| TrapKind::UnexpectedSignature)?; + ) -> Result, Trap> { + check_function_args(func.signature(), &args).map_err(|_| Trap::new(TrapKind::UnexpectedSignature))?; match *func.as_internal() { FuncInstanceInternal::Internal { .. } => { let mut interpreter = Interpreter::new(externals); diff --git a/src/host.rs b/src/host.rs index fb2934e..580be3c 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,6 +1,6 @@ use std::any::TypeId; use value::{RuntimeValue, TryInto}; -use TrapKind; +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(|_| TrapKind::UnexpectedSignature)?) + pub fn nth_checked(&self, idx: usize) -> Result where RuntimeValue: TryInto { + Ok(self.nth_value_checked(idx)?.try_into().map_err(|_| Trap::new(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(TrapKind::UnexpectedSignature); + return Err(Trap::new(TrapKind::UnexpectedSignature)); } Ok(self.0[idx]) } @@ -122,7 +122,7 @@ impl HostError { /// ```rust /// use wasmi::{ /// Externals, RuntimeValue, RuntimeArgs, Error, ModuleImportResolver, -/// FuncRef, ValueType, Signature, FuncInstance, TrapKind, +/// FuncRef, ValueType, Signature, FuncInstance, Trap, /// }; /// /// struct HostExternals { @@ -136,11 +136,11 @@ impl HostError { /// &mut self, /// index: usize, /// args: RuntimeArgs, -/// ) -> Result, TrapKind> { +/// ) -> 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))) @@ -192,7 +192,7 @@ pub trait Externals { &mut self, index: usize, args: RuntimeArgs, - ) -> Result, TrapKind>; + ) -> Result, Trap>; } /// Implementation of [`Externals`] that just traps on [`invoke_index`]. @@ -206,8 +206,8 @@ impl Externals for NopExternals { &mut self, _index: usize, _args: RuntimeArgs, - ) -> Result, TrapKind> { - Err(TrapKind::Unreachable) + ) -> Result, Trap> { + Err(Trap::new(TrapKind::Unreachable)) } } diff --git a/src/lib.rs b/src/lib.rs index a552cd9..239e49a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,6 +105,27 @@ use std::fmt; use std::error; use std::collections::HashMap; +/// 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 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. /// /// Under some conditions, wasm execution may produce a `TrapKind`, which immediately aborts execution. @@ -201,12 +222,33 @@ pub enum Error { Global(String), /// Value-level error. Value(String), - /// TrapKind. - TrapKind(TrapKind), + /// Trap. + Trap(Trap), /// Custom embedder 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 { @@ -217,7 +259,7 @@ impl Into for Error { Error::Memory(s) => s, Error::Global(s) => s, Error::Value(s) => s, - Error::TrapKind(s) => format!("trap: {:?}", s), + Error::Trap(s) => format!("trap: {:?}", s), Error::Host(e) => format!("user: {}", e), } } @@ -233,7 +275,7 @@ impl fmt::Display for Error { Error::Memory(ref s) => write!(f, "Memory: {}", s), Error::Global(ref s) => write!(f, "Global: {}", s), Error::Value(ref s) => write!(f, "Value: {}", s), - Error::TrapKind(ref s) => write!(f, "TrapKind: {:?}", s), + Error::Trap(ref s) => write!(f, "TrapKind: {:?}", s), Error::Host(ref e) => write!(f, "User: {}", e), } } @@ -249,28 +291,27 @@ impl error::Error for Error { Error::Memory(ref s) => s, Error::Global(ref s) => s, Error::Value(ref s) => s, - Error::TrapKind(_) => "TrapKind", + Error::Trap(_) => "TrapKind", Error::Host(_) => "Host error", } } } - impl From for Error where U: host::HostError + Sized { fn from(e: U) -> Self { Error::Host(Box::new(e)) } } -impl From for TrapKind where U: host::HostError + Sized { +impl From for Trap where U: host::HostError + Sized { fn from(e: U) -> Self { - TrapKind::Host(Box::new(e)) + Trap::new(TrapKind::Host(Box::new(e))) } } -impl From for Error { - fn from(e: TrapKind) -> Error { - Error::TrapKind(e) +impl From for Error { + fn from(e: Trap) -> Error { + Error::Trap(e) } } diff --git a/src/module.rs b/src/module.rs index 5c1b327..b7b3ed7 100644 --- a/src/module.rs +++ b/src/module.rs @@ -1,5 +1,5 @@ use runner::check_function_args; -use TrapKind; +use Trap; use std::rc::Rc; use std::cell::RefCell; use std::fmt; @@ -594,7 +594,7 @@ impl ModuleInstance { check_function_args(func_instance.signature(), &args)?; FuncInstance::invoke(&func_instance, args, externals) - .map_err(|t| Error::TrapKind(t)) + .map_err(|t| Error::Trap(t)) } /// Find export by a name. @@ -647,7 +647,7 @@ impl<'a> NotStartedModuleRef<'a> { /// # Errors /// /// Returns `Err` if start function traps. - 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 0bf8432..99f40eb 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, TrapKind, Signature}; +use {Error, Trap, TrapKind, Signature}; use module::ModuleRef; use func::{FuncRef, FuncInstance, FuncInstanceInternal}; use value::{ @@ -51,7 +51,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { } } - pub fn start_execution(&mut self, func: &FuncRef, args: &[RuntimeValue]) -> Result, TrapKind> { + pub fn start_execution(&mut self, func: &FuncRef, args: &[RuntimeValue]) -> Result, Trap> { let context = FunctionContext::new( func.clone(), DEFAULT_VALUE_STACK_LIMIT, @@ -66,7 +66,7 @@ impl<'a, E: Externals> Interpreter<'a, E> { self.run_interpreter_loop(&mut function_stack) } - fn run_interpreter_loop(&mut self, function_stack: &mut VecDeque) -> Result, TrapKind> { + 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(); @@ -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); } diff --git a/src/tests/host.rs b/src/tests/host.rs index 0013cc5..ad00dc1 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, TrapKind, + RuntimeValue, RuntimeArgs, TableDescriptor, MemoryDescriptor, Trap, TrapKind, }; use types::ValueType; use super::parse_wat; @@ -79,7 +79,7 @@ impl Externals for TestHost { &mut self, index: usize, args: RuntimeArgs, - ) -> Result, TrapKind> { + ) -> Result, Trap> { match index { SUB_FUNC_INDEX => { let a: i32 = args.nth(0); @@ -92,7 +92,7 @@ impl Externals for TestHost { ERR_FUNC_INDEX => { let error_code: u32 = args.nth(0); let error = HostErrorWithCode { error_code }; - Err(TrapKind::Host(Box::new(error))) + Err(Trap::new(TrapKind::Host(Box::new(error)))) } 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(TrapKind::Host(Box::new(HostErrorWithCode { error_code: 123 }))); + return Err(Trap::new(TrapKind::Host(Box::new(HostErrorWithCode { error_code: 123 })))); } Ok(Some(result)) } @@ -257,12 +257,7 @@ fn host_err() { "`test` expected to return error", ); - let host_error: Box = match error { - Error::TrapKind(TrapKind::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); @@ -459,7 +454,7 @@ fn defer_providing_externals() { &mut self, index: usize, args: RuntimeArgs, - ) -> Result, TrapKind> { + ) -> Result, Trap> { match index { INC_FUNC_INDEX => { let a = args.nth::(0); @@ -523,7 +518,7 @@ fn two_envs_one_externals() { &mut self, index: usize, _args: RuntimeArgs, - ) -> Result, TrapKind> { + ) -> Result, Trap> { match index { PRIVILEGED_FUNC_INDEX => { println!("privileged!"); @@ -643,7 +638,7 @@ fn dynamically_add_host_func() { &mut self, index: usize, _args: RuntimeArgs, - ) -> Result, TrapKind> { + ) -> Result, Trap> { match index { ADD_FUNC_FUNC_INDEX => { // Allocate indicies for the new function. @@ -657,7 +652,7 @@ fn dynamically_add_host_func() { host_func_index as usize, ); self.table.set(table_index, Some(added_func)) - .map_err(|_| TrapKind::TableAccessOutOfBounds)?; + .map_err(|_| Trap::new(TrapKind::TableAccessOutOfBounds))?; Ok(Some(RuntimeValue::I32(table_index as i32))) }