Initial sketch

This commit is contained in:
Sergey Pepyakin
2017-11-25 22:28:15 +03:00
parent 0b5939e4e7
commit 6a3b9af597
4 changed files with 93 additions and 19 deletions

View File

@ -1,11 +1,35 @@
//! WebAssembly interpreter module. //! WebAssembly interpreter module.
use std::any::TypeId;
/// Custom user error. /// 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::<Self>()
}
}
impl UserError {
pub fn downcast_ref<T: UserError>(&self) -> Option<&T> {
if self.__private_get_type_id__() == TypeId::of::<T>() {
unsafe { Some(&*(self as *const UserError as *const T)) }
} else {
None
}
}
pub fn downcast_mut<T: UserError>(&mut self) -> Option<&mut T> {
if self.__private_get_type_id__() == TypeId::of::<T>() {
unsafe { Some(&mut *(self as *mut UserError as *mut T)) }
} else {
None
}
}
} }
/// Internal interpreter error. /// Internal interpreter error.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug)]
pub enum Error<E> where E: UserError { pub enum Error<E> where E: UserError {
/// Program-level error. /// Program-level error.
Program(String), Program(String),
@ -38,7 +62,8 @@ pub enum Error<E> where E: UserError {
/// Trap. /// Trap.
Trap(String), Trap(String),
/// Custom user error. /// Custom user error.
User(E), User(Box<UserError>),
Other(E),
} }
impl<E> Into<String> for Error<E> where E: UserError { impl<E> Into<String> for Error<E> where E: UserError {
@ -60,6 +85,7 @@ impl<E> Into<String> for Error<E> where E: UserError {
Error::Native(s) => s, Error::Native(s) => s,
Error::Trap(s) => format!("trap: {}", s), Error::Trap(s) => format!("trap: {}", s),
Error::User(e) => format!("user: {}", e), Error::User(e) => format!("user: {}", e),
Error::Other(_) => panic!("TODO: Remove this arm "),
} }
} }
} }
@ -83,6 +109,7 @@ impl<E> ::std::fmt::Display for Error<E> where E: UserError {
Error::Native(ref s) => write!(f, "Native: {}", s), Error::Native(ref s) => write!(f, "Native: {}", s),
Error::Trap(ref s) => write!(f, "Trap: {}", s), Error::Trap(ref s) => write!(f, "Trap: {}", s),
Error::User(ref e) => write!(f, "User: {}", e), 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<U> From<U> for Error<U> where U: UserError + Sized { impl<U> From<U> for Error<U> where U: UserError + Sized {
fn from(e: U) -> Self { fn from(e: U) -> Self {
Error::User(e) Error::User(Box::new(e))
} }
} }

View File

@ -121,7 +121,6 @@ pub struct CallerContext<'a, E: 'a + UserError> {
} }
/// Internal function reference. /// Internal function reference.
#[derive(Clone)]
pub struct InternalFunctionReference<'a, E: UserError> { pub struct InternalFunctionReference<'a, E: UserError> {
/// Module reference. /// Module reference.
pub module: Arc<ModuleInstanceInterface<E> + 'a>, pub module: Arc<ModuleInstanceInterface<E> + 'a>,
@ -129,6 +128,16 @@ pub struct InternalFunctionReference<'a, E: UserError> {
pub internal_index: u32, 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 { impl<'a, E> fmt::Debug for InternalFunctionReference<'a, E> where E: UserError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "InternalFunctionReference") write!(f, "InternalFunctionReference")

View File

@ -186,7 +186,7 @@ impl UserFunctionExecutor<UserErrorWithCode> for FunctionExecutor {
Ok(Some(RuntimeValue::I32(diff as i32))) Ok(Some(RuntimeValue::I32(diff as i32)))
}, },
"err" => { "err" => {
Err(Error::User(UserErrorWithCode { error_code: 777 })) Err(Error::User(Box::new(UserErrorWithCode { error_code: 777 })))
}, },
_ => Err(Error::Trap("not implemented".into()).into()), _ => Err(Error::Trap("not implemented".into()).into()),
} }
@ -401,10 +401,29 @@ fn native_custom_error() {
.build(); .build();
let module_instance = program.add_module("main", module, Some(&params.externals)).unwrap(); let module_instance = program.add_module("main", module, Some(&params.externals)).unwrap();
assert_eq!(module_instance.execute_index(0, params.clone().add_argument(RuntimeValue::I32(7)).add_argument(RuntimeValue::I32(0))), let user_error1 = match module_instance.execute_index(
Err(Error::User(UserErrorWithCode { error_code: 777 }))); 0,
assert_eq!(module_instance.execute_index(1, params.clone().add_argument(RuntimeValue::I32(7)).add_argument(RuntimeValue::I32(0))), params
Err(Error::User(UserErrorWithCode { error_code: 777 }))); .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::<UserErrorWithCode>().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::<UserErrorWithCode>().unwrap(), &UserErrorWithCode { error_code: 777 });
} }
#[test] #[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()); 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))))) { match native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![], Some(ValueType::I32))))) {
Err(Error::Validation(_)) => { }, 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)))) { match native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![ValueType::I32, ValueType::I32], None)))) {
Err(Error::Validation(_)) => { }, 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))))) { match native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![ValueType::I32, ValueType::I32], Some(ValueType::I64))))) {
Err(Error::Validation(_)) => { }, 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()); 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)) { match native_env_instance.export_entry("ext_global", &ExportEntryType::Global(VariableType::F32)) {
Err(Error::Validation(_)) => { }, Err(Error::Validation(_)) => { },
result => panic!("Unexpected result {:?}.", result), result => panic!("Unexpected result {:?}", result),
} }
} }

View File

@ -35,7 +35,12 @@ fn unreachable() {
Opcode::Unreachable, // trap Opcode::Unreachable, // trap
Opcode::End])); 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] #[test]
@ -730,10 +735,24 @@ fn callindirect_2() {
let module = program.add_module("main", module, None).unwrap(); 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(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(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(), match module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(2)].into()) {
Error::Function("expected indirect function with signature ([I32, I32]) -> Some(I32) when got with ([I32]) -> Some(I32)".into())); Err(Error::Function(msg)) => {
assert_eq!(module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(3)].into()).unwrap_err(), assert_eq!(
Error::Table("trying to read table item with index 3 when there are only 3 items".into())); &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 /// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/select.txt