diff --git a/benches/build.rs b/benches/build.rs index 9cb9ac1..6a1ee5b 100644 --- a/benches/build.rs +++ b/benches/build.rs @@ -1,32 +1,31 @@ use std::env; use std::process; - fn main() { - println!("cargo:rerun-if-changed=./wasm-kernel/"); + println!("cargo:rerun-if-changed=./wasm-kernel/"); - // The CARGO environment variable provides a path to the executable that - // runs this build process. - let cargo_bin = env::var("CARGO").expect("CARGO env variable should be defined"); + // The CARGO environment variable provides a path to the executable that + // runs this build process. + let cargo_bin = env::var("CARGO").expect("CARGO env variable should be defined"); - // Build a release version of wasm-kernel. The code in the output wasm binary - // will be used in benchmarks. - let output = process::Command::new(cargo_bin) - .arg("build") - .arg("--target=wasm32-unknown-unknown") - .arg("--release") - .arg("--manifest-path=./wasm-kernel/Cargo.toml") - .arg("--verbose") - .output() - .expect("failed to execute `cargo`"); + // Build a release version of wasm-kernel. The code in the output wasm binary + // will be used in benchmarks. + let output = process::Command::new(cargo_bin) + .arg("build") + .arg("--target=wasm32-unknown-unknown") + .arg("--release") + .arg("--manifest-path=./wasm-kernel/Cargo.toml") + .arg("--verbose") + .output() + .expect("failed to execute `cargo`"); - if !output.status.success() { - let msg = format!( - "status: {status}\nstdout: {stdout}\nstderr: {stderr}\n", - status=output.status, - stdout=String::from_utf8_lossy(&output.stdout), - stderr=String::from_utf8_lossy(&output.stderr), - ); - panic!("{}", msg); - } + if !output.status.success() { + let msg = format!( + "status: {status}\nstdout: {stdout}\nstderr: {stderr}\n", + status = output.status, + stdout = String::from_utf8_lossy(&output.stdout), + stderr = String::from_utf8_lossy(&output.stderr), + ); + panic!("{}", msg); + } } diff --git a/examples/interpret.rs b/examples/interpret.rs index 2be3018..0b987a0 100644 --- a/examples/interpret.rs +++ b/examples/interpret.rs @@ -4,14 +4,14 @@ extern crate wasmi; use std::env::args; use std::fs::File; -use wasmi::{ModuleInstance, NopExternals, RuntimeValue, ImportsBuilder, Module}; +use wasmi::{ImportsBuilder, Module, ModuleInstance, NopExternals, RuntimeValue}; 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(); - Module::from_buffer(buf).unwrap() + use std::io::prelude::*; + let mut file = File::open(filename).unwrap(); + let mut buf = Vec::new(); + file.read_to_end(&mut buf).unwrap(); + Module::from_buffer(buf).unwrap() } fn main() { @@ -27,10 +27,10 @@ fn main() { let module = load_from_file(&args[1]); // Intialize deserialized module. It adds module into It expects 3 parameters: - // - a name for the module - // - a module declaration - // - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here - // This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197 + // - a name for the module + // - a module declaration + // - "main" module doesn't import native module(s) this is why we don't need to provide external native modules here + // This test shows how to implement native module https://github.com/NikVolf/parity-wasm/blob/master/src/interpreter/tests/basics.rs#L197 let main = ModuleInstance::new(&module, &ImportsBuilder::default()) .expect("Failed to instantiate module") .run_start(&mut NopExternals) @@ -40,5 +40,8 @@ fn main() { let argument: i32 = args[2].parse().expect("Integer argument required"); // "_call" export of function to be executed with an i32 argument and prints the result of execution - println!("Result: {:?}", main.invoke_export("_call", &[RuntimeValue::I32(argument)], &mut NopExternals)); + println!( + "Result: {:?}", + main.invoke_export("_call", &[RuntimeValue::I32(argument)], &mut NopExternals) + ); } diff --git a/examples/invoke.rs b/examples/invoke.rs index 2b7dd99..cb59fd1 100644 --- a/examples/invoke.rs +++ b/examples/invoke.rs @@ -3,9 +3,8 @@ extern crate wasmi; use std::env::args; -use parity_wasm::elements::{Internal, External, Type, FunctionType, ValueType}; -use wasmi::{RuntimeValue, ModuleInstance, NopExternals, ImportsBuilder}; - +use parity_wasm::elements::{External, FunctionType, Internal, Type, ValueType}; +use wasmi::{ImportsBuilder, ModuleInstance, NopExternals, RuntimeValue}; fn main() { let args: Vec<_> = args().collect(); @@ -23,14 +22,19 @@ fn main() { // Export section has an entry with a func_name with an index inside a module let export_section = module.export_section().expect("No export section found"); // It's a section with function declarations (which are references to the type section entries) - let function_section = module.function_section().expect("No function section found"); + let function_section = module + .function_section() + .expect("No function section found"); // Type section stores function types which are referenced by function_section entries let type_section = module.type_section().expect("No type section found"); // Given function name used to find export section entry which contains // an `internal` field which points to the index in the function index space - let found_entry = export_section.entries().iter() - .find(|entry| func_name == entry.field()).expect(&format!("No export with name {} found", func_name)); + let found_entry = export_section + .entries() + .iter() + .find(|entry| func_name == entry.field()) + .expect(&format!("No export with name {} found", func_name)); // Function index in the function index space (internally-defined + imported) let function_index: usize = match found_entry.internal() { @@ -41,11 +45,14 @@ fn main() { // We need to count import section entries (functions only!) to subtract it from function_index // and obtain the index within the function section let import_section_len: usize = match module.import_section() { - Some(import) => - import.entries().iter().filter(|entry| match entry.external() { + Some(import) => import + .entries() + .iter() + .filter(|entry| match entry.external() { &External::Function(_) => true, _ => false, - }).count(), + }) + .count(), None => 0, }; @@ -53,7 +60,8 @@ fn main() { let function_index_in_section = function_index - import_section_len; // Getting a type reference from a function section entry - let func_type_ref: usize = function_section.entries()[function_index_in_section].type_ref() as usize; + let func_type_ref: usize = + function_section.entries()[function_index_in_section].type_ref() as usize; // Use the reference to get an actual function type let function_type: &FunctionType = match &type_section.types()[func_type_ref] { @@ -61,12 +69,35 @@ fn main() { }; // Parses arguments and constructs runtime values in correspondence of their types - function_type.params().iter().enumerate().map(|(i, value)| match value { - &ValueType::I32 => RuntimeValue::I32(program_args[i].parse::().expect(&format!("Can't parse arg #{} as i32", program_args[i]))), - &ValueType::I64 => RuntimeValue::I64(program_args[i].parse::().expect(&format!("Can't parse arg #{} as i64", program_args[i]))), - &ValueType::F32 => RuntimeValue::F32(program_args[i].parse::().expect(&format!("Can't parse arg #{} as f32", program_args[i])).into()), - &ValueType::F64 => RuntimeValue::F64(program_args[i].parse::().expect(&format!("Can't parse arg #{} as f64", program_args[i])).into()), - }).collect::>() + function_type + .params() + .iter() + .enumerate() + .map(|(i, value)| match value { + &ValueType::I32 => RuntimeValue::I32( + program_args[i] + .parse::() + .expect(&format!("Can't parse arg #{} as i32", program_args[i])), + ), + &ValueType::I64 => RuntimeValue::I64( + program_args[i] + .parse::() + .expect(&format!("Can't parse arg #{} as i64", program_args[i])), + ), + &ValueType::F32 => RuntimeValue::F32( + program_args[i] + .parse::() + .expect(&format!("Can't parse arg #{} as f32", program_args[i])) + .into(), + ), + &ValueType::F64 => RuntimeValue::F64( + program_args[i] + .parse::() + .expect(&format!("Can't parse arg #{} as f64", program_args[i])) + .into(), + ), + }) + .collect::>() }; let loaded_module = wasmi::Module::from_parity_wasm_module(module).expect("Module to be valid"); @@ -81,5 +112,9 @@ fn main() { .run_start(&mut NopExternals) .expect("Failed to run start function in module"); - println!("Result: {:?}", main.invoke_export(func_name, &args, &mut NopExternals).expect("")); + println!( + "Result: {:?}", + main.invoke_export(func_name, &args, &mut NopExternals) + .expect("") + ); } diff --git a/examples/tictactoe.rs b/examples/tictactoe.rs index 86d7781..7ee89a0 100644 --- a/examples/tictactoe.rs +++ b/examples/tictactoe.rs @@ -1,256 +1,255 @@ -extern crate wasmi; extern crate parity_wasm; +extern crate wasmi; use std::env; use std::fmt; use std::fs::File; use wasmi::{ - Error as InterpreterError, ModuleInstance, ModuleRef, - Externals, RuntimeValue, FuncRef, ModuleImportResolver, - FuncInstance, HostError, ImportsBuilder, Signature, ValueType, - RuntimeArgs, Trap, + Error as InterpreterError, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder, + ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature, Trap, + ValueType, }; #[derive(Debug)] pub enum Error { - OutOfRange, - AlreadyOccupied, - Interpreter(InterpreterError), + OutOfRange, + AlreadyOccupied, + Interpreter(InterpreterError), } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } } impl From for Error { - fn from(e: InterpreterError) -> Self { - Error::Interpreter(e) - } + fn from(e: InterpreterError) -> Self { + Error::Interpreter(e) + } } impl HostError for Error {} mod tictactoe { - use super::Error; + use super::Error; - #[derive(Copy, Clone, Debug, PartialEq, Eq)] - pub enum Player { - X, - O, - } + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + pub enum Player { + X, + O, + } - #[derive(Copy, Clone, Debug, PartialEq, Eq)] - pub enum GameResult { - Draw, - Won(Player), - } + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + pub enum GameResult { + Draw, + Won(Player), + } - impl Player { - pub fn into_i32(maybe_player: Option) -> i32 { - match maybe_player { - None => 0, - Some(Player::X) => 1, - Some(Player::O) => 2, - } - } - } + impl Player { + pub fn into_i32(maybe_player: Option) -> i32 { + match maybe_player { + None => 0, + Some(Player::X) => 1, + Some(Player::O) => 2, + } + } + } - #[derive(Debug)] - pub struct Game { - board: [Option; 9], - } + #[derive(Debug)] + pub struct Game { + board: [Option; 9], + } - impl Game { - pub fn new() -> Game { - Game { - board: [None; 9], - } - } + impl Game { + pub fn new() -> Game { + Game { board: [None; 9] } + } - pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> { - if idx < 0 || idx > 9 { - return Err(Error::OutOfRange); - } - if self.board[idx as usize] != None { - return Err(Error::AlreadyOccupied); - } - self.board[idx as usize] = Some(player); - Ok(()) - } + pub fn set(&mut self, idx: i32, player: Player) -> Result<(), Error> { + if idx < 0 || idx > 9 { + return Err(Error::OutOfRange); + } + if self.board[idx as usize] != None { + return Err(Error::AlreadyOccupied); + } + self.board[idx as usize] = Some(player); + Ok(()) + } - pub fn get(&self, idx: i32) -> Result, Error> { - if idx < 0 || idx > 9 { - return Err(Error::OutOfRange); - } - Ok(self.board[idx as usize]) - } + pub fn get(&self, idx: i32) -> Result, Error> { + if idx < 0 || idx > 9 { + return Err(Error::OutOfRange); + } + Ok(self.board[idx as usize]) + } - pub fn game_result(&self) -> Option { - // 0, 1, 2 - // 3, 4, 5 - // 6, 7, 8 - let patterns = &[ - // Rows - (0, 1, 2), - (3, 4, 5), - (6, 7, 8), + pub fn game_result(&self) -> Option { + // 0, 1, 2 + // 3, 4, 5 + // 6, 7, 8 + let patterns = &[ + // Rows + (0, 1, 2), + (3, 4, 5), + (6, 7, 8), + // Columns + (0, 3, 6), + (1, 4, 7), + (2, 5, 8), + // Diagonals + (0, 4, 8), + (2, 4, 6), + ]; - // Columns - (0, 3, 6), - (1, 4, 7), - (2, 5, 8), + // Returns Some(player) if all cells contain same Player. + let all_same = |i1: usize, i2: usize, i3: usize| -> Option { + if self.board[i1].is_none() { + return None; + } + if self.board[i1] == self.board[i2] && self.board[i2] == self.board[i3] { + return self.board[i1]; + } + None + }; - // Diagonals - (0, 4, 8), - (2, 4, 6), - ]; + for &(i1, i2, i3) in patterns { + if let Some(player) = all_same(i1, i2, i3) { + return Some(GameResult::Won(player)); + } + } - // Returns Some(player) if all cells contain same Player. - let all_same = |i1: usize, i2: usize, i3: usize| -> Option { - if self.board[i1].is_none() { - return None; - } - if self.board[i1] == self.board[i2] && self.board[i2] == self.board[i3] { - return self.board[i1]; - } - None - }; - - for &(i1, i2, i3) in patterns { - if let Some(player) = all_same(i1, i2, i3) { - return Some(GameResult::Won(player)); - } - } - - // Ok, there is no winner. Check if it's draw. - let all_occupied = self.board.iter().all(|&cell| cell.is_some()); - if all_occupied { - Some(GameResult::Draw) - } else { - // Nah, there are still empty cells left. - None - } - } - } + // Ok, there is no winner. Check if it's draw. + let all_occupied = self.board.iter().all(|&cell| cell.is_some()); + if all_occupied { + Some(GameResult::Draw) + } else { + // Nah, there are still empty cells left. + None + } + } + } } struct Runtime<'a> { - player: tictactoe::Player, - game: &'a mut tictactoe::Game, + player: tictactoe::Player, + game: &'a mut tictactoe::Game, } const SET_FUNC_INDEX: usize = 0; const GET_FUNC_INDEX: usize = 1; impl<'a> Externals for Runtime<'a> { - fn invoke_index( - &mut self, - index: usize, - args: RuntimeArgs, - ) -> Result, Trap> { - match index { - SET_FUNC_INDEX => { - let idx: i32 = args.nth(0); - self.game.set(idx, self.player)?; - Ok(None) - } - GET_FUNC_INDEX => { - let idx: i32 = args.nth(0); - let val: i32 = tictactoe::Player::into_i32(self.game.get(idx)?); - Ok(Some(val.into())) - } - _ => panic!("unknown function index") - } - } + fn invoke_index( + &mut self, + index: usize, + args: RuntimeArgs, + ) -> Result, Trap> { + match index { + SET_FUNC_INDEX => { + let idx: i32 = args.nth(0); + self.game.set(idx, self.player)?; + Ok(None) + } + GET_FUNC_INDEX => { + let idx: i32 = args.nth(0); + let val: i32 = tictactoe::Player::into_i32(self.game.get(idx)?); + Ok(Some(val.into())) + } + _ => panic!("unknown function index"), + } + } } struct RuntimeModuleImportResolver; impl<'a> ModuleImportResolver for RuntimeModuleImportResolver { - fn resolve_func( - &self, - field_name: &str, - _signature: &Signature, - ) -> Result { - let func_ref = match field_name { - "set" => { - FuncInstance::alloc_host(Signature::new(&[ValueType::I32][..], None), SET_FUNC_INDEX) - }, - "get" => FuncInstance::alloc_host(Signature::new(&[ValueType::I32][..], Some(ValueType::I32)), GET_FUNC_INDEX), - _ => return Err( - InterpreterError::Function( - format!("host module doesn't export function with name {}", field_name) - ) - ) - }; - Ok(func_ref) - } + fn resolve_func( + &self, + field_name: &str, + _signature: &Signature, + ) -> Result { + let func_ref = match field_name { + "set" => FuncInstance::alloc_host( + Signature::new(&[ValueType::I32][..], None), + SET_FUNC_INDEX, + ), + "get" => FuncInstance::alloc_host( + Signature::new(&[ValueType::I32][..], Some(ValueType::I32)), + GET_FUNC_INDEX, + ), + _ => { + return Err(InterpreterError::Function(format!( + "host module doesn't export function with name {}", + field_name + ))); + } + }; + Ok(func_ref) + } } fn instantiate(path: &str) -> Result { - let module = { - use std::io::prelude::*; - let mut file = File::open(path).unwrap(); - let mut wasm_buf = Vec::new(); - file.read_to_end(&mut wasm_buf).unwrap(); - wasmi::Module::from_buffer(&wasm_buf)? - }; + let module = { + use std::io::prelude::*; + let mut file = File::open(path).unwrap(); + let mut wasm_buf = Vec::new(); + file.read_to_end(&mut wasm_buf).unwrap(); + wasmi::Module::from_buffer(&wasm_buf)? + }; - let mut imports = ImportsBuilder::new(); - imports.push_resolver("env", &RuntimeModuleImportResolver); + let mut imports = ImportsBuilder::new(); + imports.push_resolver("env", &RuntimeModuleImportResolver); - let instance = ModuleInstance::new(&module, &imports)? - .assert_no_start(); + let instance = ModuleInstance::new(&module, &imports)?.assert_no_start(); - Ok(instance) + Ok(instance) } fn play( - x_instance: ModuleRef, - o_instance: ModuleRef, - game: &mut tictactoe::Game, + x_instance: ModuleRef, + o_instance: ModuleRef, + game: &mut tictactoe::Game, ) -> Result { - let mut turn_of = tictactoe::Player::X; - let game_result = loop { - let (instance, next_turn_of) = match turn_of { - tictactoe::Player::X => (&x_instance, tictactoe::Player::O), - tictactoe::Player::O => (&o_instance, tictactoe::Player::X), - }; + let mut turn_of = tictactoe::Player::X; + let game_result = loop { + let (instance, next_turn_of) = match turn_of { + tictactoe::Player::X => (&x_instance, tictactoe::Player::O), + tictactoe::Player::O => (&o_instance, tictactoe::Player::X), + }; - { - let mut runtime = Runtime { - player: turn_of, - game: game, - }; - let _ = instance.invoke_export("mk_turn", &[], &mut runtime)?; - } + { + let mut runtime = Runtime { + player: turn_of, + game: game, + }; + let _ = instance.invoke_export("mk_turn", &[], &mut runtime)?; + } - match game.game_result() { - Some(game_result) => break game_result, - None => {} - } + match game.game_result() { + Some(game_result) => break game_result, + None => {} + } - turn_of = next_turn_of; - }; + turn_of = next_turn_of; + }; - Ok(game_result) + Ok(game_result) } fn main() { - let mut game = tictactoe::Game::new(); + let mut game = tictactoe::Game::new(); - let args: Vec<_> = env::args().collect(); - if args.len() < 3 { - println!("Usage: {} ", args[0]); - return; - } + let args: Vec<_> = env::args().collect(); + if args.len() < 3 { + println!("Usage: {} ", args[0]); + return; + } - // Instantiate modules of X and O players. - let x_instance = instantiate(&args[1]).expect("X player module to load"); - let o_instance = instantiate(&args[2]).expect("Y player module to load"); + // Instantiate modules of X and O players. + let x_instance = instantiate(&args[1]).expect("X player module to load"); + let o_instance = instantiate(&args[2]).expect("Y player module to load"); - let result = play(x_instance, o_instance, &mut game); - println!("result = {:?}, game = {:#?}", result, game); + let result = play(x_instance, o_instance, &mut game); + println!("result = {:?}, game = {:#?}", result, game); } diff --git a/src/bin/instantiate.rs b/src/bin/instantiate.rs index e5042f9..0c39349 100644 --- a/src/bin/instantiate.rs +++ b/src/bin/instantiate.rs @@ -5,77 +5,79 @@ extern crate wasmi; use std::env::args; use std::fs::File; -use wasmi::{ - Error, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, - ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module, - ModuleImportResolver, ModuleInstance, NopExternals, RuntimeValue, Signature, - TableDescriptor, TableInstance, TableRef}; use wasmi::memory_units::*; +use wasmi::{ + Error, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder, + MemoryDescriptor, MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, + NopExternals, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, +}; 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(); - Module::from_buffer(buf).unwrap() + use std::io::prelude::*; + let mut file = File::open(filename).unwrap(); + let mut buf = Vec::new(); + file.read_to_end(&mut buf).unwrap(); + Module::from_buffer(buf).unwrap() } struct ResolveAll; impl ModuleImportResolver for ResolveAll { - fn resolve_func(&self, _field_name: &str, signature: &Signature) -> Result { - Ok(FuncInstance::alloc_host(signature.clone(), 0)) - } + fn resolve_func(&self, _field_name: &str, signature: &Signature) -> Result { + Ok(FuncInstance::alloc_host(signature.clone(), 0)) + } - fn resolve_global( - &self, - _field_name: &str, - global_type: &GlobalDescriptor, - ) -> Result { - Ok(GlobalInstance::alloc( - RuntimeValue::default(global_type.value_type()), - global_type.is_mutable(), - )) - } + fn resolve_global( + &self, + _field_name: &str, + global_type: &GlobalDescriptor, + ) -> Result { + Ok(GlobalInstance::alloc( + RuntimeValue::default(global_type.value_type()), + global_type.is_mutable(), + )) + } - fn resolve_memory( - &self, - _field_name: &str, - memory_type: &MemoryDescriptor, - ) -> Result { - Ok(MemoryInstance::alloc( - Pages(memory_type.initial() as usize), - memory_type.maximum().map(|m| Pages(m as usize)), - ).unwrap()) - } + fn resolve_memory( + &self, + _field_name: &str, + memory_type: &MemoryDescriptor, + ) -> Result { + Ok(MemoryInstance::alloc( + Pages(memory_type.initial() as usize), + memory_type.maximum().map(|m| Pages(m as usize)), + ) + .unwrap()) + } - fn resolve_table( - &self, - _field_name: &str, - table_type: &TableDescriptor, - ) -> Result { - Ok(TableInstance::alloc(table_type.initial(), table_type.maximum()).unwrap()) - } + fn resolve_table( + &self, + _field_name: &str, + table_type: &TableDescriptor, + ) -> Result { + Ok(TableInstance::alloc(table_type.initial(), table_type.maximum()).unwrap()) + } } fn main() { - let args: Vec<_> = args().collect(); - if args.len() != 2 { - println!("Usage: {} ", args[0]); - return; - } - let module = load_from_file(&args[1]); - let _ = ModuleInstance::new( - &module, - &ImportsBuilder::default() - // Well known imports. - .with_resolver("env", &ResolveAll) - .with_resolver("global", &ResolveAll) - .with_resolver("foo", &ResolveAll) - .with_resolver("global.Math", &ResolveAll) - .with_resolver("asm2wasm", &ResolveAll) - .with_resolver("spectest", &ResolveAll), - ).expect("Failed to instantiate module") - .run_start(&mut NopExternals) - .expect("Failed to run start function in module"); + let args: Vec<_> = args().collect(); + if args.len() != 2 { + println!("Usage: {} ", args[0]); + return; + } + let module = load_from_file(&args[1]); + let _ = ModuleInstance::new( + &module, + &ImportsBuilder::default() + // Well known imports. + .with_resolver("env", &ResolveAll) + .with_resolver("global", &ResolveAll) + .with_resolver("foo", &ResolveAll) + .with_resolver("global.Math", &ResolveAll) + .with_resolver("asm2wasm", &ResolveAll) + .with_resolver("spectest", &ResolveAll), + ) + .expect("Failed to instantiate module") + .run_start(&mut NopExternals) + .expect("Failed to run start function in module"); } diff --git a/src/common/mod.rs b/src/common/mod.rs index 49ff10c..6fdb382 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,4 +1,3 @@ - pub mod stack; /// Index of default linear memory. diff --git a/src/common/stack.rs b/src/common/stack.rs index a96af35..9e96a63 100644 --- a/src/common/stack.rs +++ b/src/common/stack.rs @@ -1,88 +1,101 @@ #[allow(unused_imports)] use alloc::prelude::*; +use core::fmt; #[cfg(feature = "std")] use std::error; -use core::fmt; #[derive(Debug)] pub struct Error(String); impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } } #[cfg(feature = "std")] impl error::Error for Error { - fn description(&self) -> &str { - &self.0 - } + fn description(&self) -> &str { + &self.0 + } } /// Stack with limit. #[derive(Debug)] -pub struct StackWithLimit where T: Clone { - /// Stack values. - values: Vec, - /// Stack limit (maximal stack len). - limit: usize, +pub struct StackWithLimit +where + T: Clone, +{ + /// Stack values. + values: Vec, + /// Stack limit (maximal stack len). + limit: usize, } -impl StackWithLimit where T: Clone { - pub fn with_limit(limit: usize) -> Self { - StackWithLimit { - values: Vec::new(), - limit: limit - } - } +impl StackWithLimit +where + T: Clone, +{ + pub fn with_limit(limit: usize) -> Self { + StackWithLimit { + values: Vec::new(), + limit: limit, + } + } - pub fn is_empty(&self) -> bool { - self.values.is_empty() - } + pub fn is_empty(&self) -> bool { + self.values.is_empty() + } - pub fn len(&self) -> usize { - self.values.len() - } + pub fn len(&self) -> usize { + self.values.len() + } - pub fn top(&self) -> Result<&T, Error> { - self.values - .last() - .ok_or_else(|| Error("non-empty stack expected".into())) - } + pub fn top(&self) -> Result<&T, Error> { + self.values + .last() + .ok_or_else(|| Error("non-empty stack expected".into())) + } - pub fn top_mut(&mut self) -> Result<&mut T, Error> { - self.values - .last_mut() - .ok_or_else(|| Error("non-empty stack expected".into())) - } + pub fn top_mut(&mut self) -> Result<&mut T, Error> { + self.values + .last_mut() + .ok_or_else(|| Error("non-empty stack expected".into())) + } - pub fn get(&self, index: usize) -> Result<&T, Error> { - if index >= self.values.len() { - return Err(Error(format!("trying to get value at position {} on stack of size {}", index, self.values.len()))); - } + pub fn get(&self, index: usize) -> Result<&T, Error> { + if index >= self.values.len() { + return Err(Error(format!( + "trying to get value at position {} on stack of size {}", + index, + self.values.len() + ))); + } - Ok(self.values.get(self.values.len() - 1 - index).expect("checked couple of lines above")) - } + Ok(self + .values + .get(self.values.len() - 1 - index) + .expect("checked couple of lines above")) + } - pub fn push(&mut self, value: T) -> Result<(), Error> { - if self.values.len() >= self.limit { - return Err(Error(format!("exceeded stack limit {}", self.limit))); - } + pub fn push(&mut self, value: T) -> Result<(), Error> { + if self.values.len() >= self.limit { + return Err(Error(format!("exceeded stack limit {}", self.limit))); + } - self.values.push(value); - Ok(()) - } + self.values.push(value); + Ok(()) + } - pub fn pop(&mut self) -> Result { - self.values - .pop() - .ok_or_else(|| Error("non-empty stack expected".into())) - } + pub fn pop(&mut self) -> Result { + self.values + .pop() + .ok_or_else(|| Error("non-empty stack expected".into())) + } - pub fn resize(&mut self, new_size: usize, dummy: T) { - debug_assert!(new_size <= self.values.len()); - self.values.resize(new_size, dummy); - } + pub fn resize(&mut self, new_size: usize, dummy: T) { + debug_assert!(new_size <= self.values.len()); + self.values.resize(new_size, dummy); + } } diff --git a/src/func.rs b/src/func.rs index 3df987f..b1d66db 100644 --- a/src/func.rs +++ b/src/func.rs @@ -2,14 +2,14 @@ use alloc::prelude::*; use alloc::rc::{Rc, Weak}; use core::fmt; -use parity_wasm::elements::Local; -use {Trap, Signature}; use host::Externals; -use runner::{check_function_args, Interpreter, InterpreterState}; -use value::RuntimeValue; -use types::ValueType; -use module::ModuleInstance; use isa; +use module::ModuleInstance; +use parity_wasm::elements::Local; +use runner::{check_function_args, Interpreter, InterpreterState}; +use types::ValueType; +use value::RuntimeValue; +use {Signature, Trap}; /// Reference to a function (See [`FuncInstance`] for details). /// @@ -20,10 +20,10 @@ use isa; pub struct FuncRef(Rc); impl ::core::ops::Deref for FuncRef { - type Target = FuncInstance; - fn deref(&self) -> &FuncInstance { - &self.0 - } + type Target = FuncInstance; + fn deref(&self) -> &FuncInstance { + &self.0 + } } /// Runtime representation of a function. @@ -44,271 +44,271 @@ pub struct FuncInstance(FuncInstanceInternal); #[derive(Clone)] pub(crate) enum FuncInstanceInternal { - Internal { - signature: Rc, - module: Weak, - body: Rc, - }, - Host { - signature: Signature, - host_func_index: usize, - }, + Internal { + signature: Rc, + module: Weak, + body: Rc, + }, + Host { + signature: Signature, + host_func_index: usize, + }, } impl fmt::Debug for FuncInstance { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.as_internal() { - &FuncInstanceInternal::Internal { - ref signature, - .. - } => { - // 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, - "Internal {{ signature={:?} }}", - signature, - ) - } - &FuncInstanceInternal::Host { ref signature, .. } => { - write!(f, "Host {{ signature={:?} }}", signature) - } - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.as_internal() { + &FuncInstanceInternal::Internal { ref signature, .. } => { + // 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, "Internal {{ signature={:?} }}", signature,) + } + &FuncInstanceInternal::Host { ref signature, .. } => { + write!(f, "Host {{ signature={:?} }}", signature) + } + } + } } impl FuncInstance { - /// Allocate a function instance for a host function. - /// - /// When this function instance will be called by the wasm code, - /// the instance of [`Externals`] will be invoked by calling `invoke_index` - /// with specified `host_func_index` here. - /// This call will be made with the `signature` provided here. - /// - /// [`Externals`]: trait.Externals.html - pub fn alloc_host(signature: Signature, host_func_index: usize) -> FuncRef { - let func = FuncInstanceInternal::Host { - signature, - host_func_index, - }; - FuncRef(Rc::new(FuncInstance(func))) - } + /// Allocate a function instance for a host function. + /// + /// When this function instance will be called by the wasm code, + /// the instance of [`Externals`] will be invoked by calling `invoke_index` + /// with specified `host_func_index` here. + /// This call will be made with the `signature` provided here. + /// + /// [`Externals`]: trait.Externals.html + pub fn alloc_host(signature: Signature, host_func_index: usize) -> FuncRef { + let func = FuncInstanceInternal::Host { + signature, + host_func_index, + }; + FuncRef(Rc::new(FuncInstance(func))) + } - /// Returns [signature] of this function instance. - /// - /// This function instance can only be called with matching signatures. - /// - /// [signature]: struct.Signature.html - pub fn signature(&self) -> &Signature { - match *self.as_internal() { - FuncInstanceInternal::Internal { ref signature, .. } => signature, - FuncInstanceInternal::Host { ref signature, .. } => signature, - } - } + /// Returns [signature] of this function instance. + /// + /// This function instance can only be called with matching signatures. + /// + /// [signature]: struct.Signature.html + pub fn signature(&self) -> &Signature { + match *self.as_internal() { + FuncInstanceInternal::Internal { ref signature, .. } => signature, + FuncInstanceInternal::Host { ref signature, .. } => signature, + } + } - pub(crate) fn as_internal(&self) -> &FuncInstanceInternal { - &self.0 - } + pub(crate) fn as_internal(&self) -> &FuncInstanceInternal { + &self.0 + } - pub(crate) fn alloc_internal( - module: Weak, - signature: Rc, - body: FuncBody, - ) -> FuncRef { - let func = FuncInstanceInternal::Internal { - signature, - module: module, - body: Rc::new(body), - }; - FuncRef(Rc::new(FuncInstance(func))) - } + pub(crate) fn alloc_internal( + module: Weak, + signature: Rc, + body: FuncBody, + ) -> FuncRef { + let func = FuncInstanceInternal::Internal { + signature, + module: module, + body: Rc::new(body), + }; + FuncRef(Rc::new(FuncInstance(func))) + } - pub(crate) fn body(&self) -> Option> { - match *self.as_internal() { - FuncInstanceInternal::Internal { ref body, .. } => Some(Rc::clone(body)), - FuncInstanceInternal::Host { .. } => None, - } - } + pub(crate) fn body(&self) -> Option> { + match *self.as_internal() { + FuncInstanceInternal::Internal { ref body, .. } => Some(Rc::clone(body)), + FuncInstanceInternal::Host { .. } => None, + } + } - /// Invoke this function. - /// - /// # Errors - /// - /// Returns `Err` if `args` types is not match function [`signature`] or - /// if [`Trap`] at execution time occured. - /// - /// [`signature`]: #method.signature - /// [`Trap`]: #enum.Trap.html - pub fn invoke( - func: &FuncRef, - args: &[RuntimeValue], - externals: &mut E, - ) -> Result, Trap> { - check_function_args(func.signature(), &args)?; - match *func.as_internal() { - FuncInstanceInternal::Internal { .. } => { - let mut interpreter = Interpreter::new(func, args)?; - interpreter.start_execution(externals) - } - FuncInstanceInternal::Host { - ref host_func_index, - .. - } => externals.invoke_index(*host_func_index, args.into()), - } - } + /// Invoke this function. + /// + /// # Errors + /// + /// Returns `Err` if `args` types is not match function [`signature`] or + /// if [`Trap`] at execution time occured. + /// + /// [`signature`]: #method.signature + /// [`Trap`]: #enum.Trap.html + pub fn invoke( + func: &FuncRef, + args: &[RuntimeValue], + externals: &mut E, + ) -> Result, Trap> { + check_function_args(func.signature(), &args)?; + match *func.as_internal() { + FuncInstanceInternal::Internal { .. } => { + let mut interpreter = Interpreter::new(func, args)?; + interpreter.start_execution(externals) + } + FuncInstanceInternal::Host { + ref host_func_index, + .. + } => externals.invoke_index(*host_func_index, args.into()), + } + } - /// Invoke the function, get a resumable handle. This handle can then be used to [`start_execution`]. If a - /// Host trap happens, caller can use [`resume_execution`] to feed the expected return value back in, and then - /// continue the execution. - /// - /// This is an experimental API, and this functionality may not be available in other WebAssembly engines. - /// - /// # Errors - /// - /// Returns `Err` if `args` types is not match function [`signature`]. - /// - /// [`signature`]: #method.signature - /// [`Trap`]: #enum.Trap.html - /// [`start_execution`]: struct.FuncInvocation.html#method.start_execution - /// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution - pub fn invoke_resumable<'args>( - func: &FuncRef, - args: &'args [RuntimeValue], - ) -> Result, Trap> { - check_function_args(func.signature(), &args)?; - match *func.as_internal() { - FuncInstanceInternal::Internal { .. } => { - let interpreter = Interpreter::new(func, args)?; - Ok(FuncInvocation { - kind: FuncInvocationKind::Internal(interpreter), - }) - } - FuncInstanceInternal::Host { - ref host_func_index, - .. - } => { - Ok(FuncInvocation { - kind: FuncInvocationKind::Host { - args, - host_func_index: *host_func_index, - finished: false, - }, - }) - }, - } - } + /// Invoke the function, get a resumable handle. This handle can then be used to [`start_execution`]. If a + /// Host trap happens, caller can use [`resume_execution`] to feed the expected return value back in, and then + /// continue the execution. + /// + /// This is an experimental API, and this functionality may not be available in other WebAssembly engines. + /// + /// # Errors + /// + /// Returns `Err` if `args` types is not match function [`signature`]. + /// + /// [`signature`]: #method.signature + /// [`Trap`]: #enum.Trap.html + /// [`start_execution`]: struct.FuncInvocation.html#method.start_execution + /// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution + pub fn invoke_resumable<'args>( + func: &FuncRef, + args: &'args [RuntimeValue], + ) -> Result, Trap> { + check_function_args(func.signature(), &args)?; + match *func.as_internal() { + FuncInstanceInternal::Internal { .. } => { + let interpreter = Interpreter::new(func, args)?; + Ok(FuncInvocation { + kind: FuncInvocationKind::Internal(interpreter), + }) + } + FuncInstanceInternal::Host { + ref host_func_index, + .. + } => Ok(FuncInvocation { + kind: FuncInvocationKind::Host { + args, + host_func_index: *host_func_index, + finished: false, + }, + }), + } + } } /// A resumable invocation error. #[derive(Debug)] pub enum ResumableError { - /// Trap happened. - Trap(Trap), - /// The invocation is not resumable. - /// - /// Invocations are only resumable if a host function is called, and the host function returns a trap of `Host` kind. For other cases, this error will be returned. This includes: - /// - The invocation is directly a host function. - /// - The invocation has not been started. - /// - The invocation returns normally or returns any trap other than `Host` kind. - /// - /// This error is returned by [`resume_execution`]. - /// - /// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution - NotResumable, - /// The invocation has already been started. - /// - /// This error is returned by [`start_execution`]. - /// - /// [`start_execution`]: struct.FuncInvocation.html#method.start_execution - AlreadyStarted, + /// Trap happened. + Trap(Trap), + /// The invocation is not resumable. + /// + /// Invocations are only resumable if a host function is called, and the host function returns a trap of `Host` kind. For other cases, this error will be returned. This includes: + /// - The invocation is directly a host function. + /// - The invocation has not been started. + /// - The invocation returns normally or returns any trap other than `Host` kind. + /// + /// This error is returned by [`resume_execution`]. + /// + /// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution + NotResumable, + /// The invocation has already been started. + /// + /// This error is returned by [`start_execution`]. + /// + /// [`start_execution`]: struct.FuncInvocation.html#method.start_execution + AlreadyStarted, } impl From for ResumableError { - fn from(trap: Trap) -> Self { - ResumableError::Trap(trap) - } + fn from(trap: Trap) -> Self { + ResumableError::Trap(trap) + } } /// A resumable invocation handle. This struct is returned by `FuncInstance::invoke_resumable`. pub struct FuncInvocation<'args> { - kind: FuncInvocationKind<'args>, + kind: FuncInvocationKind<'args>, } enum FuncInvocationKind<'args> { - Internal(Interpreter), - Host { - args: &'args [RuntimeValue], - host_func_index: usize, - finished: bool - }, + Internal(Interpreter), + Host { + args: &'args [RuntimeValue], + host_func_index: usize, + finished: bool, + }, } impl<'args> FuncInvocation<'args> { - /// Whether this invocation is currently resumable. - pub fn is_resumable(&self) -> bool { - match &self.kind { - &FuncInvocationKind::Internal(ref interpreter) => interpreter.state().is_resumable(), - &FuncInvocationKind::Host { .. } => false, - } - } + /// Whether this invocation is currently resumable. + pub fn is_resumable(&self) -> bool { + match &self.kind { + &FuncInvocationKind::Internal(ref interpreter) => interpreter.state().is_resumable(), + &FuncInvocationKind::Host { .. } => false, + } + } - /// If the invocation is resumable, the expected return value type to be feed back in. - pub fn resumable_value_type(&self) -> Option { - match &self.kind { - &FuncInvocationKind::Internal(ref interpreter) => { - match interpreter.state() { - &InterpreterState::Resumable(ref value_type) => value_type.clone(), - _ => None, - } - }, - &FuncInvocationKind::Host { .. } => None, - } - } + /// If the invocation is resumable, the expected return value type to be feed back in. + pub fn resumable_value_type(&self) -> Option { + match &self.kind { + &FuncInvocationKind::Internal(ref interpreter) => match interpreter.state() { + &InterpreterState::Resumable(ref value_type) => value_type.clone(), + _ => None, + }, + &FuncInvocationKind::Host { .. } => None, + } + } - /// Start the invocation execution. - pub fn start_execution<'externals, E: Externals + 'externals>(&mut self, externals: &'externals mut E) -> Result, ResumableError> { - match self.kind { - FuncInvocationKind::Internal(ref mut interpreter) => { - if interpreter.state() != &InterpreterState::Initialized { - return Err(ResumableError::AlreadyStarted); - } - Ok(interpreter.start_execution(externals)?) - }, - FuncInvocationKind::Host { ref args, ref mut finished, ref host_func_index } => { - if *finished { - return Err(ResumableError::AlreadyStarted); - } - *finished = true; - Ok(externals.invoke_index(*host_func_index, args.clone().into())?) - }, - } - } + /// Start the invocation execution. + pub fn start_execution<'externals, E: Externals + 'externals>( + &mut self, + externals: &'externals mut E, + ) -> Result, ResumableError> { + match self.kind { + FuncInvocationKind::Internal(ref mut interpreter) => { + if interpreter.state() != &InterpreterState::Initialized { + return Err(ResumableError::AlreadyStarted); + } + Ok(interpreter.start_execution(externals)?) + } + FuncInvocationKind::Host { + ref args, + ref mut finished, + ref host_func_index, + } => { + if *finished { + return Err(ResumableError::AlreadyStarted); + } + *finished = true; + Ok(externals.invoke_index(*host_func_index, args.clone().into())?) + } + } + } - /// Resume an execution if a previous trap of Host kind happened. - /// - /// `return_val` must be of the value type [`resumable_value_type`], defined by the host function import. Otherwise, - /// `UnexpectedSignature` trap will be returned. The current invocation must also be resumable - /// [`is_resumable`]. Otherwise, a `NotResumable` error will be returned. - /// - /// [`resumable_value_type`]: #method.resumable_value_type - /// [`is_resumable`]: #method.is_resumable - pub fn resume_execution<'externals, E: Externals + 'externals>(&mut self, return_val: Option, externals: &'externals mut E) -> Result, ResumableError> { - match self.kind { - FuncInvocationKind::Internal(ref mut interpreter) => { - if !interpreter.state().is_resumable() { - return Err(ResumableError::AlreadyStarted); - } - Ok(interpreter.resume_execution(return_val, externals)?) - }, - FuncInvocationKind::Host { .. } => { - return Err(ResumableError::NotResumable); - }, - } - } + /// Resume an execution if a previous trap of Host kind happened. + /// + /// `return_val` must be of the value type [`resumable_value_type`], defined by the host function import. Otherwise, + /// `UnexpectedSignature` trap will be returned. The current invocation must also be resumable + /// [`is_resumable`]. Otherwise, a `NotResumable` error will be returned. + /// + /// [`resumable_value_type`]: #method.resumable_value_type + /// [`is_resumable`]: #method.is_resumable + pub fn resume_execution<'externals, E: Externals + 'externals>( + &mut self, + return_val: Option, + externals: &'externals mut E, + ) -> Result, ResumableError> { + match self.kind { + FuncInvocationKind::Internal(ref mut interpreter) => { + if !interpreter.state().is_resumable() { + return Err(ResumableError::AlreadyStarted); + } + Ok(interpreter.resume_execution(return_val, externals)?) + } + FuncInvocationKind::Host { .. } => { + return Err(ResumableError::NotResumable); + } + } + } } #[derive(Clone, Debug)] pub struct FuncBody { - pub locals: Vec, - pub code: isa::Instructions, + pub locals: Vec, + pub code: isa::Instructions, } diff --git a/src/global.rs b/src/global.rs index fe7e276..cde0d63 100644 --- a/src/global.rs +++ b/src/global.rs @@ -1,9 +1,9 @@ use alloc::rc::Rc; use core::cell::Cell; +use parity_wasm::elements::ValueType as EValueType; +use types::ValueType; use value::RuntimeValue; use Error; -use types::ValueType; -use parity_wasm::elements::{ValueType as EValueType}; /// Reference to a global variable (See [`GlobalInstance`] for details). /// @@ -14,10 +14,10 @@ use parity_wasm::elements::{ValueType as EValueType}; pub struct GlobalRef(Rc); impl ::core::ops::Deref for GlobalRef { - type Target = GlobalInstance; - fn deref(&self) -> &GlobalInstance { - &self.0 - } + type Target = GlobalInstance; + fn deref(&self) -> &GlobalInstance { + &self.0 + } } /// Runtime representation of a global variable (or `global` for short). @@ -33,57 +33,59 @@ impl ::core::ops::Deref for GlobalRef { /// [`I64`]: enum.RuntimeValue.html#variant.I64 #[derive(Debug)] pub struct GlobalInstance { - val: Cell, - mutable: bool, + val: Cell, + mutable: bool, } impl GlobalInstance { - /// Allocate a global variable instance. - /// - /// Since it is possible to export only immutable globals, - /// users likely want to set `mutable` to `false`. - pub fn alloc(val: RuntimeValue, mutable: bool) -> GlobalRef { - GlobalRef(Rc::new(GlobalInstance { - val: Cell::new(val), - mutable, - })) - } + /// Allocate a global variable instance. + /// + /// Since it is possible to export only immutable globals, + /// users likely want to set `mutable` to `false`. + pub fn alloc(val: RuntimeValue, mutable: bool) -> GlobalRef { + GlobalRef(Rc::new(GlobalInstance { + val: Cell::new(val), + mutable, + })) + } - /// Change the value of this global variable. - /// - /// # Errors - /// - /// Returns `Err` if this global isn't mutable or if - /// type of `val` doesn't match global's type. - pub fn set(&self, val: RuntimeValue) -> Result<(), Error> { - if !self.mutable { - return Err(Error::Global("Attempt to change an immutable variable".into())); - } - if self.value_type() != val.value_type() { - return Err(Error::Global("Attempt to change variable type".into())); - } - self.val.set(val); - Ok(()) - } + /// Change the value of this global variable. + /// + /// # Errors + /// + /// Returns `Err` if this global isn't mutable or if + /// type of `val` doesn't match global's type. + pub fn set(&self, val: RuntimeValue) -> Result<(), Error> { + if !self.mutable { + return Err(Error::Global( + "Attempt to change an immutable variable".into(), + )); + } + if self.value_type() != val.value_type() { + return Err(Error::Global("Attempt to change variable type".into())); + } + self.val.set(val); + Ok(()) + } - /// Get the value of this global variable. - pub fn get(&self) -> RuntimeValue { - self.val.get() - } + /// Get the value of this global variable. + pub fn get(&self) -> RuntimeValue { + self.val.get() + } - /// Returns if this global variable is mutable. - /// - /// Note: Imported and/or exported globals are always immutable. - pub fn is_mutable(&self) -> bool { - self.mutable - } + /// Returns if this global variable is mutable. + /// + /// Note: Imported and/or exported globals are always immutable. + pub fn is_mutable(&self) -> bool { + self.mutable + } - /// Returns value type of this global variable. - pub fn value_type(&self) -> ValueType { - self.val.get().value_type() - } + /// Returns value type of this global variable. + pub fn value_type(&self) -> ValueType { + self.val.get().value_type() + } - pub(crate) fn elements_value_type(&self) -> EValueType { - self.value_type().into_elements() - } + pub(crate) fn elements_value_type(&self) -> EValueType { + self.value_type().into_elements() + } } diff --git a/src/host.rs b/src/host.rs index 998f16f..3700fc7 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,6 +1,6 @@ use core::any::TypeId; -use value::{RuntimeValue, FromRuntimeValue}; -use {TrapKind, Trap}; +use value::{FromRuntimeValue, RuntimeValue}; +use {Trap, TrapKind}; /// Wrapper around slice of [`RuntimeValue`] for using it /// as an argument list conveniently. @@ -10,55 +10,64 @@ use {TrapKind, Trap}; pub struct RuntimeArgs<'a>(&'a [RuntimeValue]); impl<'a> From<&'a [RuntimeValue]> for RuntimeArgs<'a> { - fn from(inner: &'a [RuntimeValue]) -> Self { - RuntimeArgs(inner) - } + fn from(inner: &'a [RuntimeValue]) -> Self { + RuntimeArgs(inner) + } } impl<'a> AsRef<[RuntimeValue]> for RuntimeArgs<'a> { - fn as_ref(&self) -> &[RuntimeValue] { - self.0 - } + fn as_ref(&self) -> &[RuntimeValue] { + self.0 + } } impl<'a> RuntimeArgs<'a> { - /// Extract argument by index `idx`. - /// - /// # Errors - /// - /// Returns `Err` if cast is invalid or not enough arguments. - pub fn nth_checked(&self, idx: usize) -> Result where T: FromRuntimeValue { - Ok(self.nth_value_checked(idx)?.try_into().ok_or_else(|| TrapKind::UnexpectedSignature)?) - } + /// Extract argument by index `idx`. + /// + /// # Errors + /// + /// Returns `Err` if cast is invalid or not enough arguments. + pub fn nth_checked(&self, idx: usize) -> Result + where + T: FromRuntimeValue, + { + Ok(self + .nth_value_checked(idx)? + .try_into() + .ok_or_else(|| TrapKind::UnexpectedSignature)?) + } - /// Extract argument as a [`RuntimeValue`] by index `idx`. - /// - /// # Errors - /// - /// Returns `Err` if this list has not enough arguments. - /// - /// [`RuntimeValue`]: enum.RuntimeValue.html - pub fn nth_value_checked(&self, idx: usize) -> Result { - if self.0.len() <= idx { - return Err(TrapKind::UnexpectedSignature.into()); - } - Ok(self.0[idx]) - } + /// Extract argument as a [`RuntimeValue`] by index `idx`. + /// + /// # Errors + /// + /// Returns `Err` if this list has not enough arguments. + /// + /// [`RuntimeValue`]: enum.RuntimeValue.html + pub fn nth_value_checked(&self, idx: usize) -> Result { + if self.0.len() <= idx { + return Err(TrapKind::UnexpectedSignature.into()); + } + Ok(self.0[idx]) + } - /// Extract argument by index `idx`. - /// - /// # Panics - /// - /// Panics if cast is invalid or not enough arguments. - pub fn nth(&self, idx: usize) -> T where T: FromRuntimeValue { - let value = self.nth_value_checked(idx).expect("Invalid argument index"); - value.try_into().expect("Unexpected argument type") - } + /// Extract argument by index `idx`. + /// + /// # Panics + /// + /// Panics if cast is invalid or not enough arguments. + pub fn nth(&self, idx: usize) -> T + where + T: FromRuntimeValue, + { + let value = self.nth_value_checked(idx).expect("Invalid argument index"); + value.try_into().expect("Unexpected argument type") + } - /// Total number of arguments - pub fn len(&self) -> usize { - self.0.len() - } + /// Total number of arguments + pub fn len(&self) -> usize { + self.0.len() + } } /// Trait that allows the host to return custom error. @@ -99,31 +108,31 @@ impl<'a> RuntimeArgs<'a> { /// } /// ``` pub trait HostError: 'static + ::core::fmt::Display + ::core::fmt::Debug + Send + Sync { - #[doc(hidden)] - fn __private_get_type_id__(&self) -> TypeId { - TypeId::of::() - } + #[doc(hidden)] + fn __private_get_type_id__(&self) -> TypeId { + TypeId::of::() + } } impl HostError { - /// Attempt to downcast this `HostError` to a concrete type by reference. - pub fn downcast_ref(&self) -> Option<&T> { - if self.__private_get_type_id__() == TypeId::of::() { - unsafe { Some(&*(self as *const HostError as *const T)) } - } else { - None - } - } + /// Attempt to downcast this `HostError` to a concrete type by reference. + pub fn downcast_ref(&self) -> Option<&T> { + if self.__private_get_type_id__() == TypeId::of::() { + unsafe { Some(&*(self as *const HostError as *const T)) } + } else { + None + } + } - /// Attempt to downcast this `HostError` to a concrete type by mutable - /// reference. - pub fn downcast_mut(&mut self) -> Option<&mut T> { - if self.__private_get_type_id__() == TypeId::of::() { - unsafe { Some(&mut *(self as *mut HostError as *mut T)) } - } else { - None - } - } + /// Attempt to downcast this `HostError` to a concrete type by mutable + /// reference. + pub fn downcast_mut(&mut self) -> Option<&mut T> { + if self.__private_get_type_id__() == TypeId::of::() { + unsafe { Some(&mut *(self as *mut HostError as *mut T)) } + } else { + None + } + } } /// Trait that allows to implement host functions. @@ -189,13 +198,13 @@ impl HostError { /// )) /// } /// }; -/// +/// /// if !self.check_signature(index, signature) { /// return Err(Error::Instantiation( /// format!("Export {} has a bad signature", field_name) /// )); /// } -/// +/// /// Ok(FuncInstance::alloc_host( /// Signature::new(&[ValueType::I32, ValueType::I32][..], Some(ValueType::I32)), /// index, @@ -204,12 +213,12 @@ impl HostError { /// } /// ``` pub trait Externals { - /// Perform invoke of a host function by specified `index`. - fn invoke_index( - &mut self, - index: usize, - args: RuntimeArgs, - ) -> Result, Trap>; + /// Perform invoke of a host function by specified `index`. + fn invoke_index( + &mut self, + index: usize, + args: RuntimeArgs, + ) -> Result, Trap>; } /// Implementation of [`Externals`] that just traps on [`invoke_index`]. @@ -219,35 +228,34 @@ pub trait Externals { pub struct NopExternals; impl Externals for NopExternals { - fn invoke_index( - &mut self, - _index: usize, - _args: RuntimeArgs, - ) -> Result, Trap> { - Err(TrapKind::Unreachable.into()) - } + fn invoke_index( + &mut self, + _index: usize, + _args: RuntimeArgs, + ) -> Result, Trap> { + Err(TrapKind::Unreachable.into()) + } } #[cfg(test)] mod tests { - use value::RuntimeValue; - use super::{RuntimeArgs, HostError}; + use super::{HostError, RuntimeArgs}; + use value::RuntimeValue; - #[test] - fn i32_runtime_args() { - let args: RuntimeArgs = (&[RuntimeValue::I32(0)][..]).into(); - let val: i32 = args.nth_checked(0).unwrap(); - assert_eq!(val, 0); - } + #[test] + fn i32_runtime_args() { + let args: RuntimeArgs = (&[RuntimeValue::I32(0)][..]).into(); + let val: i32 = args.nth_checked(0).unwrap(); + assert_eq!(val, 0); + } - #[test] - fn i64_invalid_arg_cast() { - let args: RuntimeArgs = (&[RuntimeValue::I64(90534534545322)][..]).into(); - assert!(args.nth_checked::(0).is_err()); - } + #[test] + fn i64_invalid_arg_cast() { + let args: RuntimeArgs = (&[RuntimeValue::I64(90534534545322)][..]).into(); + assert!(args.nth_checked::(0).is_err()); + } - // Tests that `HostError` trait is object safe. - fn _host_error_is_object_safe(_: &HostError) { - } + // Tests that `HostError` trait is object safe. + fn _host_error_is_object_safe(_: &HostError) {} } diff --git a/src/imports.rs b/src/imports.rs index bb52eb4..c0c7852 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -1,20 +1,19 @@ #[allow(unused_imports)] use alloc::prelude::*; -#[cfg(feature = "std")] -use std::collections::HashMap; #[cfg(not(feature = "std"))] use hashmap_core::HashMap; +#[cfg(feature = "std")] +use std::collections::HashMap; +use func::FuncRef; use global::GlobalRef; use memory::MemoryRef; -use func::FuncRef; -use table::TableRef; use module::ModuleRef; -use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor}; +use table::TableRef; +use types::{GlobalDescriptor, MemoryDescriptor, TableDescriptor}; use {Error, Signature}; - /// Resolver of a module's dependencies. /// /// A module have dependencies in a form of a list of imports (i.e. @@ -27,56 +26,55 @@ use {Error, Signature}; /// /// [`ImportsBuilder`]: struct.ImportsBuilder.html pub trait ImportResolver { + /// Resolve a function. + /// + /// Returned function should match given `signature`, i.e. all parameter types and return value should have exact match. + /// Otherwise, link-time error will occur. + fn resolve_func( + &self, + _module_name: &str, + field_name: &str, + _signature: &Signature, + ) -> Result; - /// Resolve a function. - /// - /// Returned function should match given `signature`, i.e. all parameter types and return value should have exact match. - /// Otherwise, link-time error will occur. - fn resolve_func( - &self, - _module_name: &str, - field_name: &str, - _signature: &Signature, - ) -> Result; + /// Resolve a global variable. + /// + /// Returned global should match given `descriptor`, i.e. type and mutability + /// should match. Otherwise, link-time error will occur. + fn resolve_global( + &self, + module_name: &str, + field_name: &str, + descriptor: &GlobalDescriptor, + ) -> Result; - /// Resolve a global variable. - /// - /// Returned global should match given `descriptor`, i.e. type and mutability - /// should match. Otherwise, link-time error will occur. - fn resolve_global( - &self, - module_name: &str, - field_name: &str, - descriptor: &GlobalDescriptor, - ) -> Result; + /// Resolve a memory. + /// + /// Returned memory should match requested memory (described by the `descriptor`), + /// i.e. initial size of a returned memory should be equal or larger than requested memory. + /// Furthermore, if requested memory have maximum size, returned memory either should have + /// equal or larger maximum size or have no maximum size at all. + /// If returned memory doesn't match the requested then link-time error will occur. + fn resolve_memory( + &self, + module_name: &str, + field_name: &str, + descriptor: &MemoryDescriptor, + ) -> Result; - /// Resolve a memory. - /// - /// Returned memory should match requested memory (described by the `descriptor`), - /// i.e. initial size of a returned memory should be equal or larger than requested memory. - /// Furthermore, if requested memory have maximum size, returned memory either should have - /// equal or larger maximum size or have no maximum size at all. - /// If returned memory doesn't match the requested then link-time error will occur. - fn resolve_memory( - &self, - module_name: &str, - field_name: &str, - descriptor: &MemoryDescriptor, - ) -> Result; - - /// Resolve a table. - /// - /// Returned table should match requested table (described by the `descriptor`), - /// i.e. initial size of a returned table should be equal or larger than requested table. - /// Furthermore, if requested memory have maximum size, returned memory either should have - /// equal or larger maximum size or have no maximum size at all. - /// If returned table doesn't match the requested then link-time error will occur. - fn resolve_table( - &self, - module_name: &str, - field_name: &str, - descriptor: &TableDescriptor, - ) -> Result; + /// Resolve a table. + /// + /// Returned table should match requested table (described by the `descriptor`), + /// i.e. initial size of a returned table should be equal or larger than requested table. + /// Furthermore, if requested memory have maximum size, returned memory either should have + /// equal or larger maximum size or have no maximum size at all. + /// If returned table doesn't match the requested then link-time error will occur. + fn resolve_table( + &self, + module_name: &str, + field_name: &str, + descriptor: &TableDescriptor, + ) -> Result; } /// Convenience builder of [`ImportResolver`]. @@ -108,216 +106,208 @@ pub trait ImportResolver { /// [`ImportResolver`]: trait.ImportResolver.html /// [`ModuleImportResolver`]: trait.ModuleImportResolver.html pub struct ImportsBuilder<'a> { - modules: HashMap, + modules: HashMap, } impl<'a> Default for ImportsBuilder<'a> { - fn default() -> Self { - Self::new() - } + fn default() -> Self { + Self::new() + } } impl<'a> ImportsBuilder<'a> { - /// Create an empty `ImportsBuilder`. - pub fn new() -> ImportsBuilder<'a> { - ImportsBuilder { modules: HashMap::new() } - } + /// Create an empty `ImportsBuilder`. + pub fn new() -> ImportsBuilder<'a> { + ImportsBuilder { + modules: HashMap::new(), + } + } - /// Register an resolver by a name. - pub fn with_resolver>( - mut self, - name: N, - resolver: &'a ModuleImportResolver, - ) -> Self { - self.modules.insert(name.into(), resolver); - self - } + /// Register an resolver by a name. + pub fn with_resolver>( + mut self, + name: N, + resolver: &'a ModuleImportResolver, + ) -> Self { + self.modules.insert(name.into(), resolver); + self + } - /// Register an resolver by a name. - /// - /// Mutable borrowed version. - pub fn push_resolver>(&mut self, name: N, resolver: &'a ModuleImportResolver) { - self.modules.insert(name.into(), resolver); - } + /// Register an resolver by a name. + /// + /// Mutable borrowed version. + pub fn push_resolver>(&mut self, name: N, resolver: &'a ModuleImportResolver) { + self.modules.insert(name.into(), resolver); + } - fn resolver(&self, name: &str) -> Option<&ModuleImportResolver> { - self.modules.get(name).cloned() - } + fn resolver(&self, name: &str) -> Option<&ModuleImportResolver> { + self.modules.get(name).cloned() + } } impl<'a> ImportResolver for ImportsBuilder<'a> { - fn resolve_func( - &self, - module_name: &str, - field_name: &str, - signature: &Signature, - ) -> Result { - self.resolver(module_name).ok_or_else(|| - Error::Instantiation(format!("Module {} not found", module_name)) - )?.resolve_func(field_name, signature) - } + fn resolve_func( + &self, + module_name: &str, + field_name: &str, + signature: &Signature, + ) -> Result { + self.resolver(module_name) + .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))? + .resolve_func(field_name, signature) + } - fn resolve_global( - &self, - module_name: &str, - field_name: &str, - global_type: &GlobalDescriptor, - ) -> Result { - self.resolver(module_name).ok_or_else(|| - Error::Instantiation(format!("Module {} not found", module_name)) - )?.resolve_global(field_name, global_type) - } + fn resolve_global( + &self, + module_name: &str, + field_name: &str, + global_type: &GlobalDescriptor, + ) -> Result { + self.resolver(module_name) + .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))? + .resolve_global(field_name, global_type) + } - fn resolve_memory( - &self, - module_name: &str, - field_name: &str, - memory_type: &MemoryDescriptor, - ) -> Result { - self.resolver(module_name).ok_or_else(|| - Error::Instantiation(format!("Module {} not found", module_name)) - )?.resolve_memory(field_name, memory_type) - } + fn resolve_memory( + &self, + module_name: &str, + field_name: &str, + memory_type: &MemoryDescriptor, + ) -> Result { + self.resolver(module_name) + .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))? + .resolve_memory(field_name, memory_type) + } - fn resolve_table( - &self, - module_name: &str, - field_name: &str, - table_type: &TableDescriptor, - ) -> Result { - self.resolver(module_name).ok_or_else(|| - Error::Instantiation(format!("Module {} not found", module_name)) - )?.resolve_table(field_name, table_type) - } + fn resolve_table( + &self, + module_name: &str, + field_name: &str, + table_type: &TableDescriptor, + ) -> Result { + self.resolver(module_name) + .ok_or_else(|| Error::Instantiation(format!("Module {} not found", module_name)))? + .resolve_table(field_name, table_type) + } } /// Version of [`ImportResolver`] specialized for a single module. /// /// [`ImportResolver`]: trait.ImportResolver.html pub trait ModuleImportResolver { - /// Resolve a function. - /// - /// See [`ImportResolver::resolve_func`] for details. - /// - /// [`ImportResolver::resolve_func`]: trait.ImportResolver.html#tymethod.resolve_func - fn resolve_func( - &self, - field_name: &str, - _signature: &Signature, - ) -> Result { - Err(Error::Instantiation( - format!("Export {} not found", field_name), - )) - } + /// Resolve a function. + /// + /// See [`ImportResolver::resolve_func`] for details. + /// + /// [`ImportResolver::resolve_func`]: trait.ImportResolver.html#tymethod.resolve_func + fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result { + Err(Error::Instantiation(format!( + "Export {} not found", + field_name + ))) + } - /// Resolve a global variable. - /// - /// See [`ImportResolver::resolve_global`] for details. - /// - /// [`ImportResolver::resolve_global`]: trait.ImportResolver.html#tymethod.resolve_global - fn resolve_global( - &self, - field_name: &str, - _global_type: &GlobalDescriptor, - ) -> Result { - Err(Error::Instantiation( - format!("Export {} not found", field_name), - )) - } + /// Resolve a global variable. + /// + /// See [`ImportResolver::resolve_global`] for details. + /// + /// [`ImportResolver::resolve_global`]: trait.ImportResolver.html#tymethod.resolve_global + fn resolve_global( + &self, + field_name: &str, + _global_type: &GlobalDescriptor, + ) -> Result { + Err(Error::Instantiation(format!( + "Export {} not found", + field_name + ))) + } - /// Resolve a memory. - /// - /// See [`ImportResolver::resolve_memory`] for details. - /// - /// [`ImportResolver::resolve_memory`]: trait.ImportResolver.html#tymethod.resolve_memory - fn resolve_memory( - &self, - field_name: &str, - _memory_type: &MemoryDescriptor, - ) -> Result { - Err(Error::Instantiation( - format!("Export {} not found", field_name), - )) - } + /// Resolve a memory. + /// + /// See [`ImportResolver::resolve_memory`] for details. + /// + /// [`ImportResolver::resolve_memory`]: trait.ImportResolver.html#tymethod.resolve_memory + fn resolve_memory( + &self, + field_name: &str, + _memory_type: &MemoryDescriptor, + ) -> Result { + Err(Error::Instantiation(format!( + "Export {} not found", + field_name + ))) + } - /// Resolve a table. - /// - /// See [`ImportResolver::resolve_table`] for details. - /// - /// [`ImportResolver::resolve_table`]: trait.ImportResolver.html#tymethod.resolve_table - fn resolve_table( - &self, - field_name: &str, - _table_type: &TableDescriptor, - ) -> Result { - Err(Error::Instantiation( - format!("Export {} not found", field_name), - )) - } + /// Resolve a table. + /// + /// See [`ImportResolver::resolve_table`] for details. + /// + /// [`ImportResolver::resolve_table`]: trait.ImportResolver.html#tymethod.resolve_table + fn resolve_table( + &self, + field_name: &str, + _table_type: &TableDescriptor, + ) -> Result { + Err(Error::Instantiation(format!( + "Export {} not found", + field_name + ))) + } } impl ModuleImportResolver for ModuleRef { - fn resolve_func( - &self, - field_name: &str, - _signature: &Signature, - ) -> Result { - Ok(self.export_by_name(field_name) - .ok_or_else(|| { - Error::Instantiation(format!("Export {} not found", field_name)) - })? - .as_func() - .cloned() - .ok_or_else(|| { - Error::Instantiation(format!("Export {} is not a function", field_name)) - })?) - } + fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result { + Ok(self + .export_by_name(field_name) + .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))? + .as_func() + .cloned() + .ok_or_else(|| { + Error::Instantiation(format!("Export {} is not a function", field_name)) + })?) + } - fn resolve_global( - &self, - field_name: &str, - _global_type: &GlobalDescriptor, - ) -> Result { - Ok(self.export_by_name(field_name) - .ok_or_else(|| { - Error::Instantiation(format!("Export {} not found", field_name)) - })? - .as_global() - .cloned() - .ok_or_else(|| { - Error::Instantiation(format!("Export {} is not a global", field_name)) - })?) - } + fn resolve_global( + &self, + field_name: &str, + _global_type: &GlobalDescriptor, + ) -> Result { + Ok(self + .export_by_name(field_name) + .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))? + .as_global() + .cloned() + .ok_or_else(|| { + Error::Instantiation(format!("Export {} is not a global", field_name)) + })?) + } - fn resolve_memory( - &self, - field_name: &str, - _memory_type: &MemoryDescriptor, - ) -> Result { - Ok(self.export_by_name(field_name) - .ok_or_else(|| { - Error::Instantiation(format!("Export {} not found", field_name)) - })? - .as_memory() - .cloned() - .ok_or_else(|| { - Error::Instantiation(format!("Export {} is not a memory", field_name)) - })?) - } + fn resolve_memory( + &self, + field_name: &str, + _memory_type: &MemoryDescriptor, + ) -> Result { + Ok(self + .export_by_name(field_name) + .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))? + .as_memory() + .cloned() + .ok_or_else(|| { + Error::Instantiation(format!("Export {} is not a memory", field_name)) + })?) + } - fn resolve_table( - &self, - field_name: &str, - _table_type: &TableDescriptor, - ) -> Result { - Ok(self.export_by_name(field_name) - .ok_or_else(|| { - Error::Instantiation(format!("Export {} not found", field_name)) - })? - .as_table() - .cloned() - .ok_or_else(|| { - Error::Instantiation(format!("Export {} is not a table", field_name)) - })?) - } + fn resolve_table( + &self, + field_name: &str, + _table_type: &TableDescriptor, + ) -> Result { + Ok(self + .export_by_name(field_name) + .ok_or_else(|| Error::Instantiation(format!("Export {} not found", field_name)))? + .as_table() + .cloned() + .ok_or_else(|| Error::Instantiation(format!("Export {} is not a table", field_name)))?) + } } diff --git a/src/isa.rs b/src/isa.rs index e4cdde6..0325f4e 100644 --- a/src/isa.rs +++ b/src/isa.rs @@ -76,259 +76,259 @@ use alloc::prelude::*; /// values at the moment. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Keep { - None, - /// Pop one value from the yet-to-be-discarded stack frame to the - /// current stack frame. - Single, + None, + /// Pop one value from the yet-to-be-discarded stack frame to the + /// current stack frame. + Single, } /// Specifies how many values we should keep and how many we should drop. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct DropKeep { - pub drop: u32, - pub keep: Keep, + pub drop: u32, + pub keep: Keep, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Target { - pub dst_pc: u32, - pub drop_keep: DropKeep, + pub dst_pc: u32, + pub drop_keep: DropKeep, } /// A relocation entry that specifies. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Reloc { - /// Patch the destination of the branch instruction (br, br_eqz, br_nez) - /// at the specified pc. - Br { pc: u32 }, - /// Patch the specified destination index inside of br_table instruction at - /// the specified pc. - BrTable { pc: u32, idx: usize }, + /// Patch the destination of the branch instruction (br, br_eqz, br_nez) + /// at the specified pc. + Br { pc: u32 }, + /// Patch the specified destination index inside of br_table instruction at + /// the specified pc. + BrTable { pc: u32, idx: usize }, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct BrTargets<'a> { - stream: &'a [InstructionInternal], + stream: &'a [InstructionInternal], } impl<'a> BrTargets<'a> { - pub(crate) fn from_internal(targets: &'a [InstructionInternal]) -> Self { - BrTargets { stream: targets } - } + pub(crate) fn from_internal(targets: &'a [InstructionInternal]) -> Self { + BrTargets { stream: targets } + } - #[inline] - pub fn get(&self, index: u32) -> Target { - match self.stream[index.min(self.stream.len() as u32 - 1) as usize] { - InstructionInternal::BrTableTarget(target) => target, - _ => panic!("BrTable has incorrect target count"), - } - } + #[inline] + pub fn get(&self, index: u32) -> Target { + match self.stream[index.min(self.stream.len() as u32 - 1) as usize] { + InstructionInternal::BrTableTarget(target) => target, + _ => panic!("BrTable has incorrect target count"), + } + } } /// The main interpreted instruction type. This is what is returned by `InstructionIter`, but /// it is not what is stored internally. For that, see `InstructionInternal`. #[derive(Debug, Clone, PartialEq)] pub enum Instruction<'a> { - /// Push a local variable or an argument from the specified depth. - GetLocal(u32), + /// Push a local variable or an argument from the specified depth. + GetLocal(u32), - /// Pop a value and put it in at the specified depth. - SetLocal(u32), + /// Pop a value and put it in at the specified depth. + SetLocal(u32), - /// Copy a value to the specified depth. - TeeLocal(u32), + /// Copy a value to the specified depth. + TeeLocal(u32), - /// Similar to the Wasm ones, but instead of a label depth - /// they specify direct PC. - Br(Target), - BrIfEqz(Target), - BrIfNez(Target), + /// Similar to the Wasm ones, but instead of a label depth + /// they specify direct PC. + Br(Target), + BrIfEqz(Target), + BrIfNez(Target), - /// br_table [t1 t2 t3 .. tn] tdefault - /// - /// Pops the value from the stack. Then this value is used as an index - /// to the branch table. - /// - /// However, the last target represents the default target. So if the index - /// is greater than length of the branch table, then the last index will be used. - /// - /// Validation ensures that there should be at least one target. - BrTable(BrTargets<'a>), + /// br_table [t1 t2 t3 .. tn] tdefault + /// + /// Pops the value from the stack. Then this value is used as an index + /// to the branch table. + /// + /// However, the last target represents the default target. So if the index + /// is greater than length of the branch table, then the last index will be used. + /// + /// Validation ensures that there should be at least one target. + BrTable(BrTargets<'a>), - Unreachable, - Return(DropKeep), + Unreachable, + Return(DropKeep), - Call(u32), - CallIndirect(u32), + Call(u32), + CallIndirect(u32), - Drop, - Select, + Drop, + Select, - GetGlobal(u32), - SetGlobal(u32), + GetGlobal(u32), + SetGlobal(u32), - I32Load(u32), - I64Load(u32), - F32Load(u32), - F64Load(u32), - I32Load8S(u32), - I32Load8U(u32), - I32Load16S(u32), - I32Load16U(u32), - I64Load8S(u32), - I64Load8U(u32), - I64Load16S(u32), - I64Load16U(u32), - I64Load32S(u32), - I64Load32U(u32), - I32Store(u32), - I64Store(u32), - F32Store(u32), - F64Store(u32), - I32Store8(u32), - I32Store16(u32), - I64Store8(u32), - I64Store16(u32), - I64Store32(u32), + I32Load(u32), + I64Load(u32), + F32Load(u32), + F64Load(u32), + I32Load8S(u32), + I32Load8U(u32), + I32Load16S(u32), + I32Load16U(u32), + I64Load8S(u32), + I64Load8U(u32), + I64Load16S(u32), + I64Load16U(u32), + I64Load32S(u32), + I64Load32U(u32), + I32Store(u32), + I64Store(u32), + F32Store(u32), + F64Store(u32), + I32Store8(u32), + I32Store16(u32), + I64Store8(u32), + I64Store16(u32), + I64Store32(u32), - CurrentMemory, - GrowMemory, + CurrentMemory, + GrowMemory, - I32Const(i32), - I64Const(i64), - F32Const(u32), - F64Const(u64), + I32Const(i32), + I64Const(i64), + F32Const(u32), + F64Const(u64), - I32Eqz, - I32Eq, - I32Ne, - I32LtS, - I32LtU, - I32GtS, - I32GtU, - I32LeS, - I32LeU, - I32GeS, - I32GeU, + I32Eqz, + I32Eq, + I32Ne, + I32LtS, + I32LtU, + I32GtS, + I32GtU, + I32LeS, + I32LeU, + I32GeS, + I32GeU, - I64Eqz, - I64Eq, - I64Ne, - I64LtS, - I64LtU, - I64GtS, - I64GtU, - I64LeS, - I64LeU, - I64GeS, - I64GeU, + I64Eqz, + I64Eq, + I64Ne, + I64LtS, + I64LtU, + I64GtS, + I64GtU, + I64LeS, + I64LeU, + I64GeS, + I64GeU, - F32Eq, - F32Ne, - F32Lt, - F32Gt, - F32Le, - F32Ge, + F32Eq, + F32Ne, + F32Lt, + F32Gt, + F32Le, + F32Ge, - F64Eq, - F64Ne, - F64Lt, - F64Gt, - F64Le, - F64Ge, + F64Eq, + F64Ne, + F64Lt, + F64Gt, + F64Le, + F64Ge, - I32Clz, - I32Ctz, - I32Popcnt, - I32Add, - I32Sub, - I32Mul, - I32DivS, - I32DivU, - I32RemS, - I32RemU, - I32And, - I32Or, - I32Xor, - I32Shl, - I32ShrS, - I32ShrU, - I32Rotl, - I32Rotr, + I32Clz, + I32Ctz, + I32Popcnt, + I32Add, + I32Sub, + I32Mul, + I32DivS, + I32DivU, + I32RemS, + I32RemU, + I32And, + I32Or, + I32Xor, + I32Shl, + I32ShrS, + I32ShrU, + I32Rotl, + I32Rotr, - I64Clz, - I64Ctz, - I64Popcnt, - I64Add, - I64Sub, - I64Mul, - I64DivS, - I64DivU, - I64RemS, - I64RemU, - I64And, - I64Or, - I64Xor, - I64Shl, - I64ShrS, - I64ShrU, - I64Rotl, - I64Rotr, - F32Abs, - F32Neg, - F32Ceil, - F32Floor, - F32Trunc, - F32Nearest, - F32Sqrt, - F32Add, - F32Sub, - F32Mul, - F32Div, - F32Min, - F32Max, - F32Copysign, - F64Abs, - F64Neg, - F64Ceil, - F64Floor, - F64Trunc, - F64Nearest, - F64Sqrt, - F64Add, - F64Sub, - F64Mul, - F64Div, - F64Min, - F64Max, - F64Copysign, + I64Clz, + I64Ctz, + I64Popcnt, + I64Add, + I64Sub, + I64Mul, + I64DivS, + I64DivU, + I64RemS, + I64RemU, + I64And, + I64Or, + I64Xor, + I64Shl, + I64ShrS, + I64ShrU, + I64Rotl, + I64Rotr, + F32Abs, + F32Neg, + F32Ceil, + F32Floor, + F32Trunc, + F32Nearest, + F32Sqrt, + F32Add, + F32Sub, + F32Mul, + F32Div, + F32Min, + F32Max, + F32Copysign, + F64Abs, + F64Neg, + F64Ceil, + F64Floor, + F64Trunc, + F64Nearest, + F64Sqrt, + F64Add, + F64Sub, + F64Mul, + F64Div, + F64Min, + F64Max, + F64Copysign, - I32WrapI64, - I32TruncSF32, - I32TruncUF32, - I32TruncSF64, - I32TruncUF64, - I64ExtendSI32, - I64ExtendUI32, - I64TruncSF32, - I64TruncUF32, - I64TruncSF64, - I64TruncUF64, - F32ConvertSI32, - F32ConvertUI32, - F32ConvertSI64, - F32ConvertUI64, - F32DemoteF64, - F64ConvertSI32, - F64ConvertUI32, - F64ConvertSI64, - F64ConvertUI64, - F64PromoteF32, + I32WrapI64, + I32TruncSF32, + I32TruncUF32, + I32TruncSF64, + I32TruncUF64, + I64ExtendSI32, + I64ExtendUI32, + I64TruncSF32, + I64TruncUF32, + I64TruncSF64, + I64TruncUF64, + F32ConvertSI32, + F32ConvertUI32, + F32ConvertSI64, + F32ConvertUI64, + F32DemoteF64, + F64ConvertSI32, + F64ConvertUI32, + F64ConvertSI64, + F64ConvertUI64, + F64PromoteF32, - I32ReinterpretF32, - I64ReinterpretF64, - F32ReinterpretI32, - F64ReinterpretI64, + I32ReinterpretF32, + I64ReinterpretF64, + F32ReinterpretI32, + F64ReinterpretI64, } /// The internally-stored instruction type. This differs from `Instruction` in that the `BrTable` @@ -341,453 +341,453 @@ pub enum Instruction<'a> { /// borrows the list of instructions and returns targets by reading it. #[derive(Copy, Debug, Clone, PartialEq, Eq)] pub(crate) enum InstructionInternal { - GetLocal(u32), - SetLocal(u32), - TeeLocal(u32), - Br(Target), - BrIfEqz(Target), - BrIfNez(Target), - BrTable { count: u32 }, - BrTableTarget(Target), + GetLocal(u32), + SetLocal(u32), + TeeLocal(u32), + Br(Target), + BrIfEqz(Target), + BrIfNez(Target), + BrTable { count: u32 }, + BrTableTarget(Target), - Unreachable, - Return(DropKeep), + Unreachable, + Return(DropKeep), - Call(u32), - CallIndirect(u32), + Call(u32), + CallIndirect(u32), - Drop, - Select, + Drop, + Select, - GetGlobal(u32), - SetGlobal(u32), + GetGlobal(u32), + SetGlobal(u32), - I32Load(u32), - I64Load(u32), - F32Load(u32), - F64Load(u32), - I32Load8S(u32), - I32Load8U(u32), - I32Load16S(u32), - I32Load16U(u32), - I64Load8S(u32), - I64Load8U(u32), - I64Load16S(u32), - I64Load16U(u32), - I64Load32S(u32), - I64Load32U(u32), - I32Store(u32), - I64Store(u32), - F32Store(u32), - F64Store(u32), - I32Store8(u32), - I32Store16(u32), - I64Store8(u32), - I64Store16(u32), - I64Store32(u32), + I32Load(u32), + I64Load(u32), + F32Load(u32), + F64Load(u32), + I32Load8S(u32), + I32Load8U(u32), + I32Load16S(u32), + I32Load16U(u32), + I64Load8S(u32), + I64Load8U(u32), + I64Load16S(u32), + I64Load16U(u32), + I64Load32S(u32), + I64Load32U(u32), + I32Store(u32), + I64Store(u32), + F32Store(u32), + F64Store(u32), + I32Store8(u32), + I32Store16(u32), + I64Store8(u32), + I64Store16(u32), + I64Store32(u32), - CurrentMemory, - GrowMemory, + CurrentMemory, + GrowMemory, - I32Const(i32), - I64Const(i64), - F32Const(u32), - F64Const(u64), + I32Const(i32), + I64Const(i64), + F32Const(u32), + F64Const(u64), - I32Eqz, - I32Eq, - I32Ne, - I32LtS, - I32LtU, - I32GtS, - I32GtU, - I32LeS, - I32LeU, - I32GeS, - I32GeU, + I32Eqz, + I32Eq, + I32Ne, + I32LtS, + I32LtU, + I32GtS, + I32GtU, + I32LeS, + I32LeU, + I32GeS, + I32GeU, - I64Eqz, - I64Eq, - I64Ne, - I64LtS, - I64LtU, - I64GtS, - I64GtU, - I64LeS, - I64LeU, - I64GeS, - I64GeU, + I64Eqz, + I64Eq, + I64Ne, + I64LtS, + I64LtU, + I64GtS, + I64GtU, + I64LeS, + I64LeU, + I64GeS, + I64GeU, - F32Eq, - F32Ne, - F32Lt, - F32Gt, - F32Le, - F32Ge, + F32Eq, + F32Ne, + F32Lt, + F32Gt, + F32Le, + F32Ge, - F64Eq, - F64Ne, - F64Lt, - F64Gt, - F64Le, - F64Ge, + F64Eq, + F64Ne, + F64Lt, + F64Gt, + F64Le, + F64Ge, - I32Clz, - I32Ctz, - I32Popcnt, - I32Add, - I32Sub, - I32Mul, - I32DivS, - I32DivU, - I32RemS, - I32RemU, - I32And, - I32Or, - I32Xor, - I32Shl, - I32ShrS, - I32ShrU, - I32Rotl, - I32Rotr, + I32Clz, + I32Ctz, + I32Popcnt, + I32Add, + I32Sub, + I32Mul, + I32DivS, + I32DivU, + I32RemS, + I32RemU, + I32And, + I32Or, + I32Xor, + I32Shl, + I32ShrS, + I32ShrU, + I32Rotl, + I32Rotr, - I64Clz, - I64Ctz, - I64Popcnt, - I64Add, - I64Sub, - I64Mul, - I64DivS, - I64DivU, - I64RemS, - I64RemU, - I64And, - I64Or, - I64Xor, - I64Shl, - I64ShrS, - I64ShrU, - I64Rotl, - I64Rotr, - F32Abs, - F32Neg, - F32Ceil, - F32Floor, - F32Trunc, - F32Nearest, - F32Sqrt, - F32Add, - F32Sub, - F32Mul, - F32Div, - F32Min, - F32Max, - F32Copysign, - F64Abs, - F64Neg, - F64Ceil, - F64Floor, - F64Trunc, - F64Nearest, - F64Sqrt, - F64Add, - F64Sub, - F64Mul, - F64Div, - F64Min, - F64Max, - F64Copysign, + I64Clz, + I64Ctz, + I64Popcnt, + I64Add, + I64Sub, + I64Mul, + I64DivS, + I64DivU, + I64RemS, + I64RemU, + I64And, + I64Or, + I64Xor, + I64Shl, + I64ShrS, + I64ShrU, + I64Rotl, + I64Rotr, + F32Abs, + F32Neg, + F32Ceil, + F32Floor, + F32Trunc, + F32Nearest, + F32Sqrt, + F32Add, + F32Sub, + F32Mul, + F32Div, + F32Min, + F32Max, + F32Copysign, + F64Abs, + F64Neg, + F64Ceil, + F64Floor, + F64Trunc, + F64Nearest, + F64Sqrt, + F64Add, + F64Sub, + F64Mul, + F64Div, + F64Min, + F64Max, + F64Copysign, - I32WrapI64, - I32TruncSF32, - I32TruncUF32, - I32TruncSF64, - I32TruncUF64, - I64ExtendSI32, - I64ExtendUI32, - I64TruncSF32, - I64TruncUF32, - I64TruncSF64, - I64TruncUF64, - F32ConvertSI32, - F32ConvertUI32, - F32ConvertSI64, - F32ConvertUI64, - F32DemoteF64, - F64ConvertSI32, - F64ConvertUI32, - F64ConvertSI64, - F64ConvertUI64, - F64PromoteF32, + I32WrapI64, + I32TruncSF32, + I32TruncUF32, + I32TruncSF64, + I32TruncUF64, + I64ExtendSI32, + I64ExtendUI32, + I64TruncSF32, + I64TruncUF32, + I64TruncSF64, + I64TruncUF64, + F32ConvertSI32, + F32ConvertUI32, + F32ConvertSI64, + F32ConvertUI64, + F32DemoteF64, + F64ConvertSI32, + F64ConvertUI32, + F64ConvertSI64, + F64ConvertUI64, + F64PromoteF32, - I32ReinterpretF32, - I64ReinterpretF64, - F32ReinterpretI32, - F64ReinterpretI64, + I32ReinterpretF32, + I64ReinterpretF64, + F32ReinterpretI32, + F64ReinterpretI64, } #[derive(Debug, Clone)] pub struct Instructions { - vec: Vec, + vec: Vec, } impl Instructions { - pub fn with_capacity(capacity: usize) -> Self { - Instructions { - vec: Vec::with_capacity(capacity), - } - } + pub fn with_capacity(capacity: usize) -> Self { + Instructions { + vec: Vec::with_capacity(capacity), + } + } - pub fn current_pc(&self) -> u32 { - self.vec.len() as u32 - } + pub fn current_pc(&self) -> u32 { + self.vec.len() as u32 + } - pub(crate) fn push(&mut self, instruction: InstructionInternal) { - self.vec.push(instruction); - } + pub(crate) fn push(&mut self, instruction: InstructionInternal) { + self.vec.push(instruction); + } - pub fn patch_relocation(&mut self, reloc: Reloc, dst_pc: u32) { - match reloc { - Reloc::Br { pc } => match self.vec[pc as usize] { - InstructionInternal::Br(ref mut target) - | InstructionInternal::BrIfEqz(ref mut target) - | InstructionInternal::BrIfNez(ref mut target) => target.dst_pc = dst_pc, - _ => panic!("branch relocation points to a non-branch instruction"), - }, - Reloc::BrTable { pc, idx } => match &mut self.vec[pc as usize + idx + 1] { - InstructionInternal::BrTableTarget(target) => target.dst_pc = dst_pc, - _ => panic!("brtable relocation points to not brtable instruction"), - }, - } - } + pub fn patch_relocation(&mut self, reloc: Reloc, dst_pc: u32) { + match reloc { + Reloc::Br { pc } => match self.vec[pc as usize] { + InstructionInternal::Br(ref mut target) + | InstructionInternal::BrIfEqz(ref mut target) + | InstructionInternal::BrIfNez(ref mut target) => target.dst_pc = dst_pc, + _ => panic!("branch relocation points to a non-branch instruction"), + }, + Reloc::BrTable { pc, idx } => match &mut self.vec[pc as usize + idx + 1] { + InstructionInternal::BrTableTarget(target) => target.dst_pc = dst_pc, + _ => panic!("brtable relocation points to not brtable instruction"), + }, + } + } - pub fn iterate_from(&self, position: u32) -> InstructionIter { - InstructionIter { - instructions: &self.vec, - position, - } - } + pub fn iterate_from(&self, position: u32) -> InstructionIter { + InstructionIter { + instructions: &self.vec, + position, + } + } } pub struct InstructionIter<'a> { - instructions: &'a [InstructionInternal], - position: u32, + instructions: &'a [InstructionInternal], + position: u32, } impl<'a> InstructionIter<'a> { - #[inline] - pub fn position(&self) -> u32 { - self.position - } + #[inline] + pub fn position(&self) -> u32 { + self.position + } } impl<'a> Iterator for InstructionIter<'a> { - type Item = Instruction<'a>; + type Item = Instruction<'a>; - #[inline] - fn next(&mut self) -> Option { - let internal = if let Some(i) = self.instructions.get(self.position as usize) { - i - } else { - return None; - }; + #[inline] + fn next(&mut self) -> Option { + let internal = if let Some(i) = self.instructions.get(self.position as usize) { + i + } else { + return None; + }; - let out = match *internal { - InstructionInternal::GetLocal(x) => Instruction::GetLocal(x), - InstructionInternal::SetLocal(x) => Instruction::SetLocal(x), - InstructionInternal::TeeLocal(x) => Instruction::TeeLocal(x), - InstructionInternal::Br(x) => Instruction::Br(x), - InstructionInternal::BrIfEqz(x) => Instruction::BrIfEqz(x), - InstructionInternal::BrIfNez(x) => Instruction::BrIfNez(x), - InstructionInternal::BrTable { count } => { - let start = self.position as usize + 1; + let out = match *internal { + InstructionInternal::GetLocal(x) => Instruction::GetLocal(x), + InstructionInternal::SetLocal(x) => Instruction::SetLocal(x), + InstructionInternal::TeeLocal(x) => Instruction::TeeLocal(x), + InstructionInternal::Br(x) => Instruction::Br(x), + InstructionInternal::BrIfEqz(x) => Instruction::BrIfEqz(x), + InstructionInternal::BrIfNez(x) => Instruction::BrIfNez(x), + InstructionInternal::BrTable { count } => { + let start = self.position as usize + 1; - self.position += count; + self.position += count; - Instruction::BrTable(BrTargets::from_internal( - &self.instructions[start..start + count as usize], - )) - } - InstructionInternal::BrTableTarget(_) => panic!("Executed BrTableTarget"), + Instruction::BrTable(BrTargets::from_internal( + &self.instructions[start..start + count as usize], + )) + } + InstructionInternal::BrTableTarget(_) => panic!("Executed BrTableTarget"), - InstructionInternal::Unreachable => Instruction::Unreachable, - InstructionInternal::Return(x) => Instruction::Return(x), + InstructionInternal::Unreachable => Instruction::Unreachable, + InstructionInternal::Return(x) => Instruction::Return(x), - InstructionInternal::Call(x) => Instruction::Call(x), - InstructionInternal::CallIndirect(x) => Instruction::CallIndirect(x), + InstructionInternal::Call(x) => Instruction::Call(x), + InstructionInternal::CallIndirect(x) => Instruction::CallIndirect(x), - InstructionInternal::Drop => Instruction::Drop, - InstructionInternal::Select => Instruction::Select, + InstructionInternal::Drop => Instruction::Drop, + InstructionInternal::Select => Instruction::Select, - InstructionInternal::GetGlobal(x) => Instruction::GetGlobal(x), - InstructionInternal::SetGlobal(x) => Instruction::SetGlobal(x), + InstructionInternal::GetGlobal(x) => Instruction::GetGlobal(x), + InstructionInternal::SetGlobal(x) => Instruction::SetGlobal(x), - InstructionInternal::I32Load(x) => Instruction::I32Load(x), - InstructionInternal::I64Load(x) => Instruction::I64Load(x), - InstructionInternal::F32Load(x) => Instruction::F32Load(x), - InstructionInternal::F64Load(x) => Instruction::F64Load(x), - InstructionInternal::I32Load8S(x) => Instruction::I32Load8S(x), - InstructionInternal::I32Load8U(x) => Instruction::I32Load8U(x), - InstructionInternal::I32Load16S(x) => Instruction::I32Load16S(x), - InstructionInternal::I32Load16U(x) => Instruction::I32Load16U(x), - InstructionInternal::I64Load8S(x) => Instruction::I64Load8S(x), - InstructionInternal::I64Load8U(x) => Instruction::I64Load8U(x), - InstructionInternal::I64Load16S(x) => Instruction::I64Load16S(x), - InstructionInternal::I64Load16U(x) => Instruction::I64Load16U(x), - InstructionInternal::I64Load32S(x) => Instruction::I64Load32S(x), - InstructionInternal::I64Load32U(x) => Instruction::I64Load32U(x), - InstructionInternal::I32Store(x) => Instruction::I32Store(x), - InstructionInternal::I64Store(x) => Instruction::I64Store(x), - InstructionInternal::F32Store(x) => Instruction::F32Store(x), - InstructionInternal::F64Store(x) => Instruction::F64Store(x), - InstructionInternal::I32Store8(x) => Instruction::I32Store8(x), - InstructionInternal::I32Store16(x) => Instruction::I32Store16(x), - InstructionInternal::I64Store8(x) => Instruction::I64Store8(x), - InstructionInternal::I64Store16(x) => Instruction::I64Store16(x), - InstructionInternal::I64Store32(x) => Instruction::I64Store32(x), + InstructionInternal::I32Load(x) => Instruction::I32Load(x), + InstructionInternal::I64Load(x) => Instruction::I64Load(x), + InstructionInternal::F32Load(x) => Instruction::F32Load(x), + InstructionInternal::F64Load(x) => Instruction::F64Load(x), + InstructionInternal::I32Load8S(x) => Instruction::I32Load8S(x), + InstructionInternal::I32Load8U(x) => Instruction::I32Load8U(x), + InstructionInternal::I32Load16S(x) => Instruction::I32Load16S(x), + InstructionInternal::I32Load16U(x) => Instruction::I32Load16U(x), + InstructionInternal::I64Load8S(x) => Instruction::I64Load8S(x), + InstructionInternal::I64Load8U(x) => Instruction::I64Load8U(x), + InstructionInternal::I64Load16S(x) => Instruction::I64Load16S(x), + InstructionInternal::I64Load16U(x) => Instruction::I64Load16U(x), + InstructionInternal::I64Load32S(x) => Instruction::I64Load32S(x), + InstructionInternal::I64Load32U(x) => Instruction::I64Load32U(x), + InstructionInternal::I32Store(x) => Instruction::I32Store(x), + InstructionInternal::I64Store(x) => Instruction::I64Store(x), + InstructionInternal::F32Store(x) => Instruction::F32Store(x), + InstructionInternal::F64Store(x) => Instruction::F64Store(x), + InstructionInternal::I32Store8(x) => Instruction::I32Store8(x), + InstructionInternal::I32Store16(x) => Instruction::I32Store16(x), + InstructionInternal::I64Store8(x) => Instruction::I64Store8(x), + InstructionInternal::I64Store16(x) => Instruction::I64Store16(x), + InstructionInternal::I64Store32(x) => Instruction::I64Store32(x), - InstructionInternal::CurrentMemory => Instruction::CurrentMemory, - InstructionInternal::GrowMemory => Instruction::GrowMemory, + InstructionInternal::CurrentMemory => Instruction::CurrentMemory, + InstructionInternal::GrowMemory => Instruction::GrowMemory, - InstructionInternal::I32Const(x) => Instruction::I32Const(x), - InstructionInternal::I64Const(x) => Instruction::I64Const(x), - InstructionInternal::F32Const(x) => Instruction::F32Const(x), - InstructionInternal::F64Const(x) => Instruction::F64Const(x), + InstructionInternal::I32Const(x) => Instruction::I32Const(x), + InstructionInternal::I64Const(x) => Instruction::I64Const(x), + InstructionInternal::F32Const(x) => Instruction::F32Const(x), + InstructionInternal::F64Const(x) => Instruction::F64Const(x), - InstructionInternal::I32Eqz => Instruction::I32Eqz, - InstructionInternal::I32Eq => Instruction::I32Eq, - InstructionInternal::I32Ne => Instruction::I32Ne, - InstructionInternal::I32LtS => Instruction::I32LtS, - InstructionInternal::I32LtU => Instruction::I32LtU, - InstructionInternal::I32GtS => Instruction::I32GtS, - InstructionInternal::I32GtU => Instruction::I32GtU, - InstructionInternal::I32LeS => Instruction::I32LeS, - InstructionInternal::I32LeU => Instruction::I32LeU, - InstructionInternal::I32GeS => Instruction::I32GeS, - InstructionInternal::I32GeU => Instruction::I32GeU, + InstructionInternal::I32Eqz => Instruction::I32Eqz, + InstructionInternal::I32Eq => Instruction::I32Eq, + InstructionInternal::I32Ne => Instruction::I32Ne, + InstructionInternal::I32LtS => Instruction::I32LtS, + InstructionInternal::I32LtU => Instruction::I32LtU, + InstructionInternal::I32GtS => Instruction::I32GtS, + InstructionInternal::I32GtU => Instruction::I32GtU, + InstructionInternal::I32LeS => Instruction::I32LeS, + InstructionInternal::I32LeU => Instruction::I32LeU, + InstructionInternal::I32GeS => Instruction::I32GeS, + InstructionInternal::I32GeU => Instruction::I32GeU, - InstructionInternal::I64Eqz => Instruction::I64Eqz, - InstructionInternal::I64Eq => Instruction::I64Eq, - InstructionInternal::I64Ne => Instruction::I64Ne, - InstructionInternal::I64LtS => Instruction::I64LtS, - InstructionInternal::I64LtU => Instruction::I64LtU, - InstructionInternal::I64GtS => Instruction::I64GtS, - InstructionInternal::I64GtU => Instruction::I64GtU, - InstructionInternal::I64LeS => Instruction::I64LeS, - InstructionInternal::I64LeU => Instruction::I64LeU, - InstructionInternal::I64GeS => Instruction::I64GeS, - InstructionInternal::I64GeU => Instruction::I64GeU, + InstructionInternal::I64Eqz => Instruction::I64Eqz, + InstructionInternal::I64Eq => Instruction::I64Eq, + InstructionInternal::I64Ne => Instruction::I64Ne, + InstructionInternal::I64LtS => Instruction::I64LtS, + InstructionInternal::I64LtU => Instruction::I64LtU, + InstructionInternal::I64GtS => Instruction::I64GtS, + InstructionInternal::I64GtU => Instruction::I64GtU, + InstructionInternal::I64LeS => Instruction::I64LeS, + InstructionInternal::I64LeU => Instruction::I64LeU, + InstructionInternal::I64GeS => Instruction::I64GeS, + InstructionInternal::I64GeU => Instruction::I64GeU, - InstructionInternal::F32Eq => Instruction::F32Eq, - InstructionInternal::F32Ne => Instruction::F32Ne, - InstructionInternal::F32Lt => Instruction::F32Lt, - InstructionInternal::F32Gt => Instruction::F32Gt, - InstructionInternal::F32Le => Instruction::F32Le, - InstructionInternal::F32Ge => Instruction::F32Ge, + InstructionInternal::F32Eq => Instruction::F32Eq, + InstructionInternal::F32Ne => Instruction::F32Ne, + InstructionInternal::F32Lt => Instruction::F32Lt, + InstructionInternal::F32Gt => Instruction::F32Gt, + InstructionInternal::F32Le => Instruction::F32Le, + InstructionInternal::F32Ge => Instruction::F32Ge, - InstructionInternal::F64Eq => Instruction::F64Eq, - InstructionInternal::F64Ne => Instruction::F64Ne, - InstructionInternal::F64Lt => Instruction::F64Lt, - InstructionInternal::F64Gt => Instruction::F64Gt, - InstructionInternal::F64Le => Instruction::F64Le, - InstructionInternal::F64Ge => Instruction::F64Ge, + InstructionInternal::F64Eq => Instruction::F64Eq, + InstructionInternal::F64Ne => Instruction::F64Ne, + InstructionInternal::F64Lt => Instruction::F64Lt, + InstructionInternal::F64Gt => Instruction::F64Gt, + InstructionInternal::F64Le => Instruction::F64Le, + InstructionInternal::F64Ge => Instruction::F64Ge, - InstructionInternal::I32Clz => Instruction::I32Clz, - InstructionInternal::I32Ctz => Instruction::I32Ctz, - InstructionInternal::I32Popcnt => Instruction::I32Popcnt, - InstructionInternal::I32Add => Instruction::I32Add, - InstructionInternal::I32Sub => Instruction::I32Sub, - InstructionInternal::I32Mul => Instruction::I32Mul, - InstructionInternal::I32DivS => Instruction::I32DivS, - InstructionInternal::I32DivU => Instruction::I32DivU, - InstructionInternal::I32RemS => Instruction::I32RemS, - InstructionInternal::I32RemU => Instruction::I32RemU, - InstructionInternal::I32And => Instruction::I32And, - InstructionInternal::I32Or => Instruction::I32Or, - InstructionInternal::I32Xor => Instruction::I32Xor, - InstructionInternal::I32Shl => Instruction::I32Shl, - InstructionInternal::I32ShrS => Instruction::I32ShrS, - InstructionInternal::I32ShrU => Instruction::I32ShrU, - InstructionInternal::I32Rotl => Instruction::I32Rotl, - InstructionInternal::I32Rotr => Instruction::I32Rotr, + InstructionInternal::I32Clz => Instruction::I32Clz, + InstructionInternal::I32Ctz => Instruction::I32Ctz, + InstructionInternal::I32Popcnt => Instruction::I32Popcnt, + InstructionInternal::I32Add => Instruction::I32Add, + InstructionInternal::I32Sub => Instruction::I32Sub, + InstructionInternal::I32Mul => Instruction::I32Mul, + InstructionInternal::I32DivS => Instruction::I32DivS, + InstructionInternal::I32DivU => Instruction::I32DivU, + InstructionInternal::I32RemS => Instruction::I32RemS, + InstructionInternal::I32RemU => Instruction::I32RemU, + InstructionInternal::I32And => Instruction::I32And, + InstructionInternal::I32Or => Instruction::I32Or, + InstructionInternal::I32Xor => Instruction::I32Xor, + InstructionInternal::I32Shl => Instruction::I32Shl, + InstructionInternal::I32ShrS => Instruction::I32ShrS, + InstructionInternal::I32ShrU => Instruction::I32ShrU, + InstructionInternal::I32Rotl => Instruction::I32Rotl, + InstructionInternal::I32Rotr => Instruction::I32Rotr, - InstructionInternal::I64Clz => Instruction::I64Clz, - InstructionInternal::I64Ctz => Instruction::I64Ctz, - InstructionInternal::I64Popcnt => Instruction::I64Popcnt, - InstructionInternal::I64Add => Instruction::I64Add, - InstructionInternal::I64Sub => Instruction::I64Sub, - InstructionInternal::I64Mul => Instruction::I64Mul, - InstructionInternal::I64DivS => Instruction::I64DivS, - InstructionInternal::I64DivU => Instruction::I64DivU, - InstructionInternal::I64RemS => Instruction::I64RemS, - InstructionInternal::I64RemU => Instruction::I64RemU, - InstructionInternal::I64And => Instruction::I64And, - InstructionInternal::I64Or => Instruction::I64Or, - InstructionInternal::I64Xor => Instruction::I64Xor, - InstructionInternal::I64Shl => Instruction::I64Shl, - InstructionInternal::I64ShrS => Instruction::I64ShrS, - InstructionInternal::I64ShrU => Instruction::I64ShrU, - InstructionInternal::I64Rotl => Instruction::I64Rotl, - InstructionInternal::I64Rotr => Instruction::I64Rotr, - InstructionInternal::F32Abs => Instruction::F32Abs, - InstructionInternal::F32Neg => Instruction::F32Neg, - InstructionInternal::F32Ceil => Instruction::F32Ceil, - InstructionInternal::F32Floor => Instruction::F32Floor, - InstructionInternal::F32Trunc => Instruction::F32Trunc, - InstructionInternal::F32Nearest => Instruction::F32Nearest, - InstructionInternal::F32Sqrt => Instruction::F32Sqrt, - InstructionInternal::F32Add => Instruction::F32Add, - InstructionInternal::F32Sub => Instruction::F32Sub, - InstructionInternal::F32Mul => Instruction::F32Mul, - InstructionInternal::F32Div => Instruction::F32Div, - InstructionInternal::F32Min => Instruction::F32Min, - InstructionInternal::F32Max => Instruction::F32Max, - InstructionInternal::F32Copysign => Instruction::F32Copysign, - InstructionInternal::F64Abs => Instruction::F64Abs, - InstructionInternal::F64Neg => Instruction::F64Neg, - InstructionInternal::F64Ceil => Instruction::F64Ceil, - InstructionInternal::F64Floor => Instruction::F64Floor, - InstructionInternal::F64Trunc => Instruction::F64Trunc, - InstructionInternal::F64Nearest => Instruction::F64Nearest, - InstructionInternal::F64Sqrt => Instruction::F64Sqrt, - InstructionInternal::F64Add => Instruction::F64Add, - InstructionInternal::F64Sub => Instruction::F64Sub, - InstructionInternal::F64Mul => Instruction::F64Mul, - InstructionInternal::F64Div => Instruction::F64Div, - InstructionInternal::F64Min => Instruction::F64Min, - InstructionInternal::F64Max => Instruction::F64Max, - InstructionInternal::F64Copysign => Instruction::F64Copysign, + InstructionInternal::I64Clz => Instruction::I64Clz, + InstructionInternal::I64Ctz => Instruction::I64Ctz, + InstructionInternal::I64Popcnt => Instruction::I64Popcnt, + InstructionInternal::I64Add => Instruction::I64Add, + InstructionInternal::I64Sub => Instruction::I64Sub, + InstructionInternal::I64Mul => Instruction::I64Mul, + InstructionInternal::I64DivS => Instruction::I64DivS, + InstructionInternal::I64DivU => Instruction::I64DivU, + InstructionInternal::I64RemS => Instruction::I64RemS, + InstructionInternal::I64RemU => Instruction::I64RemU, + InstructionInternal::I64And => Instruction::I64And, + InstructionInternal::I64Or => Instruction::I64Or, + InstructionInternal::I64Xor => Instruction::I64Xor, + InstructionInternal::I64Shl => Instruction::I64Shl, + InstructionInternal::I64ShrS => Instruction::I64ShrS, + InstructionInternal::I64ShrU => Instruction::I64ShrU, + InstructionInternal::I64Rotl => Instruction::I64Rotl, + InstructionInternal::I64Rotr => Instruction::I64Rotr, + InstructionInternal::F32Abs => Instruction::F32Abs, + InstructionInternal::F32Neg => Instruction::F32Neg, + InstructionInternal::F32Ceil => Instruction::F32Ceil, + InstructionInternal::F32Floor => Instruction::F32Floor, + InstructionInternal::F32Trunc => Instruction::F32Trunc, + InstructionInternal::F32Nearest => Instruction::F32Nearest, + InstructionInternal::F32Sqrt => Instruction::F32Sqrt, + InstructionInternal::F32Add => Instruction::F32Add, + InstructionInternal::F32Sub => Instruction::F32Sub, + InstructionInternal::F32Mul => Instruction::F32Mul, + InstructionInternal::F32Div => Instruction::F32Div, + InstructionInternal::F32Min => Instruction::F32Min, + InstructionInternal::F32Max => Instruction::F32Max, + InstructionInternal::F32Copysign => Instruction::F32Copysign, + InstructionInternal::F64Abs => Instruction::F64Abs, + InstructionInternal::F64Neg => Instruction::F64Neg, + InstructionInternal::F64Ceil => Instruction::F64Ceil, + InstructionInternal::F64Floor => Instruction::F64Floor, + InstructionInternal::F64Trunc => Instruction::F64Trunc, + InstructionInternal::F64Nearest => Instruction::F64Nearest, + InstructionInternal::F64Sqrt => Instruction::F64Sqrt, + InstructionInternal::F64Add => Instruction::F64Add, + InstructionInternal::F64Sub => Instruction::F64Sub, + InstructionInternal::F64Mul => Instruction::F64Mul, + InstructionInternal::F64Div => Instruction::F64Div, + InstructionInternal::F64Min => Instruction::F64Min, + InstructionInternal::F64Max => Instruction::F64Max, + InstructionInternal::F64Copysign => Instruction::F64Copysign, - InstructionInternal::I32WrapI64 => Instruction::I32WrapI64, - InstructionInternal::I32TruncSF32 => Instruction::I32TruncSF32, - InstructionInternal::I32TruncUF32 => Instruction::I32TruncUF32, - InstructionInternal::I32TruncSF64 => Instruction::I32TruncSF64, - InstructionInternal::I32TruncUF64 => Instruction::I32TruncUF64, - InstructionInternal::I64ExtendSI32 => Instruction::I64ExtendSI32, - InstructionInternal::I64ExtendUI32 => Instruction::I64ExtendUI32, - InstructionInternal::I64TruncSF32 => Instruction::I64TruncSF32, - InstructionInternal::I64TruncUF32 => Instruction::I64TruncUF32, - InstructionInternal::I64TruncSF64 => Instruction::I64TruncSF64, - InstructionInternal::I64TruncUF64 => Instruction::I64TruncUF64, - InstructionInternal::F32ConvertSI32 => Instruction::F32ConvertSI32, - InstructionInternal::F32ConvertUI32 => Instruction::F32ConvertUI32, - InstructionInternal::F32ConvertSI64 => Instruction::F32ConvertSI64, - InstructionInternal::F32ConvertUI64 => Instruction::F32ConvertUI64, - InstructionInternal::F32DemoteF64 => Instruction::F32DemoteF64, - InstructionInternal::F64ConvertSI32 => Instruction::F64ConvertSI32, - InstructionInternal::F64ConvertUI32 => Instruction::F64ConvertUI32, - InstructionInternal::F64ConvertSI64 => Instruction::F64ConvertSI64, - InstructionInternal::F64ConvertUI64 => Instruction::F64ConvertUI64, - InstructionInternal::F64PromoteF32 => Instruction::F64PromoteF32, + InstructionInternal::I32WrapI64 => Instruction::I32WrapI64, + InstructionInternal::I32TruncSF32 => Instruction::I32TruncSF32, + InstructionInternal::I32TruncUF32 => Instruction::I32TruncUF32, + InstructionInternal::I32TruncSF64 => Instruction::I32TruncSF64, + InstructionInternal::I32TruncUF64 => Instruction::I32TruncUF64, + InstructionInternal::I64ExtendSI32 => Instruction::I64ExtendSI32, + InstructionInternal::I64ExtendUI32 => Instruction::I64ExtendUI32, + InstructionInternal::I64TruncSF32 => Instruction::I64TruncSF32, + InstructionInternal::I64TruncUF32 => Instruction::I64TruncUF32, + InstructionInternal::I64TruncSF64 => Instruction::I64TruncSF64, + InstructionInternal::I64TruncUF64 => Instruction::I64TruncUF64, + InstructionInternal::F32ConvertSI32 => Instruction::F32ConvertSI32, + InstructionInternal::F32ConvertUI32 => Instruction::F32ConvertUI32, + InstructionInternal::F32ConvertSI64 => Instruction::F32ConvertSI64, + InstructionInternal::F32ConvertUI64 => Instruction::F32ConvertUI64, + InstructionInternal::F32DemoteF64 => Instruction::F32DemoteF64, + InstructionInternal::F64ConvertSI32 => Instruction::F64ConvertSI32, + InstructionInternal::F64ConvertUI32 => Instruction::F64ConvertUI32, + InstructionInternal::F64ConvertSI64 => Instruction::F64ConvertSI64, + InstructionInternal::F64ConvertUI64 => Instruction::F64ConvertUI64, + InstructionInternal::F64PromoteF32 => Instruction::F64PromoteF32, - InstructionInternal::I32ReinterpretF32 => Instruction::I32ReinterpretF32, - InstructionInternal::I64ReinterpretF64 => Instruction::I64ReinterpretF64, - InstructionInternal::F32ReinterpretI32 => Instruction::F32ReinterpretI32, - InstructionInternal::F64ReinterpretI64 => Instruction::F64ReinterpretI64, - }; + InstructionInternal::I32ReinterpretF32 => Instruction::I32ReinterpretF32, + InstructionInternal::I64ReinterpretF64 => Instruction::I64ReinterpretF64, + InstructionInternal::F32ReinterpretI32 => Instruction::F32ReinterpretI32, + InstructionInternal::F64ReinterpretI64 => Instruction::F64ReinterpretI64, + }; - self.position += 1; + self.position += 1; - Some(out) - } + Some(out) + } } diff --git a/src/lib.rs b/src/lib.rs index fc1e3cc..f92c4d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,9 +95,7 @@ //! ``` #![warn(missing_docs)] - #![cfg_attr(not(feature = "std"), no_std)] - //// alloc is required in no_std #![cfg_attr(not(feature = "std"), feature(alloc))] @@ -117,11 +115,11 @@ extern crate wabt; #[macro_use] extern crate assert_matches; -extern crate parity_wasm; extern crate byteorder; #[cfg(not(feature = "std"))] extern crate hashmap_core; extern crate memory_units as memory_units_crate; +extern crate parity_wasm; #[allow(unused_imports)] use alloc::prelude::*; @@ -138,32 +136,32 @@ extern crate libm; /// Traps can't be handled by WebAssembly code, but are reported to the embedder. #[derive(Debug)] pub struct Trap { - kind: TrapKind, + kind: TrapKind, } impl Trap { - /// Create new trap. - pub fn new(kind: TrapKind) -> Trap { - Trap { kind } - } + /// Create new trap. + pub fn new(kind: TrapKind) -> Trap { + Trap { kind } + } - /// Returns kind of this trap. - pub fn kind(&self) -> &TrapKind { - &self.kind - } + /// Returns kind of this trap. + pub fn kind(&self) -> &TrapKind { + &self.kind + } } impl fmt::Display for Trap { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Trap: {:?}", self.kind) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Trap: {:?}", self.kind) + } } #[cfg(feature = "std")] impl error::Error for Trap { - fn description(&self) -> &str { - "runtime trap" - } + fn description(&self) -> &str { + "runtime trap" + } } /// Error type which can be thrown by wasm code or by host environment. @@ -173,393 +171,393 @@ impl error::Error for Trap { /// [`Trap`]: struct.Trap.html #[derive(Debug)] pub enum TrapKind { - /// Wasm code executed `unreachable` opcode. - /// - /// `unreachable` is a special opcode which always traps upon execution. - /// This opcode have a similar purpose as `ud2` in x86. - Unreachable, + /// Wasm code executed `unreachable` opcode. + /// + /// `unreachable` is a special opcode which always traps upon execution. + /// This opcode have a similar purpose as `ud2` in x86. + Unreachable, - /// Attempt to load or store at the address which - /// lies outside of bounds of the memory. - /// - /// Since addresses are interpreted as unsigned integers, out of bounds access - /// can't happen with negative addresses (i.e. they will always wrap). - MemoryAccessOutOfBounds, + /// Attempt to load or store at the address which + /// lies outside of bounds of the memory. + /// + /// Since addresses are interpreted as unsigned integers, out of bounds access + /// can't happen with negative addresses (i.e. they will always wrap). + MemoryAccessOutOfBounds, - /// Attempt to access table element at index which - /// lies outside of bounds. - /// - /// This typically can happen when `call_indirect` is executed - /// with index that lies out of bounds. - /// - /// Since indexes are interpreted as unsinged integers, out of bounds access - /// can't happen with negative indexes (i.e. they will always wrap). - TableAccessOutOfBounds, + /// Attempt to access table element at index which + /// lies outside of bounds. + /// + /// This typically can happen when `call_indirect` is executed + /// with index that lies out of bounds. + /// + /// Since indexes are interpreted as unsinged integers, out of bounds access + /// can't happen with negative indexes (i.e. they will always wrap). + TableAccessOutOfBounds, - /// Attempt to access table element which is uninitialized (i.e. `None`). - /// - /// This typically can happen when `call_indirect` is executed. - ElemUninitialized, + /// Attempt to access table element which is uninitialized (i.e. `None`). + /// + /// This typically can happen when `call_indirect` is executed. + ElemUninitialized, - /// Attempt to divide by zero. - /// - /// This trap typically can happen if `div` or `rem` is executed with - /// zero as divider. - DivisionByZero, + /// Attempt to divide by zero. + /// + /// This trap typically can happen if `div` or `rem` is executed with + /// zero as divider. + DivisionByZero, - /// Attempt to make a conversion to an int failed. - /// - /// This can happen when: - /// - /// - trying to do signed division (or get the remainder) -2N-1 over -1. This is - /// because the result +2N-1 isn't representable as a N-bit signed integer. - /// - trying to truncate NaNs, infinity, or value for which the result is out of range into an integer. - InvalidConversionToInt, + /// Attempt to make a conversion to an int failed. + /// + /// This can happen when: + /// + /// - trying to do signed division (or get the remainder) -2N-1 over -1. This is + /// because the result +2N-1 isn't representable as a N-bit signed integer. + /// - trying to truncate NaNs, infinity, or value for which the result is out of range into an integer. + InvalidConversionToInt, - /// Stack overflow. - /// - /// This is likely caused by some infinite or very deep recursion. - /// Extensive inlining might also be the cause of stack overflow. - StackOverflow, + /// Stack overflow. + /// + /// This is likely caused by some infinite or very deep recursion. + /// Extensive inlining might also be the cause of stack overflow. + StackOverflow, - /// Attempt to invoke a function with mismatching signature. - /// - /// This can happen if [`FuncInstance`] was invoked - /// with mismatching [signature][`Signature`]. - /// - /// This can always happen with indirect calls. `call_indirect` instruction always - /// specifies the expected signature of function. If `call_indirect` is executed - /// with index that points on function with signature different that is - /// expected by this `call_indirect`, this trap is raised. - /// - /// [`Signature`]: struct.Signature.html - UnexpectedSignature, + /// Attempt to invoke a function with mismatching signature. + /// + /// This can happen if [`FuncInstance`] was invoked + /// with mismatching [signature][`Signature`]. + /// + /// This can always happen with indirect calls. `call_indirect` instruction always + /// specifies the expected signature of function. If `call_indirect` is executed + /// with index that points on function with signature different that is + /// expected by this `call_indirect`, this trap is raised. + /// + /// [`Signature`]: struct.Signature.html + UnexpectedSignature, - /// Error specified by the host. - /// - /// Typically returned from an implementation of [`Externals`]. - /// - /// [`Externals`]: trait.Externals.html - Host(Box), + /// Error specified by the host. + /// + /// Typically returned from an implementation of [`Externals`]. + /// + /// [`Externals`]: trait.Externals.html + Host(Box), } impl TrapKind { - /// Whether this trap is specified by the host. - pub fn is_host(&self) -> bool { - match self { - &TrapKind::Host(_) => true, - _ => false, - } - } + /// Whether this trap is specified by the host. + pub fn is_host(&self) -> bool { + match self { + &TrapKind::Host(_) => true, + _ => false, + } + } } /// Internal interpreter error. #[derive(Debug)] pub enum Error { - /// Module validation error. Might occur only at load time. - Validation(String), - /// Error while instantiating a module. Might occur when provided - /// with incorrect exports (i.e. linkage failure). - Instantiation(String), - /// Function-level error. - Function(String), - /// Table-level error. - Table(String), - /// Memory-level error. - Memory(String), - /// Global-level error. - Global(String), - /// Value-level error. - Value(String), - /// Trap. - Trap(Trap), - /// Custom embedder error. - Host(Box), + /// Module validation error. Might occur only at load time. + Validation(String), + /// Error while instantiating a module. Might occur when provided + /// with incorrect exports (i.e. linkage failure). + Instantiation(String), + /// Function-level error. + Function(String), + /// Table-level error. + Table(String), + /// Memory-level error. + Memory(String), + /// Global-level error. + Global(String), + /// Value-level error. + Value(String), + /// 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, - } - } + /// 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 { - Error::Validation(s) => s, - Error::Instantiation(s) => s, - Error::Function(s) => s, - Error::Table(s) => s, - Error::Memory(s) => s, - Error::Global(s) => s, - Error::Value(s) => s, - Error::Trap(s) => format!("trap: {:?}", s), - Error::Host(e) => format!("user: {}", e), - } - } + fn into(self) -> String { + match self { + Error::Validation(s) => s, + Error::Instantiation(s) => s, + Error::Function(s) => s, + Error::Table(s) => s, + Error::Memory(s) => s, + Error::Global(s) => s, + Error::Value(s) => s, + Error::Trap(s) => format!("trap: {:?}", s), + Error::Host(e) => format!("user: {}", e), + } + } } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Validation(ref s) => write!(f, "Validation: {}", s), - Error::Instantiation(ref s) => write!(f, "Instantiation: {}", s), - Error::Function(ref s) => write!(f, "Function: {}", s), - Error::Table(ref s) => write!(f, "Table: {}", s), - 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::Trap(ref s) => write!(f, "Trap: {:?}", s), - Error::Host(ref e) => write!(f, "User: {}", e), - } - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::Validation(ref s) => write!(f, "Validation: {}", s), + Error::Instantiation(ref s) => write!(f, "Instantiation: {}", s), + Error::Function(ref s) => write!(f, "Function: {}", s), + Error::Table(ref s) => write!(f, "Table: {}", s), + 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::Trap(ref s) => write!(f, "Trap: {:?}", s), + Error::Host(ref e) => write!(f, "User: {}", e), + } + } } #[cfg(feature = "std")] impl error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::Validation(ref s) => s, - Error::Instantiation(ref s) => s, - Error::Function(ref s) => s, - Error::Table(ref s) => s, - Error::Memory(ref s) => s, - Error::Global(ref s) => s, - Error::Value(ref s) => s, - Error::Trap(_) => "Trap", - Error::Host(_) => "Host error", - } - } + fn description(&self) -> &str { + match *self { + Error::Validation(ref s) => s, + Error::Instantiation(ref s) => s, + Error::Function(ref s) => s, + Error::Table(ref s) => s, + Error::Memory(ref s) => s, + Error::Global(ref s) => s, + Error::Value(ref s) => s, + Error::Trap(_) => "Trap", + 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 Error +where + U: host::HostError + Sized, +{ + fn from(e: U) -> Self { + Error::Host(Box::new(e)) + } } -impl From for Trap where U: host::HostError + Sized { - fn from(e: U) -> Self { - Trap::new(TrapKind::Host(Box::new(e))) - } +impl From for Trap +where + U: host::HostError + Sized, +{ + fn from(e: U) -> Self { + Trap::new(TrapKind::Host(Box::new(e))) + } } impl From for Error { - fn from(e: Trap) -> Error { - Error::Trap(e) - } + fn from(e: Trap) -> Error { + Error::Trap(e) + } } impl From for Trap { - fn from(e: TrapKind) -> Trap { - Trap::new(e) - } + fn from(e: TrapKind) -> Trap { + Trap::new(e) + } } impl From for Error { - fn from(e: validation::Error) -> Error { - Error::Validation(e.to_string()) - } + fn from(e: validation::Error) -> Error { + Error::Validation(e.to_string()) + } } -mod validation; mod common; -mod memory; -mod module; -mod runner; -mod table; -mod value; +mod func; +mod global; mod host; mod imports; -mod global; -mod func; -mod types; mod isa; +mod memory; +mod module; pub mod nan_preserving_float; +mod runner; +mod table; +mod types; +mod validation; +mod value; #[cfg(test)] mod tests; -pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE}; -pub use self::table::{TableInstance, TableRef}; -pub use self::value::{FromRuntimeValue, RuntimeValue, LittleEndianConvert, Error as ValueError}; -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::func::{FuncInstance, FuncInvocation, FuncRef, ResumableError}; pub use self::global::{GlobalInstance, GlobalRef}; -pub use self::func::{FuncInstance, FuncRef, FuncInvocation, ResumableError}; -pub use self::types::{Signature, ValueType, GlobalDescriptor, TableDescriptor, MemoryDescriptor}; +pub use self::host::{Externals, HostError, NopExternals, RuntimeArgs}; +pub use self::imports::{ImportResolver, ImportsBuilder, ModuleImportResolver}; +pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE}; +pub use self::module::{ExternVal, ModuleInstance, ModuleRef, NotStartedModuleRef}; +pub use self::table::{TableInstance, TableRef}; +pub use self::types::{GlobalDescriptor, MemoryDescriptor, Signature, TableDescriptor, ValueType}; +pub use self::value::{Error as ValueError, FromRuntimeValue, LittleEndianConvert, RuntimeValue}; /// WebAssembly-specific sizes and units. pub mod memory_units { - pub use memory_units_crate::wasm32::*; - pub use memory_units_crate::{Bytes, ByteSize, RoundUpTo, size_of}; + pub use memory_units_crate::wasm32::*; + pub use memory_units_crate::{size_of, ByteSize, Bytes, RoundUpTo}; } /// Deserialized module prepared for instantiation. pub struct Module { - code_map: Vec, - module: parity_wasm::elements::Module, + code_map: Vec, + module: parity_wasm::elements::Module, } impl Module { - /// Create `Module` from `parity_wasm::elements::Module`. - /// - /// This function will load, validate and prepare a `parity_wasm`'s `Module`. - /// - /// # Errors - /// - /// Returns `Err` if provided `Module` is not valid. - /// - /// # Examples - /// - /// ```rust - /// extern crate parity_wasm; - /// extern crate wasmi; - /// - /// use parity_wasm::builder; - /// use parity_wasm::elements; - /// - /// fn main() { - /// let parity_module = - /// builder::module() - /// .function() - /// .signature().with_param(elements::ValueType::I32).build() - /// .body().build() - /// .build() - /// .build(); - /// - /// let module = wasmi::Module::from_parity_wasm_module(parity_module) - /// .expect("parity-wasm builder generated invalid module!"); - /// - /// // Instantiate `module`, etc... - /// } - /// ``` - pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result { - use validation::{validate_module, ValidatedModule}; - let ValidatedModule { - code_map, - module, - } = validate_module(module)?; + /// Create `Module` from `parity_wasm::elements::Module`. + /// + /// This function will load, validate and prepare a `parity_wasm`'s `Module`. + /// + /// # Errors + /// + /// Returns `Err` if provided `Module` is not valid. + /// + /// # Examples + /// + /// ```rust + /// extern crate parity_wasm; + /// extern crate wasmi; + /// + /// use parity_wasm::builder; + /// use parity_wasm::elements; + /// + /// fn main() { + /// let parity_module = + /// builder::module() + /// .function() + /// .signature().with_param(elements::ValueType::I32).build() + /// .body().build() + /// .build() + /// .build(); + /// + /// let module = wasmi::Module::from_parity_wasm_module(parity_module) + /// .expect("parity-wasm builder generated invalid module!"); + /// + /// // Instantiate `module`, etc... + /// } + /// ``` + pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result { + use validation::{validate_module, ValidatedModule}; + let ValidatedModule { code_map, module } = validate_module(module)?; - Ok(Module { - code_map, - module, - }) - } + Ok(Module { code_map, module }) + } - /// Fail if the module contains any floating-point operations - /// - /// # Errors - /// - /// Returns `Err` if provided `Module` is not valid. - /// - /// # Examples - /// - /// ```rust - /// # extern crate wasmi; - /// # extern crate wabt; - /// - /// let wasm_binary: Vec = - /// wabt::wat2wasm( - /// r#" - /// (module - /// (func $add (param $lhs i32) (param $rhs i32) (result i32) - /// get_local $lhs - /// get_local $rhs - /// i32.add)) - /// "#, - /// ) - /// .expect("failed to parse wat"); - /// - /// // Load wasm binary and prepare it for instantiation. - /// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed"); - /// assert!(module.deny_floating_point().is_ok()); - /// - /// let wasm_binary: Vec = - /// wabt::wat2wasm( - /// r#" - /// (module - /// (func $add (param $lhs f32) (param $rhs f32) (result f32) - /// get_local $lhs - /// get_local $rhs - /// f32.add)) - /// "#, - /// ) - /// .expect("failed to parse wat"); - /// - /// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed"); - /// assert!(module.deny_floating_point().is_err()); - /// - /// let wasm_binary: Vec = - /// wabt::wat2wasm( - /// r#" - /// (module - /// (func $add (param $lhs f32) (param $rhs f32) (result f32) - /// get_local $lhs)) - /// "#, - /// ) - /// .expect("failed to parse wat"); - /// - /// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed"); - /// assert!(module.deny_floating_point().is_err()); - /// ``` - pub fn deny_floating_point(&self) -> Result<(), Error> { - validation::deny_floating_point(&self.module).map_err(Into::into) - } + /// Fail if the module contains any floating-point operations + /// + /// # Errors + /// + /// Returns `Err` if provided `Module` is not valid. + /// + /// # Examples + /// + /// ```rust + /// # extern crate wasmi; + /// # extern crate wabt; + /// + /// let wasm_binary: Vec = + /// wabt::wat2wasm( + /// r#" + /// (module + /// (func $add (param $lhs i32) (param $rhs i32) (result i32) + /// get_local $lhs + /// get_local $rhs + /// i32.add)) + /// "#, + /// ) + /// .expect("failed to parse wat"); + /// + /// // Load wasm binary and prepare it for instantiation. + /// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed"); + /// assert!(module.deny_floating_point().is_ok()); + /// + /// let wasm_binary: Vec = + /// wabt::wat2wasm( + /// r#" + /// (module + /// (func $add (param $lhs f32) (param $rhs f32) (result f32) + /// get_local $lhs + /// get_local $rhs + /// f32.add)) + /// "#, + /// ) + /// .expect("failed to parse wat"); + /// + /// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed"); + /// assert!(module.deny_floating_point().is_err()); + /// + /// let wasm_binary: Vec = + /// wabt::wat2wasm( + /// r#" + /// (module + /// (func $add (param $lhs f32) (param $rhs f32) (result f32) + /// get_local $lhs)) + /// "#, + /// ) + /// .expect("failed to parse wat"); + /// + /// let module = wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed"); + /// assert!(module.deny_floating_point().is_err()); + /// ``` + pub fn deny_floating_point(&self) -> Result<(), Error> { + validation::deny_floating_point(&self.module).map_err(Into::into) + } - /// Create `Module` from a given buffer. - /// - /// This function will deserialize wasm module from a given module, - /// validate and prepare it for instantiation. - /// - /// # Errors - /// - /// Returns `Err` if wasm binary in provided `buffer` is not valid wasm binary. - /// - /// # Examples - /// - /// ```rust - /// extern crate wasmi; - /// - /// fn main() { - /// let module = - /// wasmi::Module::from_buffer( - /// // Minimal module: - /// // \0asm - magic - /// // 0x01 - version (in little-endian) - /// &[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00] - /// ).expect("Failed to load minimal module"); - /// - /// // Instantiate `module`, etc... - /// } - /// ``` - pub fn from_buffer>(buffer: B) -> Result { - let module = parity_wasm::elements::deserialize_buffer(buffer.as_ref()) - .map_err(|e: parity_wasm::elements::Error| Error::Validation(e.to_string()))?; - Module::from_parity_wasm_module(module) - } + /// Create `Module` from a given buffer. + /// + /// This function will deserialize wasm module from a given module, + /// validate and prepare it for instantiation. + /// + /// # Errors + /// + /// Returns `Err` if wasm binary in provided `buffer` is not valid wasm binary. + /// + /// # Examples + /// + /// ```rust + /// extern crate wasmi; + /// + /// fn main() { + /// let module = + /// wasmi::Module::from_buffer( + /// // Minimal module: + /// // \0asm - magic + /// // 0x01 - version (in little-endian) + /// &[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00] + /// ).expect("Failed to load minimal module"); + /// + /// // Instantiate `module`, etc... + /// } + /// ``` + pub fn from_buffer>(buffer: B) -> Result { + let module = parity_wasm::elements::deserialize_buffer(buffer.as_ref()) + .map_err(|e: parity_wasm::elements::Error| Error::Validation(e.to_string()))?; + Module::from_parity_wasm_module(module) + } - pub(crate) fn module(&self) -> &parity_wasm::elements::Module { - &self.module - } + pub(crate) fn module(&self) -> &parity_wasm::elements::Module { + &self.module + } - pub(crate) fn code(&self) -> &Vec { - &self.code_map - } + pub(crate) fn code(&self) -> &Vec { + &self.code_map + } } diff --git a/src/memory.rs b/src/memory.rs index c25f335..038ab58 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,15 +1,15 @@ #[allow(unused_imports)] use alloc::prelude::*; use alloc::rc::Rc; -use core::u32; -use core::ops::Range; +use core::cell::{Cell, RefCell}; use core::cmp; use core::fmt; -use core::cell::{Cell, RefCell}; +use core::ops::Range; +use core::u32; +use memory_units::{Bytes, Pages, RoundUpTo}; use parity_wasm::elements::ResizableLimits; -use Error; -use memory_units::{RoundUpTo, Pages, Bytes}; use value::LittleEndianConvert; +use Error; /// Size of a page of [linear memory][`MemoryInstance`] - 64KiB. /// @@ -31,10 +31,10 @@ const LINEAR_MEMORY_MAX_PAGES: Pages = Pages(65536); pub struct MemoryRef(Rc); impl ::core::ops::Deref for MemoryRef { - type Target = MemoryInstance; - fn deref(&self) -> &MemoryInstance { - &self.0 - } + type Target = MemoryInstance; + fn deref(&self) -> &MemoryInstance { + &self.0 + } } /// Runtime representation of a linear memory (or `memory` for short). @@ -51,595 +51,687 @@ impl ::core::ops::Deref for MemoryRef { /// /// [`LINEAR_MEMORY_PAGE_SIZE`]: constant.LINEAR_MEMORY_PAGE_SIZE.html pub struct MemoryInstance { - /// Memory limits. - limits: ResizableLimits, - /// Linear memory buffer with lazy allocation. - buffer: RefCell>, - initial: Pages, - current_size: Cell, - maximum: Option, + /// Memory limits. + limits: ResizableLimits, + /// Linear memory buffer with lazy allocation. + buffer: RefCell>, + initial: Pages, + current_size: Cell, + maximum: Option, } impl fmt::Debug for MemoryInstance { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("MemoryInstance") - .field("limits", &self.limits) - .field("buffer.len", &self.buffer.borrow().len()) - .field("maximum", &self.maximum) - .field("initial", &self.initial) - .finish() - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("MemoryInstance") + .field("limits", &self.limits) + .field("buffer.len", &self.buffer.borrow().len()) + .field("maximum", &self.maximum) + .field("initial", &self.initial) + .finish() + } } struct CheckedRegion { - offset: usize, - size: usize, + offset: usize, + size: usize, } impl CheckedRegion { - fn range(&self) -> Range { - self.offset..self.offset+self.size - } + fn range(&self) -> Range { + self.offset..self.offset + self.size + } - fn intersects(&self, other: &Self) -> bool { - let low = cmp::max(self.offset, other.offset); - let high = cmp::min(self.offset + self.size, other.offset + other.size); + fn intersects(&self, other: &Self) -> bool { + let low = cmp::max(self.offset, other.offset); + let high = cmp::min(self.offset + self.size, other.offset + other.size); - low < high - } + low < high + } } impl MemoryInstance { - /// Allocate a memory instance. - /// - /// The memory allocated with initial number of pages specified by `initial`. - /// Minimal possible value for `initial` is 0 and maximum possible is `65536`. - /// (Since maximum addressible memory is 232 = 4GiB = 65536 * [64KiB][`LINEAR_MEMORY_PAGE_SIZE`]). - /// - /// It is possible to limit maximum number of pages this memory instance can have by specifying - /// `maximum`. If not specified, this memory instance would be able to allocate up to 4GiB. - /// - /// Allocated memory is always zeroed. - /// - /// # Errors - /// - /// Returns `Err` if: - /// - /// - `initial` is greater than `maximum` - /// - either `initial` or `maximum` is greater than `65536`. - /// - /// [`LINEAR_MEMORY_PAGE_SIZE`]: constant.LINEAR_MEMORY_PAGE_SIZE.html - pub fn alloc(initial: Pages, maximum: Option) -> Result { - validate_memory(initial, maximum).map_err(Error::Memory)?; + /// Allocate a memory instance. + /// + /// The memory allocated with initial number of pages specified by `initial`. + /// Minimal possible value for `initial` is 0 and maximum possible is `65536`. + /// (Since maximum addressible memory is 232 = 4GiB = 65536 * [64KiB][`LINEAR_MEMORY_PAGE_SIZE`]). + /// + /// It is possible to limit maximum number of pages this memory instance can have by specifying + /// `maximum`. If not specified, this memory instance would be able to allocate up to 4GiB. + /// + /// Allocated memory is always zeroed. + /// + /// # Errors + /// + /// Returns `Err` if: + /// + /// - `initial` is greater than `maximum` + /// - either `initial` or `maximum` is greater than `65536`. + /// + /// [`LINEAR_MEMORY_PAGE_SIZE`]: constant.LINEAR_MEMORY_PAGE_SIZE.html + pub fn alloc(initial: Pages, maximum: Option) -> Result { + validate_memory(initial, maximum).map_err(Error::Memory)?; - let memory = MemoryInstance::new(initial, maximum); - Ok(MemoryRef(Rc::new(memory))) - } + let memory = MemoryInstance::new(initial, maximum); + Ok(MemoryRef(Rc::new(memory))) + } - /// Create new linear memory instance. - fn new(initial: Pages, maximum: Option) -> Self { - let limits = ResizableLimits::new(initial.0 as u32, maximum.map(|p| p.0 as u32)); + /// Create new linear memory instance. + fn new(initial: Pages, maximum: Option) -> Self { + let limits = ResizableLimits::new(initial.0 as u32, maximum.map(|p| p.0 as u32)); - let initial_size: Bytes = initial.into(); - MemoryInstance { - limits: limits, - buffer: RefCell::new(Vec::with_capacity(4096)), - initial: initial, - current_size: Cell::new(initial_size.0), - maximum: maximum, - } - } + let initial_size: Bytes = initial.into(); + MemoryInstance { + limits: limits, + buffer: RefCell::new(Vec::with_capacity(4096)), + initial: initial, + current_size: Cell::new(initial_size.0), + maximum: maximum, + } + } - /// Return linear memory limits. - pub(crate) fn limits(&self) -> &ResizableLimits { - &self.limits - } + /// Return linear memory limits. + pub(crate) fn limits(&self) -> &ResizableLimits { + &self.limits + } - /// Returns number of pages this `MemoryInstance` was created with. - pub fn initial(&self) -> Pages { - self.initial - } + /// Returns number of pages this `MemoryInstance` was created with. + pub fn initial(&self) -> Pages { + self.initial + } - /// Returns maximum amount of pages this `MemoryInstance` can grow to. - /// - /// Returns `None` if there is no limit set. - /// Maximum memory size cannot exceed `65536` pages or 4GiB. - pub fn maximum(&self) -> Option { - self.maximum - } + /// Returns maximum amount of pages this `MemoryInstance` can grow to. + /// + /// Returns `None` if there is no limit set. + /// Maximum memory size cannot exceed `65536` pages or 4GiB. + pub fn maximum(&self) -> Option { + self.maximum + } - /// Returns current linear memory size. - /// - /// Maximum memory size cannot exceed `65536` pages or 4GiB. - /// - /// # Example - /// - /// To convert number of pages to number of bytes you can use the following code: - /// - /// ```rust - /// use wasmi::MemoryInstance; - /// use wasmi::memory_units::*; - /// - /// let memory = MemoryInstance::alloc(Pages(1), None).unwrap(); - /// let byte_size: Bytes = memory.current_size().into(); - /// assert_eq!( - /// byte_size, - /// Bytes(65536), - /// ); - /// ``` - pub fn current_size(&self) -> Pages { - Bytes(self.current_size.get()).round_up_to() - } + /// Returns current linear memory size. + /// + /// Maximum memory size cannot exceed `65536` pages or 4GiB. + /// + /// # Example + /// + /// To convert number of pages to number of bytes you can use the following code: + /// + /// ```rust + /// use wasmi::MemoryInstance; + /// use wasmi::memory_units::*; + /// + /// let memory = MemoryInstance::alloc(Pages(1), None).unwrap(); + /// let byte_size: Bytes = memory.current_size().into(); + /// assert_eq!( + /// byte_size, + /// Bytes(65536), + /// ); + /// ``` + pub fn current_size(&self) -> Pages { + Bytes(self.current_size.get()).round_up_to() + } - /// Get value from memory at given offset. - pub fn get_value(&self, offset: u32) -> Result { - let mut buffer = self.buffer.borrow_mut(); - let region = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::())?; - Ok(T::from_little_endian(&buffer[region.range()]).expect("Slice size is checked")) - } + /// Get value from memory at given offset. + pub fn get_value(&self, offset: u32) -> Result { + let mut buffer = self.buffer.borrow_mut(); + let region = + self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::())?; + Ok(T::from_little_endian(&buffer[region.range()]).expect("Slice size is checked")) + } - /// Copy data from memory at given offset. - /// - /// This will allocate vector for you. - /// If you can provide a mutable slice you can use [`get_into`]. - /// - /// [`get_into`]: #method.get_into - pub fn get(&self, offset: u32, size: usize) -> Result, Error> { - let mut buffer = self.buffer.borrow_mut(); - let region = self.checked_region(&mut buffer, offset as usize, size)?; + /// Copy data from memory at given offset. + /// + /// This will allocate vector for you. + /// If you can provide a mutable slice you can use [`get_into`]. + /// + /// [`get_into`]: #method.get_into + pub fn get(&self, offset: u32, size: usize) -> Result, Error> { + let mut buffer = self.buffer.borrow_mut(); + let region = self.checked_region(&mut buffer, offset as usize, size)?; - Ok(buffer[region.range()].to_vec()) - } + Ok(buffer[region.range()].to_vec()) + } - /// Copy data from given offset in the memory into `target` slice. - /// - /// # Errors - /// - /// Returns `Err` if the specified region is out of bounds. - pub fn get_into(&self, offset: u32, target: &mut [u8]) -> Result<(), Error> { - let mut buffer = self.buffer.borrow_mut(); - let region = self.checked_region(&mut buffer, offset as usize, target.len())?; + /// Copy data from given offset in the memory into `target` slice. + /// + /// # Errors + /// + /// Returns `Err` if the specified region is out of bounds. + pub fn get_into(&self, offset: u32, target: &mut [u8]) -> Result<(), Error> { + let mut buffer = self.buffer.borrow_mut(); + let region = self.checked_region(&mut buffer, offset as usize, target.len())?; - target.copy_from_slice(&buffer[region.range()]); + target.copy_from_slice(&buffer[region.range()]); - Ok(()) - } + Ok(()) + } - /// Copy data in the memory at given offset. - pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error> { - let mut buffer = self.buffer.borrow_mut(); - let range = self.checked_region(&mut buffer, offset as usize, value.len())?.range(); + /// Copy data in the memory at given offset. + pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error> { + let mut buffer = self.buffer.borrow_mut(); + let range = self + .checked_region(&mut buffer, offset as usize, value.len())? + .range(); - buffer[range].copy_from_slice(value); + buffer[range].copy_from_slice(value); - Ok(()) - } + Ok(()) + } - /// Copy value in the memory at given offset. - pub fn set_value(&self, offset: u32, value: T) -> Result<(), Error> { - let mut buffer = self.buffer.borrow_mut(); - let range = self.checked_region(&mut buffer, offset as usize, ::core::mem::size_of::())?.range(); - value.into_little_endian(&mut buffer[range]); - Ok(()) - } + /// Copy value in the memory at given offset. + pub fn set_value(&self, offset: u32, value: T) -> Result<(), Error> { + let mut buffer = self.buffer.borrow_mut(); + let range = self + .checked_region(&mut buffer, offset as usize, ::core::mem::size_of::())? + .range(); + value.into_little_endian(&mut buffer[range]); + Ok(()) + } - /// Increases the size of the linear memory by given number of pages. - /// Returns previous memory size if succeeds. - /// - /// # Errors - /// - /// Returns `Err` if attempted to allocate more memory than permited by the limit. - pub fn grow(&self, additional: Pages) -> Result { - let size_before_grow: Pages = self.current_size(); + /// Increases the size of the linear memory by given number of pages. + /// Returns previous memory size if succeeds. + /// + /// # Errors + /// + /// Returns `Err` if attempted to allocate more memory than permited by the limit. + pub fn grow(&self, additional: Pages) -> Result { + let size_before_grow: Pages = self.current_size(); - if additional == Pages(0) { - return Ok(size_before_grow); - } - if additional > Pages(65536) { - return Err(Error::Memory(format!( - "Trying to grow memory by more than 65536 pages" - ))); - } + if additional == Pages(0) { + return Ok(size_before_grow); + } + if additional > Pages(65536) { + return Err(Error::Memory(format!( + "Trying to grow memory by more than 65536 pages" + ))); + } - let new_size: Pages = size_before_grow + additional; - let maximum = self.maximum.unwrap_or(LINEAR_MEMORY_MAX_PAGES); - if new_size > maximum { - return Err(Error::Memory(format!( - "Trying to grow memory by {} pages when already have {}", - additional.0, size_before_grow.0, - ))); - } + let new_size: Pages = size_before_grow + additional; + let maximum = self.maximum.unwrap_or(LINEAR_MEMORY_MAX_PAGES); + if new_size > maximum { + return Err(Error::Memory(format!( + "Trying to grow memory by {} pages when already have {}", + additional.0, size_before_grow.0, + ))); + } - let new_buffer_length: Bytes = new_size.into(); - self.current_size.set(new_buffer_length.0); - Ok(size_before_grow) - } + let new_buffer_length: Bytes = new_size.into(); + self.current_size.set(new_buffer_length.0); + Ok(size_before_grow) + } - fn checked_region(&self, buffer: &mut B, offset: usize, size: usize) -> Result - where B: ::core::ops::DerefMut> - { - let end = offset.checked_add(size) - .ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size, offset)))?; + fn checked_region( + &self, + buffer: &mut B, + offset: usize, + size: usize, + ) -> Result + where + B: ::core::ops::DerefMut>, + { + let end = offset.checked_add(size).ok_or_else(|| { + Error::Memory(format!( + "trying to access memory block of size {} from offset {}", + size, offset + )) + })?; - if end <= self.current_size.get() && buffer.len() < end { - buffer.resize(end, 0); - } + if end <= self.current_size.get() && buffer.len() < end { + buffer.resize(end, 0); + } - if end > buffer.len() { - return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset, end, buffer.len()))); - } + if end > buffer.len() { + return Err(Error::Memory(format!( + "trying to access region [{}..{}] in memory [0..{}]", + offset, + end, + buffer.len() + ))); + } - Ok(CheckedRegion { - offset: offset, - size: size, - }) - } + Ok(CheckedRegion { + offset: offset, + size: size, + }) + } - fn checked_region_pair(&self, buffer: &mut B, offset1: usize, size1: usize, offset2: usize, size2: usize) - -> Result<(CheckedRegion, CheckedRegion), Error> - where B: ::core::ops::DerefMut> - { - let end1 = offset1.checked_add(size1) - .ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size1, offset1)))?; + fn checked_region_pair( + &self, + buffer: &mut B, + offset1: usize, + size1: usize, + offset2: usize, + size2: usize, + ) -> Result<(CheckedRegion, CheckedRegion), Error> + where + B: ::core::ops::DerefMut>, + { + let end1 = offset1.checked_add(size1).ok_or_else(|| { + Error::Memory(format!( + "trying to access memory block of size {} from offset {}", + size1, offset1 + )) + })?; - let end2 = offset2.checked_add(size2) - .ok_or_else(|| Error::Memory(format!("trying to access memory block of size {} from offset {}", size2, offset2)))?; + let end2 = offset2.checked_add(size2).ok_or_else(|| { + Error::Memory(format!( + "trying to access memory block of size {} from offset {}", + size2, offset2 + )) + })?; - let max = cmp::max(end1, end2); - if max <= self.current_size.get() && buffer.len() < max { - buffer.resize(max, 0); - } + let max = cmp::max(end1, end2); + if max <= self.current_size.get() && buffer.len() < max { + buffer.resize(max, 0); + } - if end1 > buffer.len() { - return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset1, end1, buffer.len()))); - } + if end1 > buffer.len() { + return Err(Error::Memory(format!( + "trying to access region [{}..{}] in memory [0..{}]", + offset1, + end1, + buffer.len() + ))); + } - if end2 > buffer.len() { - return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset2, end2, buffer.len()))); - } + if end2 > buffer.len() { + return Err(Error::Memory(format!( + "trying to access region [{}..{}] in memory [0..{}]", + offset2, + end2, + buffer.len() + ))); + } - Ok(( - CheckedRegion { offset: offset1, size: size1 }, - CheckedRegion { offset: offset2, size: size2 }, - )) - } + Ok(( + CheckedRegion { + offset: offset1, + size: size1, + }, + CheckedRegion { + offset: offset2, + size: size2, + }, + )) + } - /// Copy contents of one memory region to another. - /// - /// Semantically equivalent to `memmove`. - /// - /// # Errors - /// - /// Returns `Err` if either of specified regions is out of bounds. - pub fn copy(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> { - let mut buffer = self.buffer.borrow_mut(); + /// Copy contents of one memory region to another. + /// + /// Semantically equivalent to `memmove`. + /// + /// # Errors + /// + /// Returns `Err` if either of specified regions is out of bounds. + pub fn copy(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> { + let mut buffer = self.buffer.borrow_mut(); - let (read_region, write_region) = self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?; + let (read_region, write_region) = + self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?; - unsafe { ::core::ptr::copy( - buffer[read_region.range()].as_ptr(), - buffer[write_region.range()].as_mut_ptr(), - len, - )} + unsafe { + ::core::ptr::copy( + buffer[read_region.range()].as_ptr(), + buffer[write_region.range()].as_mut_ptr(), + len, + ) + } - Ok(()) - } + Ok(()) + } - /// Copy contents of one memory region to another (non-overlapping version). - /// - /// Semantically equivalent to `memcpy`. - /// but returns Error if source overlaping with destination. - /// - /// # Errors - /// - /// Returns `Err` if: - /// - /// - either of specified regions is out of bounds, - /// - these regions overlaps. - pub fn copy_nonoverlapping(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> { - let mut buffer = self.buffer.borrow_mut(); + /// Copy contents of one memory region to another (non-overlapping version). + /// + /// Semantically equivalent to `memcpy`. + /// but returns Error if source overlaping with destination. + /// + /// # Errors + /// + /// Returns `Err` if: + /// + /// - either of specified regions is out of bounds, + /// - these regions overlaps. + pub fn copy_nonoverlapping( + &self, + src_offset: usize, + dst_offset: usize, + len: usize, + ) -> Result<(), Error> { + let mut buffer = self.buffer.borrow_mut(); - let (read_region, write_region) = self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?; + let (read_region, write_region) = + self.checked_region_pair(&mut buffer, src_offset, len, dst_offset, len)?; - if read_region.intersects(&write_region) { - return Err(Error::Memory(format!("non-overlapping copy is used for overlapping regions"))) - } + if read_region.intersects(&write_region) { + return Err(Error::Memory(format!( + "non-overlapping copy is used for overlapping regions" + ))); + } - unsafe { ::core::ptr::copy_nonoverlapping( - buffer[read_region.range()].as_ptr(), - buffer[write_region.range()].as_mut_ptr(), - len, - )} + unsafe { + ::core::ptr::copy_nonoverlapping( + buffer[read_region.range()].as_ptr(), + buffer[write_region.range()].as_mut_ptr(), + len, + ) + } - Ok(()) - } + Ok(()) + } - /// Copy memory between two (possibly distinct) memory instances. - /// - /// If the same memory instance passed as `src` and `dst` then usual `copy` will be used. - pub fn transfer(src: &MemoryRef, src_offset: usize, dst: &MemoryRef, dst_offset: usize, len: usize) -> Result<(), Error> { - if Rc::ptr_eq(&src.0, &dst.0) { - // `transfer` is invoked with with same source and destination. Let's assume that regions may - // overlap and use `copy`. - return src.copy(src_offset, dst_offset, len); - } + /// Copy memory between two (possibly distinct) memory instances. + /// + /// If the same memory instance passed as `src` and `dst` then usual `copy` will be used. + pub fn transfer( + src: &MemoryRef, + src_offset: usize, + dst: &MemoryRef, + dst_offset: usize, + len: usize, + ) -> Result<(), Error> { + if Rc::ptr_eq(&src.0, &dst.0) { + // `transfer` is invoked with with same source and destination. Let's assume that regions may + // overlap and use `copy`. + return src.copy(src_offset, dst_offset, len); + } - // Because memory references point to different memory instances, it is safe to `borrow_mut` - // both buffers at once (modulo `with_direct_access_mut`). - let mut src_buffer = src.buffer.borrow_mut(); - let mut dst_buffer = dst.buffer.borrow_mut(); + // Because memory references point to different memory instances, it is safe to `borrow_mut` + // both buffers at once (modulo `with_direct_access_mut`). + let mut src_buffer = src.buffer.borrow_mut(); + let mut dst_buffer = dst.buffer.borrow_mut(); - let src_range = src.checked_region(&mut src_buffer, src_offset, len)?.range(); - let dst_range = dst.checked_region(&mut dst_buffer, dst_offset, len)?.range(); + let src_range = src + .checked_region(&mut src_buffer, src_offset, len)? + .range(); + let dst_range = dst + .checked_region(&mut dst_buffer, dst_offset, len)? + .range(); - dst_buffer[dst_range].copy_from_slice(&src_buffer[src_range]); + dst_buffer[dst_range].copy_from_slice(&src_buffer[src_range]); - Ok(()) - } + Ok(()) + } - /// Fill the memory region with the specified value. - /// - /// Semantically equivalent to `memset`. - /// - /// # Errors - /// - /// Returns `Err` if the specified region is out of bounds. - pub fn clear(&self, offset: usize, new_val: u8, len: usize) -> Result<(), Error> { - let mut buffer = self.buffer.borrow_mut(); + /// Fill the memory region with the specified value. + /// + /// Semantically equivalent to `memset`. + /// + /// # Errors + /// + /// Returns `Err` if the specified region is out of bounds. + pub fn clear(&self, offset: usize, new_val: u8, len: usize) -> Result<(), Error> { + let mut buffer = self.buffer.borrow_mut(); - let range = self.checked_region(&mut buffer, offset, len)?.range(); - for val in &mut buffer[range] { *val = new_val } - Ok(()) - } + let range = self.checked_region(&mut buffer, offset, len)?.range(); + for val in &mut buffer[range] { + *val = new_val + } + Ok(()) + } - /// Fill the specified memory region with zeroes. - /// - /// # Errors - /// - /// Returns `Err` if the specified region is out of bounds. - pub fn zero(&self, offset: usize, len: usize) -> Result<(), Error> { - self.clear(offset, 0, len) - } + /// Fill the specified memory region with zeroes. + /// + /// # Errors + /// + /// Returns `Err` if the specified region is out of bounds. + pub fn zero(&self, offset: usize, len: usize) -> Result<(), Error> { + self.clear(offset, 0, len) + } - /// Provides direct access to the underlying memory buffer. - /// - /// # Panics - /// - /// Any call that requires write access to memory (such as [`set`], [`clear`], etc) made within - /// the closure will panic. Note that the buffer size may be arbitraty. Proceed with caution. - /// - /// [`set`]: #method.get - /// [`clear`]: #method.set - pub fn with_direct_access R>(&self, f: F) -> R { - let buf = self.buffer.borrow(); - f(&*buf) - } + /// Provides direct access to the underlying memory buffer. + /// + /// # Panics + /// + /// Any call that requires write access to memory (such as [`set`], [`clear`], etc) made within + /// the closure will panic. Note that the buffer size may be arbitraty. Proceed with caution. + /// + /// [`set`]: #method.get + /// [`clear`]: #method.set + pub fn with_direct_access R>(&self, f: F) -> R { + let buf = self.buffer.borrow(); + f(&*buf) + } - /// Provides direct mutable access to the underlying memory buffer. - /// - /// # Panics - /// - /// Any calls that requires either read or write access to memory (such as [`get`], [`set`], [`copy`], etc) made - /// within the closure will panic. Note that the buffer size may be arbitraty. - /// The closure may however resize it. Proceed with caution. - /// - /// [`get`]: #method.get - /// [`set`]: #method.set - /// [`copy`]: #method.copy - pub fn with_direct_access_mut) -> R>(&self, f: F) -> R { - let mut buf = self.buffer.borrow_mut(); - f(&mut buf) - } + /// Provides direct mutable access to the underlying memory buffer. + /// + /// # Panics + /// + /// Any calls that requires either read or write access to memory (such as [`get`], [`set`], [`copy`], etc) made + /// within the closure will panic. Note that the buffer size may be arbitraty. + /// The closure may however resize it. Proceed with caution. + /// + /// [`get`]: #method.get + /// [`set`]: #method.set + /// [`copy`]: #method.copy + pub fn with_direct_access_mut) -> R>(&self, f: F) -> R { + let mut buf = self.buffer.borrow_mut(); + f(&mut buf) + } } pub fn validate_memory(initial: Pages, maximum: Option) -> Result<(), String> { - if initial > LINEAR_MEMORY_MAX_PAGES { - return Err(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES.0)); - } - if let Some(maximum) = maximum { - if initial > maximum { - return Err(format!( - "maximum limit {} is less than minimum {}", - maximum.0, - initial.0, - )); - } + if initial > LINEAR_MEMORY_MAX_PAGES { + return Err(format!( + "initial memory size must be at most {} pages", + LINEAR_MEMORY_MAX_PAGES.0 + )); + } + if let Some(maximum) = maximum { + if initial > maximum { + return Err(format!( + "maximum limit {} is less than minimum {}", + maximum.0, initial.0, + )); + } - if maximum > LINEAR_MEMORY_MAX_PAGES { - return Err(format!("maximum memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES.0)); - } - } - Ok(()) + if maximum > LINEAR_MEMORY_MAX_PAGES { + return Err(format!( + "maximum memory size must be at most {} pages", + LINEAR_MEMORY_MAX_PAGES.0 + )); + } + } + Ok(()) } #[cfg(test)] mod tests { - use super::{MemoryRef, MemoryInstance, LINEAR_MEMORY_PAGE_SIZE}; - use std::rc::Rc; - use Error; - use memory_units::Pages; + use super::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE}; + use memory_units::Pages; + use std::rc::Rc; + use Error; - #[test] - fn alloc() { - #[cfg(target_pointer_width = "64")] - let fixtures = &[ - (0, None, true), - (0, Some(0), true), - (1, None, true), - (1, Some(1), true), - (0, Some(1), true), - (1, Some(0), false), - (0, Some(65536), true), - (65536, Some(65536), true), - (65536, Some(0), false), - (65536, None, true), - ]; + #[test] + fn alloc() { + #[cfg(target_pointer_width = "64")] + let fixtures = &[ + (0, None, true), + (0, Some(0), true), + (1, None, true), + (1, Some(1), true), + (0, Some(1), true), + (1, Some(0), false), + (0, Some(65536), true), + (65536, Some(65536), true), + (65536, Some(0), false), + (65536, None, true), + ]; - #[cfg(target_pointer_width = "32")] - let fixtures = &[ - (0, None, true), - (0, Some(0), true), - (1, None, true), - (1, Some(1), true), - (0, Some(1), true), - (1, Some(0), false), - ]; + #[cfg(target_pointer_width = "32")] + let fixtures = &[ + (0, None, true), + (0, Some(0), true), + (1, None, true), + (1, Some(1), true), + (0, Some(1), true), + (1, Some(0), false), + ]; - for (index, &(initial, maybe_max, expected_ok)) in fixtures.iter().enumerate() { - let initial: Pages = Pages(initial); - let maximum: Option = maybe_max.map(|m| Pages(m)); - let result = MemoryInstance::alloc(initial, maximum); - if result.is_ok() != expected_ok { - panic!( - "unexpected error at {}, initial={:?}, max={:?}, expected={}, result={:?}", - index, - initial, - maybe_max, - expected_ok, - result, - ); - } - } - } + for (index, &(initial, maybe_max, expected_ok)) in fixtures.iter().enumerate() { + let initial: Pages = Pages(initial); + let maximum: Option = maybe_max.map(|m| Pages(m)); + let result = MemoryInstance::alloc(initial, maximum); + if result.is_ok() != expected_ok { + panic!( + "unexpected error at {}, initial={:?}, max={:?}, expected={}, result={:?}", + index, initial, maybe_max, expected_ok, result, + ); + } + } + } - #[test] - fn ensure_page_size() { - use memory_units::ByteSize; - assert_eq!(LINEAR_MEMORY_PAGE_SIZE, Pages::byte_size()); - } + #[test] + fn ensure_page_size() { + use memory_units::ByteSize; + assert_eq!(LINEAR_MEMORY_PAGE_SIZE, Pages::byte_size()); + } - fn create_memory(initial_content: &[u8]) -> MemoryInstance { - let mem = MemoryInstance::new(Pages(1), Some(Pages(1))); - mem.set(0, initial_content).expect("Successful initialize the memory"); - mem - } + fn create_memory(initial_content: &[u8]) -> MemoryInstance { + let mem = MemoryInstance::new(Pages(1), Some(Pages(1))); + mem.set(0, initial_content) + .expect("Successful initialize the memory"); + mem + } - #[test] - fn copy_overlaps_1() { - let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - mem.copy(0, 4, 6).expect("Successfully copy the elements"); - let result = mem.get(0, 10).expect("Successfully retrieve the result"); - assert_eq!(result, &[0, 1, 2, 3, 0, 1, 2, 3, 4, 5]); - } + #[test] + fn copy_overlaps_1() { + let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + mem.copy(0, 4, 6).expect("Successfully copy the elements"); + let result = mem.get(0, 10).expect("Successfully retrieve the result"); + assert_eq!(result, &[0, 1, 2, 3, 0, 1, 2, 3, 4, 5]); + } - #[test] - fn copy_overlaps_2() { - let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - mem.copy(4, 0, 6).expect("Successfully copy the elements"); - let result = mem.get(0, 10).expect("Successfully retrieve the result"); - assert_eq!(result, &[4, 5, 6, 7, 8, 9, 6, 7, 8, 9]); - } + #[test] + fn copy_overlaps_2() { + let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + mem.copy(4, 0, 6).expect("Successfully copy the elements"); + let result = mem.get(0, 10).expect("Successfully retrieve the result"); + assert_eq!(result, &[4, 5, 6, 7, 8, 9, 6, 7, 8, 9]); + } - #[test] - fn copy_nonoverlapping() { - let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - mem.copy_nonoverlapping(0, 10, 10).expect("Successfully copy the elements"); - let result = mem.get(10, 10).expect("Successfully retrieve the result"); - assert_eq!(result, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - } + #[test] + fn copy_nonoverlapping() { + let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + mem.copy_nonoverlapping(0, 10, 10) + .expect("Successfully copy the elements"); + let result = mem.get(10, 10).expect("Successfully retrieve the result"); + assert_eq!(result, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + } - #[test] - fn copy_nonoverlapping_overlaps_1() { - let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - let result = mem.copy_nonoverlapping(0, 4, 6); - match result { - Err(Error::Memory(_)) => {}, - _ => panic!("Expected Error::Memory(_) result, but got {:?}", result), - } - } + #[test] + fn copy_nonoverlapping_overlaps_1() { + let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + let result = mem.copy_nonoverlapping(0, 4, 6); + match result { + Err(Error::Memory(_)) => {} + _ => panic!("Expected Error::Memory(_) result, but got {:?}", result), + } + } - #[test] - fn copy_nonoverlapping_overlaps_2() { - let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - let result = mem.copy_nonoverlapping(4, 0, 6); - match result { - Err(Error::Memory(_)) => {}, - _ => panic!("Expected Error::Memory(_), but got {:?}", result), - } - } + #[test] + fn copy_nonoverlapping_overlaps_2() { + let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + let result = mem.copy_nonoverlapping(4, 0, 6); + match result { + Err(Error::Memory(_)) => {} + _ => panic!("Expected Error::Memory(_), but got {:?}", result), + } + } - #[test] - fn transfer_works() { - let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); - let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]))); + #[test] + fn transfer_works() { + let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); + let dst = MemoryRef(Rc::new(create_memory(&[ + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + ]))); - MemoryInstance::transfer(&src, 4, &dst, 0, 3).unwrap(); + MemoryInstance::transfer(&src, 4, &dst, 0, 3).unwrap(); - assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - assert_eq!(dst.get(0, 10).unwrap(), &[4, 5, 6, 13, 14, 15, 16, 17, 18, 19]); - } + assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + assert_eq!( + dst.get(0, 10).unwrap(), + &[4, 5, 6, 13, 14, 15, 16, 17, 18, 19] + ); + } - #[test] - fn transfer_still_works_with_same_memory() { - let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); + #[test] + fn transfer_still_works_with_same_memory() { + let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); - MemoryInstance::transfer(&src, 4, &src, 0, 3).unwrap(); + MemoryInstance::transfer(&src, 4, &src, 0, 3).unwrap(); - assert_eq!(src.get(0, 10).unwrap(), &[4, 5, 6, 3, 4, 5, 6, 7, 8, 9]); - } + assert_eq!(src.get(0, 10).unwrap(), &[4, 5, 6, 3, 4, 5, 6, 7, 8, 9]); + } - #[test] - fn transfer_oob_with_same_memory_errors() { - let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); - assert!(MemoryInstance::transfer(&src, 65535, &src, 0, 3).is_err()); + #[test] + fn transfer_oob_with_same_memory_errors() { + let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); + assert!(MemoryInstance::transfer(&src, 65535, &src, 0, 3).is_err()); - // Check that memories content left untouched - assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - } + // Check that memories content left untouched + assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + } - #[test] - fn transfer_oob_errors() { - let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); - let dst = MemoryRef(Rc::new(create_memory(&[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]))); + #[test] + fn transfer_oob_errors() { + let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))); + let dst = MemoryRef(Rc::new(create_memory(&[ + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + ]))); - assert!(MemoryInstance::transfer(&src, 65535, &dst, 0, 3).is_err()); + assert!(MemoryInstance::transfer(&src, 65535, &dst, 0, 3).is_err()); - // Check that memories content left untouched - assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - assert_eq!(dst.get(0, 10).unwrap(), &[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]); - } + // Check that memories content left untouched + assert_eq!(src.get(0, 10).unwrap(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + assert_eq!( + dst.get(0, 10).unwrap(), + &[10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + ); + } - #[test] - fn clear() { - let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - mem.clear(0, 0x4A, 10).expect("To successfully clear the memory"); - let result = mem.get(0, 10).expect("To successfully retrieve the result"); - assert_eq!(result, &[0x4A; 10]); - } + #[test] + fn clear() { + let mem = create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + mem.clear(0, 0x4A, 10) + .expect("To successfully clear the memory"); + let result = mem.get(0, 10).expect("To successfully retrieve the result"); + assert_eq!(result, &[0x4A; 10]); + } - #[test] - fn get_into() { - let mem = MemoryInstance::new(Pages(1), None); - mem.set(6, &[13, 17, 129]).expect("memory set should not fail"); + #[test] + fn get_into() { + let mem = MemoryInstance::new(Pages(1), None); + mem.set(6, &[13, 17, 129]) + .expect("memory set should not fail"); - let mut data = [0u8; 2]; - mem.get_into(7, &mut data[..]).expect("get_into should not fail"); + let mut data = [0u8; 2]; + mem.get_into(7, &mut data[..]) + .expect("get_into should not fail"); - assert_eq!(data, [17, 129]); - } + assert_eq!(data, [17, 129]); + } - #[test] - fn zero_copy() { - let mem = MemoryInstance::alloc(Pages(1), None).unwrap(); - mem.set(100, &[0]).expect("memory set should not fail"); - mem.with_direct_access_mut(|buf| { - assert_eq!(buf.len(), 101); - buf[..10].copy_from_slice(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - }); - mem.with_direct_access(|buf| { - assert_eq!(buf.len(), 101); - assert_eq!(&buf[..10], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - }); - } + #[test] + fn zero_copy() { + let mem = MemoryInstance::alloc(Pages(1), None).unwrap(); + mem.set(100, &[0]).expect("memory set should not fail"); + mem.with_direct_access_mut(|buf| { + assert_eq!(buf.len(), 101); + buf[..10].copy_from_slice(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + }); + mem.with_direct_access(|buf| { + assert_eq!(buf.len(), 101); + assert_eq!(&buf[..10], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + }); + } - #[should_panic] - #[test] - fn zero_copy_panics_on_nested_access() { - let mem = MemoryInstance::alloc(Pages(1), None).unwrap(); - let mem_inner = mem.clone(); - mem.with_direct_access(move |_| { - let _ = mem_inner.set(0, &[11, 12, 13]); - }); - } + #[should_panic] + #[test] + fn zero_copy_panics_on_nested_access() { + let mem = MemoryInstance::alloc(Pages(1), None).unwrap(); + let mem_inner = mem.clone(); + mem.with_direct_access(move |_| { + let _ = mem_inner.set(0, &[11, 12, 13]); + }); + } } diff --git a/src/module.rs b/src/module.rs index 6a52c15..e755c4b 100644 --- a/src/module.rs +++ b/src/module.rs @@ -1,26 +1,26 @@ #[allow(unused_imports)] use alloc::prelude::*; use alloc::rc::Rc; -use Trap; use core::cell::RefCell; use core::fmt; +use Trap; -#[cfg(feature = "std")] -use std::collections::HashMap; #[cfg(not(feature = "std"))] use hashmap_core::HashMap; +#[cfg(feature = "std")] +use std::collections::HashMap; -use parity_wasm::elements::{External, InitExpr, Internal, Instruction, ResizableLimits, Type}; -use {Module, Error, Signature, MemoryInstance, RuntimeValue, TableInstance}; -use imports::ImportResolver; -use global::{GlobalInstance, GlobalRef}; -use func::{FuncRef, FuncBody, FuncInstance}; -use table::TableRef; -use memory::MemoryRef; -use host::Externals; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; -use types::{GlobalDescriptor, TableDescriptor, MemoryDescriptor}; +use func::{FuncBody, FuncInstance, FuncRef}; +use global::{GlobalInstance, GlobalRef}; +use host::Externals; +use imports::ImportResolver; +use memory::MemoryRef; use memory_units::Pages; +use parity_wasm::elements::{External, InitExpr, Instruction, Internal, ResizableLimits, Type}; +use table::TableRef; +use types::{GlobalDescriptor, MemoryDescriptor, TableDescriptor}; +use {Error, MemoryInstance, Module, RuntimeValue, Signature, TableInstance}; /// Reference to a [`ModuleInstance`]. /// @@ -39,97 +39,97 @@ use memory_units::Pages; pub struct ModuleRef(pub(crate) Rc); impl ::core::ops::Deref for ModuleRef { - type Target = ModuleInstance; - fn deref(&self) -> &ModuleInstance { - &self.0 - } + type Target = ModuleInstance; + fn deref(&self) -> &ModuleInstance { + &self.0 + } } /// An external value is the runtime representation of an entity /// that can be imported or exported. pub enum ExternVal { - /// [Function][`FuncInstance`]. - /// - /// [`FuncInstance`]: struct.FuncInstance.html - Func(FuncRef), - /// [Table][`TableInstance`]. - /// - /// [`TableInstance`]: struct.TableInstance.html - Table(TableRef), - /// [Memory][`MemoryInstance`]. - /// - /// [`MemoryInstance`]: struct.MemoryInstance.html - Memory(MemoryRef), - /// [Global][`GlobalInstance`]. - /// - /// Should be immutable. - /// - /// [`GlobalInstance`]: struct.GlobalInstance.html - Global(GlobalRef), + /// [Function][`FuncInstance`]. + /// + /// [`FuncInstance`]: struct.FuncInstance.html + Func(FuncRef), + /// [Table][`TableInstance`]. + /// + /// [`TableInstance`]: struct.TableInstance.html + Table(TableRef), + /// [Memory][`MemoryInstance`]. + /// + /// [`MemoryInstance`]: struct.MemoryInstance.html + Memory(MemoryRef), + /// [Global][`GlobalInstance`]. + /// + /// Should be immutable. + /// + /// [`GlobalInstance`]: struct.GlobalInstance.html + Global(GlobalRef), } impl Clone for ExternVal { - fn clone(&self) -> Self { - match *self { - ExternVal::Func(ref func) => ExternVal::Func(func.clone()), - ExternVal::Table(ref table) => ExternVal::Table(table.clone()), - ExternVal::Memory(ref memory) => ExternVal::Memory(memory.clone()), - ExternVal::Global(ref global) => ExternVal::Global(global.clone()), - } - } + fn clone(&self) -> Self { + match *self { + ExternVal::Func(ref func) => ExternVal::Func(func.clone()), + ExternVal::Table(ref table) => ExternVal::Table(table.clone()), + ExternVal::Memory(ref memory) => ExternVal::Memory(memory.clone()), + ExternVal::Global(ref global) => ExternVal::Global(global.clone()), + } + } } impl fmt::Debug for ExternVal { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "ExternVal {{ {} }}", - match *self { - ExternVal::Func(_) => "Func", - ExternVal::Table(_) => "Table", - ExternVal::Memory(_) => "Memory", - ExternVal::Global(_) => "Global", - } - ) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "ExternVal {{ {} }}", + match *self { + ExternVal::Func(_) => "Func", + ExternVal::Table(_) => "Table", + ExternVal::Memory(_) => "Memory", + ExternVal::Global(_) => "Global", + } + ) + } } impl ExternVal { - /// Get underlying function reference if this `ExternVal` contains - /// a function, or `None` if it is some other kind. - pub fn as_func(&self) -> Option<&FuncRef> { - match *self { - ExternVal::Func(ref func) => Some(func), - _ => None, - } - } + /// Get underlying function reference if this `ExternVal` contains + /// a function, or `None` if it is some other kind. + pub fn as_func(&self) -> Option<&FuncRef> { + match *self { + ExternVal::Func(ref func) => Some(func), + _ => None, + } + } - /// Get underlying table reference if this `ExternVal` contains - /// a table, or `None` if it is some other kind. - pub fn as_table(&self) -> Option<&TableRef> { - match *self { - ExternVal::Table(ref table) => Some(table), - _ => None, - } - } + /// Get underlying table reference if this `ExternVal` contains + /// a table, or `None` if it is some other kind. + pub fn as_table(&self) -> Option<&TableRef> { + match *self { + ExternVal::Table(ref table) => Some(table), + _ => None, + } + } - /// Get underlying memory reference if this `ExternVal` contains - /// a memory, or `None` if it is some other kind. - pub fn as_memory(&self) -> Option<&MemoryRef> { - match *self { - ExternVal::Memory(ref memory) => Some(memory), - _ => None, - } - } + /// Get underlying memory reference if this `ExternVal` contains + /// a memory, or `None` if it is some other kind. + pub fn as_memory(&self) -> Option<&MemoryRef> { + match *self { + ExternVal::Memory(ref memory) => Some(memory), + _ => None, + } + } - /// Get underlying global variable reference if this `ExternVal` contains - /// a global, or `None` if it is some other kind. - pub fn as_global(&self) -> Option<&GlobalRef> { - match *self { - ExternVal::Global(ref global) => Some(global), - _ => None, - } - } + /// Get underlying global variable reference if this `ExternVal` contains + /// a global, or `None` if it is some other kind. + pub fn as_global(&self) -> Option<&GlobalRef> { + match *self { + ExternVal::Global(ref global) => Some(global), + _ => None, + } + } } /// A module instance is the runtime representation of a [module][`Module`]. @@ -155,490 +155,492 @@ impl ExternVal { /// [`invoke_export`]: #method.invoke_export #[derive(Debug)] pub struct ModuleInstance { - signatures: RefCell>>, - tables: RefCell>, - funcs: RefCell>, - memories: RefCell>, - globals: RefCell>, - exports: RefCell>, + signatures: RefCell>>, + tables: RefCell>, + funcs: RefCell>, + memories: RefCell>, + globals: RefCell>, + exports: RefCell>, } impl ModuleInstance { - fn default() -> Self { - ModuleInstance { - funcs: RefCell::new(Vec::new()), - signatures: RefCell::new(Vec::new()), - tables: RefCell::new(Vec::new()), - memories: RefCell::new(Vec::new()), - globals: RefCell::new(Vec::new()), - exports: RefCell::new(HashMap::new()), - } - } + fn default() -> Self { + ModuleInstance { + funcs: RefCell::new(Vec::new()), + signatures: RefCell::new(Vec::new()), + tables: RefCell::new(Vec::new()), + memories: RefCell::new(Vec::new()), + globals: RefCell::new(Vec::new()), + exports: RefCell::new(HashMap::new()), + } + } - pub(crate) fn memory_by_index(&self, idx: u32) -> Option { - self.memories.borrow_mut().get(idx as usize).cloned() - } + pub(crate) fn memory_by_index(&self, idx: u32) -> Option { + self.memories.borrow_mut().get(idx as usize).cloned() + } - pub(crate) fn table_by_index(&self, idx: u32) -> Option { - self.tables.borrow_mut().get(idx as usize).cloned() - } + pub(crate) fn table_by_index(&self, idx: u32) -> Option { + self.tables.borrow_mut().get(idx as usize).cloned() + } - pub(crate) fn global_by_index(&self, idx: u32) -> Option { - self.globals.borrow_mut().get(idx as usize).cloned() - } + pub(crate) fn global_by_index(&self, idx: u32) -> Option { + self.globals.borrow_mut().get(idx as usize).cloned() + } - pub(crate) fn func_by_index(&self, idx: u32) -> Option { - self.funcs.borrow().get(idx as usize).cloned() - } + pub(crate) fn func_by_index(&self, idx: u32) -> Option { + self.funcs.borrow().get(idx as usize).cloned() + } - pub(crate) fn signature_by_index(&self, idx: u32) -> Option> { - self.signatures.borrow().get(idx as usize).cloned() - } + pub(crate) fn signature_by_index(&self, idx: u32) -> Option> { + self.signatures.borrow().get(idx as usize).cloned() + } - fn push_func(&self, func: FuncRef) { - self.funcs.borrow_mut().push(func); - } + fn push_func(&self, func: FuncRef) { + self.funcs.borrow_mut().push(func); + } - fn push_signature(&self, signature: Rc) { - self.signatures.borrow_mut().push(signature) - } + fn push_signature(&self, signature: Rc) { + self.signatures.borrow_mut().push(signature) + } - fn push_memory(&self, memory: MemoryRef) { - self.memories.borrow_mut().push(memory) - } + fn push_memory(&self, memory: MemoryRef) { + self.memories.borrow_mut().push(memory) + } - fn push_table(&self, table: TableRef) { - self.tables.borrow_mut().push(table) - } + fn push_table(&self, table: TableRef) { + self.tables.borrow_mut().push(table) + } - fn push_global(&self, global: GlobalRef) { - self.globals.borrow_mut().push(global) - } + fn push_global(&self, global: GlobalRef) { + self.globals.borrow_mut().push(global) + } - fn insert_export>(&self, name: N, extern_val: ExternVal) { - self.exports.borrow_mut().insert(name.into(), extern_val); - } + fn insert_export>(&self, name: N, extern_val: ExternVal) { + self.exports.borrow_mut().insert(name.into(), extern_val); + } - fn alloc_module<'i, I: Iterator>( - loaded_module: &Module, - extern_vals: I, - ) -> Result { - let module = loaded_module.module(); - let instance = ModuleRef(Rc::new(ModuleInstance::default())); + fn alloc_module<'i, I: Iterator>( + loaded_module: &Module, + extern_vals: I, + ) -> Result { + let module = loaded_module.module(); + let instance = ModuleRef(Rc::new(ModuleInstance::default())); - for &Type::Function(ref ty) in module.type_section().map(|ts| ts.types()).unwrap_or(&[]) { - let signature = Rc::new(Signature::from_elements(ty)); - instance.push_signature(signature); - } + for &Type::Function(ref ty) in module.type_section().map(|ts| ts.types()).unwrap_or(&[]) { + let signature = Rc::new(Signature::from_elements(ty)); + instance.push_signature(signature); + } - { - let mut imports = module - .import_section() - .map(|is| is.entries()) - .unwrap_or(&[]) - .into_iter(); - let mut extern_vals = extern_vals; - loop { - // Iterate on imports and extern_vals in lockstep, a-la `Iterator:zip`. - // We can't use `Iterator::zip` since we want to check if lengths of both iterators are same and - // `Iterator::zip` just returns `None` if either of iterators return `None`. - let (import, extern_val) = match (imports.next(), extern_vals.next()) { - (Some(import), Some(extern_val)) => (import, extern_val), - (None, None) => break, - (Some(_), None) | (None, Some(_)) => { - return Err(Error::Instantiation( - "extern_vals length is not equal to import section entries".to_owned(), - )); - } - }; + { + let mut imports = module + .import_section() + .map(|is| is.entries()) + .unwrap_or(&[]) + .into_iter(); + let mut extern_vals = extern_vals; + loop { + // Iterate on imports and extern_vals in lockstep, a-la `Iterator:zip`. + // We can't use `Iterator::zip` since we want to check if lengths of both iterators are same and + // `Iterator::zip` just returns `None` if either of iterators return `None`. + let (import, extern_val) = match (imports.next(), extern_vals.next()) { + (Some(import), Some(extern_val)) => (import, extern_val), + (None, None) => break, + (Some(_), None) | (None, Some(_)) => { + return Err(Error::Instantiation( + "extern_vals length is not equal to import section entries".to_owned(), + )); + } + }; - match (import.external(), extern_val) { - (&External::Function(fn_type_idx), &ExternVal::Func(ref func)) => { - let expected_fn_type = instance.signature_by_index(fn_type_idx).expect( - "Due to validation function type should exists", - ); - let actual_fn_type = func.signature(); - if &*expected_fn_type != actual_fn_type { - return Err(Error::Instantiation(format!( + match (import.external(), extern_val) { + (&External::Function(fn_type_idx), &ExternVal::Func(ref func)) => { + let expected_fn_type = instance + .signature_by_index(fn_type_idx) + .expect("Due to validation function type should exists"); + let actual_fn_type = func.signature(); + if &*expected_fn_type != actual_fn_type { + return Err(Error::Instantiation(format!( "Expected function with type {:?}, but actual type is {:?} for entry {}", expected_fn_type, actual_fn_type, import.field(), ))); - } - instance.push_func(func.clone()) - } - (&External::Table(ref tt), &ExternVal::Table(ref table)) => { - match_limits(table.limits(), tt.limits())?; - instance.push_table(table.clone()); - } - (&External::Memory(ref mt), &ExternVal::Memory(ref memory)) => { - match_limits(memory.limits(), mt.limits())?; - instance.push_memory(memory.clone()); - } - (&External::Global(ref gl), &ExternVal::Global(ref global)) => { - if gl.content_type() != global.elements_value_type() { - return Err(Error::Instantiation(format!( - "Expect global with {:?} type, but provided global with {:?} type", - gl.content_type(), - global.value_type(), - ))); - } - instance.push_global(global.clone()); - } - (expected_import, actual_extern_val) => { - return Err(Error::Instantiation(format!( - "Expected {:?} type, but provided {:?} extern_val", - expected_import, - actual_extern_val - ))); - } - } - } - } + } + instance.push_func(func.clone()) + } + (&External::Table(ref tt), &ExternVal::Table(ref table)) => { + match_limits(table.limits(), tt.limits())?; + instance.push_table(table.clone()); + } + (&External::Memory(ref mt), &ExternVal::Memory(ref memory)) => { + match_limits(memory.limits(), mt.limits())?; + instance.push_memory(memory.clone()); + } + (&External::Global(ref gl), &ExternVal::Global(ref global)) => { + if gl.content_type() != global.elements_value_type() { + return Err(Error::Instantiation(format!( + "Expect global with {:?} type, but provided global with {:?} type", + gl.content_type(), + global.value_type(), + ))); + } + instance.push_global(global.clone()); + } + (expected_import, actual_extern_val) => { + return Err(Error::Instantiation(format!( + "Expected {:?} type, but provided {:?} extern_val", + expected_import, actual_extern_val + ))); + } + } + } + } - let code = loaded_module.code(); - { - let funcs = module.function_section().map(|fs| fs.entries()).unwrap_or( - &[], - ); - let bodies = module.code_section().map(|cs| cs.bodies()).unwrap_or(&[]); - debug_assert!( - funcs.len() == bodies.len(), - "Due to validation func and body counts must match" - ); + let code = loaded_module.code(); + { + let funcs = module + .function_section() + .map(|fs| fs.entries()) + .unwrap_or(&[]); + let bodies = module.code_section().map(|cs| cs.bodies()).unwrap_or(&[]); + debug_assert!( + funcs.len() == bodies.len(), + "Due to validation func and body counts must match" + ); - for (index, (ty, body)) in - Iterator::zip(funcs.into_iter(), bodies.into_iter()).enumerate() - { - let signature = instance.signature_by_index(ty.type_ref()).expect( - "Due to validation type should exists", - ); - let code = code.get(index).expect( + for (index, (ty, body)) in + Iterator::zip(funcs.into_iter(), bodies.into_iter()).enumerate() + { + let signature = instance + .signature_by_index(ty.type_ref()) + .expect("Due to validation type should exists"); + let code = code.get(index).expect( "At func validation time labels are collected; Collected labels are added by index; qed", ).clone(); - let func_body = FuncBody { - locals: body.locals().to_vec(), - code: code, - }; - let func_instance = - FuncInstance::alloc_internal(Rc::downgrade(&instance.0), signature, func_body); - instance.push_func(func_instance); - } - } + let func_body = FuncBody { + locals: body.locals().to_vec(), + code: code, + }; + let func_instance = + FuncInstance::alloc_internal(Rc::downgrade(&instance.0), signature, func_body); + instance.push_func(func_instance); + } + } - for table_type in module.table_section().map(|ts| ts.entries()).unwrap_or(&[]) { - let table = TableInstance::alloc( - table_type.limits().initial(), - table_type.limits().maximum(), - )?; - instance.push_table(table); - } + for table_type in module.table_section().map(|ts| ts.entries()).unwrap_or(&[]) { + let table = + TableInstance::alloc(table_type.limits().initial(), table_type.limits().maximum())?; + instance.push_table(table); + } - for memory_type in module.memory_section().map(|ms| ms.entries()).unwrap_or( - &[], - ) - { - let initial: Pages = Pages(memory_type.limits().initial() as usize); - let maximum: Option = memory_type.limits().maximum().map(|m| Pages(m as usize)); + for memory_type in module + .memory_section() + .map(|ms| ms.entries()) + .unwrap_or(&[]) + { + let initial: Pages = Pages(memory_type.limits().initial() as usize); + let maximum: Option = memory_type.limits().maximum().map(|m| Pages(m as usize)); - let memory = MemoryInstance::alloc(initial, maximum) - .expect("Due to validation `initial` and `maximum` should be valid"); - instance.push_memory(memory); - } + let memory = MemoryInstance::alloc(initial, maximum) + .expect("Due to validation `initial` and `maximum` should be valid"); + instance.push_memory(memory); + } - for global_entry in module.global_section().map(|gs| gs.entries()).unwrap_or( - &[], - ) - { - let init_val = eval_init_expr(global_entry.init_expr(), &*instance); - let global = GlobalInstance::alloc( - init_val, - global_entry.global_type().is_mutable(), - ); - instance.push_global(global); - } + for global_entry in module + .global_section() + .map(|gs| gs.entries()) + .unwrap_or(&[]) + { + let init_val = eval_init_expr(global_entry.init_expr(), &*instance); + let global = GlobalInstance::alloc(init_val, global_entry.global_type().is_mutable()); + instance.push_global(global); + } - for export in module.export_section().map(|es| es.entries()).unwrap_or( - &[], - ) - { - let field = export.field(); - let extern_val: ExternVal = match *export.internal() { - Internal::Function(idx) => { - let func = instance.func_by_index(idx).expect( - "Due to validation func should exists", - ); - ExternVal::Func(func) - } - Internal::Global(idx) => { - let global = instance.global_by_index(idx).expect( - "Due to validation global should exists", - ); - ExternVal::Global(global) - } - Internal::Memory(idx) => { - let memory = instance.memory_by_index(idx).expect( - "Due to validation memory should exists", - ); - ExternVal::Memory(memory) - } - Internal::Table(idx) => { - let table = instance.table_by_index(idx).expect( - "Due to validation table should exists", - ); - ExternVal::Table(table) - } - }; - instance.insert_export(field, extern_val); - } + for export in module + .export_section() + .map(|es| es.entries()) + .unwrap_or(&[]) + { + let field = export.field(); + let extern_val: ExternVal = match *export.internal() { + Internal::Function(idx) => { + let func = instance + .func_by_index(idx) + .expect("Due to validation func should exists"); + ExternVal::Func(func) + } + Internal::Global(idx) => { + let global = instance + .global_by_index(idx) + .expect("Due to validation global should exists"); + ExternVal::Global(global) + } + Internal::Memory(idx) => { + let memory = instance + .memory_by_index(idx) + .expect("Due to validation memory should exists"); + ExternVal::Memory(memory) + } + Internal::Table(idx) => { + let table = instance + .table_by_index(idx) + .expect("Due to validation table should exists"); + ExternVal::Table(table) + } + }; + instance.insert_export(field, extern_val); + } - Ok(instance) - } + Ok(instance) + } - /// Instantiate a module with given [external values][ExternVal] as imports. - /// - /// See [new] for details. - /// - /// [new]: #method.new - /// [ExternVal]: https://webassembly.github.io/spec/core/exec/runtime.html#syntax-externval - pub fn with_externvals<'a, 'i, I: Iterator>( - loaded_module: &'a Module, - extern_vals: I, - ) -> Result, Error> { - let module = loaded_module.module(); + /// Instantiate a module with given [external values][ExternVal] as imports. + /// + /// See [new] for details. + /// + /// [new]: #method.new + /// [ExternVal]: https://webassembly.github.io/spec/core/exec/runtime.html#syntax-externval + pub fn with_externvals<'a, 'i, I: Iterator>( + loaded_module: &'a Module, + extern_vals: I, + ) -> Result, Error> { + let module = loaded_module.module(); - let module_ref = ModuleInstance::alloc_module(loaded_module, extern_vals)?; + let module_ref = ModuleInstance::alloc_module(loaded_module, extern_vals)?; - for element_segment in module.elements_section().map(|es| es.entries()).unwrap_or( - &[], - ) - { - let offset_val = match eval_init_expr(element_segment.offset(), &module_ref) { - RuntimeValue::I32(v) => v as u32, - _ => panic!("Due to validation elem segment offset should evaluate to i32"), - }; + for element_segment in module + .elements_section() + .map(|es| es.entries()) + .unwrap_or(&[]) + { + let offset_val = match eval_init_expr(element_segment.offset(), &module_ref) { + RuntimeValue::I32(v) => v as u32, + _ => panic!("Due to validation elem segment offset should evaluate to i32"), + }; - let table_inst = module_ref.table_by_index(DEFAULT_TABLE_INDEX).expect( - "Due to validation default table should exists", - ); + let table_inst = module_ref + .table_by_index(DEFAULT_TABLE_INDEX) + .expect("Due to validation default table should exists"); - // This check is not only for bailing out early, but also to check the case when - // segment consist of 0 members. - if offset_val as u64 + element_segment.members().len() as u64 > table_inst.current_size() as u64 { - return Err( - Error::Instantiation("elements segment does not fit".to_string()) - ); - } + // This check is not only for bailing out early, but also to check the case when + // segment consist of 0 members. + if offset_val as u64 + element_segment.members().len() as u64 + > table_inst.current_size() as u64 + { + return Err(Error::Instantiation( + "elements segment does not fit".to_string(), + )); + } - for (j, func_idx) in element_segment.members().into_iter().enumerate() { - let func = module_ref.func_by_index(*func_idx).expect( - "Due to validation funcs from element segments should exists", - ); + for (j, func_idx) in element_segment.members().into_iter().enumerate() { + let func = module_ref + .func_by_index(*func_idx) + .expect("Due to validation funcs from element segments should exists"); - table_inst.set(offset_val + j as u32, Some(func))?; - } - } + table_inst.set(offset_val + j as u32, Some(func))?; + } + } - for data_segment in module.data_section().map(|ds| ds.entries()).unwrap_or(&[]) { - let offset_val = match eval_init_expr(data_segment.offset(), &module_ref) { - RuntimeValue::I32(v) => v as u32, - _ => panic!("Due to validation data segment offset should evaluate to i32"), - }; + for data_segment in module.data_section().map(|ds| ds.entries()).unwrap_or(&[]) { + let offset_val = match eval_init_expr(data_segment.offset(), &module_ref) { + RuntimeValue::I32(v) => v as u32, + _ => panic!("Due to validation data segment offset should evaluate to i32"), + }; - let memory_inst = module_ref.memory_by_index(DEFAULT_MEMORY_INDEX).expect( - "Due to validation default memory should exists", - ); - memory_inst.set(offset_val, data_segment.value())?; - } + let memory_inst = module_ref + .memory_by_index(DEFAULT_MEMORY_INDEX) + .expect("Due to validation default memory should exists"); + memory_inst.set(offset_val, data_segment.value())?; + } - Ok(NotStartedModuleRef { - loaded_module, - instance: module_ref, - }) - } + Ok(NotStartedModuleRef { + loaded_module, + instance: module_ref, + }) + } - /// Instantiate a [module][`Module`]. - /// - /// Note that in case of successful instantiation this function returns a reference to - /// a module which `start` function is not called. - /// In order to complete instantiatiation `start` function must be called. However, there are - /// situations where you might need to do additional setup before calling `start` function. - /// For such sitations this separation might be useful. - /// - /// See [`NotStartedModuleRef`] for details. - /// - /// # Errors - /// - /// Returns `Err` if the module cannot be instantiated. - /// - /// This can happen if one of the imports can't - /// be satisfied (e.g module isn't registered in `imports` [resolver][`ImportResolver`]) or - /// there is a mismatch between requested import and provided (e.g. module requested memory with no - /// maximum size limit, however, was provided memory with the maximum size limit). - /// - /// # Examples - /// - /// ```rust - /// use wasmi::{ModuleInstance, ImportsBuilder, NopExternals}; - /// # fn func() -> Result<(), ::wasmi::Error> { - /// # let module = wasmi::Module::from_buffer(&[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]).unwrap(); - /// - /// // ModuleInstance::new returns instance which `start` function isn't called. - /// let not_started = ModuleInstance::new( - /// &module, - /// &ImportsBuilder::default() - /// )?; - /// // Call `start` function if any. - /// let instance = not_started.run_start(&mut NopExternals)?; - /// - /// # Ok(()) - /// # } - /// ``` - /// - /// If you sure that the module doesn't have `start` function you can use [`assert_no_start`] to get - /// instantiated module without calling `start` function. - /// - /// ```rust - /// use wasmi::{ModuleInstance, ImportsBuilder, NopExternals}; - /// # fn func() -> Result<(), ::wasmi::Error> { - /// # let module = wasmi::Module::from_buffer(&[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]).unwrap(); - /// - /// // This will panic if the module actually contain `start` function. - /// let not_started = ModuleInstance::new( - /// &module, - /// &ImportsBuilder::default() - /// )?.assert_no_start(); - /// - /// # Ok(()) - /// # } - /// ``` - /// - /// [`Module`]: struct.Module.html - /// [`NotStartedModuleRef`]: struct.NotStartedModuleRef.html - /// [`ImportResolver`]: trait.ImportResolver.html - /// [`assert_no_start`]: struct.NotStartedModuleRef.html#method.assert_no_start - pub fn new<'m, I: ImportResolver>( - loaded_module: &'m Module, - imports: &I, - ) -> Result, Error> { - let module = loaded_module.module(); + /// Instantiate a [module][`Module`]. + /// + /// Note that in case of successful instantiation this function returns a reference to + /// a module which `start` function is not called. + /// In order to complete instantiatiation `start` function must be called. However, there are + /// situations where you might need to do additional setup before calling `start` function. + /// For such sitations this separation might be useful. + /// + /// See [`NotStartedModuleRef`] for details. + /// + /// # Errors + /// + /// Returns `Err` if the module cannot be instantiated. + /// + /// This can happen if one of the imports can't + /// be satisfied (e.g module isn't registered in `imports` [resolver][`ImportResolver`]) or + /// there is a mismatch between requested import and provided (e.g. module requested memory with no + /// maximum size limit, however, was provided memory with the maximum size limit). + /// + /// # Examples + /// + /// ```rust + /// use wasmi::{ModuleInstance, ImportsBuilder, NopExternals}; + /// # fn func() -> Result<(), ::wasmi::Error> { + /// # let module = wasmi::Module::from_buffer(&[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]).unwrap(); + /// + /// // ModuleInstance::new returns instance which `start` function isn't called. + /// let not_started = ModuleInstance::new( + /// &module, + /// &ImportsBuilder::default() + /// )?; + /// // Call `start` function if any. + /// let instance = not_started.run_start(&mut NopExternals)?; + /// + /// # Ok(()) + /// # } + /// ``` + /// + /// If you sure that the module doesn't have `start` function you can use [`assert_no_start`] to get + /// instantiated module without calling `start` function. + /// + /// ```rust + /// use wasmi::{ModuleInstance, ImportsBuilder, NopExternals}; + /// # fn func() -> Result<(), ::wasmi::Error> { + /// # let module = wasmi::Module::from_buffer(&[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]).unwrap(); + /// + /// // This will panic if the module actually contain `start` function. + /// let not_started = ModuleInstance::new( + /// &module, + /// &ImportsBuilder::default() + /// )?.assert_no_start(); + /// + /// # Ok(()) + /// # } + /// ``` + /// + /// [`Module`]: struct.Module.html + /// [`NotStartedModuleRef`]: struct.NotStartedModuleRef.html + /// [`ImportResolver`]: trait.ImportResolver.html + /// [`assert_no_start`]: struct.NotStartedModuleRef.html#method.assert_no_start + pub fn new<'m, I: ImportResolver>( + loaded_module: &'m Module, + imports: &I, + ) -> Result, Error> { + let module = loaded_module.module(); - let mut extern_vals = Vec::new(); - for import_entry in module.import_section().map(|s| s.entries()).unwrap_or(&[]) { - let module_name = import_entry.module(); - let field_name = import_entry.field(); - let extern_val = match *import_entry.external() { - External::Function(fn_ty_idx) => { - let types = module.type_section().map(|s| s.types()).unwrap_or(&[]); - let &Type::Function(ref func_type) = types - .get(fn_ty_idx as usize) - .expect("Due to validation functions should have valid types"); - let signature = Signature::from_elements(func_type); - let func = imports.resolve_func(module_name, field_name, &signature)?; - ExternVal::Func(func) - } - External::Table(ref table_type) => { - let table_descriptor = TableDescriptor::from_elements(table_type); - let table = imports.resolve_table(module_name, field_name, &table_descriptor)?; - ExternVal::Table(table) - } - External::Memory(ref memory_type) => { - let memory_descriptor = MemoryDescriptor::from_elements(memory_type); - let memory = imports.resolve_memory(module_name, field_name, &memory_descriptor)?; - ExternVal::Memory(memory) - } - External::Global(ref global_type) => { - let global_descriptor = GlobalDescriptor::from_elements(global_type); - let global = imports.resolve_global(module_name, field_name, &global_descriptor)?; - ExternVal::Global(global) - } - }; - extern_vals.push(extern_val); - } + let mut extern_vals = Vec::new(); + for import_entry in module.import_section().map(|s| s.entries()).unwrap_or(&[]) { + let module_name = import_entry.module(); + let field_name = import_entry.field(); + let extern_val = match *import_entry.external() { + External::Function(fn_ty_idx) => { + let types = module.type_section().map(|s| s.types()).unwrap_or(&[]); + let &Type::Function(ref func_type) = types + .get(fn_ty_idx as usize) + .expect("Due to validation functions should have valid types"); + let signature = Signature::from_elements(func_type); + let func = imports.resolve_func(module_name, field_name, &signature)?; + ExternVal::Func(func) + } + External::Table(ref table_type) => { + let table_descriptor = TableDescriptor::from_elements(table_type); + let table = + imports.resolve_table(module_name, field_name, &table_descriptor)?; + ExternVal::Table(table) + } + External::Memory(ref memory_type) => { + let memory_descriptor = MemoryDescriptor::from_elements(memory_type); + let memory = + imports.resolve_memory(module_name, field_name, &memory_descriptor)?; + ExternVal::Memory(memory) + } + External::Global(ref global_type) => { + let global_descriptor = GlobalDescriptor::from_elements(global_type); + let global = + imports.resolve_global(module_name, field_name, &global_descriptor)?; + ExternVal::Global(global) + } + }; + extern_vals.push(extern_val); + } - Self::with_externvals(loaded_module, extern_vals.iter()) - } + Self::with_externvals(loaded_module, extern_vals.iter()) + } - /// Invoke exported function by a name. - /// - /// This function finds exported function by a name, and calls it with provided arguments and - /// external state. - /// - /// # Errors - /// - /// Returns `Err` if: - /// - /// - there are no export with a given name or this export is not a function, - /// - given arguments doesn't match to function signature, - /// - trap occurred at the execution time, - /// - /// # Examples - /// - /// Invoke a function that takes two numbers and returns sum of them. - /// - /// ```rust - /// # extern crate wasmi; - /// # extern crate wabt; - /// # use wasmi::{ModuleInstance, ImportsBuilder, NopExternals, RuntimeValue}; - /// # fn main() { - /// # let wasm_binary: Vec = wabt::wat2wasm( - /// # r#" - /// # (module - /// # (func (export "add") (param i32 i32) (result i32) - /// # get_local 0 - /// # get_local 1 - /// # i32.add - /// # ) - /// # ) - /// # "#, - /// # ).expect("failed to parse wat"); - /// # let module = wasmi::Module::from_buffer(&wasm_binary).expect("failed to load wasm"); - /// # let instance = ModuleInstance::new( - /// # &module, - /// # &ImportsBuilder::default() - /// # ).expect("failed to instantiate wasm module").assert_no_start(); - /// assert_eq!( - /// instance.invoke_export( - /// "add", - /// &[RuntimeValue::I32(5), RuntimeValue::I32(3)], - /// &mut NopExternals, - /// ).expect("failed to execute export"), - /// Some(RuntimeValue::I32(8)), - /// ); - /// # } - /// ``` - pub fn invoke_export( - &self, - func_name: &str, - args: &[RuntimeValue], - externals: &mut E, - ) -> Result, Error> { - let extern_val = self.export_by_name(func_name).ok_or_else(|| { - Error::Function(format!("Module doesn't have export {}", func_name)) - })?; + /// Invoke exported function by a name. + /// + /// This function finds exported function by a name, and calls it with provided arguments and + /// external state. + /// + /// # Errors + /// + /// Returns `Err` if: + /// + /// - there are no export with a given name or this export is not a function, + /// - given arguments doesn't match to function signature, + /// - trap occurred at the execution time, + /// + /// # Examples + /// + /// Invoke a function that takes two numbers and returns sum of them. + /// + /// ```rust + /// # extern crate wasmi; + /// # extern crate wabt; + /// # use wasmi::{ModuleInstance, ImportsBuilder, NopExternals, RuntimeValue}; + /// # fn main() { + /// # let wasm_binary: Vec = wabt::wat2wasm( + /// # r#" + /// # (module + /// # (func (export "add") (param i32 i32) (result i32) + /// # get_local 0 + /// # get_local 1 + /// # i32.add + /// # ) + /// # ) + /// # "#, + /// # ).expect("failed to parse wat"); + /// # let module = wasmi::Module::from_buffer(&wasm_binary).expect("failed to load wasm"); + /// # let instance = ModuleInstance::new( + /// # &module, + /// # &ImportsBuilder::default() + /// # ).expect("failed to instantiate wasm module").assert_no_start(); + /// assert_eq!( + /// instance.invoke_export( + /// "add", + /// &[RuntimeValue::I32(5), RuntimeValue::I32(3)], + /// &mut NopExternals, + /// ).expect("failed to execute export"), + /// Some(RuntimeValue::I32(8)), + /// ); + /// # } + /// ``` + pub fn invoke_export( + &self, + func_name: &str, + args: &[RuntimeValue], + externals: &mut E, + ) -> Result, Error> { + let extern_val = self + .export_by_name(func_name) + .ok_or_else(|| Error::Function(format!("Module doesn't have export {}", func_name)))?; - let func_instance = match extern_val { - ExternVal::Func(func_instance) => func_instance, - unexpected => { - return Err(Error::Function(format!( - "Export {} is not a function, but {:?}", - func_name, - unexpected - ))); - } - }; + let func_instance = match extern_val { + ExternVal::Func(func_instance) => func_instance, + unexpected => { + return Err(Error::Function(format!( + "Export {} is not a function, but {:?}", + func_name, unexpected + ))); + } + }; - FuncInstance::invoke(&func_instance, args, externals) - .map_err(|t| Error::Trap(t)) - } + FuncInstance::invoke(&func_instance, args, externals).map_err(|t| Error::Trap(t)) + } - /// Find export by a name. - /// - /// Returns `None` if there is no export with such name. - pub fn export_by_name(&self, name: &str) -> Option { - self.exports.borrow().get(name).cloned() - } + /// Find export by a name. + /// + /// Returns `None` if there is no export with such name. + pub fn export_by_name(&self, name: &str) -> Option { + self.exports.borrow().get(name).cloned() + } } /// Mostly instantiated [`ModuleRef`]. @@ -660,180 +662,178 @@ impl ModuleInstance { /// [`assert_no_start`]: #method.assert_no_start /// [`not_started_instance`]: #method.not_started_instance pub struct NotStartedModuleRef<'a> { - loaded_module: &'a Module, - instance: ModuleRef, + loaded_module: &'a Module, + instance: ModuleRef, } impl<'a> NotStartedModuleRef<'a> { - /// Returns not fully initialized instance. - /// - /// To fully initialize the instance you need to call either [`run_start`] or - /// [`assert_no_start`]. See struct documentation for details. - /// - /// [`NotStartedModuleRef`]: struct.NotStartedModuleRef.html - /// [`ModuleRef`]: struct.ModuleRef.html - /// [`run_start`]: #method.run_start - /// [`assert_no_start`]: #method.assert_no_start - pub fn not_started_instance(&self) -> &ModuleRef { - &self.instance - } + /// Returns not fully initialized instance. + /// + /// To fully initialize the instance you need to call either [`run_start`] or + /// [`assert_no_start`]. See struct documentation for details. + /// + /// [`NotStartedModuleRef`]: struct.NotStartedModuleRef.html + /// [`ModuleRef`]: struct.ModuleRef.html + /// [`run_start`]: #method.run_start + /// [`assert_no_start`]: #method.assert_no_start + pub fn not_started_instance(&self) -> &ModuleRef { + &self.instance + } - /// Executes `start` function (if any) and returns fully instantiated module. - /// - /// # Errors - /// - /// Returns `Err` if start function traps. - 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", - ); - FuncInstance::invoke(&start_func, &[], state)?; - } - Ok(self.instance) - } + /// Executes `start` function (if any) and returns fully instantiated module. + /// + /// # Errors + /// + /// Returns `Err` if start function traps. + 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"); + FuncInstance::invoke(&start_func, &[], state)?; + } + Ok(self.instance) + } - /// Returns fully instantiated module without running `start` function. - /// - /// # Panics - /// - /// This function panics if original module contains `start` function. - pub fn assert_no_start(self) -> ModuleRef { - if self.loaded_module.module().start_section().is_some() { - panic!("assert_no_start called on module with `start` function"); - } - self.instance - } + /// Returns fully instantiated module without running `start` function. + /// + /// # Panics + /// + /// This function panics if original module contains `start` function. + pub fn assert_no_start(self) -> ModuleRef { + if self.loaded_module.module().start_section().is_some() { + panic!("assert_no_start called on module with `start` function"); + } + self.instance + } } fn eval_init_expr(init_expr: &InitExpr, module: &ModuleInstance) -> RuntimeValue { - let code = init_expr.code(); - debug_assert!( - code.len() == 2, - "Due to validation `code`.len() should be 2" - ); - match code[0] { - Instruction::I32Const(v) => v.into(), - Instruction::I64Const(v) => v.into(), - Instruction::F32Const(v) => RuntimeValue::decode_f32(v), - Instruction::F64Const(v) => RuntimeValue::decode_f64(v), - Instruction::GetGlobal(idx) => { - let global = module.global_by_index(idx).expect( - "Due to validation global should exists in module", - ); - global.get() - } - _ => panic!("Due to validation init should be a const expr"), - } + let code = init_expr.code(); + debug_assert!( + code.len() == 2, + "Due to validation `code`.len() should be 2" + ); + match code[0] { + Instruction::I32Const(v) => v.into(), + Instruction::I64Const(v) => v.into(), + Instruction::F32Const(v) => RuntimeValue::decode_f32(v), + Instruction::F64Const(v) => RuntimeValue::decode_f64(v), + Instruction::GetGlobal(idx) => { + let global = module + .global_by_index(idx) + .expect("Due to validation global should exists in module"); + global.get() + } + _ => panic!("Due to validation init should be a const expr"), + } } fn match_limits(l1: &ResizableLimits, l2: &ResizableLimits) -> Result<(), Error> { - if l1.initial() < l2.initial() { - return Err(Error::Instantiation(format!( - "trying to import with limits l1.initial={} and l2.initial={}", - l1.initial(), - l2.initial() - ))); - } + if l1.initial() < l2.initial() { + return Err(Error::Instantiation(format!( + "trying to import with limits l1.initial={} and l2.initial={}", + l1.initial(), + l2.initial() + ))); + } - match (l1.maximum(), l2.maximum()) { - (_, None) => (), - (Some(m1), Some(m2)) if m1 <= m2 => (), - _ => { - return Err(Error::Instantiation(format!( - "trying to import with limits l1.max={:?} and l2.max={:?}", - l1.maximum(), - l2.maximum() - ))) - } - } + match (l1.maximum(), l2.maximum()) { + (_, None) => (), + (Some(m1), Some(m2)) if m1 <= m2 => (), + _ => { + return Err(Error::Instantiation(format!( + "trying to import with limits l1.max={:?} and l2.max={:?}", + l1.maximum(), + l2.maximum() + ))); + } + } - Ok(()) + Ok(()) } pub fn check_limits(limits: &ResizableLimits) -> Result<(), Error> { - if let Some(maximum) = limits.maximum() { - if maximum < limits.initial() { - return Err(Error::Instantiation(format!( - "maximum limit {} is less than minimum {}", - maximum, - limits.initial() - ))); - } - } + if let Some(maximum) = limits.maximum() { + if maximum < limits.initial() { + return Err(Error::Instantiation(format!( + "maximum limit {} is less than minimum {}", + maximum, + limits.initial() + ))); + } + } - Ok(()) + Ok(()) } - #[cfg(test)] mod tests { - use imports::ImportsBuilder; - use func::FuncInstance; - use types::{Signature, ValueType}; - use super::{ModuleInstance, ExternVal}; - use tests::parse_wat; + use super::{ExternVal, ModuleInstance}; + use func::FuncInstance; + use imports::ImportsBuilder; + use tests::parse_wat; + use types::{Signature, ValueType}; - #[should_panic] - #[test] - fn assert_no_start_panics_on_module_with_start() { - let module_with_start = parse_wat( - r#" + #[should_panic] + #[test] + fn assert_no_start_panics_on_module_with_start() { + let module_with_start = parse_wat( + r#" (module (func $f) (start $f)) - "# - ); - ModuleInstance::new( - &module_with_start, - &ImportsBuilder::default() - ).unwrap().assert_no_start(); - } + "#, + ); + ModuleInstance::new(&module_with_start, &ImportsBuilder::default()) + .unwrap() + .assert_no_start(); + } - #[test] - fn imports_provided_by_externvals() { - let module_with_single_import = parse_wat( - r#" + #[test] + fn imports_provided_by_externvals() { + let module_with_single_import = parse_wat( + r#" (module (import "foo" "bar" (func)) ) "#, - ); + ); - assert!( - ModuleInstance::with_externvals( - &module_with_single_import, - [ - ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 0),) - ].iter(), - ).is_ok() - ); + assert!(ModuleInstance::with_externvals( + &module_with_single_import, + [ExternVal::Func(FuncInstance::alloc_host( + Signature::new(&[][..], None), + 0 + ),)] + .iter(), + ) + .is_ok()); - // externval vector is longer than import count. - assert!( - ModuleInstance::with_externvals( - &module_with_single_import, - [ - ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 0)), - ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 1)), - ].iter(), - ).is_err() - ); + // externval vector is longer than import count. + assert!(ModuleInstance::with_externvals( + &module_with_single_import, + [ + ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 0)), + ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 1)), + ] + .iter(), + ) + .is_err()); - // externval vector is shorter than import count. - assert!(ModuleInstance::with_externvals(&module_with_single_import, [].iter(),).is_err()); + // externval vector is shorter than import count. + assert!(ModuleInstance::with_externvals(&module_with_single_import, [].iter(),).is_err()); - // externval vector has an unexpected type. - assert!( - ModuleInstance::with_externvals( - &module_with_single_import, - [ - ExternVal::Func(FuncInstance::alloc_host( - Signature::new(&[][..], Some(ValueType::I32)), - 0 - ),) - ].iter(), - ).is_err() - ); - } + // externval vector has an unexpected type. + assert!(ModuleInstance::with_externvals( + &module_with_single_import, + [ExternVal::Func(FuncInstance::alloc_host( + Signature::new(&[][..], Some(ValueType::I32)), + 0 + ),)] + .iter(), + ) + .is_err()); + } } diff --git a/src/nan_preserving_float.rs b/src/nan_preserving_float.rs index 6579662..1f12089 100644 --- a/src/nan_preserving_float.rs +++ b/src/nan_preserving_float.rs @@ -3,8 +3,8 @@ #[cfg(not(feature = "std"))] use libm::{F32Ext, F64Ext}; -use core::ops::{Add, Div, Mul, Neg, Sub, Rem}; use core::cmp::{Ordering, PartialEq, PartialOrd}; +use core::ops::{Add, Div, Mul, Neg, Rem, Sub}; macro_rules! impl_binop { ($for:ident, $is:ident, $op:ident, $func_name:ident) => { @@ -13,19 +13,22 @@ macro_rules! impl_binop { fn $func_name(self, other: T) -> Self { $for( - $op::$func_name( - $is::from_bits(self.0), - $is::from_bits(other.into().0) - ).to_bits() + $op::$func_name($is::from_bits(self.0), $is::from_bits(other.into().0)) + .to_bits(), ) } } - } + }; } macro_rules! float { ($for:ident, $rep:ident, $is:ident) => { - float!($for, $rep, $is, 1 << (::core::mem::size_of::<$is>() * 8 - 1)); + float!( + $for, + $rep, + $is, + 1 << (::core::mem::size_of::<$is>() * 8 - 1) + ); }; ($for:ident, $rep:ident, $is:ident, $sign_bit:expr) => { #[derive(Copy, Clone)] @@ -112,7 +115,7 @@ macro_rules! float { $is::from(*self).fmt(f) } } - } + }; } float!(F32, u32, f32); @@ -150,9 +153,9 @@ mod tests { use super::{F32, F64}; - use core::ops::{Add, Div, Mul, Neg, Sub}; use core::fmt::Debug; use core::iter; + use core::ops::{Add, Div, Mul, Neg, Sub}; fn test_ops(iter: I) where diff --git a/src/runner.rs b/src/runner.rs index fcdd765..ed7bfc7 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -13,8 +13,8 @@ use module::ModuleRef; use nan_preserving_float::{F32, F64}; use parity_wasm::elements::Local; use value::{ - ArithmeticOps, ExtendInto, Float, Integer, LittleEndianConvert, RuntimeValue, TransmuteInto, - TryTruncateInto, WrapInto, + ArithmeticOps, ExtendInto, Float, Integer, LittleEndianConvert, RuntimeValue, TransmuteInto, + TryTruncateInto, WrapInto, }; use {Signature, Trap, TrapKind, ValueType}; @@ -39,21 +39,21 @@ pub const DEFAULT_CALL_STACK_LIMIT: usize = 64 * 1024; struct RuntimeValueInternal(pub u64); impl RuntimeValueInternal { - pub fn with_type(self, ty: ValueType) -> RuntimeValue { - match ty { - ValueType::I32 => RuntimeValue::I32(<_>::from_runtime_value_internal(self)), - ValueType::I64 => RuntimeValue::I64(<_>::from_runtime_value_internal(self)), - ValueType::F32 => RuntimeValue::F32(<_>::from_runtime_value_internal(self)), - ValueType::F64 => RuntimeValue::F64(<_>::from_runtime_value_internal(self)), - } - } + pub fn with_type(self, ty: ValueType) -> RuntimeValue { + match ty { + ValueType::I32 => RuntimeValue::I32(<_>::from_runtime_value_internal(self)), + ValueType::I64 => RuntimeValue::I64(<_>::from_runtime_value_internal(self)), + ValueType::F32 => RuntimeValue::F32(<_>::from_runtime_value_internal(self)), + ValueType::F64 => RuntimeValue::F64(<_>::from_runtime_value_internal(self)), + } + } } trait FromRuntimeValueInternal where - Self: Sized, + Self: Sized, { - fn from_runtime_value_internal(val: RuntimeValueInternal) -> Self; + fn from_runtime_value_internal(val: RuntimeValueInternal) -> Self; } macro_rules! impl_from_runtime_value_internal { @@ -100,1356 +100,1357 @@ impl_from_runtime_value_internal!(i8, u8, i16, u16, i32, u32, i64, u64); impl_from_runtime_value_internal_float!(f32, f64, F32, F64); impl From for RuntimeValueInternal { - fn from(other: bool) -> Self { - (if other { 1 } else { 0 }).into() - } + fn from(other: bool) -> Self { + (if other { 1 } else { 0 }).into() + } } impl FromRuntimeValueInternal for bool { - fn from_runtime_value_internal(RuntimeValueInternal(val): RuntimeValueInternal) -> Self { - val != 0 - } + fn from_runtime_value_internal(RuntimeValueInternal(val): RuntimeValueInternal) -> Self { + val != 0 + } } impl From for RuntimeValueInternal { - fn from(other: RuntimeValue) -> Self { - match other { - RuntimeValue::I32(val) => val.into(), - RuntimeValue::I64(val) => val.into(), - RuntimeValue::F32(val) => val.into(), - RuntimeValue::F64(val) => val.into(), - } - } + fn from(other: RuntimeValue) -> Self { + match other { + RuntimeValue::I32(val) => val.into(), + RuntimeValue::I64(val) => val.into(), + RuntimeValue::F32(val) => val.into(), + RuntimeValue::F64(val) => val.into(), + } + } } /// Interpreter action to execute after executing instruction. pub enum InstructionOutcome { - /// Continue with next instruction. - RunNextInstruction, - /// Branch to an instruction at the given position. - Branch(isa::Target), - /// Execute function call. - ExecuteCall(FuncRef), - /// Return from current function block. - Return(isa::DropKeep), + /// Continue with next instruction. + RunNextInstruction, + /// Branch to an instruction at the given position. + Branch(isa::Target), + /// Execute function call. + ExecuteCall(FuncRef), + /// Return from current function block. + Return(isa::DropKeep), } #[derive(PartialEq, Eq)] /// Function execution state, related to pause and resume. pub enum InterpreterState { - /// The interpreter has been created, but has not been executed. - Initialized, - /// The interpreter has started execution, and cannot be called again if it exits normally, or no Host traps happened. - Started, - /// The interpreter has been executed, and returned a Host trap. It can resume execution by providing back a return - /// value. - Resumable(Option), + /// The interpreter has been created, but has not been executed. + Initialized, + /// The interpreter has started execution, and cannot be called again if it exits normally, or no Host traps happened. + Started, + /// The interpreter has been executed, and returned a Host trap. It can resume execution by providing back a return + /// value. + Resumable(Option), } impl InterpreterState { - pub fn is_resumable(&self) -> bool { - match self { - &InterpreterState::Resumable(_) => true, - _ => false, - } - } + pub fn is_resumable(&self) -> bool { + match self { + &InterpreterState::Resumable(_) => true, + _ => false, + } + } } /// Function run result. enum RunResult { - /// Function has returned. - Return, - /// Function is calling other function. - NestedCall(FuncRef), + /// Function has returned. + Return, + /// Function is calling other function. + NestedCall(FuncRef), } /// Function interpreter. pub struct Interpreter { - value_stack: ValueStack, - call_stack: Vec, - return_type: Option, - state: InterpreterState, + value_stack: ValueStack, + call_stack: Vec, + return_type: Option, + state: InterpreterState, } impl Interpreter { - pub fn new(func: &FuncRef, args: &[RuntimeValue]) -> Result { - let mut value_stack = ValueStack::with_limit(DEFAULT_VALUE_STACK_LIMIT); - for &arg in args { - let arg = arg.into(); - value_stack.push(arg).map_err( - // There is not enough space for pushing initial arguments. - // Weird, but bail out anyway. - |_| Trap::from(TrapKind::StackOverflow), - )?; - } + pub fn new(func: &FuncRef, args: &[RuntimeValue]) -> Result { + let mut value_stack = ValueStack::with_limit(DEFAULT_VALUE_STACK_LIMIT); + for &arg in args { + let arg = arg.into(); + value_stack.push(arg).map_err( + // There is not enough space for pushing initial arguments. + // Weird, but bail out anyway. + |_| Trap::from(TrapKind::StackOverflow), + )?; + } - let mut call_stack = Vec::new(); - let initial_frame = FunctionContext::new(func.clone()); - call_stack.push(initial_frame); + let mut call_stack = Vec::new(); + let initial_frame = FunctionContext::new(func.clone()); + call_stack.push(initial_frame); - let return_type = func.signature().return_type(); + let return_type = func.signature().return_type(); - Ok(Interpreter { - value_stack, - call_stack, - return_type, - state: InterpreterState::Initialized, - }) - } + Ok(Interpreter { + value_stack, + call_stack, + return_type, + state: InterpreterState::Initialized, + }) + } - pub fn state(&self) -> &InterpreterState { - &self.state - } + pub fn state(&self) -> &InterpreterState { + &self.state + } - pub fn start_execution<'a, E: Externals + 'a>( - &mut self, - externals: &'a mut E, - ) -> Result, Trap> { - // Ensure that the VM has not been executed. This is checked in `FuncInvocation::start_execution`. - assert!(self.state == InterpreterState::Initialized); + pub fn start_execution<'a, E: Externals + 'a>( + &mut self, + externals: &'a mut E, + ) -> Result, Trap> { + // Ensure that the VM has not been executed. This is checked in `FuncInvocation::start_execution`. + assert!(self.state == InterpreterState::Initialized); - self.state = InterpreterState::Started; - self.run_interpreter_loop(externals)?; + self.state = InterpreterState::Started; + self.run_interpreter_loop(externals)?; - let opt_return_value = self - .return_type - .map(|vt| self.value_stack.pop().with_type(vt)); + let opt_return_value = self + .return_type + .map(|vt| self.value_stack.pop().with_type(vt)); - // Ensure that stack is empty after the execution. This is guaranteed by the validation properties. - assert!(self.value_stack.len() == 0); + // Ensure that stack is empty after the execution. This is guaranteed by the validation properties. + assert!(self.value_stack.len() == 0); - Ok(opt_return_value) - } + Ok(opt_return_value) + } - pub fn resume_execution<'a, E: Externals + 'a>( - &mut self, - return_val: Option, - externals: &'a mut E, - ) -> Result, Trap> { - use core::mem::swap; + pub fn resume_execution<'a, E: Externals + 'a>( + &mut self, + return_val: Option, + externals: &'a mut E, + ) -> Result, Trap> { + use core::mem::swap; - // Ensure that the VM is resumable. This is checked in `FuncInvocation::resume_execution`. - assert!(self.state.is_resumable()); + // Ensure that the VM is resumable. This is checked in `FuncInvocation::resume_execution`. + assert!(self.state.is_resumable()); - let mut resumable_state = InterpreterState::Started; - swap(&mut self.state, &mut resumable_state); + let mut resumable_state = InterpreterState::Started; + swap(&mut self.state, &mut resumable_state); - if let Some(return_val) = return_val { - self.value_stack - .push(return_val.into()) - .map_err(Trap::new)?; - } + if let Some(return_val) = return_val { + self.value_stack + .push(return_val.into()) + .map_err(Trap::new)?; + } - self.run_interpreter_loop(externals)?; + self.run_interpreter_loop(externals)?; - let opt_return_value = self - .return_type - .map(|vt| self.value_stack.pop().with_type(vt)); + let opt_return_value = self + .return_type + .map(|vt| self.value_stack.pop().with_type(vt)); - // Ensure that stack is empty after the execution. This is guaranteed by the validation properties. - assert!(self.value_stack.len() == 0); + // Ensure that stack is empty after the execution. This is guaranteed by the validation properties. + assert!(self.value_stack.len() == 0); - Ok(opt_return_value) - } + Ok(opt_return_value) + } - fn run_interpreter_loop<'a, E: Externals + 'a>( - &mut self, - externals: &'a mut E, - ) -> Result<(), Trap> { - loop { - let mut function_context = self.call_stack.pop().expect( - "on loop entry - not empty; on loop continue - checking for emptiness; qed", - ); - let function_ref = function_context.function.clone(); - let function_body = function_ref + fn run_interpreter_loop<'a, E: Externals + 'a>( + &mut self, + externals: &'a mut E, + ) -> Result<(), Trap> { + loop { + let mut function_context = self.call_stack.pop().expect( + "on loop entry - not empty; on loop continue - checking for emptiness; qed", + ); + let function_ref = function_context.function.clone(); + let function_body = function_ref .body() .expect( "Host functions checked in function_return below; Internal functions always have a body; qed" ); - if !function_context.is_initialized() { - // Initialize stack frame for the function call. - function_context.initialize(&function_body.locals, &mut self.value_stack)?; - } - - let function_return = self - .do_run_function(&mut function_context, &function_body.code) - .map_err(Trap::new)?; - - match function_return { - RunResult::Return => { - if self.call_stack.last().is_none() { - // This was the last frame in the call stack. This means we - // are done executing. - return Ok(()); - } - } - RunResult::NestedCall(nested_func) => { - if self.call_stack.len() + 1 >= DEFAULT_CALL_STACK_LIMIT { - return Err(TrapKind::StackOverflow.into()); - } - - match *nested_func.as_internal() { - FuncInstanceInternal::Internal { .. } => { - let nested_context = FunctionContext::new(nested_func.clone()); - self.call_stack.push(function_context); - self.call_stack.push(nested_context); - } - FuncInstanceInternal::Host { ref signature, .. } => { - let args = prepare_function_args(signature, &mut self.value_stack); - // We push the function context first. If the VM is not resumable, it does no harm. If it is, we then save the context here. - self.call_stack.push(function_context); - - let return_val = - match FuncInstance::invoke(&nested_func, &args, externals) { - Ok(val) => val, - Err(trap) => { - if trap.kind().is_host() { - self.state = InterpreterState::Resumable( - nested_func.signature().return_type(), - ); - } - return Err(trap); - } - }; - - // Check if `return_val` matches the signature. - let value_ty = return_val.as_ref().map(|val| val.value_type()); - let expected_ty = nested_func.signature().return_type(); - if value_ty != expected_ty { - return Err(TrapKind::UnexpectedSignature.into()); - } - - if let Some(return_val) = return_val { - self.value_stack - .push(return_val.into()) - .map_err(Trap::new)?; - } - } - } - } - } - } - } - - fn do_run_function( - &mut self, - function_context: &mut FunctionContext, - instructions: &isa::Instructions, - ) -> Result { - let mut iter = instructions.iterate_from(function_context.position); - - loop { - let instruction = iter.next().expect( - "Ran out of instructions, this should be impossible \ - since validation ensures that we either have an explicit \ - return or an implicit block `end`.", - ); - - match self.run_instruction(function_context, &instruction)? { - InstructionOutcome::RunNextInstruction => {} - InstructionOutcome::Branch(target) => { - iter = instructions.iterate_from(target.dst_pc); - self.value_stack.drop_keep(target.drop_keep); - } - InstructionOutcome::ExecuteCall(func_ref) => { - function_context.position = iter.position(); - return Ok(RunResult::NestedCall(func_ref)); - } - InstructionOutcome::Return(drop_keep) => { - self.value_stack.drop_keep(drop_keep); - break; - } - } - } - - Ok(RunResult::Return) - } - - #[inline(always)] - fn run_instruction( - &mut self, - context: &mut FunctionContext, - instruction: &isa::Instruction, - ) -> Result { - match instruction { - &isa::Instruction::Unreachable => self.run_unreachable(context), - - &isa::Instruction::Br(target) => self.run_br(context, target.clone()), - &isa::Instruction::BrIfEqz(target) => self.run_br_eqz(target.clone()), - &isa::Instruction::BrIfNez(target) => self.run_br_nez(target.clone()), - &isa::Instruction::BrTable(targets) => self.run_br_table(targets), - &isa::Instruction::Return(drop_keep) => self.run_return(drop_keep), - - &isa::Instruction::Call(index) => self.run_call(context, index), - &isa::Instruction::CallIndirect(index) => self.run_call_indirect(context, index), - - &isa::Instruction::Drop => self.run_drop(), - &isa::Instruction::Select => self.run_select(), - - &isa::Instruction::GetLocal(depth) => self.run_get_local(depth), - &isa::Instruction::SetLocal(depth) => self.run_set_local(depth), - &isa::Instruction::TeeLocal(depth) => self.run_tee_local(depth), - &isa::Instruction::GetGlobal(index) => self.run_get_global(context, index), - &isa::Instruction::SetGlobal(index) => self.run_set_global(context, index), - - &isa::Instruction::I32Load(offset) => self.run_load::(context, offset), - &isa::Instruction::I64Load(offset) => self.run_load::(context, offset), - &isa::Instruction::F32Load(offset) => self.run_load::(context, offset), - &isa::Instruction::F64Load(offset) => self.run_load::(context, offset), - &isa::Instruction::I32Load8S(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I32Load8U(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I32Load16S(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I32Load16U(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I64Load8S(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I64Load8U(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I64Load16S(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I64Load16U(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I64Load32S(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I64Load32U(offset) => { - self.run_load_extend::(context, offset) - } - - &isa::Instruction::I32Store(offset) => self.run_store::(context, offset), - &isa::Instruction::I64Store(offset) => self.run_store::(context, offset), - &isa::Instruction::F32Store(offset) => self.run_store::(context, offset), - &isa::Instruction::F64Store(offset) => self.run_store::(context, offset), - &isa::Instruction::I32Store8(offset) => self.run_store_wrap::(context, offset), - &isa::Instruction::I32Store16(offset) => { - self.run_store_wrap::(context, offset) - } - &isa::Instruction::I64Store8(offset) => self.run_store_wrap::(context, offset), - &isa::Instruction::I64Store16(offset) => { - self.run_store_wrap::(context, offset) - } - &isa::Instruction::I64Store32(offset) => { - self.run_store_wrap::(context, offset) - } - - &isa::Instruction::CurrentMemory => self.run_current_memory(context), - &isa::Instruction::GrowMemory => self.run_grow_memory(context), - - &isa::Instruction::I32Const(val) => self.run_const(val.into()), - &isa::Instruction::I64Const(val) => self.run_const(val.into()), - &isa::Instruction::F32Const(val) => self.run_const(val.into()), - &isa::Instruction::F64Const(val) => self.run_const(val.into()), - - &isa::Instruction::I32Eqz => self.run_eqz::(), - &isa::Instruction::I32Eq => self.run_eq::(), - &isa::Instruction::I32Ne => self.run_ne::(), - &isa::Instruction::I32LtS => self.run_lt::(), - &isa::Instruction::I32LtU => self.run_lt::(), - &isa::Instruction::I32GtS => self.run_gt::(), - &isa::Instruction::I32GtU => self.run_gt::(), - &isa::Instruction::I32LeS => self.run_lte::(), - &isa::Instruction::I32LeU => self.run_lte::(), - &isa::Instruction::I32GeS => self.run_gte::(), - &isa::Instruction::I32GeU => self.run_gte::(), - - &isa::Instruction::I64Eqz => self.run_eqz::(), - &isa::Instruction::I64Eq => self.run_eq::(), - &isa::Instruction::I64Ne => self.run_ne::(), - &isa::Instruction::I64LtS => self.run_lt::(), - &isa::Instruction::I64LtU => self.run_lt::(), - &isa::Instruction::I64GtS => self.run_gt::(), - &isa::Instruction::I64GtU => self.run_gt::(), - &isa::Instruction::I64LeS => self.run_lte::(), - &isa::Instruction::I64LeU => self.run_lte::(), - &isa::Instruction::I64GeS => self.run_gte::(), - &isa::Instruction::I64GeU => self.run_gte::(), - - &isa::Instruction::F32Eq => self.run_eq::(), - &isa::Instruction::F32Ne => self.run_ne::(), - &isa::Instruction::F32Lt => self.run_lt::(), - &isa::Instruction::F32Gt => self.run_gt::(), - &isa::Instruction::F32Le => self.run_lte::(), - &isa::Instruction::F32Ge => self.run_gte::(), - - &isa::Instruction::F64Eq => self.run_eq::(), - &isa::Instruction::F64Ne => self.run_ne::(), - &isa::Instruction::F64Lt => self.run_lt::(), - &isa::Instruction::F64Gt => self.run_gt::(), - &isa::Instruction::F64Le => self.run_lte::(), - &isa::Instruction::F64Ge => self.run_gte::(), - - &isa::Instruction::I32Clz => self.run_clz::(), - &isa::Instruction::I32Ctz => self.run_ctz::(), - &isa::Instruction::I32Popcnt => self.run_popcnt::(), - &isa::Instruction::I32Add => self.run_add::(), - &isa::Instruction::I32Sub => self.run_sub::(), - &isa::Instruction::I32Mul => self.run_mul::(), - &isa::Instruction::I32DivS => self.run_div::(), - &isa::Instruction::I32DivU => self.run_div::(), - &isa::Instruction::I32RemS => self.run_rem::(), - &isa::Instruction::I32RemU => self.run_rem::(), - &isa::Instruction::I32And => self.run_and::(), - &isa::Instruction::I32Or => self.run_or::(), - &isa::Instruction::I32Xor => self.run_xor::(), - &isa::Instruction::I32Shl => self.run_shl::(0x1F), - &isa::Instruction::I32ShrS => self.run_shr::(0x1F), - &isa::Instruction::I32ShrU => self.run_shr::(0x1F), - &isa::Instruction::I32Rotl => self.run_rotl::(), - &isa::Instruction::I32Rotr => self.run_rotr::(), - - &isa::Instruction::I64Clz => self.run_clz::(), - &isa::Instruction::I64Ctz => self.run_ctz::(), - &isa::Instruction::I64Popcnt => self.run_popcnt::(), - &isa::Instruction::I64Add => self.run_add::(), - &isa::Instruction::I64Sub => self.run_sub::(), - &isa::Instruction::I64Mul => self.run_mul::(), - &isa::Instruction::I64DivS => self.run_div::(), - &isa::Instruction::I64DivU => self.run_div::(), - &isa::Instruction::I64RemS => self.run_rem::(), - &isa::Instruction::I64RemU => self.run_rem::(), - &isa::Instruction::I64And => self.run_and::(), - &isa::Instruction::I64Or => self.run_or::(), - &isa::Instruction::I64Xor => self.run_xor::(), - &isa::Instruction::I64Shl => self.run_shl::(0x3F), - &isa::Instruction::I64ShrS => self.run_shr::(0x3F), - &isa::Instruction::I64ShrU => self.run_shr::(0x3F), - &isa::Instruction::I64Rotl => self.run_rotl::(), - &isa::Instruction::I64Rotr => self.run_rotr::(), - - &isa::Instruction::F32Abs => self.run_abs::(), - &isa::Instruction::F32Neg => self.run_neg::(), - &isa::Instruction::F32Ceil => self.run_ceil::(), - &isa::Instruction::F32Floor => self.run_floor::(), - &isa::Instruction::F32Trunc => self.run_trunc::(), - &isa::Instruction::F32Nearest => self.run_nearest::(), - &isa::Instruction::F32Sqrt => self.run_sqrt::(), - &isa::Instruction::F32Add => self.run_add::(), - &isa::Instruction::F32Sub => self.run_sub::(), - &isa::Instruction::F32Mul => self.run_mul::(), - &isa::Instruction::F32Div => self.run_div::(), - &isa::Instruction::F32Min => self.run_min::(), - &isa::Instruction::F32Max => self.run_max::(), - &isa::Instruction::F32Copysign => self.run_copysign::(), - - &isa::Instruction::F64Abs => self.run_abs::(), - &isa::Instruction::F64Neg => self.run_neg::(), - &isa::Instruction::F64Ceil => self.run_ceil::(), - &isa::Instruction::F64Floor => self.run_floor::(), - &isa::Instruction::F64Trunc => self.run_trunc::(), - &isa::Instruction::F64Nearest => self.run_nearest::(), - &isa::Instruction::F64Sqrt => self.run_sqrt::(), - &isa::Instruction::F64Add => self.run_add::(), - &isa::Instruction::F64Sub => self.run_sub::(), - &isa::Instruction::F64Mul => self.run_mul::(), - &isa::Instruction::F64Div => self.run_div::(), - &isa::Instruction::F64Min => self.run_min::(), - &isa::Instruction::F64Max => self.run_max::(), - &isa::Instruction::F64Copysign => self.run_copysign::(), - - &isa::Instruction::I32WrapI64 => self.run_wrap::(), - &isa::Instruction::I32TruncSF32 => self.run_trunc_to_int::(), - &isa::Instruction::I32TruncUF32 => self.run_trunc_to_int::(), - &isa::Instruction::I32TruncSF64 => self.run_trunc_to_int::(), - &isa::Instruction::I32TruncUF64 => self.run_trunc_to_int::(), - &isa::Instruction::I64ExtendSI32 => self.run_extend::(), - &isa::Instruction::I64ExtendUI32 => self.run_extend::(), - &isa::Instruction::I64TruncSF32 => self.run_trunc_to_int::(), - &isa::Instruction::I64TruncUF32 => self.run_trunc_to_int::(), - &isa::Instruction::I64TruncSF64 => self.run_trunc_to_int::(), - &isa::Instruction::I64TruncUF64 => self.run_trunc_to_int::(), - &isa::Instruction::F32ConvertSI32 => self.run_extend::(), - &isa::Instruction::F32ConvertUI32 => self.run_extend::(), - &isa::Instruction::F32ConvertSI64 => self.run_wrap::(), - &isa::Instruction::F32ConvertUI64 => self.run_wrap::(), - &isa::Instruction::F32DemoteF64 => self.run_wrap::(), - &isa::Instruction::F64ConvertSI32 => self.run_extend::(), - &isa::Instruction::F64ConvertUI32 => self.run_extend::(), - &isa::Instruction::F64ConvertSI64 => self.run_extend::(), - &isa::Instruction::F64ConvertUI64 => self.run_extend::(), - &isa::Instruction::F64PromoteF32 => self.run_extend::(), - - &isa::Instruction::I32ReinterpretF32 => self.run_reinterpret::(), - &isa::Instruction::I64ReinterpretF64 => self.run_reinterpret::(), - &isa::Instruction::F32ReinterpretI32 => self.run_reinterpret::(), - &isa::Instruction::F64ReinterpretI64 => self.run_reinterpret::(), - } - } - - fn run_unreachable( - &mut self, - _context: &mut FunctionContext, - ) -> Result { - Err(TrapKind::Unreachable) - } - - fn run_br( - &mut self, - _context: &mut FunctionContext, - target: isa::Target, - ) -> Result { - Ok(InstructionOutcome::Branch(target)) - } - - fn run_br_nez(&mut self, target: isa::Target) -> Result { - let condition = self.value_stack.pop_as(); - if condition { - Ok(InstructionOutcome::Branch(target)) - } else { - Ok(InstructionOutcome::RunNextInstruction) - } - } - - fn run_br_eqz(&mut self, target: isa::Target) -> Result { - let condition = self.value_stack.pop_as(); - if condition { - Ok(InstructionOutcome::RunNextInstruction) - } else { - Ok(InstructionOutcome::Branch(target)) - } - } - - fn run_br_table(&mut self, targets: isa::BrTargets) -> Result { - let index: u32 = self.value_stack.pop_as(); - - let dst = targets.get(index); - - Ok(InstructionOutcome::Branch(dst)) - } - - fn run_return(&mut self, drop_keep: isa::DropKeep) -> Result { - Ok(InstructionOutcome::Return(drop_keep)) - } - - fn run_call( - &mut self, - context: &mut FunctionContext, - func_idx: u32, - ) -> Result { - let func = context - .module() - .func_by_index(func_idx) - .expect("Due to validation func should exists"); - Ok(InstructionOutcome::ExecuteCall(func)) - } - - fn run_call_indirect( - &mut self, - context: &mut FunctionContext, - signature_idx: u32, - ) -> Result { - let table_func_idx: u32 = self.value_stack.pop_as(); - let table = context - .module() - .table_by_index(DEFAULT_TABLE_INDEX) - .expect("Due to validation table should exists"); - let func_ref = table - .get(table_func_idx) - .map_err(|_| TrapKind::TableAccessOutOfBounds)? - .ok_or_else(|| TrapKind::ElemUninitialized)?; - - { - let actual_function_type = func_ref.signature(); - let required_function_type = context - .module() - .signature_by_index(signature_idx) - .expect("Due to validation type should exists"); - - if &*required_function_type != actual_function_type { - return Err(TrapKind::UnexpectedSignature); - } - } - - Ok(InstructionOutcome::ExecuteCall(func_ref)) - } - - fn run_drop(&mut self) -> Result { - let _ = self.value_stack.pop(); - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_select(&mut self) -> Result { - let (left, mid, right) = self.value_stack.pop_triple(); - - let condition = <_>::from_runtime_value_internal(right); - let val = if condition { left } else { mid }; - self.value_stack.push(val)?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_get_local(&mut self, index: u32) -> Result { - let val = *self.value_stack.pick_mut(index as usize); - self.value_stack.push(val)?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_set_local(&mut self, index: u32) -> Result { - let val = self.value_stack.pop(); - *self.value_stack.pick_mut(index as usize) = val; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_tee_local(&mut self, index: u32) -> Result { - let val = self.value_stack.top().clone(); - *self.value_stack.pick_mut(index as usize) = val; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_get_global( - &mut self, - context: &mut FunctionContext, - index: u32, - ) -> Result { - let global = context - .module() - .global_by_index(index) - .expect("Due to validation global should exists"); - let val = global.get(); - self.value_stack.push(val.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_set_global( - &mut self, - context: &mut FunctionContext, - index: u32, - ) -> Result { - let val = self.value_stack.pop(); - let global = context - .module() - .global_by_index(index) - .expect("Due to validation global should exists"); - global - .set(val.with_type(global.value_type())) - .expect("Due to validation set to a global should succeed"); - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_load( - &mut self, - context: &mut FunctionContext, - offset: u32, - ) -> Result - where - RuntimeValueInternal: From, - T: LittleEndianConvert, - { - let raw_address = self.value_stack.pop_as(); - let address = effective_address(offset, raw_address)?; - let m = context - .memory() - .expect("Due to validation memory should exists"); - let n: T = m - .get_value(address) - .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; - self.value_stack.push(n.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_load_extend( - &mut self, - context: &mut FunctionContext, - offset: u32, - ) -> Result - where - T: ExtendInto, - RuntimeValueInternal: From, - T: LittleEndianConvert, - { - let raw_address = self.value_stack.pop_as(); - let address = effective_address(offset, raw_address)?; - let m = context - .memory() - .expect("Due to validation memory should exists"); - let v: T = m - .get_value(address) - .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; - let stack_value: U = v.extend_into(); - self.value_stack - .push(stack_value.into()) - .map_err(Into::into) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_store( - &mut self, - context: &mut FunctionContext, - offset: u32, - ) -> Result - where - T: FromRuntimeValueInternal, - T: LittleEndianConvert, - { - let stack_value = self.value_stack.pop_as::(); - let raw_address = self.value_stack.pop_as::(); - let address = effective_address(offset, raw_address)?; - - let m = context - .memory() - .expect("Due to validation memory should exists"); - m.set_value(address, stack_value) - .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_store_wrap( - &mut self, - context: &mut FunctionContext, - offset: u32, - ) -> Result - where - T: FromRuntimeValueInternal, - T: WrapInto, - U: LittleEndianConvert, - { - let stack_value: T = <_>::from_runtime_value_internal(self.value_stack.pop()); - let stack_value = stack_value.wrap_into(); - let raw_address = self.value_stack.pop_as::(); - let address = effective_address(offset, raw_address)?; - let m = context - .memory() - .expect("Due to validation memory should exists"); - m.set_value(address, stack_value) - .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_current_memory( - &mut self, - context: &mut FunctionContext, - ) -> Result { - let m = context - .memory() - .expect("Due to validation memory should exists"); - let s = m.current_size().0; - self.value_stack.push(RuntimeValueInternal(s as _))?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_grow_memory( - &mut self, - context: &mut FunctionContext, - ) -> Result { - let pages: u32 = self.value_stack.pop_as(); - let m = context - .memory() - .expect("Due to validation memory should exists"); - let m = match m.grow(Pages(pages as usize)) { - Ok(Pages(new_size)) => new_size as u32, - Err(_) => u32::MAX, // Returns -1 (or 0xFFFFFFFF) in case of error. - }; - self.value_stack.push(RuntimeValueInternal(m as _))?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_const(&mut self, val: RuntimeValue) -> Result { - self.value_stack - .push(val.into()) - .map_err(Into::into) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_relop(&mut self, f: F) -> Result - where - T: FromRuntimeValueInternal, - F: FnOnce(T, T) -> bool, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let v = if f(left, right) { - RuntimeValueInternal(1) - } else { - RuntimeValueInternal(0) - }; - self.value_stack.push(v)?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_eqz(&mut self) -> Result - where - T: FromRuntimeValueInternal, - T: PartialEq + Default, - { - let v = self.value_stack.pop_as::(); - let v = RuntimeValueInternal(if v == Default::default() { 1 } else { 0 }); - self.value_stack.push(v)?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_eq(&mut self) -> Result - where - T: FromRuntimeValueInternal + PartialEq, - { - self.run_relop(|left: T, right: T| left == right) - } - - fn run_ne(&mut self) -> Result - where - T: FromRuntimeValueInternal + PartialEq, - { - self.run_relop(|left: T, right: T| left != right) - } - - fn run_lt(&mut self) -> Result - where - T: FromRuntimeValueInternal + PartialOrd, - { - self.run_relop(|left: T, right: T| left < right) - } - - fn run_gt(&mut self) -> Result - where - T: FromRuntimeValueInternal + PartialOrd, - { - self.run_relop(|left: T, right: T| left > right) - } - - fn run_lte(&mut self) -> Result - where - T: FromRuntimeValueInternal + PartialOrd, - { - self.run_relop(|left: T, right: T| left <= right) - } - - fn run_gte(&mut self) -> Result - where - T: FromRuntimeValueInternal + PartialOrd, - { - self.run_relop(|left: T, right: T| left >= right) - } - - fn run_unop(&mut self, f: F) -> Result - where - F: FnOnce(T) -> U, - T: FromRuntimeValueInternal, - RuntimeValueInternal: From, - { - let v = self.value_stack.pop_as::(); - let v = f(v); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_clz(&mut self) -> Result - where - RuntimeValueInternal: From, - T: Integer + FromRuntimeValueInternal, - { - self.run_unop(|v: T| v.leading_zeros()) - } - - fn run_ctz(&mut self) -> Result - where - RuntimeValueInternal: From, - T: Integer + FromRuntimeValueInternal, - { - self.run_unop(|v: T| v.trailing_zeros()) - } - - fn run_popcnt(&mut self) -> Result - where - RuntimeValueInternal: From, - T: Integer + FromRuntimeValueInternal, - { - self.run_unop(|v: T| v.count_ones()) - } - - fn run_add(&mut self) -> Result - where - RuntimeValueInternal: From, - T: ArithmeticOps + FromRuntimeValueInternal, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let v = left.add(right); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_sub(&mut self) -> Result - where - RuntimeValueInternal: From, - T: ArithmeticOps + FromRuntimeValueInternal, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let v = left.sub(right); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_mul(&mut self) -> Result - where - RuntimeValueInternal: From, - T: ArithmeticOps + FromRuntimeValueInternal, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let v = left.mul(right); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_div(&mut self) -> Result - where - RuntimeValueInternal: From, - T: TransmuteInto + FromRuntimeValueInternal, - U: ArithmeticOps + TransmuteInto, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let (left, right) = (left.transmute_into(), right.transmute_into()); - let v = left.div(right)?; - let v = v.transmute_into(); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_rem(&mut self) -> Result - where - RuntimeValueInternal: From, - T: TransmuteInto + FromRuntimeValueInternal, - U: Integer + TransmuteInto, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let (left, right) = (left.transmute_into(), right.transmute_into()); - let v = left.rem(right)?; - let v = v.transmute_into(); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_and(&mut self) -> Result - where - RuntimeValueInternal: From<::Output>, - T: ops::BitAnd + FromRuntimeValueInternal, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let v = left.bitand(right); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_or(&mut self) -> Result - where - RuntimeValueInternal: From<::Output>, - T: ops::BitOr + FromRuntimeValueInternal, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let v = left.bitor(right); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_xor(&mut self) -> Result - where - RuntimeValueInternal: From<::Output>, - T: ops::BitXor + FromRuntimeValueInternal, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let v = left.bitxor(right); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_shl(&mut self, mask: T) -> Result - where - RuntimeValueInternal: From<>::Output>, - T: ops::Shl + ops::BitAnd + FromRuntimeValueInternal, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let v = left.shl(right & mask); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_shr(&mut self, mask: U) -> Result - where - RuntimeValueInternal: From, - T: TransmuteInto + FromRuntimeValueInternal, - U: ops::Shr + ops::BitAnd, - >::Output: TransmuteInto, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let (left, right) = (left.transmute_into(), right.transmute_into()); - let v = left.shr(right & mask); - let v = v.transmute_into(); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_rotl(&mut self) -> Result - where - RuntimeValueInternal: From, - T: Integer + FromRuntimeValueInternal, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let v = left.rotl(right); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_rotr(&mut self) -> Result - where - RuntimeValueInternal: From, - T: Integer + FromRuntimeValueInternal, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let v = left.rotr(right); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_abs(&mut self) -> Result - where - RuntimeValueInternal: From, - T: Float + FromRuntimeValueInternal, - { - self.run_unop(|v: T| v.abs()) - } - - fn run_neg(&mut self) -> Result - where - RuntimeValueInternal: From<::Output>, - T: ops::Neg + FromRuntimeValueInternal, - { - self.run_unop(|v: T| v.neg()) - } - - fn run_ceil(&mut self) -> Result - where - RuntimeValueInternal: From, - T: Float + FromRuntimeValueInternal, - { - self.run_unop(|v: T| v.ceil()) - } - - fn run_floor(&mut self) -> Result - where - RuntimeValueInternal: From, - T: Float + FromRuntimeValueInternal, - { - self.run_unop(|v: T| v.floor()) - } - - fn run_trunc(&mut self) -> Result - where - RuntimeValueInternal: From, - T: Float + FromRuntimeValueInternal, - { - self.run_unop(|v: T| v.trunc()) - } - - fn run_nearest(&mut self) -> Result - where - RuntimeValueInternal: From, - T: Float + FromRuntimeValueInternal, - { - self.run_unop(|v: T| v.nearest()) - } - - fn run_sqrt(&mut self) -> Result - where - RuntimeValueInternal: From, - T: Float + FromRuntimeValueInternal, - { - self.run_unop(|v: T| v.sqrt()) - } - - fn run_min(&mut self) -> Result - where - RuntimeValueInternal: From, - T: Float + FromRuntimeValueInternal, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let v = left.min(right); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_max(&mut self) -> Result - where - RuntimeValueInternal: From, - T: Float + FromRuntimeValueInternal, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let v = left.max(right); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_copysign(&mut self) -> Result - where - RuntimeValueInternal: From, - T: Float + FromRuntimeValueInternal, - { - let (left, right) = self.value_stack.pop_pair_as::(); - let v = left.copysign(right); - self.value_stack.push(v.into())?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_wrap(&mut self) -> Result - where - RuntimeValueInternal: From, - T: WrapInto + FromRuntimeValueInternal, - { - self.run_unop(|v: T| v.wrap_into()) - } - - fn run_trunc_to_int(&mut self) -> Result - where - RuntimeValueInternal: From, - T: TryTruncateInto + FromRuntimeValueInternal, - U: TransmuteInto, - { - let v = self.value_stack.pop_as::(); - - v.try_truncate_into() - .map(|v| v.transmute_into()) - .map(|v| self.value_stack.push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_extend(&mut self) -> Result - where - RuntimeValueInternal: From, - T: ExtendInto + FromRuntimeValueInternal, - U: TransmuteInto, - { - let v = self.value_stack.pop_as::(); - - let v = v.extend_into().transmute_into(); - self.value_stack.push(v.into())?; - - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_reinterpret(&mut self) -> Result - where - RuntimeValueInternal: From, - T: FromRuntimeValueInternal, - T: TransmuteInto, - { - let v = self.value_stack.pop_as::(); - - let v = v.transmute_into(); - self.value_stack.push(v.into())?; - - Ok(InstructionOutcome::RunNextInstruction) - } + if !function_context.is_initialized() { + // Initialize stack frame for the function call. + function_context.initialize(&function_body.locals, &mut self.value_stack)?; + } + + let function_return = self + .do_run_function(&mut function_context, &function_body.code) + .map_err(Trap::new)?; + + match function_return { + RunResult::Return => { + if self.call_stack.last().is_none() { + // This was the last frame in the call stack. This means we + // are done executing. + return Ok(()); + } + } + RunResult::NestedCall(nested_func) => { + if self.call_stack.len() + 1 >= DEFAULT_CALL_STACK_LIMIT { + return Err(TrapKind::StackOverflow.into()); + } + + match *nested_func.as_internal() { + FuncInstanceInternal::Internal { .. } => { + let nested_context = FunctionContext::new(nested_func.clone()); + self.call_stack.push(function_context); + self.call_stack.push(nested_context); + } + FuncInstanceInternal::Host { ref signature, .. } => { + let args = prepare_function_args(signature, &mut self.value_stack); + // We push the function context first. If the VM is not resumable, it does no harm. If it is, we then save the context here. + self.call_stack.push(function_context); + + let return_val = + match FuncInstance::invoke(&nested_func, &args, externals) { + Ok(val) => val, + Err(trap) => { + if trap.kind().is_host() { + self.state = InterpreterState::Resumable( + nested_func.signature().return_type(), + ); + } + return Err(trap); + } + }; + + // Check if `return_val` matches the signature. + let value_ty = return_val.as_ref().map(|val| val.value_type()); + let expected_ty = nested_func.signature().return_type(); + if value_ty != expected_ty { + return Err(TrapKind::UnexpectedSignature.into()); + } + + if let Some(return_val) = return_val { + self.value_stack + .push(return_val.into()) + .map_err(Trap::new)?; + } + } + } + } + } + } + } + + fn do_run_function( + &mut self, + function_context: &mut FunctionContext, + instructions: &isa::Instructions, + ) -> Result { + let mut iter = instructions.iterate_from(function_context.position); + + loop { + let instruction = iter.next().expect( + "Ran out of instructions, this should be impossible \ + since validation ensures that we either have an explicit \ + return or an implicit block `end`.", + ); + + match self.run_instruction(function_context, &instruction)? { + InstructionOutcome::RunNextInstruction => {} + InstructionOutcome::Branch(target) => { + iter = instructions.iterate_from(target.dst_pc); + self.value_stack.drop_keep(target.drop_keep); + } + InstructionOutcome::ExecuteCall(func_ref) => { + function_context.position = iter.position(); + return Ok(RunResult::NestedCall(func_ref)); + } + InstructionOutcome::Return(drop_keep) => { + self.value_stack.drop_keep(drop_keep); + break; + } + } + } + + Ok(RunResult::Return) + } + + #[inline(always)] + fn run_instruction( + &mut self, + context: &mut FunctionContext, + instruction: &isa::Instruction, + ) -> Result { + match instruction { + &isa::Instruction::Unreachable => self.run_unreachable(context), + + &isa::Instruction::Br(target) => self.run_br(context, target.clone()), + &isa::Instruction::BrIfEqz(target) => self.run_br_eqz(target.clone()), + &isa::Instruction::BrIfNez(target) => self.run_br_nez(target.clone()), + &isa::Instruction::BrTable(targets) => self.run_br_table(targets), + &isa::Instruction::Return(drop_keep) => self.run_return(drop_keep), + + &isa::Instruction::Call(index) => self.run_call(context, index), + &isa::Instruction::CallIndirect(index) => self.run_call_indirect(context, index), + + &isa::Instruction::Drop => self.run_drop(), + &isa::Instruction::Select => self.run_select(), + + &isa::Instruction::GetLocal(depth) => self.run_get_local(depth), + &isa::Instruction::SetLocal(depth) => self.run_set_local(depth), + &isa::Instruction::TeeLocal(depth) => self.run_tee_local(depth), + &isa::Instruction::GetGlobal(index) => self.run_get_global(context, index), + &isa::Instruction::SetGlobal(index) => self.run_set_global(context, index), + + &isa::Instruction::I32Load(offset) => self.run_load::(context, offset), + &isa::Instruction::I64Load(offset) => self.run_load::(context, offset), + &isa::Instruction::F32Load(offset) => self.run_load::(context, offset), + &isa::Instruction::F64Load(offset) => self.run_load::(context, offset), + &isa::Instruction::I32Load8S(offset) => { + self.run_load_extend::(context, offset) + } + &isa::Instruction::I32Load8U(offset) => { + self.run_load_extend::(context, offset) + } + &isa::Instruction::I32Load16S(offset) => { + self.run_load_extend::(context, offset) + } + &isa::Instruction::I32Load16U(offset) => { + self.run_load_extend::(context, offset) + } + &isa::Instruction::I64Load8S(offset) => { + self.run_load_extend::(context, offset) + } + &isa::Instruction::I64Load8U(offset) => { + self.run_load_extend::(context, offset) + } + &isa::Instruction::I64Load16S(offset) => { + self.run_load_extend::(context, offset) + } + &isa::Instruction::I64Load16U(offset) => { + self.run_load_extend::(context, offset) + } + &isa::Instruction::I64Load32S(offset) => { + self.run_load_extend::(context, offset) + } + &isa::Instruction::I64Load32U(offset) => { + self.run_load_extend::(context, offset) + } + + &isa::Instruction::I32Store(offset) => self.run_store::(context, offset), + &isa::Instruction::I64Store(offset) => self.run_store::(context, offset), + &isa::Instruction::F32Store(offset) => self.run_store::(context, offset), + &isa::Instruction::F64Store(offset) => self.run_store::(context, offset), + &isa::Instruction::I32Store8(offset) => self.run_store_wrap::(context, offset), + &isa::Instruction::I32Store16(offset) => { + self.run_store_wrap::(context, offset) + } + &isa::Instruction::I64Store8(offset) => self.run_store_wrap::(context, offset), + &isa::Instruction::I64Store16(offset) => { + self.run_store_wrap::(context, offset) + } + &isa::Instruction::I64Store32(offset) => { + self.run_store_wrap::(context, offset) + } + + &isa::Instruction::CurrentMemory => self.run_current_memory(context), + &isa::Instruction::GrowMemory => self.run_grow_memory(context), + + &isa::Instruction::I32Const(val) => self.run_const(val.into()), + &isa::Instruction::I64Const(val) => self.run_const(val.into()), + &isa::Instruction::F32Const(val) => self.run_const(val.into()), + &isa::Instruction::F64Const(val) => self.run_const(val.into()), + + &isa::Instruction::I32Eqz => self.run_eqz::(), + &isa::Instruction::I32Eq => self.run_eq::(), + &isa::Instruction::I32Ne => self.run_ne::(), + &isa::Instruction::I32LtS => self.run_lt::(), + &isa::Instruction::I32LtU => self.run_lt::(), + &isa::Instruction::I32GtS => self.run_gt::(), + &isa::Instruction::I32GtU => self.run_gt::(), + &isa::Instruction::I32LeS => self.run_lte::(), + &isa::Instruction::I32LeU => self.run_lte::(), + &isa::Instruction::I32GeS => self.run_gte::(), + &isa::Instruction::I32GeU => self.run_gte::(), + + &isa::Instruction::I64Eqz => self.run_eqz::(), + &isa::Instruction::I64Eq => self.run_eq::(), + &isa::Instruction::I64Ne => self.run_ne::(), + &isa::Instruction::I64LtS => self.run_lt::(), + &isa::Instruction::I64LtU => self.run_lt::(), + &isa::Instruction::I64GtS => self.run_gt::(), + &isa::Instruction::I64GtU => self.run_gt::(), + &isa::Instruction::I64LeS => self.run_lte::(), + &isa::Instruction::I64LeU => self.run_lte::(), + &isa::Instruction::I64GeS => self.run_gte::(), + &isa::Instruction::I64GeU => self.run_gte::(), + + &isa::Instruction::F32Eq => self.run_eq::(), + &isa::Instruction::F32Ne => self.run_ne::(), + &isa::Instruction::F32Lt => self.run_lt::(), + &isa::Instruction::F32Gt => self.run_gt::(), + &isa::Instruction::F32Le => self.run_lte::(), + &isa::Instruction::F32Ge => self.run_gte::(), + + &isa::Instruction::F64Eq => self.run_eq::(), + &isa::Instruction::F64Ne => self.run_ne::(), + &isa::Instruction::F64Lt => self.run_lt::(), + &isa::Instruction::F64Gt => self.run_gt::(), + &isa::Instruction::F64Le => self.run_lte::(), + &isa::Instruction::F64Ge => self.run_gte::(), + + &isa::Instruction::I32Clz => self.run_clz::(), + &isa::Instruction::I32Ctz => self.run_ctz::(), + &isa::Instruction::I32Popcnt => self.run_popcnt::(), + &isa::Instruction::I32Add => self.run_add::(), + &isa::Instruction::I32Sub => self.run_sub::(), + &isa::Instruction::I32Mul => self.run_mul::(), + &isa::Instruction::I32DivS => self.run_div::(), + &isa::Instruction::I32DivU => self.run_div::(), + &isa::Instruction::I32RemS => self.run_rem::(), + &isa::Instruction::I32RemU => self.run_rem::(), + &isa::Instruction::I32And => self.run_and::(), + &isa::Instruction::I32Or => self.run_or::(), + &isa::Instruction::I32Xor => self.run_xor::(), + &isa::Instruction::I32Shl => self.run_shl::(0x1F), + &isa::Instruction::I32ShrS => self.run_shr::(0x1F), + &isa::Instruction::I32ShrU => self.run_shr::(0x1F), + &isa::Instruction::I32Rotl => self.run_rotl::(), + &isa::Instruction::I32Rotr => self.run_rotr::(), + + &isa::Instruction::I64Clz => self.run_clz::(), + &isa::Instruction::I64Ctz => self.run_ctz::(), + &isa::Instruction::I64Popcnt => self.run_popcnt::(), + &isa::Instruction::I64Add => self.run_add::(), + &isa::Instruction::I64Sub => self.run_sub::(), + &isa::Instruction::I64Mul => self.run_mul::(), + &isa::Instruction::I64DivS => self.run_div::(), + &isa::Instruction::I64DivU => self.run_div::(), + &isa::Instruction::I64RemS => self.run_rem::(), + &isa::Instruction::I64RemU => self.run_rem::(), + &isa::Instruction::I64And => self.run_and::(), + &isa::Instruction::I64Or => self.run_or::(), + &isa::Instruction::I64Xor => self.run_xor::(), + &isa::Instruction::I64Shl => self.run_shl::(0x3F), + &isa::Instruction::I64ShrS => self.run_shr::(0x3F), + &isa::Instruction::I64ShrU => self.run_shr::(0x3F), + &isa::Instruction::I64Rotl => self.run_rotl::(), + &isa::Instruction::I64Rotr => self.run_rotr::(), + + &isa::Instruction::F32Abs => self.run_abs::(), + &isa::Instruction::F32Neg => self.run_neg::(), + &isa::Instruction::F32Ceil => self.run_ceil::(), + &isa::Instruction::F32Floor => self.run_floor::(), + &isa::Instruction::F32Trunc => self.run_trunc::(), + &isa::Instruction::F32Nearest => self.run_nearest::(), + &isa::Instruction::F32Sqrt => self.run_sqrt::(), + &isa::Instruction::F32Add => self.run_add::(), + &isa::Instruction::F32Sub => self.run_sub::(), + &isa::Instruction::F32Mul => self.run_mul::(), + &isa::Instruction::F32Div => self.run_div::(), + &isa::Instruction::F32Min => self.run_min::(), + &isa::Instruction::F32Max => self.run_max::(), + &isa::Instruction::F32Copysign => self.run_copysign::(), + + &isa::Instruction::F64Abs => self.run_abs::(), + &isa::Instruction::F64Neg => self.run_neg::(), + &isa::Instruction::F64Ceil => self.run_ceil::(), + &isa::Instruction::F64Floor => self.run_floor::(), + &isa::Instruction::F64Trunc => self.run_trunc::(), + &isa::Instruction::F64Nearest => self.run_nearest::(), + &isa::Instruction::F64Sqrt => self.run_sqrt::(), + &isa::Instruction::F64Add => self.run_add::(), + &isa::Instruction::F64Sub => self.run_sub::(), + &isa::Instruction::F64Mul => self.run_mul::(), + &isa::Instruction::F64Div => self.run_div::(), + &isa::Instruction::F64Min => self.run_min::(), + &isa::Instruction::F64Max => self.run_max::(), + &isa::Instruction::F64Copysign => self.run_copysign::(), + + &isa::Instruction::I32WrapI64 => self.run_wrap::(), + &isa::Instruction::I32TruncSF32 => self.run_trunc_to_int::(), + &isa::Instruction::I32TruncUF32 => self.run_trunc_to_int::(), + &isa::Instruction::I32TruncSF64 => self.run_trunc_to_int::(), + &isa::Instruction::I32TruncUF64 => self.run_trunc_to_int::(), + &isa::Instruction::I64ExtendSI32 => self.run_extend::(), + &isa::Instruction::I64ExtendUI32 => self.run_extend::(), + &isa::Instruction::I64TruncSF32 => self.run_trunc_to_int::(), + &isa::Instruction::I64TruncUF32 => self.run_trunc_to_int::(), + &isa::Instruction::I64TruncSF64 => self.run_trunc_to_int::(), + &isa::Instruction::I64TruncUF64 => self.run_trunc_to_int::(), + &isa::Instruction::F32ConvertSI32 => self.run_extend::(), + &isa::Instruction::F32ConvertUI32 => self.run_extend::(), + &isa::Instruction::F32ConvertSI64 => self.run_wrap::(), + &isa::Instruction::F32ConvertUI64 => self.run_wrap::(), + &isa::Instruction::F32DemoteF64 => self.run_wrap::(), + &isa::Instruction::F64ConvertSI32 => self.run_extend::(), + &isa::Instruction::F64ConvertUI32 => self.run_extend::(), + &isa::Instruction::F64ConvertSI64 => self.run_extend::(), + &isa::Instruction::F64ConvertUI64 => self.run_extend::(), + &isa::Instruction::F64PromoteF32 => self.run_extend::(), + + &isa::Instruction::I32ReinterpretF32 => self.run_reinterpret::(), + &isa::Instruction::I64ReinterpretF64 => self.run_reinterpret::(), + &isa::Instruction::F32ReinterpretI32 => self.run_reinterpret::(), + &isa::Instruction::F64ReinterpretI64 => self.run_reinterpret::(), + } + } + + fn run_unreachable( + &mut self, + _context: &mut FunctionContext, + ) -> Result { + Err(TrapKind::Unreachable) + } + + fn run_br( + &mut self, + _context: &mut FunctionContext, + target: isa::Target, + ) -> Result { + Ok(InstructionOutcome::Branch(target)) + } + + fn run_br_nez(&mut self, target: isa::Target) -> Result { + let condition = self.value_stack.pop_as(); + if condition { + Ok(InstructionOutcome::Branch(target)) + } else { + Ok(InstructionOutcome::RunNextInstruction) + } + } + + fn run_br_eqz(&mut self, target: isa::Target) -> Result { + let condition = self.value_stack.pop_as(); + if condition { + Ok(InstructionOutcome::RunNextInstruction) + } else { + Ok(InstructionOutcome::Branch(target)) + } + } + + fn run_br_table(&mut self, targets: isa::BrTargets) -> Result { + let index: u32 = self.value_stack.pop_as(); + + let dst = targets.get(index); + + Ok(InstructionOutcome::Branch(dst)) + } + + fn run_return(&mut self, drop_keep: isa::DropKeep) -> Result { + Ok(InstructionOutcome::Return(drop_keep)) + } + + fn run_call( + &mut self, + context: &mut FunctionContext, + func_idx: u32, + ) -> Result { + let func = context + .module() + .func_by_index(func_idx) + .expect("Due to validation func should exists"); + Ok(InstructionOutcome::ExecuteCall(func)) + } + + fn run_call_indirect( + &mut self, + context: &mut FunctionContext, + signature_idx: u32, + ) -> Result { + let table_func_idx: u32 = self.value_stack.pop_as(); + let table = context + .module() + .table_by_index(DEFAULT_TABLE_INDEX) + .expect("Due to validation table should exists"); + let func_ref = table + .get(table_func_idx) + .map_err(|_| TrapKind::TableAccessOutOfBounds)? + .ok_or_else(|| TrapKind::ElemUninitialized)?; + + { + let actual_function_type = func_ref.signature(); + let required_function_type = context + .module() + .signature_by_index(signature_idx) + .expect("Due to validation type should exists"); + + if &*required_function_type != actual_function_type { + return Err(TrapKind::UnexpectedSignature); + } + } + + Ok(InstructionOutcome::ExecuteCall(func_ref)) + } + + fn run_drop(&mut self) -> Result { + let _ = self.value_stack.pop(); + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_select(&mut self) -> Result { + let (left, mid, right) = self.value_stack.pop_triple(); + + let condition = <_>::from_runtime_value_internal(right); + let val = if condition { left } else { mid }; + self.value_stack.push(val)?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_get_local(&mut self, index: u32) -> Result { + let val = *self.value_stack.pick_mut(index as usize); + self.value_stack.push(val)?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_set_local(&mut self, index: u32) -> Result { + let val = self.value_stack.pop(); + *self.value_stack.pick_mut(index as usize) = val; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_tee_local(&mut self, index: u32) -> Result { + let val = self.value_stack.top().clone(); + *self.value_stack.pick_mut(index as usize) = val; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_get_global( + &mut self, + context: &mut FunctionContext, + index: u32, + ) -> Result { + let global = context + .module() + .global_by_index(index) + .expect("Due to validation global should exists"); + let val = global.get(); + self.value_stack.push(val.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_set_global( + &mut self, + context: &mut FunctionContext, + index: u32, + ) -> Result { + let val = self.value_stack.pop(); + let global = context + .module() + .global_by_index(index) + .expect("Due to validation global should exists"); + global + .set(val.with_type(global.value_type())) + .expect("Due to validation set to a global should succeed"); + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_load( + &mut self, + context: &mut FunctionContext, + offset: u32, + ) -> Result + where + RuntimeValueInternal: From, + T: LittleEndianConvert, + { + let raw_address = self.value_stack.pop_as(); + let address = effective_address(offset, raw_address)?; + let m = context + .memory() + .expect("Due to validation memory should exists"); + let n: T = m + .get_value(address) + .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; + self.value_stack.push(n.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_load_extend( + &mut self, + context: &mut FunctionContext, + offset: u32, + ) -> Result + where + T: ExtendInto, + RuntimeValueInternal: From, + T: LittleEndianConvert, + { + let raw_address = self.value_stack.pop_as(); + let address = effective_address(offset, raw_address)?; + let m = context + .memory() + .expect("Due to validation memory should exists"); + let v: T = m + .get_value(address) + .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; + let stack_value: U = v.extend_into(); + self.value_stack + .push(stack_value.into()) + .map_err(Into::into) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_store( + &mut self, + context: &mut FunctionContext, + offset: u32, + ) -> Result + where + T: FromRuntimeValueInternal, + T: LittleEndianConvert, + { + let stack_value = self.value_stack.pop_as::(); + let raw_address = self.value_stack.pop_as::(); + let address = effective_address(offset, raw_address)?; + + let m = context + .memory() + .expect("Due to validation memory should exists"); + m.set_value(address, stack_value) + .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_store_wrap( + &mut self, + context: &mut FunctionContext, + offset: u32, + ) -> Result + where + T: FromRuntimeValueInternal, + T: WrapInto, + U: LittleEndianConvert, + { + let stack_value: T = <_>::from_runtime_value_internal(self.value_stack.pop()); + let stack_value = stack_value.wrap_into(); + let raw_address = self.value_stack.pop_as::(); + let address = effective_address(offset, raw_address)?; + let m = context + .memory() + .expect("Due to validation memory should exists"); + m.set_value(address, stack_value) + .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_current_memory( + &mut self, + context: &mut FunctionContext, + ) -> Result { + let m = context + .memory() + .expect("Due to validation memory should exists"); + let s = m.current_size().0; + self.value_stack.push(RuntimeValueInternal(s as _))?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_grow_memory( + &mut self, + context: &mut FunctionContext, + ) -> Result { + let pages: u32 = self.value_stack.pop_as(); + let m = context + .memory() + .expect("Due to validation memory should exists"); + let m = match m.grow(Pages(pages as usize)) { + Ok(Pages(new_size)) => new_size as u32, + Err(_) => u32::MAX, // Returns -1 (or 0xFFFFFFFF) in case of error. + }; + self.value_stack.push(RuntimeValueInternal(m as _))?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_const(&mut self, val: RuntimeValue) -> Result { + self.value_stack + .push(val.into()) + .map_err(Into::into) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_relop(&mut self, f: F) -> Result + where + T: FromRuntimeValueInternal, + F: FnOnce(T, T) -> bool, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let v = if f(left, right) { + RuntimeValueInternal(1) + } else { + RuntimeValueInternal(0) + }; + self.value_stack.push(v)?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_eqz(&mut self) -> Result + where + T: FromRuntimeValueInternal, + T: PartialEq + Default, + { + let v = self.value_stack.pop_as::(); + let v = RuntimeValueInternal(if v == Default::default() { 1 } else { 0 }); + self.value_stack.push(v)?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_eq(&mut self) -> Result + where + T: FromRuntimeValueInternal + PartialEq, + { + self.run_relop(|left: T, right: T| left == right) + } + + fn run_ne(&mut self) -> Result + where + T: FromRuntimeValueInternal + PartialEq, + { + self.run_relop(|left: T, right: T| left != right) + } + + fn run_lt(&mut self) -> Result + where + T: FromRuntimeValueInternal + PartialOrd, + { + self.run_relop(|left: T, right: T| left < right) + } + + fn run_gt(&mut self) -> Result + where + T: FromRuntimeValueInternal + PartialOrd, + { + self.run_relop(|left: T, right: T| left > right) + } + + fn run_lte(&mut self) -> Result + where + T: FromRuntimeValueInternal + PartialOrd, + { + self.run_relop(|left: T, right: T| left <= right) + } + + fn run_gte(&mut self) -> Result + where + T: FromRuntimeValueInternal + PartialOrd, + { + self.run_relop(|left: T, right: T| left >= right) + } + + fn run_unop(&mut self, f: F) -> Result + where + F: FnOnce(T) -> U, + T: FromRuntimeValueInternal, + RuntimeValueInternal: From, + { + let v = self.value_stack.pop_as::(); + let v = f(v); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_clz(&mut self) -> Result + where + RuntimeValueInternal: From, + T: Integer + FromRuntimeValueInternal, + { + self.run_unop(|v: T| v.leading_zeros()) + } + + fn run_ctz(&mut self) -> Result + where + RuntimeValueInternal: From, + T: Integer + FromRuntimeValueInternal, + { + self.run_unop(|v: T| v.trailing_zeros()) + } + + fn run_popcnt(&mut self) -> Result + where + RuntimeValueInternal: From, + T: Integer + FromRuntimeValueInternal, + { + self.run_unop(|v: T| v.count_ones()) + } + + fn run_add(&mut self) -> Result + where + RuntimeValueInternal: From, + T: ArithmeticOps + FromRuntimeValueInternal, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let v = left.add(right); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_sub(&mut self) -> Result + where + RuntimeValueInternal: From, + T: ArithmeticOps + FromRuntimeValueInternal, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let v = left.sub(right); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_mul(&mut self) -> Result + where + RuntimeValueInternal: From, + T: ArithmeticOps + FromRuntimeValueInternal, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let v = left.mul(right); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_div(&mut self) -> Result + where + RuntimeValueInternal: From, + T: TransmuteInto + FromRuntimeValueInternal, + U: ArithmeticOps + TransmuteInto, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let (left, right) = (left.transmute_into(), right.transmute_into()); + let v = left.div(right)?; + let v = v.transmute_into(); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_rem(&mut self) -> Result + where + RuntimeValueInternal: From, + T: TransmuteInto + FromRuntimeValueInternal, + U: Integer + TransmuteInto, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let (left, right) = (left.transmute_into(), right.transmute_into()); + let v = left.rem(right)?; + let v = v.transmute_into(); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_and(&mut self) -> Result + where + RuntimeValueInternal: From<::Output>, + T: ops::BitAnd + FromRuntimeValueInternal, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let v = left.bitand(right); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_or(&mut self) -> Result + where + RuntimeValueInternal: From<::Output>, + T: ops::BitOr + FromRuntimeValueInternal, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let v = left.bitor(right); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_xor(&mut self) -> Result + where + RuntimeValueInternal: From<::Output>, + T: ops::BitXor + FromRuntimeValueInternal, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let v = left.bitxor(right); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_shl(&mut self, mask: T) -> Result + where + RuntimeValueInternal: From<>::Output>, + T: ops::Shl + ops::BitAnd + FromRuntimeValueInternal, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let v = left.shl(right & mask); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_shr(&mut self, mask: U) -> Result + where + RuntimeValueInternal: From, + T: TransmuteInto + FromRuntimeValueInternal, + U: ops::Shr + ops::BitAnd, + >::Output: TransmuteInto, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let (left, right) = (left.transmute_into(), right.transmute_into()); + let v = left.shr(right & mask); + let v = v.transmute_into(); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_rotl(&mut self) -> Result + where + RuntimeValueInternal: From, + T: Integer + FromRuntimeValueInternal, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let v = left.rotl(right); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_rotr(&mut self) -> Result + where + RuntimeValueInternal: From, + T: Integer + FromRuntimeValueInternal, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let v = left.rotr(right); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_abs(&mut self) -> Result + where + RuntimeValueInternal: From, + T: Float + FromRuntimeValueInternal, + { + self.run_unop(|v: T| v.abs()) + } + + fn run_neg(&mut self) -> Result + where + RuntimeValueInternal: From<::Output>, + T: ops::Neg + FromRuntimeValueInternal, + { + self.run_unop(|v: T| v.neg()) + } + + fn run_ceil(&mut self) -> Result + where + RuntimeValueInternal: From, + T: Float + FromRuntimeValueInternal, + { + self.run_unop(|v: T| v.ceil()) + } + + fn run_floor(&mut self) -> Result + where + RuntimeValueInternal: From, + T: Float + FromRuntimeValueInternal, + { + self.run_unop(|v: T| v.floor()) + } + + fn run_trunc(&mut self) -> Result + where + RuntimeValueInternal: From, + T: Float + FromRuntimeValueInternal, + { + self.run_unop(|v: T| v.trunc()) + } + + fn run_nearest(&mut self) -> Result + where + RuntimeValueInternal: From, + T: Float + FromRuntimeValueInternal, + { + self.run_unop(|v: T| v.nearest()) + } + + fn run_sqrt(&mut self) -> Result + where + RuntimeValueInternal: From, + T: Float + FromRuntimeValueInternal, + { + self.run_unop(|v: T| v.sqrt()) + } + + fn run_min(&mut self) -> Result + where + RuntimeValueInternal: From, + T: Float + FromRuntimeValueInternal, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let v = left.min(right); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_max(&mut self) -> Result + where + RuntimeValueInternal: From, + T: Float + FromRuntimeValueInternal, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let v = left.max(right); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_copysign(&mut self) -> Result + where + RuntimeValueInternal: From, + T: Float + FromRuntimeValueInternal, + { + let (left, right) = self.value_stack.pop_pair_as::(); + let v = left.copysign(right); + self.value_stack.push(v.into())?; + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_wrap(&mut self) -> Result + where + RuntimeValueInternal: From, + T: WrapInto + FromRuntimeValueInternal, + { + self.run_unop(|v: T| v.wrap_into()) + } + + fn run_trunc_to_int(&mut self) -> Result + where + RuntimeValueInternal: From, + T: TryTruncateInto + FromRuntimeValueInternal, + U: TransmuteInto, + { + let v = self.value_stack.pop_as::(); + + v.try_truncate_into() + .map(|v| v.transmute_into()) + .map(|v| self.value_stack.push(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_extend(&mut self) -> Result + where + RuntimeValueInternal: From, + T: ExtendInto + FromRuntimeValueInternal, + U: TransmuteInto, + { + let v = self.value_stack.pop_as::(); + + let v = v.extend_into().transmute_into(); + self.value_stack.push(v.into())?; + + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_reinterpret(&mut self) -> Result + where + RuntimeValueInternal: From, + T: FromRuntimeValueInternal, + T: TransmuteInto, + { + let v = self.value_stack.pop_as::(); + + let v = v.transmute_into(); + self.value_stack.push(v.into())?; + + Ok(InstructionOutcome::RunNextInstruction) + } } /// Function execution context. struct FunctionContext { - /// Is context initialized. - pub is_initialized: bool, - /// Internal function reference. - pub function: FuncRef, - pub module: ModuleRef, - pub memory: Option, - /// Current instruction position. - pub position: u32, + /// Is context initialized. + pub is_initialized: bool, + /// Internal function reference. + pub function: FuncRef, + pub module: ModuleRef, + pub memory: Option, + /// Current instruction position. + pub position: u32, } impl FunctionContext { - pub fn new(function: FuncRef) -> Self { - let module = match function.as_internal() { + pub fn new(function: FuncRef) -> Self { + let module = match function.as_internal() { FuncInstanceInternal::Internal { module, .. } => module.upgrade().expect("module deallocated"), FuncInstanceInternal::Host { .. } => panic!("Host functions can't be called as internally defined functions; Thus FunctionContext can be created only with internally defined functions; qed"), }; - let memory = module.memory_by_index(DEFAULT_MEMORY_INDEX); - FunctionContext { - is_initialized: false, - function: function, - module: ModuleRef(module), - memory: memory, - position: 0, - } - } + let memory = module.memory_by_index(DEFAULT_MEMORY_INDEX); + FunctionContext { + is_initialized: false, + function: function, + module: ModuleRef(module), + memory: memory, + position: 0, + } + } - pub fn is_initialized(&self) -> bool { - self.is_initialized - } + pub fn is_initialized(&self) -> bool { + self.is_initialized + } - pub fn initialize( - &mut self, - locals: &[Local], - value_stack: &mut ValueStack, - ) -> Result<(), TrapKind> { - debug_assert!(!self.is_initialized); + pub fn initialize( + &mut self, + locals: &[Local], + value_stack: &mut ValueStack, + ) -> Result<(), TrapKind> { + debug_assert!(!self.is_initialized); - let num_locals = locals.iter().map(|l| l.count() as usize).sum(); - let locals = vec![Default::default(); num_locals]; + let num_locals = locals.iter().map(|l| l.count() as usize).sum(); + let locals = vec![Default::default(); num_locals]; - // TODO: Replace with extend. - for local in locals { - value_stack - .push(local) - .map_err(|_| TrapKind::StackOverflow)?; - } + // TODO: Replace with extend. + for local in locals { + value_stack + .push(local) + .map_err(|_| TrapKind::StackOverflow)?; + } - self.is_initialized = true; - Ok(()) - } + self.is_initialized = true; + Ok(()) + } - pub fn module(&self) -> ModuleRef { - self.module.clone() - } + pub fn module(&self) -> ModuleRef { + self.module.clone() + } - pub fn memory(&self) -> Option<&MemoryRef> { - self.memory.as_ref() - } + pub fn memory(&self) -> Option<&MemoryRef> { + self.memory.as_ref() + } } impl fmt::Debug for FunctionContext { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "FunctionContext") - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "FunctionContext") + } } fn effective_address(address: u32, offset: u32) -> Result { - match offset.checked_add(address) { - None => Err(TrapKind::MemoryAccessOutOfBounds), - Some(address) => Ok(address), - } + match offset.checked_add(address) { + None => Err(TrapKind::MemoryAccessOutOfBounds), + Some(address) => Ok(address), + } } fn prepare_function_args( - signature: &Signature, - caller_stack: &mut ValueStack, + signature: &Signature, + caller_stack: &mut ValueStack, ) -> Vec { - let mut out = signature - .params() - .iter() - .rev() - .map(|¶m_ty| caller_stack.pop().with_type(param_ty)) - .collect::>(); - out.reverse(); - out + let mut out = signature + .params() + .iter() + .rev() + .map(|¶m_ty| caller_stack.pop().with_type(param_ty)) + .collect::>(); + out.reverse(); + out } pub fn check_function_args(signature: &Signature, args: &[RuntimeValue]) -> Result<(), Trap> { - if signature.params().len() != args.len() { - return Err(TrapKind::UnexpectedSignature.into()); - } + if signature.params().len() != args.len() { + return Err(TrapKind::UnexpectedSignature.into()); + } - if signature - .params() - .iter() - .zip(args) - .any(|(expected_type, param_value)| { - let actual_type = param_value.value_type(); - &actual_type != expected_type - }) { - return Err(TrapKind::UnexpectedSignature.into()); - } + if signature + .params() + .iter() + .zip(args) + .any(|(expected_type, param_value)| { + let actual_type = param_value.value_type(); + &actual_type != expected_type + }) + { + return Err(TrapKind::UnexpectedSignature.into()); + } - Ok(()) + Ok(()) } #[derive(Debug)] struct ValueStack { - buf: Box<[RuntimeValueInternal]>, - /// Index of the first free place in the stack. - sp: usize, + buf: Box<[RuntimeValueInternal]>, + /// Index of the first free place in the stack. + sp: usize, } impl ValueStack { - fn with_limit(limit: usize) -> ValueStack { - let mut buf = Vec::new(); - buf.resize(limit, RuntimeValueInternal(0)); + fn with_limit(limit: usize) -> ValueStack { + let mut buf = Vec::new(); + buf.resize(limit, RuntimeValueInternal(0)); - ValueStack { - buf: buf.into_boxed_slice(), - sp: 0, - } - } + ValueStack { + buf: buf.into_boxed_slice(), + sp: 0, + } + } - #[inline] - fn drop_keep(&mut self, drop_keep: isa::DropKeep) { - if drop_keep.keep == isa::Keep::Single { - let top = *self.top(); - *self.pick_mut(drop_keep.drop as usize + 1) = top; - } + #[inline] + fn drop_keep(&mut self, drop_keep: isa::DropKeep) { + if drop_keep.keep == isa::Keep::Single { + let top = *self.top(); + *self.pick_mut(drop_keep.drop as usize + 1) = top; + } - let cur_stack_len = self.len(); - self.sp = cur_stack_len - drop_keep.drop as usize; - } + let cur_stack_len = self.len(); + self.sp = cur_stack_len - drop_keep.drop as usize; + } - #[inline] - fn pop_as(&mut self) -> T - where - T: FromRuntimeValueInternal, - { - let value = self.pop(); + #[inline] + fn pop_as(&mut self) -> T + where + T: FromRuntimeValueInternal, + { + let value = self.pop(); - T::from_runtime_value_internal(value) - } + T::from_runtime_value_internal(value) + } - #[inline] - fn pop_pair_as(&mut self) -> (T, T) - where - T: FromRuntimeValueInternal, - { - let right = self.pop_as(); - let left = self.pop_as(); - (left, right) - } + #[inline] + fn pop_pair_as(&mut self) -> (T, T) + where + T: FromRuntimeValueInternal, + { + let right = self.pop_as(); + let left = self.pop_as(); + (left, right) + } - #[inline] - fn pop_triple( - &mut self, - ) -> ( - RuntimeValueInternal, - RuntimeValueInternal, - RuntimeValueInternal, - ) { - let right = self.pop(); - let mid = self.pop(); - let left = self.pop(); - (left, mid, right) - } + #[inline] + fn pop_triple( + &mut self, + ) -> ( + RuntimeValueInternal, + RuntimeValueInternal, + RuntimeValueInternal, + ) { + let right = self.pop(); + let mid = self.pop(); + let left = self.pop(); + (left, mid, right) + } - #[inline] - fn top(&self) -> &RuntimeValueInternal { - self.pick(1) - } + #[inline] + fn top(&self) -> &RuntimeValueInternal { + self.pick(1) + } - fn pick(&self, depth: usize) -> &RuntimeValueInternal { - &self.buf[self.sp - depth] - } + fn pick(&self, depth: usize) -> &RuntimeValueInternal { + &self.buf[self.sp - depth] + } - #[inline] - fn pick_mut(&mut self, depth: usize) -> &mut RuntimeValueInternal { - &mut self.buf[self.sp - depth] - } + #[inline] + fn pick_mut(&mut self, depth: usize) -> &mut RuntimeValueInternal { + &mut self.buf[self.sp - depth] + } - #[inline] - fn pop(&mut self) -> RuntimeValueInternal { - self.sp -= 1; - self.buf[self.sp] - } + #[inline] + fn pop(&mut self) -> RuntimeValueInternal { + self.sp -= 1; + self.buf[self.sp] + } - #[inline] - fn push(&mut self, value: RuntimeValueInternal) -> Result<(), TrapKind> { - let cell = self - .buf - .get_mut(self.sp) - .ok_or_else(|| TrapKind::StackOverflow)?; - *cell = value; - self.sp += 1; - Ok(()) - } + #[inline] + fn push(&mut self, value: RuntimeValueInternal) -> Result<(), TrapKind> { + let cell = self + .buf + .get_mut(self.sp) + .ok_or_else(|| TrapKind::StackOverflow)?; + *cell = value; + self.sp += 1; + Ok(()) + } - #[inline] - fn len(&self) -> usize { - self.sp - } + #[inline] + fn len(&self) -> usize { + self.sp + } } diff --git a/src/table.rs b/src/table.rs index 0e42196..7d8d744 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1,13 +1,13 @@ #[allow(unused_imports)] use alloc::prelude::*; use alloc::rc::Rc; -use core::u32; -use core::fmt; use core::cell::RefCell; -use parity_wasm::elements::ResizableLimits; -use Error; +use core::fmt; +use core::u32; use func::FuncRef; use module::check_limits; +use parity_wasm::elements::ResizableLimits; +use Error; /// Reference to a table (See [`TableInstance`] for details). /// @@ -19,10 +19,10 @@ use module::check_limits; pub struct TableRef(Rc); impl ::core::ops::Deref for TableRef { - type Target = TableInstance; - fn deref(&self) -> &TableInstance { - &self.0 - } + type Target = TableInstance; + fn deref(&self) -> &TableInstance { + &self.0 + } } /// Runtime representation of a table. @@ -39,118 +39,118 @@ impl ::core::ops::Deref for TableRef { /// [`grow`]: #method.grow /// pub struct TableInstance { - /// Table limits. - limits: ResizableLimits, - /// Table memory buffer. - buffer: RefCell>>, + /// Table limits. + limits: ResizableLimits, + /// Table memory buffer. + buffer: RefCell>>, } impl fmt::Debug for TableInstance { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("TableInstance") - .field("limits", &self.limits) - .field("buffer.len", &self.buffer.borrow().len()) - .finish() - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("TableInstance") + .field("limits", &self.limits) + .field("buffer.len", &self.buffer.borrow().len()) + .finish() + } } impl TableInstance { - /// Allocate a table instance. - /// - /// The table allocated with initial size, specified by `initial_size`. - /// Maximum size can be specified by `maximum_size`. - /// - /// All table elements are allocated uninitialized. - /// - /// # Errors - /// - /// Returns `Err` if `initial_size` is greater than `maximum_size`. - pub fn alloc(initial_size: u32, maximum_size: Option) -> Result { - let table = TableInstance::new(ResizableLimits::new(initial_size, maximum_size))?; - Ok(TableRef(Rc::new(table))) - } + /// Allocate a table instance. + /// + /// The table allocated with initial size, specified by `initial_size`. + /// Maximum size can be specified by `maximum_size`. + /// + /// All table elements are allocated uninitialized. + /// + /// # Errors + /// + /// Returns `Err` if `initial_size` is greater than `maximum_size`. + pub fn alloc(initial_size: u32, maximum_size: Option) -> Result { + let table = TableInstance::new(ResizableLimits::new(initial_size, maximum_size))?; + Ok(TableRef(Rc::new(table))) + } - fn new(limits: ResizableLimits) -> Result { - check_limits(&limits)?; - Ok(TableInstance { - buffer: RefCell::new(vec![None; limits.initial() as usize]), - limits: limits, - }) - } + fn new(limits: ResizableLimits) -> Result { + check_limits(&limits)?; + Ok(TableInstance { + buffer: RefCell::new(vec![None; limits.initial() as usize]), + limits: limits, + }) + } - /// Return table limits. - pub(crate) fn limits(&self) -> &ResizableLimits { - &self.limits - } + /// Return table limits. + pub(crate) fn limits(&self) -> &ResizableLimits { + &self.limits + } - /// Returns size this table was created with. - pub fn initial_size(&self) -> u32 { - self.limits.initial() - } + /// Returns size this table was created with. + pub fn initial_size(&self) -> u32 { + self.limits.initial() + } - /// Returns maximum size `TableInstance` can grow to. - pub fn maximum_size(&self) -> Option { - self.limits.maximum() - } + /// Returns maximum size `TableInstance` can grow to. + pub fn maximum_size(&self) -> Option { + self.limits.maximum() + } - /// Returns current size of the table. - pub fn current_size(&self) -> u32 { - self.buffer.borrow().len() as u32 - } + /// Returns current size of the table. + pub fn current_size(&self) -> u32 { + self.buffer.borrow().len() as u32 + } - /// Increases the size of the table by given number of elements. - /// - /// # Errors - /// - /// Returns `Err` if tried to allocate more elements than permited by limit. - pub fn grow(&self, by: u32) -> Result<(), Error> { - let mut buffer = self.buffer.borrow_mut(); - let maximum_size = self.maximum_size().unwrap_or(u32::MAX); - let new_size = self.current_size().checked_add(by) - .and_then(|new_size| { - if maximum_size < new_size { - None - } else { - Some(new_size) - } - }) - .ok_or_else(|| - Error::Table(format!( - "Trying to grow table by {} items when there are already {} items", - by, - self.current_size(), - )) - )?; - buffer.resize(new_size as usize, None); - Ok(()) - } + /// Increases the size of the table by given number of elements. + /// + /// # Errors + /// + /// Returns `Err` if tried to allocate more elements than permited by limit. + pub fn grow(&self, by: u32) -> Result<(), Error> { + let mut buffer = self.buffer.borrow_mut(); + let maximum_size = self.maximum_size().unwrap_or(u32::MAX); + let new_size = self + .current_size() + .checked_add(by) + .and_then(|new_size| { + if maximum_size < new_size { + None + } else { + Some(new_size) + } + }) + .ok_or_else(|| { + Error::Table(format!( + "Trying to grow table by {} items when there are already {} items", + by, + self.current_size(), + )) + })?; + buffer.resize(new_size as usize, None); + Ok(()) + } - /// Get the specific value in the table - pub fn get(&self, offset: u32) -> Result, Error> { - let buffer = self.buffer.borrow(); - let buffer_len = buffer.len(); - let table_elem = buffer.get(offset as usize).cloned().ok_or_else(|| - Error::Table(format!( - "trying to read table item with index {} when there are only {} items", - offset, - buffer_len - )), - )?; - Ok(table_elem) - } + /// Get the specific value in the table + pub fn get(&self, offset: u32) -> Result, Error> { + let buffer = self.buffer.borrow(); + let buffer_len = buffer.len(); + let table_elem = buffer.get(offset as usize).cloned().ok_or_else(|| { + Error::Table(format!( + "trying to read table item with index {} when there are only {} items", + offset, buffer_len + )) + })?; + Ok(table_elem) + } - /// Set the table element to the specified function. - pub fn set(&self, offset: u32, value: Option) -> Result<(), Error> { - let mut buffer = self.buffer.borrow_mut(); - let buffer_len = buffer.len(); - let table_elem = buffer.get_mut(offset as usize).ok_or_else(|| - Error::Table(format!( - "trying to update table item with index {} when there are only {} items", - offset, - buffer_len - )) - )?; - *table_elem = value; - Ok(()) - } + /// Set the table element to the specified function. + pub fn set(&self, offset: u32, value: Option) -> Result<(), Error> { + let mut buffer = self.buffer.borrow_mut(); + let buffer_len = buffer.len(); + let table_elem = buffer.get_mut(offset as usize).ok_or_else(|| { + Error::Table(format!( + "trying to update table item with index {} when there are only {} items", + offset, buffer_len + )) + })?; + *table_elem = value; + Ok(()) + } } diff --git a/src/tests/host.rs b/src/tests/host.rs index 4ba09d2..81b2100 100644 --- a/src/tests/host.rs +++ b/src/tests/host.rs @@ -1,21 +1,21 @@ -use { - Error, Signature, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder, - MemoryInstance, MemoryRef, TableInstance, TableRef, ModuleImportResolver, ModuleInstance, ModuleRef, - RuntimeValue, RuntimeArgs, TableDescriptor, MemoryDescriptor, Trap, TrapKind, ResumableError, -}; -use types::ValueType; -use memory_units::Pages; use super::parse_wat; +use memory_units::Pages; +use types::ValueType; +use { + Error, Externals, FuncInstance, FuncRef, HostError, ImportsBuilder, MemoryDescriptor, + MemoryInstance, MemoryRef, ModuleImportResolver, ModuleInstance, ModuleRef, ResumableError, + RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap, TrapKind, +}; #[derive(Debug, Clone, PartialEq)] struct HostErrorWithCode { - error_code: u32, + error_code: u32, } impl ::core::fmt::Display for HostErrorWithCode { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> Result<(), ::core::fmt::Error> { - write!(f, "{}", self.error_code) - } + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> Result<(), ::core::fmt::Error> { + write!(f, "{}", self.error_code) + } } impl HostError for HostErrorWithCode {} @@ -30,21 +30,21 @@ impl HostError for HostErrorWithCode {} /// and `ModuleImportResolver` traits for different structures. /// See `defer_providing_externals` test for details. struct TestHost { - memory: Option, - instance: Option, + memory: Option, + instance: Option, - trap_sub_result: Option, + trap_sub_result: Option, } impl TestHost { - fn new() -> TestHost { - TestHost { - memory: Some(MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap()), - instance: None, + fn new() -> TestHost { + TestHost { + memory: Some(MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap()), + instance: None, - trap_sub_result: None, - } - } + trap_sub_result: None, + } + } } /// sub(a: i32, b: i32) -> i32 @@ -85,145 +85,153 @@ const RECURSE_FUNC_INDEX: usize = 4; const TRAP_SUB_FUNC_INDEX: usize = 5; impl Externals for TestHost { - fn invoke_index( - &mut self, - index: usize, - args: RuntimeArgs, - ) -> Result, Trap> { - match index { - SUB_FUNC_INDEX => { - let a: i32 = args.nth(0); - let b: i32 = args.nth(1); + fn invoke_index( + &mut self, + index: usize, + args: RuntimeArgs, + ) -> Result, Trap> { + match index { + SUB_FUNC_INDEX => { + let a: i32 = args.nth(0); + let b: i32 = args.nth(1); - let result: RuntimeValue = (a - b).into(); + let result: RuntimeValue = (a - b).into(); - Ok(Some(result)) - } - ERR_FUNC_INDEX => { - let error_code: u32 = args.nth(0); - let error = HostErrorWithCode { error_code }; - Err(TrapKind::Host(Box::new(error)).into()) - } - INC_MEM_FUNC_INDEX => { - let ptr: u32 = args.nth(0); + Ok(Some(result)) + } + ERR_FUNC_INDEX => { + let error_code: u32 = args.nth(0); + let error = HostErrorWithCode { error_code }; + Err(TrapKind::Host(Box::new(error)).into()) + } + INC_MEM_FUNC_INDEX => { + let ptr: u32 = args.nth(0); - let memory = self.memory.as_ref().expect( - "Function 'inc_mem' expects attached memory", - ); - let mut buf = [0u8; 1]; - memory.get_into(ptr, &mut buf).unwrap(); - buf[0] += 1; - memory.set(ptr, &buf).unwrap(); + let memory = self + .memory + .as_ref() + .expect("Function 'inc_mem' expects attached memory"); + let mut buf = [0u8; 1]; + memory.get_into(ptr, &mut buf).unwrap(); + buf[0] += 1; + memory.set(ptr, &buf).unwrap(); - Ok(None) - } - GET_MEM_FUNC_INDEX => { - let ptr: u32 = args.nth(0); + Ok(None) + } + GET_MEM_FUNC_INDEX => { + let ptr: u32 = args.nth(0); - let memory = self.memory.as_ref().expect( - "Function 'get_mem' expects attached memory", - ); - let mut buf = [0u8; 1]; - memory.get_into(ptr, &mut buf).unwrap(); + let memory = self + .memory + .as_ref() + .expect("Function 'get_mem' expects attached memory"); + let mut buf = [0u8; 1]; + memory.get_into(ptr, &mut buf).unwrap(); - Ok(Some(RuntimeValue::I32(buf[0] as i32))) - } - RECURSE_FUNC_INDEX => { - let val = args.nth_value_checked(0).expect("Exactly one argument expected"); + Ok(Some(RuntimeValue::I32(buf[0] as i32))) + } + RECURSE_FUNC_INDEX => { + let val = args + .nth_value_checked(0) + .expect("Exactly one argument expected"); - let instance = self.instance - .as_ref() - .expect("Function 'recurse' expects attached module instance") - .clone(); - let result = instance - .invoke_export("recursive", &[val.into()], self) - .expect("Failed to call 'recursive'") - .expect("expected to be Some"); + let instance = self + .instance + .as_ref() + .expect("Function 'recurse' expects attached module instance") + .clone(); + let result = instance + .invoke_export("recursive", &[val.into()], self) + .expect("Failed to call 'recursive'") + .expect("expected to be Some"); - if val.value_type() != result.value_type() { - return Err(TrapKind::Host(Box::new(HostErrorWithCode { error_code: 123 })).into()); - } - Ok(Some(result)) - } - TRAP_SUB_FUNC_INDEX => { - let a: i32 = args.nth(0); - let b: i32 = args.nth(1); + if val.value_type() != result.value_type() { + return Err( + TrapKind::Host(Box::new(HostErrorWithCode { error_code: 123 })).into(), + ); + } + Ok(Some(result)) + } + TRAP_SUB_FUNC_INDEX => { + let a: i32 = args.nth(0); + let b: i32 = args.nth(1); - let result: RuntimeValue = (a - b).into(); - self.trap_sub_result = Some(result); - return Err(TrapKind::Host(Box::new(HostErrorWithCode { error_code: 301 })).into()); - } - _ => panic!("env doesn't provide function at index {}", index), - } - } + let result: RuntimeValue = (a - b).into(); + self.trap_sub_result = Some(result); + return Err(TrapKind::Host(Box::new(HostErrorWithCode { error_code: 301 })).into()); + } + _ => panic!("env doesn't provide function at index {}", index), + } + } } impl TestHost { - fn check_signature(&self, index: usize, signature: &Signature) -> bool { - if index == RECURSE_FUNC_INDEX { - // This function requires special handling because it is polymorphic. - if signature.params().len() != 1 { - return false; - } - let param_type = signature.params()[0]; - return signature.return_type() == Some(param_type); - } + fn check_signature(&self, index: usize, signature: &Signature) -> bool { + if index == RECURSE_FUNC_INDEX { + // This function requires special handling because it is polymorphic. + if signature.params().len() != 1 { + return false; + } + let param_type = signature.params()[0]; + return signature.return_type() == Some(param_type); + } - let (params, ret_ty): (&[ValueType], Option) = match index { - SUB_FUNC_INDEX => (&[ValueType::I32, ValueType::I32], Some(ValueType::I32)), - ERR_FUNC_INDEX => (&[ValueType::I32], None), - INC_MEM_FUNC_INDEX => (&[ValueType::I32], None), - GET_MEM_FUNC_INDEX => (&[ValueType::I32], Some(ValueType::I32)), - TRAP_SUB_FUNC_INDEX => (&[ValueType::I32, ValueType::I32], Some(ValueType::I32)), - _ => return false, - }; + let (params, ret_ty): (&[ValueType], Option) = match index { + SUB_FUNC_INDEX => (&[ValueType::I32, ValueType::I32], Some(ValueType::I32)), + ERR_FUNC_INDEX => (&[ValueType::I32], None), + INC_MEM_FUNC_INDEX => (&[ValueType::I32], None), + GET_MEM_FUNC_INDEX => (&[ValueType::I32], Some(ValueType::I32)), + TRAP_SUB_FUNC_INDEX => (&[ValueType::I32, ValueType::I32], Some(ValueType::I32)), + _ => return false, + }; - signature.params() == params && signature.return_type() == ret_ty - } + signature.params() == params && signature.return_type() == ret_ty + } } impl ModuleImportResolver for TestHost { - fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result { - let index = match field_name { - "sub" => SUB_FUNC_INDEX, - "err" => ERR_FUNC_INDEX, - "inc_mem" => INC_MEM_FUNC_INDEX, - "get_mem" => GET_MEM_FUNC_INDEX, - "recurse" => RECURSE_FUNC_INDEX, - "trap_sub" => TRAP_SUB_FUNC_INDEX, - _ => { - return Err(Error::Instantiation( - format!("Export {} not found", field_name), - )) - } - }; + fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result { + let index = match field_name { + "sub" => SUB_FUNC_INDEX, + "err" => ERR_FUNC_INDEX, + "inc_mem" => INC_MEM_FUNC_INDEX, + "get_mem" => GET_MEM_FUNC_INDEX, + "recurse" => RECURSE_FUNC_INDEX, + "trap_sub" => TRAP_SUB_FUNC_INDEX, + _ => { + return Err(Error::Instantiation(format!( + "Export {} not found", + field_name + ))); + } + }; - if !self.check_signature(index, signature) { - return Err(Error::Instantiation(format!( - "Export `{}` doesnt match expected type {:?}", - field_name, - signature - ))); - } + if !self.check_signature(index, signature) { + return Err(Error::Instantiation(format!( + "Export `{}` doesnt match expected type {:?}", + field_name, signature + ))); + } - Ok(FuncInstance::alloc_host(signature.clone(), index)) - } + Ok(FuncInstance::alloc_host(signature.clone(), index)) + } - fn resolve_memory( - &self, - field_name: &str, - _memory_type: &MemoryDescriptor, - ) -> Result { - Err(Error::Instantiation( - format!("Export {} not found", field_name), - )) - } + fn resolve_memory( + &self, + field_name: &str, + _memory_type: &MemoryDescriptor, + ) -> Result { + Err(Error::Instantiation(format!( + "Export {} not found", + field_name + ))) + } } #[test] fn call_host_func() { - let module = parse_wat( - r#" + let module = parse_wat( + r#" (module (import "env" "sub" (func $sub (param i32 i32) (result i32))) @@ -235,26 +243,26 @@ fn call_host_func() { ) ) "#, - ); + ); - let mut env = TestHost::new(); + let mut env = TestHost::new(); - let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env)) - .expect("Failed to instantiate module") - .assert_no_start(); + let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env)) + .expect("Failed to instantiate module") + .assert_no_start(); - assert_eq!( - instance.invoke_export("test", &[], &mut env).expect( - "Failed to invoke 'test' function", - ), - Some(RuntimeValue::I32(-2)) - ); + assert_eq!( + instance + .invoke_export("test", &[], &mut env) + .expect("Failed to invoke 'test' function",), + Some(RuntimeValue::I32(-2)) + ); } #[test] fn resume_call_host_func() { - let module = parse_wat( - r#" + let module = parse_wat( + r#" (module (import "env" "trap_sub" (func $trap_sub (param i32 i32) (result i32))) @@ -266,38 +274,38 @@ fn resume_call_host_func() { ) ) "#, - ); + ); - let mut env = TestHost::new(); + let mut env = TestHost::new(); - let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env)) - .expect("Failed to instantiate module") - .assert_no_start(); + let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env)) + .expect("Failed to instantiate module") + .assert_no_start(); - let export = instance.export_by_name("test").unwrap(); - let func_instance = export.as_func().unwrap(); + let export = instance.export_by_name("test").unwrap(); + let func_instance = export.as_func().unwrap(); - let mut invocation = FuncInstance::invoke_resumable(&func_instance, &[]).unwrap(); - let result = invocation.start_execution(&mut env); - match result { - Err(ResumableError::Trap(_)) => {}, - _ => panic!(), - } + let mut invocation = FuncInstance::invoke_resumable(&func_instance, &[]).unwrap(); + let result = invocation.start_execution(&mut env); + match result { + Err(ResumableError::Trap(_)) => {} + _ => panic!(), + } - assert!(invocation.is_resumable()); - let trap_sub_result = env.trap_sub_result.take(); - assert_eq!( - invocation.resume_execution(trap_sub_result, &mut env).expect( - "Failed to invoke 'test' function", - ), - Some(RuntimeValue::I32(-2)) - ); + assert!(invocation.is_resumable()); + let trap_sub_result = env.trap_sub_result.take(); + assert_eq!( + invocation + .resume_execution(trap_sub_result, &mut env) + .expect("Failed to invoke 'test' function",), + Some(RuntimeValue::I32(-2)) + ); } #[test] fn host_err() { - let module = parse_wat( - r#" + let module = parse_wat( + r#" (module (import "env" "err" (func $err (param i32))) @@ -308,28 +316,30 @@ fn host_err() { ) ) "#, - ); + ); - let mut env = TestHost::new(); + let mut env = TestHost::new(); - let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env)) - .expect("Failed to instantiate module") - .assert_no_start(); + let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env)) + .expect("Failed to instantiate module") + .assert_no_start(); - let error = instance.invoke_export("test", &[], &mut env).expect_err( - "`test` expected to return error", - ); + let error = instance + .invoke_export("test", &[], &mut env) + .expect_err("`test` expected to return error"); - 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); + 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); } #[test] fn modify_mem_with_host_funcs() { - let module = parse_wat( - r#" + let module = parse_wat( + r#" (module (import "env" "inc_mem" (func $inc_mem (param i32))) ;; (import "env" "get_mem" (func $get_mem (param i32) (result i32))) @@ -343,29 +353,29 @@ fn modify_mem_with_host_funcs() { ) ) "#, - ); + ); - let mut env = TestHost::new(); + let mut env = TestHost::new(); - let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env)) - .expect("Failed to instantiate module") - .assert_no_start(); + let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env)) + .expect("Failed to instantiate module") + .assert_no_start(); - instance.invoke_export("modify_mem", &[], &mut env).expect( - "Failed to invoke 'test' function", - ); + instance + .invoke_export("modify_mem", &[], &mut env) + .expect("Failed to invoke 'test' function"); - // Check contents of memory at address 12. - let mut buf = [0u8; 1]; - env.memory.unwrap().get_into(12, &mut buf).unwrap(); + // Check contents of memory at address 12. + let mut buf = [0u8; 1]; + env.memory.unwrap().get_into(12, &mut buf).unwrap(); - assert_eq!(&buf, &[4]); + assert_eq!(&buf, &[4]); } #[test] fn pull_internal_mem_from_module() { - let module = parse_wat( - r#" + let module = parse_wat( + r#" (module (import "env" "inc_mem" (func $inc_mem (param i32))) (import "env" "get_mem" (func $get_mem (param i32) (result i32))) @@ -382,39 +392,39 @@ fn pull_internal_mem_from_module() { ) ) "#, - ); + ); - let mut env = TestHost { - memory: None, - instance: None, + let mut env = TestHost { + memory: None, + instance: None, - trap_sub_result: None, - }; + trap_sub_result: None, + }; - let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env)) - .expect("Failed to instantiate module") - .assert_no_start(); + let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env)) + .expect("Failed to instantiate module") + .assert_no_start(); - // Get memory instance exported by name 'mem' from the module instance. - let internal_mem = instance - .export_by_name("mem") - .expect("Module expected to have 'mem' export") - .as_memory() - .cloned() - .expect("'mem' export should be a memory"); + // Get memory instance exported by name 'mem' from the module instance. + let internal_mem = instance + .export_by_name("mem") + .expect("Module expected to have 'mem' export") + .as_memory() + .cloned() + .expect("'mem' export should be a memory"); - env.memory = Some(internal_mem); + env.memory = Some(internal_mem); - assert_eq!( - instance.invoke_export("test", &[], &mut env).unwrap(), - Some(RuntimeValue::I32(1)) - ); + assert_eq!( + instance.invoke_export("test", &[], &mut env).unwrap(), + Some(RuntimeValue::I32(1)) + ); } #[test] fn recursion() { - let module = parse_wat( - r#" + let module = parse_wat( + r#" (module ;; Import 'recurse' function. Upon a call it will call back inside ;; this module, namely to function 'recursive' defined below. @@ -438,101 +448,98 @@ fn recursion() { ) ) "#, - ); + ); - let mut env = TestHost::new(); + let mut env = TestHost::new(); - let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env)) - .expect("Failed to instantiate module") - .assert_no_start(); + let instance = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env)) + .expect("Failed to instantiate module") + .assert_no_start(); - // Put instance into the env, because $recurse function expects - // attached module instance. - env.instance = Some(instance.clone()); + // Put instance into the env, because $recurse function expects + // attached module instance. + env.instance = Some(instance.clone()); - assert_eq!( - instance.invoke_export("test", &[], &mut env).expect( - "Failed to invoke 'test' function", - ), - // 363 = 321 + 42 - Some(RuntimeValue::I64(363)) - ); + assert_eq!( + instance + .invoke_export("test", &[], &mut env) + .expect("Failed to invoke 'test' function",), + // 363 = 321 + 42 + Some(RuntimeValue::I64(363)) + ); } #[test] fn defer_providing_externals() { - const INC_FUNC_INDEX: usize = 0; + const INC_FUNC_INDEX: usize = 0; - /// `HostImportResolver` will be passed at instantiation time. - /// - /// Main purpose of this struct is to statsify imports of - /// the module being instantiated. - struct HostImportResolver { - mem: MemoryRef, - } + /// `HostImportResolver` will be passed at instantiation time. + /// + /// Main purpose of this struct is to statsify imports of + /// the module being instantiated. + struct HostImportResolver { + mem: MemoryRef, + } - impl ModuleImportResolver for HostImportResolver { - fn resolve_func( - &self, - field_name: &str, - signature: &Signature, - ) -> Result { - if field_name != "inc" { - return Err(Error::Instantiation( - format!("Export {} not found", field_name), - )); - } - if signature.params() != &[ValueType::I32] || signature.return_type() != None { - return Err(Error::Instantiation(format!( - "Export `{}` doesnt match expected type {:?}", - field_name, - signature - ))); - } + impl ModuleImportResolver for HostImportResolver { + fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result { + if field_name != "inc" { + return Err(Error::Instantiation(format!( + "Export {} not found", + field_name + ))); + } + if signature.params() != &[ValueType::I32] || signature.return_type() != None { + return Err(Error::Instantiation(format!( + "Export `{}` doesnt match expected type {:?}", + field_name, signature + ))); + } - Ok(FuncInstance::alloc_host(signature.clone(), INC_FUNC_INDEX)) - } + Ok(FuncInstance::alloc_host(signature.clone(), INC_FUNC_INDEX)) + } - fn resolve_memory( - &self, - field_name: &str, - _memory_type: &MemoryDescriptor, - ) -> Result { - if field_name == "mem" { - Ok(self.mem.clone()) - } else { - Err(Error::Instantiation( - format!("Export {} not found", field_name), - )) - } - } - } + fn resolve_memory( + &self, + field_name: &str, + _memory_type: &MemoryDescriptor, + ) -> Result { + if field_name == "mem" { + Ok(self.mem.clone()) + } else { + Err(Error::Instantiation(format!( + "Export {} not found", + field_name + ))) + } + } + } - /// This struct implements external functions that can be called - /// by wasm module. - struct HostExternals<'a> { - acc: &'a mut u32, - } + /// This struct implements external functions that can be called + /// by wasm module. + struct HostExternals<'a> { + acc: &'a mut u32, + } - impl<'a> Externals for HostExternals<'a> { - fn invoke_index( - &mut self, - index: usize, - args: RuntimeArgs, - ) -> Result, Trap> { - match index { - INC_FUNC_INDEX => { - let a = args.nth::(0); - *self.acc += a; - Ok(None) - } - _ => panic!("env module doesn't provide function at index {}", index), - } - } - } + impl<'a> Externals for HostExternals<'a> { + fn invoke_index( + &mut self, + index: usize, + args: RuntimeArgs, + ) -> Result, Trap> { + match index { + INC_FUNC_INDEX => { + let a = args.nth::(0); + *self.acc += a; + Ok(None) + } + _ => panic!("env module doesn't provide function at index {}", index), + } + } + } - let module = parse_wat( - r#" + let module = parse_wat( + r#" (module ;; Just to require 'mem' from 'host'. (import "host" "mem" (memory 1)) @@ -543,107 +550,103 @@ fn defer_providing_externals() { ) ) "#, - ); + ); - // Create HostImportResolver with some initialized memory instance. - // This memory instance will be provided as 'mem' export. - let host_import_resolver = - HostImportResolver { mem: MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap() }; + // Create HostImportResolver with some initialized memory instance. + // This memory instance will be provided as 'mem' export. + let host_import_resolver = HostImportResolver { + mem: MemoryInstance::alloc(Pages(1), Some(Pages(1))).unwrap(), + }; - // Instantiate module with `host_import_resolver` as import resolver for "host" module. - let instance = ModuleInstance::new( - &module, - &ImportsBuilder::new().with_resolver("host", &host_import_resolver), - ).expect("Failed to instantiate module") - .assert_no_start(); + // Instantiate module with `host_import_resolver` as import resolver for "host" module. + let instance = ModuleInstance::new( + &module, + &ImportsBuilder::new().with_resolver("host", &host_import_resolver), + ) + .expect("Failed to instantiate module") + .assert_no_start(); - let mut acc = 89; - { - let mut host_externals = HostExternals { acc: &mut acc }; + let mut acc = 89; + { + let mut host_externals = HostExternals { acc: &mut acc }; - instance - .invoke_export("test", &[], &mut host_externals) - .unwrap(); // acc += 1; - instance - .invoke_export("test", &[], &mut host_externals) - .unwrap(); // acc += 1; - } - assert_eq!(acc, 91); + instance + .invoke_export("test", &[], &mut host_externals) + .unwrap(); // acc += 1; + instance + .invoke_export("test", &[], &mut host_externals) + .unwrap(); // acc += 1; + } + assert_eq!(acc, 91); } #[test] fn two_envs_one_externals() { - const PRIVILEGED_FUNC_INDEX: usize = 0; - const ORDINARY_FUNC_INDEX: usize = 1; + const PRIVILEGED_FUNC_INDEX: usize = 0; + const ORDINARY_FUNC_INDEX: usize = 1; - struct HostExternals; + struct HostExternals; - impl Externals for HostExternals { - fn invoke_index( - &mut self, - index: usize, - _args: RuntimeArgs, - ) -> Result, Trap> { - match index { - PRIVILEGED_FUNC_INDEX => { - println!("privileged!"); - Ok(None) - } - ORDINARY_FUNC_INDEX => Ok(None), - _ => panic!("env module doesn't provide function at index {}", index), - } - } - } + impl Externals for HostExternals { + fn invoke_index( + &mut self, + index: usize, + _args: RuntimeArgs, + ) -> Result, Trap> { + match index { + PRIVILEGED_FUNC_INDEX => { + println!("privileged!"); + Ok(None) + } + ORDINARY_FUNC_INDEX => Ok(None), + _ => panic!("env module doesn't provide function at index {}", index), + } + } + } - struct PrivilegedResolver; - struct OrdinaryResolver; + struct PrivilegedResolver; + struct OrdinaryResolver; - impl ModuleImportResolver for PrivilegedResolver { - fn resolve_func( - &self, - field_name: &str, - signature: &Signature, - ) -> Result { - let index = match field_name { - "ordinary" => ORDINARY_FUNC_INDEX, - "privileged" => PRIVILEGED_FUNC_INDEX, - _ => { - return Err(Error::Instantiation( - format!("Export {} not found", field_name), - )) - } - }; + impl ModuleImportResolver for PrivilegedResolver { + fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result { + let index = match field_name { + "ordinary" => ORDINARY_FUNC_INDEX, + "privileged" => PRIVILEGED_FUNC_INDEX, + _ => { + return Err(Error::Instantiation(format!( + "Export {} not found", + field_name + ))); + } + }; - Ok(FuncInstance::alloc_host(signature.clone(), index)) - } - } + Ok(FuncInstance::alloc_host(signature.clone(), index)) + } + } - impl ModuleImportResolver for OrdinaryResolver { - fn resolve_func( - &self, - field_name: &str, - signature: &Signature, - ) -> Result { - let index = match field_name { - "ordinary" => ORDINARY_FUNC_INDEX, - "privileged" => { - return Err(Error::Instantiation( - "'priveleged' can be imported only in privileged context".into(), - )) - } - _ => { - return Err(Error::Instantiation( - format!("Export {} not found", field_name), - )) - } - }; + impl ModuleImportResolver for OrdinaryResolver { + fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result { + let index = match field_name { + "ordinary" => ORDINARY_FUNC_INDEX, + "privileged" => { + return Err(Error::Instantiation( + "'priveleged' can be imported only in privileged context".into(), + )); + } + _ => { + return Err(Error::Instantiation(format!( + "Export {} not found", + field_name + ))); + } + }; - Ok(FuncInstance::alloc_host(signature.clone(), index)) - } - } + Ok(FuncInstance::alloc_host(signature.clone(), index)) + } + } - let trusted_module = parse_wat( - r#" + let trusted_module = parse_wat( + r#" (module ;; Trusted module can import both ordinary and privileged functions. (import "env" "ordinary" (func $ordinary)) @@ -654,10 +657,10 @@ fn two_envs_one_externals() { ) ) "#, - ); + ); - let untrusted_module = parse_wat( - r#" + let untrusted_module = parse_wat( + r#" (module ;; Untrusted module can import only ordinary functions. (import "env" "ordinary" (func $ordinary)) @@ -668,106 +671,107 @@ fn two_envs_one_externals() { ) ) "#, - ); + ); - let trusted_instance = ModuleInstance::new( - &trusted_module, - &ImportsBuilder::new().with_resolver("env", &PrivilegedResolver), - ).expect("Failed to instantiate module") - .assert_no_start(); + let trusted_instance = ModuleInstance::new( + &trusted_module, + &ImportsBuilder::new().with_resolver("env", &PrivilegedResolver), + ) + .expect("Failed to instantiate module") + .assert_no_start(); - let untrusted_instance = ModuleInstance::new( - &untrusted_module, - &ImportsBuilder::new() - .with_resolver("env", &OrdinaryResolver) - .with_resolver("trusted", &trusted_instance), - ).expect("Failed to instantiate module") - .assert_no_start(); + let untrusted_instance = ModuleInstance::new( + &untrusted_module, + &ImportsBuilder::new() + .with_resolver("env", &OrdinaryResolver) + .with_resolver("trusted", &trusted_instance), + ) + .expect("Failed to instantiate module") + .assert_no_start(); - untrusted_instance - .invoke_export("test", &[], &mut HostExternals) - .expect("Failed to invoke 'test' function"); + untrusted_instance + .invoke_export("test", &[], &mut HostExternals) + .expect("Failed to invoke 'test' function"); } #[test] fn dynamically_add_host_func() { - const ADD_FUNC_FUNC_INDEX: usize = 0; + const ADD_FUNC_FUNC_INDEX: usize = 0; - struct HostExternals { - table: TableRef, - added_funcs: u32, - } + struct HostExternals { + table: TableRef, + added_funcs: u32, + } - impl Externals for HostExternals { - fn invoke_index( - &mut self, - index: usize, - _args: RuntimeArgs, - ) -> Result, Trap> { - match index { - ADD_FUNC_FUNC_INDEX => { - // Allocate indicies for the new function. - // host_func_index is in host index space, and first index is occupied by ADD_FUNC_FUNC_INDEX. - let table_index = self.added_funcs; - let host_func_index = table_index + 1; - self.added_funcs += 1; + impl Externals for HostExternals { + fn invoke_index( + &mut self, + index: usize, + _args: RuntimeArgs, + ) -> Result, Trap> { + match index { + ADD_FUNC_FUNC_INDEX => { + // Allocate indicies for the new function. + // host_func_index is in host index space, and first index is occupied by ADD_FUNC_FUNC_INDEX. + let table_index = self.added_funcs; + let host_func_index = table_index + 1; + self.added_funcs += 1; - let added_func = FuncInstance::alloc_host( - Signature::new(&[][..], Some(ValueType::I32)), - host_func_index as usize, - ); - self.table.set(table_index, Some(added_func)) - .map_err(|_| TrapKind::TableAccessOutOfBounds)?; + let added_func = FuncInstance::alloc_host( + Signature::new(&[][..], Some(ValueType::I32)), + host_func_index as usize, + ); + self.table + .set(table_index, Some(added_func)) + .map_err(|_| TrapKind::TableAccessOutOfBounds)?; - Ok(Some(RuntimeValue::I32(table_index as i32))) - } - index if index as u32 <= self.added_funcs => { - Ok(Some(RuntimeValue::I32(index as i32))) - } - _ => panic!("'env' module doesn't provide function at index {}", index), - } - } - } + Ok(Some(RuntimeValue::I32(table_index as i32))) + } + index if index as u32 <= self.added_funcs => { + Ok(Some(RuntimeValue::I32(index as i32))) + } + _ => panic!("'env' module doesn't provide function at index {}", index), + } + } + } - impl ModuleImportResolver for HostExternals { - fn resolve_func( - &self, - field_name: &str, - signature: &Signature, - ) -> Result { - let index = match field_name { - "add_func" => ADD_FUNC_FUNC_INDEX, - _ => { - return Err(Error::Instantiation( - format!("Export {} not found", field_name), - )) - } - }; - Ok(FuncInstance::alloc_host(signature.clone(), index)) - } + impl ModuleImportResolver for HostExternals { + fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result { + let index = match field_name { + "add_func" => ADD_FUNC_FUNC_INDEX, + _ => { + return Err(Error::Instantiation(format!( + "Export {} not found", + field_name + ))); + } + }; + Ok(FuncInstance::alloc_host(signature.clone(), index)) + } - fn resolve_table( - &self, - field_name: &str, - _table_type: &TableDescriptor, - ) -> Result { - if field_name == "table" { - Ok(self.table.clone()) - } else { - Err(Error::Instantiation( - format!("Export {} not found", field_name), - )) - } - } - } + fn resolve_table( + &self, + field_name: &str, + _table_type: &TableDescriptor, + ) -> Result { + if field_name == "table" { + Ok(self.table.clone()) + } else { + Err(Error::Instantiation(format!( + "Export {} not found", + field_name + ))) + } + } + } - let mut host_externals = HostExternals { - table: TableInstance::alloc(10, None).unwrap(), - added_funcs: 0, - }; + let mut host_externals = HostExternals { + table: TableInstance::alloc(10, None).unwrap(), + added_funcs: 0, + }; - let module = parse_wat( - r#" + let module = parse_wat( + r#" (module (type $t0 (func (result i32))) (import "env" "add_func" (func $add_func (result i32))) @@ -783,18 +787,19 @@ fn dynamically_add_host_func() { ) ) "#, - ); + ); - let instance = ModuleInstance::new( - &module, - &ImportsBuilder::new().with_resolver("env", &host_externals), - ).expect("Failed to instantiate module") - .assert_no_start(); + let instance = ModuleInstance::new( + &module, + &ImportsBuilder::new().with_resolver("env", &host_externals), + ) + .expect("Failed to instantiate module") + .assert_no_start(); - assert_eq!( - instance - .invoke_export("test", &[], &mut host_externals) - .expect("Failed to invoke 'test' function"), - Some(RuntimeValue::I32(2)) - ); + assert_eq!( + instance + .invoke_export("test", &[], &mut host_externals) + .expect("Failed to invoke 'test' function"), + Some(RuntimeValue::I32(2)) + ); } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 193e274..a60ce73 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,5 +1,5 @@ use wabt; -use {Module}; +use Module; mod host; mod wasm; @@ -12,25 +12,31 @@ fn assert_std_err_impl() {} #[test] fn assert_error_properties() { - assert_send::(); - assert_sync::(); - assert_std_err_impl::(); + assert_send::(); + assert_sync::(); + assert_std_err_impl::(); } /// Test that converting an u32 (u64) that does not fit in an i32 (i64) /// to a RuntimeValue and back works as expected and the number remains unchanged. #[test] fn unsigned_to_runtime_value() { - use super::RuntimeValue; + use super::RuntimeValue; - let overflow_i32: u32 = ::core::i32::MAX as u32 + 1; - assert_eq!(RuntimeValue::from(overflow_i32).try_into::().unwrap(), overflow_i32); + let overflow_i32: u32 = ::core::i32::MAX as u32 + 1; + assert_eq!( + RuntimeValue::from(overflow_i32).try_into::().unwrap(), + overflow_i32 + ); - let overflow_i64: u64 = ::core::i64::MAX as u64 + 1; - assert_eq!(RuntimeValue::from(overflow_i64).try_into::().unwrap(), overflow_i64); + let overflow_i64: u64 = ::core::i64::MAX as u64 + 1; + assert_eq!( + RuntimeValue::from(overflow_i64).try_into::().unwrap(), + overflow_i64 + ); } pub fn parse_wat(source: &str) -> Module { - let wasm_binary = wabt::wat2wasm(source).expect("Failed to parse wat source"); - Module::from_buffer(wasm_binary).expect("Failed to load parsed module") + let wasm_binary = wabt::wat2wasm(source).expect("Failed to parse wat source"); + Module::from_buffer(wasm_binary).expect("Failed to load parsed module") } diff --git a/src/tests/wasm.rs b/src/tests/wasm.rs index c05d5f8..07efa51 100644 --- a/src/tests/wasm.rs +++ b/src/tests/wasm.rs @@ -1,110 +1,113 @@ -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; +use { + Error, FuncRef, GlobalDescriptor, GlobalInstance, GlobalRef, ImportsBuilder, MemoryDescriptor, + MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, NopExternals, + RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, +}; struct Env { - table_base: GlobalRef, - memory_base: GlobalRef, - memory: MemoryRef, - table: TableRef, + 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(), - } - } + 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 { - Err(Error::Instantiation( - "env module doesn't provide any functions".into(), - )) - } + fn resolve_func(&self, _field_name: &str, _func_type: &Signature) -> Result { + Err(Error::Instantiation( + "env module doesn't provide any functions".into(), + )) + } - fn resolve_global( - &self, - field_name: &str, - _global_type: &GlobalDescriptor, - ) -> Result { - 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_global( + &self, + field_name: &str, + _global_type: &GlobalDescriptor, + ) -> Result { + 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 { - match field_name { - "memory" => Ok(self.memory.clone()), - _ => Err(Error::Instantiation(format!( - "env module doesn't provide memory '{}'", - field_name - ))), - } - } + fn resolve_memory( + &self, + field_name: &str, + _memory_type: &MemoryDescriptor, + ) -> Result { + 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 { - match field_name { - "table" => Ok(self.table.clone()), - _ => Err(Error::Instantiation( - format!("env module doesn't provide table '{}'", field_name), - )), - } - } + fn resolve_table( + &self, + field_name: &str, + _table_type: &TableDescriptor, + ) -> Result { + 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() + 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"; + // 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 module = load_from_file(WASM_FILE); - let env = Env::new(); + let env = Env::new(); - let instance = ModuleInstance::new( - &module, - &ImportsBuilder::new().with_resolver("env", &env), - ).expect("Failed to instantiate module") - .assert_no_start(); + 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 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); + let retval = instance + .invoke_export(FUNCTION_NAME, args, &mut NopExternals) + .expect(""); + assert_eq!(exp_retval, retval); } #[test] @@ -114,18 +117,15 @@ fn interpreter_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]; - + 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 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 = 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(); @@ -134,7 +134,10 @@ fn interpreter_accumulate_u8() { 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 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"); diff --git a/src/types.rs b/src/types.rs index 6cf139a..f0b5c84 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,8 @@ use alloc::borrow::Cow; use parity_wasm::elements::{ - FunctionType, ValueType as EValueType, GlobalType, TableType, MemoryType}; + FunctionType, GlobalType, MemoryType, TableType, ValueType as EValueType, +}; /// Signature of a [function]. /// @@ -13,55 +14,60 @@ use parity_wasm::elements::{ /// [function]: struct.FuncInstance.html #[derive(Debug, Clone, PartialEq, Eq)] pub struct Signature { - params: Cow<'static, [ValueType]>, - return_type: Option, + params: Cow<'static, [ValueType]>, + return_type: Option, } impl Signature { - /// Creates new signature with givens - /// parameter types and optional return type. - /// - /// # Examples - /// - /// ```rust - /// use wasmi::{Signature, ValueType}; - /// - /// // s1: (i32) -> () - /// let s1 = Signature::new(&[ValueType::I32][..], None); - /// - /// // s2: () -> i32 - /// let s2 = Signature::new(&[][..], Some(ValueType::I32)); - /// - /// // s3: (I64) -> () - /// let dynamic_params = vec![ValueType::I64]; - /// let s3 = Signature::new(dynamic_params, None); - /// ``` - pub fn new>>( - params: C, - return_type: Option - ) -> Signature { - Signature { - params: params.into(), - return_type: return_type, - } - } + /// Creates new signature with givens + /// parameter types and optional return type. + /// + /// # Examples + /// + /// ```rust + /// use wasmi::{Signature, ValueType}; + /// + /// // s1: (i32) -> () + /// let s1 = Signature::new(&[ValueType::I32][..], None); + /// + /// // s2: () -> i32 + /// let s2 = Signature::new(&[][..], Some(ValueType::I32)); + /// + /// // s3: (I64) -> () + /// let dynamic_params = vec![ValueType::I64]; + /// let s3 = Signature::new(dynamic_params, None); + /// ``` + pub fn new>>( + params: C, + return_type: Option, + ) -> Signature { + Signature { + params: params.into(), + return_type: return_type, + } + } - /// Returns parameter types of this signature. - pub fn params(&self) -> &[ValueType] { - &self.params.as_ref() - } + /// Returns parameter types of this signature. + pub fn params(&self) -> &[ValueType] { + &self.params.as_ref() + } - /// Returns return type of this signature. - pub fn return_type(&self) -> Option { - self.return_type - } + /// Returns return type of this signature. + pub fn return_type(&self) -> Option { + self.return_type + } - pub(crate) fn from_elements(func_type: &FunctionType) -> Signature { - Signature { - params: func_type.params().iter().cloned().map(ValueType::from_elements).collect(), - return_type: func_type.return_type().map(ValueType::from_elements), - } - } + pub(crate) fn from_elements(func_type: &FunctionType) -> Signature { + Signature { + params: func_type + .params() + .iter() + .cloned() + .map(ValueType::from_elements) + .collect(), + return_type: func_type.return_type().map(ValueType::from_elements), + } + } } /// Type of a value. @@ -71,34 +77,34 @@ impl Signature { /// [`RuntimeValue`]: enum.RuntimeValue.html #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ValueType { - /// 32-bit signed or unsigned integer. - I32, - /// 64-bit signed or unsigned integer. - I64, - /// 32-bit IEEE 754-2008 floating point number. - F32, - /// 64-bit IEEE 754-2008 floating point number. - F64, + /// 32-bit signed or unsigned integer. + I32, + /// 64-bit signed or unsigned integer. + I64, + /// 32-bit IEEE 754-2008 floating point number. + F32, + /// 64-bit IEEE 754-2008 floating point number. + F64, } impl ValueType { - pub(crate) fn from_elements(value_type: EValueType) -> ValueType { - match value_type { - EValueType::I32 => ValueType::I32, - EValueType::I64 => ValueType::I64, - EValueType::F32 => ValueType::F32, - EValueType::F64 => ValueType::F64, - } - } + pub(crate) fn from_elements(value_type: EValueType) -> ValueType { + match value_type { + EValueType::I32 => ValueType::I32, + EValueType::I64 => ValueType::I64, + EValueType::F32 => ValueType::F32, + EValueType::F64 => ValueType::F64, + } + } - pub(crate) fn into_elements(self) -> EValueType { - match self { - ValueType::I32 => EValueType::I32, - ValueType::I64 => EValueType::I64, - ValueType::F32 => EValueType::F32, - ValueType::F64 => EValueType::F64, - } - } + pub(crate) fn into_elements(self) -> EValueType { + match self { + ValueType::I32 => EValueType::I32, + ValueType::I64 => EValueType::I64, + ValueType::F32 => EValueType::F32, + ValueType::F64 => EValueType::F64, + } + } } /// Description of a global variable. @@ -108,29 +114,29 @@ impl ValueType { /// /// [`ImportResolver`]: trait.ImportResolver.html pub struct GlobalDescriptor { - value_type: ValueType, - mutable: bool, + value_type: ValueType, + mutable: bool, } impl GlobalDescriptor { - pub(crate) fn from_elements(global_type: &GlobalType) -> GlobalDescriptor { - GlobalDescriptor { - value_type: ValueType::from_elements(global_type.content_type()), - mutable: global_type.is_mutable(), - } - } + pub(crate) fn from_elements(global_type: &GlobalType) -> GlobalDescriptor { + GlobalDescriptor { + value_type: ValueType::from_elements(global_type.content_type()), + mutable: global_type.is_mutable(), + } + } - /// Returns [`ValueType`] of the requested global. - /// - /// [`ValueType`]: enum.ValueType.html - pub fn value_type(&self) -> ValueType { - self.value_type - } + /// Returns [`ValueType`] of the requested global. + /// + /// [`ValueType`]: enum.ValueType.html + pub fn value_type(&self) -> ValueType { + self.value_type + } - /// Returns whether the requested global mutable. - pub fn is_mutable(&self) -> bool { - self.mutable - } + /// Returns whether the requested global mutable. + pub fn is_mutable(&self) -> bool { + self.mutable + } } /// Description of a table. @@ -140,27 +146,27 @@ impl GlobalDescriptor { /// /// [`ImportResolver`]: trait.ImportResolver.html pub struct TableDescriptor { - initial: u32, - maximum: Option, + initial: u32, + maximum: Option, } impl TableDescriptor { - pub(crate) fn from_elements(table_type: &TableType) -> TableDescriptor { - TableDescriptor { - initial: table_type.limits().initial(), - maximum: table_type.limits().maximum(), - } - } + pub(crate) fn from_elements(table_type: &TableType) -> TableDescriptor { + TableDescriptor { + initial: table_type.limits().initial(), + maximum: table_type.limits().maximum(), + } + } - /// Returns initial size of the requested table. - pub fn initial(&self) -> u32 { - self.initial - } + /// Returns initial size of the requested table. + pub fn initial(&self) -> u32 { + self.initial + } - /// Returns maximum size of the requested table. - pub fn maximum(&self) -> Option { - self.maximum - } + /// Returns maximum size of the requested table. + pub fn maximum(&self) -> Option { + self.maximum + } } /// Description of a linear memory. @@ -170,25 +176,25 @@ impl TableDescriptor { /// /// [`ImportResolver`]: trait.ImportResolver.html pub struct MemoryDescriptor { - initial: u32, - maximum: Option, + initial: u32, + maximum: Option, } impl MemoryDescriptor { - pub(crate) fn from_elements(memory_type: &MemoryType) -> MemoryDescriptor { - MemoryDescriptor { - initial: memory_type.limits().initial(), - maximum: memory_type.limits().maximum(), - } - } + pub(crate) fn from_elements(memory_type: &MemoryType) -> MemoryDescriptor { + MemoryDescriptor { + initial: memory_type.limits().initial(), + maximum: memory_type.limits().maximum(), + } + } - /// Returns initial size (in pages) of the requested memory. - pub fn initial(&self) -> u32 { - self.initial - } + /// Returns initial size (in pages) of the requested memory. + pub fn initial(&self) -> u32 { + self.initial + } - /// Returns maximum size (in pages) of the requested memory. - pub fn maximum(&self) -> Option { - self.maximum - } + /// Returns maximum size (in pages) of the requested memory. + pub fn maximum(&self) -> Option { + self.maximum + } } diff --git a/src/validation/context.rs b/src/validation/context.rs index 19805e5..62ccf6f 100644 --- a/src/validation/context.rs +++ b/src/validation/context.rs @@ -1,136 +1,142 @@ #[allow(unused_imports)] use alloc::prelude::*; -use parity_wasm::elements::{MemoryType, TableType, GlobalType, BlockType, ValueType, FunctionType}; +use parity_wasm::elements::{ + BlockType, FunctionType, GlobalType, MemoryType, TableType, ValueType, +}; use validation::Error; #[derive(Default, Debug)] pub struct ModuleContext { - pub memories: Vec, - pub tables: Vec, - pub globals: Vec, - pub types: Vec, - pub func_type_indexes: Vec, + pub memories: Vec, + pub tables: Vec, + pub globals: Vec, + pub types: Vec, + pub func_type_indexes: Vec, } impl ModuleContext { - pub fn memories(&self) -> &[MemoryType] { - &self.memories - } + pub fn memories(&self) -> &[MemoryType] { + &self.memories + } - pub fn tables(&self) -> &[TableType] { - &self.tables - } + pub fn tables(&self) -> &[TableType] { + &self.tables + } - pub fn globals(&self) -> &[GlobalType] { - &self.globals - } + pub fn globals(&self) -> &[GlobalType] { + &self.globals + } - pub fn types(&self) -> &[FunctionType] { - &self.types - } + pub fn types(&self) -> &[FunctionType] { + &self.types + } - pub fn func_type_indexes(&self) -> &[u32] { - &self.func_type_indexes - } + pub fn func_type_indexes(&self) -> &[u32] { + &self.func_type_indexes + } - pub fn require_memory(&self, idx: u32) -> Result<(), Error> { - if self.memories().get(idx as usize).is_none() { - return Err(Error(format!("Memory at index {} doesn't exists", idx))); - } - Ok(()) - } + pub fn require_memory(&self, idx: u32) -> Result<(), Error> { + if self.memories().get(idx as usize).is_none() { + return Err(Error(format!("Memory at index {} doesn't exists", idx))); + } + Ok(()) + } - pub fn require_table(&self, idx: u32) -> Result<&TableType, Error> { - self.tables() - .get(idx as usize) - .ok_or_else(|| Error(format!("Table at index {} doesn't exists", idx))) - } + pub fn require_table(&self, idx: u32) -> Result<&TableType, Error> { + self.tables() + .get(idx as usize) + .ok_or_else(|| Error(format!("Table at index {} doesn't exists", idx))) + } - pub fn require_function(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> { - let ty_idx = self.func_type_indexes() - .get(idx as usize) - .ok_or_else(|| Error(format!("Function at index {} doesn't exists", idx)))?; - self.require_function_type(*ty_idx) - } + pub fn require_function(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> { + let ty_idx = self + .func_type_indexes() + .get(idx as usize) + .ok_or_else(|| Error(format!("Function at index {} doesn't exists", idx)))?; + self.require_function_type(*ty_idx) + } - pub fn require_function_type(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> { - let ty = self.types() - .get(idx as usize) - .ok_or_else(|| Error(format!("Type at index {} doesn't exists", idx)))?; + pub fn require_function_type(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> { + let ty = self + .types() + .get(idx as usize) + .ok_or_else(|| Error(format!("Type at index {} doesn't exists", idx)))?; - let params = ty.params(); - let return_ty = ty.return_type() - .map(BlockType::Value) - .unwrap_or(BlockType::NoResult); - Ok((params, return_ty)) - } + let params = ty.params(); + let return_ty = ty + .return_type() + .map(BlockType::Value) + .unwrap_or(BlockType::NoResult); + Ok((params, return_ty)) + } - pub fn require_global(&self, idx: u32, mutability: Option) -> Result<&GlobalType, Error> { - let global = self.globals() - .get(idx as usize) - .ok_or_else(|| Error(format!("Global at index {} doesn't exists", idx)))?; + pub fn require_global(&self, idx: u32, mutability: Option) -> Result<&GlobalType, Error> { + let global = self + .globals() + .get(idx as usize) + .ok_or_else(|| Error(format!("Global at index {} doesn't exists", idx)))?; - if let Some(expected_mutable) = mutability { - if expected_mutable && !global.is_mutable() { - return Err(Error(format!("Expected global {} to be mutable", idx))); - } - if !expected_mutable && global.is_mutable() { - return Err(Error(format!("Expected global {} to be immutable", idx))); - } - } - Ok(global) - } + if let Some(expected_mutable) = mutability { + if expected_mutable && !global.is_mutable() { + return Err(Error(format!("Expected global {} to be mutable", idx))); + } + if !expected_mutable && global.is_mutable() { + return Err(Error(format!("Expected global {} to be immutable", idx))); + } + } + Ok(global) + } } #[derive(Default)] pub struct ModuleContextBuilder { - memories: Vec, - tables: Vec, - globals: Vec, - types: Vec, - func_type_indexes: Vec, + memories: Vec, + tables: Vec, + globals: Vec, + types: Vec, + func_type_indexes: Vec, } impl ModuleContextBuilder { - pub fn new() -> ModuleContextBuilder { - ModuleContextBuilder::default() - } + pub fn new() -> ModuleContextBuilder { + ModuleContextBuilder::default() + } - pub fn push_memory(&mut self, memory: MemoryType) { - self.memories.push(memory); - } + pub fn push_memory(&mut self, memory: MemoryType) { + self.memories.push(memory); + } - pub fn push_table(&mut self, table: TableType) { - self.tables.push(table); - } + pub fn push_table(&mut self, table: TableType) { + self.tables.push(table); + } - pub fn push_global(&mut self, global: GlobalType) { - self.globals.push(global); - } + pub fn push_global(&mut self, global: GlobalType) { + self.globals.push(global); + } - pub fn set_types(&mut self, types: Vec) { - self.types = types; - } + pub fn set_types(&mut self, types: Vec) { + self.types = types; + } - pub fn push_func_type_index(&mut self, func_type_index: u32) { - self.func_type_indexes.push(func_type_index); - } + pub fn push_func_type_index(&mut self, func_type_index: u32) { + self.func_type_indexes.push(func_type_index); + } - pub fn build(self) -> ModuleContext { - let ModuleContextBuilder { - memories, - tables, - globals, - types, - func_type_indexes, - } = self; + pub fn build(self) -> ModuleContext { + let ModuleContextBuilder { + memories, + tables, + globals, + types, + func_type_indexes, + } = self; - ModuleContext { - memories, - tables, - globals, - types, - func_type_indexes, - } - } + ModuleContext { + memories, + tables, + globals, + types, + func_type_indexes, + } + } } diff --git a/src/validation/func.rs b/src/validation/func.rs index 4f700bd..78dfd5e 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -2,9 +2,7 @@ use alloc::prelude::*; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; use core::u32; -use parity_wasm::elements::{ - BlockType, Func, FuncBody, Instruction, TableElementType, ValueType, -}; +use parity_wasm::elements::{BlockType, Func, FuncBody, Instruction, TableElementType, ValueType}; use validation::context::ModuleContext; use validation::util::Locals; @@ -21,1784 +19,1784 @@ const DEFAULT_FRAME_STACK_LIMIT: usize = 16384; /// Control stack frame. #[derive(Debug, Clone)] struct BlockFrame { - /// Frame type. - frame_type: BlockFrameType, - /// A signature, which is a block signature type indicating the number and types of result values of the region. - block_type: BlockType, - /// A label for reference to block instruction. - begin_position: usize, - /// A limit integer value, which is an index into the value stack indicating where to reset it to on a branch to that label. - value_stack_len: usize, - /// Boolean which signals whether value stack became polymorphic. Value stack starts in non-polymorphic state and - /// becomes polymorphic only after an instruction that never passes control further is executed, - /// i.e. `unreachable`, `br` (but not `br_if`!), etc. - polymorphic_stack: bool, + /// Frame type. + frame_type: BlockFrameType, + /// A signature, which is a block signature type indicating the number and types of result values of the region. + block_type: BlockType, + /// A label for reference to block instruction. + begin_position: usize, + /// A limit integer value, which is an index into the value stack indicating where to reset it to on a branch to that label. + value_stack_len: usize, + /// Boolean which signals whether value stack became polymorphic. Value stack starts in non-polymorphic state and + /// becomes polymorphic only after an instruction that never passes control further is executed, + /// i.e. `unreachable`, `br` (but not `br_if`!), etc. + polymorphic_stack: bool, } /// Type of block frame. #[derive(Debug, Clone, Copy, PartialEq)] enum BlockFrameType { - /// Usual block frame. - /// - /// Can be used for an implicit function block. - Block { end_label: LabelId }, - /// Loop frame (branching to the beginning of block). - Loop { header: LabelId }, - /// True-subblock of if expression. - IfTrue { - /// If jump happens inside the if-true block then control will - /// land on this label. - end_label: LabelId, + /// Usual block frame. + /// + /// Can be used for an implicit function block. + Block { end_label: LabelId }, + /// Loop frame (branching to the beginning of block). + Loop { header: LabelId }, + /// True-subblock of if expression. + IfTrue { + /// If jump happens inside the if-true block then control will + /// land on this label. + end_label: LabelId, - /// If the condition of the `if` statement is unsatisfied, control - /// will land on this label. This label might point to `else` block if it - /// exists. Otherwise it equal to `end_label`. - if_not: LabelId, - }, - /// False-subblock of if expression. - IfFalse { end_label: LabelId }, + /// If the condition of the `if` statement is unsatisfied, control + /// will land on this label. This label might point to `else` block if it + /// exists. Otherwise it equal to `end_label`. + if_not: LabelId, + }, + /// False-subblock of if expression. + IfFalse { end_label: LabelId }, } impl BlockFrameType { - /// Returns a label which should be used as a branch destination. - fn br_destination(&self) -> LabelId { - match *self { - BlockFrameType::Block { end_label } => end_label, - BlockFrameType::Loop { header } => header, - BlockFrameType::IfTrue { end_label, .. } => end_label, - BlockFrameType::IfFalse { end_label } => end_label, - } - } + /// Returns a label which should be used as a branch destination. + fn br_destination(&self) -> LabelId { + match *self { + BlockFrameType::Block { end_label } => end_label, + BlockFrameType::Loop { header } => header, + BlockFrameType::IfTrue { end_label, .. } => end_label, + BlockFrameType::IfFalse { end_label } => end_label, + } + } - /// Returns a label which should be resolved at the `End` opcode. - /// - /// All block types have it except loops. Loops doesn't use end as a branch - /// destination. - fn end_label(&self) -> LabelId { - match *self { - BlockFrameType::Block { end_label } => end_label, - BlockFrameType::IfTrue { end_label, .. } => end_label, - BlockFrameType::IfFalse { end_label } => end_label, - BlockFrameType::Loop { .. } => panic!("loop doesn't use end label"), - } - } + /// Returns a label which should be resolved at the `End` opcode. + /// + /// All block types have it except loops. Loops doesn't use end as a branch + /// destination. + fn end_label(&self) -> LabelId { + match *self { + BlockFrameType::Block { end_label } => end_label, + BlockFrameType::IfTrue { end_label, .. } => end_label, + BlockFrameType::IfFalse { end_label } => end_label, + BlockFrameType::Loop { .. } => panic!("loop doesn't use end label"), + } + } - fn is_loop(&self) -> bool { - match *self { - BlockFrameType::Loop { .. } => true, - _ => false, - } - } + fn is_loop(&self) -> bool { + match *self { + BlockFrameType::Loop { .. } => true, + _ => false, + } + } } /// Value type on the stack. #[derive(Debug, Clone, Copy)] enum StackValueType { - /// Any value type. - Any, - /// Concrete value type. - Specific(ValueType), + /// Any value type. + Any, + /// Concrete value type. + Specific(ValueType), } impl StackValueType { - fn is_any(&self) -> bool { - match self { - &StackValueType::Any => true, - _ => false, - } - } + fn is_any(&self) -> bool { + match self { + &StackValueType::Any => true, + _ => false, + } + } - fn value_type(&self) -> ValueType { - match self { - &StackValueType::Any => unreachable!("must be checked by caller"), - &StackValueType::Specific(value_type) => value_type, - } - } + fn value_type(&self) -> ValueType { + match self { + &StackValueType::Any => unreachable!("must be checked by caller"), + &StackValueType::Specific(value_type) => value_type, + } + } } impl From for StackValueType { - fn from(value_type: ValueType) -> Self { - StackValueType::Specific(value_type) - } + fn from(value_type: ValueType) -> Self { + StackValueType::Specific(value_type) + } } impl PartialEq for StackValueType { - fn eq(&self, other: &StackValueType) -> bool { - if self.is_any() || other.is_any() { - true - } else { - self.value_type() == other.value_type() - } - } + fn eq(&self, other: &StackValueType) -> bool { + if self.is_any() || other.is_any() { + true + } else { + self.value_type() == other.value_type() + } + } } impl PartialEq for StackValueType { - fn eq(&self, other: &ValueType) -> bool { - if self.is_any() { - true - } else { - self.value_type() == *other - } - } + fn eq(&self, other: &ValueType) -> bool { + if self.is_any() { + true + } else { + self.value_type() == *other + } + } } impl PartialEq for ValueType { - fn eq(&self, other: &StackValueType) -> bool { - other == self - } + fn eq(&self, other: &StackValueType) -> bool { + other == self + } } /// Instruction outcome. #[derive(Debug, Clone)] enum Outcome { - /// Continue with next instruction. - NextInstruction, - /// Unreachable instruction reached. - Unreachable, + /// Continue with next instruction. + NextInstruction, + /// Unreachable instruction reached. + Unreachable, } pub struct FunctionReader; impl FunctionReader { - pub fn read_function( - module: &ModuleContext, - func: &Func, - body: &FuncBody, - ) -> Result { - let (params, result_ty) = module.require_function_type(func.type_ref())?; + pub fn read_function( + module: &ModuleContext, + func: &Func, + body: &FuncBody, + ) -> Result { + let (params, result_ty) = module.require_function_type(func.type_ref())?; - let ins_size_estimate = body.code().elements().len(); - let mut context = FunctionValidationContext::new( - &module, - Locals::new(params, body.locals())?, - DEFAULT_VALUE_STACK_LIMIT, - DEFAULT_FRAME_STACK_LIMIT, - result_ty, - ins_size_estimate, - ); + let ins_size_estimate = body.code().elements().len(); + let mut context = FunctionValidationContext::new( + &module, + Locals::new(params, body.locals())?, + DEFAULT_VALUE_STACK_LIMIT, + DEFAULT_FRAME_STACK_LIMIT, + result_ty, + ins_size_estimate, + ); - let end_label = context.sink.new_label(); - push_label( - BlockFrameType::Block { end_label }, - result_ty, - context.position, - &context.value_stack, - &mut context.frame_stack, - )?; - FunctionReader::read_function_body(&mut context, body.code().elements())?; + let end_label = context.sink.new_label(); + push_label( + BlockFrameType::Block { end_label }, + result_ty, + context.position, + &context.value_stack, + &mut context.frame_stack, + )?; + FunctionReader::read_function_body(&mut context, body.code().elements())?; - assert!(context.frame_stack.is_empty()); + assert!(context.frame_stack.is_empty()); - Ok(context.into_code()) - } + Ok(context.into_code()) + } - fn read_function_body( - context: &mut FunctionValidationContext, - body: &[Instruction], - ) -> Result<(), Error> { - let body_len = body.len(); - if body_len == 0 { - return Err(Error("Non-empty function body expected".into())); - } + fn read_function_body( + context: &mut FunctionValidationContext, + body: &[Instruction], + ) -> Result<(), Error> { + let body_len = body.len(); + if body_len == 0 { + return Err(Error("Non-empty function body expected".into())); + } - loop { - let instruction = &body[context.position]; + loop { + let instruction = &body[context.position]; - let outcome = - FunctionReader::read_instruction(context, instruction).map_err(|err| { - Error(format!( - "At instruction {:?}(@{}): {}", - instruction, context.position, err - )) - })?; + let outcome = + FunctionReader::read_instruction(context, instruction).map_err(|err| { + Error(format!( + "At instruction {:?}(@{}): {}", + instruction, context.position, err + )) + })?; - match outcome { - Outcome::NextInstruction => (), - Outcome::Unreachable => { - make_top_frame_polymorphic(&mut context.value_stack, &mut context.frame_stack) - } - } + match outcome { + Outcome::NextInstruction => (), + Outcome::Unreachable => { + make_top_frame_polymorphic(&mut context.value_stack, &mut context.frame_stack) + } + } - context.position += 1; - if context.position == body_len { - return Ok(()); - } - } - } + context.position += 1; + if context.position == body_len { + return Ok(()); + } + } + } - fn read_instruction( - context: &mut FunctionValidationContext, - instruction: &Instruction, - ) -> Result { - use self::Instruction::*; + fn read_instruction( + context: &mut FunctionValidationContext, + instruction: &Instruction, + ) -> Result { + use self::Instruction::*; - match *instruction { - // Nop instruction doesn't do anything. It is safe to just skip it. - Nop => {} + match *instruction { + // Nop instruction doesn't do anything. It is safe to just skip it. + Nop => {} - Unreachable => { - context.sink.emit(isa::InstructionInternal::Unreachable); - return Ok(Outcome::Unreachable); - } + Unreachable => { + context.sink.emit(isa::InstructionInternal::Unreachable); + return Ok(Outcome::Unreachable); + } - Block(block_type) => { - let end_label = context.sink.new_label(); - push_label( - BlockFrameType::Block { end_label }, - block_type, - context.position, - &context.value_stack, - &mut context.frame_stack, - )?; - } - Loop(block_type) => { - // Resolve loop header right away. - let header = context.sink.new_label(); - context.sink.resolve_label(header); + Block(block_type) => { + let end_label = context.sink.new_label(); + push_label( + BlockFrameType::Block { end_label }, + block_type, + context.position, + &context.value_stack, + &mut context.frame_stack, + )?; + } + Loop(block_type) => { + // Resolve loop header right away. + let header = context.sink.new_label(); + context.sink.resolve_label(header); - push_label( - BlockFrameType::Loop { header }, - block_type, - context.position, - &context.value_stack, - &mut context.frame_stack, - )?; - } - If(block_type) => { - // `if_not` will be resolved whenever `End` or `Else` operator will be met. - // `end_label` will always be resolved at `End`. - let if_not = context.sink.new_label(); - let end_label = context.sink.new_label(); + push_label( + BlockFrameType::Loop { header }, + block_type, + context.position, + &context.value_stack, + &mut context.frame_stack, + )?; + } + If(block_type) => { + // `if_not` will be resolved whenever `End` or `Else` operator will be met. + // `end_label` will always be resolved at `End`. + let if_not = context.sink.new_label(); + let end_label = context.sink.new_label(); - pop_value( - &mut context.value_stack, - &context.frame_stack, - ValueType::I32.into(), - )?; - push_label( - BlockFrameType::IfTrue { if_not, end_label }, - block_type, - context.position, - &context.value_stack, - &mut context.frame_stack, - )?; + pop_value( + &mut context.value_stack, + &context.frame_stack, + ValueType::I32.into(), + )?; + push_label( + BlockFrameType::IfTrue { if_not, end_label }, + block_type, + context.position, + &context.value_stack, + &mut context.frame_stack, + )?; - context.sink.emit_br_eqz(Target { - label: if_not, - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }); - } - Else => { - let (block_type, if_not, end_label) = { - let top_frame = top_label(&context.frame_stack); + context.sink.emit_br_eqz(Target { + label: if_not, + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }); + } + Else => { + let (block_type, if_not, end_label) = { + let top_frame = top_label(&context.frame_stack); - let (if_not, end_label) = match top_frame.frame_type { - BlockFrameType::IfTrue { if_not, end_label } => (if_not, end_label), - _ => return Err(Error("Misplaced else instruction".into())), - }; - (top_frame.block_type, if_not, end_label) - }; + let (if_not, end_label) = match top_frame.frame_type { + BlockFrameType::IfTrue { if_not, end_label } => (if_not, end_label), + _ => return Err(Error("Misplaced else instruction".into())), + }; + (top_frame.block_type, if_not, end_label) + }; - // First, we need to finish if-true block: add a jump from the end of the if-true block - // to the "end_label" (it will be resolved at End). - context.sink.emit_br(Target { - label: end_label, - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }); + // First, we need to finish if-true block: add a jump from the end of the if-true block + // to the "end_label" (it will be resolved at End). + context.sink.emit_br(Target { + label: end_label, + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }); - // Resolve `if_not` to here so when if condition is unsatisfied control flow - // will jump to this label. - context.sink.resolve_label(if_not); + // Resolve `if_not` to here so when if condition is unsatisfied control flow + // will jump to this label. + context.sink.resolve_label(if_not); - // Then, we pop the current label. It discards all values that pushed in the current - // frame. - pop_label(&mut context.value_stack, &mut context.frame_stack)?; - push_label( - BlockFrameType::IfFalse { end_label }, - block_type, - context.position, - &context.value_stack, - &mut context.frame_stack, - )?; - } - End => { - let (frame_type, block_type) = { - let top = top_label(&context.frame_stack); - (top.frame_type, top.block_type) - }; + // Then, we pop the current label. It discards all values that pushed in the current + // frame. + pop_label(&mut context.value_stack, &mut context.frame_stack)?; + push_label( + BlockFrameType::IfFalse { end_label }, + block_type, + context.position, + &context.value_stack, + &mut context.frame_stack, + )?; + } + End => { + let (frame_type, block_type) = { + let top = top_label(&context.frame_stack); + (top.frame_type, top.block_type) + }; - if let BlockFrameType::IfTrue { if_not, .. } = frame_type { - // A `if` without an `else` can't return a result. - if block_type != BlockType::NoResult { - return Err(Error(format!( + if let BlockFrameType::IfTrue { if_not, .. } = frame_type { + // A `if` without an `else` can't return a result. + if block_type != BlockType::NoResult { + return Err(Error(format!( "If block without else required to have NoResult block type. But it has {:?} type", block_type ))); - } + } - // Resolve `if_not` label. If the `if's` condition doesn't hold the control will jump - // to here. - context.sink.resolve_label(if_not); - } + // Resolve `if_not` label. If the `if's` condition doesn't hold the control will jump + // to here. + context.sink.resolve_label(if_not); + } - // Unless it's a loop, resolve the `end_label` position here. - if !frame_type.is_loop() { - let end_label = frame_type.end_label(); - context.sink.resolve_label(end_label); - } + // Unless it's a loop, resolve the `end_label` position here. + if !frame_type.is_loop() { + let end_label = frame_type.end_label(); + context.sink.resolve_label(end_label); + } - if context.frame_stack.len() == 1 { - // We are about to close the last frame. Insert - // an explicit return. + if context.frame_stack.len() == 1 { + // We are about to close the last frame. Insert + // an explicit return. - // Check the return type. - if let BlockType::Value(value_type) = context.return_type()? { - tee_value( - &mut context.value_stack, - &context.frame_stack, - value_type.into(), - )?; - } + // Check the return type. + if let BlockType::Value(value_type) = context.return_type()? { + tee_value( + &mut context.value_stack, + &context.frame_stack, + value_type.into(), + )?; + } - // Emit the return instruction. - let drop_keep = drop_keep_return( - &context.locals, - &context.value_stack, - &context.frame_stack, - ); - context - .sink - .emit(isa::InstructionInternal::Return(drop_keep)); - } + // Emit the return instruction. + let drop_keep = drop_keep_return( + &context.locals, + &context.value_stack, + &context.frame_stack, + ); + context + .sink + .emit(isa::InstructionInternal::Return(drop_keep)); + } - pop_label(&mut context.value_stack, &mut context.frame_stack)?; + pop_label(&mut context.value_stack, &mut context.frame_stack)?; - // Push the result value. - if let BlockType::Value(value_type) = block_type { - push_value(&mut context.value_stack, value_type.into())?; - } - } - Br(depth) => { - Validator::validate_br(context, depth)?; + // Push the result value. + if let BlockType::Value(value_type) = block_type { + push_value(&mut context.value_stack, value_type.into())?; + } + } + Br(depth) => { + Validator::validate_br(context, depth)?; - let target = require_target(depth, &context.value_stack, &context.frame_stack); - context.sink.emit_br(target); + let target = require_target(depth, &context.value_stack, &context.frame_stack); + context.sink.emit_br(target); - return Ok(Outcome::Unreachable); - } - BrIf(depth) => { - Validator::validate_br_if(context, depth)?; + return Ok(Outcome::Unreachable); + } + BrIf(depth) => { + Validator::validate_br_if(context, depth)?; - let target = require_target(depth, &context.value_stack, &context.frame_stack); - context.sink.emit_br_nez(target); - } - BrTable(ref table, default) => { - Validator::validate_br_table(context, table, default)?; + let target = require_target(depth, &context.value_stack, &context.frame_stack); + context.sink.emit_br_nez(target); + } + BrTable(ref table, default) => { + Validator::validate_br_table(context, table, default)?; - let mut targets = Vec::new(); - for depth in table.iter() { - let target = require_target(*depth, &context.value_stack, &context.frame_stack); - targets.push(target); - } - let default_target = - require_target(default, &context.value_stack, &context.frame_stack); - context.sink.emit_br_table(&targets, default_target); + let mut targets = Vec::new(); + for depth in table.iter() { + let target = require_target(*depth, &context.value_stack, &context.frame_stack); + targets.push(target); + } + let default_target = + require_target(default, &context.value_stack, &context.frame_stack); + context.sink.emit_br_table(&targets, default_target); - return Ok(Outcome::Unreachable); - } - Return => { - if let BlockType::Value(value_type) = context.return_type()? { - tee_value( - &mut context.value_stack, - &context.frame_stack, - value_type.into(), - )?; - } + return Ok(Outcome::Unreachable); + } + Return => { + if let BlockType::Value(value_type) = context.return_type()? { + tee_value( + &mut context.value_stack, + &context.frame_stack, + value_type.into(), + )?; + } - let drop_keep = - drop_keep_return(&context.locals, &context.value_stack, &context.frame_stack); - context - .sink - .emit(isa::InstructionInternal::Return(drop_keep)); + let drop_keep = + drop_keep_return(&context.locals, &context.value_stack, &context.frame_stack); + context + .sink + .emit(isa::InstructionInternal::Return(drop_keep)); - return Ok(Outcome::Unreachable); - } + return Ok(Outcome::Unreachable); + } - Call(index) => { - Validator::validate_call(context, index)?; - context.sink.emit(isa::InstructionInternal::Call(index)); - } - CallIndirect(index, _reserved) => { - Validator::validate_call_indirect(context, index)?; - context - .sink - .emit(isa::InstructionInternal::CallIndirect(index)); - } + Call(index) => { + Validator::validate_call(context, index)?; + context.sink.emit(isa::InstructionInternal::Call(index)); + } + CallIndirect(index, _reserved) => { + Validator::validate_call_indirect(context, index)?; + context + .sink + .emit(isa::InstructionInternal::CallIndirect(index)); + } - Drop => { - Validator::validate_drop(context)?; - context.sink.emit(isa::InstructionInternal::Drop); - } - Select => { - Validator::validate_select(context)?; - context.sink.emit(isa::InstructionInternal::Select); - } + Drop => { + Validator::validate_drop(context)?; + context.sink.emit(isa::InstructionInternal::Drop); + } + Select => { + Validator::validate_select(context)?; + context.sink.emit(isa::InstructionInternal::Select); + } - GetLocal(index) => { - // We need to calculate relative depth before validation since - // it will change the value stack size. - let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; - Validator::validate_get_local(context, index)?; - context.sink.emit(isa::InstructionInternal::GetLocal(depth)); - } - SetLocal(index) => { - Validator::validate_set_local(context, index)?; - let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; - context.sink.emit(isa::InstructionInternal::SetLocal(depth)); - } - TeeLocal(index) => { - Validator::validate_tee_local(context, index)?; - let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; - context.sink.emit(isa::InstructionInternal::TeeLocal(depth)); - } - GetGlobal(index) => { - Validator::validate_get_global(context, index)?; - context - .sink - .emit(isa::InstructionInternal::GetGlobal(index)); - } - SetGlobal(index) => { - Validator::validate_set_global(context, index)?; - context - .sink - .emit(isa::InstructionInternal::SetGlobal(index)); - } + GetLocal(index) => { + // We need to calculate relative depth before validation since + // it will change the value stack size. + let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; + Validator::validate_get_local(context, index)?; + context.sink.emit(isa::InstructionInternal::GetLocal(depth)); + } + SetLocal(index) => { + Validator::validate_set_local(context, index)?; + let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; + context.sink.emit(isa::InstructionInternal::SetLocal(depth)); + } + TeeLocal(index) => { + Validator::validate_tee_local(context, index)?; + let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; + context.sink.emit(isa::InstructionInternal::TeeLocal(depth)); + } + GetGlobal(index) => { + Validator::validate_get_global(context, index)?; + context + .sink + .emit(isa::InstructionInternal::GetGlobal(index)); + } + SetGlobal(index) => { + Validator::validate_set_global(context, index)?; + context + .sink + .emit(isa::InstructionInternal::SetGlobal(index)); + } - I32Load(align, offset) => { - Validator::validate_load(context, align, 4, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Load(offset)); - } - I64Load(align, offset) => { - Validator::validate_load(context, align, 8, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Load(offset)); - } - F32Load(align, offset) => { - Validator::validate_load(context, align, 4, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Load(offset)); - } - F64Load(align, offset) => { - Validator::validate_load(context, align, 8, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Load(offset)); - } - I32Load8S(align, offset) => { - Validator::validate_load(context, align, 1, ValueType::I32)?; - context - .sink - .emit(isa::InstructionInternal::I32Load8S(offset)); - } - I32Load8U(align, offset) => { - Validator::validate_load(context, align, 1, ValueType::I32)?; - context - .sink - .emit(isa::InstructionInternal::I32Load8U(offset)); - } - I32Load16S(align, offset) => { - Validator::validate_load(context, align, 2, ValueType::I32)?; - context - .sink - .emit(isa::InstructionInternal::I32Load16S(offset)); - } - I32Load16U(align, offset) => { - Validator::validate_load(context, align, 2, ValueType::I32)?; - context - .sink - .emit(isa::InstructionInternal::I32Load16U(offset)); - } - I64Load8S(align, offset) => { - Validator::validate_load(context, align, 1, ValueType::I64)?; - context - .sink - .emit(isa::InstructionInternal::I64Load8S(offset)); - } - I64Load8U(align, offset) => { - Validator::validate_load(context, align, 1, ValueType::I64)?; - context - .sink - .emit(isa::InstructionInternal::I64Load8U(offset)); - } - I64Load16S(align, offset) => { - Validator::validate_load(context, align, 2, ValueType::I64)?; - context - .sink - .emit(isa::InstructionInternal::I64Load16S(offset)); - } - I64Load16U(align, offset) => { - Validator::validate_load(context, align, 2, ValueType::I64)?; - context - .sink - .emit(isa::InstructionInternal::I64Load16U(offset)); - } - I64Load32S(align, offset) => { - Validator::validate_load(context, align, 4, ValueType::I64)?; - context - .sink - .emit(isa::InstructionInternal::I64Load32S(offset)); - } - I64Load32U(align, offset) => { - Validator::validate_load(context, align, 4, ValueType::I64)?; - context - .sink - .emit(isa::InstructionInternal::I64Load32U(offset)); - } + I32Load(align, offset) => { + Validator::validate_load(context, align, 4, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Load(offset)); + } + I64Load(align, offset) => { + Validator::validate_load(context, align, 8, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Load(offset)); + } + F32Load(align, offset) => { + Validator::validate_load(context, align, 4, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Load(offset)); + } + F64Load(align, offset) => { + Validator::validate_load(context, align, 8, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Load(offset)); + } + I32Load8S(align, offset) => { + Validator::validate_load(context, align, 1, ValueType::I32)?; + context + .sink + .emit(isa::InstructionInternal::I32Load8S(offset)); + } + I32Load8U(align, offset) => { + Validator::validate_load(context, align, 1, ValueType::I32)?; + context + .sink + .emit(isa::InstructionInternal::I32Load8U(offset)); + } + I32Load16S(align, offset) => { + Validator::validate_load(context, align, 2, ValueType::I32)?; + context + .sink + .emit(isa::InstructionInternal::I32Load16S(offset)); + } + I32Load16U(align, offset) => { + Validator::validate_load(context, align, 2, ValueType::I32)?; + context + .sink + .emit(isa::InstructionInternal::I32Load16U(offset)); + } + I64Load8S(align, offset) => { + Validator::validate_load(context, align, 1, ValueType::I64)?; + context + .sink + .emit(isa::InstructionInternal::I64Load8S(offset)); + } + I64Load8U(align, offset) => { + Validator::validate_load(context, align, 1, ValueType::I64)?; + context + .sink + .emit(isa::InstructionInternal::I64Load8U(offset)); + } + I64Load16S(align, offset) => { + Validator::validate_load(context, align, 2, ValueType::I64)?; + context + .sink + .emit(isa::InstructionInternal::I64Load16S(offset)); + } + I64Load16U(align, offset) => { + Validator::validate_load(context, align, 2, ValueType::I64)?; + context + .sink + .emit(isa::InstructionInternal::I64Load16U(offset)); + } + I64Load32S(align, offset) => { + Validator::validate_load(context, align, 4, ValueType::I64)?; + context + .sink + .emit(isa::InstructionInternal::I64Load32S(offset)); + } + I64Load32U(align, offset) => { + Validator::validate_load(context, align, 4, ValueType::I64)?; + context + .sink + .emit(isa::InstructionInternal::I64Load32U(offset)); + } - I32Store(align, offset) => { - Validator::validate_store(context, align, 4, ValueType::I32)?; - context - .sink - .emit(isa::InstructionInternal::I32Store(offset)); - } - I64Store(align, offset) => { - Validator::validate_store(context, align, 8, ValueType::I64)?; - context - .sink - .emit(isa::InstructionInternal::I64Store(offset)); - } - F32Store(align, offset) => { - Validator::validate_store(context, align, 4, ValueType::F32)?; - context - .sink - .emit(isa::InstructionInternal::F32Store(offset)); - } - F64Store(align, offset) => { - Validator::validate_store(context, align, 8, ValueType::F64)?; - context - .sink - .emit(isa::InstructionInternal::F64Store(offset)); - } - I32Store8(align, offset) => { - Validator::validate_store(context, align, 1, ValueType::I32)?; - context - .sink - .emit(isa::InstructionInternal::I32Store8(offset)); - } - I32Store16(align, offset) => { - Validator::validate_store(context, align, 2, ValueType::I32)?; - context - .sink - .emit(isa::InstructionInternal::I32Store16(offset)); - } - I64Store8(align, offset) => { - Validator::validate_store(context, align, 1, ValueType::I64)?; - context - .sink - .emit(isa::InstructionInternal::I64Store8(offset)); - } - I64Store16(align, offset) => { - Validator::validate_store(context, align, 2, ValueType::I64)?; - context - .sink - .emit(isa::InstructionInternal::I64Store16(offset)); - } - I64Store32(align, offset) => { - Validator::validate_store(context, align, 4, ValueType::I64)?; - context - .sink - .emit(isa::InstructionInternal::I64Store32(offset)); - } + I32Store(align, offset) => { + Validator::validate_store(context, align, 4, ValueType::I32)?; + context + .sink + .emit(isa::InstructionInternal::I32Store(offset)); + } + I64Store(align, offset) => { + Validator::validate_store(context, align, 8, ValueType::I64)?; + context + .sink + .emit(isa::InstructionInternal::I64Store(offset)); + } + F32Store(align, offset) => { + Validator::validate_store(context, align, 4, ValueType::F32)?; + context + .sink + .emit(isa::InstructionInternal::F32Store(offset)); + } + F64Store(align, offset) => { + Validator::validate_store(context, align, 8, ValueType::F64)?; + context + .sink + .emit(isa::InstructionInternal::F64Store(offset)); + } + I32Store8(align, offset) => { + Validator::validate_store(context, align, 1, ValueType::I32)?; + context + .sink + .emit(isa::InstructionInternal::I32Store8(offset)); + } + I32Store16(align, offset) => { + Validator::validate_store(context, align, 2, ValueType::I32)?; + context + .sink + .emit(isa::InstructionInternal::I32Store16(offset)); + } + I64Store8(align, offset) => { + Validator::validate_store(context, align, 1, ValueType::I64)?; + context + .sink + .emit(isa::InstructionInternal::I64Store8(offset)); + } + I64Store16(align, offset) => { + Validator::validate_store(context, align, 2, ValueType::I64)?; + context + .sink + .emit(isa::InstructionInternal::I64Store16(offset)); + } + I64Store32(align, offset) => { + Validator::validate_store(context, align, 4, ValueType::I64)?; + context + .sink + .emit(isa::InstructionInternal::I64Store32(offset)); + } - CurrentMemory(_) => { - Validator::validate_current_memory(context)?; - context.sink.emit(isa::InstructionInternal::CurrentMemory); - } - GrowMemory(_) => { - Validator::validate_grow_memory(context)?; - context.sink.emit(isa::InstructionInternal::GrowMemory); - } + CurrentMemory(_) => { + Validator::validate_current_memory(context)?; + context.sink.emit(isa::InstructionInternal::CurrentMemory); + } + GrowMemory(_) => { + Validator::validate_grow_memory(context)?; + context.sink.emit(isa::InstructionInternal::GrowMemory); + } - I32Const(v) => { - Validator::validate_const(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Const(v)); - } - I64Const(v) => { - Validator::validate_const(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Const(v)); - } - F32Const(v) => { - Validator::validate_const(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Const(v)); - } - F64Const(v) => { - Validator::validate_const(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Const(v)); - } + I32Const(v) => { + Validator::validate_const(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Const(v)); + } + I64Const(v) => { + Validator::validate_const(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Const(v)); + } + F32Const(v) => { + Validator::validate_const(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Const(v)); + } + F64Const(v) => { + Validator::validate_const(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Const(v)); + } - I32Eqz => { - Validator::validate_testop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Eqz); - } - I32Eq => { - Validator::validate_relop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Eq); - } - I32Ne => { - Validator::validate_relop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Ne); - } - I32LtS => { - Validator::validate_relop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32LtS); - } - I32LtU => { - Validator::validate_relop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32LtU); - } - I32GtS => { - Validator::validate_relop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32GtS); - } - I32GtU => { - Validator::validate_relop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32GtU); - } - I32LeS => { - Validator::validate_relop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32LeS); - } - I32LeU => { - Validator::validate_relop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32LeU); - } - I32GeS => { - Validator::validate_relop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32GeS); - } - I32GeU => { - Validator::validate_relop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32GeU); - } + I32Eqz => { + Validator::validate_testop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Eqz); + } + I32Eq => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Eq); + } + I32Ne => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Ne); + } + I32LtS => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32LtS); + } + I32LtU => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32LtU); + } + I32GtS => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32GtS); + } + I32GtU => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32GtU); + } + I32LeS => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32LeS); + } + I32LeU => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32LeU); + } + I32GeS => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32GeS); + } + I32GeU => { + Validator::validate_relop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32GeU); + } - I64Eqz => { - Validator::validate_testop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Eqz); - } - I64Eq => { - Validator::validate_relop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Eq); - } - I64Ne => { - Validator::validate_relop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Ne); - } - I64LtS => { - Validator::validate_relop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64LtS); - } - I64LtU => { - Validator::validate_relop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64LtU); - } - I64GtS => { - Validator::validate_relop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64GtS); - } - I64GtU => { - Validator::validate_relop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64GtU); - } - I64LeS => { - Validator::validate_relop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64LeS); - } - I64LeU => { - Validator::validate_relop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64LeU); - } - I64GeS => { - Validator::validate_relop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64GeS); - } - I64GeU => { - Validator::validate_relop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64GeU); - } + I64Eqz => { + Validator::validate_testop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Eqz); + } + I64Eq => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Eq); + } + I64Ne => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Ne); + } + I64LtS => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64LtS); + } + I64LtU => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64LtU); + } + I64GtS => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64GtS); + } + I64GtU => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64GtU); + } + I64LeS => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64LeS); + } + I64LeU => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64LeU); + } + I64GeS => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64GeS); + } + I64GeU => { + Validator::validate_relop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64GeU); + } - F32Eq => { - Validator::validate_relop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Eq); - } - F32Ne => { - Validator::validate_relop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Ne); - } - F32Lt => { - Validator::validate_relop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Lt); - } - F32Gt => { - Validator::validate_relop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Gt); - } - F32Le => { - Validator::validate_relop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Le); - } - F32Ge => { - Validator::validate_relop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Ge); - } + F32Eq => { + Validator::validate_relop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Eq); + } + F32Ne => { + Validator::validate_relop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Ne); + } + F32Lt => { + Validator::validate_relop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Lt); + } + F32Gt => { + Validator::validate_relop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Gt); + } + F32Le => { + Validator::validate_relop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Le); + } + F32Ge => { + Validator::validate_relop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Ge); + } - F64Eq => { - Validator::validate_relop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Eq); - } - F64Ne => { - Validator::validate_relop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Ne); - } - F64Lt => { - Validator::validate_relop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Lt); - } - F64Gt => { - Validator::validate_relop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Gt); - } - F64Le => { - Validator::validate_relop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Le); - } - F64Ge => { - Validator::validate_relop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Ge); - } + F64Eq => { + Validator::validate_relop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Eq); + } + F64Ne => { + Validator::validate_relop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Ne); + } + F64Lt => { + Validator::validate_relop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Lt); + } + F64Gt => { + Validator::validate_relop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Gt); + } + F64Le => { + Validator::validate_relop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Le); + } + F64Ge => { + Validator::validate_relop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Ge); + } - I32Clz => { - Validator::validate_unop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Clz); - } - I32Ctz => { - Validator::validate_unop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Ctz); - } - I32Popcnt => { - Validator::validate_unop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Popcnt); - } - I32Add => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Add); - } - I32Sub => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Sub); - } - I32Mul => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Mul); - } - I32DivS => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32DivS); - } - I32DivU => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32DivU); - } - I32RemS => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32RemS); - } - I32RemU => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32RemU); - } - I32And => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32And); - } - I32Or => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Or); - } - I32Xor => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Xor); - } - I32Shl => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Shl); - } - I32ShrS => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32ShrS); - } - I32ShrU => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32ShrU); - } - I32Rotl => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Rotl); - } - I32Rotr => { - Validator::validate_binop(context, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32Rotr); - } + I32Clz => { + Validator::validate_unop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Clz); + } + I32Ctz => { + Validator::validate_unop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Ctz); + } + I32Popcnt => { + Validator::validate_unop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Popcnt); + } + I32Add => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Add); + } + I32Sub => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Sub); + } + I32Mul => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Mul); + } + I32DivS => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32DivS); + } + I32DivU => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32DivU); + } + I32RemS => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32RemS); + } + I32RemU => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32RemU); + } + I32And => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32And); + } + I32Or => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Or); + } + I32Xor => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Xor); + } + I32Shl => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Shl); + } + I32ShrS => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32ShrS); + } + I32ShrU => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32ShrU); + } + I32Rotl => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Rotl); + } + I32Rotr => { + Validator::validate_binop(context, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32Rotr); + } - I64Clz => { - Validator::validate_unop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Clz); - } - I64Ctz => { - Validator::validate_unop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Ctz); - } - I64Popcnt => { - Validator::validate_unop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Popcnt); - } - I64Add => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Add); - } - I64Sub => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Sub); - } - I64Mul => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Mul); - } - I64DivS => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64DivS); - } - I64DivU => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64DivU); - } - I64RemS => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64RemS); - } - I64RemU => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64RemU); - } - I64And => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64And); - } - I64Or => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Or); - } - I64Xor => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Xor); - } - I64Shl => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Shl); - } - I64ShrS => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64ShrS); - } - I64ShrU => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64ShrU); - } - I64Rotl => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Rotl); - } - I64Rotr => { - Validator::validate_binop(context, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64Rotr); - } + I64Clz => { + Validator::validate_unop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Clz); + } + I64Ctz => { + Validator::validate_unop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Ctz); + } + I64Popcnt => { + Validator::validate_unop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Popcnt); + } + I64Add => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Add); + } + I64Sub => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Sub); + } + I64Mul => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Mul); + } + I64DivS => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64DivS); + } + I64DivU => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64DivU); + } + I64RemS => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64RemS); + } + I64RemU => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64RemU); + } + I64And => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64And); + } + I64Or => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Or); + } + I64Xor => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Xor); + } + I64Shl => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Shl); + } + I64ShrS => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64ShrS); + } + I64ShrU => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64ShrU); + } + I64Rotl => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Rotl); + } + I64Rotr => { + Validator::validate_binop(context, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64Rotr); + } - F32Abs => { - Validator::validate_unop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Abs); - } - F32Neg => { - Validator::validate_unop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Neg); - } - F32Ceil => { - Validator::validate_unop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Ceil); - } - F32Floor => { - Validator::validate_unop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Floor); - } - F32Trunc => { - Validator::validate_unop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Trunc); - } - F32Nearest => { - Validator::validate_unop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Nearest); - } - F32Sqrt => { - Validator::validate_unop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Sqrt); - } - F32Add => { - Validator::validate_binop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Add); - } - F32Sub => { - Validator::validate_binop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Sub); - } - F32Mul => { - Validator::validate_binop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Mul); - } - F32Div => { - Validator::validate_binop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Div); - } - F32Min => { - Validator::validate_binop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Min); - } - F32Max => { - Validator::validate_binop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Max); - } - F32Copysign => { - Validator::validate_binop(context, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32Copysign); - } + F32Abs => { + Validator::validate_unop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Abs); + } + F32Neg => { + Validator::validate_unop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Neg); + } + F32Ceil => { + Validator::validate_unop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Ceil); + } + F32Floor => { + Validator::validate_unop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Floor); + } + F32Trunc => { + Validator::validate_unop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Trunc); + } + F32Nearest => { + Validator::validate_unop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Nearest); + } + F32Sqrt => { + Validator::validate_unop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Sqrt); + } + F32Add => { + Validator::validate_binop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Add); + } + F32Sub => { + Validator::validate_binop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Sub); + } + F32Mul => { + Validator::validate_binop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Mul); + } + F32Div => { + Validator::validate_binop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Div); + } + F32Min => { + Validator::validate_binop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Min); + } + F32Max => { + Validator::validate_binop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Max); + } + F32Copysign => { + Validator::validate_binop(context, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32Copysign); + } - F64Abs => { - Validator::validate_unop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Abs); - } - F64Neg => { - Validator::validate_unop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Neg); - } - F64Ceil => { - Validator::validate_unop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Ceil); - } - F64Floor => { - Validator::validate_unop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Floor); - } - F64Trunc => { - Validator::validate_unop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Trunc); - } - F64Nearest => { - Validator::validate_unop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Nearest); - } - F64Sqrt => { - Validator::validate_unop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Sqrt); - } - F64Add => { - Validator::validate_binop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Add); - } - F64Sub => { - Validator::validate_binop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Sub); - } - F64Mul => { - Validator::validate_binop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Mul); - } - F64Div => { - Validator::validate_binop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Div); - } - F64Min => { - Validator::validate_binop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Min); - } - F64Max => { - Validator::validate_binop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Max); - } - F64Copysign => { - Validator::validate_binop(context, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64Copysign); - } + F64Abs => { + Validator::validate_unop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Abs); + } + F64Neg => { + Validator::validate_unop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Neg); + } + F64Ceil => { + Validator::validate_unop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Ceil); + } + F64Floor => { + Validator::validate_unop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Floor); + } + F64Trunc => { + Validator::validate_unop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Trunc); + } + F64Nearest => { + Validator::validate_unop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Nearest); + } + F64Sqrt => { + Validator::validate_unop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Sqrt); + } + F64Add => { + Validator::validate_binop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Add); + } + F64Sub => { + Validator::validate_binop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Sub); + } + F64Mul => { + Validator::validate_binop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Mul); + } + F64Div => { + Validator::validate_binop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Div); + } + F64Min => { + Validator::validate_binop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Min); + } + F64Max => { + Validator::validate_binop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Max); + } + F64Copysign => { + Validator::validate_binop(context, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64Copysign); + } - I32WrapI64 => { - Validator::validate_cvtop(context, ValueType::I64, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32WrapI64); - } - I32TruncSF32 => { - Validator::validate_cvtop(context, ValueType::F32, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32TruncSF32); - } - I32TruncUF32 => { - Validator::validate_cvtop(context, ValueType::F32, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32TruncUF32); - } - I32TruncSF64 => { - Validator::validate_cvtop(context, ValueType::F64, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32TruncSF64); - } - I32TruncUF64 => { - Validator::validate_cvtop(context, ValueType::F64, ValueType::I32)?; - context.sink.emit(isa::InstructionInternal::I32TruncUF64); - } - I64ExtendSI32 => { - Validator::validate_cvtop(context, ValueType::I32, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64ExtendSI32); - } - I64ExtendUI32 => { - Validator::validate_cvtop(context, ValueType::I32, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64ExtendUI32); - } - I64TruncSF32 => { - Validator::validate_cvtop(context, ValueType::F32, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64TruncSF32); - } - I64TruncUF32 => { - Validator::validate_cvtop(context, ValueType::F32, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64TruncUF32); - } - I64TruncSF64 => { - Validator::validate_cvtop(context, ValueType::F64, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64TruncSF64); - } - I64TruncUF64 => { - Validator::validate_cvtop(context, ValueType::F64, ValueType::I64)?; - context.sink.emit(isa::InstructionInternal::I64TruncUF64); - } - F32ConvertSI32 => { - Validator::validate_cvtop(context, ValueType::I32, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32ConvertSI32); - } - F32ConvertUI32 => { - Validator::validate_cvtop(context, ValueType::I32, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32ConvertUI32); - } - F32ConvertSI64 => { - Validator::validate_cvtop(context, ValueType::I64, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32ConvertSI64); - } - F32ConvertUI64 => { - Validator::validate_cvtop(context, ValueType::I64, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32ConvertUI64); - } - F32DemoteF64 => { - Validator::validate_cvtop(context, ValueType::F64, ValueType::F32)?; - context.sink.emit(isa::InstructionInternal::F32DemoteF64); - } - F64ConvertSI32 => { - Validator::validate_cvtop(context, ValueType::I32, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64ConvertSI32); - } - F64ConvertUI32 => { - Validator::validate_cvtop(context, ValueType::I32, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64ConvertUI32); - } - F64ConvertSI64 => { - Validator::validate_cvtop(context, ValueType::I64, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64ConvertSI64); - } - F64ConvertUI64 => { - Validator::validate_cvtop(context, ValueType::I64, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64ConvertUI64); - } - F64PromoteF32 => { - Validator::validate_cvtop(context, ValueType::F32, ValueType::F64)?; - context.sink.emit(isa::InstructionInternal::F64PromoteF32); - } + I32WrapI64 => { + Validator::validate_cvtop(context, ValueType::I64, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32WrapI64); + } + I32TruncSF32 => { + Validator::validate_cvtop(context, ValueType::F32, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32TruncSF32); + } + I32TruncUF32 => { + Validator::validate_cvtop(context, ValueType::F32, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32TruncUF32); + } + I32TruncSF64 => { + Validator::validate_cvtop(context, ValueType::F64, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32TruncSF64); + } + I32TruncUF64 => { + Validator::validate_cvtop(context, ValueType::F64, ValueType::I32)?; + context.sink.emit(isa::InstructionInternal::I32TruncUF64); + } + I64ExtendSI32 => { + Validator::validate_cvtop(context, ValueType::I32, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64ExtendSI32); + } + I64ExtendUI32 => { + Validator::validate_cvtop(context, ValueType::I32, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64ExtendUI32); + } + I64TruncSF32 => { + Validator::validate_cvtop(context, ValueType::F32, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64TruncSF32); + } + I64TruncUF32 => { + Validator::validate_cvtop(context, ValueType::F32, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64TruncUF32); + } + I64TruncSF64 => { + Validator::validate_cvtop(context, ValueType::F64, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64TruncSF64); + } + I64TruncUF64 => { + Validator::validate_cvtop(context, ValueType::F64, ValueType::I64)?; + context.sink.emit(isa::InstructionInternal::I64TruncUF64); + } + F32ConvertSI32 => { + Validator::validate_cvtop(context, ValueType::I32, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32ConvertSI32); + } + F32ConvertUI32 => { + Validator::validate_cvtop(context, ValueType::I32, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32ConvertUI32); + } + F32ConvertSI64 => { + Validator::validate_cvtop(context, ValueType::I64, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32ConvertSI64); + } + F32ConvertUI64 => { + Validator::validate_cvtop(context, ValueType::I64, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32ConvertUI64); + } + F32DemoteF64 => { + Validator::validate_cvtop(context, ValueType::F64, ValueType::F32)?; + context.sink.emit(isa::InstructionInternal::F32DemoteF64); + } + F64ConvertSI32 => { + Validator::validate_cvtop(context, ValueType::I32, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64ConvertSI32); + } + F64ConvertUI32 => { + Validator::validate_cvtop(context, ValueType::I32, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64ConvertUI32); + } + F64ConvertSI64 => { + Validator::validate_cvtop(context, ValueType::I64, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64ConvertSI64); + } + F64ConvertUI64 => { + Validator::validate_cvtop(context, ValueType::I64, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64ConvertUI64); + } + F64PromoteF32 => { + Validator::validate_cvtop(context, ValueType::F32, ValueType::F64)?; + context.sink.emit(isa::InstructionInternal::F64PromoteF32); + } - I32ReinterpretF32 => { - Validator::validate_cvtop(context, ValueType::F32, ValueType::I32)?; - context - .sink - .emit(isa::InstructionInternal::I32ReinterpretF32); - } - I64ReinterpretF64 => { - Validator::validate_cvtop(context, ValueType::F64, ValueType::I64)?; - context - .sink - .emit(isa::InstructionInternal::I64ReinterpretF64); - } - F32ReinterpretI32 => { - Validator::validate_cvtop(context, ValueType::I32, ValueType::F32)?; - context - .sink - .emit(isa::InstructionInternal::F32ReinterpretI32); - } - F64ReinterpretI64 => { - Validator::validate_cvtop(context, ValueType::I64, ValueType::F64)?; - context - .sink - .emit(isa::InstructionInternal::F64ReinterpretI64); - } - } + I32ReinterpretF32 => { + Validator::validate_cvtop(context, ValueType::F32, ValueType::I32)?; + context + .sink + .emit(isa::InstructionInternal::I32ReinterpretF32); + } + I64ReinterpretF64 => { + Validator::validate_cvtop(context, ValueType::F64, ValueType::I64)?; + context + .sink + .emit(isa::InstructionInternal::I64ReinterpretF64); + } + F32ReinterpretI32 => { + Validator::validate_cvtop(context, ValueType::I32, ValueType::F32)?; + context + .sink + .emit(isa::InstructionInternal::F32ReinterpretI32); + } + F64ReinterpretI64 => { + Validator::validate_cvtop(context, ValueType::I64, ValueType::F64)?; + context + .sink + .emit(isa::InstructionInternal::F64ReinterpretI64); + } + } - Ok(Outcome::NextInstruction) - } + Ok(Outcome::NextInstruction) + } } /// Function validator. struct Validator; impl Validator { - fn validate_const( - context: &mut FunctionValidationContext, - value_type: ValueType, - ) -> Result<(), Error> { - push_value(&mut context.value_stack, value_type.into())?; - Ok(()) - } + fn validate_const( + context: &mut FunctionValidationContext, + value_type: ValueType, + ) -> Result<(), Error> { + push_value(&mut context.value_stack, value_type.into())?; + Ok(()) + } - fn validate_unop( - context: &mut FunctionValidationContext, - value_type: ValueType, - ) -> Result<(), Error> { - pop_value( - &mut context.value_stack, - &context.frame_stack, - value_type.into(), - )?; - push_value(&mut context.value_stack, value_type.into())?; - Ok(()) - } + fn validate_unop( + context: &mut FunctionValidationContext, + value_type: ValueType, + ) -> Result<(), Error> { + pop_value( + &mut context.value_stack, + &context.frame_stack, + value_type.into(), + )?; + push_value(&mut context.value_stack, value_type.into())?; + Ok(()) + } - fn validate_binop( - context: &mut FunctionValidationContext, - value_type: ValueType, - ) -> Result<(), Error> { - pop_value( - &mut context.value_stack, - &context.frame_stack, - value_type.into(), - )?; - pop_value( - &mut context.value_stack, - &context.frame_stack, - value_type.into(), - )?; - push_value(&mut context.value_stack, value_type.into())?; - Ok(()) - } + fn validate_binop( + context: &mut FunctionValidationContext, + value_type: ValueType, + ) -> Result<(), Error> { + pop_value( + &mut context.value_stack, + &context.frame_stack, + value_type.into(), + )?; + pop_value( + &mut context.value_stack, + &context.frame_stack, + value_type.into(), + )?; + push_value(&mut context.value_stack, value_type.into())?; + Ok(()) + } - fn validate_testop( - context: &mut FunctionValidationContext, - value_type: ValueType, - ) -> Result<(), Error> { - pop_value( - &mut context.value_stack, - &context.frame_stack, - value_type.into(), - )?; - push_value(&mut context.value_stack, ValueType::I32.into())?; - Ok(()) - } + fn validate_testop( + context: &mut FunctionValidationContext, + value_type: ValueType, + ) -> Result<(), Error> { + pop_value( + &mut context.value_stack, + &context.frame_stack, + value_type.into(), + )?; + push_value(&mut context.value_stack, ValueType::I32.into())?; + Ok(()) + } - fn validate_relop( - context: &mut FunctionValidationContext, - value_type: ValueType, - ) -> Result<(), Error> { - pop_value( - &mut context.value_stack, - &context.frame_stack, - value_type.into(), - )?; - pop_value( - &mut context.value_stack, - &context.frame_stack, - value_type.into(), - )?; - push_value(&mut context.value_stack, ValueType::I32.into())?; - Ok(()) - } + fn validate_relop( + context: &mut FunctionValidationContext, + value_type: ValueType, + ) -> Result<(), Error> { + pop_value( + &mut context.value_stack, + &context.frame_stack, + value_type.into(), + )?; + pop_value( + &mut context.value_stack, + &context.frame_stack, + value_type.into(), + )?; + push_value(&mut context.value_stack, ValueType::I32.into())?; + Ok(()) + } - fn validate_cvtop( - context: &mut FunctionValidationContext, - value_type1: ValueType, - value_type2: ValueType, - ) -> Result<(), Error> { - pop_value( - &mut context.value_stack, - &context.frame_stack, - value_type1.into(), - )?; - push_value(&mut context.value_stack, value_type2.into())?; - Ok(()) - } + fn validate_cvtop( + context: &mut FunctionValidationContext, + value_type1: ValueType, + value_type2: ValueType, + ) -> Result<(), Error> { + pop_value( + &mut context.value_stack, + &context.frame_stack, + value_type1.into(), + )?; + push_value(&mut context.value_stack, value_type2.into())?; + Ok(()) + } - fn validate_drop(context: &mut FunctionValidationContext) -> Result<(), Error> { - pop_value( - &mut context.value_stack, - &context.frame_stack, - StackValueType::Any, - )?; - Ok(()) - } + fn validate_drop(context: &mut FunctionValidationContext) -> Result<(), Error> { + pop_value( + &mut context.value_stack, + &context.frame_stack, + StackValueType::Any, + )?; + Ok(()) + } - fn validate_select(context: &mut FunctionValidationContext) -> Result<(), Error> { - pop_value( - &mut context.value_stack, - &context.frame_stack, - ValueType::I32.into(), - )?; - let select_type = pop_value( - &mut context.value_stack, - &context.frame_stack, - StackValueType::Any, - )?; - pop_value(&mut context.value_stack, &context.frame_stack, select_type)?; - push_value(&mut context.value_stack, select_type)?; - Ok(()) - } + fn validate_select(context: &mut FunctionValidationContext) -> Result<(), Error> { + pop_value( + &mut context.value_stack, + &context.frame_stack, + ValueType::I32.into(), + )?; + let select_type = pop_value( + &mut context.value_stack, + &context.frame_stack, + StackValueType::Any, + )?; + pop_value(&mut context.value_stack, &context.frame_stack, select_type)?; + push_value(&mut context.value_stack, select_type)?; + Ok(()) + } - fn validate_get_local( - context: &mut FunctionValidationContext, - index: u32, - ) -> Result<(), Error> { - let local_type = require_local(&context.locals, index)?; - push_value(&mut context.value_stack, local_type.into())?; - Ok(()) - } + fn validate_get_local( + context: &mut FunctionValidationContext, + index: u32, + ) -> Result<(), Error> { + let local_type = require_local(&context.locals, index)?; + push_value(&mut context.value_stack, local_type.into())?; + Ok(()) + } - fn validate_set_local( - context: &mut FunctionValidationContext, - index: u32, - ) -> Result<(), Error> { - let local_type = require_local(&context.locals, index)?; - let value_type = pop_value( - &mut context.value_stack, - &context.frame_stack, - StackValueType::Any, - )?; - if StackValueType::from(local_type) != value_type { - return Err(Error(format!( - "Trying to update local {} of type {:?} with value of type {:?}", - index, local_type, value_type - ))); - } - Ok(()) - } + fn validate_set_local( + context: &mut FunctionValidationContext, + index: u32, + ) -> Result<(), Error> { + let local_type = require_local(&context.locals, index)?; + let value_type = pop_value( + &mut context.value_stack, + &context.frame_stack, + StackValueType::Any, + )?; + if StackValueType::from(local_type) != value_type { + return Err(Error(format!( + "Trying to update local {} of type {:?} with value of type {:?}", + index, local_type, value_type + ))); + } + Ok(()) + } - fn validate_tee_local( - context: &mut FunctionValidationContext, - index: u32, - ) -> Result<(), Error> { - let local_type = require_local(&context.locals, index)?; - tee_value( - &mut context.value_stack, - &context.frame_stack, - local_type.into(), - )?; - Ok(()) - } + fn validate_tee_local( + context: &mut FunctionValidationContext, + index: u32, + ) -> Result<(), Error> { + let local_type = require_local(&context.locals, index)?; + tee_value( + &mut context.value_stack, + &context.frame_stack, + local_type.into(), + )?; + Ok(()) + } - fn validate_get_global( - context: &mut FunctionValidationContext, - index: u32, - ) -> Result<(), Error> { - let global_type: StackValueType = { - let global = context.module.require_global(index, None)?; - global.content_type().into() - }; - push_value(&mut context.value_stack, global_type)?; - Ok(()) - } + fn validate_get_global( + context: &mut FunctionValidationContext, + index: u32, + ) -> Result<(), Error> { + let global_type: StackValueType = { + let global = context.module.require_global(index, None)?; + global.content_type().into() + }; + push_value(&mut context.value_stack, global_type)?; + Ok(()) + } - fn validate_set_global( - context: &mut FunctionValidationContext, - index: u32, - ) -> Result<(), Error> { - let global_type: StackValueType = { - let global = context.module.require_global(index, Some(true))?; - global.content_type().into() - }; - let value_type = pop_value( - &mut context.value_stack, - &context.frame_stack, - StackValueType::Any, - )?; - if global_type != value_type { - return Err(Error(format!( - "Trying to update global {} of type {:?} with value of type {:?}", - index, global_type, value_type - ))); - } - Ok(()) - } + fn validate_set_global( + context: &mut FunctionValidationContext, + index: u32, + ) -> Result<(), Error> { + let global_type: StackValueType = { + let global = context.module.require_global(index, Some(true))?; + global.content_type().into() + }; + let value_type = pop_value( + &mut context.value_stack, + &context.frame_stack, + StackValueType::Any, + )?; + if global_type != value_type { + return Err(Error(format!( + "Trying to update global {} of type {:?} with value of type {:?}", + index, global_type, value_type + ))); + } + Ok(()) + } - fn validate_load( - context: &mut FunctionValidationContext, - align: u32, - max_align: u32, - value_type: ValueType, - ) -> Result<(), Error> { - if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align { - return Err(Error(format!( - "Too large memory alignment 2^{} (expected at most {})", - align, max_align - ))); - } + fn validate_load( + context: &mut FunctionValidationContext, + align: u32, + max_align: u32, + value_type: ValueType, + ) -> Result<(), Error> { + if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align { + return Err(Error(format!( + "Too large memory alignment 2^{} (expected at most {})", + align, max_align + ))); + } - pop_value( - &mut context.value_stack, - &context.frame_stack, - ValueType::I32.into(), - )?; - context.module.require_memory(DEFAULT_MEMORY_INDEX)?; - push_value(&mut context.value_stack, value_type.into())?; - Ok(()) - } + pop_value( + &mut context.value_stack, + &context.frame_stack, + ValueType::I32.into(), + )?; + context.module.require_memory(DEFAULT_MEMORY_INDEX)?; + push_value(&mut context.value_stack, value_type.into())?; + Ok(()) + } - fn validate_store( - context: &mut FunctionValidationContext, - align: u32, - max_align: u32, - value_type: ValueType, - ) -> Result<(), Error> { - if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align { - return Err(Error(format!( - "Too large memory alignment 2^{} (expected at most {})", - align, max_align - ))); - } + fn validate_store( + context: &mut FunctionValidationContext, + align: u32, + max_align: u32, + value_type: ValueType, + ) -> Result<(), Error> { + if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align { + return Err(Error(format!( + "Too large memory alignment 2^{} (expected at most {})", + align, max_align + ))); + } - context.module.require_memory(DEFAULT_MEMORY_INDEX)?; - pop_value( - &mut context.value_stack, - &context.frame_stack, - value_type.into(), - )?; - pop_value( - &mut context.value_stack, - &context.frame_stack, - ValueType::I32.into(), - )?; - Ok(()) - } + context.module.require_memory(DEFAULT_MEMORY_INDEX)?; + pop_value( + &mut context.value_stack, + &context.frame_stack, + value_type.into(), + )?; + pop_value( + &mut context.value_stack, + &context.frame_stack, + ValueType::I32.into(), + )?; + Ok(()) + } - fn validate_br(context: &mut FunctionValidationContext, depth: u32) -> Result<(), Error> { - let (frame_type, frame_block_type) = { - let frame = require_label(depth, &context.frame_stack)?; - (frame.frame_type, frame.block_type) - }; - if !frame_type.is_loop() { - if let BlockType::Value(value_type) = frame_block_type { - tee_value( - &mut context.value_stack, - &context.frame_stack, - value_type.into(), - )?; - } - } - Ok(()) - } + fn validate_br(context: &mut FunctionValidationContext, depth: u32) -> Result<(), Error> { + let (frame_type, frame_block_type) = { + let frame = require_label(depth, &context.frame_stack)?; + (frame.frame_type, frame.block_type) + }; + if !frame_type.is_loop() { + if let BlockType::Value(value_type) = frame_block_type { + tee_value( + &mut context.value_stack, + &context.frame_stack, + value_type.into(), + )?; + } + } + Ok(()) + } - fn validate_br_if(context: &mut FunctionValidationContext, depth: u32) -> Result<(), Error> { - pop_value( - &mut context.value_stack, - &context.frame_stack, - ValueType::I32.into(), - )?; + fn validate_br_if(context: &mut FunctionValidationContext, depth: u32) -> Result<(), Error> { + pop_value( + &mut context.value_stack, + &context.frame_stack, + ValueType::I32.into(), + )?; - let (frame_type, frame_block_type) = { - let frame = require_label(depth, &context.frame_stack)?; - (frame.frame_type, frame.block_type) - }; - if !frame_type.is_loop() { - if let BlockType::Value(value_type) = frame_block_type { - tee_value( - &mut context.value_stack, - &context.frame_stack, - value_type.into(), - )?; - } - } - Ok(()) - } + let (frame_type, frame_block_type) = { + let frame = require_label(depth, &context.frame_stack)?; + (frame.frame_type, frame.block_type) + }; + if !frame_type.is_loop() { + if let BlockType::Value(value_type) = frame_block_type { + tee_value( + &mut context.value_stack, + &context.frame_stack, + value_type.into(), + )?; + } + } + Ok(()) + } - fn validate_br_table( - context: &mut FunctionValidationContext, - table: &[u32], - default: u32, - ) -> Result<(), Error> { - let required_block_type: BlockType = { - let default_block = require_label(default, &context.frame_stack)?; - let required_block_type = if !default_block.frame_type.is_loop() { - default_block.block_type - } else { - BlockType::NoResult - }; + fn validate_br_table( + context: &mut FunctionValidationContext, + table: &[u32], + default: u32, + ) -> Result<(), Error> { + let required_block_type: BlockType = { + let default_block = require_label(default, &context.frame_stack)?; + let required_block_type = if !default_block.frame_type.is_loop() { + default_block.block_type + } else { + BlockType::NoResult + }; - for label in table { - let label_block = require_label(*label, &context.frame_stack)?; - let label_block_type = if !label_block.frame_type.is_loop() { - label_block.block_type - } else { - BlockType::NoResult - }; - if required_block_type != label_block_type { - return Err(Error(format!( - "Labels in br_table points to block of different types: {:?} and {:?}", - required_block_type, label_block.block_type - ))); - } - } - required_block_type - }; + for label in table { + let label_block = require_label(*label, &context.frame_stack)?; + let label_block_type = if !label_block.frame_type.is_loop() { + label_block.block_type + } else { + BlockType::NoResult + }; + if required_block_type != label_block_type { + return Err(Error(format!( + "Labels in br_table points to block of different types: {:?} and {:?}", + required_block_type, label_block.block_type + ))); + } + } + required_block_type + }; - pop_value( - &mut context.value_stack, - &context.frame_stack, - ValueType::I32.into(), - )?; - if let BlockType::Value(value_type) = required_block_type { - tee_value( - &mut context.value_stack, - &context.frame_stack, - value_type.into(), - )?; - } + pop_value( + &mut context.value_stack, + &context.frame_stack, + ValueType::I32.into(), + )?; + if let BlockType::Value(value_type) = required_block_type { + tee_value( + &mut context.value_stack, + &context.frame_stack, + value_type.into(), + )?; + } - Ok(()) - } + Ok(()) + } - fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<(), Error> { - let (argument_types, return_type) = context.module.require_function(idx)?; - for argument_type in argument_types.iter().rev() { - pop_value( - &mut context.value_stack, - &context.frame_stack, - (*argument_type).into(), - )?; - } - if let BlockType::Value(value_type) = return_type { - push_value(&mut context.value_stack, value_type.into())?; - } - Ok(()) - } + fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<(), Error> { + let (argument_types, return_type) = context.module.require_function(idx)?; + for argument_type in argument_types.iter().rev() { + pop_value( + &mut context.value_stack, + &context.frame_stack, + (*argument_type).into(), + )?; + } + if let BlockType::Value(value_type) = return_type { + push_value(&mut context.value_stack, value_type.into())?; + } + Ok(()) + } - fn validate_call_indirect( - context: &mut FunctionValidationContext, - idx: u32, - ) -> Result<(), Error> { - { - let table = context.module.require_table(DEFAULT_TABLE_INDEX)?; - if table.elem_type() != TableElementType::AnyFunc { - return Err(Error(format!( - "Table {} has element type {:?} while `anyfunc` expected", - idx, - table.elem_type() - ))); - } - } + fn validate_call_indirect( + context: &mut FunctionValidationContext, + idx: u32, + ) -> Result<(), Error> { + { + let table = context.module.require_table(DEFAULT_TABLE_INDEX)?; + if table.elem_type() != TableElementType::AnyFunc { + return Err(Error(format!( + "Table {} has element type {:?} while `anyfunc` expected", + idx, + table.elem_type() + ))); + } + } - pop_value( - &mut context.value_stack, - &context.frame_stack, - ValueType::I32.into(), - )?; - let (argument_types, return_type) = context.module.require_function_type(idx)?; - for argument_type in argument_types.iter().rev() { - pop_value( - &mut context.value_stack, - &context.frame_stack, - (*argument_type).into(), - )?; - } - if let BlockType::Value(value_type) = return_type { - push_value(&mut context.value_stack, value_type.into())?; - } - Ok(()) - } + pop_value( + &mut context.value_stack, + &context.frame_stack, + ValueType::I32.into(), + )?; + let (argument_types, return_type) = context.module.require_function_type(idx)?; + for argument_type in argument_types.iter().rev() { + pop_value( + &mut context.value_stack, + &context.frame_stack, + (*argument_type).into(), + )?; + } + if let BlockType::Value(value_type) = return_type { + push_value(&mut context.value_stack, value_type.into())?; + } + Ok(()) + } - fn validate_current_memory(context: &mut FunctionValidationContext) -> Result<(), Error> { - context.module.require_memory(DEFAULT_MEMORY_INDEX)?; - push_value(&mut context.value_stack, ValueType::I32.into())?; - Ok(()) - } + fn validate_current_memory(context: &mut FunctionValidationContext) -> Result<(), Error> { + context.module.require_memory(DEFAULT_MEMORY_INDEX)?; + push_value(&mut context.value_stack, ValueType::I32.into())?; + Ok(()) + } - fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<(), Error> { - context.module.require_memory(DEFAULT_MEMORY_INDEX)?; - pop_value( - &mut context.value_stack, - &context.frame_stack, - ValueType::I32.into(), - )?; - push_value(&mut context.value_stack, ValueType::I32.into())?; - Ok(()) - } + fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<(), Error> { + context.module.require_memory(DEFAULT_MEMORY_INDEX)?; + pop_value( + &mut context.value_stack, + &context.frame_stack, + ValueType::I32.into(), + )?; + push_value(&mut context.value_stack, ValueType::I32.into())?; + Ok(()) + } } /// Function validation context. struct FunctionValidationContext<'a> { - /// Wasm module - module: &'a ModuleContext, - /// Current instruction position. - position: usize, - /// Local variables. - locals: Locals<'a>, - /// Value stack. - value_stack: StackWithLimit, - /// Frame stack. - frame_stack: StackWithLimit, - /// Function return type. - return_type: BlockType, - /// A sink used to emit optimized code. - sink: Sink, + /// Wasm module + module: &'a ModuleContext, + /// Current instruction position. + position: usize, + /// Local variables. + locals: Locals<'a>, + /// Value stack. + value_stack: StackWithLimit, + /// Frame stack. + frame_stack: StackWithLimit, + /// Function return type. + return_type: BlockType, + /// A sink used to emit optimized code. + sink: Sink, } impl<'a> FunctionValidationContext<'a> { - fn new( - module: &'a ModuleContext, - locals: Locals<'a>, - value_stack_limit: usize, - frame_stack_limit: usize, - return_type: BlockType, - size_estimate: usize, - ) -> Self { - FunctionValidationContext { - module: module, - position: 0, - locals: locals, - value_stack: StackWithLimit::with_limit(value_stack_limit), - frame_stack: StackWithLimit::with_limit(frame_stack_limit), - return_type: return_type, - sink: Sink::with_instruction_capacity(size_estimate), - } - } + fn new( + module: &'a ModuleContext, + locals: Locals<'a>, + value_stack_limit: usize, + frame_stack_limit: usize, + return_type: BlockType, + size_estimate: usize, + ) -> Self { + FunctionValidationContext { + module: module, + position: 0, + locals: locals, + value_stack: StackWithLimit::with_limit(value_stack_limit), + frame_stack: StackWithLimit::with_limit(frame_stack_limit), + return_type: return_type, + sink: Sink::with_instruction_capacity(size_estimate), + } + } - fn return_type(&self) -> Result { - Ok(self.return_type) - } + fn return_type(&self) -> Result { + Ok(self.return_type) + } - fn into_code(self) -> isa::Instructions { - self.sink.into_inner() - } + fn into_code(self) -> isa::Instructions { + self.sink.into_inner() + } } fn make_top_frame_polymorphic( - value_stack: &mut StackWithLimit, - frame_stack: &mut StackWithLimit, + value_stack: &mut StackWithLimit, + frame_stack: &mut StackWithLimit, ) { - let frame = frame_stack - .top_mut() - .expect("make_top_frame_polymorphic is called with empty frame stack"); - value_stack.resize(frame.value_stack_len, StackValueType::Any); - frame.polymorphic_stack = true; + let frame = frame_stack + .top_mut() + .expect("make_top_frame_polymorphic is called with empty frame stack"); + value_stack.resize(frame.value_stack_len, StackValueType::Any); + frame.polymorphic_stack = true; } fn push_value( - value_stack: &mut StackWithLimit, - value_type: StackValueType, + value_stack: &mut StackWithLimit, + value_type: StackValueType, ) -> Result<(), Error> { - Ok(value_stack.push(value_type.into())?) + Ok(value_stack.push(value_type.into())?) } // TODO: Rename value_type -> expected_value_ty fn pop_value( - value_stack: &mut StackWithLimit, - frame_stack: &StackWithLimit, - value_type: StackValueType, + value_stack: &mut StackWithLimit, + frame_stack: &StackWithLimit, + value_type: StackValueType, ) -> Result { - let (is_stack_polymorphic, label_value_stack_len) = { - let frame = top_label(frame_stack); - (frame.polymorphic_stack, frame.value_stack_len) - }; - let stack_is_empty = value_stack.len() == label_value_stack_len; - let actual_value = if stack_is_empty && is_stack_polymorphic { - StackValueType::Any - } else { - let value_stack_min = frame_stack - .top() - .expect("at least 1 topmost block") - .value_stack_len; - if value_stack.len() <= value_stack_min { - return Err(Error("Trying to access parent frame stack values.".into())); - } - value_stack.pop()? - }; - match actual_value { - StackValueType::Specific(stack_value_type) if stack_value_type == value_type => { - Ok(actual_value) - } - StackValueType::Any => Ok(actual_value), - stack_value_type @ _ => Err(Error(format!( - "Expected value of type {:?} on top of stack. Got {:?}", - value_type, stack_value_type - ))), - } + let (is_stack_polymorphic, label_value_stack_len) = { + let frame = top_label(frame_stack); + (frame.polymorphic_stack, frame.value_stack_len) + }; + let stack_is_empty = value_stack.len() == label_value_stack_len; + let actual_value = if stack_is_empty && is_stack_polymorphic { + StackValueType::Any + } else { + let value_stack_min = frame_stack + .top() + .expect("at least 1 topmost block") + .value_stack_len; + if value_stack.len() <= value_stack_min { + return Err(Error("Trying to access parent frame stack values.".into())); + } + value_stack.pop()? + }; + match actual_value { + StackValueType::Specific(stack_value_type) if stack_value_type == value_type => { + Ok(actual_value) + } + StackValueType::Any => Ok(actual_value), + stack_value_type @ _ => Err(Error(format!( + "Expected value of type {:?} on top of stack. Got {:?}", + value_type, stack_value_type + ))), + } } fn tee_value( - value_stack: &mut StackWithLimit, - frame_stack: &StackWithLimit, - value_type: StackValueType, + value_stack: &mut StackWithLimit, + frame_stack: &StackWithLimit, + value_type: StackValueType, ) -> Result<(), Error> { - let _ = pop_value(value_stack, frame_stack, value_type)?; - push_value(value_stack, value_type)?; - Ok(()) + let _ = pop_value(value_stack, frame_stack, value_type)?; + push_value(value_stack, value_type)?; + Ok(()) } fn push_label( - frame_type: BlockFrameType, - block_type: BlockType, - position: usize, - value_stack: &StackWithLimit, - frame_stack: &mut StackWithLimit, + frame_type: BlockFrameType, + block_type: BlockType, + position: usize, + value_stack: &StackWithLimit, + frame_stack: &mut StackWithLimit, ) -> Result<(), Error> { - Ok(frame_stack.push(BlockFrame { - frame_type: frame_type, - block_type: block_type, - begin_position: position, - value_stack_len: value_stack.len(), - polymorphic_stack: false, - })?) + Ok(frame_stack.push(BlockFrame { + frame_type: frame_type, + block_type: block_type, + begin_position: position, + value_stack_len: value_stack.len(), + polymorphic_stack: false, + })?) } // TODO: Refactor fn pop_label( - value_stack: &mut StackWithLimit, - frame_stack: &mut StackWithLimit, + value_stack: &mut StackWithLimit, + frame_stack: &mut StackWithLimit, ) -> Result<(), Error> { - // Don't pop frame yet. This is essential since we still might pop values from the value stack - // and this in turn requires current frame to check whether or not we've reached - // unreachable. - let block_type = frame_stack.top()?.block_type; - match block_type { - BlockType::NoResult => (), - BlockType::Value(required_value_type) => { - let _ = pop_value( - value_stack, - frame_stack, - StackValueType::Specific(required_value_type), - )?; - } - } + // Don't pop frame yet. This is essential since we still might pop values from the value stack + // and this in turn requires current frame to check whether or not we've reached + // unreachable. + let block_type = frame_stack.top()?.block_type; + match block_type { + BlockType::NoResult => (), + BlockType::Value(required_value_type) => { + let _ = pop_value( + value_stack, + frame_stack, + StackValueType::Specific(required_value_type), + )?; + } + } - let frame = frame_stack.pop()?; - if value_stack.len() != frame.value_stack_len { - return Err(Error(format!( - "Unexpected stack height {}, expected {}", - value_stack.len(), - frame.value_stack_len - ))); - } + let frame = frame_stack.pop()?; + if value_stack.len() != frame.value_stack_len { + return Err(Error(format!( + "Unexpected stack height {}, expected {}", + value_stack.len(), + frame.value_stack_len + ))); + } - Ok(()) + Ok(()) } fn top_label(frame_stack: &StackWithLimit) -> &BlockFrame { - frame_stack - .top() - .expect("this function can't be called with empty frame stack") + frame_stack + .top() + .expect("this function can't be called with empty frame stack") } fn require_label( - depth: u32, - frame_stack: &StackWithLimit, + depth: u32, + frame_stack: &StackWithLimit, ) -> Result<&BlockFrame, Error> { - Ok(frame_stack.get(depth as usize)?) + Ok(frame_stack.get(depth as usize)?) } fn require_target( - depth: u32, - value_stack: &StackWithLimit, - frame_stack: &StackWithLimit, + depth: u32, + value_stack: &StackWithLimit, + frame_stack: &StackWithLimit, ) -> Target { - let is_stack_polymorphic = top_label(frame_stack).polymorphic_stack; - let frame = - require_label(depth, frame_stack).expect("require_target called with a bogus depth"); + let is_stack_polymorphic = top_label(frame_stack).polymorphic_stack; + let frame = + require_label(depth, frame_stack).expect("require_target called with a bogus depth"); - // Find out how many values we need to keep (copy to the new stack location after the drop). - let keep: isa::Keep = match (frame.frame_type, frame.block_type) { - // A loop doesn't take a value upon a branch. It can return value - // only via reaching it's closing `End` operator. - (BlockFrameType::Loop { .. }, _) => isa::Keep::None, + // Find out how many values we need to keep (copy to the new stack location after the drop). + let keep: isa::Keep = match (frame.frame_type, frame.block_type) { + // A loop doesn't take a value upon a branch. It can return value + // only via reaching it's closing `End` operator. + (BlockFrameType::Loop { .. }, _) => isa::Keep::None, - (_, BlockType::Value(_)) => isa::Keep::Single, - (_, BlockType::NoResult) => isa::Keep::None, - }; + (_, BlockType::Value(_)) => isa::Keep::Single, + (_, BlockType::NoResult) => isa::Keep::None, + }; - // Find out how many values we need to discard. - let drop = if is_stack_polymorphic { - // Polymorphic stack is a weird state. Fortunately, it always about the code that - // will not be executed, so we don't bother and return 0 here. - 0 - } else { - let value_stack_height = value_stack.len(); - assert!( + // Find out how many values we need to discard. + let drop = if is_stack_polymorphic { + // Polymorphic stack is a weird state. Fortunately, it always about the code that + // will not be executed, so we don't bother and return 0 here. + 0 + } else { + let value_stack_height = value_stack.len(); + assert!( value_stack_height >= frame.value_stack_len, "Stack underflow detected: value stack height ({}) is lower than minimum stack len ({})", value_stack_height, frame.value_stack_len, ); - assert!( - (value_stack_height as u32 - frame.value_stack_len as u32) >= keep as u32, - "Stack underflow detected: asked to keep {:?} values, but there are only {}", - keep, - value_stack_height as u32 - frame.value_stack_len as u32, - ); - (value_stack_height as u32 - frame.value_stack_len as u32) - keep as u32 - }; + assert!( + (value_stack_height as u32 - frame.value_stack_len as u32) >= keep as u32, + "Stack underflow detected: asked to keep {:?} values, but there are only {}", + keep, + value_stack_height as u32 - frame.value_stack_len as u32, + ); + (value_stack_height as u32 - frame.value_stack_len as u32) - keep as u32 + }; - Target { - label: frame.frame_type.br_destination(), - drop_keep: isa::DropKeep { drop, keep }, - } + Target { + label: frame.frame_type.br_destination(), + drop_keep: isa::DropKeep { drop, keep }, + } } fn drop_keep_return( - locals: &Locals, - value_stack: &StackWithLimit, - frame_stack: &StackWithLimit, + locals: &Locals, + value_stack: &StackWithLimit, + frame_stack: &StackWithLimit, ) -> isa::DropKeep { - assert!( - !frame_stack.is_empty(), - "drop_keep_return can't be called with the frame stack empty" - ); + assert!( + !frame_stack.is_empty(), + "drop_keep_return can't be called with the frame stack empty" + ); - let deepest = (frame_stack.len() - 1) as u32; - let mut drop_keep = require_target(deepest, value_stack, frame_stack).drop_keep; + let deepest = (frame_stack.len() - 1) as u32; + let mut drop_keep = require_target(deepest, value_stack, frame_stack).drop_keep; - // Drop all local variables and parameters upon exit. - drop_keep.drop += locals.count(); + // Drop all local variables and parameters upon exit. + drop_keep.drop += locals.count(); - drop_keep + drop_keep } fn require_local(locals: &Locals, idx: u32) -> Result { - Ok(locals.type_of_local(idx)?) + Ok(locals.type_of_local(idx)?) } /// See stack layout definition in mod isa. fn relative_local_depth( - idx: u32, - locals: &Locals, - value_stack: &StackWithLimit, + idx: u32, + locals: &Locals, + value_stack: &StackWithLimit, ) -> Result { - let value_stack_height = value_stack.len() as u32; - let locals_and_params_count = locals.count(); + let value_stack_height = value_stack.len() as u32; + let locals_and_params_count = locals.count(); - let depth = value_stack_height - .checked_add(locals_and_params_count) - .and_then(|x| x.checked_sub(idx)) - .ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?; - Ok(depth) + let depth = value_stack_height + .checked_add(locals_and_params_count) + .and_then(|x| x.checked_sub(idx)) + .ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?; + Ok(depth) } /// The target of a branch instruction. @@ -1807,8 +1805,8 @@ fn relative_local_depth( /// for emitting code right away with labels resolved later. #[derive(Clone)] struct Target { - label: LabelId, - drop_keep: isa::DropKeep, + label: LabelId, + drop_keep: isa::DropKeep, } /// Identifier of a label. @@ -1817,143 +1815,142 @@ struct LabelId(usize); #[derive(Debug, PartialEq, Eq)] enum Label { - Resolved(u32), - NotResolved, + Resolved(u32), + NotResolved, } struct Sink { - ins: isa::Instructions, - labels: Vec<(Label, Vec)>, + ins: isa::Instructions, + labels: Vec<(Label, Vec)>, } impl Sink { - fn with_instruction_capacity(capacity: usize) -> Sink { - Sink { - ins: isa::Instructions::with_capacity(capacity), - labels: Vec::new(), - } - } + fn with_instruction_capacity(capacity: usize) -> Sink { + Sink { + ins: isa::Instructions::with_capacity(capacity), + labels: Vec::new(), + } + } - fn cur_pc(&self) -> u32 { - self.ins.current_pc() - } + fn cur_pc(&self) -> u32 { + self.ins.current_pc() + } - fn pc_or_placeholder isa::Reloc>( - &mut self, - label: LabelId, - reloc_creator: F, - ) -> u32 { - match self.labels[label.0] { - (Label::Resolved(dst_pc), _) => dst_pc, - (Label::NotResolved, ref mut unresolved) => { - unresolved.push(reloc_creator()); - u32::max_value() - } - } - } + fn pc_or_placeholder isa::Reloc>( + &mut self, + label: LabelId, + reloc_creator: F, + ) -> u32 { + match self.labels[label.0] { + (Label::Resolved(dst_pc), _) => dst_pc, + (Label::NotResolved, ref mut unresolved) => { + unresolved.push(reloc_creator()); + u32::max_value() + } + } + } - fn emit(&mut self, instruction: isa::InstructionInternal) { - self.ins.push(instruction); - } + fn emit(&mut self, instruction: isa::InstructionInternal) { + self.ins.push(instruction); + } - fn emit_br(&mut self, target: Target) { - let Target { label, drop_keep } = target; - let pc = self.cur_pc(); - let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); - self.ins.push(isa::InstructionInternal::Br(isa::Target { - dst_pc, - drop_keep: drop_keep.into(), - })); - } + fn emit_br(&mut self, target: Target) { + let Target { label, drop_keep } = target; + let pc = self.cur_pc(); + let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); + self.ins.push(isa::InstructionInternal::Br(isa::Target { + dst_pc, + drop_keep: drop_keep.into(), + })); + } - fn emit_br_eqz(&mut self, target: Target) { - let Target { label, drop_keep } = target; - let pc = self.cur_pc(); - let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); - self.ins - .push(isa::InstructionInternal::BrIfEqz(isa::Target { - dst_pc, - drop_keep: drop_keep.into(), - })); - } + fn emit_br_eqz(&mut self, target: Target) { + let Target { label, drop_keep } = target; + let pc = self.cur_pc(); + let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); + self.ins + .push(isa::InstructionInternal::BrIfEqz(isa::Target { + dst_pc, + drop_keep: drop_keep.into(), + })); + } - fn emit_br_nez(&mut self, target: Target) { - let Target { label, drop_keep } = target; - let pc = self.cur_pc(); - let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); - self.ins - .push(isa::InstructionInternal::BrIfNez(isa::Target { - dst_pc, - drop_keep: drop_keep.into(), - })); - } + fn emit_br_nez(&mut self, target: Target) { + let Target { label, drop_keep } = target; + let pc = self.cur_pc(); + let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); + self.ins + .push(isa::InstructionInternal::BrIfNez(isa::Target { + dst_pc, + drop_keep: drop_keep.into(), + })); + } - fn emit_br_table(&mut self, targets: &[Target], default: Target) { - use core::iter; + fn emit_br_table(&mut self, targets: &[Target], default: Target) { + use core::iter; - let pc = self.cur_pc(); + let pc = self.cur_pc(); - self.ins.push(isa::InstructionInternal::BrTable { - count: targets.len() as u32 + 1, - }); + self.ins.push(isa::InstructionInternal::BrTable { + count: targets.len() as u32 + 1, + }); - for (idx, &Target { label, drop_keep }) in - targets.iter().chain(iter::once(&default)).enumerate() - { - let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::BrTable { pc, idx }); - self.ins - .push(isa::InstructionInternal::BrTableTarget(isa::Target { - dst_pc, - drop_keep: drop_keep.into(), - })); - } - } + for (idx, &Target { label, drop_keep }) in + targets.iter().chain(iter::once(&default)).enumerate() + { + let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::BrTable { pc, idx }); + self.ins + .push(isa::InstructionInternal::BrTableTarget(isa::Target { + dst_pc, + drop_keep: drop_keep.into(), + })); + } + } - /// Create a new unresolved label. - fn new_label(&mut self) -> LabelId { - let label_idx = self.labels.len(); - self.labels.push((Label::NotResolved, Vec::new())); - LabelId(label_idx) - } + /// Create a new unresolved label. + fn new_label(&mut self) -> LabelId { + let label_idx = self.labels.len(); + self.labels.push((Label::NotResolved, Vec::new())); + LabelId(label_idx) + } - /// Resolve the label at the current position. - /// - /// Panics if the label is already resolved. - fn resolve_label(&mut self, label: LabelId) { - use core::mem; + /// Resolve the label at the current position. + /// + /// Panics if the label is already resolved. + fn resolve_label(&mut self, label: LabelId) { + use core::mem; - if let (Label::Resolved(_), _) = self.labels[label.0] { - panic!("Trying to resolve already resolved label"); - } - let dst_pc = self.cur_pc(); + if let (Label::Resolved(_), _) = self.labels[label.0] { + panic!("Trying to resolve already resolved label"); + } + let dst_pc = self.cur_pc(); - // Patch all relocations that was previously recorded for this - // particular label. - let unresolved_rels = mem::replace(&mut self.labels[label.0].1, Vec::new()); - for reloc in unresolved_rels { - self.ins.patch_relocation(reloc, dst_pc); - } + // Patch all relocations that was previously recorded for this + // particular label. + let unresolved_rels = mem::replace(&mut self.labels[label.0].1, Vec::new()); + for reloc in unresolved_rels { + self.ins.patch_relocation(reloc, dst_pc); + } - // Mark this label as resolved. - self.labels[label.0] = (Label::Resolved(dst_pc), Vec::new()); - } + // Mark this label as resolved. + self.labels[label.0] = (Label::Resolved(dst_pc), Vec::new()); + } - /// Consume this Sink and returns isa::Instructions. - fn into_inner(self) -> isa::Instructions { - // At this moment all labels should be resolved. - assert!( - { - self.labels - .iter() - .all(|(state, unresolved)| match (state, unresolved) { - (Label::Resolved(_), unresolved) if unresolved.is_empty() => true, - _ => false, - }) - }, - "there are unresolved labels left: {:?}", - self.labels - ); - self.ins - } + /// Consume this Sink and returns isa::Instructions. + fn into_inner(self) -> isa::Instructions { + // At this moment all labels should be resolved. + assert!( + { + self.labels + .iter() + .all(|(state, unresolved)| match (state, unresolved) { + (Label::Resolved(_), unresolved) if unresolved.is_empty() => true, + _ => false, + }) + }, + "there are unresolved labels left: {:?}", + self.labels + ); + self.ins + } } - diff --git a/src/validation/mod.rs b/src/validation/mod.rs index fdb2d11..40f9edc 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -1,23 +1,23 @@ #[allow(unused_imports)] use alloc::prelude::*; +use core::fmt; #[cfg(feature = "std")] use std::error; -use core::fmt; -#[cfg(feature = "std")] -use std::collections::HashSet; #[cfg(not(feature = "std"))] use hashmap_core::HashSet; +#[cfg(feature = "std")] +use std::collections::HashSet; -use parity_wasm::elements::{ - BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, Module, Instruction, - ResizableLimits, TableType, ValueType, InitExpr, Type, -}; -use common::stack; use self::context::ModuleContextBuilder; use self::func::FunctionReader; -use memory_units::Pages; +use common::stack; use isa; +use memory_units::Pages; +use parity_wasm::elements::{ + BlockType, External, GlobalEntry, GlobalType, InitExpr, Instruction, Internal, MemoryType, + Module, ResizableLimits, TableType, Type, ValueType, +}; mod context; mod func; @@ -30,432 +30,427 @@ mod tests; pub struct Error(String); impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } } #[cfg(feature = "std")] impl error::Error for Error { - fn description(&self) -> &str { - &self.0 - } + fn description(&self) -> &str { + &self.0 + } } impl From for Error { - fn from(e: stack::Error) -> Error { - Error(format!("Stack: {}", e)) - } + fn from(e: stack::Error) -> Error { + Error(format!("Stack: {}", e)) + } } #[derive(Clone)] pub struct ValidatedModule { - pub code_map: Vec, - pub module: Module, + pub code_map: Vec, + pub module: Module, } impl ::core::ops::Deref for ValidatedModule { - type Target = Module; - fn deref(&self) -> &Module { - &self.module - } + type Target = Module; + fn deref(&self) -> &Module { + &self.module + } } pub fn deny_floating_point(module: &Module) -> Result<(), Error> { - if let Some(code) = module.code_section() { - for op in code.bodies().iter().flat_map(|body| body.code().elements()) { - use parity_wasm::elements::Instruction::*; + if let Some(code) = module.code_section() { + for op in code.bodies().iter().flat_map(|body| body.code().elements()) { + use parity_wasm::elements::Instruction::*; - macro_rules! match_eq { - ($pattern:pat) => { - |val| if let $pattern = *val { true } else { false } - }; - } + macro_rules! match_eq { + ($pattern:pat) => { + |val| if let $pattern = *val { true } else { false } + }; + } - const DENIED: &[fn(&Instruction) -> bool] = &[ - match_eq!(F32Load(_, _)), - match_eq!(F64Load(_, _)), - match_eq!(F32Store(_, _)), - match_eq!(F64Store(_, _)), - match_eq!(F32Const(_)), - match_eq!(F64Const(_)), - match_eq!(F32Eq), - match_eq!(F32Ne), - match_eq!(F32Lt), - match_eq!(F32Gt), - match_eq!(F32Le), - match_eq!(F32Ge), - match_eq!(F64Eq), - match_eq!(F64Ne), - match_eq!(F64Lt), - match_eq!(F64Gt), - match_eq!(F64Le), - match_eq!(F64Ge), - match_eq!(F32Abs), - match_eq!(F32Neg), - match_eq!(F32Ceil), - match_eq!(F32Floor), - match_eq!(F32Trunc), - match_eq!(F32Nearest), - match_eq!(F32Sqrt), - match_eq!(F32Add), - match_eq!(F32Sub), - match_eq!(F32Mul), - match_eq!(F32Div), - match_eq!(F32Min), - match_eq!(F32Max), - match_eq!(F32Copysign), - match_eq!(F64Abs), - match_eq!(F64Neg), - match_eq!(F64Ceil), - match_eq!(F64Floor), - match_eq!(F64Trunc), - match_eq!(F64Nearest), - match_eq!(F64Sqrt), - match_eq!(F64Add), - match_eq!(F64Sub), - match_eq!(F64Mul), - match_eq!(F64Div), - match_eq!(F64Min), - match_eq!(F64Max), - match_eq!(F64Copysign), - match_eq!(F32ConvertSI32), - match_eq!(F32ConvertUI32), - match_eq!(F32ConvertSI64), - match_eq!(F32ConvertUI64), - match_eq!(F32DemoteF64), - match_eq!(F64ConvertSI32), - match_eq!(F64ConvertUI32), - match_eq!(F64ConvertSI64), - match_eq!(F64ConvertUI64), - match_eq!(F64PromoteF32), - match_eq!(F32ReinterpretI32), - match_eq!(F64ReinterpretI64), - match_eq!(I32TruncSF32), - match_eq!(I32TruncUF32), - match_eq!(I32TruncSF64), - match_eq!(I32TruncUF64), - match_eq!(I64TruncSF32), - match_eq!(I64TruncUF32), - match_eq!(I64TruncSF64), - match_eq!(I64TruncUF64), - match_eq!(I32ReinterpretF32), - match_eq!(I64ReinterpretF64), - ]; + const DENIED: &[fn(&Instruction) -> bool] = &[ + match_eq!(F32Load(_, _)), + match_eq!(F64Load(_, _)), + match_eq!(F32Store(_, _)), + match_eq!(F64Store(_, _)), + match_eq!(F32Const(_)), + match_eq!(F64Const(_)), + match_eq!(F32Eq), + match_eq!(F32Ne), + match_eq!(F32Lt), + match_eq!(F32Gt), + match_eq!(F32Le), + match_eq!(F32Ge), + match_eq!(F64Eq), + match_eq!(F64Ne), + match_eq!(F64Lt), + match_eq!(F64Gt), + match_eq!(F64Le), + match_eq!(F64Ge), + match_eq!(F32Abs), + match_eq!(F32Neg), + match_eq!(F32Ceil), + match_eq!(F32Floor), + match_eq!(F32Trunc), + match_eq!(F32Nearest), + match_eq!(F32Sqrt), + match_eq!(F32Add), + match_eq!(F32Sub), + match_eq!(F32Mul), + match_eq!(F32Div), + match_eq!(F32Min), + match_eq!(F32Max), + match_eq!(F32Copysign), + match_eq!(F64Abs), + match_eq!(F64Neg), + match_eq!(F64Ceil), + match_eq!(F64Floor), + match_eq!(F64Trunc), + match_eq!(F64Nearest), + match_eq!(F64Sqrt), + match_eq!(F64Add), + match_eq!(F64Sub), + match_eq!(F64Mul), + match_eq!(F64Div), + match_eq!(F64Min), + match_eq!(F64Max), + match_eq!(F64Copysign), + match_eq!(F32ConvertSI32), + match_eq!(F32ConvertUI32), + match_eq!(F32ConvertSI64), + match_eq!(F32ConvertUI64), + match_eq!(F32DemoteF64), + match_eq!(F64ConvertSI32), + match_eq!(F64ConvertUI32), + match_eq!(F64ConvertSI64), + match_eq!(F64ConvertUI64), + match_eq!(F64PromoteF32), + match_eq!(F32ReinterpretI32), + match_eq!(F64ReinterpretI64), + match_eq!(I32TruncSF32), + match_eq!(I32TruncUF32), + match_eq!(I32TruncSF64), + match_eq!(I32TruncUF64), + match_eq!(I64TruncSF32), + match_eq!(I64TruncUF32), + match_eq!(I64TruncSF64), + match_eq!(I64TruncUF64), + match_eq!(I32ReinterpretF32), + match_eq!(I64ReinterpretF64), + ]; - if DENIED.iter().any(|is_denied| is_denied(op)) { - return Err(Error(format!("Floating point operation denied: {:?}", op))); - } - } - } + if DENIED.iter().any(|is_denied| is_denied(op)) { + return Err(Error(format!("Floating point operation denied: {:?}", op))); + } + } + } - if let (Some(sec), Some(types)) = (module.function_section(), module.type_section()) { - use parity_wasm::elements::{Type, ValueType}; + if let (Some(sec), Some(types)) = (module.function_section(), module.type_section()) { + use parity_wasm::elements::{Type, ValueType}; - let types = types.types(); + let types = types.types(); - for sig in sec.entries() { - if let Some(typ) = types.get(sig.type_ref() as usize) { - match *typ { - Type::Function(ref func) => { - if func.params() - .iter() - .chain(func.return_type().as_ref()) - .any(|&typ| typ == ValueType::F32 || typ == ValueType::F64) - { - return Err(Error(format!("Use of floating point types denied"))); - } - } - } - } - } - } + for sig in sec.entries() { + if let Some(typ) = types.get(sig.type_ref() as usize) { + match *typ { + Type::Function(ref func) => { + if func + .params() + .iter() + .chain(func.return_type().as_ref()) + .any(|&typ| typ == ValueType::F32 || typ == ValueType::F64) + { + return Err(Error(format!("Use of floating point types denied"))); + } + } + } + } + } + } - Ok(()) + Ok(()) } pub fn validate_module(module: Module) -> Result { - let mut context_builder = ModuleContextBuilder::new(); - let mut imported_globals = Vec::new(); - let mut code_map = Vec::new(); + let mut context_builder = ModuleContextBuilder::new(); + let mut imported_globals = Vec::new(); + let mut code_map = Vec::new(); - // Copy types from module as is. - context_builder.set_types( - module - .type_section() - .map(|ts| { - ts.types() - .into_iter() - .map(|&Type::Function(ref ty)| ty) - .cloned() - .collect() - }) - .unwrap_or_default(), - ); + // Copy types from module as is. + context_builder.set_types( + module + .type_section() + .map(|ts| { + ts.types() + .into_iter() + .map(|&Type::Function(ref ty)| ty) + .cloned() + .collect() + }) + .unwrap_or_default(), + ); - // Fill elements with imported values. - for import_entry in module - .import_section() - .map(|i| i.entries()) - .unwrap_or_default() - { - match *import_entry.external() { - External::Function(idx) => context_builder.push_func_type_index(idx), - External::Table(ref table) => context_builder.push_table(table.clone()), - External::Memory(ref memory) => context_builder.push_memory(memory.clone()), - External::Global(ref global) => { - context_builder.push_global(global.clone()); - imported_globals.push(global.clone()); - } - } - } + // Fill elements with imported values. + for import_entry in module + .import_section() + .map(|i| i.entries()) + .unwrap_or_default() + { + match *import_entry.external() { + External::Function(idx) => context_builder.push_func_type_index(idx), + External::Table(ref table) => context_builder.push_table(table.clone()), + External::Memory(ref memory) => context_builder.push_memory(memory.clone()), + External::Global(ref global) => { + context_builder.push_global(global.clone()); + imported_globals.push(global.clone()); + } + } + } - // Concatenate elements with defined in the module. - if let Some(function_section) = module.function_section() { - for func_entry in function_section.entries() { - context_builder.push_func_type_index(func_entry.type_ref()) - } - } - if let Some(table_section) = module.table_section() { - for table_entry in table_section.entries() { - validate_table_type(table_entry)?; - context_builder.push_table(table_entry.clone()); - } - } - if let Some(mem_section) = module.memory_section() { - for mem_entry in mem_section.entries() { - validate_memory_type(mem_entry)?; - context_builder.push_memory(mem_entry.clone()); - } - } - if let Some(global_section) = module.global_section() { - for global_entry in global_section.entries() { - validate_global_entry(global_entry, &imported_globals)?; - context_builder.push_global(global_entry.global_type().clone()); - } - } + // Concatenate elements with defined in the module. + if let Some(function_section) = module.function_section() { + for func_entry in function_section.entries() { + context_builder.push_func_type_index(func_entry.type_ref()) + } + } + if let Some(table_section) = module.table_section() { + for table_entry in table_section.entries() { + validate_table_type(table_entry)?; + context_builder.push_table(table_entry.clone()); + } + } + if let Some(mem_section) = module.memory_section() { + for mem_entry in mem_section.entries() { + validate_memory_type(mem_entry)?; + context_builder.push_memory(mem_entry.clone()); + } + } + if let Some(global_section) = module.global_section() { + for global_entry in global_section.entries() { + validate_global_entry(global_entry, &imported_globals)?; + context_builder.push_global(global_entry.global_type().clone()); + } + } - let context = context_builder.build(); + let context = context_builder.build(); - let function_section_len = module - .function_section() - .map(|s| s.entries().len()) - .unwrap_or(0); - let code_section_len = module.code_section().map(|s| s.bodies().len()).unwrap_or(0); - if function_section_len != code_section_len { - return Err(Error(format!( - "length of function section is {}, while len of code section is {}", - function_section_len, - code_section_len - ))); - } + let function_section_len = module + .function_section() + .map(|s| s.entries().len()) + .unwrap_or(0); + let code_section_len = module.code_section().map(|s| s.bodies().len()).unwrap_or(0); + if function_section_len != code_section_len { + return Err(Error(format!( + "length of function section is {}, while len of code section is {}", + function_section_len, code_section_len + ))); + } - // validate every function body in user modules - if function_section_len != 0 { - // tests use invalid code - let function_section = module.function_section().expect( - "function_section_len != 0; qed", - ); - let code_section = module.code_section().expect( - "function_section_len != 0; function_section_len == code_section_len; qed", - ); - // check every function body - for (index, function) in function_section.entries().iter().enumerate() { - let function_body = code_section.bodies().get(index as usize).ok_or( - Error(format!( - "Missing body for function {}", - index - )), - )?; - let code = FunctionReader::read_function(&context, function, function_body) - .map_err(|e| { - let Error(ref msg) = e; - Error(format!("Function #{} reading/validation error: {}", index, msg)) - })?; - code_map.push(code); - } - } + // validate every function body in user modules + if function_section_len != 0 { + // tests use invalid code + let function_section = module + .function_section() + .expect("function_section_len != 0; qed"); + let code_section = module + .code_section() + .expect("function_section_len != 0; function_section_len == code_section_len; qed"); + // check every function body + for (index, function) in function_section.entries().iter().enumerate() { + let function_body = code_section + .bodies() + .get(index as usize) + .ok_or(Error(format!("Missing body for function {}", index)))?; + let code = + FunctionReader::read_function(&context, function, function_body).map_err(|e| { + let Error(ref msg) = e; + Error(format!( + "Function #{} reading/validation error: {}", + index, msg + )) + })?; + code_map.push(code); + } + } - // validate start section - if let Some(start_fn_idx) = module.start_section() { - let (params, return_ty) = context.require_function(start_fn_idx)?; - if return_ty != BlockType::NoResult || params.len() != 0 { - return Err(Error( - "start function expected to have type [] -> []".into(), - )); - } - } + // validate start section + if let Some(start_fn_idx) = module.start_section() { + let (params, return_ty) = context.require_function(start_fn_idx)?; + if return_ty != BlockType::NoResult || params.len() != 0 { + return Err(Error( + "start function expected to have type [] -> []".into(), + )); + } + } - // validate export section - if let Some(export_section) = module.export_section() { - let mut export_names = HashSet::with_capacity(export_section.entries().len()); - for export in export_section.entries() { - // HashSet::insert returns false if item already in set. - let duplicate = export_names.insert(export.field()) == false; - if duplicate { - return Err(Error( - format!("duplicate export {}", export.field()), - )); - } - match *export.internal() { - Internal::Function(function_index) => { - context.require_function(function_index)?; - } - Internal::Global(global_index) => { - context.require_global(global_index, Some(false))?; - } - Internal::Memory(memory_index) => { - context.require_memory(memory_index)?; - } - Internal::Table(table_index) => { - context.require_table(table_index)?; - } - } - } - } + // validate export section + if let Some(export_section) = module.export_section() { + let mut export_names = HashSet::with_capacity(export_section.entries().len()); + for export in export_section.entries() { + // HashSet::insert returns false if item already in set. + let duplicate = export_names.insert(export.field()) == false; + if duplicate { + return Err(Error(format!("duplicate export {}", export.field()))); + } + match *export.internal() { + Internal::Function(function_index) => { + context.require_function(function_index)?; + } + Internal::Global(global_index) => { + context.require_global(global_index, Some(false))?; + } + Internal::Memory(memory_index) => { + context.require_memory(memory_index)?; + } + Internal::Table(table_index) => { + context.require_table(table_index)?; + } + } + } + } - // validate import section - if let Some(import_section) = module.import_section() { - for import in import_section.entries() { - match *import.external() { - External::Function(function_type_index) => { - context.require_function_type(function_type_index)?; - } - External::Global(ref global_type) => { - if global_type.is_mutable() { - return Err(Error(format!( - "trying to import mutable global {}", - import.field() - ))); - } - } - External::Memory(ref memory_type) => { - validate_memory_type(memory_type)?; - } - External::Table(ref table_type) => { - validate_table_type(table_type)?; - } - } - } - } + // validate import section + if let Some(import_section) = module.import_section() { + for import in import_section.entries() { + match *import.external() { + External::Function(function_type_index) => { + context.require_function_type(function_type_index)?; + } + External::Global(ref global_type) => { + if global_type.is_mutable() { + return Err(Error(format!( + "trying to import mutable global {}", + import.field() + ))); + } + } + External::Memory(ref memory_type) => { + validate_memory_type(memory_type)?; + } + External::Table(ref table_type) => { + validate_table_type(table_type)?; + } + } + } + } - // there must be no greater than 1 table in tables index space - if context.tables().len() > 1 { - return Err(Error(format!( - "too many tables in index space: {}", - context.tables().len() - ))); - } + // there must be no greater than 1 table in tables index space + if context.tables().len() > 1 { + return Err(Error(format!( + "too many tables in index space: {}", + context.tables().len() + ))); + } - // there must be no greater than 1 linear memory in memory index space - if context.memories().len() > 1 { - return Err(Error(format!( - "too many memory regions in index space: {}", - context.memories().len() - ))); - } + // there must be no greater than 1 linear memory in memory index space + if context.memories().len() > 1 { + return Err(Error(format!( + "too many memory regions in index space: {}", + context.memories().len() + ))); + } - // use data section to initialize linear memory regions - if let Some(data_section) = module.data_section() { - for data_segment in data_section.entries() { - context.require_memory(data_segment.index())?; - let init_ty = expr_const_type(data_segment.offset(), context.globals())?; - if init_ty != ValueType::I32 { - return Err(Error("segment offset should return I32".into())); - } - } - } + // use data section to initialize linear memory regions + if let Some(data_section) = module.data_section() { + for data_segment in data_section.entries() { + context.require_memory(data_segment.index())?; + let init_ty = expr_const_type(data_segment.offset(), context.globals())?; + if init_ty != ValueType::I32 { + return Err(Error("segment offset should return I32".into())); + } + } + } - // use element section to fill tables - if let Some(element_section) = module.elements_section() { - for element_segment in element_section.entries() { - context.require_table(element_segment.index())?; + // use element section to fill tables + if let Some(element_section) = module.elements_section() { + for element_segment in element_section.entries() { + context.require_table(element_segment.index())?; - let init_ty = expr_const_type(element_segment.offset(), context.globals())?; - if init_ty != ValueType::I32 { - return Err(Error("segment offset should return I32".into())); - } + let init_ty = expr_const_type(element_segment.offset(), context.globals())?; + if init_ty != ValueType::I32 { + return Err(Error("segment offset should return I32".into())); + } - for function_index in element_segment.members() { - context.require_function(*function_index)?; - } - } - } + for function_index in element_segment.members() { + context.require_function(*function_index)?; + } + } + } - Ok(ValidatedModule { - module, - code_map, - }) + Ok(ValidatedModule { module, code_map }) } fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> { - if let Some(maximum) = limits.maximum() { - if limits.initial() > maximum { - return Err(Error(format!( - "maximum limit {} is less than minimum {}", - maximum, - limits.initial() - ))); - } - } - Ok(()) + if let Some(maximum) = limits.maximum() { + if limits.initial() > maximum { + return Err(Error(format!( + "maximum limit {} is less than minimum {}", + maximum, + limits.initial() + ))); + } + } + Ok(()) } fn validate_memory_type(memory_type: &MemoryType) -> Result<(), Error> { - let initial: Pages = Pages(memory_type.limits().initial() as usize); - let maximum: Option = memory_type.limits().maximum().map(|m| Pages(m as usize)); - ::memory::validate_memory(initial, maximum).map_err(Error) + let initial: Pages = Pages(memory_type.limits().initial() as usize); + let maximum: Option = memory_type.limits().maximum().map(|m| Pages(m as usize)); + ::memory::validate_memory(initial, maximum).map_err(Error) } fn validate_table_type(table_type: &TableType) -> Result<(), Error> { - validate_limits(table_type.limits()) + validate_limits(table_type.limits()) } fn validate_global_entry(global_entry: &GlobalEntry, globals: &[GlobalType]) -> Result<(), Error> { - let init = global_entry.init_expr(); - let init_expr_ty = expr_const_type(init, globals)?; - if init_expr_ty != global_entry.global_type().content_type() { - return Err(Error(format!( - "Trying to initialize variable of type {:?} with value of type {:?}", - global_entry.global_type().content_type(), - init_expr_ty - ))); - } - Ok(()) + let init = global_entry.init_expr(); + let init_expr_ty = expr_const_type(init, globals)?; + if init_expr_ty != global_entry.global_type().content_type() { + return Err(Error(format!( + "Trying to initialize variable of type {:?} with value of type {:?}", + global_entry.global_type().content_type(), + init_expr_ty + ))); + } + Ok(()) } /// Returns type of this constant expression. fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result { - let code = init_expr.code(); - if code.len() != 2 { - return Err(Error( - "Init expression should always be with length 2".into(), - )); - } - let expr_ty: ValueType = match code[0] { - Instruction::I32Const(_) => ValueType::I32, - Instruction::I64Const(_) => ValueType::I64, - Instruction::F32Const(_) => ValueType::F32, - Instruction::F64Const(_) => ValueType::F64, - Instruction::GetGlobal(idx) => { - match globals.get(idx as usize) { - Some(target_global) => { - if target_global.is_mutable() { - return Err(Error(format!("Global {} is mutable", idx))); - } - target_global.content_type() - } - None => { - return Err(Error( - format!("Global {} doesn't exists or not yet defined", idx), - )) - } - } - } - _ => return Err(Error("Non constant opcode in init expr".into())), - }; - if code[1] != Instruction::End { - return Err(Error("Expression doesn't ends with `end` opcode".into())); - } - Ok(expr_ty) + let code = init_expr.code(); + if code.len() != 2 { + return Err(Error( + "Init expression should always be with length 2".into(), + )); + } + let expr_ty: ValueType = match code[0] { + Instruction::I32Const(_) => ValueType::I32, + Instruction::I64Const(_) => ValueType::I64, + Instruction::F32Const(_) => ValueType::F32, + Instruction::F64Const(_) => ValueType::F64, + Instruction::GetGlobal(idx) => match globals.get(idx as usize) { + Some(target_global) => { + if target_global.is_mutable() { + return Err(Error(format!("Global {} is mutable", idx))); + } + target_global.content_type() + } + None => { + return Err(Error(format!( + "Global {} doesn't exists or not yet defined", + idx + ))); + } + }, + _ => return Err(Error("Non constant opcode in init expr".into())), + }; + if code[1] != Instruction::End { + return Err(Error("Expression doesn't ends with `end` opcode".into())); + } + Ok(expr_ty) } diff --git a/src/validation/tests.rs b/src/validation/tests.rs index fad0615..49cee36 100644 --- a/src/validation/tests.rs +++ b/src/validation/tests.rs @@ -2,278 +2,299 @@ use super::{validate_module, ValidatedModule}; use isa; use parity_wasm::builder::module; use parity_wasm::elements::{ - deserialize_buffer, BlockType, External, GlobalEntry, GlobalType, ImportEntry, InitExpr, - Instruction, Instructions, MemoryType, Module, TableType, ValueType, + deserialize_buffer, BlockType, External, GlobalEntry, GlobalType, ImportEntry, InitExpr, + Instruction, Instructions, MemoryType, Module, TableType, ValueType, }; use wabt; #[test] fn empty_is_valid() { - let module = module().build(); - assert!(validate_module(module).is_ok()); + let module = module().build(); + assert!(validate_module(module).is_ok()); } #[test] fn limits() { - let test_cases = vec![ - // min > max - (10, Some(9), false), - // min = max - (10, Some(10), true), - // table/memory is always valid without max - (10, None, true), - ]; + let test_cases = vec![ + // min > max + (10, Some(9), false), + // min = max + (10, Some(10), true), + // table/memory is always valid without max + (10, None, true), + ]; - for (min, max, is_valid) in test_cases { - // defined table - let m = module().table().with_min(min).with_max(max).build().build(); - assert_eq!(validate_module(m).is_ok(), is_valid); + for (min, max, is_valid) in test_cases { + // defined table + let m = module().table().with_min(min).with_max(max).build().build(); + assert_eq!(validate_module(m).is_ok(), is_valid); - // imported table - let m = module() - .with_import(ImportEntry::new( - "core".into(), - "table".into(), - External::Table(TableType::new(min, max)), - )).build(); - assert_eq!(validate_module(m).is_ok(), is_valid); + // imported table + let m = module() + .with_import(ImportEntry::new( + "core".into(), + "table".into(), + External::Table(TableType::new(min, max)), + )) + .build(); + assert_eq!(validate_module(m).is_ok(), is_valid); - // defined memory - let m = module() - .memory() - .with_min(min) - .with_max(max) - .build() - .build(); - assert_eq!(validate_module(m).is_ok(), is_valid); + // defined memory + let m = module() + .memory() + .with_min(min) + .with_max(max) + .build() + .build(); + assert_eq!(validate_module(m).is_ok(), is_valid); - // imported table - let m = module() - .with_import(ImportEntry::new( - "core".into(), - "memory".into(), - External::Memory(MemoryType::new(min, max)), - )).build(); - assert_eq!(validate_module(m).is_ok(), is_valid); - } + // imported table + let m = module() + .with_import(ImportEntry::new( + "core".into(), + "memory".into(), + External::Memory(MemoryType::new(min, max)), + )) + .build(); + assert_eq!(validate_module(m).is_ok(), is_valid); + } } #[test] fn global_init_const() { - let m = module() - .with_global(GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]), - )).build(); - assert!(validate_module(m).is_ok()); + let m = module() + .with_global(GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]), + )) + .build(); + assert!(validate_module(m).is_ok()); - // init expr type differs from declared global type - let m = module() - .with_global(GlobalEntry::new( - GlobalType::new(ValueType::I64, true), - InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]), - )).build(); - assert!(validate_module(m).is_err()); + // init expr type differs from declared global type + let m = module() + .with_global(GlobalEntry::new( + GlobalType::new(ValueType::I64, true), + InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]), + )) + .build(); + assert!(validate_module(m).is_err()); } #[test] fn global_init_global() { - let m = module() - .with_import(ImportEntry::new( - "env".into(), - "ext_global".into(), - External::Global(GlobalType::new(ValueType::I32, false)), - )).with_global(GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]), - )).build(); - assert!(validate_module(m).is_ok()); + let m = module() + .with_import(ImportEntry::new( + "env".into(), + "ext_global".into(), + External::Global(GlobalType::new(ValueType::I32, false)), + )) + .with_global(GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]), + )) + .build(); + assert!(validate_module(m).is_ok()); - // get_global can reference only previously defined globals - let m = module() - .with_global(GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]), - )).build(); - assert!(validate_module(m).is_err()); + // get_global can reference only previously defined globals + let m = module() + .with_global(GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]), + )) + .build(); + assert!(validate_module(m).is_err()); - // get_global can reference only const globals - let m = module() - .with_import(ImportEntry::new( - "env".into(), - "ext_global".into(), - External::Global(GlobalType::new(ValueType::I32, true)), - )).with_global(GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]), - )).build(); - assert!(validate_module(m).is_err()); + // get_global can reference only const globals + let m = module() + .with_import(ImportEntry::new( + "env".into(), + "ext_global".into(), + External::Global(GlobalType::new(ValueType::I32, true)), + )) + .with_global(GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]), + )) + .build(); + assert!(validate_module(m).is_err()); - // get_global in init_expr can only refer to imported globals. - let m = module() - .with_global(GlobalEntry::new( - GlobalType::new(ValueType::I32, false), - InitExpr::new(vec![Instruction::I32Const(0), Instruction::End]), - )).with_global(GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]), - )).build(); - assert!(validate_module(m).is_err()); + // get_global in init_expr can only refer to imported globals. + let m = module() + .with_global(GlobalEntry::new( + GlobalType::new(ValueType::I32, false), + InitExpr::new(vec![Instruction::I32Const(0), Instruction::End]), + )) + .with_global(GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]), + )) + .build(); + assert!(validate_module(m).is_err()); } #[test] fn global_init_misc() { - // without delimiting End opcode - let m = module() - .with_global(GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Instruction::I32Const(42)]), - )).build(); - assert!(validate_module(m).is_err()); + // without delimiting End opcode + let m = module() + .with_global(GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Instruction::I32Const(42)]), + )) + .build(); + assert!(validate_module(m).is_err()); - // empty init expr - let m = module() - .with_global(GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Instruction::End]), - )).build(); - assert!(validate_module(m).is_err()); + // empty init expr + let m = module() + .with_global(GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Instruction::End]), + )) + .build(); + assert!(validate_module(m).is_err()); - // not an constant opcode used - let m = module() - .with_global(GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Instruction::Unreachable, Instruction::End]), - )).build(); - assert!(validate_module(m).is_err()); + // not an constant opcode used + let m = module() + .with_global(GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Instruction::Unreachable, Instruction::End]), + )) + .build(); + assert!(validate_module(m).is_err()); } #[test] fn module_limits_validity() { - // module cannot contain more than 1 memory atm. - let m = module() - .with_import(ImportEntry::new( - "core".into(), - "memory".into(), - External::Memory(MemoryType::new(10, None)), - )).memory() - .with_min(10) - .build() - .build(); - assert!(validate_module(m).is_err()); + // module cannot contain more than 1 memory atm. + let m = module() + .with_import(ImportEntry::new( + "core".into(), + "memory".into(), + External::Memory(MemoryType::new(10, None)), + )) + .memory() + .with_min(10) + .build() + .build(); + assert!(validate_module(m).is_err()); - // module cannot contain more than 1 table atm. - let m = module() - .with_import(ImportEntry::new( - "core".into(), - "table".into(), - External::Table(TableType::new(10, None)), - )).table() - .with_min(10) - .build() - .build(); - assert!(validate_module(m).is_err()); + // module cannot contain more than 1 table atm. + let m = module() + .with_import(ImportEntry::new( + "core".into(), + "table".into(), + External::Table(TableType::new(10, None)), + )) + .table() + .with_min(10) + .build() + .build(); + assert!(validate_module(m).is_err()); } #[test] fn funcs() { - // recursive function calls is legal. - let m = module() - .function() - .signature() - .return_type() - .i32() - .build() - .body() - .with_instructions(Instructions::new(vec![ - Instruction::Call(1), - Instruction::End, - ])).build() - .build() - .function() - .signature() - .return_type() - .i32() - .build() - .body() - .with_instructions(Instructions::new(vec![ - Instruction::Call(0), - Instruction::End, - ])).build() - .build() - .build(); - assert!(validate_module(m).is_ok()); + // recursive function calls is legal. + let m = module() + .function() + .signature() + .return_type() + .i32() + .build() + .body() + .with_instructions(Instructions::new(vec![ + Instruction::Call(1), + Instruction::End, + ])) + .build() + .build() + .function() + .signature() + .return_type() + .i32() + .build() + .body() + .with_instructions(Instructions::new(vec![ + Instruction::Call(0), + Instruction::End, + ])) + .build() + .build() + .build(); + assert!(validate_module(m).is_ok()); } #[test] fn globals() { - // import immutable global is legal. - let m = module() - .with_import(ImportEntry::new( - "env".into(), - "ext_global".into(), - External::Global(GlobalType::new(ValueType::I32, false)), - )).build(); - assert!(validate_module(m).is_ok()); + // import immutable global is legal. + let m = module() + .with_import(ImportEntry::new( + "env".into(), + "ext_global".into(), + External::Global(GlobalType::new(ValueType::I32, false)), + )) + .build(); + assert!(validate_module(m).is_ok()); - // import mutable global is invalid. - let m = module() - .with_import(ImportEntry::new( - "env".into(), - "ext_global".into(), - External::Global(GlobalType::new(ValueType::I32, true)), - )).build(); - assert!(validate_module(m).is_err()); + // import mutable global is invalid. + let m = module() + .with_import(ImportEntry::new( + "env".into(), + "ext_global".into(), + External::Global(GlobalType::new(ValueType::I32, true)), + )) + .build(); + assert!(validate_module(m).is_err()); } #[test] fn if_else_with_return_type_validation() { - let m = module() - .function() - .signature() - .build() - .body() - .with_instructions(Instructions::new(vec![ - Instruction::I32Const(1), - Instruction::If(BlockType::NoResult), - Instruction::I32Const(1), - Instruction::If(BlockType::Value(ValueType::I32)), - Instruction::I32Const(1), - Instruction::Else, - Instruction::I32Const(2), - Instruction::End, - Instruction::Drop, - Instruction::End, - Instruction::End, - ])).build() - .build() - .build(); - validate_module(m).unwrap(); + let m = module() + .function() + .signature() + .build() + .body() + .with_instructions(Instructions::new(vec![ + Instruction::I32Const(1), + Instruction::If(BlockType::NoResult), + Instruction::I32Const(1), + Instruction::If(BlockType::Value(ValueType::I32)), + Instruction::I32Const(1), + Instruction::Else, + Instruction::I32Const(2), + Instruction::End, + Instruction::Drop, + Instruction::End, + Instruction::End, + ])) + .build() + .build() + .build(); + validate_module(m).unwrap(); } fn validate(wat: &str) -> ValidatedModule { - let wasm = wabt::wat2wasm(wat).unwrap(); - let module = deserialize_buffer::(&wasm).unwrap(); - let validated_module = validate_module(module).unwrap(); - validated_module + let wasm = wabt::wat2wasm(wat).unwrap(); + let module = deserialize_buffer::(&wasm).unwrap(); + let validated_module = validate_module(module).unwrap(); + validated_module } fn compile(module: &ValidatedModule) -> (Vec, Vec) { - let code = &module.code_map[0]; - let mut instructions = Vec::new(); - let mut pcs = Vec::new(); - let mut iter = code.iterate_from(0); - loop { - let pc = iter.position(); - if let Some(instruction) = iter.next() { - instructions.push(instruction.clone()); - pcs.push(pc); - } else { - break; - } - } + let code = &module.code_map[0]; + let mut instructions = Vec::new(); + let mut pcs = Vec::new(); + let mut iter = code.iterate_from(0); + loop { + let pc = iter.position(); + if let Some(instruction) = iter.next() { + instructions.push(instruction.clone()); + pcs.push(pc); + } else { + break; + } + } - (instructions, pcs) + (instructions, pcs) } macro_rules! targets { @@ -289,96 +310,96 @@ macro_rules! targets { #[test] fn implicit_return_no_value() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") ) ) "#, - ); - let (code, _) = compile(&module); - assert_eq!( - code, - vec![isa::Instruction::Return(isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - })] - ) + ); + let (code, _) = compile(&module); + assert_eq!( + code, + vec![isa::Instruction::Return(isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + })] + ) } #[test] fn implicit_return_with_value() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") (result i32) i32.const 0 ) ) "#, - ); - let (code, _) = compile(&module); - assert_eq!( - code, - vec![ - isa::Instruction::I32Const(0), - isa::Instruction::Return(isa::DropKeep { - drop: 0, - keep: isa::Keep::Single, - }), - ] - ) + ); + let (code, _) = compile(&module); + assert_eq!( + code, + vec![ + isa::Instruction::I32Const(0), + isa::Instruction::Return(isa::DropKeep { + drop: 0, + keep: isa::Keep::Single, + }), + ] + ) } #[test] fn implicit_return_param() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") (param i32) ) ) "#, - ); - let (code, _) = compile(&module); - assert_eq!( - code, - vec![isa::Instruction::Return(isa::DropKeep { - drop: 1, - keep: isa::Keep::None, - }),] - ) + ); + let (code, _) = compile(&module); + assert_eq!( + code, + vec![isa::Instruction::Return(isa::DropKeep { + drop: 1, + keep: isa::Keep::None, + }),] + ) } #[test] fn get_local() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") (param i32) (result i32) get_local 0 ) ) "#, - ); - let (code, _) = compile(&module); - assert_eq!( - code, - vec![ - isa::Instruction::GetLocal(1), - isa::Instruction::Return(isa::DropKeep { - drop: 1, - keep: isa::Keep::Single, - }), - ] - ) + ); + let (code, _) = compile(&module); + assert_eq!( + code, + vec![ + isa::Instruction::GetLocal(1), + isa::Instruction::Return(isa::DropKeep { + drop: 1, + keep: isa::Keep::Single, + }), + ] + ) } #[test] fn explicit_return() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") (param i32) (result i32) get_local 0 @@ -386,28 +407,28 @@ fn explicit_return() { ) ) "#, - ); - let (code, _) = compile(&module); - assert_eq!( - code, - vec![ - isa::Instruction::GetLocal(1), - isa::Instruction::Return(isa::DropKeep { - drop: 1, - keep: isa::Keep::Single, - }), - isa::Instruction::Return(isa::DropKeep { - drop: 1, - keep: isa::Keep::Single, - }), - ] - ) + ); + let (code, _) = compile(&module); + assert_eq!( + code, + vec![ + isa::Instruction::GetLocal(1), + isa::Instruction::Return(isa::DropKeep { + drop: 1, + keep: isa::Keep::Single, + }), + isa::Instruction::Return(isa::DropKeep { + drop: 1, + keep: isa::Keep::Single, + }), + ] + ) } #[test] fn add_params() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") (param i32) (param i32) (result i32) get_local 0 @@ -416,31 +437,31 @@ fn add_params() { ) ) "#, - ); - let (code, _) = compile(&module); - assert_eq!( - code, - vec![ - // This is tricky. Locals are now loaded from the stack. The load - // happens from address relative of the current stack pointer. The first load - // takes the value below the previous one (i.e the second argument) and then, it increments - // the stack pointer. And then the same thing hapens with the value below the previous one - // (which happens to be the value loaded by the first get_local). - isa::Instruction::GetLocal(2), - isa::Instruction::GetLocal(2), - isa::Instruction::I32Add, - isa::Instruction::Return(isa::DropKeep { - drop: 2, - keep: isa::Keep::Single, - }), - ] - ) + ); + let (code, _) = compile(&module); + assert_eq!( + code, + vec![ + // This is tricky. Locals are now loaded from the stack. The load + // happens from address relative of the current stack pointer. The first load + // takes the value below the previous one (i.e the second argument) and then, it increments + // the stack pointer. And then the same thing hapens with the value below the previous one + // (which happens to be the value loaded by the first get_local). + isa::Instruction::GetLocal(2), + isa::Instruction::GetLocal(2), + isa::Instruction::I32Add, + isa::Instruction::Return(isa::DropKeep { + drop: 2, + keep: isa::Keep::Single, + }), + ] + ) } #[test] fn drop_locals() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") (param i32) (local i32) @@ -449,25 +470,25 @@ fn drop_locals() { ) ) "#, - ); - let (code, _) = compile(&module); - assert_eq!( - code, - vec![ - isa::Instruction::GetLocal(2), - isa::Instruction::SetLocal(1), - isa::Instruction::Return(isa::DropKeep { - drop: 2, - keep: isa::Keep::None, - }), - ] - ) + ); + let (code, _) = compile(&module); + assert_eq!( + code, + vec![ + isa::Instruction::GetLocal(2), + isa::Instruction::SetLocal(1), + isa::Instruction::Return(isa::DropKeep { + drop: 2, + keep: isa::Keep::None, + }), + ] + ) } #[test] fn if_without_else() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") (param i32) (result i32) i32.const 1 @@ -479,37 +500,37 @@ fn if_without_else() { ) ) "#, - ); - let (code, pcs) = compile(&module); - assert_eq!( - code, - vec![ - isa::Instruction::I32Const(1), - isa::Instruction::BrIfEqz(isa::Target { - dst_pc: pcs[4], - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }), - isa::Instruction::I32Const(2), - isa::Instruction::Return(isa::DropKeep { - drop: 1, // 1 param - keep: isa::Keep::Single, // 1 result - }), - isa::Instruction::I32Const(3), - isa::Instruction::Return(isa::DropKeep { - drop: 1, - keep: isa::Keep::Single, - }), - ] - ) + ); + let (code, pcs) = compile(&module); + assert_eq!( + code, + vec![ + isa::Instruction::I32Const(1), + isa::Instruction::BrIfEqz(isa::Target { + dst_pc: pcs[4], + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }), + isa::Instruction::I32Const(2), + isa::Instruction::Return(isa::DropKeep { + drop: 1, // 1 param + keep: isa::Keep::Single, // 1 result + }), + isa::Instruction::I32Const(3), + isa::Instruction::Return(isa::DropKeep { + drop: 1, + keep: isa::Keep::Single, + }), + ] + ) } #[test] fn if_else() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") (local i32) @@ -524,42 +545,42 @@ fn if_else() { ) ) "#, - ); - let (code, pcs) = compile(&module); - assert_eq!( - code, - vec![ - isa::Instruction::I32Const(1), - isa::Instruction::BrIfEqz(isa::Target { - dst_pc: pcs[5], - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }), - isa::Instruction::I32Const(2), - isa::Instruction::SetLocal(1), - isa::Instruction::Br(isa::Target { - dst_pc: pcs[7], - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }), - isa::Instruction::I32Const(3), - isa::Instruction::SetLocal(1), - isa::Instruction::Return(isa::DropKeep { - drop: 1, - keep: isa::Keep::None, - }), - ] - ) + ); + let (code, pcs) = compile(&module); + assert_eq!( + code, + vec![ + isa::Instruction::I32Const(1), + isa::Instruction::BrIfEqz(isa::Target { + dst_pc: pcs[5], + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }), + isa::Instruction::I32Const(2), + isa::Instruction::SetLocal(1), + isa::Instruction::Br(isa::Target { + dst_pc: pcs[7], + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }), + isa::Instruction::I32Const(3), + isa::Instruction::SetLocal(1), + isa::Instruction::Return(isa::DropKeep { + drop: 1, + keep: isa::Keep::None, + }), + ] + ) } #[test] fn if_else_returns_result() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") i32.const 1 @@ -572,41 +593,41 @@ fn if_else_returns_result() { ) ) "#, - ); - let (code, pcs) = compile(&module); - assert_eq!( - code, - vec![ - isa::Instruction::I32Const(1), - isa::Instruction::BrIfEqz(isa::Target { - dst_pc: pcs[4], - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }), - isa::Instruction::I32Const(2), - isa::Instruction::Br(isa::Target { - dst_pc: pcs[5], - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }), - isa::Instruction::I32Const(3), - isa::Instruction::Drop, - isa::Instruction::Return(isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }), - ] - ) + ); + let (code, pcs) = compile(&module); + assert_eq!( + code, + vec![ + isa::Instruction::I32Const(1), + isa::Instruction::BrIfEqz(isa::Target { + dst_pc: pcs[4], + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }), + isa::Instruction::I32Const(2), + isa::Instruction::Br(isa::Target { + dst_pc: pcs[5], + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }), + isa::Instruction::I32Const(3), + isa::Instruction::Drop, + isa::Instruction::Return(isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }), + ] + ) } #[test] fn if_else_branch_from_true_branch() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") i32.const 1 @@ -623,51 +644,51 @@ fn if_else_branch_from_true_branch() { ) ) "#, - ); - let (code, pcs) = compile(&module); - assert_eq!( - code, - vec![ - isa::Instruction::I32Const(1), - isa::Instruction::BrIfEqz(isa::Target { - dst_pc: pcs[8], - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }), - isa::Instruction::I32Const(1), - isa::Instruction::I32Const(1), - isa::Instruction::BrIfNez(isa::Target { - dst_pc: pcs[9], - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::Single, - }, - }), - isa::Instruction::Drop, - isa::Instruction::I32Const(2), - isa::Instruction::Br(isa::Target { - dst_pc: pcs[9], - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }), - isa::Instruction::I32Const(3), - isa::Instruction::Drop, - isa::Instruction::Return(isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }), - ] - ) + ); + let (code, pcs) = compile(&module); + assert_eq!( + code, + vec![ + isa::Instruction::I32Const(1), + isa::Instruction::BrIfEqz(isa::Target { + dst_pc: pcs[8], + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }), + isa::Instruction::I32Const(1), + isa::Instruction::I32Const(1), + isa::Instruction::BrIfNez(isa::Target { + dst_pc: pcs[9], + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::Single, + }, + }), + isa::Instruction::Drop, + isa::Instruction::I32Const(2), + isa::Instruction::Br(isa::Target { + dst_pc: pcs[9], + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }), + isa::Instruction::I32Const(3), + isa::Instruction::Drop, + isa::Instruction::Return(isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }), + ] + ) } #[test] fn if_else_branch_from_false_branch() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") i32.const 1 @@ -684,51 +705,51 @@ fn if_else_branch_from_false_branch() { ) ) "#, - ); - let (code, pcs) = compile(&module); - assert_eq!( - code, - vec![ - isa::Instruction::I32Const(1), - isa::Instruction::BrIfEqz(isa::Target { - dst_pc: pcs[4], - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }), - isa::Instruction::I32Const(1), - isa::Instruction::Br(isa::Target { - dst_pc: pcs[9], - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }), - isa::Instruction::I32Const(2), - isa::Instruction::I32Const(1), - isa::Instruction::BrIfNez(isa::Target { - dst_pc: pcs[9], - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::Single, - }, - }), - isa::Instruction::Drop, - isa::Instruction::I32Const(3), - isa::Instruction::Drop, - isa::Instruction::Return(isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }), - ] - ) + ); + let (code, pcs) = compile(&module); + assert_eq!( + code, + vec![ + isa::Instruction::I32Const(1), + isa::Instruction::BrIfEqz(isa::Target { + dst_pc: pcs[4], + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }), + isa::Instruction::I32Const(1), + isa::Instruction::Br(isa::Target { + dst_pc: pcs[9], + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }), + isa::Instruction::I32Const(2), + isa::Instruction::I32Const(1), + isa::Instruction::BrIfNez(isa::Target { + dst_pc: pcs[9], + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::Single, + }, + }), + isa::Instruction::Drop, + isa::Instruction::I32Const(3), + isa::Instruction::Drop, + isa::Instruction::Return(isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }), + ] + ) } #[test] fn loop_() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") loop (result i32) @@ -740,33 +761,33 @@ fn loop_() { ) ) "#, - ); - let (code, _) = compile(&module); - assert_eq!( - code, - vec![ - isa::Instruction::I32Const(1), - isa::Instruction::BrIfNez(isa::Target { - dst_pc: 0, - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }), - isa::Instruction::I32Const(2), - isa::Instruction::Drop, - isa::Instruction::Return(isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }), - ] - ) + ); + let (code, _) = compile(&module); + assert_eq!( + code, + vec![ + isa::Instruction::I32Const(1), + isa::Instruction::BrIfNez(isa::Target { + dst_pc: 0, + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }), + isa::Instruction::I32Const(2), + isa::Instruction::Drop, + isa::Instruction::Return(isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }), + ] + ) } #[test] fn loop_empty() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") loop @@ -774,21 +795,21 @@ fn loop_empty() { ) ) "#, - ); - let (code, _) = compile(&module); - assert_eq!( - code, - vec![isa::Instruction::Return(isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }),] - ) + ); + let (code, _) = compile(&module); + assert_eq!( + code, + vec![isa::Instruction::Return(isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }),] + ) } #[test] fn brtable() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") block $1 @@ -800,40 +821,40 @@ fn brtable() { ) ) "#, - ); - let (code, pcs) = compile(&module); - assert_eq!( - code, - vec![ - isa::Instruction::I32Const(0), - isa::Instruction::BrTable(targets![ - isa::Target { - dst_pc: 0, - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }, - isa::Target { - dst_pc: pcs[2], - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - } - ]), - isa::Instruction::Return(isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }), - ] - ) + ); + let (code, pcs) = compile(&module); + assert_eq!( + code, + vec![ + isa::Instruction::I32Const(0), + isa::Instruction::BrTable(targets![ + isa::Target { + dst_pc: 0, + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }, + isa::Target { + dst_pc: pcs[2], + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + } + ]), + isa::Instruction::Return(isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }), + ] + ) } #[test] fn brtable_returns_result() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") block $1 (result i32) @@ -848,44 +869,44 @@ fn brtable_returns_result() { ) ) "#, - ); - let (code, pcs) = compile(&module); - println!("{:?}", (&code, &pcs)); - assert_eq!( - code, - vec![ - isa::Instruction::I32Const(0), - isa::Instruction::I32Const(1), - isa::Instruction::BrTable(targets![ - isa::Target { - dst_pc: pcs[3], - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::Single, - }, - }, - isa::Target { - dst_pc: pcs[4], - drop_keep: isa::DropKeep { - keep: isa::Keep::Single, - drop: 0, - }, - } - ]), - isa::Instruction::Unreachable, - isa::Instruction::Drop, - isa::Instruction::Return(isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }), - ] - ) + ); + let (code, pcs) = compile(&module); + println!("{:?}", (&code, &pcs)); + assert_eq!( + code, + vec![ + isa::Instruction::I32Const(0), + isa::Instruction::I32Const(1), + isa::Instruction::BrTable(targets![ + isa::Target { + dst_pc: pcs[3], + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::Single, + }, + }, + isa::Target { + dst_pc: pcs[4], + drop_keep: isa::DropKeep { + keep: isa::Keep::Single, + drop: 0, + }, + } + ]), + isa::Instruction::Unreachable, + isa::Instruction::Drop, + isa::Instruction::Return(isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }), + ] + ) } #[test] fn wabt_example() { - let module = validate( - r#" + let module = validate( + r#" (module (func (export "call") (param i32) (result i32) block $exit @@ -899,33 +920,33 @@ fn wabt_example() { ) ) "#, - ); - let (code, pcs) = compile(&module); - assert_eq!( - code, - vec![ - isa::Instruction::GetLocal(1), - isa::Instruction::BrIfNez(isa::Target { - dst_pc: pcs[4], - drop_keep: isa::DropKeep { - drop: 0, - keep: isa::Keep::None, - }, - }), - isa::Instruction::I32Const(1), - isa::Instruction::Return(isa::DropKeep { - drop: 1, // 1 parameter - keep: isa::Keep::Single, - }), - isa::Instruction::I32Const(2), - isa::Instruction::Return(isa::DropKeep { - drop: 1, - keep: isa::Keep::Single, - }), - isa::Instruction::Return(isa::DropKeep { - drop: 1, - keep: isa::Keep::Single, - }), - ] - ) + ); + let (code, pcs) = compile(&module); + assert_eq!( + code, + vec![ + isa::Instruction::GetLocal(1), + isa::Instruction::BrIfNez(isa::Target { + dst_pc: pcs[4], + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, + }), + isa::Instruction::I32Const(1), + isa::Instruction::Return(isa::DropKeep { + drop: 1, // 1 parameter + keep: isa::Keep::Single, + }), + isa::Instruction::I32Const(2), + isa::Instruction::Return(isa::DropKeep { + drop: 1, + keep: isa::Keep::Single, + }), + isa::Instruction::Return(isa::DropKeep { + drop: 1, + keep: isa::Keep::Single, + }), + ] + ) } diff --git a/src/validation/util.rs b/src/validation/util.rs index 69d70ac..6a42de4 100644 --- a/src/validation/util.rs +++ b/src/validation/util.rs @@ -10,124 +10,119 @@ use validation::Error; /// of a value_type and a count. #[derive(Debug)] pub struct Locals<'a> { - params: &'a [ValueType], - local_groups: &'a [Local], - count: u32, + params: &'a [ValueType], + local_groups: &'a [Local], + count: u32, } impl<'a> Locals<'a> { - /// Create a new wrapper around declared variables and parameters. - pub fn new(params: &'a [ValueType], local_groups: &'a [Local]) -> Result, Error> { - let mut acc = params.len() as u32; - for locals_group in local_groups { - acc = acc - .checked_add(locals_group.count()) - .ok_or_else(|| - Error(String::from("Locals range not in 32-bit range")) - )?; - } + /// Create a new wrapper around declared variables and parameters. + pub fn new(params: &'a [ValueType], local_groups: &'a [Local]) -> Result, Error> { + let mut acc = params.len() as u32; + for locals_group in local_groups { + acc = acc + .checked_add(locals_group.count()) + .ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?; + } - Ok(Locals { - params, - local_groups, - count: acc, - }) - } + Ok(Locals { + params, + local_groups, + count: acc, + }) + } - /// Returns parameter count. - pub fn param_count(&self) -> u32 { - self.params.len() as u32 - } + /// Returns parameter count. + pub fn param_count(&self) -> u32 { + self.params.len() as u32 + } - /// Returns total count of all declared locals and paramaterers. - pub fn count(&self) -> u32 { - self.count - } + /// Returns total count of all declared locals and paramaterers. + pub fn count(&self) -> u32 { + self.count + } - /// Returns the type of a local variable (either a declared local or a param). - /// - /// Returns `Err` in the case of overflow or when idx falls out of range. - pub fn type_of_local(&self, idx: u32) -> Result { - if let Some(param) = self.params.get(idx as usize) { - return Ok(*param); - } + /// Returns the type of a local variable (either a declared local or a param). + /// + /// Returns `Err` in the case of overflow or when idx falls out of range. + pub fn type_of_local(&self, idx: u32) -> Result { + if let Some(param) = self.params.get(idx as usize) { + return Ok(*param); + } - // If an index doesn't point to a param, then we have to look into local declarations. - let mut start_idx = self.param_count(); - for locals_group in self.local_groups { - let end_idx = start_idx - .checked_add(locals_group.count()) - .ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?; + // If an index doesn't point to a param, then we have to look into local declarations. + let mut start_idx = self.param_count(); + for locals_group in self.local_groups { + let end_idx = start_idx + .checked_add(locals_group.count()) + .ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?; - if idx >= start_idx && idx < end_idx { - return Ok(locals_group.value_type()); - } + if idx >= start_idx && idx < end_idx { + return Ok(locals_group.value_type()); + } - start_idx = end_idx; - } + start_idx = end_idx; + } - // We didn't find anything, that's an error. - // At this moment `start_idx` should hold the count of all locals - // (since it's either set to the `end_idx` or equal to `params.len()`) - let total_count = start_idx; + // We didn't find anything, that's an error. + // At this moment `start_idx` should hold the count of all locals + // (since it's either set to the `end_idx` or equal to `params.len()`) + let total_count = start_idx; - Err(Error(format!( - "Trying to access local with index {} when there are only {} locals", - idx, total_count - ))) - } + Err(Error(format!( + "Trying to access local with index {} when there are only {} locals", + idx, total_count + ))) + } } #[cfg(test)] mod tests { - use super::*; + use super::*; - #[test] - fn locals_it_works() { - let params = vec![ValueType::I32, ValueType::I64]; - let local_groups = vec![Local::new(2, ValueType::F32), Local::new(2, ValueType::F64)]; - let locals = Locals::new(¶ms, &local_groups).unwrap(); + #[test] + fn locals_it_works() { + let params = vec![ValueType::I32, ValueType::I64]; + let local_groups = vec![Local::new(2, ValueType::F32), Local::new(2, ValueType::F64)]; + let locals = Locals::new(¶ms, &local_groups).unwrap(); - assert_matches!(locals.type_of_local(0), Ok(ValueType::I32)); - assert_matches!(locals.type_of_local(1), Ok(ValueType::I64)); - assert_matches!(locals.type_of_local(2), Ok(ValueType::F32)); - assert_matches!(locals.type_of_local(3), Ok(ValueType::F32)); - assert_matches!(locals.type_of_local(4), Ok(ValueType::F64)); - assert_matches!(locals.type_of_local(5), Ok(ValueType::F64)); - assert_matches!(locals.type_of_local(6), Err(_)); - } + assert_matches!(locals.type_of_local(0), Ok(ValueType::I32)); + assert_matches!(locals.type_of_local(1), Ok(ValueType::I64)); + assert_matches!(locals.type_of_local(2), Ok(ValueType::F32)); + assert_matches!(locals.type_of_local(3), Ok(ValueType::F32)); + assert_matches!(locals.type_of_local(4), Ok(ValueType::F64)); + assert_matches!(locals.type_of_local(5), Ok(ValueType::F64)); + assert_matches!(locals.type_of_local(6), Err(_)); + } - #[test] - fn locals_no_declared_locals() { - let params = vec![ValueType::I32]; - let locals = Locals::new(¶ms, &[]).unwrap(); + #[test] + fn locals_no_declared_locals() { + let params = vec![ValueType::I32]; + let locals = Locals::new(¶ms, &[]).unwrap(); - assert_matches!(locals.type_of_local(0), Ok(ValueType::I32)); - assert_matches!(locals.type_of_local(1), Err(_)); - } + assert_matches!(locals.type_of_local(0), Ok(ValueType::I32)); + assert_matches!(locals.type_of_local(1), Err(_)); + } - #[test] - fn locals_no_params() { - let local_groups = vec![Local::new(2, ValueType::I32), Local::new(3, ValueType::I64)]; - let locals = Locals::new(&[], &local_groups).unwrap(); + #[test] + fn locals_no_params() { + let local_groups = vec![Local::new(2, ValueType::I32), Local::new(3, ValueType::I64)]; + let locals = Locals::new(&[], &local_groups).unwrap(); - assert_matches!(locals.type_of_local(0), Ok(ValueType::I32)); - assert_matches!(locals.type_of_local(1), Ok(ValueType::I32)); - assert_matches!(locals.type_of_local(2), Ok(ValueType::I64)); - assert_matches!(locals.type_of_local(3), Ok(ValueType::I64)); - assert_matches!(locals.type_of_local(4), Ok(ValueType::I64)); - assert_matches!(locals.type_of_local(5), Err(_)); - } + assert_matches!(locals.type_of_local(0), Ok(ValueType::I32)); + assert_matches!(locals.type_of_local(1), Ok(ValueType::I32)); + assert_matches!(locals.type_of_local(2), Ok(ValueType::I64)); + assert_matches!(locals.type_of_local(3), Ok(ValueType::I64)); + assert_matches!(locals.type_of_local(4), Ok(ValueType::I64)); + assert_matches!(locals.type_of_local(5), Err(_)); + } - #[test] - fn locals_u32_overflow() { - let local_groups = vec![ - Local::new(u32::max_value(), ValueType::I32), - Local::new(1, ValueType::I64), - ]; - assert_matches!( - Locals::new(&[], &local_groups), - Err(_) - ); - } + #[test] + fn locals_u32_overflow() { + let local_groups = vec![ + Local::new(u32::max_value(), ValueType::I32), + Local::new(1, ValueType::I64), + ]; + assert_matches!(Locals::new(&[], &local_groups), Err(_)); + } } diff --git a/src/value.rs b/src/value.rs index 96fa805..45e8970 100644 --- a/src/value.rs +++ b/src/value.rs @@ -8,7 +8,7 @@ use TrapKind; #[derive(Debug)] pub enum Error { /// The buffer is too short for the type being deserialized - InvalidLittleEndianBuffer, + InvalidLittleEndianBuffer, } /// Runtime representation of a value. @@ -20,14 +20,14 @@ pub enum Error { /// interpreted by respective operations as either unsigned or signed in two’s complement representation. #[derive(Copy, Clone, Debug, PartialEq)] pub enum RuntimeValue { - /// Value of 32-bit signed or unsigned integer. - I32(i32), - /// Value of 64-bit signed or unsigned integer. - I64(i64), - /// Value of 32-bit IEEE 754-2008 floating point number. - F32(F32), - /// Value of 64-bit IEEE 754-2008 floating point number. - F64(F64), + /// Value of 32-bit signed or unsigned integer. + I32(i32), + /// Value of 64-bit signed or unsigned integer. + I64(i64), + /// Value of 32-bit IEEE 754-2008 floating point number. + F32(F32), + /// Value of 64-bit IEEE 754-2008 floating point number. + F64(F64), } /// Trait for creating value from a [`RuntimeValue`]. @@ -41,218 +41,218 @@ pub enum RuntimeValue { /// [`RuntimeValue`]: enum.RuntimeValue.html pub trait FromRuntimeValue where - Self: Sized, + Self: Sized, { - /// Create a value of type `Self` from a given [`RuntimeValue`]. - /// - /// Returns `None` if the [`RuntimeValue`] is of type different than - /// expected by the conversion in question. - /// - /// [`RuntimeValue`]: enum.RuntimeValue.html - fn from_runtime_value(val: RuntimeValue) -> Option; + /// Create a value of type `Self` from a given [`RuntimeValue`]. + /// + /// Returns `None` if the [`RuntimeValue`] is of type different than + /// expected by the conversion in question. + /// + /// [`RuntimeValue`]: enum.RuntimeValue.html + fn from_runtime_value(val: RuntimeValue) -> Option; } /// Convert one type to another by wrapping. pub trait WrapInto { - /// Convert one type to another by wrapping. - fn wrap_into(self) -> T; + /// Convert one type to another by wrapping. + fn wrap_into(self) -> T; } /// Convert one type to another by rounding to the nearest integer towards zero. pub trait TryTruncateInto { - /// Convert one type to another by rounding to the nearest integer towards zero. - fn try_truncate_into(self) -> Result; + /// Convert one type to another by rounding to the nearest integer towards zero. + fn try_truncate_into(self) -> Result; } /// Convert one type to another by extending with leading zeroes. pub trait ExtendInto { - /// Convert one type to another by extending with leading zeroes. - fn extend_into(self) -> T; + /// Convert one type to another by extending with leading zeroes. + fn extend_into(self) -> T; } /// Reinterprets the bits of a value of one type as another type. pub trait TransmuteInto { - /// Reinterprets the bits of a value of one type as another type. - fn transmute_into(self) -> T; + /// Reinterprets the bits of a value of one type as another type. + fn transmute_into(self) -> T; } /// Convert from and to little endian. pub trait LittleEndianConvert where - Self: Sized, + Self: Sized, { - /// Convert to little endian buffer. - fn into_little_endian(self, buffer: &mut [u8]); - /// Convert from little endian buffer. - fn from_little_endian(buffer: &[u8]) -> Result; + /// Convert to little endian buffer. + fn into_little_endian(self, buffer: &mut [u8]); + /// Convert from little endian buffer. + fn from_little_endian(buffer: &[u8]) -> Result; } /// Arithmetic operations. pub trait ArithmeticOps { - /// Add two values. - fn add(self, other: T) -> T; - /// Subtract two values. - fn sub(self, other: T) -> T; - /// Multiply two values. - fn mul(self, other: T) -> T; - /// Divide two values. - fn div(self, other: T) -> Result; + /// Add two values. + fn add(self, other: T) -> T; + /// Subtract two values. + fn sub(self, other: T) -> T; + /// Multiply two values. + fn mul(self, other: T) -> T; + /// Divide two values. + fn div(self, other: T) -> Result; } /// Integer value. pub trait Integer: ArithmeticOps { - /// Counts leading zeros in the bitwise representation of the value. - fn leading_zeros(self) -> T; - /// Counts trailing zeros in the bitwise representation of the value. - fn trailing_zeros(self) -> T; - /// Counts 1-bits in the bitwise representation of the value. - fn count_ones(self) -> T; - /// Get left bit rotation result. - fn rotl(self, other: T) -> T; - /// Get right bit rotation result. - fn rotr(self, other: T) -> T; - /// Get division remainder. - fn rem(self, other: T) -> Result; + /// Counts leading zeros in the bitwise representation of the value. + fn leading_zeros(self) -> T; + /// Counts trailing zeros in the bitwise representation of the value. + fn trailing_zeros(self) -> T; + /// Counts 1-bits in the bitwise representation of the value. + fn count_ones(self) -> T; + /// Get left bit rotation result. + fn rotl(self, other: T) -> T; + /// Get right bit rotation result. + fn rotr(self, other: T) -> T; + /// Get division remainder. + fn rem(self, other: T) -> Result; } /// Float-point value. pub trait Float: ArithmeticOps { - /// Get absolute value. - fn abs(self) -> T; - /// Returns the largest integer less than or equal to a number. - fn floor(self) -> T; - /// Returns the smallest integer greater than or equal to a number. - fn ceil(self) -> T; - /// Returns the integer part of a number. - fn trunc(self) -> T; - /// Returns the nearest integer to a number. Round half-way cases away from 0.0. - fn round(self) -> T; - /// Returns the nearest integer to a number. Ties are round to even number. - fn nearest(self) -> T; - /// Takes the square root of a number. - fn sqrt(self) -> T; - /// Returns the minimum of the two numbers. - fn min(self, other: T) -> T; - /// Returns the maximum of the two numbers. - fn max(self, other: T) -> T; - /// Sets sign of this value to the sign of other value. - fn copysign(self, other: T) -> T; + /// Get absolute value. + fn abs(self) -> T; + /// Returns the largest integer less than or equal to a number. + fn floor(self) -> T; + /// Returns the smallest integer greater than or equal to a number. + fn ceil(self) -> T; + /// Returns the integer part of a number. + fn trunc(self) -> T; + /// Returns the nearest integer to a number. Round half-way cases away from 0.0. + fn round(self) -> T; + /// Returns the nearest integer to a number. Ties are round to even number. + fn nearest(self) -> T; + /// Takes the square root of a number. + fn sqrt(self) -> T; + /// Returns the minimum of the two numbers. + fn min(self, other: T) -> T; + /// Returns the maximum of the two numbers. + fn max(self, other: T) -> T; + /// Sets sign of this value to the sign of other value. + fn copysign(self, other: T) -> T; } impl RuntimeValue { - /// Creates new default value of given type. - pub fn default(value_type: ValueType) -> Self { - match value_type { - ValueType::I32 => RuntimeValue::I32(0), - ValueType::I64 => RuntimeValue::I64(0), - ValueType::F32 => RuntimeValue::F32(0f32.into()), - ValueType::F64 => RuntimeValue::F64(0f64.into()), - } - } + /// Creates new default value of given type. + pub fn default(value_type: ValueType) -> Self { + match value_type { + ValueType::I32 => RuntimeValue::I32(0), + ValueType::I64 => RuntimeValue::I64(0), + ValueType::F32 => RuntimeValue::F32(0f32.into()), + ValueType::F64 => RuntimeValue::F64(0f64.into()), + } + } - /// Creates new value by interpreting passed u32 as f32. - pub fn decode_f32(val: u32) -> Self { - RuntimeValue::F32(F32::from_bits(val)) - } + /// Creates new value by interpreting passed u32 as f32. + pub fn decode_f32(val: u32) -> Self { + RuntimeValue::F32(F32::from_bits(val)) + } - /// Creates new value by interpreting passed u64 as f64. - pub fn decode_f64(val: u64) -> Self { - RuntimeValue::F64(F64::from_bits(val)) - } + /// Creates new value by interpreting passed u64 as f64. + pub fn decode_f64(val: u64) -> Self { + RuntimeValue::F64(F64::from_bits(val)) + } - /// Get variable type for this value. - pub fn value_type(&self) -> ValueType { - match *self { - RuntimeValue::I32(_) => ValueType::I32, - RuntimeValue::I64(_) => ValueType::I64, - RuntimeValue::F32(_) => ValueType::F32, - RuntimeValue::F64(_) => ValueType::F64, - } - } + /// Get variable type for this value. + pub fn value_type(&self) -> ValueType { + match *self { + RuntimeValue::I32(_) => ValueType::I32, + RuntimeValue::I64(_) => ValueType::I64, + RuntimeValue::F32(_) => ValueType::F32, + RuntimeValue::F64(_) => ValueType::F64, + } + } - /// Returns `T` if this particular [`RuntimeValue`] contains - /// appropriate type. - /// - /// See [`FromRuntimeValue`] for details. - /// - /// [`FromRuntimeValue`]: trait.FromRuntimeValue.html - /// [`RuntimeValue`]: enum.RuntimeValue.html - pub fn try_into(self) -> Option { - FromRuntimeValue::from_runtime_value(self) - } + /// Returns `T` if this particular [`RuntimeValue`] contains + /// appropriate type. + /// + /// See [`FromRuntimeValue`] for details. + /// + /// [`FromRuntimeValue`]: trait.FromRuntimeValue.html + /// [`RuntimeValue`]: enum.RuntimeValue.html + pub fn try_into(self) -> Option { + FromRuntimeValue::from_runtime_value(self) + } } impl From for RuntimeValue { - fn from(val: i8) -> Self { - RuntimeValue::I32(val as i32) - } + fn from(val: i8) -> Self { + RuntimeValue::I32(val as i32) + } } impl From for RuntimeValue { - fn from(val: i16) -> Self { - RuntimeValue::I32(val as i32) - } + fn from(val: i16) -> Self { + RuntimeValue::I32(val as i32) + } } impl From for RuntimeValue { - fn from(val: i32) -> Self { - RuntimeValue::I32(val) - } + fn from(val: i32) -> Self { + RuntimeValue::I32(val) + } } impl From for RuntimeValue { - fn from(val: i64) -> Self { - RuntimeValue::I64(val) - } + fn from(val: i64) -> Self { + RuntimeValue::I64(val) + } } impl From for RuntimeValue { - fn from(val: u8) -> Self { - RuntimeValue::I32(val as i32) - } + fn from(val: u8) -> Self { + RuntimeValue::I32(val as i32) + } } impl From for RuntimeValue { - fn from(val: u16) -> Self { - RuntimeValue::I32(val as i32) - } + fn from(val: u16) -> Self { + RuntimeValue::I32(val as i32) + } } impl From for RuntimeValue { - fn from(val: u32) -> Self { - RuntimeValue::I32(val.transmute_into()) - } + fn from(val: u32) -> Self { + RuntimeValue::I32(val.transmute_into()) + } } impl From for RuntimeValue { - fn from(val: u64) -> Self { - RuntimeValue::I64(val.transmute_into()) - } + fn from(val: u64) -> Self { + RuntimeValue::I64(val.transmute_into()) + } } impl From for RuntimeValue { - fn from(val: F32) -> Self { - RuntimeValue::F32(val) - } + fn from(val: F32) -> Self { + RuntimeValue::F32(val) + } } impl From for RuntimeValue { - fn from(val: F64) -> Self { - RuntimeValue::F64(val) - } + fn from(val: F64) -> Self { + RuntimeValue::F64(val) + } } macro_rules! impl_from_runtime_value { - ($expected_rt_ty: ident, $into: ty) => { - impl FromRuntimeValue for $into { - fn from_runtime_value(val: RuntimeValue) -> Option { - match val { - RuntimeValue::$expected_rt_ty(val) => Some(val.transmute_into()), - _ => None, - } - } - } - }; + ($expected_rt_ty: ident, $into: ty) => { + impl FromRuntimeValue for $into { + fn from_runtime_value(val: RuntimeValue) -> Option { + match val { + RuntimeValue::$expected_rt_ty(val) => Some(val.transmute_into()), + _ => None, + } + } + } + }; } /// This conversion assumes that boolean values are represented by @@ -260,68 +260,68 @@ macro_rules! impl_from_runtime_value { /// /// [`I32`]: enum.RuntimeValue.html#variant.I32 impl FromRuntimeValue for bool { - fn from_runtime_value(val: RuntimeValue) -> Option { - match val { - RuntimeValue::I32(val) => Some(val != 0), - _ => None, - } - } + fn from_runtime_value(val: RuntimeValue) -> Option { + match val { + RuntimeValue::I32(val) => Some(val != 0), + _ => None, + } + } } /// This conversion assumes that `i8` is represented as an [`I32`]. /// /// [`I32`]: enum.RuntimeValue.html#variant.I32 impl FromRuntimeValue for i8 { - fn from_runtime_value(val: RuntimeValue) -> Option { - let min = i8::min_value() as i32; - let max = i8::max_value() as i32; - match val { - RuntimeValue::I32(val) if min <= val && val <= max => Some(val as i8), - _ => None, - } - } + fn from_runtime_value(val: RuntimeValue) -> Option { + let min = i8::min_value() as i32; + let max = i8::max_value() as i32; + match val { + RuntimeValue::I32(val) if min <= val && val <= max => Some(val as i8), + _ => None, + } + } } /// This conversion assumes that `i16` is represented as an [`I32`]. /// /// [`I32`]: enum.RuntimeValue.html#variant.I32 impl FromRuntimeValue for i16 { - fn from_runtime_value(val: RuntimeValue) -> Option { - let min = i16::min_value() as i32; - let max = i16::max_value() as i32; - match val { - RuntimeValue::I32(val) if min <= val && val <= max => Some(val as i16), - _ => None, - } - } + fn from_runtime_value(val: RuntimeValue) -> Option { + let min = i16::min_value() as i32; + let max = i16::max_value() as i32; + match val { + RuntimeValue::I32(val) if min <= val && val <= max => Some(val as i16), + _ => None, + } + } } /// This conversion assumes that `u8` is represented as an [`I32`]. /// /// [`I32`]: enum.RuntimeValue.html#variant.I32 impl FromRuntimeValue for u8 { - fn from_runtime_value(val: RuntimeValue) -> Option { - let min = u8::min_value() as i32; - let max = u8::max_value() as i32; - match val { - RuntimeValue::I32(val) if min <= val && val <= max => Some(val as u8), - _ => None, - } - } + fn from_runtime_value(val: RuntimeValue) -> Option { + let min = u8::min_value() as i32; + let max = u8::max_value() as i32; + match val { + RuntimeValue::I32(val) if min <= val && val <= max => Some(val as u8), + _ => None, + } + } } /// This conversion assumes that `u16` is represented as an [`I32`]. /// /// [`I32`]: enum.RuntimeValue.html#variant.I32 impl FromRuntimeValue for u16 { - fn from_runtime_value(val: RuntimeValue) -> Option { - let min = u16::min_value() as i32; - let max = u16::max_value() as i32; - match val { - RuntimeValue::I32(val) if min <= val && val <= max => Some(val as u16), - _ => None, - } - } + fn from_runtime_value(val: RuntimeValue) -> Option { + let min = u16::min_value() as i32; + let max = u16::max_value() as i32; + match val { + RuntimeValue::I32(val) if min <= val && val <= max => Some(val as u16), + _ => None, + } + } } impl_from_runtime_value!(I32, i32); @@ -332,20 +332,20 @@ impl_from_runtime_value!(I32, u32); impl_from_runtime_value!(I64, u64); macro_rules! impl_wrap_into { - ($from:ident, $into:ident) => { - impl WrapInto<$into> for $from { - fn wrap_into(self) -> $into { - self as $into - } - } - }; - ($from:ident, $intermediate:ident, $into:ident) => { - impl WrapInto<$into> for $from { - fn wrap_into(self) -> $into { - $into::from(self as $intermediate) - } - } - }; + ($from:ident, $into:ident) => { + impl WrapInto<$into> for $from { + fn wrap_into(self) -> $into { + self as $into + } + } + }; + ($from:ident, $intermediate:ident, $into:ident) => { + impl WrapInto<$into> for $from { + fn wrap_into(self) -> $into { + $into::from(self as $intermediate) + } + } + }; } impl_wrap_into!(i32, i8); @@ -361,39 +361,39 @@ impl_wrap_into!(u64, f32, F32); impl_wrap_into!(f64, f32); impl WrapInto for F64 { - fn wrap_into(self) -> F32 { - (f64::from(self) as f32).into() - } + fn wrap_into(self) -> F32 { + (f64::from(self) as f32).into() + } } macro_rules! impl_try_truncate_into { - ($from: ident, $into: ident) => { - impl TryTruncateInto<$into, TrapKind> for $from { - fn try_truncate_into(self) -> Result<$into, TrapKind> { - // Casting from a float to an integer will round the float towards zero - // NOTE: currently this will cause Undefined Behavior if the rounded value cannot be represented by the - // target integer type. This includes Inf and NaN. This is a bug and will be fixed. - if self.is_nan() || self.is_infinite() { - return Err(TrapKind::InvalidConversionToInt); - } + ($from: ident, $into: ident) => { + impl TryTruncateInto<$into, TrapKind> for $from { + fn try_truncate_into(self) -> Result<$into, TrapKind> { + // Casting from a float to an integer will round the float towards zero + // NOTE: currently this will cause Undefined Behavior if the rounded value cannot be represented by the + // target integer type. This includes Inf and NaN. This is a bug and will be fixed. + if self.is_nan() || self.is_infinite() { + return Err(TrapKind::InvalidConversionToInt); + } - // range check - let result = self as $into; - if result as $from != self.trunc() { - return Err(TrapKind::InvalidConversionToInt); - } + // range check + let result = self as $into; + if result as $from != self.trunc() { + return Err(TrapKind::InvalidConversionToInt); + } - Ok(self as $into) - } - } - }; - ($from:ident, $intermediate:ident, $into:ident) => { - impl TryTruncateInto<$into, TrapKind> for $from { - fn try_truncate_into(self) -> Result<$into, TrapKind> { - $intermediate::from(self).try_truncate_into() - } - } - }; + Ok(self as $into) + } + } + }; + ($from:ident, $intermediate:ident, $into:ident) => { + impl TryTruncateInto<$into, TrapKind> for $from { + fn try_truncate_into(self) -> Result<$into, TrapKind> { + $intermediate::from(self).try_truncate_into() + } + } + }; } impl_try_truncate_into!(f32, i32); @@ -414,20 +414,20 @@ impl_try_truncate_into!(F64, f64, u32); impl_try_truncate_into!(F64, f64, u64); macro_rules! impl_extend_into { - ($from:ident, $into:ident) => { - impl ExtendInto<$into> for $from { - fn extend_into(self) -> $into { - self as $into - } - } - }; - ($from:ident, $intermediate:ident, $into:ident) => { - impl ExtendInto<$into> for $from { - fn extend_into(self) -> $into { - $into::from(self as $intermediate) - } - } - }; + ($from:ident, $into:ident) => { + impl ExtendInto<$into> for $from { + fn extend_into(self) -> $into { + self as $into + } + } + }; + ($from:ident, $intermediate:ident, $into:ident) => { + impl ExtendInto<$into> for $from { + fn extend_into(self) -> $into { + $into::from(self as $intermediate) + } + } + }; } impl_extend_into!(i8, i32); @@ -458,19 +458,19 @@ impl_extend_into!(u64, f64, F64); impl_extend_into!(f32, f64, F64); impl ExtendInto for F32 { - fn extend_into(self) -> F64 { - (f32::from(self) as f64).into() - } + fn extend_into(self) -> F64 { + (f32::from(self) as f64).into() + } } macro_rules! impl_transmute_into_self { - ($type: ident) => { - impl TransmuteInto<$type> for $type { - fn transmute_into(self) -> $type { - self - } - } - }; + ($type: ident) => { + impl TransmuteInto<$type> for $type { + fn transmute_into(self) -> $type { + self + } + } + }; } impl_transmute_into_self!(i32); @@ -481,13 +481,13 @@ impl_transmute_into_self!(F32); impl_transmute_into_self!(F64); macro_rules! impl_transmute_into_as { - ($from: ident, $into: ident) => { - impl TransmuteInto<$into> for $from { - fn transmute_into(self) -> $into { - self as $into - } - } - }; + ($from: ident, $into: ident) => { + impl TransmuteInto<$into> for $from { + fn transmute_into(self) -> $into { + self as $into + } + } + }; } impl_transmute_into_as!(i8, u8); @@ -495,247 +495,247 @@ impl_transmute_into_as!(i32, u32); impl_transmute_into_as!(i64, u64); macro_rules! impl_transmute_into_npf { - ($npf:ident, $float:ident, $signed:ident, $unsigned:ident) => { - impl TransmuteInto<$float> for $npf { - fn transmute_into(self) -> $float { - self.into() - } - } + ($npf:ident, $float:ident, $signed:ident, $unsigned:ident) => { + impl TransmuteInto<$float> for $npf { + fn transmute_into(self) -> $float { + self.into() + } + } - impl TransmuteInto<$npf> for $float { - fn transmute_into(self) -> $npf { - self.into() - } - } + impl TransmuteInto<$npf> for $float { + fn transmute_into(self) -> $npf { + self.into() + } + } - impl TransmuteInto<$signed> for $npf { - fn transmute_into(self) -> $signed { - self.to_bits() as _ - } - } + impl TransmuteInto<$signed> for $npf { + fn transmute_into(self) -> $signed { + self.to_bits() as _ + } + } - impl TransmuteInto<$unsigned> for $npf { - fn transmute_into(self) -> $unsigned { - self.to_bits() - } - } + impl TransmuteInto<$unsigned> for $npf { + fn transmute_into(self) -> $unsigned { + self.to_bits() + } + } - impl TransmuteInto<$npf> for $signed { - fn transmute_into(self) -> $npf { - $npf::from_bits(self as _) - } - } + impl TransmuteInto<$npf> for $signed { + fn transmute_into(self) -> $npf { + $npf::from_bits(self as _) + } + } - impl TransmuteInto<$npf> for $unsigned { - fn transmute_into(self) -> $npf { - $npf::from_bits(self) - } - } - }; + impl TransmuteInto<$npf> for $unsigned { + fn transmute_into(self) -> $npf { + $npf::from_bits(self) + } + } + }; } impl_transmute_into_npf!(F32, f32, i32, u32); impl_transmute_into_npf!(F64, f64, i64, u64); impl TransmuteInto for f32 { - fn transmute_into(self) -> i32 { - self.to_bits() as i32 - } + fn transmute_into(self) -> i32 { + self.to_bits() as i32 + } } impl TransmuteInto for f64 { - fn transmute_into(self) -> i64 { - self.to_bits() as i64 - } + fn transmute_into(self) -> i64 { + self.to_bits() as i64 + } } impl TransmuteInto for i32 { - fn transmute_into(self) -> f32 { - f32::from_bits(self as u32) - } + fn transmute_into(self) -> f32 { + f32::from_bits(self as u32) + } } impl TransmuteInto for i64 { - fn transmute_into(self) -> f64 { - f64::from_bits(self as u64) - } + fn transmute_into(self) -> f64 { + f64::from_bits(self as u64) + } } impl TransmuteInto for u32 { - fn transmute_into(self) -> i32 { - self as _ - } + fn transmute_into(self) -> i32 { + self as _ + } } impl TransmuteInto for u64 { - fn transmute_into(self) -> i64 { - self as _ - } + fn transmute_into(self) -> i64 { + self as _ + } } impl LittleEndianConvert for i8 { - fn into_little_endian(self, buffer: &mut [u8]) { - buffer[0] = self as u8; - } + fn into_little_endian(self, buffer: &mut [u8]) { + buffer[0] = self as u8; + } - fn from_little_endian(buffer: &[u8]) -> Result { - buffer - .get(0) - .map(|v| *v as i8) - .ok_or_else(|| Error::InvalidLittleEndianBuffer) - } + fn from_little_endian(buffer: &[u8]) -> Result { + buffer + .get(0) + .map(|v| *v as i8) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) + } } impl LittleEndianConvert for u8 { - fn into_little_endian(self, buffer: &mut [u8]) { - buffer[0] = self; - } + fn into_little_endian(self, buffer: &mut [u8]) { + buffer[0] = self; + } - fn from_little_endian(buffer: &[u8]) -> Result { - buffer - .get(0) - .cloned() - .ok_or_else(|| Error::InvalidLittleEndianBuffer) - } + fn from_little_endian(buffer: &[u8]) -> Result { + buffer + .get(0) + .cloned() + .ok_or_else(|| Error::InvalidLittleEndianBuffer) + } } impl LittleEndianConvert for i16 { - fn into_little_endian(self, buffer: &mut [u8]) { - LittleEndian::write_i16(buffer, self); - } + fn into_little_endian(self, buffer: &mut [u8]) { + LittleEndian::write_i16(buffer, self); + } - fn from_little_endian(buffer: &[u8]) -> Result { - buffer - .get(0..2) - .map(LittleEndian::read_i16) - .ok_or_else(|| Error::InvalidLittleEndianBuffer) - } + fn from_little_endian(buffer: &[u8]) -> Result { + buffer + .get(0..2) + .map(LittleEndian::read_i16) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) + } } impl LittleEndianConvert for u16 { - fn into_little_endian(self, buffer: &mut [u8]) { - LittleEndian::write_u16(buffer, self); - } + fn into_little_endian(self, buffer: &mut [u8]) { + LittleEndian::write_u16(buffer, self); + } - fn from_little_endian(buffer: &[u8]) -> Result { - buffer - .get(0..2) - .map(LittleEndian::read_u16) - .ok_or_else(|| Error::InvalidLittleEndianBuffer) - } + fn from_little_endian(buffer: &[u8]) -> Result { + buffer + .get(0..2) + .map(LittleEndian::read_u16) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) + } } impl LittleEndianConvert for i32 { - fn into_little_endian(self, buffer: &mut [u8]) { - LittleEndian::write_i32(buffer, self); - } + fn into_little_endian(self, buffer: &mut [u8]) { + LittleEndian::write_i32(buffer, self); + } - fn from_little_endian(buffer: &[u8]) -> Result { - buffer - .get(0..4) - .map(LittleEndian::read_i32) - .ok_or_else(|| Error::InvalidLittleEndianBuffer) - } + fn from_little_endian(buffer: &[u8]) -> Result { + buffer + .get(0..4) + .map(LittleEndian::read_i32) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) + } } impl LittleEndianConvert for u32 { - fn into_little_endian(self, buffer: &mut [u8]) { - LittleEndian::write_u32(buffer, self); - } + fn into_little_endian(self, buffer: &mut [u8]) { + LittleEndian::write_u32(buffer, self); + } - fn from_little_endian(buffer: &[u8]) -> Result { - buffer - .get(0..4) - .map(LittleEndian::read_u32) - .ok_or_else(|| Error::InvalidLittleEndianBuffer) - } + fn from_little_endian(buffer: &[u8]) -> Result { + buffer + .get(0..4) + .map(LittleEndian::read_u32) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) + } } impl LittleEndianConvert for i64 { - fn into_little_endian(self, buffer: &mut [u8]) { - LittleEndian::write_i64(buffer, self); - } + fn into_little_endian(self, buffer: &mut [u8]) { + LittleEndian::write_i64(buffer, self); + } - fn from_little_endian(buffer: &[u8]) -> Result { - buffer - .get(0..8) - .map(LittleEndian::read_i64) - .ok_or_else(|| Error::InvalidLittleEndianBuffer) - } + fn from_little_endian(buffer: &[u8]) -> Result { + buffer + .get(0..8) + .map(LittleEndian::read_i64) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) + } } impl LittleEndianConvert for f32 { - fn into_little_endian(self, buffer: &mut [u8]) { - LittleEndian::write_f32(buffer, self); - } + fn into_little_endian(self, buffer: &mut [u8]) { + LittleEndian::write_f32(buffer, self); + } - fn from_little_endian(buffer: &[u8]) -> Result { - buffer - .get(0..4) - .map(LittleEndian::read_f32) - .ok_or_else(|| Error::InvalidLittleEndianBuffer) - } + fn from_little_endian(buffer: &[u8]) -> Result { + buffer + .get(0..4) + .map(LittleEndian::read_f32) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) + } } impl LittleEndianConvert for f64 { - fn into_little_endian(self, buffer: &mut [u8]) { - LittleEndian::write_f64(buffer, self); - } + fn into_little_endian(self, buffer: &mut [u8]) { + LittleEndian::write_f64(buffer, self); + } - fn from_little_endian(buffer: &[u8]) -> Result { - buffer - .get(0..8) - .map(LittleEndian::read_f64) - .ok_or_else(|| Error::InvalidLittleEndianBuffer) - } + fn from_little_endian(buffer: &[u8]) -> Result { + buffer + .get(0..8) + .map(LittleEndian::read_f64) + .ok_or_else(|| Error::InvalidLittleEndianBuffer) + } } impl LittleEndianConvert for F32 { - fn into_little_endian(self, buffer: &mut [u8]) { - (self.to_bits() as i32).into_little_endian(buffer) - } + fn into_little_endian(self, buffer: &mut [u8]) { + (self.to_bits() as i32).into_little_endian(buffer) + } - fn from_little_endian(buffer: &[u8]) -> Result { - i32::from_little_endian(buffer).map(|val| Self::from_bits(val as _)) - } + fn from_little_endian(buffer: &[u8]) -> Result { + i32::from_little_endian(buffer).map(|val| Self::from_bits(val as _)) + } } impl LittleEndianConvert for F64 { - fn into_little_endian(self, buffer: &mut [u8]) { - (self.to_bits() as i64).into_little_endian(buffer) - } + fn into_little_endian(self, buffer: &mut [u8]) { + (self.to_bits() as i64).into_little_endian(buffer) + } - fn from_little_endian(buffer: &[u8]) -> Result { - i64::from_little_endian(buffer).map(|val| Self::from_bits(val as _)) - } + fn from_little_endian(buffer: &[u8]) -> Result { + i64::from_little_endian(buffer).map(|val| Self::from_bits(val as _)) + } } macro_rules! impl_integer_arithmetic_ops { - ($type: ident) => { - impl ArithmeticOps<$type> for $type { - fn add(self, other: $type) -> $type { - self.wrapping_add(other) - } - fn sub(self, other: $type) -> $type { - self.wrapping_sub(other) - } - fn mul(self, other: $type) -> $type { - self.wrapping_mul(other) - } - fn div(self, other: $type) -> Result<$type, TrapKind> { - if other == 0 { - Err(TrapKind::DivisionByZero) - } else { - let (result, overflow) = self.overflowing_div(other); - if overflow { - Err(TrapKind::InvalidConversionToInt) - } else { - Ok(result) - } - } - } - } - }; + ($type: ident) => { + impl ArithmeticOps<$type> for $type { + fn add(self, other: $type) -> $type { + self.wrapping_add(other) + } + fn sub(self, other: $type) -> $type { + self.wrapping_sub(other) + } + fn mul(self, other: $type) -> $type { + self.wrapping_mul(other) + } + fn div(self, other: $type) -> Result<$type, TrapKind> { + if other == 0 { + Err(TrapKind::DivisionByZero) + } else { + let (result, overflow) = self.overflowing_div(other); + if overflow { + Err(TrapKind::InvalidConversionToInt) + } else { + Ok(result) + } + } + } + } + }; } impl_integer_arithmetic_ops!(i32); @@ -744,22 +744,22 @@ impl_integer_arithmetic_ops!(i64); impl_integer_arithmetic_ops!(u64); macro_rules! impl_float_arithmetic_ops { - ($type: ident) => { - impl ArithmeticOps<$type> for $type { - fn add(self, other: $type) -> $type { - self + other - } - fn sub(self, other: $type) -> $type { - self - other - } - fn mul(self, other: $type) -> $type { - self * other - } - fn div(self, other: $type) -> Result<$type, TrapKind> { - Ok(self / other) - } - } - }; + ($type: ident) => { + impl ArithmeticOps<$type> for $type { + fn add(self, other: $type) -> $type { + self + other + } + fn sub(self, other: $type) -> $type { + self - other + } + fn mul(self, other: $type) -> $type { + self * other + } + fn div(self, other: $type) -> Result<$type, TrapKind> { + Ok(self / other) + } + } + }; } impl_float_arithmetic_ops!(f32); @@ -768,32 +768,32 @@ impl_float_arithmetic_ops!(F32); impl_float_arithmetic_ops!(F64); macro_rules! impl_integer { - ($type: ident) => { - impl Integer<$type> for $type { - fn leading_zeros(self) -> $type { - self.leading_zeros() as $type - } - fn trailing_zeros(self) -> $type { - self.trailing_zeros() as $type - } - fn count_ones(self) -> $type { - self.count_ones() as $type - } - fn rotl(self, other: $type) -> $type { - self.rotate_left(other as u32) - } - fn rotr(self, other: $type) -> $type { - self.rotate_right(other as u32) - } - fn rem(self, other: $type) -> Result<$type, TrapKind> { - if other == 0 { - Err(TrapKind::DivisionByZero) - } else { - Ok(self.wrapping_rem(other)) - } - } - } - }; + ($type: ident) => { + impl Integer<$type> for $type { + fn leading_zeros(self) -> $type { + self.leading_zeros() as $type + } + fn trailing_zeros(self) -> $type { + self.trailing_zeros() as $type + } + fn count_ones(self) -> $type { + self.count_ones() as $type + } + fn rotl(self, other: $type) -> $type { + self.rotate_left(other as u32) + } + fn rotr(self, other: $type) -> $type { + self.rotate_right(other as u32) + } + fn rem(self, other: $type) -> Result<$type, TrapKind> { + if other == 0 { + Err(TrapKind::DivisionByZero) + } else { + Ok(self.wrapping_rem(other)) + } + } + } + }; } impl_integer!(i32); @@ -805,15 +805,15 @@ impl_integer!(u64); // And libm's implementation in no_std #[cfg(feature = "std")] macro_rules! call_math { - ($op:ident, $e:expr, $fXX:ident, $FXXExt:ident) => { - $fXX::$op($e) - }; + ($op:ident, $e:expr, $fXX:ident, $FXXExt:ident) => { + $fXX::$op($e) + }; } #[cfg(not(feature = "std"))] macro_rules! call_math { - ($op:ident, $e:expr, $fXX:ident, $FXXExt:ident) => { - ::libm::$FXXExt::$op($e) - }; + ($op:ident, $e:expr, $fXX:ident, $FXXExt:ident) => { + ::libm::$FXXExt::$op($e) + }; } // We cannot call the math functions directly, because there are multiple available implementaitons in no_std. @@ -823,85 +823,85 @@ macro_rules! call_math { // both of which are trait implementations and hence ambiguous. // So we have to use a full path, which is what `call_math!` does. macro_rules! impl_float { - ($type:ident, $fXX:ident, $FXXExt:ident, $iXX:ident) => { - impl Float<$type> for $type { - fn abs(self) -> $type { - call_math!(abs, $fXX::from(self), $fXX, $FXXExt).into() - } - fn floor(self) -> $type { - call_math!(floor, $fXX::from(self), $fXX, $FXXExt).into() - } - fn ceil(self) -> $type { - call_math!(ceil, $fXX::from(self), $fXX, $FXXExt).into() - } - fn trunc(self) -> $type { - call_math!(trunc, $fXX::from(self), $fXX, $FXXExt).into() - } - fn round(self) -> $type { - call_math!(round, $fXX::from(self), $fXX, $FXXExt).into() - } - fn nearest(self) -> $type { - let round = self.round(); - if call_math!(fract, $fXX::from(self), $fXX, $FXXExt).abs() != 0.5 { - return round; - } + ($type:ident, $fXX:ident, $FXXExt:ident, $iXX:ident) => { + impl Float<$type> for $type { + fn abs(self) -> $type { + call_math!(abs, $fXX::from(self), $fXX, $FXXExt).into() + } + fn floor(self) -> $type { + call_math!(floor, $fXX::from(self), $fXX, $FXXExt).into() + } + fn ceil(self) -> $type { + call_math!(ceil, $fXX::from(self), $fXX, $FXXExt).into() + } + fn trunc(self) -> $type { + call_math!(trunc, $fXX::from(self), $fXX, $FXXExt).into() + } + fn round(self) -> $type { + call_math!(round, $fXX::from(self), $fXX, $FXXExt).into() + } + fn nearest(self) -> $type { + let round = self.round(); + if call_math!(fract, $fXX::from(self), $fXX, $FXXExt).abs() != 0.5 { + return round; + } - use core::ops::Rem; - if round.rem(2.0) == 1.0 { - self.floor() - } else if round.rem(2.0) == -1.0 { - self.ceil() - } else { - round - } - } - fn sqrt(self) -> $type { - call_math!(sqrt, $fXX::from(self), $fXX, $FXXExt).into() - } - // This instruction corresponds to what is sometimes called "minNaN" in other languages. - fn min(self, other: $type) -> $type { - if self.is_nan() { - return self; - } - if other.is_nan() { - return other; - } + use core::ops::Rem; + if round.rem(2.0) == 1.0 { + self.floor() + } else if round.rem(2.0) == -1.0 { + self.ceil() + } else { + round + } + } + fn sqrt(self) -> $type { + call_math!(sqrt, $fXX::from(self), $fXX, $FXXExt).into() + } + // This instruction corresponds to what is sometimes called "minNaN" in other languages. + fn min(self, other: $type) -> $type { + if self.is_nan() { + return self; + } + if other.is_nan() { + return other; + } - self.min(other) - } - // This instruction corresponds to what is sometimes called "maxNaN" in other languages. - fn max(self, other: $type) -> $type { - if self.is_nan() { - return self; - } - if other.is_nan() { - return other; - } + self.min(other) + } + // This instruction corresponds to what is sometimes called "maxNaN" in other languages. + fn max(self, other: $type) -> $type { + if self.is_nan() { + return self; + } + if other.is_nan() { + return other; + } - self.max(other) - } - fn copysign(self, other: $type) -> $type { - use core::mem::size_of; + self.max(other) + } + fn copysign(self, other: $type) -> $type { + use core::mem::size_of; - if self.is_nan() { - return self; - } + if self.is_nan() { + return self; + } - let sign_mask: $iXX = 1 << ((size_of::<$iXX>() << 3) - 1); - let self_int: $iXX = self.transmute_into(); - let other_int: $iXX = other.transmute_into(); - let is_self_sign_set = (self_int & sign_mask) != 0; - let is_other_sign_set = (other_int & sign_mask) != 0; - if is_self_sign_set == is_other_sign_set { - self - } else if is_other_sign_set { - (self_int | sign_mask).transmute_into() - } else { - (self_int & !sign_mask).transmute_into() - } - } - } - }; + let sign_mask: $iXX = 1 << ((size_of::<$iXX>() << 3) - 1); + let self_int: $iXX = self.transmute_into(); + let other_int: $iXX = other.transmute_into(); + let is_self_sign_set = (self_int & sign_mask) != 0; + let is_other_sign_set = (other_int & sign_mask) != 0; + if is_self_sign_set == is_other_sign_set { + self + } else if is_other_sign_set { + (self_int | sign_mask).transmute_into() + } else { + (self_int & !sign_mask).transmute_into() + } + } + } + }; } impl_float!(f32, f32, F32Ext, i32); diff --git a/tests/spec/mod.rs b/tests/spec/mod.rs index 856b26b..3cd1f97 100644 --- a/tests/spec/mod.rs +++ b/tests/spec/mod.rs @@ -1,12 +1,12 @@ mod run; macro_rules! run_test { - ($label: expr, $test_name: ident) => ( + ($label: expr, $test_name: ident) => { #[test] fn $test_name() { self::run::spec($label) } - ); + }; } run_test!("address", wasm_address); diff --git a/tests/spec/run.rs b/tests/spec/run.rs index 3f69cd8..59db57b 100644 --- a/tests/spec/run.rs +++ b/tests/spec/run.rs @@ -1,22 +1,24 @@ #![cfg(test)] -use std::fs::File; use std::collections::HashMap; +use std::fs::File; use wabt::script::{self, Action, Command, CommandKind, ScriptParser, Value}; use wasmi::memory_units::Pages; -use wasmi::{Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor, - GlobalInstance, GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor, - MemoryInstance, MemoryRef, Module, ModuleImportResolver, ModuleInstance, ModuleRef, - RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableInstance, TableRef, Trap}; +use wasmi::{ + Error as InterpreterError, Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalInstance, + GlobalRef, ImportResolver, ImportsBuilder, MemoryDescriptor, MemoryInstance, MemoryRef, Module, + ModuleImportResolver, ModuleInstance, ModuleRef, RuntimeArgs, RuntimeValue, Signature, + TableDescriptor, TableInstance, TableRef, Trap, +}; fn spec_to_runtime_value(val: Value) -> RuntimeValue { - match val { - Value::I32(v) => RuntimeValue::I32(v), - Value::I64(v) => RuntimeValue::I64(v), - Value::F32(v) => RuntimeValue::F32(v.into()), - Value::F64(v) => RuntimeValue::F64(v.into()), - } + match val { + Value::I32(v) => RuntimeValue::I32(v), + Value::I64(v) => RuntimeValue::I64(v), + Value::F32(v) => RuntimeValue::F32(v.into()), + Value::F64(v) => RuntimeValue::F64(v.into()), + } } #[derive(Debug)] @@ -48,15 +50,15 @@ struct SpecModule { } impl SpecModule { - fn new() -> Self { - SpecModule { - table: TableInstance::alloc(10, Some(20)).unwrap(), - memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(), - global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false), - global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0.into()), false), - global_f64: GlobalInstance::alloc(RuntimeValue::F64(666.0.into()), false), - } - } + fn new() -> Self { + SpecModule { + table: TableInstance::alloc(10, Some(20)).unwrap(), + memory: MemoryInstance::alloc(Pages(1), Some(Pages(2))).unwrap(), + global_i32: GlobalInstance::alloc(RuntimeValue::I32(666), false), + global_f32: GlobalInstance::alloc(RuntimeValue::F32(666.0.into()), false), + global_f64: GlobalInstance::alloc(RuntimeValue::F64(666.0.into()), false), + } + } } const PRINT_FUNC_INDEX: usize = 0; @@ -83,29 +85,29 @@ impl ModuleImportResolver for SpecModule { field_name: &str, func_type: &Signature, ) -> Result { - let index = match field_name { - "print" => PRINT_FUNC_INDEX, - "print_i32" => PRINT_FUNC_INDEX, - "print_i32_f32" => PRINT_FUNC_INDEX, - "print_f64_f64" => PRINT_FUNC_INDEX, - "print_f32" => PRINT_FUNC_INDEX, - "print_f64" => PRINT_FUNC_INDEX, - _ => { - return Err(InterpreterError::Instantiation(format!( - "Unknown host func import {}", - field_name - ))); - } - }; + let index = match field_name { + "print" => PRINT_FUNC_INDEX, + "print_i32" => PRINT_FUNC_INDEX, + "print_i32_f32" => PRINT_FUNC_INDEX, + "print_f64_f64" => PRINT_FUNC_INDEX, + "print_f32" => PRINT_FUNC_INDEX, + "print_f64" => PRINT_FUNC_INDEX, + _ => { + return Err(InterpreterError::Instantiation(format!( + "Unknown host func import {}", + field_name + ))); + } + }; - if func_type.return_type().is_some() { - return Err(InterpreterError::Instantiation( - "Function `print_` have unit return type".into(), - )); - } + if func_type.return_type().is_some() { + return Err(InterpreterError::Instantiation( + "Function `print_` have unit return type".into(), + )); + } let func = FuncInstance::alloc_host(func_type.clone(), index); - return Ok(func); + return Ok(func); } fn resolve_global( @@ -113,380 +115,389 @@ impl ModuleImportResolver for SpecModule { field_name: &str, _global_type: &GlobalDescriptor, ) -> Result { - match field_name { - "global_i32" => Ok(self.global_i32.clone()), - "global_f32" => Ok(self.global_f32.clone()), - "global_f64" => Ok(self.global_f64.clone()), - _ => Err(InterpreterError::Instantiation(format!( - "Unknown host global import {}", - field_name - ))), - } - } + match field_name { + "global_i32" => Ok(self.global_i32.clone()), + "global_f32" => Ok(self.global_f32.clone()), + "global_f64" => Ok(self.global_f64.clone()), + _ => Err(InterpreterError::Instantiation(format!( + "Unknown host global import {}", + field_name + ))), + } + } - fn resolve_memory( - &self, - field_name: &str, - _memory_type: &MemoryDescriptor, - ) -> Result { - if field_name == "memory" { - return Ok(self.memory.clone()); - } + fn resolve_memory( + &self, + field_name: &str, + _memory_type: &MemoryDescriptor, + ) -> Result { + if field_name == "memory" { + return Ok(self.memory.clone()); + } - Err(InterpreterError::Instantiation(format!( - "Unknown host memory import {}", - field_name - ))) - } + Err(InterpreterError::Instantiation(format!( + "Unknown host memory import {}", + field_name + ))) + } - fn resolve_table( - &self, - field_name: &str, - _table_type: &TableDescriptor, - ) -> Result { - if field_name == "table" { - return Ok(self.table.clone()); - } + fn resolve_table( + &self, + field_name: &str, + _table_type: &TableDescriptor, + ) -> Result { + if field_name == "table" { + return Ok(self.table.clone()); + } - Err(InterpreterError::Instantiation(format!( - "Unknown host table import {}", - field_name - ))) - } + Err(InterpreterError::Instantiation(format!( + "Unknown host table import {}", + field_name + ))) + } } struct SpecDriver { - spec_module: SpecModule, - instances: HashMap, - last_module: Option, + spec_module: SpecModule, + instances: HashMap, + last_module: Option, } impl SpecDriver { - fn new() -> SpecDriver { - SpecDriver { - spec_module: SpecModule::new(), - instances: HashMap::new(), - last_module: None, - } - } + fn new() -> SpecDriver { + SpecDriver { + spec_module: SpecModule::new(), + instances: HashMap::new(), + last_module: None, + } + } - fn spec_module(&mut self) -> &mut SpecModule { - &mut self.spec_module - } + fn spec_module(&mut self) -> &mut SpecModule { + &mut self.spec_module + } - fn add_module(&mut self, name: Option, module: ModuleRef) { - self.last_module = Some(module.clone()); - if let Some(name) = name { - self.instances.insert(name, module); - } - } + fn add_module(&mut self, name: Option, module: ModuleRef) { + self.last_module = Some(module.clone()); + if let Some(name) = name { + self.instances.insert(name, module); + } + } - fn module(&self, name: &str) -> Result { - self.instances.get(name).cloned().ok_or_else(|| { - InterpreterError::Instantiation(format!("Module not registered {}", name)) - }) - } + fn module(&self, name: &str) -> Result { + self.instances.get(name).cloned().ok_or_else(|| { + InterpreterError::Instantiation(format!("Module not registered {}", name)) + }) + } - fn module_or_last(&self, name: Option<&str>) -> Result { - match name { - Some(name) => self.module(name), - None => self.last_module - .clone() - .ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())), - } - } + fn module_or_last(&self, name: Option<&str>) -> Result { + match name { + Some(name) => self.module(name), + None => self + .last_module + .clone() + .ok_or_else(|| InterpreterError::Instantiation("No modules registered".into())), + } + } } impl ImportResolver for SpecDriver { - fn resolve_func( - &self, - module_name: &str, - field_name: &str, - func_type: &Signature, - ) -> Result { - if module_name == "spectest" { - self.spec_module.resolve_func(field_name, func_type) - } else { - self.module(module_name)? - .resolve_func(field_name, func_type) - } - } + fn resolve_func( + &self, + module_name: &str, + field_name: &str, + func_type: &Signature, + ) -> Result { + if module_name == "spectest" { + self.spec_module.resolve_func(field_name, func_type) + } else { + self.module(module_name)? + .resolve_func(field_name, func_type) + } + } - fn resolve_global( - &self, - module_name: &str, - field_name: &str, - global_type: &GlobalDescriptor, - ) -> Result { - if module_name == "spectest" { - self.spec_module.resolve_global(field_name, global_type) - } else { - self.module(module_name)? - .resolve_global(field_name, global_type) - } - } + fn resolve_global( + &self, + module_name: &str, + field_name: &str, + global_type: &GlobalDescriptor, + ) -> Result { + if module_name == "spectest" { + self.spec_module.resolve_global(field_name, global_type) + } else { + self.module(module_name)? + .resolve_global(field_name, global_type) + } + } - fn resolve_memory( - &self, - module_name: &str, - field_name: &str, - memory_type: &MemoryDescriptor, - ) -> Result { - if module_name == "spectest" { - self.spec_module.resolve_memory(field_name, memory_type) - } else { - self.module(module_name)? - .resolve_memory(field_name, memory_type) - } - } + fn resolve_memory( + &self, + module_name: &str, + field_name: &str, + memory_type: &MemoryDescriptor, + ) -> Result { + if module_name == "spectest" { + self.spec_module.resolve_memory(field_name, memory_type) + } else { + self.module(module_name)? + .resolve_memory(field_name, memory_type) + } + } - fn resolve_table( - &self, - module_name: &str, - field_name: &str, - table_type: &TableDescriptor, - ) -> Result { - if module_name == "spectest" { - self.spec_module.resolve_table(field_name, table_type) - } else { - self.module(module_name)? - .resolve_table(field_name, table_type) - } - } + fn resolve_table( + &self, + module_name: &str, + field_name: &str, + table_type: &TableDescriptor, + ) -> Result { + if module_name == "spectest" { + self.spec_module.resolve_table(field_name, table_type) + } else { + self.module(module_name)? + .resolve_table(field_name, table_type) + } + } } fn try_load_module(wasm: &[u8]) -> Result { - Module::from_buffer(wasm).map_err(|e| Error::Load(e.to_string())) + Module::from_buffer(wasm).map_err(|e| Error::Load(e.to_string())) } fn try_load(wasm: &[u8], spec_driver: &mut SpecDriver) -> Result<(), Error> { - let module = try_load_module(wasm)?; - let instance = ModuleInstance::new(&module, &ImportsBuilder::default())?; - instance - .run_start(spec_driver.spec_module()) - .map_err(|trap| Error::Start(trap))?; - Ok(()) + let module = try_load_module(wasm)?; + let instance = ModuleInstance::new(&module, &ImportsBuilder::default())?; + instance + .run_start(spec_driver.spec_module()) + .map_err(|trap| Error::Start(trap))?; + Ok(()) } fn load_module( - wasm: &[u8], - name: &Option, - spec_driver: &mut SpecDriver, + wasm: &[u8], + name: &Option, + spec_driver: &mut SpecDriver, ) -> Result { - let module = try_load_module(wasm)?; - let instance = ModuleInstance::new(&module, spec_driver) - .map_err(|e| Error::Load(e.to_string()))? - .run_start(spec_driver.spec_module()) - .map_err(|trap| Error::Start(trap))?; + let module = try_load_module(wasm)?; + let instance = ModuleInstance::new(&module, spec_driver) + .map_err(|e| Error::Load(e.to_string()))? + .run_start(spec_driver.spec_module()) + .map_err(|trap| Error::Start(trap))?; - let module_name = name.clone(); - spec_driver.add_module(module_name, instance.clone()); + let module_name = name.clone(); + spec_driver.add_module(module_name, instance.clone()); - Ok(instance) + Ok(instance) } fn run_action( - program: &mut SpecDriver, - action: &Action, + program: &mut SpecDriver, + action: &Action, ) -> Result, InterpreterError> { - match *action { - Action::Invoke { - ref module, - ref field, - ref args, - } => { - let module = program - .module_or_last(module.as_ref().map(|x| x.as_ref())) - .expect(&format!( - "Expected program to have loaded module {:?}", - module - )); - let vec_args = args.iter() - .cloned() - .map(spec_to_runtime_value) - .collect::>(); - module.invoke_export(field, &vec_args, program.spec_module()) - } - Action::Get { - ref module, - ref field, - .. - } => { - let module = program - .module_or_last(module.as_ref().map(|x| x.as_ref())) - .expect(&format!( - "Expected program to have loaded module {:?}", - module - )); - let global = module - .export_by_name(&field) - .ok_or_else(|| { - InterpreterError::Global(format!("Expected to have export with name {}", field)) - })? - .as_global() - .cloned() - .ok_or_else(|| { - InterpreterError::Global(format!("Expected export {} to be a global", field)) - })?; - Ok(Some(global.get())) - } - } + match *action { + Action::Invoke { + ref module, + ref field, + ref args, + } => { + let module = program + .module_or_last(module.as_ref().map(|x| x.as_ref())) + .expect(&format!( + "Expected program to have loaded module {:?}", + module + )); + let vec_args = args + .iter() + .cloned() + .map(spec_to_runtime_value) + .collect::>(); + module.invoke_export(field, &vec_args, program.spec_module()) + } + Action::Get { + ref module, + ref field, + .. + } => { + let module = program + .module_or_last(module.as_ref().map(|x| x.as_ref())) + .expect(&format!( + "Expected program to have loaded module {:?}", + module + )); + let global = module + .export_by_name(&field) + .ok_or_else(|| { + InterpreterError::Global(format!("Expected to have export with name {}", field)) + })? + .as_global() + .cloned() + .ok_or_else(|| { + InterpreterError::Global(format!("Expected export {} to be a global", field)) + })?; + Ok(Some(global.get())) + } + } } pub fn spec(name: &str) { - println!("running test: {}", name); - try_spec(name).expect("Failed to run spec"); + println!("running test: {}", name); + try_spec(name).expect("Failed to run spec"); } fn try_spec(name: &str) -> Result<(), Error> { - let mut spec_driver = SpecDriver::new(); - let spec_script_path = format!("tests/spec/testsuite/{}.wast", name); + let mut spec_driver = SpecDriver::new(); + let spec_script_path = format!("tests/spec/testsuite/{}.wast", name); - use std::io::Read; - let mut spec_source = Vec::new(); - let mut spec_file = File::open(&spec_script_path).expect("Can't open file"); - spec_file.read_to_end(&mut spec_source).expect("Can't read file"); + use std::io::Read; + let mut spec_source = Vec::new(); + let mut spec_file = File::open(&spec_script_path).expect("Can't open file"); + spec_file + .read_to_end(&mut spec_source) + .expect("Can't read file"); - let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name)).expect("Can't read spec script"); - let mut errors = vec![]; + let mut parser = ScriptParser::from_source_and_name(&spec_source, &format!("{}.wast", name)) + .expect("Can't read spec script"); + let mut errors = vec![]; - while let Some(Command { kind, line }) = parser.next()? { - macro_rules! assert_eq { - ($a:expr, $b:expr) => {{ - let (a, b) = ($a, $b); + while let Some(Command { kind, line }) = parser.next()? { + macro_rules! assert_eq { + ($a:expr, $b:expr) => {{ + let (a, b) = ($a, $b); - if a != b { - errors.push(format!( - r#"ERROR (line {}): + if a != b { + errors.push(format!( + r#"ERROR (line {}): expected: {:?} got: {:?} "#, - line, b, a, - )); - } - }}; - } + line, b, a, + )); + } + }}; + } - println!("Running spec cmd {}: {:?}", line, kind); + println!("Running spec cmd {}: {:?}", line, kind); - match kind { - CommandKind::Module { name, module, .. } => { - load_module(&module.into_vec(), &name, &mut spec_driver) - .expect("Failed to load module"); - } - CommandKind::AssertReturn { action, expected } => { - let result = run_action(&mut spec_driver, &action); - match result { - Ok(result) => { - let spec_expected = expected - .iter() - .cloned() - .map(spec_to_runtime_value) - .collect::>(); - let actual_result = result.into_iter().collect::>(); - for (actual_result, spec_expected) in - actual_result.iter().zip(spec_expected.iter()) - { - assert_eq!(actual_result.value_type(), spec_expected.value_type()); - // f32::NAN != f32::NAN - match spec_expected { - &RuntimeValue::F32(val) if val.is_nan() => match actual_result { - &RuntimeValue::F32(val) => assert!(val.is_nan()), - _ => unreachable!(), // checked above that types are same - }, - &RuntimeValue::F64(val) if val.is_nan() => match actual_result { - &RuntimeValue::F64(val) => assert!(val.is_nan()), - _ => unreachable!(), // checked above that types are same - }, - spec_expected @ _ => assert_eq!(actual_result, spec_expected), - } - } - } - Err(e) => { - panic!("Expected action to return value, got error: {:?}", e); - } - } - } - CommandKind::AssertReturnCanonicalNan { action } - | CommandKind::AssertReturnArithmeticNan { action } => { - let result = run_action(&mut spec_driver, &action); - match result { - Ok(result) => { - for actual_result in result.into_iter().collect::>() { - match actual_result { - RuntimeValue::F32(val) => if !val.is_nan() { - panic!("Expected nan value, got {:?}", val) - }, - RuntimeValue::F64(val) => if !val.is_nan() { - panic!("Expected nan value, got {:?}", val) - }, - val @ _ => { - panic!("Expected action to return float value, got {:?}", val) - } - } - } - } - Err(e) => { - panic!("Expected action to return value, got error: {:?}", e); - } - } - } - CommandKind::AssertExhaustion { action, .. } => { - let result = run_action(&mut spec_driver, &action); - match result { - Ok(result) => panic!("Expected exhaustion, got result: {:?}", result), - Err(_e) => {}, - } - } - CommandKind::AssertTrap { action, .. } => { - let result = run_action(&mut spec_driver, &action); - match result { - Ok(result) => { - panic!( - "Expected action to result in a trap, got result: {:?}", - result - ); - } - Err(_e) => {} - } - } - CommandKind::AssertInvalid { module, .. } - | CommandKind::AssertMalformed { module, .. } - | CommandKind::AssertUnlinkable { module, .. } => { - let module_load = try_load(&module.into_vec(), &mut spec_driver); - match module_load { - Ok(_) => panic!("Expected invalid module definition, got some module!"), - Err(_e) => {}, - } - } - CommandKind::AssertUninstantiable { module, .. } => { - match try_load(&module.into_vec(), &mut spec_driver) { - Ok(_) => panic!("Expected error running start function at line {}", line), - Err(_e) => {}, - } - } - CommandKind::Register { name, as_name, .. } => { - let module = match spec_driver.module_or_last(name.as_ref().map(|x| x.as_ref())) { - Ok(module) => module, - Err(e) => panic!("No such module, at line {} - ({:?})", e, line), - }; - spec_driver.add_module(Some(as_name.clone()), module); - } - CommandKind::PerformAction(action) => match run_action(&mut spec_driver, &action) { - Ok(_) => {} - Err(e) => panic!("Failed to invoke action at line {}: {:?}", line, e), - }, - } - } + match kind { + CommandKind::Module { name, module, .. } => { + load_module(&module.into_vec(), &name, &mut spec_driver) + .expect("Failed to load module"); + } + CommandKind::AssertReturn { action, expected } => { + let result = run_action(&mut spec_driver, &action); + match result { + Ok(result) => { + let spec_expected = expected + .iter() + .cloned() + .map(spec_to_runtime_value) + .collect::>(); + let actual_result = result.into_iter().collect::>(); + for (actual_result, spec_expected) in + actual_result.iter().zip(spec_expected.iter()) + { + assert_eq!(actual_result.value_type(), spec_expected.value_type()); + // f32::NAN != f32::NAN + match spec_expected { + &RuntimeValue::F32(val) if val.is_nan() => match actual_result { + &RuntimeValue::F32(val) => assert!(val.is_nan()), + _ => unreachable!(), // checked above that types are same + }, + &RuntimeValue::F64(val) if val.is_nan() => match actual_result { + &RuntimeValue::F64(val) => assert!(val.is_nan()), + _ => unreachable!(), // checked above that types are same + }, + spec_expected @ _ => assert_eq!(actual_result, spec_expected), + } + } + } + Err(e) => { + panic!("Expected action to return value, got error: {:?}", e); + } + } + } + CommandKind::AssertReturnCanonicalNan { action } + | CommandKind::AssertReturnArithmeticNan { action } => { + let result = run_action(&mut spec_driver, &action); + match result { + Ok(result) => { + for actual_result in result.into_iter().collect::>() { + match actual_result { + RuntimeValue::F32(val) => { + if !val.is_nan() { + panic!("Expected nan value, got {:?}", val) + } + } + RuntimeValue::F64(val) => { + if !val.is_nan() { + panic!("Expected nan value, got {:?}", val) + } + } + val @ _ => { + panic!("Expected action to return float value, got {:?}", val) + } + } + } + } + Err(e) => { + panic!("Expected action to return value, got error: {:?}", e); + } + } + } + CommandKind::AssertExhaustion { action, .. } => { + let result = run_action(&mut spec_driver, &action); + match result { + Ok(result) => panic!("Expected exhaustion, got result: {:?}", result), + Err(_e) => {} + } + } + CommandKind::AssertTrap { action, .. } => { + let result = run_action(&mut spec_driver, &action); + match result { + Ok(result) => { + panic!( + "Expected action to result in a trap, got result: {:?}", + result + ); + } + Err(_e) => {} + } + } + CommandKind::AssertInvalid { module, .. } + | CommandKind::AssertMalformed { module, .. } + | CommandKind::AssertUnlinkable { module, .. } => { + let module_load = try_load(&module.into_vec(), &mut spec_driver); + match module_load { + Ok(_) => panic!("Expected invalid module definition, got some module!"), + Err(_e) => {} + } + } + CommandKind::AssertUninstantiable { module, .. } => { + match try_load(&module.into_vec(), &mut spec_driver) { + Ok(_) => panic!("Expected error running start function at line {}", line), + Err(_e) => {} + } + } + CommandKind::Register { name, as_name, .. } => { + let module = match spec_driver.module_or_last(name.as_ref().map(|x| x.as_ref())) { + Ok(module) => module, + Err(e) => panic!("No such module, at line {} - ({:?})", e, line), + }; + spec_driver.add_module(Some(as_name.clone()), module); + } + CommandKind::PerformAction(action) => match run_action(&mut spec_driver, &action) { + Ok(_) => {} + Err(e) => panic!("Failed to invoke action at line {}: {:?}", line, e), + }, + } + } - if !errors.is_empty() { - use std::fmt::Write; - let mut out = "\n".to_owned(); - for err in errors { - write!(out, "{}", err).expect("Error formatting errors"); - } - panic!(out); - } + if !errors.is_empty() { + use std::fmt::Write; + let mut out = "\n".to_owned(); + for err in errors { + write!(out, "{}", err).expect("Error formatting errors"); + } + panic!(out); + } - Ok(()) + Ok(()) } diff --git a/tests/spec_shim.rs b/tests/spec_shim.rs index 094d833..e254b66 100644 --- a/tests/spec_shim.rs +++ b/tests/spec_shim.rs @@ -1,6 +1,6 @@ //! Official spec testsuite. -extern crate wasmi; extern crate wabt; +extern crate wasmi; mod spec;