| use { |
| Error, Signature, FuncRef, GlobalInstance, GlobalRef, ImportsBuilder, MemoryInstance, |
| MemoryRef, ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue, |
| TableInstance, TableRef, Module, GlobalDescriptor, TableDescriptor, MemoryDescriptor, |
| }; |
| use memory_units::Pages; |
| use std::fs::File; |
| |
| struct Env { |
| table_base: GlobalRef, |
| memory_base: GlobalRef, |
| memory: MemoryRef, |
| table: TableRef, |
| } |
| |
| impl Env { |
| fn new() -> Env { |
| Env { |
| table_base: GlobalInstance::alloc(RuntimeValue::I32(0), false), |
| memory_base: GlobalInstance::alloc(RuntimeValue::I32(0), false), |
| memory: MemoryInstance::alloc(Pages(256), None).unwrap(), |
| table: TableInstance::alloc(64, None).unwrap(), |
| } |
| } |
| } |
| |
| impl ModuleImportResolver for Env { |
| fn resolve_func(&self, _field_name: &str, _func_type: &Signature) -> Result<FuncRef, Error> { |
| Err(Error::Instantiation( |
| "env module doesn't provide any functions".into(), |
| )) |
| } |
| |
| fn resolve_global( |
| &self, |
| field_name: &str, |
| _global_type: &GlobalDescriptor, |
| ) -> Result<GlobalRef, Error> { |
| match field_name { |
| "tableBase" => Ok(self.table_base.clone()), |
| "memoryBase" => Ok(self.memory_base.clone()), |
| _ => Err(Error::Instantiation(format!( |
| "env module doesn't provide global '{}'", |
| field_name |
| ))), |
| } |
| } |
| |
| fn resolve_memory( |
| &self, |
| field_name: &str, |
| _memory_type: &MemoryDescriptor, |
| ) -> Result<MemoryRef, Error> { |
| match field_name { |
| "memory" => Ok(self.memory.clone()), |
| _ => Err(Error::Instantiation(format!( |
| "env module doesn't provide memory '{}'", |
| field_name |
| ))), |
| } |
| } |
| |
| fn resolve_table(&self, field_name: &str, _table_type: &TableDescriptor) -> Result<TableRef, Error> { |
| match field_name { |
| "table" => Ok(self.table.clone()), |
| _ => Err(Error::Instantiation( |
| format!("env module doesn't provide table '{}'", field_name), |
| )), |
| } |
| } |
| } |
| |
| fn load_from_file(filename: &str) -> Module { |
| use std::io::prelude::*; |
| let mut file = File::open(filename).unwrap(); |
| let mut buf = Vec::new(); |
| file.read_to_end(&mut buf).unwrap(); |
| let wasm_buf = ::wabt::wat2wasm(&buf).unwrap(); |
| Module::from_buffer(wasm_buf).unwrap() |
| } |
| |
| #[test] |
| fn interpreter_inc_i32() { |
| // Name of function contained in WASM file (note the leading underline) |
| const FUNCTION_NAME: &'static str = "_inc_i32"; |
| // The WASM file containing the module and function |
| const WASM_FILE: &str = &"res/fixtures/inc_i32.wast"; |
| |
| let module = load_from_file(WASM_FILE); |
| |
| let env = Env::new(); |
| |
| let instance = ModuleInstance::new( |
| &module, |
| &ImportsBuilder::new().with_resolver("env", &env), |
| ).expect("Failed to instantiate module") |
| .assert_no_start(); |
| |
| let i32_val = 42; |
| // the functions expects a single i32 parameter |
| let args = &[RuntimeValue::I32(i32_val)]; |
| let exp_retval = Some(RuntimeValue::I32(i32_val + 1)); |
| |
| let retval = instance |
| .invoke_export(FUNCTION_NAME, args, &mut NopExternals) |
| .expect(""); |
| assert_eq!(exp_retval, retval); |
| } |
| |
| #[test] |
| fn interpreter_accumulate_u8() { |
| // Name of function contained in WASM file (note the leading underline) |
| const FUNCTION_NAME: &'static str = "_accumulate_u8"; |
| // The WASM file containing the module and function |
| const WASM_FILE: &str = &"res/fixtures/accumulate_u8.wast"; |
| // The octet sequence being accumulated |
| const BUF: &[u8] = &[9,8,7,6,5,4,3,2,1]; |
| |
| |
| // Load the module-structure from wasm-file and add to program |
| let module = load_from_file(WASM_FILE); |
| |
| let env = Env::new(); |
| let instance = ModuleInstance::new( |
| &module, |
| &ImportsBuilder::new().with_resolver("env", &env), |
| ).expect("Failed to instantiate module") |
| .assert_no_start(); |
| |
| let env_memory = env.memory.clone(); |
| |
| // Place the octet-sequence at index 0 in linear memory |
| let offset: u32 = 0; |
| let _ = env_memory.set(offset, BUF); |
| |
| // Set up the function argument list and invoke the function |
| let args = &[RuntimeValue::I32(BUF.len() as i32), RuntimeValue::I32(offset as i32)]; |
| let retval = instance |
| .invoke_export(FUNCTION_NAME, args, &mut NopExternals) |
| .expect("Failed to execute function"); |
| |
| // For verification, repeat accumulation using native code |
| let accu = BUF.into_iter().fold(0 as i32, |a, b| a + *b as i32); |
| let exp_retval: Option<RuntimeValue> = Some(RuntimeValue::I32(accu)); |
| |
| // Verify calculation from WebAssembly runtime is identical to expected result |
| assert_eq!(exp_retval, retval); |
| } |