diff --git a/examples/tictactoe.rs b/examples/tictactoe.rs index 76760fc..203d185 100644 --- a/examples/tictactoe.rs +++ b/examples/tictactoe.rs @@ -6,8 +6,9 @@ use std::fmt; use std::fs::File; use wasmi::{ Error as InterpreterError, ModuleInstance, ModuleRef, - Externals, RuntimeValue, FuncRef, TryInto, ModuleImportResolver, + Externals, RuntimeValue, FuncRef, ModuleImportResolver, FuncInstance, HostError, ImportsBuilder, Signature, ValueType, + RuntimeArgs, }; #[derive(Debug)] @@ -147,16 +148,16 @@ impl<'a> Externals for Runtime<'a> { fn invoke_index( &mut self, index: usize, - args: &[RuntimeValue], + args: RuntimeArgs, ) -> Result, InterpreterError> { match index { SET_FUNC_INDEX => { - let idx: i32 = args[0].try_into().unwrap(); + let idx: i32 = args.nth(0)?; self.game.set(idx, self.player)?; Ok(None) } GET_FUNC_INDEX => { - let idx: i32 = args[0].try_into().unwrap(); + 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 e85f833..a74b526 100644 --- a/spec/src/run.rs +++ b/spec/src/run.rs @@ -13,8 +13,8 @@ use wasmi::{ GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryInstance, MemoryRef, ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeValue, TableInstance, TableRef, ValueType, - load_from_buffer, LoadedModule, Signature, MemoryDescriptor, - TableDescriptor, GlobalDescriptor, FuncInstance, + load_from_buffer, LoadedModule, Signature, MemoryDescriptor, + TableDescriptor, GlobalDescriptor, FuncInstance, RuntimeArgs, }; #[derive(Debug)] @@ -57,7 +57,7 @@ impl Externals for SpecModule { fn invoke_index( &mut self, index: usize, - args: &[RuntimeValue], + args: RuntimeArgs, ) -> Result, InterpreterError> { match index { PRINT_FUNC_INDEX => { diff --git a/src/func.rs b/src/func.rs index 7e1c443..7370124 100644 --- a/src/func.rs +++ b/src/func.rs @@ -43,7 +43,7 @@ impl fmt::Debug for FuncInstance { ref signature, .. } => { - // We can't write description of self.module here, because it generate + // We can't write description of self.module here, because it generate // debug string for function instances and this will lead to infinite loop. write!( f, @@ -132,7 +132,7 @@ impl FuncInstance { let mut interpreter = Interpreter::new(externals); interpreter.run_function(ctx) } - InvokeKind::Host(host_func, args) => externals.invoke_index(host_func, args), + InvokeKind::Host(host_func, args) => externals.invoke_index(host_func, args.into()), } } } diff --git a/src/host.rs b/src/host.rs index ab686ef..09b6c85 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,7 +1,38 @@ use std::any::TypeId; -use value::RuntimeValue; +use value::{RuntimeValue, TryInto}; use Error; +/// Safe wrapper for list of arguments +#[derive(Debug)] +pub struct RuntimeArgs<'a>(&'a [RuntimeValue]); + +impl<'a> From<&'a [RuntimeValue]> for RuntimeArgs<'a> { + fn from(inner: &'a [RuntimeValue]) -> Self { + RuntimeArgs(inner) + } +} + +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 as a runtime value by index `idx` returning error is not enough arguments + pub fn nth_value(&self, idx: usize) -> Result { + if self.0.len() <= idx { + return Err(Error::Value("Invalid argument index".to_owned())); + } + Ok(self.0[idx]) + } + + /// Total number of arguments + pub fn len(&self) -> usize { + self.0.len() + } +} + /// Custom user error. pub trait HostError: 'static + ::std::fmt::Display + ::std::fmt::Debug { #[doc(hidden)] @@ -35,7 +66,7 @@ pub trait Externals { fn invoke_index( &mut self, index: usize, - args: &[RuntimeValue], + args: RuntimeArgs, ) -> Result, Error>; } @@ -45,8 +76,28 @@ impl Externals for NopExternals { fn invoke_index( &mut self, _index: usize, - _args: &[RuntimeValue], + _args: RuntimeArgs, ) -> Result, Error> { Err(Error::Trap("invoke index on no-op externals".into())) } } + +#[cfg(test)] +mod tests { + + use value::RuntimeValue; + use super::RuntimeArgs; + + #[test] + fn i32_runtime_args() { + let args: RuntimeArgs = (&[RuntimeValue::I32(0)][..]).into(); + let val: i32 = args.nth(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()); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 3523d49..d763009 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -130,7 +130,7 @@ mod tests; pub use self::memory::{MemoryInstance, MemoryRef}; pub use self::table::{TableInstance, TableRef}; pub use self::value::{RuntimeValue, TryInto}; -pub use self::host::{Externals, NopExternals, HostError}; +pub use self::host::{Externals, NopExternals, HostError, RuntimeArgs}; pub use self::imports::{ModuleImportResolver, ImportResolver, ImportsBuilder}; pub use self::module::{ModuleInstance, ModuleRef, ExternVal, NotStartedModuleRef}; pub use self::global::{GlobalInstance, GlobalRef}; diff --git a/src/tests/host.rs b/src/tests/host.rs index c96a2d9..7795753 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, TryInto, LoadedModule, load_from_buffer, TableDescriptor, MemoryDescriptor, + RuntimeValue, RuntimeArgs, LoadedModule, load_from_buffer, TableDescriptor, MemoryDescriptor, }; use types::ValueType; use wabt::wat2wasm; @@ -78,25 +78,24 @@ impl Externals for TestHost { fn invoke_index( &mut self, index: usize, - args: &[RuntimeValue], + args: RuntimeArgs, ) -> Result, Error> { - let mut args = args.iter().cloned(); match index { SUB_FUNC_INDEX => { - let a: i32 = args.next().unwrap().try_into().unwrap(); - let b: i32 = args.next().unwrap().try_into().unwrap(); + 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.next().unwrap().try_into().unwrap(); + let error_code = args.nth::(0)? as u32; let error = HostErrorWithCode { error_code }; Err(Error::Host(Box::new(error))) } INC_MEM_FUNC_INDEX => { - let ptr: u32 = args.next().unwrap().try_into().unwrap(); + let ptr = args.nth::(0)? as u32; let memory = self.memory.as_ref().expect( "Function 'inc_mem' expects attached memory", @@ -109,7 +108,7 @@ impl Externals for TestHost { Ok(None) } GET_MEM_FUNC_INDEX => { - let ptr: u32 = args.next().unwrap().try_into().unwrap(); + let ptr = args.nth::(0)? as u32; let memory = self.memory.as_ref().expect( "Function 'get_mem' expects attached memory", @@ -120,7 +119,7 @@ impl Externals for TestHost { Ok(Some(RuntimeValue::I32(buf[0] as i32))) } RECURSE_FUNC_INDEX => { - let val: RuntimeValue = args.next().unwrap(); + let val = args.nth_value(0)?; let instance = self.instance .as_ref() @@ -463,12 +462,11 @@ fn defer_providing_externals() { fn invoke_index( &mut self, index: usize, - args: &[RuntimeValue], + args: RuntimeArgs, ) -> Result, Error> { match index { INC_FUNC_INDEX => { - let mut args = args.iter().cloned(); - let a: u32 = args.next().unwrap().try_into().unwrap(); + let a = args.nth::(0)? as u32; *self.acc += a; Ok(None) } @@ -528,7 +526,7 @@ fn two_envs_one_externals() { fn invoke_index( &mut self, index: usize, - _args: &[RuntimeValue], + _args: RuntimeArgs, ) -> Result, Error> { match index { PRIVILEGED_FUNC_INDEX => { @@ -648,7 +646,7 @@ fn dynamically_add_host_func() { fn invoke_index( &mut self, index: usize, - _args: &[RuntimeValue], + _args: RuntimeArgs, ) -> Result, Error> { match index { ADD_FUNC_FUNC_INDEX => {