diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 2471561..c85390b 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,11 +1,35 @@ //! WebAssembly interpreter module. +use std::any::TypeId; + /// Custom user error. -pub trait UserError: 'static + ::std::fmt::Display + ::std::fmt::Debug + Clone + PartialEq { +pub trait UserError: 'static + ::std::fmt::Display + ::std::fmt::Debug { + #[doc(hidden)] + fn __private_get_type_id__(&self) -> TypeId { + TypeId::of::() + } +} + +impl UserError { + pub fn downcast_ref(&self) -> Option<&T> { + if self.__private_get_type_id__() == TypeId::of::() { + unsafe { Some(&*(self as *const UserError as *const T)) } + } else { + None + } + } + + pub fn downcast_mut(&mut self) -> Option<&mut T> { + if self.__private_get_type_id__() == TypeId::of::() { + unsafe { Some(&mut *(self as *mut UserError as *mut T)) } + } else { + None + } + } } /// Internal interpreter error. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug)] pub enum Error where E: UserError { /// Program-level error. Program(String), @@ -38,7 +62,8 @@ pub enum Error where E: UserError { /// Trap. Trap(String), /// Custom user error. - User(E), + User(Box), + Other(E), } impl Into for Error where E: UserError { @@ -60,6 +85,7 @@ impl Into for Error where E: UserError { Error::Native(s) => s, Error::Trap(s) => format!("trap: {}", s), Error::User(e) => format!("user: {}", e), + Error::Other(_) => panic!("TODO: Remove this arm "), } } } @@ -83,6 +109,7 @@ impl ::std::fmt::Display for Error where E: UserError { Error::Native(ref s) => write!(f, "Native: {}", s), Error::Trap(ref s) => write!(f, "Trap: {}", s), Error::User(ref e) => write!(f, "User: {}", e), + Error::Other(_) => panic!("TODO: Remove this arm "), } } } @@ -99,7 +126,7 @@ impl ::std::fmt::Display for DummyUserError { impl From for Error where U: UserError + Sized { fn from(e: U) -> Self { - Error::User(e) + Error::User(Box::new(e)) } } diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index 69f641a..f3aa332 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -121,7 +121,6 @@ pub struct CallerContext<'a, E: 'a + UserError> { } /// Internal function reference. -#[derive(Clone)] pub struct InternalFunctionReference<'a, E: UserError> { /// Module reference. pub module: Arc + 'a>, @@ -129,6 +128,16 @@ pub struct InternalFunctionReference<'a, E: UserError> { pub internal_index: u32, } +// TODO: This impl should be removed once `E` not needed anymore. +impl<'a, E> Clone for InternalFunctionReference<'a, E> where E: UserError { + fn clone(&self) -> InternalFunctionReference<'a, E> { + InternalFunctionReference { + module: Arc::clone(&self.module), + internal_index: self.internal_index, + } + } +} + impl<'a, E> fmt::Debug for InternalFunctionReference<'a, E> where E: UserError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "InternalFunctionReference") diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs index bca315e..7235e5f 100644 --- a/src/interpreter/tests/basics.rs +++ b/src/interpreter/tests/basics.rs @@ -186,7 +186,7 @@ impl UserFunctionExecutor for FunctionExecutor { Ok(Some(RuntimeValue::I32(diff as i32))) }, "err" => { - Err(Error::User(UserErrorWithCode { error_code: 777 })) + Err(Error::User(Box::new(UserErrorWithCode { error_code: 777 }))) }, _ => Err(Error::Trap("not implemented".into()).into()), } @@ -401,10 +401,29 @@ fn native_custom_error() { .build(); let module_instance = program.add_module("main", module, Some(¶ms.externals)).unwrap(); - assert_eq!(module_instance.execute_index(0, params.clone().add_argument(RuntimeValue::I32(7)).add_argument(RuntimeValue::I32(0))), - Err(Error::User(UserErrorWithCode { error_code: 777 }))); - assert_eq!(module_instance.execute_index(1, params.clone().add_argument(RuntimeValue::I32(7)).add_argument(RuntimeValue::I32(0))), - Err(Error::User(UserErrorWithCode { error_code: 777 }))); + let user_error1 = match module_instance.execute_index( + 0, + params + .clone() + .add_argument(RuntimeValue::I32(7)) + .add_argument(RuntimeValue::I32(0)), + ) { + Err(Error::User(user_error)) => user_error, + result => panic!("Unexpected result {:?}", result), + }; + assert_eq!(user_error1.downcast_ref::().unwrap(), &UserErrorWithCode { error_code: 777 }); + + let user_error2 = match module_instance.execute_index( + 0, + params + .clone() + .add_argument(RuntimeValue::I32(7)) + .add_argument(RuntimeValue::I32(0)), + ) { + Err(Error::User(user_error)) => user_error, + result => panic!("Unexpected result {:?}", result), + }; + assert_eq!(user_error2.downcast_ref::().unwrap(), &UserErrorWithCode { error_code: 777 }); } #[test] @@ -434,21 +453,21 @@ fn env_native_export_entry_type_check() { assert!(native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![ValueType::I32, ValueType::I32], Some(ValueType::I32))))).is_ok()); match native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![], Some(ValueType::I32))))) { Err(Error::Validation(_)) => { }, - result => panic!("Unexpected result {:?}.", result), + result => panic!("Unexpected result {:?}", result), } match native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![ValueType::I32, ValueType::I32], None)))) { Err(Error::Validation(_)) => { }, - result => panic!("Unexpected result {:?}.", result), + result => panic!("Unexpected result {:?}", result), } match native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![ValueType::I32, ValueType::I32], Some(ValueType::I64))))) { Err(Error::Validation(_)) => { }, - result => panic!("Unexpected result {:?}.", result), + result => panic!("Unexpected result {:?}", result), } assert!(native_env_instance.export_entry("ext_global", &ExportEntryType::Global(VariableType::I32)).is_ok()); match native_env_instance.export_entry("ext_global", &ExportEntryType::Global(VariableType::F32)) { Err(Error::Validation(_)) => { }, - result => panic!("Unexpected result {:?}.", result), + result => panic!("Unexpected result {:?}", result), } } diff --git a/src/interpreter/tests/wabt.rs b/src/interpreter/tests/wabt.rs index 636a7d5..bbf07d5 100644 --- a/src/interpreter/tests/wabt.rs +++ b/src/interpreter/tests/wabt.rs @@ -35,7 +35,12 @@ fn unreachable() { Opcode::Unreachable, // trap Opcode::End])); - assert_eq!(run_function_i32(&module, 0).unwrap_err(), Error::Trap("programmatic".into())); + match run_function_i32(&module, 0) { + Err(Error::Trap(msg)) => { + assert_eq!(msg, "programmatic"); + }, + result => panic!("Unexpected result {:?}", result), + } } #[test] @@ -730,10 +735,24 @@ fn callindirect_2() { let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(0)].into()).unwrap().unwrap(), RuntimeValue::I32(14)); assert_eq!(module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(1)].into()).unwrap().unwrap(), RuntimeValue::I32(6)); - assert_eq!(module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(2)].into()).unwrap_err(), - Error::Function("expected indirect function with signature ([I32, I32]) -> Some(I32) when got with ([I32]) -> Some(I32)".into())); - assert_eq!(module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(3)].into()).unwrap_err(), - Error::Table("trying to read table item with index 3 when there are only 3 items".into())); + match module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(2)].into()) { + Err(Error::Function(msg)) => { + assert_eq!( + &msg, + "expected indirect function with signature ([I32, I32]) -> Some(I32) when got with ([I32]) -> Some(I32)" + ); + } + result => panic!("Unexpected result {:?}", result), + } + match module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(3)].into()) { + Err(Error::Table(msg)) => { + assert_eq!( + &msg, + "trying to read table item with index 3 when there are only 3 items" + ) + }, + result => panic!("Unexpected result {:?}", result), + } } /// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/select.txt