From 4e28d0c61ecd0331b45880f5769e2d2a34a0fabf Mon Sep 17 00:00:00 2001 From: NikVolf Date: Wed, 21 Feb 2018 13:37:13 +0300 Subject: [PATCH] drop interpreter --- .travis.yml | 2 - examples/interpret.rs | 37 - examples/invoke.rs | 85 - pwasm-emscripten/.gitignore | 1 - pwasm-emscripten/Cargo.toml | 7 - pwasm-emscripten/src/lib.rs | 263 --- src/common/mod.rs | 40 - src/common/stack.rs | 113 -- src/interpreter/imports.rs | 166 -- src/interpreter/memory.rs | 280 --- src/interpreter/mod.rs | 181 -- src/interpreter/module.rs | 746 ------- src/interpreter/native.rs | 288 --- src/interpreter/program.rs | 64 - src/interpreter/runner.rs | 1131 ----------- src/interpreter/stack.rs | 35 - src/interpreter/table.rs | 94 - src/interpreter/tests/basics.rs | 608 ------ src/interpreter/tests/mod.rs | 29 - src/interpreter/tests/wabt.rs | 3213 ------------------------------- src/interpreter/tests/wasm.rs | 77 - src/interpreter/validator.rs | 809 -------- src/interpreter/value.rs | 664 ------- src/interpreter/variable.rs | 163 -- src/lib.rs | 13 - src/validation/context.rs | 82 - src/validation/func.rs | 774 -------- src/validation/mod.rs | 326 ---- src/validation/module.rs | 10 - src/validation/tests.rs | 301 --- 30 files changed, 10602 deletions(-) delete mode 100644 examples/interpret.rs delete mode 100644 examples/invoke.rs delete mode 100644 pwasm-emscripten/.gitignore delete mode 100644 pwasm-emscripten/Cargo.toml delete mode 100644 pwasm-emscripten/src/lib.rs delete mode 100644 src/common/mod.rs delete mode 100644 src/common/stack.rs delete mode 100644 src/interpreter/imports.rs delete mode 100644 src/interpreter/memory.rs delete mode 100644 src/interpreter/mod.rs delete mode 100644 src/interpreter/module.rs delete mode 100644 src/interpreter/native.rs delete mode 100644 src/interpreter/program.rs delete mode 100644 src/interpreter/runner.rs delete mode 100644 src/interpreter/stack.rs delete mode 100644 src/interpreter/table.rs delete mode 100644 src/interpreter/tests/basics.rs delete mode 100644 src/interpreter/tests/mod.rs delete mode 100644 src/interpreter/tests/wabt.rs delete mode 100644 src/interpreter/tests/wasm.rs delete mode 100644 src/interpreter/validator.rs delete mode 100644 src/interpreter/value.rs delete mode 100644 src/interpreter/variable.rs delete mode 100644 src/validation/context.rs delete mode 100644 src/validation/func.rs delete mode 100644 src/validation/mod.rs delete mode 100644 src/validation/module.rs delete mode 100644 src/validation/tests.rs diff --git a/.travis.yml b/.travis.yml index d56268a..3055cf4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ dist: trusty sudo: required language: - rust -- cpp addons: apt: sources: @@ -17,7 +16,6 @@ script: - cargo build --release --verbose - cargo test --release --verbose - cargo test --release --manifest-path=spec/Cargo.toml -- cargo test --manifest-path=pwasm-emscripten/Cargo.toml - cargo run --example bench-decoder --release after_success: |- [ $TRAVIS_BRANCH = master ] && diff --git a/examples/interpret.rs b/examples/interpret.rs deleted file mode 100644 index 5577755..0000000 --- a/examples/interpret.rs +++ /dev/null @@ -1,37 +0,0 @@ -// In this example we execute a contract funciton exported as "_call" -#![allow(deprecated)] - -extern crate parity_wasm; - -use std::env::args; - -use parity_wasm::ModuleInstanceInterface; - -fn main() { - let args: Vec<_> = args().collect(); - if args.len() != 3 { - println!("Usage: {} ", args[0]); - println!(" wasm file should contain exported `_call` function with single I32 argument"); - return; - } - - // Intrepreter initialization. - let program = parity_wasm::ProgramInstance::new(); - - // Here we load module using dedicated for this purpose - // `deserialize_file` function (which works only with modules) - let module = parity_wasm::deserialize_file(&args[1]).expect("Failed to load module"); - - // 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 - let module = program.add_module("main", module, None).expect("Failed to initialize module"); - - // The argument should be parsable as a valid integer - 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: {:?}", module.execute_export("_call", vec![parity_wasm::RuntimeValue::I32(argument)].into())); -} diff --git a/examples/invoke.rs b/examples/invoke.rs deleted file mode 100644 index 611108f..0000000 --- a/examples/invoke.rs +++ /dev/null @@ -1,85 +0,0 @@ -#![allow(deprecated)] - -extern crate parity_wasm; - -use std::env::args; - -use parity_wasm::{interpreter, ModuleInstanceInterface, RuntimeValue}; -use parity_wasm::elements::{Internal, External, Type, FunctionType, ValueType}; - -fn main() { - let args: Vec<_> = args().collect(); - if args.len() < 3 { - println!("Usage: {} [...]", args[0]); - return; - } - let func_name = &args[2]; - let (_, program_args) = args.split_at(3); - - // Intrepreter initialization. - let program = parity_wasm::ProgramInstance::new(); - - let module = parity_wasm::deserialize_file(&args[1]).expect("File to be deserialized"); - - // Extracts call arguments from command-line arguments - let execution_params = { - // 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"); - // 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)); - - // Function index in the function index space (internally-defined + imported) - let function_index: usize = match found_entry.internal() { - &Internal::Function(index) => index as usize, - _ => panic!("Founded export is not a function"), - }; - - // 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() { - &External::Function(_) => true, - _ => false, - }).count(), - None => 0, - }; - - // Calculates a function index within module's function section - 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; - - // Use the reference to get an actual function type - let function_type: &FunctionType = match &type_section.types()[func_type_ref] { - &Type::Function(ref func_type) => func_type, - }; - - // Parses arguments and constructs runtime values in correspondence of their types - let args: Vec = 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]))), - &ValueType::F64 => RuntimeValue::F64(program_args[i].parse::().expect(&format!("Can't parse arg #{} as f64", program_args[i]))), - }).collect(); - - interpreter::ExecutionParams::from(args) - }; - - // 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 - let module = program.add_module("main", module, None).expect("Failed to initialize module"); - - println!("Result: {:?}", module.execute_export(func_name, execution_params).expect("")); -} diff --git a/pwasm-emscripten/.gitignore b/pwasm-emscripten/.gitignore deleted file mode 100644 index eb5a316..0000000 --- a/pwasm-emscripten/.gitignore +++ /dev/null @@ -1 +0,0 @@ -target diff --git a/pwasm-emscripten/Cargo.toml b/pwasm-emscripten/Cargo.toml deleted file mode 100644 index 541a82a..0000000 --- a/pwasm-emscripten/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "pwasm-emscripten" -version = "0.1.0" -authors = ["Sergey Pepyakin "] - -[dependencies] -parity-wasm = "0.18" diff --git a/pwasm-emscripten/src/lib.rs b/pwasm-emscripten/src/lib.rs deleted file mode 100644 index 84dd195..0000000 --- a/pwasm-emscripten/src/lib.rs +++ /dev/null @@ -1,263 +0,0 @@ -//! This crate provides some of the simplest exports -//! from the Emscripten runtime, such as `STACKTOP` or `abort`. - -extern crate parity_wasm; - -use std::sync::{Arc, Weak}; -use parity_wasm::builder::module; -use parity_wasm::elements::{ExportEntry, Internal, ValueType}; -use parity_wasm::interpreter::Error; -use parity_wasm::interpreter::{native_module, UserDefinedElements, UserFunctionDescriptor, UserFunctionExecutor}; -use parity_wasm::interpreter::{CallerContext, ModuleInstance, ModuleInstanceInterface}; -use parity_wasm::interpreter::RuntimeValue; -use parity_wasm::interpreter::ProgramInstance; -use parity_wasm::interpreter::{VariableInstance, VariableType}; - -/// Memory address, at which stack begins. -const DEFAULT_STACK_TOP: u32 = 256 * 1024; -/// Memory, allocated for stack. -const DEFAULT_TOTAL_STACK: u32 = 5 * 1024 * 1024; -/// Total memory, allocated by default. -const DEFAULT_TOTAL_MEMORY: u32 = 16 * 1024 * 1024; -/// Whether memory can be enlarged, or not. -const DEFAULT_ALLOW_MEMORY_GROWTH: bool = true; -/// Default tableBase variable value. -const DEFAULT_TABLE_BASE: u32 = 0; -/// Default tableBase variable value. -const DEFAULT_MEMORY_BASE: u32 = 0; - -/// Default table size. -const DEFAULT_TABLE_SIZE: u32 = 64; - -/// Index of default memory. -const INDEX_MEMORY: u32 = 0; - -/// Index of default table. -const INDEX_TABLE: u32 = 0; - -const LINEAR_MEMORY_PAGE_SIZE: u32 = 64 * 1024; - -/// Emscripten environment parameters. -pub struct EmscriptenParams { - /// Stack size in bytes. - pub total_stack: u32, - /// Total memory size in bytes. - pub total_memory: u32, - /// Allow memory growth. - pub allow_memory_growth: bool, - /// Table size. - pub table_size: u32, - /// Static reserve, if any - pub static_size: Option, -} - -struct EmscriptenFunctionExecutor { - total_mem_global: Arc, -} - -impl<'a> UserFunctionExecutor for EmscriptenFunctionExecutor { - fn execute( - &mut self, - name: &str, - context: CallerContext, - ) -> Result, Error> { - match name { - "_abort" | "abort" => { - Err(Error::Trap("abort".into()).into()) - }, - "assert" => { - let condition = context.value_stack.pop_as::()?; - if condition == 0 { - Err(Error::Trap("assertion failed".into())) - } else { - Ok(None) - } - }, - "enlargeMemory" => { - // TODO: support memory enlarge - Ok(Some(RuntimeValue::I32(0))) - }, - "getTotalMemory" => { - let total_memory = self.total_mem_global.get(); - Ok(Some(total_memory)) - }, - _ => Err(Error::Trap("not implemented".into()).into()), - } - } -} - -pub fn env_module(params: EmscriptenParams) -> Result, Error> { - debug_assert!(params.total_stack < params.total_memory); - debug_assert!((params.total_stack % LINEAR_MEMORY_PAGE_SIZE) == 0); - debug_assert!((params.total_memory % LINEAR_MEMORY_PAGE_SIZE) == 0); - - let stack_top = params.static_size.unwrap_or(DEFAULT_STACK_TOP); - - // Build module with defined memory and table, - // instantiate it and wrap into an Arc. - let instance = { - let builder = module() - // memory regions - .memory() - .with_min(params.total_memory / LINEAR_MEMORY_PAGE_SIZE) - .with_max(params.max_memory().map(|m| m / LINEAR_MEMORY_PAGE_SIZE)) - .build() - .with_export(ExportEntry::new("memory".into(), Internal::Memory(INDEX_MEMORY))) - // tables - .table() - .with_min(params.table_size) - .build() - .with_export(ExportEntry::new("table".into(), Internal::Table(INDEX_TABLE))); - let mut instance = ModuleInstance::new(Weak::default(), "env".into(), builder.build())?; - instance.instantiate(None)?; - Arc::new(instance) - }; - let total_mem_global = Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32(params.total_memory as i32), - ).unwrap(), - ); - - let function_executor = EmscriptenFunctionExecutor { - total_mem_global: Arc::clone(&total_mem_global), - }; - - const SIGNATURES: &'static [UserFunctionDescriptor] = &[ - UserFunctionDescriptor::Static("_abort", &[], None), - UserFunctionDescriptor::Static("abort", &[], None), - UserFunctionDescriptor::Static("assert", &[ValueType::I32], None), - UserFunctionDescriptor::Static("enlargeMemory", &[], Some(ValueType::I32)), - UserFunctionDescriptor::Static("getTotalMemory", &[], Some(ValueType::I32)), - ]; - - let elements = UserDefinedElements { - executor: Some(function_executor), - globals: vec![ - ( - "STACK_BASE".into(), - Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32(stack_top as i32), - ).unwrap(), - ), - ), - ( - "STACKTOP".into(), - Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32(stack_top as i32), - ).unwrap(), - ), - ), - ( - "STACK_MAX".into(), - Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32((stack_top + params.total_stack) as i32), - ).unwrap(), - ), - ), - ( - "DYNAMIC_BASE".into(), - Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32((stack_top + params.total_stack) as i32), - ).unwrap(), - ), - ), - ( - "DYNAMICTOP_PTR".into(), - Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32((stack_top + params.total_stack) as i32), - ).unwrap(), - ), - ), - ( - "EXITSTATUS".into(), - Arc::new( - VariableInstance::new(false, VariableType::I32, RuntimeValue::I32(0)).unwrap(), - ), - ), - ( - "tableBase".into(), - Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32(DEFAULT_TABLE_BASE as i32), - ).unwrap(), - ), - ), - ( - "memoryBase".into(), - Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32(DEFAULT_MEMORY_BASE as i32), - ).unwrap(), - ), - ), - ("TOTAL_MEMORY".into(), total_mem_global), - ].into_iter() - .collect(), - functions: ::std::borrow::Cow::from(SIGNATURES), - }; - - Ok(native_module(instance, elements)?) -} - -impl Default for EmscriptenParams { - fn default() -> Self { - EmscriptenParams { - total_stack: DEFAULT_TOTAL_STACK, - total_memory: DEFAULT_TOTAL_MEMORY, - allow_memory_growth: DEFAULT_ALLOW_MEMORY_GROWTH, - table_size: DEFAULT_TABLE_SIZE, - static_size: None, - } - } -} - -impl EmscriptenParams { - fn max_memory(&self) -> Option { - if self.allow_memory_growth { None } else { Some(self.total_memory) } - } -} - -pub fn program_with_emscripten_env(params: EmscriptenParams) -> Result { - let program = ProgramInstance::new(); - program.insert_loaded_module("env", env_module(params)?)?; - Ok(program) -} - -#[cfg(test)] -mod tests { - use super::program_with_emscripten_env; - use parity_wasm::builder::module; - use parity_wasm::elements::{ImportEntry, External, GlobalType, ValueType}; - - #[test] - fn import_env_mutable_global() { - let program = program_with_emscripten_env(Default::default()).unwrap(); - - let module = module() - .with_import(ImportEntry::new("env".into(), "STACKTOP".into(), External::Global(GlobalType::new(ValueType::I32, false)))) - .build(); - - program.add_module("main", module, None).unwrap(); - } -} diff --git a/src/common/mod.rs b/src/common/mod.rs deleted file mode 100644 index 50f72ae..0000000 --- a/src/common/mod.rs +++ /dev/null @@ -1,40 +0,0 @@ -use elements::BlockType; - -pub mod stack; - -/// Index of default linear memory. -pub const DEFAULT_MEMORY_INDEX: u32 = 0; -/// Index of default table. -pub const DEFAULT_TABLE_INDEX: u32 = 0; - -/// Control stack frame. -#[derive(Debug, Clone)] -pub struct BlockFrame { - /// Frame type. - pub frame_type: BlockFrameType, - /// A signature, which is a block signature type indicating the number and types of result values of the region. - pub block_type: BlockType, - /// A label for reference to block instruction. - pub begin_position: usize, - /// A label for reference from branch instructions. - pub branch_position: usize, - /// A label for reference from end instructions. - pub end_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. - pub value_stack_len: usize, -} - -/// Type of block frame. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum BlockFrameType { - /// Function frame. - Function, - /// Usual block frame. - Block, - /// Loop frame (branching to the beginning of block). - Loop, - /// True-subblock of if expression. - IfTrue, - /// False-subblock of if expression. - IfFalse, -} diff --git a/src/common/stack.rs b/src/common/stack.rs deleted file mode 100644 index 9866363..0000000 --- a/src/common/stack.rs +++ /dev/null @@ -1,113 +0,0 @@ - -use std::collections::VecDeque; -use std::error; -use std::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) - } -} - -impl error::Error for Error { - fn description(&self) -> &str { - &self.0 - } -} - -/// Stack with limit. -#[derive(Debug)] -pub struct StackWithLimit where T: Clone { - /// Stack values. - values: VecDeque, - /// Stack limit (maximal stack len). - limit: usize, -} - -impl StackWithLimit where T: Clone { - pub fn with_data(data: Vec, limit: usize) -> Self { - StackWithLimit { - values: data.into_iter().collect(), - limit: limit - } - } - - pub fn with_limit(limit: usize) -> Self { - StackWithLimit { - values: VecDeque::new(), - limit: limit - } - } - - pub fn is_empty(&self) -> bool { - self.values.is_empty() - } - - pub fn len(&self) -> usize { - self.values.len() - } - - pub fn limit(&self) -> usize { - self.limit - } - - pub fn values(&self) -> &VecDeque { - &self.values - } - - pub fn top(&self) -> Result<&T, Error> { - self.values - .back() - .ok_or(Error("non-empty stack expected".into())) - } - - pub fn top_mut(&mut self) -> Result<&mut T, Error> { - self.values - .back_mut() - .ok_or(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()))); - } - - 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))); - } - - self.values.push_back(value); - Ok(()) - } - - pub fn push_penultimate(&mut self, value: T) -> Result<(), Error> { - if self.values.is_empty() { - return Err(Error("trying to insert penultimate element into empty stack".into())); - } - self.push(value)?; - - let last_index = self.values.len() - 1; - let penultimate_index = last_index - 1; - self.values.swap(last_index, penultimate_index); - - Ok(()) - } - - pub fn pop(&mut self) -> Result { - self.values - .pop_back() - .ok_or(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); - } -} diff --git a/src/interpreter/imports.rs b/src/interpreter/imports.rs deleted file mode 100644 index 7243e27..0000000 --- a/src/interpreter/imports.rs +++ /dev/null @@ -1,166 +0,0 @@ -use std::sync::{Arc, Weak}; -use std::collections::HashMap; -use elements::{ImportSection, ImportEntry, External, Internal}; -use interpreter::Error; -use interpreter::memory::MemoryInstance; -use interpreter::module::{ModuleInstanceInterface, ItemIndex, ExportEntryType, FunctionSignature}; -use interpreter::program::ProgramInstanceEssence; -use interpreter::table::TableInstance; -use interpreter::variable::{VariableInstance, VariableType}; - -/// Module imports. -pub struct ModuleImports { - /// Program instance. - program: Weak, - /// External functions. - functions: Vec, - /// External tables. - tables: Vec, - /// External memory regions. - memory: Vec, - /// External globals. - globals: Vec, -} - -impl ModuleImports { - /// Create new imports for given import section. - pub fn new(program: Weak, import_section: Option<&ImportSection>) -> Self { - let mut functions = Vec::new(); - let mut tables = Vec::new(); - let mut memory = Vec::new(); - let mut globals = Vec::new(); - if let Some(import_section) = import_section { - for (import_index, import_entry) in import_section.entries().iter().enumerate() { - match import_entry.external() { - &External::Function(_) => functions.push(import_index), - &External::Table(_) => tables.push(import_index), - &External::Memory(_) => memory.push(import_index), - &External::Global(_) => globals.push(import_index), - } - } - } - - ModuleImports { - program: program, - functions: functions, - tables: tables, - memory: memory, - globals: globals, - } - } - - /// Number of imported tables. - pub fn tables_len(&self) -> usize { - self.tables.len() - } - - /// Number of imported memory regions. - pub fn memory_regions_len(&self) -> usize { - self.memory.len() - } - - /// Parse function index. - pub fn parse_function_index(&self, index: ItemIndex) -> ItemIndex { - match index { - ItemIndex::IndexSpace(index) => match index.checked_sub(self.functions.len() as u32) { - Some(index) => ItemIndex::Internal(index), - None => ItemIndex::External(self.functions[index as usize] as u32), - }, - index @ _ => index, - } - } - - /// Parse table index. - pub fn parse_table_index(&self, index: ItemIndex) -> ItemIndex { - match index { - ItemIndex::IndexSpace(index) => match index.checked_sub(self.tables.len() as u32) { - Some(index) => ItemIndex::Internal(index), - None => ItemIndex::External(self.tables[index as usize] as u32), - }, - index @ _ => index, - } - } - - /// Parse memory index. - pub fn parse_memory_index(&self, index: ItemIndex) -> ItemIndex { - match index { - ItemIndex::IndexSpace(index) => match index.checked_sub(self.memory.len() as u32) { - Some(index) => ItemIndex::Internal(index), - None => ItemIndex::External(self.memory[index as usize] as u32), - }, - index @ _ => index, - } - } - - /// Parse global index. - pub fn parse_global_index(&self, index: ItemIndex) -> ItemIndex { - match index { - ItemIndex::IndexSpace(index) => match index.checked_sub(self.globals.len() as u32) { - Some(index) => ItemIndex::Internal(index), - None => ItemIndex::External(self.globals[index as usize] as u32), - }, - index @ _ => index, - } - } - - /// Get module reference. - pub fn module<'a>(&self, externals: Option<&'a HashMap>>, name: &str) -> Result, Error> { - if let Some(externals) = externals { - if let Some(module) = externals.get(name).cloned() { - return Ok(module); - } - } - - self.program - .upgrade() - .ok_or(Error::Program("program unloaded".into())) - .and_then(|p| p.module(name).ok_or(Error::Program(format!("module {} is not loaded", name)))) - } - - /// Get function index. - pub fn function<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry, required_type: Option) -> Result { - let (_, export) = self.external_export(externals, import, &required_type.map(|ft| ExportEntryType::Function(ft)).unwrap_or(ExportEntryType::Any))?; - if let Internal::Function(external_index) = export { - return Ok(external_index); - } - - Err(Error::Program(format!("wrong import {} from module {} (expecting function)", import.field(), import.module()))) - } - - /// Get table reference. - pub fn table<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry) -> Result, Error> { - let (module, export) = self.external_export(externals, import, &ExportEntryType::Any)?; - if let Internal::Table(external_index) = export { - return module.table(ItemIndex::Internal(external_index)); - } - - Err(Error::Program(format!("wrong import {} from module {} (expecting table)", import.field(), import.module()))) - } - - /// Get memory reference. - pub fn memory<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry) -> Result, Error> { - let (module, export) = self.external_export(externals, import, &ExportEntryType::Any)?; - if let Internal::Memory(external_index) = export { - return module.memory(ItemIndex::Internal(external_index)); - } - - Err(Error::Program(format!("wrong import {} from module {} (expecting memory)", import.field(), import.module()))) - } - - /// Get global reference. - pub fn global<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry, required_type: Option) -> Result, Error> { - let (module, export) = self.external_export(externals, import, &required_type.clone().map(|rt| ExportEntryType::Global(rt)).unwrap_or(ExportEntryType::Any))?; - if let Internal::Global(external_index) = export { - return module.global(ItemIndex::Internal(external_index), required_type, externals); - } - - Err(Error::Program(format!("wrong import {} from module {} (expecting global)", import.field(), import.module()))) - } - - fn external_export<'a>(&self, externals: Option<&'a HashMap>>, import: &ImportEntry, required_type: &ExportEntryType) -> Result<(Arc, Internal), Error> { - self.module(externals, import.module()) - .and_then(|m| - m.export_entry(import.field(), required_type) - .map(|e| (m, e))) - } -} diff --git a/src/interpreter/memory.rs b/src/interpreter/memory.rs deleted file mode 100644 index 8fcb38f..0000000 --- a/src/interpreter/memory.rs +++ /dev/null @@ -1,280 +0,0 @@ -use std::u32; -use std::sync::Arc; -use std::ops::Range; -use std::cmp; -use parking_lot::RwLock; -use elements::{MemoryType, ResizableLimits}; -use interpreter::Error; -use interpreter::module::check_limits; - -/// Linear memory page size. -pub const LINEAR_MEMORY_PAGE_SIZE: u32 = 65536; -/// Maximal number of pages. -const LINEAR_MEMORY_MAX_PAGES: u32 = 65536; - -/// Linear memory instance. -pub struct MemoryInstance { - /// Memofy limits. - limits: ResizableLimits, - /// Linear memory buffer. - buffer: RwLock>, - /// Maximum buffer size. - maximum_size: u32, -} - -struct CheckedRegion<'a, B: 'a> where B: ::std::ops::Deref> { - buffer: &'a B, - offset: usize, - size: usize, -} - -impl<'a, B: 'a> CheckedRegion<'a, B> where B: ::std::ops::Deref> { - fn range(&self) -> Range { - self.offset..self.offset+self.size - } - - fn slice(&self) -> &[u8] { - &self.buffer[self.range()] - } - - 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 - } -} - -impl MemoryInstance { - /// Create new linear memory instance. - pub fn new(memory_type: &MemoryType) -> Result, Error> { - check_limits(memory_type.limits())?; - - let maximum_size = match memory_type.limits().maximum() { - Some(maximum_pages) if maximum_pages > LINEAR_MEMORY_MAX_PAGES => - return Err(Error::Memory(format!("maximum memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES))), - Some(maximum_pages) => maximum_pages.saturating_mul(LINEAR_MEMORY_PAGE_SIZE), - None => u32::MAX, - }; - let initial_size = calculate_memory_size(0, memory_type.limits().initial(), maximum_size) - .ok_or(Error::Memory(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)))?; - - let memory = MemoryInstance { - limits: memory_type.limits().clone(), - buffer: RwLock::new(vec![0; initial_size as usize]), - maximum_size: maximum_size, - }; - - Ok(Arc::new(memory)) - } - - /// Return linear memory limits. - pub fn limits(&self) -> &ResizableLimits { - &self.limits - } - - /// Return linear memory size (in pages). - pub fn size(&self) -> u32 { - self.buffer.read().len() as u32 / LINEAR_MEMORY_PAGE_SIZE - } - - /// Get data at given offset. - pub fn get(&self, offset: u32, size: usize) -> Result, Error> { - let buffer = self.buffer.read(); - let region = self.checked_region(&buffer, offset as usize, size)?; - - Ok(region.slice().to_vec()) - } - - /// Write memory slice into another slice - pub fn get_into(&self, offset: u32, target: &mut [u8]) -> Result<(), Error> { - let buffer = self.buffer.read(); - let region = self.checked_region(&buffer, offset as usize, target.len())?; - - target.copy_from_slice(region.slice()); - - Ok(()) - } - - /// Set data at given offset. - pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error> { - let mut buffer = self.buffer.write(); - let range = self.checked_region(&buffer, offset as usize, value.len())?.range(); - - buffer[range].copy_from_slice(value); - - Ok(()) - } - - /// Increases the size of the linear memory by given number of pages. - /// Returns -1 if allocation fails or previous memory size, if succeeds. - pub fn grow(&self, pages: u32) -> Result { - let mut buffer = self.buffer.write(); - let old_size = buffer.len() as u32; - match calculate_memory_size(old_size, pages, self.maximum_size) { - None => Ok(u32::MAX), - Some(new_size) => { - buffer.resize(new_size as usize, 0); - Ok(old_size / LINEAR_MEMORY_PAGE_SIZE) - }, - } - } - - fn checked_region<'a, B>(&self, buffer: &'a B, offset: usize, size: usize) -> Result, Error> - where B: ::std::ops::Deref> - { - let end = offset.checked_add(size) - .ok_or(Error::Memory(format!("trying to access memory block of size {} from offset {}", size, offset)))?; - - if end > buffer.len() { - return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset, end, buffer.len()))); - } - - Ok(CheckedRegion { - buffer: buffer, - offset: offset, - size: size, - }) - } - - /// Copy memory region. Semantically equivalent to `memmove`. - pub fn copy(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> { - let buffer = self.buffer.write(); - - let read_region = self.checked_region(&buffer, src_offset, len)?; - let write_region = self.checked_region(&buffer, dst_offset, len)?; - - unsafe { ::std::ptr::copy( - buffer[read_region.range()].as_ptr(), - buffer[write_region.range()].as_ptr() as *mut _, - len, - )} - - Ok(()) - } - - /// Copy memory region, non-overlapping version. Semantically equivalent to `memcpy`, - /// but returns Error if source overlaping with destination. - pub fn copy_nonoverlapping(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> { - let buffer = self.buffer.write(); - - let read_region = self.checked_region(&buffer, src_offset, len)?; - let write_region = self.checked_region(&buffer, dst_offset, len)?; - - if read_region.intersects(&write_region) { - return Err(Error::Memory(format!("non-overlapping copy is used for overlapping regions"))) - } - - unsafe { ::std::ptr::copy_nonoverlapping( - buffer[read_region.range()].as_ptr(), - buffer[write_region.range()].as_ptr() as *mut _, - len, - )} - - Ok(()) - } - - /// Clear memory region with a specified value. Semantically equivalent to `memset`. - pub fn clear(&self, offset: usize, new_val: u8, len: usize) -> Result<(), Error> { - let mut buffer = self.buffer.write(); - - let range = self.checked_region(&buffer, offset, len)?.range(); - for val in &mut buffer[range] { *val = new_val } - Ok(()) - } - - /// Zero memory region - pub fn zero(&self, offset: usize, len: usize) -> Result<(), Error> { - self.clear(offset, 0, len) - } -} - -fn calculate_memory_size(old_size: u32, additional_pages: u32, maximum_size: u32) -> Option { - additional_pages - .checked_mul(LINEAR_MEMORY_PAGE_SIZE) - .and_then(|size| size.checked_add(old_size)) - .and_then(|size| if size > maximum_size { - None - } else { - Some(size) - }) -} - -#[cfg(test)] -mod tests { - - use super::MemoryInstance; - use interpreter::Error; - use elements::MemoryType; - use std::sync::Arc; - - fn create_memory(initial_content: &[u8]) -> Arc { - let mem = MemoryInstance::new(&MemoryType::new(1, Some(1))) - .expect("MemoryInstance created successfuly"); - 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_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_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 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(&MemoryType::new(1, None)).expect("memory instance creation should not fail"); - 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"); - - assert_eq!(data, [17, 129]); - } -} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs deleted file mode 100644 index 61ac6ff..0000000 --- a/src/interpreter/mod.rs +++ /dev/null @@ -1,181 +0,0 @@ -//! WebAssembly interpreter module. - -#![deprecated(since = "0.23", note = "Use wasmi crate to interpret wasm")] - -use std::any::TypeId; -use std::error; -use std::fmt; -use validation; -use common; - -/// Custom user error. -pub trait UserError: 'static + ::std::fmt::Display + ::std::fmt::Debug { - #[doc(hidden)] - fn __private_get_type_id__(&self) -> TypeId { - TypeId::of::() - } -} - -impl UserError { - /// Attempt to downcast this `UserError` 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 UserError as *const T)) } - } else { - None - } - } - - /// Attempt to downcast this `UserError` 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 UserError as *mut T)) } - } else { - None - } - } -} - -/// Internal interpreter error. -#[derive(Debug)] -pub enum Error { - /// Program-level error. - Program(String), - /// Validation error. - Validation(String), - /// Initialization error. - Initialization(String), - /// Function-level error. - Function(String), - /// Table-level error. - Table(String), - /// Memory-level error. - Memory(String), - /// Variable-level error. - Variable(String), - /// Global-level error. - Global(String), - /// Local-level error. - Local(String), - /// Stack-level error. - Stack(String), - /// Value-level error. - Value(String), - /// Interpreter (code) error. - Interpreter(String), - /// Native module error. - Native(String), - /// Trap. - Trap(String), - /// Custom user error. - User(Box), -} - -impl Into for Error { - fn into(self) -> String { - match self { - Error::Program(s) => s, - Error::Validation(s) => s, - Error::Initialization(s) => s, - Error::Function(s) => s, - Error::Table(s) => s, - Error::Memory(s) => s, - Error::Variable(s) => s, - Error::Global(s) => s, - Error::Local(s) => s, - Error::Stack(s) => s, - Error::Interpreter(s) => s, - Error::Value(s) => s, - Error::Native(s) => s, - Error::Trap(s) => format!("trap: {}", s), - Error::User(e) => format!("user: {}", e), - } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Program(ref s) => write!(f, "Program: {}", s), - Error::Validation(ref s) => write!(f, "Validation: {}", s), - Error::Initialization(ref s) => write!(f, "Initialization: {}", 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::Variable(ref s) => write!(f, "Variable: {}", s), - Error::Global(ref s) => write!(f, "Global: {}", s), - Error::Local(ref s) => write!(f, "Local: {}", s), - Error::Stack(ref s) => write!(f, "Stack: {}", s), - Error::Interpreter(ref s) => write!(f, "Interpreter: {}", s), - Error::Value(ref s) => write!(f, "Value: {}", s), - Error::Native(ref s) => write!(f, "Native: {}", s), - Error::Trap(ref s) => write!(f, "Trap: {}", s), - Error::User(ref e) => write!(f, "User: {}", e), - } - } -} - -impl error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::Program(ref s) => s, - Error::Validation(ref s) => s, - Error::Initialization(ref s) => s, - Error::Function(ref s) => s, - Error::Table(ref s) => s, - Error::Memory(ref s) => s, - Error::Variable(ref s) => s, - Error::Global(ref s) => s, - Error::Local(ref s) => s, - Error::Stack(ref s) => s, - Error::Interpreter(ref s) => s, - Error::Value(ref s) => s, - Error::Native(ref s) => s, - Error::Trap(ref s) => s, - Error::User(_) => "User error", - } - } -} - -impl From for Error where U: UserError + Sized { - fn from(e: U) -> Self { - Error::User(Box::new(e)) - } -} - -impl From for Error { - fn from(e: validation::Error) -> Self { - Error::Validation(e.to_string()) - } -} - -impl From for Error { - fn from(e: common::stack::Error) -> Self { - Error::Stack(e.to_string()) - } -} - -mod validator; -mod native; -mod imports; -mod memory; -mod module; -mod program; -mod runner; -mod stack; -mod table; -mod value; -mod variable; - -#[cfg(test)] -mod tests; - -pub use self::memory::MemoryInstance; -pub use self::module::{ModuleInstance, ModuleInstanceInterface, - ItemIndex, ExportEntryType, CallerContext, ExecutionParams, FunctionSignature}; -pub use self::table::TableInstance; -pub use self::program::ProgramInstance; -pub use self::value::RuntimeValue; -pub use self::variable::{VariableInstance, VariableType, ExternalVariableValue}; -pub use self::native::{native_module, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor}; diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs deleted file mode 100644 index f7be5ed..0000000 --- a/src/interpreter/module.rs +++ /dev/null @@ -1,746 +0,0 @@ -use std::collections::HashMap; -use std::iter::repeat; -use std::sync::{Arc, Weak}; -use std::fmt; -use elements::{Module, InitExpr, Opcode, Type, FunctionType, Internal, External, ResizableLimits, Local, ValueType, BlockType}; -use interpreter::Error; -use interpreter::native::UserFunctionDescriptor; -use interpreter::imports::ModuleImports; -use interpreter::memory::MemoryInstance; -use interpreter::program::ProgramInstanceEssence; -use interpreter::runner::{Interpreter, FunctionContext, prepare_function_args}; -use interpreter::table::TableInstance; -use interpreter::validator::{Validator, FunctionValidationContext}; -use interpreter::value::{RuntimeValue, TryInto}; -use interpreter::variable::{VariableInstance, VariableType}; -use common::stack::StackWithLimit; - -/// Maximum number of entries in value stack. -const DEFAULT_VALUE_STACK_LIMIT: usize = 16384; -/// Maximum number of entries in frame stack. -const DEFAULT_FRAME_STACK_LIMIT: usize = 1024; - -/// Execution context. -#[derive(Clone)] -pub struct ExecutionParams<'a> { - /// Arguments. - pub args: Vec, - /// Execution-local external modules. - pub externals: HashMap>, -} - -/// Export type. -#[derive(Debug, Clone)] -pub enum ExportEntryType<'a> { - /// Any type. - Any, - /// Type of function. - Function(FunctionSignature<'a>), - /// Type of global. - Global(VariableType), -} - -/// Function signature. -#[derive(Debug, Clone)] -pub enum FunctionSignature<'a> { - /// Module function reference. - Module(&'a FunctionType), - /// Native user function refrence. - User(&'a UserFunctionDescriptor), -} - -/// Module instance API. -pub trait ModuleInstanceInterface { - /// Execute function with the given index. - fn execute_index(&self, index: u32, params: ExecutionParams) -> Result, Error>; - /// Execute function with the given export name. - fn execute_export(&self, name: &str, params: ExecutionParams) -> Result, Error>; - /// Get export entry. - fn export_entry<'a>(&self, name: &str, required_type: &ExportEntryType) -> Result; - /// Get table reference. - fn table(&self, index: ItemIndex) -> Result, Error>; - /// Get memory reference. - fn memory(&self, index: ItemIndex) -> Result, Error>; - /// Get global reference. - fn global<'a>(&self, index: ItemIndex, variable_type: Option, externals: Option<&'a HashMap>>) -> Result, Error>; - /// Get function type for given function index. - fn function_type(&self, function_index: ItemIndex) -> Result; - /// Get function type for given function index. - fn function_type_by_index(&self, type_index: u32) -> Result; - /// Get function reference. - fn function_reference<'a>(&self, index: ItemIndex, externals: Option<&'a HashMap>>) -> Result, Error>; - /// Get function indirect reference. - fn function_reference_indirect<'a>(&self, table_idx: u32, type_idx: u32, func_idx: u32, externals: Option<&'a HashMap>>) -> Result, Error>; - /// Get internal function for interpretation. - fn function_body<'a>(&'a self, internal_index: u32) -> Result>, Error>; - /// Call function with given internal index. - fn call_internal_function(&self, outer: CallerContext, index: u32) -> Result, Error>; -} - -/// Item index in items index space. -#[derive(Debug, Clone, Copy)] -pub enum ItemIndex { - /// Index in index space. - IndexSpace(u32), - /// Internal item index (i.e. index of item in items section). - Internal(u32), - /// External module item index (i.e. index of item in the import section). - External(u32), -} - -/// Module instance. -pub struct ModuleInstance { - /// Module name. - name: String, - /// Module. - module: Module, - /// Function labels. - functions_labels: HashMap>, - /// Module imports. - imports: ModuleImports, - /// Module exports. - exports: HashMap>, - /// Tables. - tables: Vec>, - /// Linear memory regions. - memory: Vec>, - /// Globals. - globals: Vec>, -} - -/// Caller context. -pub struct CallerContext<'a> { - /// Value stack limit - pub value_stack_limit: usize, - /// Frame stack limit - pub frame_stack_limit: usize, - /// Stack of the input parameters - pub value_stack: &'a mut StackWithLimit, - /// Execution-local external modules. - pub externals: &'a HashMap>, -} - -/// Internal function reference. -#[derive(Clone)] -pub struct InternalFunctionReference<'a> { - /// Module reference. - pub module: Arc, - /// Internal function index. - pub internal_index: u32, -} - -impl<'a> fmt::Debug for InternalFunctionReference<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "InternalFunctionReference") - } -} - -/// Internal function ready for interpretation. -pub struct InternalFunction<'a> { - /// Function locals. - pub locals: &'a [Local], - /// Function body. - pub body: &'a [Opcode], - /// Function labels. - pub labels: &'a HashMap, -} - -impl<'a> ExecutionParams<'a> { - /// Create new execution params with given externa; module override. - pub fn with_external(name: String, module: Arc) -> Self { - let mut externals = HashMap::new(); - externals.insert(name, module); - ExecutionParams { - args: Vec::new(), - externals: externals, - } - } - - /// Add argument. - pub fn add_argument(mut self, arg: RuntimeValue) -> Self { - self.args.push(arg); - self - } -} - -impl<'a> Default for ExecutionParams<'a> { - fn default() -> Self { - ExecutionParams { - args: Vec::default(), - externals: HashMap::default(), - } - } -} - -impl<'a> From> for ExecutionParams<'a> { - fn from(args: Vec) -> ExecutionParams<'a> { - ExecutionParams { - args: args, - externals: HashMap::new(), - } - } -} - -impl ModuleInstance { - /// Instantiate given module within program context. - pub fn new<'a>(program: Weak, name: String, module: Module) -> Result { - // load entries from import section - let imports = ModuleImports::new(program, module.import_section()); - - // instantiate linear memory regions, if any - let memory = match module.memory_section() { - Some(memory_section) => memory_section.entries() - .iter() - .map(MemoryInstance::new) - .collect::, _>>()?, - None => Vec::new(), - }; - - // instantiate tables, if any - let tables = match module.table_section() { - Some(table_section) => table_section.entries() - .iter() - .map(|tt| TableInstance::new(tt)) - .collect::, _>>()?, - None => Vec::new(), - }; - - // instantiate globals, if any - let globals = match module.global_section() { - Some(global_section) => global_section.entries() - .iter() - .map(|g| { - get_initializer(g.init_expr(), &module, &imports, g.global_type().content_type().into()) - .map_err(|e| Error::Initialization(e.into())) - .and_then(|v| VariableInstance::new_global(g.global_type(), v).map(Arc::new)) - }) - .collect::, _>>()?, - None => Vec::new(), - }; - - Ok(ModuleInstance { - name: name, - module: module, - imports: imports, - exports: HashMap::new(), - functions_labels: HashMap::new(), - memory: memory, - tables: tables, - globals: globals, - }) - } - - /// Run instantiation-time procedures (validation). Module is not completely validated until this call. - pub fn instantiate<'a>(&mut self, externals: Option<&'a HashMap>>) -> Result<(), Error> { - ::validation::validate_module(&self.module)?; - - // validate start section - if let Some(start_function) = self.module.start_section() { - let func_type_index = self.require_function(ItemIndex::IndexSpace(start_function))?; - let func_type = self.function_type_by_index(func_type_index)?; - if func_type.return_type() != None || func_type.params().len() != 0 { - return Err(Error::Validation("start function expected to have type [] -> []".into())); - } - } - - // validate export section - if let Some(export_section) = self.module.export_section() { - for export in export_section.entries() { - match export.internal() { - &Internal::Function(function_index) => { - self.require_function(ItemIndex::IndexSpace(function_index)).map(|_| ())?; - self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Function(function_index)); - }, - &Internal::Global(global_index) => { - self.global(ItemIndex::IndexSpace(global_index), None, externals) - .and_then(|g| if g.is_mutable() { - Err(Error::Validation(format!("trying to export mutable global {}", export.field()))) - } else { - Ok(()) - })?; - self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Global(global_index)); - }, - &Internal::Memory(memory_index) => { - self.memory(ItemIndex::IndexSpace(memory_index)).map(|_| ())?; - self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Memory(memory_index)); - }, - &Internal::Table(table_index) => { - self.table(ItemIndex::IndexSpace(table_index)).map(|_| ())?; - self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Table(table_index)); - }, - } - } - } - - // validate import section - if let Some(import_section) = self.module.import_section() { - for import in import_section.entries() { - match import.external() { - // for functions we need to check if function type matches in both modules - &External::Function(ref function_type_index) => { - // External::Function points to function type in type section in this module - let import_function_type = self.function_type_by_index(*function_type_index)?; - - // get export entry in external module - let external_module = self.imports.module(externals, import.module())?; - let export_entry = external_module.export_entry(import.field(), &ExportEntryType::Function(import_function_type.clone()))?; - - // export entry points to function in function index space - // and Internal::Function points to type in type section - match export_entry { - Internal::Function(function_index) => { - external_module.function_type(ItemIndex::IndexSpace(function_index))? - } - _ => { - return Err(Error::Validation(format!( - "Export with name {} from module {} is not a function", - import.field(), - import.module() - ))) - } - }; - }, - &External::Global(ref global_type) => if global_type.is_mutable() { - return Err(Error::Validation(format!("trying to import mutable global {}", import.field()))); - } else { - self.imports.global(externals, import, Some(global_type.content_type().into()))?; - }, - &External::Memory(ref memory_type) => { - let import_limits = memory_type.limits(); - check_limits(import_limits)?; - - let memory = self.imports.memory(externals, import)?; - let memory_limits = memory.limits(); - - // a linear-memory import's minimum length is required to be at most the imported linear memory's minimum length. - if import_limits.initial() > memory_limits.initial() { - return Err(Error::Validation(format!("trying to import memory with initial={} and import.initial={}", memory_limits.initial(), import_limits.initial()))); - } - - // not working because of wabt tests: - // a linear-memory import is required to have a maximum length if the imported linear memory has a maximum length. - - // if present, a linear-memory import's maximum length is required to be at least the imported linear memory's maximum length. - match (memory_limits.maximum(), import_limits.maximum()) { - (Some(ml), Some(il)) if il < ml => - return Err(Error::Validation(format!("trying to import memory with maximum={} and import.maximum={}", ml, il))), - _ => (), - } - }, - &External::Table(ref table_type) => { - let import_limits = table_type.limits(); - check_limits(import_limits)?; - - let table = self.imports.table(externals, import)?; - let table_limits = table.limits(); - - // a table import's minimum length is required to be at most the imported table's minimum length. - if import_limits.initial() > table_limits.initial() { - return Err(Error::Validation(format!("trying to import table with initial={} and import.initial={}", table_limits.initial(), import_limits.initial()))); - } - - // not working because of wabt tests: - // a table import is required to have a maximum length if the imported table has a maximum length. - - // if present, a table import's maximum length is required to be at least the imported table's maximum length. - match (table_limits.maximum(), import_limits.maximum()) { - (Some(ml), Some(il)) if il < ml => - return Err(Error::Validation(format!("trying to import table with maximum={} and import.maximum={}", ml, il))), - _ => (), - } - }, - } - } - } - - // there must be no greater than 1 table in tables index space - if self.imports.tables_len() + self.tables.len() > 1 { - return Err(Error::Validation(format!("too many tables in index space: {}", self.imports.tables_len() + self.tables.len()))); - } - - // there must be no greater than 1 memory region in memory regions index space - if self.imports.memory_regions_len() + self.memory.len() > 1 { - return Err(Error::Validation(format!("too many memory regions in index space: {}", self.imports.memory_regions_len() + self.memory.len()))); - } - - // for every function section entry there must be corresponding entry in code section and type && vice versa - let function_section_len = self.module.function_section().map(|s| s.entries().len()).unwrap_or(0); - let code_section_len = self.module.code_section().map(|s| s.bodies().len()).unwrap_or(0); - if function_section_len != code_section_len { - return Err(Error::Validation(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 = self.module.function_section().expect("function_section_len != 0; qed"); - let code_section = self.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_labels = { - let function_type = self.function_type_by_index(function.type_ref())?; - let function_body = code_section.bodies().get(index as usize).ok_or(Error::Validation(format!("Missing body for function {}", index)))?; - let mut locals = function_type.params().to_vec(); - locals.extend(function_body.locals().iter().flat_map(|l| repeat(l.value_type()).take(l.count() as usize))); - - let mut context = FunctionValidationContext::new( - self, - externals, - &locals, - DEFAULT_VALUE_STACK_LIMIT, - DEFAULT_FRAME_STACK_LIMIT, - function_type.clone()); - - let block_type = function_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult); - Validator::validate_function(&mut context, block_type, function_body.code().elements()) - .map_err(|e| { - if let Error::Validation(msg) = e { - Error::Validation(format!("Function #{} validation error: {}", index, msg)) - } else { - e - } - })?; - context.function_labels() - }; - self.functions_labels.insert(index as u32, function_labels); - } - } - - // use data section to initialize linear memory regions - if let Some(data_section) = self.module.data_section() { - for (data_segment_index, data_segment) in data_section.entries().iter().enumerate() { - let offset: u32 = get_initializer(data_segment.offset(), &self.module, &self.imports, VariableType::I32)?.try_into()?; - self.memory(ItemIndex::IndexSpace(data_segment.index())) - .map_err(|e| Error::Initialization(format!("DataSegment {} initializes non-existant MemoryInstance {}: {:?}", data_segment_index, data_segment.index(), e))) - .and_then(|m| m.set(offset, data_segment.value())) - .map_err(|e| Error::Initialization(e.into()))?; - } - } - - // use element section to fill tables - if let Some(element_section) = self.module.elements_section() { - for (element_segment_index, element_segment) in element_section.entries().iter().enumerate() { - let offset: u32 = get_initializer(element_segment.offset(), &self.module, &self.imports, VariableType::I32)?.try_into()?; - for function_index in element_segment.members() { - self.require_function(ItemIndex::IndexSpace(*function_index))?; - } - - self.table(ItemIndex::IndexSpace(element_segment.index())) - .map_err(|e| Error::Initialization(format!("ElementSegment {} initializes non-existant Table {}: {:?}", element_segment_index, element_segment.index(), e))) - .and_then(|m| m.set_raw(offset, self.name.clone(), element_segment.members())) - .map_err(|e| Error::Initialization(e.into()))?; - } - } - - Ok(()) - } - - /// Run start function [if any]. - pub fn run_start_function(&self) -> Result<(), Error> { - // execute start function (if any) - if let Some(start_function) = self.module.start_section() { - self.execute_index(start_function, ExecutionParams::default())?; - } - Ok(()) - } - - fn self_ref<'a>(&self, externals: Option<&'a HashMap>>) -> Result, Error> { - self.imports.module(externals, &self.name) - } - - fn require_function(&self, index: ItemIndex) -> Result { - match self.imports.parse_function_index(index) { - ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"), - ItemIndex::Internal(index) => self.module.function_section() - .ok_or(Error::Function(format!("missing internal function {}", index))) - .and_then(|s| s.entries().get(index as usize) - .ok_or(Error::Function(format!("missing internal function {}", index)))) - .map(|f| f.type_ref()), - ItemIndex::External(index) => self.module.import_section() - .ok_or(Error::Function(format!("missing external function {}", index))) - .and_then(|s| s.entries().get(index as usize) - .ok_or(Error::Function(format!("missing external function {}", index)))) - .and_then(|import| match import.external() { - &External::Function(type_idx) => Ok(type_idx), - _ => Err(Error::Function(format!("external function {} is pointing to non-function import", index))), - }), - } - } -} - -impl ModuleInstanceInterface for ModuleInstance { - fn execute_index(&self, index: u32, params: ExecutionParams) -> Result, Error> { - let ExecutionParams { args, externals } = params; - let mut args = StackWithLimit::with_data(args, DEFAULT_VALUE_STACK_LIMIT); - let function_reference = self.function_reference(ItemIndex::IndexSpace(index), Some(&externals))?; - let function_context = CallerContext::topmost(&mut args, &externals); - function_reference.module.call_internal_function(function_context, function_reference.internal_index) - } - - fn execute_export(&self, name: &str, params: ExecutionParams) -> Result, Error> { - let index = self.exports.get(name) - .ok_or(Error::Function(format!("missing executable export with name {}", name))) - .and_then(|l| l.iter() - .find(|i| match i { - &&Internal::Function(_) => true, - _ => false, - }) - .ok_or(Error::Function(format!("missing exported function with name {}", name))) - .map(|i| match i { - &Internal::Function(index) => index, - _ => unreachable!(), // checked couple of lines above - }) - )?; - self.execute_index(index, params) - } - - fn export_entry<'a>(&self, name: &str, required_type: &ExportEntryType) -> Result { - self.exports.get(name) - .ok_or(Error::Function(format!("missing export entry with name {}", name))) - .and_then(|l| l.iter() - .find(|i| match required_type { - &ExportEntryType::Any => true, - &ExportEntryType::Global(global_type) => match i { - &&Internal::Global(global_index) => self.global(ItemIndex::IndexSpace(global_index), Some(global_type), None).map(|_| true).unwrap_or(false), - _ => false, - }, - &ExportEntryType::Function(ref required_type) => match i { - &&Internal::Function(function_index) => - self.function_type(ItemIndex::IndexSpace(function_index)) - .map(|ft| ft == *required_type) - .unwrap_or(false), - _ => false, - }, - }) - .map(|i| *i) - .ok_or(Error::Program(format!("unresolved export {}", name)))) - } - - fn table(&self, index: ItemIndex) -> Result, Error> { - match self.imports.parse_table_index(index) { - ItemIndex::IndexSpace(_) => unreachable!("parse_table_index resolves IndexSpace option"), - ItemIndex::Internal(index) => self.tables.get(index as usize).cloned() - .ok_or(Error::Table(format!("trying to access table with local index {} when there are only {} local tables", index, self.tables.len()))), - ItemIndex::External(index) => self.module.import_section() - .ok_or(Error::Table(format!("trying to access external table with index {} in module without import section", index))) - .and_then(|s| s.entries().get(index as usize) - .ok_or(Error::Table(format!("trying to access external table with index {} in module with {}-entries import section", index, s.entries().len())))) - .and_then(|e| self.imports.table(None, e)), - } - } - - fn memory(&self, index: ItemIndex) -> Result, Error> { - match self.imports.parse_memory_index(index) { - ItemIndex::IndexSpace(_) => unreachable!("parse_memory_index resolves IndexSpace option"), - ItemIndex::Internal(index) => self.memory.get(index as usize).cloned() - .ok_or(Error::Memory(format!("trying to access memory with local index {} when there are only {} memory regions", index, self.memory.len()))), - ItemIndex::External(index) => self.module.import_section() - .ok_or(Error::Memory(format!("trying to access external memory with index {} in module without import section", index))) - .and_then(|s| s.entries().get(index as usize) - .ok_or(Error::Memory(format!("trying to access external memory with index {} in module with {}-entries import section", index, s.entries().len())))) - .and_then(|e| self.imports.memory(None, e)), - } - } - - fn global<'a>(&self, index: ItemIndex, variable_type: Option, externals: Option<&'a HashMap>>) -> Result, Error> { - match self.imports.parse_global_index(index) { - ItemIndex::IndexSpace(_) => unreachable!("parse_global_index resolves IndexSpace option"), - ItemIndex::Internal(index) => self.globals.get(index as usize).cloned() - .ok_or(Error::Global(format!("trying to access global with local index {} when there are only {} globals", index, self.globals.len()))), - ItemIndex::External(index) => self.module.import_section() - .ok_or(Error::Global(format!("trying to access external global with index {} in module without import section", index))) - .and_then(|s| s.entries().get(index as usize) - .ok_or(Error::Global(format!("trying to access external global with index {} in module with {}-entries import section", index, s.entries().len())))) - .and_then(|e| self.imports.global(externals, e, variable_type)), - } - } - - fn function_type(&self, function_index: ItemIndex) -> Result { - match self.imports.parse_function_index(function_index) { - ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"), - ItemIndex::Internal(index) => self.require_function(ItemIndex::Internal(index)) - .and_then(|ft| self.function_type_by_index(ft)), - ItemIndex::External(index) => self.module.import_section() - .ok_or(Error::Function(format!("trying to access external function with index {} in module without import section", index))) - .and_then(|s| s.entries().get(index as usize) - .ok_or(Error::Function(format!("trying to access external function with index {} in module with {}-entries import section", index, s.entries().len())))) - .and_then(|e| match e.external() { - &External::Function(type_index) => self.function_type_by_index(type_index), - _ => Err(Error::Function(format!("exported function {} is not a function", index))), - }), - } - } - - fn function_type_by_index(&self, type_index: u32) -> Result { - self.module.type_section() - .ok_or(Error::Validation(format!("type reference {} exists in module without type section", type_index))) - .and_then(|s| match s.types().get(type_index as usize) { - Some(&Type::Function(ref function_type)) => Ok(function_type), - _ => Err(Error::Validation(format!("missing function type with index {}", type_index))), - }) - .map(FunctionSignature::Module) - } - - fn function_reference<'a>(&self, index: ItemIndex, externals: Option<&'a HashMap>>) -> Result, Error> { - match self.imports.parse_function_index(index) { - ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"), - ItemIndex::Internal(index) => Ok(InternalFunctionReference { - module: self.self_ref(externals)?, - internal_index: index, - }), - ItemIndex::External(index) => { - let import_entry = self.module.import_section() - .expect("parse_function_index has returned External(index); it is only returned when import section exists; qed") - .entries().get(index as usize) - .expect("parse_function_index has returned External(index); it is only returned when entry with index exists in import section exists; qed"); - let required_function_type = self.function_type(ItemIndex::External(index))?; - let internal_function_index = self.imports.function(externals, import_entry, Some(required_function_type))?; - Ok(InternalFunctionReference { - module: self.imports.module(externals, import_entry.module())?, - internal_index: internal_function_index, - }) - }, - } - } - - fn function_reference_indirect<'a>(&self, table_idx: u32, type_idx: u32, func_idx: u32, externals: Option<&'a HashMap>>) -> Result, Error> { - let table = self.table(ItemIndex::IndexSpace(table_idx))?; - let (module, index) = match table.get(func_idx)? { - RuntimeValue::AnyFunc(module, index) => (module.clone(), index), - _ => return Err(Error::Function(format!("trying to indirect call function {} via non-anyfunc table {:?}", func_idx, table_idx))), - }; - - let module = self.imports.module(externals, &module)?; - let required_function_type = self.function_type_by_index(type_idx)?; - let actual_function_type = module.function_type(ItemIndex::IndexSpace(index))?; - if required_function_type != actual_function_type { - return Err(Error::Function(format!("expected indirect function with signature ({:?}) -> {:?} when got with ({:?}) -> {:?}", - required_function_type.params(), required_function_type.return_type(), - actual_function_type.params(), actual_function_type.return_type()))); - } - - module.function_reference(ItemIndex::IndexSpace(index), externals) - } - - fn function_body<'a>(&'a self, internal_index: u32) -> Result>, Error> { - let function_body = self.module - .code_section() - .ok_or(Error::Function(format!("trying to call function with index {} in module without code section", internal_index))) - .and_then(|s| s.bodies() - .get(internal_index as usize) - .ok_or(Error::Function(format!("trying to call function with index {} in module with {} functions codes", internal_index, s.bodies().len()))))?; - let function_labels = self.functions_labels.get(&internal_index) - .ok_or(Error::Function(format!("trying to call non-validated internal function {}", internal_index)))?; - - Ok(Some(InternalFunction { - locals: function_body.locals(), - body: function_body.code().elements(), - labels: function_labels, - })) - } - - fn call_internal_function(&self, outer: CallerContext, index: u32) -> Result, Error> { - let function_type = self.function_type(ItemIndex::Internal(index))?; - let args = prepare_function_args(&function_type, outer.value_stack)?; - let function_ref = InternalFunctionReference { module: self.self_ref(Some(outer.externals))?, internal_index: index }; - let inner = FunctionContext::new(function_ref, outer.externals, outer.value_stack_limit, outer.frame_stack_limit, &function_type, args); - Interpreter::run_function(inner) - } -} - -impl<'a> CallerContext<'a> { - /// Top most args - pub fn topmost(args: &'a mut StackWithLimit, externals: &'a HashMap>) -> Self { - CallerContext { - value_stack_limit: DEFAULT_VALUE_STACK_LIMIT, - frame_stack_limit: DEFAULT_FRAME_STACK_LIMIT, - value_stack: args, - externals: externals, - } - } - - /// Nested context - pub fn nested(outer: &'a mut FunctionContext) -> Self { - CallerContext { - value_stack_limit: outer.value_stack().limit() - outer.value_stack().len(), - frame_stack_limit: outer.frame_stack().limit() - outer.frame_stack().len(), - value_stack: &mut outer.value_stack, - externals: &outer.externals, - } - } -} - -pub fn check_limits(limits: &ResizableLimits) -> Result<(), Error> { - if let Some(maximum) = limits.maximum() { - if maximum < limits.initial() { - return Err(Error::Validation(format!("maximum limit {} is lesser than minimum {}", maximum, limits.initial()))); - } - } - - Ok(()) -} - -fn get_initializer(expr: &InitExpr, module: &Module, imports: &ModuleImports, expected_type: VariableType) -> Result { - let first_opcode = match expr.code().len() { - 1 => &expr.code()[0], - 2 if expr.code().len() == 2 && expr.code()[1] == Opcode::End => &expr.code()[0], - _ => return Err(Error::Initialization(format!("expected 1-instruction len initializer. Got {:?}", expr.code()))), - }; - - match first_opcode { - &Opcode::GetGlobal(index) => { - let index = match imports.parse_global_index(ItemIndex::IndexSpace(index)) { - ItemIndex::External(index) => index, - _ => return Err(Error::Global(format!("trying to initialize with non-external global {}", index))), - }; - module.import_section() - .ok_or(Error::Global(format!("trying to initialize with external global with index {} in module without import section", index))) - .and_then(|s| s.entries().get(index as usize) - .ok_or(Error::Global(format!("trying to initialize with external global with index {} in module with {}-entries import section", index, s.entries().len())))) - .and_then(|e| imports.global(None, e, Some(expected_type))) - .map(|g| g.get()) - }, - &Opcode::I32Const(val) => Ok(RuntimeValue::I32(val)), - &Opcode::I64Const(val) => Ok(RuntimeValue::I64(val)), - &Opcode::F32Const(val) => Ok(RuntimeValue::decode_f32(val)), - &Opcode::F64Const(val) => Ok(RuntimeValue::decode_f64(val)), - _ => Err(Error::Initialization(format!("not-supported {:?} instruction in instantiation-time initializer", first_opcode))), - } -} - -impl<'a> FunctionSignature<'a> { - /// Get return type of this function. - pub fn return_type(&self) -> Option { - match self { - &FunctionSignature::Module(ft) => ft.return_type(), - &FunctionSignature::User(fd) => fd.return_type(), - } - } - - /// Get parameters of this function. - pub fn params(&self) -> &[ValueType] { - match self { - &FunctionSignature::Module(ft) => ft.params(), - &FunctionSignature::User(fd) => fd.params(), - } - } -} - -impl<'a> PartialEq for FunctionSignature<'a> { - fn eq<'b>(&self, other: &FunctionSignature<'b>) -> bool { - match self { - &FunctionSignature::Module(ft1) => match other { - &FunctionSignature::Module(ft2) => ft1 == ft2, - &FunctionSignature::User(ft2) => ft1.params() == ft2.params() && ft1.return_type() == ft2.return_type(), - }, - &FunctionSignature::User(ft1) => match other { - &FunctionSignature::User(ft2) => ft1 == ft2, - &FunctionSignature::Module(ft2) => ft1.params() == ft2.params() && ft1.return_type() == ft2.return_type(), - }, - } - } -} -impl<'a> From<&'a FunctionType> for FunctionSignature<'a> { - fn from(other: &'a FunctionType) -> Self { - FunctionSignature::Module(other) - } -} diff --git a/src/interpreter/native.rs b/src/interpreter/native.rs deleted file mode 100644 index 09cfc6d..0000000 --- a/src/interpreter/native.rs +++ /dev/null @@ -1,288 +0,0 @@ - -use std::sync::Arc; -use std::collections::HashMap; -use std::borrow::Cow; -use parking_lot::RwLock; -use elements::{Internal, ValueType}; -use interpreter::Error; -use interpreter::module::{ModuleInstanceInterface, ExecutionParams, ItemIndex, - CallerContext, ExportEntryType, InternalFunctionReference, InternalFunction, FunctionSignature}; -use interpreter::memory::MemoryInstance; -use interpreter::table::TableInstance; -use interpreter::value::RuntimeValue; -use interpreter::variable::{VariableInstance, VariableType}; - -/// Min index of native function. -pub const NATIVE_INDEX_FUNC_MIN: u32 = 10001; -/// Min index of native global. -pub const NATIVE_INDEX_GLOBAL_MIN: u32 = 20001; - -/// User functions executor. -pub trait UserFunctionExecutor { - /// Execute function with given name. - fn execute(&mut self, name: &str, context: CallerContext) -> Result, Error>; -} - -/// User function descriptor -#[derive(Debug, Clone)] -pub enum UserFunctionDescriptor { - /// Static function definition - Static(&'static str, &'static [ValueType], Option), - /// Dynamic heap function definition - Heap(String, Vec, Option), -} - -impl UserFunctionDescriptor { - /// New function with statically known params - pub fn statik(name: &'static str, params: &'static [ValueType], result: Option) -> Self { - UserFunctionDescriptor::Static(name, params, result) - } - - /// New function with statically unknown params - pub fn heap(name: String, params: Vec, result: Option) -> Self { - UserFunctionDescriptor::Heap(name, params, result) - } - - /// Name of the function - pub fn name(&self) -> &str { - match self { - &UserFunctionDescriptor::Static(name, _, _) => name, - &UserFunctionDescriptor::Heap(ref name, _, _) => name, - } - } - - /// Arguments of the function - pub fn params(&self) -> &[ValueType] { - match self { - &UserFunctionDescriptor::Static(_, params, _) => params, - &UserFunctionDescriptor::Heap(_, ref params, _) => params, - } - } - - /// Return type of the function - pub fn return_type(&self) -> Option { - match self { - &UserFunctionDescriptor::Static(_, _, result) => result, - &UserFunctionDescriptor::Heap(_, _, result) => result, - } - } -} - -/// Set of user-defined module elements. -pub struct UserDefinedElements { - /// User globals list. - pub globals: HashMap>, - /// User functions list. - pub functions: Cow<'static, [UserFunctionDescriptor]>, - /// Functions executor. - pub executor: Option, -} - -/// Native module instance. -pub struct NativeModuleInstance { - /// Underlying module reference. - base: Arc, - /// User function executor. - executor: RwLock>, - /// By-name functions index. - functions_by_name: HashMap, - /// User functions list. - functions: Cow<'static, [UserFunctionDescriptor]>, - /// By-name functions index. - globals_by_name: HashMap, - /// User globals list. - globals: Vec>, -} - -impl NativeModuleInstance { - /// Create new native module - pub fn new(base: Arc, elements: UserDefinedElements) -> Result { - if !elements.functions.is_empty() && elements.executor.is_none() { - return Err(Error::Function("trying to construct native module with functions, but without executor".into())); - } - - Ok(NativeModuleInstance { - base: base, - executor: RwLock::new(elements.executor), - functions_by_name: elements.functions.iter().enumerate().map(|(i, f)| (f.name().to_owned(), i as u32)).collect(), - functions: elements.functions, - globals_by_name: elements.globals.iter().enumerate().map(|(i, (g_name, _))| (g_name.to_owned(), i as u32)).collect(), - globals: elements.globals.into_iter().map(|(_, g)| g).collect(), - }) - } -} - -impl ModuleInstanceInterface for NativeModuleInstance { - fn execute_index(&self, index: u32, params: ExecutionParams) -> Result, Error> { - self.base.execute_index(index, params) - } - - fn execute_export(&self, name: &str, params: ExecutionParams) -> Result, Error> { - self.base.execute_export(name, params) - } - - fn export_entry<'b>(&self, name: &str, required_type: &ExportEntryType) -> Result { - if let Some(index) = self.functions_by_name.get(name) { - let composite_index = NATIVE_INDEX_FUNC_MIN + *index; - match required_type { - &ExportEntryType::Any => return Ok(Internal::Function(composite_index)), - &ExportEntryType::Function(ref required_type) => { - let actual_type = self.function_type(ItemIndex::Internal(composite_index)) - .expect( - "by_name contains index; function_type succeeds for all functions from by_name; qed", - ); - return if actual_type == *required_type { - Ok(Internal::Function(composite_index)) - } else { - Err(Error::Validation(format!( - "Export function type {} mismatch. Expected function with signature ({:?}) -> {:?} when got with ({:?}) -> {:?}", - index, - required_type.params(), - required_type.return_type(), - actual_type.params(), - actual_type.return_type() - ))) - }; - } - _ => (), - } - } - if let Some(index) = self.globals_by_name.get(name) { - let composite_index = NATIVE_INDEX_GLOBAL_MIN + *index; - match required_type { - &ExportEntryType::Any => { - return Ok(Internal::Global(composite_index)) - } - &ExportEntryType::Global(ref required_type) => { - let actual_type = self.globals - .get(*index as usize) - .expect( - "globals_by_name maps to indexes of globals; index read from globals_by_name; qed", - ) - .variable_type(); - return if actual_type == *required_type { - Ok(Internal::Global(composite_index)) - } else { - Err(Error::Validation(format!( - "Export global type {} mismatch. Expected type {:?} when got {:?}", - index, - required_type, - actual_type - ))) - }; - } - _ => (), - } - } - - self.base.export_entry(name, required_type) - } - - fn table(&self, index: ItemIndex) -> Result, Error> { - self.base.table(index) - } - - fn memory(&self, index: ItemIndex) -> Result, Error> { - self.base.memory(index) - } - - fn global<'b>(&self, global_index: ItemIndex, variable_type: Option, externals: Option<&'b HashMap>>) -> Result, Error> { - let index = match global_index { - ItemIndex::IndexSpace(index) | ItemIndex::Internal(index) => index, - ItemIndex::External(_) => unreachable!("trying to get global, exported by native module"), - }; - - if index < NATIVE_INDEX_GLOBAL_MIN { - return self.base.global(global_index, variable_type, externals); - } - - self.globals - .get((index - NATIVE_INDEX_GLOBAL_MIN) as usize) - .cloned() - .ok_or(Error::Native(format!("trying to get native global with index {}", index))) - } - - fn function_type(&self, function_index: ItemIndex) -> Result { - let index = match function_index { - ItemIndex::IndexSpace(index) | ItemIndex::Internal(index) => index, - ItemIndex::External(_) => unreachable!("trying to call function, exported by native module"), - }; - - if index < NATIVE_INDEX_FUNC_MIN || index >= NATIVE_INDEX_GLOBAL_MIN { - return self.base.function_type(function_index); - } - - Ok(FunctionSignature::User(self.functions - .get((index - NATIVE_INDEX_FUNC_MIN) as usize) - .ok_or(Error::Native(format!("missing native function with index {}", index)))?)) - } - - fn function_type_by_index(&self, type_index: u32) -> Result { - self.function_type(ItemIndex::Internal(type_index)) - } - - fn function_reference<'b>(&self, index: ItemIndex, externals: Option<&'b HashMap>>) -> Result, Error> { - self.base.function_reference(index, externals) - } - - fn function_reference_indirect<'b>(&self, table_idx: u32, type_idx: u32, func_idx: u32, externals: Option<&'b HashMap>>) -> Result, Error> { - self.base.function_reference_indirect(table_idx, type_idx, func_idx, externals) - } - - fn function_body<'b>(&'b self, _internal_index: u32) -> Result>, Error> { - Ok(None) - } - - fn call_internal_function(&self, outer: CallerContext, index: u32) -> Result, Error> { - if index < NATIVE_INDEX_FUNC_MIN || index >= NATIVE_INDEX_GLOBAL_MIN { - return self.base.call_internal_function(outer, index); - } - - self.functions - .get((index - NATIVE_INDEX_FUNC_MIN) as usize) - .ok_or(Error::Native(format!("trying to call native function with index {}", index)).into()) - .and_then(|f| self.executor.write() - .as_mut() - .expect("function exists; if function exists, executor must also exists [checked in constructor]; qed") - .execute(&f.name(), outer)) - } -} - -/// Create wrapper for a module with given native user functions. -/// -/// # Examples -/// -/// ```rust -/// use parity_wasm::interpreter::{CallerContext, Error, RuntimeValue, UserFunctionExecutor}; -/// -/// struct MyExecutor; -/// -/// impl UserFunctionExecutor for MyExecutor { -/// fn execute( -/// &mut self, -/// name: &str, -/// context: CallerContext, -/// ) -> Result, Error> { -/// match name { -/// "add" => { -/// // fn add(a: u32, b: u32) -> u32 -/// let b = context.value_stack.pop_as::()?; -/// let a = context.value_stack.pop_as::()?; -/// let sum = a + b; -/// Ok(Some(RuntimeValue::I32(sum as i32))) -/// } -/// _ => Err(Error::Trap("not implemented".into()).into()), -/// } -/// } -/// } -/// ``` -pub fn native_module<'a, E: UserFunctionExecutor + 'a>(base: Arc, user_elements: UserDefinedElements) -> Result, Error> { - Ok(Arc::new(NativeModuleInstance::new(base, user_elements)?)) -} - -impl<'a> PartialEq for UserFunctionDescriptor { - fn eq(&self, other: &Self) -> bool { - self.params() == other.params() - && self.return_type() == other.return_type() - } -} diff --git a/src/interpreter/program.rs b/src/interpreter/program.rs deleted file mode 100644 index 9efedc7..0000000 --- a/src/interpreter/program.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::sync::Arc; -use std::collections::HashMap; -use parking_lot::RwLock; -use elements::Module; -use interpreter::Error; -use interpreter::module::{ModuleInstance, ModuleInstanceInterface}; - -/// Program instance. Program is a set of instantiated modules. -pub struct ProgramInstance { - /// Shared data reference. - essence: Arc, -} - -/// Program instance essence. -pub struct ProgramInstanceEssence { - /// Loaded modules. - modules: RwLock>>, -} - -impl ProgramInstance { - /// Create new program instance. - pub fn new() -> Self { - ProgramInstance { - essence: Arc::new(ProgramInstanceEssence::new()), - } - } - - /// Instantiate module with validation. - pub fn add_module<'a>(&self, name: &str, module: Module, externals: Option<&'a HashMap>>) -> Result, Error> { - let mut module_instance = ModuleInstance::new(Arc::downgrade(&self.essence), name.into(), module)?; - module_instance.instantiate(externals)?; - - let module_instance = Arc::new(module_instance); - self.essence.modules.write().insert(name.into(), module_instance.clone()); - module_instance.run_start_function()?; - Ok(module_instance) - } - - /// Insert instantiated module. - pub fn insert_loaded_module(&self, name: &str, module_instance: Arc) -> Result, Error> { - // replace existing module with the same name with new one - self.essence.modules.write().insert(name.into(), Arc::clone(&module_instance)); - Ok(module_instance) - } - - /// Get one of the modules by name - pub fn module(&self, name: &str) -> Option> { - self.essence.module(name) - } -} - -impl ProgramInstanceEssence { - /// Create new program essence. - pub fn new() -> Self { - ProgramInstanceEssence { - modules: RwLock::new(HashMap::new()), - } - } - - /// Get module reference. - pub fn module(&self, name: &str) -> Option> { - self.modules.read().get(name).cloned() - } -} diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs deleted file mode 100644 index f1e0cf9..0000000 --- a/src/interpreter/runner.rs +++ /dev/null @@ -1,1131 +0,0 @@ -use std::mem; -use std::ops; -use std::{u32, usize}; -use std::sync::Arc; -use std::fmt::{self, Display}; -use std::iter::repeat; -use std::collections::{HashMap, VecDeque}; -use elements::{Opcode, BlockType, Local}; -use interpreter::Error; -use interpreter::module::{ModuleInstanceInterface, CallerContext, ItemIndex, InternalFunctionReference, FunctionSignature}; -use interpreter::value::{ - RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto, - ArithmeticOps, Integer, Float, LittleEndianConvert, TransmuteInto, -}; -use interpreter::variable::VariableInstance; -use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, BlockFrame, BlockFrameType}; -use common::stack::StackWithLimit; - -/// Function interpreter. -pub struct Interpreter; - -/// Function execution context. -pub struct FunctionContext<'a> { - /// Is context initialized. - pub is_initialized: bool, - /// Internal function reference. - pub function: InternalFunctionReference<'a>, - /// Execution-local external modules. - pub externals: &'a HashMap>, - /// Function return type. - pub return_type: BlockType, - /// Local variables. - pub locals: Vec, - /// Values stack. - pub value_stack: StackWithLimit, - /// Blocks frames stack. - pub frame_stack: StackWithLimit, - /// Current instruction position. - pub position: usize, -} - -/// Interpreter action to execute after executing instruction. -#[derive(Debug)] -pub enum InstructionOutcome<'a> { - /// Continue with next instruction. - RunNextInstruction, - /// Branch to given frame. - Branch(usize), - /// Execute function call. - ExecuteCall(InternalFunctionReference<'a>), - /// End current frame. - End, - /// Return from current function block. - Return, -} - -/// Function run result. -enum RunResult<'a> { - /// Function has returned (optional) value. - Return(Option), - /// Function is calling other function. - NestedCall(FunctionContext<'a>), -} - -impl Interpreter { - pub fn run_function(function_context: FunctionContext) -> Result, Error> { - let mut function_stack = VecDeque::new(); - function_stack.push_back(function_context); - - loop { - let mut function_context = function_stack.pop_back().expect("on loop entry - not empty; on loop continue - checking for emptiness; qed"); - let function_ref = function_context.function.clone(); - let function_return = { - let function_body = function_ref.module.function_body(function_ref.internal_index)?; - - match function_body { - Some(function_body) => { - if !function_context.is_initialized() { - let return_type = function_context.return_type; - function_context.initialize(function_body.locals)?; - function_context.push_frame(function_body.labels, BlockFrameType::Function, return_type)?; - } - - Interpreter::do_run_function(&mut function_context, function_body.body, function_body.labels)? - }, - None => { - // move locals back to the stack - let locals_to_move: Vec<_> = function_context.locals.drain(..).collect(); - for local in locals_to_move { - function_context.value_stack_mut().push(local.get())?; - } - - let nested_context = CallerContext::nested(&mut function_context); - RunResult::Return(function_ref.module.call_internal_function(nested_context, function_ref.internal_index)?) - }, - } - }; - - match function_return { - RunResult::Return(return_value) => { - match function_stack.back_mut() { - Some(caller_context) => if let Some(return_value) = return_value { - caller_context.value_stack_mut().push(return_value)?; - }, - None => return Ok(return_value), - } - }, - RunResult::NestedCall(nested_context) => { - function_stack.push_back(function_context); - function_stack.push_back(nested_context); - }, - } - } - } - - fn do_run_function<'a>(function_context: &mut FunctionContext<'a>, function_body: &[Opcode], function_labels: &HashMap) -> Result, Error> { - loop { - let instruction = &function_body[function_context.position]; - - debug!(target: "interpreter", "running {:?}", instruction); - match Interpreter::run_instruction(function_context, function_labels, instruction)? { - InstructionOutcome::RunNextInstruction => function_context.position += 1, - InstructionOutcome::Branch(mut index) => { - // discard index - 1 blocks - while index >= 1 { - function_context.discard_frame()?; - index -= 1; - } - - function_context.pop_frame(true)?; - if function_context.frame_stack().is_empty() { - break; - } - }, - InstructionOutcome::ExecuteCall(func_ref) => { - function_context.position += 1; - return Ok(RunResult::NestedCall(function_context.nested(func_ref)?)); - }, - InstructionOutcome::End => { - if function_context.frame_stack().is_empty() { - break; - } - }, - InstructionOutcome::Return => break, - } - } - - Ok(RunResult::Return(match function_context.return_type { - BlockType::Value(_) => Some(function_context.value_stack_mut().pop()?), - BlockType::NoResult => None, - })) - } - - fn run_instruction<'a>(context: &mut FunctionContext<'a>, labels: &HashMap, opcode: &Opcode) -> Result, Error> { - match opcode { - &Opcode::Unreachable => Interpreter::run_unreachable(context), - &Opcode::Nop => Interpreter::run_nop(context), - &Opcode::Block(block_type) => Interpreter::run_block(context, labels, block_type), - &Opcode::Loop(block_type) => Interpreter::run_loop(context, labels, block_type), - &Opcode::If(block_type) => Interpreter::run_if(context, labels, block_type), - &Opcode::Else => Interpreter::run_else(context, labels), - &Opcode::End => Interpreter::run_end(context), - &Opcode::Br(idx) => Interpreter::run_br(context, idx), - &Opcode::BrIf(idx) => Interpreter::run_br_if(context, idx), - &Opcode::BrTable(ref table, default) => Interpreter::run_br_table(context, table, default), - &Opcode::Return => Interpreter::run_return(context), - - &Opcode::Call(index) => Interpreter::run_call(context, index), - &Opcode::CallIndirect(index, _reserved) => Interpreter::run_call_indirect(context, index), - - &Opcode::Drop => Interpreter::run_drop(context), - &Opcode::Select => Interpreter::run_select(context), - - &Opcode::GetLocal(index) => Interpreter::run_get_local(context, index), - &Opcode::SetLocal(index) => Interpreter::run_set_local(context, index), - &Opcode::TeeLocal(index) => Interpreter::run_tee_local(context, index), - &Opcode::GetGlobal(index) => Interpreter::run_get_global(context, index), - &Opcode::SetGlobal(index) => Interpreter::run_set_global(context, index), - - &Opcode::I32Load(align, offset) => Interpreter::run_load::(context, align, offset), - &Opcode::I64Load(align, offset) => Interpreter::run_load::(context, align, offset), - &Opcode::F32Load(align, offset) => Interpreter::run_load::(context, align, offset), - &Opcode::F64Load(align, offset) => Interpreter::run_load::(context, align, offset), - &Opcode::I32Load8S(align, offset) => Interpreter::run_load_extend::(context, align, offset), - &Opcode::I32Load8U(align, offset) => Interpreter::run_load_extend::(context, align, offset), - &Opcode::I32Load16S(align, offset) => Interpreter::run_load_extend::(context, align, offset), - &Opcode::I32Load16U(align, offset) => Interpreter::run_load_extend::(context, align, offset), - &Opcode::I64Load8S(align, offset) => Interpreter::run_load_extend::(context, align, offset), - &Opcode::I64Load8U(align, offset) => Interpreter::run_load_extend::(context, align, offset), - &Opcode::I64Load16S(align, offset) => Interpreter::run_load_extend::(context, align, offset), - &Opcode::I64Load16U(align, offset) => Interpreter::run_load_extend::(context, align, offset), - &Opcode::I64Load32S(align, offset) => Interpreter::run_load_extend::(context, align, offset), - &Opcode::I64Load32U(align, offset) => Interpreter::run_load_extend::(context, align, offset), - - &Opcode::I32Store(align, offset) => Interpreter::run_store::(context, align, offset), - &Opcode::I64Store(align, offset) => Interpreter::run_store::(context, align, offset), - &Opcode::F32Store(align, offset) => Interpreter::run_store::(context, align, offset), - &Opcode::F64Store(align, offset) => Interpreter::run_store::(context, align, offset), - &Opcode::I32Store8(align, offset) => Interpreter::run_store_wrap::(context, align, offset), - &Opcode::I32Store16(align, offset) => Interpreter::run_store_wrap::(context, align, offset), - &Opcode::I64Store8(align, offset) => Interpreter::run_store_wrap::(context, align, offset), - &Opcode::I64Store16(align, offset) => Interpreter::run_store_wrap::(context, align, offset), - &Opcode::I64Store32(align, offset) => Interpreter::run_store_wrap::(context, align, offset), - - &Opcode::CurrentMemory(_) => Interpreter::run_current_memory(context), - &Opcode::GrowMemory(_) => Interpreter::run_grow_memory(context), - - &Opcode::I32Const(val) => Interpreter::run_const(context, val.into()), - &Opcode::I64Const(val) => Interpreter::run_const(context, val.into()), - &Opcode::F32Const(val) => Interpreter::run_const(context, RuntimeValue::decode_f32(val)), - &Opcode::F64Const(val) => Interpreter::run_const(context, RuntimeValue::decode_f64(val)), - - &Opcode::I32Eqz => Interpreter::run_eqz::(context), - &Opcode::I32Eq => Interpreter::run_eq::(context), - &Opcode::I32Ne => Interpreter::run_ne::(context), - &Opcode::I32LtS => Interpreter::run_lt::(context), - &Opcode::I32LtU => Interpreter::run_lt::(context), - &Opcode::I32GtS => Interpreter::run_gt::(context), - &Opcode::I32GtU => Interpreter::run_gt::(context), - &Opcode::I32LeS => Interpreter::run_lte::(context), - &Opcode::I32LeU => Interpreter::run_lte::(context), - &Opcode::I32GeS => Interpreter::run_gte::(context), - &Opcode::I32GeU => Interpreter::run_gte::(context), - - &Opcode::I64Eqz => Interpreter::run_eqz::(context), - &Opcode::I64Eq => Interpreter::run_eq::(context), - &Opcode::I64Ne => Interpreter::run_ne::(context), - &Opcode::I64LtS => Interpreter::run_lt::(context), - &Opcode::I64LtU => Interpreter::run_lt::(context), - &Opcode::I64GtS => Interpreter::run_gt::(context), - &Opcode::I64GtU => Interpreter::run_gt::(context), - &Opcode::I64LeS => Interpreter::run_lte::(context), - &Opcode::I64LeU => Interpreter::run_lte::(context), - &Opcode::I64GeS => Interpreter::run_gte::(context), - &Opcode::I64GeU => Interpreter::run_gte::(context), - - &Opcode::F32Eq => Interpreter::run_eq::(context), - &Opcode::F32Ne => Interpreter::run_ne::(context), - &Opcode::F32Lt => Interpreter::run_lt::(context), - &Opcode::F32Gt => Interpreter::run_gt::(context), - &Opcode::F32Le => Interpreter::run_lte::(context), - &Opcode::F32Ge => Interpreter::run_gte::(context), - - &Opcode::F64Eq => Interpreter::run_eq::(context), - &Opcode::F64Ne => Interpreter::run_ne::(context), - &Opcode::F64Lt => Interpreter::run_lt::(context), - &Opcode::F64Gt => Interpreter::run_gt::(context), - &Opcode::F64Le => Interpreter::run_lte::(context), - &Opcode::F64Ge => Interpreter::run_gte::(context), - - &Opcode::I32Clz => Interpreter::run_clz::(context), - &Opcode::I32Ctz => Interpreter::run_ctz::(context), - &Opcode::I32Popcnt => Interpreter::run_popcnt::(context), - &Opcode::I32Add => Interpreter::run_add::(context), - &Opcode::I32Sub => Interpreter::run_sub::(context), - &Opcode::I32Mul => Interpreter::run_mul::(context), - &Opcode::I32DivS => Interpreter::run_div::(context), - &Opcode::I32DivU => Interpreter::run_div::(context), - &Opcode::I32RemS => Interpreter::run_rem::(context), - &Opcode::I32RemU => Interpreter::run_rem::(context), - &Opcode::I32And => Interpreter::run_and::(context), - &Opcode::I32Or => Interpreter::run_or::(context), - &Opcode::I32Xor => Interpreter::run_xor::(context), - &Opcode::I32Shl => Interpreter::run_shl::(context, 0x1F), - &Opcode::I32ShrS => Interpreter::run_shr::(context, 0x1F), - &Opcode::I32ShrU => Interpreter::run_shr::(context, 0x1F), - &Opcode::I32Rotl => Interpreter::run_rotl::(context), - &Opcode::I32Rotr => Interpreter::run_rotr::(context), - - &Opcode::I64Clz => Interpreter::run_clz::(context), - &Opcode::I64Ctz => Interpreter::run_ctz::(context), - &Opcode::I64Popcnt => Interpreter::run_popcnt::(context), - &Opcode::I64Add => Interpreter::run_add::(context), - &Opcode::I64Sub => Interpreter::run_sub::(context), - &Opcode::I64Mul => Interpreter::run_mul::(context), - &Opcode::I64DivS => Interpreter::run_div::(context), - &Opcode::I64DivU => Interpreter::run_div::(context), - &Opcode::I64RemS => Interpreter::run_rem::(context), - &Opcode::I64RemU => Interpreter::run_rem::(context), - &Opcode::I64And => Interpreter::run_and::(context), - &Opcode::I64Or => Interpreter::run_or::(context), - &Opcode::I64Xor => Interpreter::run_xor::(context), - &Opcode::I64Shl => Interpreter::run_shl::(context, 0x3F), - &Opcode::I64ShrS => Interpreter::run_shr::(context, 0x3F), - &Opcode::I64ShrU => Interpreter::run_shr::(context, 0x3F), - &Opcode::I64Rotl => Interpreter::run_rotl::(context), - &Opcode::I64Rotr => Interpreter::run_rotr::(context), - - &Opcode::F32Abs => Interpreter::run_abs::(context), - &Opcode::F32Neg => Interpreter::run_neg::(context), - &Opcode::F32Ceil => Interpreter::run_ceil::(context), - &Opcode::F32Floor => Interpreter::run_floor::(context), - &Opcode::F32Trunc => Interpreter::run_trunc::(context), - &Opcode::F32Nearest => Interpreter::run_nearest::(context), - &Opcode::F32Sqrt => Interpreter::run_sqrt::(context), - &Opcode::F32Add => Interpreter::run_add::(context), - &Opcode::F32Sub => Interpreter::run_sub::(context), - &Opcode::F32Mul => Interpreter::run_mul::(context), - &Opcode::F32Div => Interpreter::run_div::(context), - &Opcode::F32Min => Interpreter::run_min::(context), - &Opcode::F32Max => Interpreter::run_max::(context), - &Opcode::F32Copysign => Interpreter::run_copysign::(context), - - &Opcode::F64Abs => Interpreter::run_abs::(context), - &Opcode::F64Neg => Interpreter::run_neg::(context), - &Opcode::F64Ceil => Interpreter::run_ceil::(context), - &Opcode::F64Floor => Interpreter::run_floor::(context), - &Opcode::F64Trunc => Interpreter::run_trunc::(context), - &Opcode::F64Nearest => Interpreter::run_nearest::(context), - &Opcode::F64Sqrt => Interpreter::run_sqrt::(context), - &Opcode::F64Add => Interpreter::run_add::(context), - &Opcode::F64Sub => Interpreter::run_sub::(context), - &Opcode::F64Mul => Interpreter::run_mul::(context), - &Opcode::F64Div => Interpreter::run_div::(context), - &Opcode::F64Min => Interpreter::run_min::(context), - &Opcode::F64Max => Interpreter::run_max::(context), - &Opcode::F64Copysign => Interpreter::run_copysign::(context), - - &Opcode::I32WrapI64 => Interpreter::run_wrap::(context), - &Opcode::I32TruncSF32 => Interpreter::run_trunc_to_int::(context), - &Opcode::I32TruncUF32 => Interpreter::run_trunc_to_int::(context), - &Opcode::I32TruncSF64 => Interpreter::run_trunc_to_int::(context), - &Opcode::I32TruncUF64 => Interpreter::run_trunc_to_int::(context), - &Opcode::I64ExtendSI32 => Interpreter::run_extend::(context), - &Opcode::I64ExtendUI32 => Interpreter::run_extend::(context), - &Opcode::I64TruncSF32 => Interpreter::run_trunc_to_int::(context), - &Opcode::I64TruncUF32 => Interpreter::run_trunc_to_int::(context), - &Opcode::I64TruncSF64 => Interpreter::run_trunc_to_int::(context), - &Opcode::I64TruncUF64 => Interpreter::run_trunc_to_int::(context), - &Opcode::F32ConvertSI32 => Interpreter::run_extend::(context), - &Opcode::F32ConvertUI32 => Interpreter::run_extend::(context), - &Opcode::F32ConvertSI64 => Interpreter::run_wrap::(context), - &Opcode::F32ConvertUI64 => Interpreter::run_wrap::(context), - &Opcode::F32DemoteF64 => Interpreter::run_wrap::(context), - &Opcode::F64ConvertSI32 => Interpreter::run_extend::(context), - &Opcode::F64ConvertUI32 => Interpreter::run_extend::(context), - &Opcode::F64ConvertSI64 => Interpreter::run_extend::(context), - &Opcode::F64ConvertUI64 => Interpreter::run_extend::(context), - &Opcode::F64PromoteF32 => Interpreter::run_extend::(context), - - &Opcode::I32ReinterpretF32 => Interpreter::run_reinterpret::(context), - &Opcode::I64ReinterpretF64 => Interpreter::run_reinterpret::(context), - &Opcode::F32ReinterpretI32 => Interpreter::run_reinterpret::(context), - &Opcode::F64ReinterpretI64 => Interpreter::run_reinterpret::(context), - } - } - - fn run_unreachable<'a>(_context: &mut FunctionContext) -> Result, Error> { - Err(Error::Trap("programmatic".into())) - } - - fn run_nop<'a>(_context: &mut FunctionContext) -> Result, Error> { - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_block<'a>(context: &mut FunctionContext<'a>, labels: &HashMap, block_type: BlockType) -> Result, Error> { - context.push_frame(labels, BlockFrameType::Block, block_type)?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_loop<'a>(context: &mut FunctionContext<'a>, labels: &HashMap, block_type: BlockType) -> Result, Error> { - context.push_frame(labels, BlockFrameType::Loop, block_type)?; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_if<'a>(context: &mut FunctionContext<'a>, labels: &HashMap, block_type: BlockType) -> Result, Error> { - let branch = context.value_stack_mut().pop_as()?; - let block_frame_type = if branch { BlockFrameType::IfTrue } else { - let else_pos = labels[&context.position]; - if !labels.contains_key(&else_pos) { - context.position = else_pos; - return Ok(InstructionOutcome::RunNextInstruction); - } - - context.position = else_pos; - BlockFrameType::IfFalse - }; - context.push_frame(labels, block_frame_type, block_type).map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_else<'a>(context: &mut FunctionContext, labels: &HashMap) -> Result, Error> { - let end_pos = labels[&context.position]; - context.pop_frame(false)?; - context.position = end_pos; - Ok(InstructionOutcome::RunNextInstruction) - } - - fn run_end<'a>(context: &mut FunctionContext) -> Result, Error> { - context.pop_frame(false)?; - Ok(InstructionOutcome::End) - } - - fn run_br<'a>(_context: &mut FunctionContext, label_idx: u32) -> Result, Error> { - Ok(InstructionOutcome::Branch(label_idx as usize)) - } - - fn run_br_if<'a>(context: &mut FunctionContext, label_idx: u32) -> Result, Error> { - if context.value_stack_mut().pop_as()? { - Ok(InstructionOutcome::Branch(label_idx as usize)) - } else { - Ok(InstructionOutcome::RunNextInstruction) - } - } - - fn run_br_table<'a>(context: &mut FunctionContext, table: &[u32], default: u32) -> Result, Error> { - let index: u32 = context.value_stack_mut().pop_as()?; - Ok(InstructionOutcome::Branch(table.get(index as usize).cloned().unwrap_or(default) as usize)) - } - - fn run_return<'a>(_context: &mut FunctionContext) -> Result, Error> { - Ok(InstructionOutcome::Return) - } - - fn run_call<'a>(context: &mut FunctionContext<'a>, func_idx: u32) -> Result, Error> { - Ok(InstructionOutcome::ExecuteCall(context.module().function_reference(ItemIndex::IndexSpace(func_idx), Some(context.externals))?)) - } - - fn run_call_indirect<'a>(context: &mut FunctionContext<'a>, type_idx: u32) -> Result, Error> { - let table_func_idx: u32 = context.value_stack_mut().pop_as()?; - let function_reference = context.module().function_reference_indirect(DEFAULT_TABLE_INDEX, type_idx, table_func_idx, Some(context.externals))?; - { - let required_function_type = context.module().function_type_by_index(type_idx)?; - let actual_function_type = function_reference.module.function_type(ItemIndex::Internal(function_reference.internal_index))?; - if required_function_type != actual_function_type { - return Err(Error::Function(format!("expected function with signature ({:?}) -> {:?} when got with ({:?}) -> {:?}", - required_function_type.params(), required_function_type.return_type(), - actual_function_type.params(), actual_function_type.return_type()))); - } - } - Ok(InstructionOutcome::ExecuteCall(function_reference)) - } - - fn run_drop<'a>(context: &mut FunctionContext) -> Result, Error> { - context - .value_stack_mut() - .pop() - .map_err(Into::into) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_select<'a>(context: &mut FunctionContext) -> Result, Error> { - context - .value_stack_mut() - .pop_triple() - .and_then(|(left, mid, right)| { - let right: Result<_, Error> = right.try_into(); - match (left, mid, right) { - (left, mid, Ok(condition)) => Ok((left, mid, condition)), - _ => Err(Error::Stack("expected to get int value from stack".into())) - } - }) - .map(|(left, mid, condition)| if condition { left } else { mid }) - .map(|val| context.value_stack_mut().push(val)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_get_local<'a>(context: &mut FunctionContext, index: u32) -> Result, Error> { - context.get_local(index as usize) - .map(|value| context.value_stack_mut().push(value)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_set_local<'a>(context: &mut FunctionContext, index: u32) -> Result, Error> { - let arg = context.value_stack_mut().pop()?; - context.set_local(index as usize, arg) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_tee_local<'a>(context: &mut FunctionContext, index: u32) -> Result, Error> { - let arg = context.value_stack().top()?.clone(); - context.set_local(index as usize, arg) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_get_global<'a>(context: &mut FunctionContext, index: u32) -> Result, Error> { - context.module() - .global(ItemIndex::IndexSpace(index), None, Some(context.externals)) - .and_then(|g| context.value_stack_mut().push(g.get()).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_set_global<'a>(context: &mut FunctionContext, index: u32) -> Result, Error> { - context - .value_stack_mut() - .pop() - .map_err(Into::into) - .and_then(|v| context.module().global(ItemIndex::IndexSpace(index), None, Some(context.externals)).and_then(|g| g.set(v))) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_load<'a, T>(context: &mut FunctionContext, _align: u32, offset: u32) -> Result, Error> - where RuntimeValue: From, T: LittleEndianConvert { - let address = effective_address(offset, context.value_stack_mut().pop_as()?)?; - context.module() - .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) - .and_then(|m| m.get(address, mem::size_of::())) - .and_then(|b| T::from_little_endian(b)) - .and_then(|n| context.value_stack_mut().push(n.into()).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_load_extend<'a, T, U>(context: &mut FunctionContext, _align: u32, offset: u32) -> Result, Error> - where T: ExtendInto, RuntimeValue: From, T: LittleEndianConvert { - let address = effective_address(offset, context.value_stack_mut().pop_as()?)?; - let stack_value: U = context.module() - .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) - .and_then(|m| m.get(address, mem::size_of::())) - .and_then(|b| T::from_little_endian(b)) - .map(|v| v.extend_into())?; - context - .value_stack_mut() - .push(stack_value.into()) - .map_err(Into::into) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_store<'a, T>(context: &mut FunctionContext, _align: u32, offset: u32) -> Result, Error> - where RuntimeValue: TryInto, T: LittleEndianConvert { - let stack_value = context - .value_stack_mut() - .pop_as::() - .map(|n| n.into_little_endian())?; - let address = effective_address(offset, context.value_stack_mut().pop_as::()?)?; - context.module() - .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) - .and_then(|m| m.set(address, &stack_value)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_store_wrap<'a, T, U>( - context: &mut FunctionContext, - _align: u32, - offset: u32, - ) -> Result, Error> - where - RuntimeValue: TryInto, - T: WrapInto, - U: LittleEndianConvert, - { - let stack_value: T = context - .value_stack_mut() - .pop() - .map_err(Into::into) - .and_then(|v| v.try_into())?; - let stack_value = stack_value.wrap_into().into_little_endian(); - let address = effective_address(offset, context.value_stack_mut().pop_as::()?)?; - context.module() - .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) - .and_then(|m| m.set(address, &stack_value)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_current_memory<'a>(context: &mut FunctionContext) -> Result, Error> { - context - .module() - .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) - .map(|m| m.size()) - .and_then(|s| - context - .value_stack_mut() - .push(RuntimeValue::I32(s as i32)) - .map_err(Into::into) - ) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_grow_memory<'a>(context: &mut FunctionContext) -> Result, Error> { - let pages: u32 = context.value_stack_mut().pop_as()?; - context - .module() - .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) - .and_then(|m| m.grow(pages)) - .and_then(|m| - context - .value_stack_mut() - .push(RuntimeValue::I32(m as i32)) - .map_err(Into::into) - ) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_const<'a>(context: &mut FunctionContext, val: RuntimeValue) -> Result, Error> { - context - .value_stack_mut() - .push(val) - .map_err(Into::into) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_eqz<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: TryInto, T: PartialEq + Default { - context - .value_stack_mut() - .pop_as::() - .map(|v| RuntimeValue::I32(if v == Default::default() { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_eq<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: TryInto, T: PartialEq { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| RuntimeValue::I32(if left == right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_ne<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: TryInto, T: PartialEq { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| RuntimeValue::I32(if left != right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_lt<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: TryInto, T: PartialOrd + Display { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| RuntimeValue::I32(if left < right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_gt<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: TryInto, T: PartialOrd { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| RuntimeValue::I32(if left > right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_lte<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: TryInto, T: PartialOrd { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| RuntimeValue::I32(if left <= right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_gte<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: TryInto, T: PartialOrd { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| RuntimeValue::I32(if left >= right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_clz<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: Integer { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.leading_zeros()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_ctz<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: Integer { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.trailing_zeros()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_popcnt<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: Integer { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.count_ones()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_add<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: ArithmeticOps { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.add(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_sub<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: ArithmeticOps { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.sub(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_mul<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: ArithmeticOps { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.mul(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_div<'a, T, U>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: TransmuteInto + Display, U: ArithmeticOps + TransmuteInto { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| (left.transmute_into(), right.transmute_into())) - .map(|(left, right)| left.div(right))? - .map(|v| v.transmute_into()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_rem<'a, T, U>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: TransmuteInto, U: Integer + TransmuteInto { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| (left.transmute_into(), right.transmute_into())) - .map(|(left, right)| left.rem(right))? - .map(|v| v.transmute_into()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_and<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From<::Output> + TryInto, T: ops::BitAnd { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.bitand(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_or<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From<::Output> + TryInto, T: ops::BitOr { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.bitor(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_xor<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From<::Output> + TryInto, T: ops::BitXor { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.bitxor(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_shl<'a, T>(context: &mut FunctionContext, mask: T) -> Result, Error> - where RuntimeValue: From<>::Output> + TryInto, T: ops::Shl + ops::BitAnd { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.shl(right & mask)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_shr<'a, T, U>(context: &mut FunctionContext, mask: U) -> Result, Error> - where RuntimeValue: From + TryInto, T: TransmuteInto, U: ops::Shr + ops::BitAnd, >::Output: TransmuteInto { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| (left.transmute_into(), right.transmute_into())) - .map(|(left, right)| left.shr(right & mask)) - .map(|v| v.transmute_into()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_rotl<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: Integer { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.rotl(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_rotr<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: Integer { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.rotr(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_abs<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.abs()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_neg<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From<::Output> + TryInto, T: ops::Neg { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.neg()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_ceil<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.ceil()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_floor<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.floor()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_trunc<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.trunc()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_nearest<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.nearest()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_sqrt<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.sqrt()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_min<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.min(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_max<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.max(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_copysign<'a, T>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: Float { - context - .value_stack_mut() - .pop_pair_as::() - .map(|(left, right)| left.copysign(right)) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_wrap<'a, T, U>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: WrapInto { - context - .value_stack_mut() - .pop_as::() - .map(|v| v.wrap_into()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_trunc_to_int<'a, T, U, V>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: TryTruncateInto, U: TransmuteInto, { - context - .value_stack_mut() - .pop_as::() - .and_then(|v| v.try_truncate_into()) - .map(|v| v.transmute_into()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_extend<'a, T, U, V>( - context: &mut FunctionContext, - ) -> Result, Error> - where - RuntimeValue: From + TryInto, - T: ExtendInto, - U: TransmuteInto, - { - context - .value_stack_mut() - .pop_as::() - .map_err(Error::into) - .map(|v| v.extend_into()) - .map(|v| v.transmute_into()) - .map(|v| context.value_stack_mut().push(v.into())) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - fn run_reinterpret<'a, T, U>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From, RuntimeValue: TryInto, T: TransmuteInto { - context - .value_stack_mut() - .pop_as::() - .map(TransmuteInto::transmute_into) - .and_then(|val| context.value_stack_mut().push(val.into()).map_err(Into::into)) - .map(|_| InstructionOutcome::RunNextInstruction) - } -} - -impl<'a> FunctionContext<'a> { - pub fn new(function: InternalFunctionReference<'a>, externals: &'a HashMap>, value_stack_limit: usize, frame_stack_limit: usize, function_type: &FunctionSignature, args: Vec) -> Self { - FunctionContext { - is_initialized: false, - function: function, - externals: externals, - return_type: function_type.return_type().map(|vt| BlockType::Value(vt)).unwrap_or(BlockType::NoResult), - value_stack: StackWithLimit::with_limit(value_stack_limit), - frame_stack: StackWithLimit::with_limit(frame_stack_limit), - locals: args, - position: 0, - } - } - - pub fn nested(&mut self, function: InternalFunctionReference<'a>) -> Result { - let (function_locals, function_return_type) = { - let function_type = function.module.function_type(ItemIndex::Internal(function.internal_index))?; - let function_return_type = function_type.return_type().map(|vt| BlockType::Value(vt)).unwrap_or(BlockType::NoResult); - let function_locals = prepare_function_args(&function_type, &mut self.value_stack)?; - (function_locals, function_return_type) - }; - - Ok(FunctionContext { - is_initialized: false, - function: function, - externals: self.externals, - return_type: function_return_type, - value_stack: StackWithLimit::with_limit(self.value_stack.limit() - self.value_stack.len()), - frame_stack: StackWithLimit::with_limit(self.frame_stack.limit() - self.frame_stack.len()), - locals: function_locals, - position: 0, - }) - } - - pub fn is_initialized(&self) -> bool { - self.is_initialized - } - - pub fn initialize(&mut self, locals: &[Local]) -> Result<(), Error> { - debug_assert!(!self.is_initialized); - self.is_initialized = true; - - let locals = locals.iter() - .flat_map(|l| repeat(l.value_type().into()).take(l.count() as usize)) - .map(|vt| VariableInstance::new(true, vt, RuntimeValue::default(vt))) - .collect::, _>>()?; - self.locals.extend(locals); - Ok(()) - } - - pub fn module(&self) -> &Arc { - &self.function.module - } - - pub fn externals(&self) -> &HashMap> { - &self.externals - } - - pub fn set_local(&mut self, index: usize, value: RuntimeValue) -> Result, Error> { - self.locals.get_mut(index) - .ok_or(Error::Local(format!("expected to have local with index {}", index))) - .and_then(|l| l.set(value)) - .map(|_| InstructionOutcome::RunNextInstruction) - } - - pub fn get_local(&mut self, index: usize) -> Result { - self.locals.get(index) - .ok_or(Error::Local(format!("expected to have local with index {}", index))) - .map(|l| l.get()) - } - - pub fn value_stack(&self) -> &StackWithLimit { - &self.value_stack - } - - pub fn value_stack_mut(&mut self) -> &mut StackWithLimit { - &mut self.value_stack - } - - pub fn frame_stack(&self) -> &StackWithLimit { - &self.frame_stack - } - - pub fn frame_stack_mut(&mut self) -> &mut StackWithLimit { - &mut self.frame_stack - } - - pub fn push_frame(&mut self, labels: &HashMap, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), Error> { - let begin_position = self.position; - let branch_position = match frame_type { - BlockFrameType::Function => usize::MAX, - BlockFrameType::Loop => begin_position, - BlockFrameType::IfTrue => { - let else_pos = labels[&begin_position]; - 1usize + match labels.get(&else_pos) { - Some(end_pos) => *end_pos, - None => else_pos, - } - }, - _ => labels[&begin_position] + 1, - }; - let end_position = match frame_type { - BlockFrameType::Function => usize::MAX, - _ => labels[&begin_position] + 1, - }; - Ok(self.frame_stack.push(BlockFrame { - frame_type: frame_type, - block_type: block_type, - begin_position: begin_position, - branch_position: branch_position, - end_position: end_position, - value_stack_len: self.value_stack.len(), - })?) - } - - pub fn discard_frame(&mut self) -> Result<(), Error> { - Ok(self.frame_stack.pop().map(|_| ())?) - } - - pub fn pop_frame(&mut self, is_branch: bool) -> Result<(), Error> { - let frame = self.frame_stack.pop()?; - if frame.value_stack_len > self.value_stack.len() { - return Err(Error::Stack("invalid stack len".into())); - } - - let frame_value = match frame.block_type { - BlockType::Value(_) if frame.frame_type != BlockFrameType::Loop || !is_branch => Some(self.value_stack.pop()?), - _ => None, - }; - self.value_stack.resize(frame.value_stack_len, RuntimeValue::I32(0)); - self.position = if is_branch { frame.branch_position } else { frame.end_position }; - if let Some(frame_value) = frame_value { - self.value_stack.push(frame_value)?; - } - - Ok(()) - } -} - -impl<'a> fmt::Debug for FunctionContext<'a> { - 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(Error::Memory(format!("invalid memory access: {} + {}", offset, address))), - Some(address) => Ok(address), - } -} - -pub fn prepare_function_args(function_type: &FunctionSignature, caller_stack: &mut StackWithLimit) -> Result, Error> { - let mut args = function_type.params().iter().rev().map(|param_type| { - let param_value = caller_stack.pop()?; - let actual_type = param_value.variable_type(); - let expected_type = (*param_type).into(); - if actual_type != Some(expected_type) { - return Err(Error::Function(format!("invalid parameter type {:?} when expected {:?}", actual_type, expected_type))); - } - - VariableInstance::new(true, expected_type, param_value) - }).collect::, _>>()?; - args.reverse(); - Ok(args) -} diff --git a/src/interpreter/stack.rs b/src/interpreter/stack.rs deleted file mode 100644 index ae81933..0000000 --- a/src/interpreter/stack.rs +++ /dev/null @@ -1,35 +0,0 @@ -use interpreter::{Error as InterpreterError}; -use interpreter::value::{RuntimeValue, TryInto}; -use common::stack::StackWithLimit; - -impl StackWithLimit { - pub fn pop_as(&mut self) -> Result - where - RuntimeValue: TryInto, - { - let value = self.pop()?; - TryInto::try_into(value) - } - - pub fn pop_pair(&mut self) -> Result<(RuntimeValue, RuntimeValue), InterpreterError> { - let right = self.pop()?; - let left = self.pop()?; - Ok((left, right)) - } - - pub fn pop_pair_as(&mut self) -> Result<(T, T), InterpreterError> - where - RuntimeValue: TryInto, - { - let right = self.pop_as()?; - let left = self.pop_as()?; - Ok((left, right)) - } - - pub fn pop_triple(&mut self) -> Result<(RuntimeValue, RuntimeValue, RuntimeValue), InterpreterError> { - let right = self.pop()?; - let mid = self.pop()?; - let left = self.pop()?; - Ok((left, mid, right)) - } -} diff --git a/src/interpreter/table.rs b/src/interpreter/table.rs deleted file mode 100644 index a5c2641..0000000 --- a/src/interpreter/table.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::u32; -use std::sync::Arc; -use parking_lot::RwLock; -use elements::{TableType, ResizableLimits}; -use interpreter::Error; -use interpreter::module::check_limits; -use interpreter::variable::{VariableInstance, VariableType}; -use interpreter::value::RuntimeValue; - -/// Table instance. -pub struct TableInstance { - /// Table limits. - limits: ResizableLimits, - /// Table variables type. - variable_type: VariableType, - /// Table memory buffer. - buffer: RwLock>, -} - -/// Table element. Cloneable wrapper around VariableInstance. -struct TableElement { - pub var: VariableInstance, -} - -impl TableInstance { - /// New instance of the table - pub fn new(table_type: &TableType) -> Result, Error> { - check_limits(table_type.limits())?; - - let variable_type = table_type.elem_type().into(); - Ok(Arc::new(TableInstance { - limits: table_type.limits().clone(), - variable_type: variable_type, - buffer: RwLock::new( - vec![TableElement::new(VariableInstance::new(true, variable_type, RuntimeValue::Null)?); table_type.limits().initial() as usize] - ), - })) - } - - /// Return table limits. - pub fn limits(&self) -> &ResizableLimits { - &self.limits - } - - /// Get variable type for this table. - pub fn variable_type(&self) -> VariableType { - self.variable_type - } - - /// Get the specific value in the table - pub fn get(&self, offset: u32) -> Result { - let buffer = self.buffer.read(); - let buffer_len = buffer.len(); - buffer.get(offset as usize) - .map(|v| v.var.get()) - .ok_or(Error::Table(format!("trying to read table item with index {} when there are only {} items", offset, buffer_len))) - } - - /// Set the table value from raw slice - pub fn set_raw(&self, mut offset: u32, module_name: String, value: &[u32]) -> Result<(), Error> { - for val in value { - match self.variable_type { - VariableType::AnyFunc => self.set(offset, RuntimeValue::AnyFunc(module_name.clone(), *val))?, - _ => return Err(Error::Table(format!("table of type {:?} is not supported", self.variable_type))), - } - offset += 1; - } - Ok(()) - } - - /// Set the table from runtime variable value - pub fn set(&self, offset: u32, value: RuntimeValue) -> Result<(), Error> { - let mut buffer = self.buffer.write(); - let buffer_len = buffer.len(); - buffer.get_mut(offset as usize) - .ok_or(Error::Table(format!("trying to update table item with index {} when there are only {} items", offset, buffer_len))) - .and_then(|v| v.var.set(value)) - } -} - -impl TableElement { - pub fn new(var: VariableInstance) -> Self { - TableElement { - var: var, - } - } -} - -impl Clone for TableElement { - fn clone(&self) -> Self { - TableElement::new(VariableInstance::new(self.var.is_mutable(), self.var.variable_type(), self.var.get()) - .expect("it only fails when variable_type() != passed variable value; both are read from already constructed var; qed")) - } -} diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs deleted file mode 100644 index 3361182..0000000 --- a/src/interpreter/tests/basics.rs +++ /dev/null @@ -1,608 +0,0 @@ -///! Basic tests for instructions/constructions, missing in wabt tests - -use std::sync::Arc; -use std::cell::RefCell; -use std::collections::HashMap; -use builder::module; -use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType, - InitExpr, ValueType, Opcodes, Opcode, FunctionType, TableType, MemoryType}; -use interpreter::{Error, UserError, ProgramInstance}; -use interpreter::native::{native_module, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor}; -use interpreter::memory::MemoryInstance; -use interpreter::module::{ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams, ExportEntryType, FunctionSignature}; -use interpreter::value::{RuntimeValue, TryInto}; -use interpreter::variable::{VariableInstance, ExternalVariableValue, VariableType}; -use super::utils::program_with_default_env; - -#[test] -fn import_function() { - let module1 = module() - .with_export(ExportEntry::new("external_func".into(), Internal::Function(0))) - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(3), - Opcode::End, - ])).build() - .build() - .build(); - - let module2 = module() - .with_import(ImportEntry::new("external_module".into(), "external_func".into(), External::Function(0))) - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::Call(0), - Opcode::I32Const(7), - Opcode::I32Add, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let external_module = program.add_module("external_module", module1, None).unwrap(); - let main_module = program.add_module("main", module2, None).unwrap(); - - assert_eq!(external_module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(3)); - assert_eq!(main_module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(10)); -} - -#[test] -fn wrong_import() { - let side_module = module() - .with_export(ExportEntry::new("cool_func".into(), Internal::Function(0))) - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(3), - Opcode::End, - ])).build() - .build() - .build(); - - let module = module() - .with_import(ImportEntry::new("side_module".into(), "not_cool_func".into(), External::Function(0))) - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::Call(0), - Opcode::I32Const(7), - Opcode::I32Add, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let _side_module_instance = program.add_module("side_module", side_module, None).unwrap(); - assert!(program.add_module("main", module, None).is_err()); -} - -#[test] -fn global_get_set() { - let module = module() - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(42), Opcode::End]))) - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::GetGlobal(0), - Opcode::I32Const(8), - Opcode::I32Add, - Opcode::SetGlobal(0), - Opcode::GetGlobal(0), - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(50)); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(58)); -} - -const SIGNATURE_I32_I32: &'static [ValueType] = &[ValueType::I32, ValueType::I32]; - -const SIGNATURES: &'static [UserFunctionDescriptor] = &[ - UserFunctionDescriptor::Static( - "add", - SIGNATURE_I32_I32, - Some(ValueType::I32), - ), - UserFunctionDescriptor::Static( - "sub", - SIGNATURE_I32_I32, - Some(ValueType::I32), - ), - UserFunctionDescriptor::Static( - "err", - SIGNATURE_I32_I32, - Some(ValueType::I32), - ), -]; - -const NO_SIGNATURES: &'static [UserFunctionDescriptor] = &[]; - -// 'external' variable -struct MeasuredVariable { - pub val: i32, -} - -impl ExternalVariableValue for MeasuredVariable { - fn get(&self) -> RuntimeValue { - RuntimeValue::I32(self.val) - } - - fn set(&mut self, val: RuntimeValue) -> Result<(), Error> { - self.val = val.try_into()?; - Ok(()) - } -} - -// custom user error -#[derive(Debug, Clone, PartialEq)] -struct UserErrorWithCode { - error_code: i32, -} - -impl ::std::fmt::Display for UserErrorWithCode { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { - write!(f, "{}", self.error_code) - } -} - -impl UserError for UserErrorWithCode {} - -// user function executor -struct FunctionExecutor { - pub memory: Arc, - pub values: Vec, -} - -impl<'a> UserFunctionExecutor for &'a mut FunctionExecutor { - fn execute(&mut self, name: &str, context: CallerContext) -> Result, Error> { - match name { - "add" => { - let memory_value = self.memory.get(0, 1).unwrap()[0]; - let fn_argument_unused = context.value_stack.pop_as::().unwrap() as u8; - let fn_argument = context.value_stack.pop_as::().unwrap() as u8; - assert_eq!(fn_argument_unused, 0); - - let sum = memory_value + fn_argument; - self.memory.set(0, &vec![sum]).unwrap(); - self.values.push(sum as i32); - Ok(Some(RuntimeValue::I32(sum as i32))) - }, - "sub" => { - let memory_value = self.memory.get(0, 1).unwrap()[0]; - let fn_argument_unused = context.value_stack.pop_as::().unwrap() as u8; - let fn_argument = context.value_stack.pop_as::().unwrap() as u8; - assert_eq!(fn_argument_unused, 0); - - let diff = memory_value - fn_argument; - self.memory.set(0, &vec![diff]).unwrap(); - self.values.push(diff as i32); - Ok(Some(RuntimeValue::I32(diff as i32))) - }, - "err" => { - Err(Error::User(Box::new(UserErrorWithCode { error_code: 777 }))) - }, - _ => Err(Error::Trap("not implemented".into()).into()), - } - } -} - -#[test] -fn native_env_function() { - // create new program - let program = program_with_default_env(); - // => env module is created - let env_instance = program.module("env").unwrap(); - // => linear memory is created - let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap(); - - // create native env module executor - let mut executor = FunctionExecutor { - memory: env_memory.clone(), - values: Vec::new(), - }; - { - let functions = UserDefinedElements { - executor: Some(&mut executor), - globals: HashMap::new(), - functions: ::std::borrow::Cow::from(SIGNATURES), - }; - let native_env_instance = native_module(env_instance, functions).unwrap(); - let params = ExecutionParams::with_external("env".into(), native_env_instance); - - let module = module() - .with_import(ImportEntry::new("env".into(), "add".into(), External::Function(0))) - .with_import(ImportEntry::new("env".into(), "sub".into(), External::Function(0))) - .function() - .signature().param().i32().param().i32().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::GetLocal(1), - Opcode::Call(0), - Opcode::End, - ])).build() - .build() - .function() - .signature().param().i32().param().i32().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::GetLocal(1), - Opcode::Call(1), - Opcode::End, - ])).build() - .build() - .build(); - - // load module - let module_instance = program.add_module("main", module, Some(¶ms.externals)).unwrap(); - - { - assert_eq!(module_instance.execute_index(2, params.clone().add_argument(RuntimeValue::I32(7)).add_argument(RuntimeValue::I32(0))).unwrap().unwrap(), RuntimeValue::I32(7)); - assert_eq!(module_instance.execute_index(2, params.clone().add_argument(RuntimeValue::I32(50)).add_argument(RuntimeValue::I32(0))).unwrap().unwrap(), RuntimeValue::I32(57)); - assert_eq!(module_instance.execute_index(3, params.clone().add_argument(RuntimeValue::I32(15)).add_argument(RuntimeValue::I32(0))).unwrap().unwrap(), RuntimeValue::I32(42)); - } - } - - assert_eq!(executor.memory.get(0, 1).unwrap()[0], 42); - assert_eq!(executor.values, vec![7, 57, 42]); -} - -#[test] -fn native_env_function_own_memory() { - // create program + env module is auto instantiated + env module memory is instantiated (we do not need this) - let program = program_with_default_env(); - - struct OwnMemoryReference { - pub memory: RefCell>>, - } - struct OwnMemoryExecutor { - pub memory_ref: Arc, - } - - impl<'a> UserFunctionExecutor for &'a mut OwnMemoryExecutor { - fn execute(&mut self, name: &str, context: CallerContext) -> Result, Error> { - match name { - "add" => { - let memory = self.memory_ref.memory.borrow_mut().as_ref().expect("initialized before execution; qed").clone(); - let memory_value = memory.get(0, 1).unwrap()[0]; - let fn_argument_unused = context.value_stack.pop_as::().unwrap() as u8; - let fn_argument = context.value_stack.pop_as::().unwrap() as u8; - assert_eq!(fn_argument_unused, 0); - - let sum = memory_value + fn_argument; - memory.set(0, &vec![sum]).unwrap(); - Ok(Some(RuntimeValue::I32(sum as i32))) - }, - _ => Err(Error::Trap("not implemented".into()).into()), - } - } - } - - let env_instance = program.module("env").unwrap(); - let memory_ref = Arc::new(OwnMemoryReference { memory: RefCell::new(None) }); - let mut executor = OwnMemoryExecutor { memory_ref: memory_ref.clone() }; - let native_env_instance = native_module(env_instance, UserDefinedElements { - executor: Some(&mut executor), - globals: HashMap::new(), - functions: ::std::borrow::Cow::from(SIGNATURES), - }).unwrap(); - let params = ExecutionParams::with_external("env".into(), native_env_instance); - - // create module definition with its own memory - // => since we do not import env' memory, all instructions from this module will access this memory - let module = module() - .memory().build() // new memory is created - .with_import(ImportEntry::new("env".into(), "add".into(), External::Function(0))) // import 'native' function - .function() // add simple wasm function - .signature().param().i32().param().i32().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::GetLocal(1), - Opcode::Call(0), - Opcode::End, - ])).build() - .build() - .build(); - - // instantiate module - let module_instance = program.add_module("main", module, Some(¶ms.externals)).unwrap(); - // now get memory reference - let module_memory = module_instance.memory(ItemIndex::Internal(0)).unwrap(); - // post-initialize our executor with memory reference - *memory_ref.memory.borrow_mut() = Some(module_memory); - - // now execute function => executor updates memory - assert_eq!(module_instance.execute_index(1, params.clone().add_argument(RuntimeValue::I32(7)).add_argument(RuntimeValue::I32(0))).unwrap().unwrap(), - RuntimeValue::I32(7)); -} - -#[test] -fn native_env_global() { - struct DummyExecutor; - impl UserFunctionExecutor for DummyExecutor { - fn execute(&mut self, _name: &str, _context: CallerContext) -> Result, Error> { - // this code should be unreachable, because we actually doesn't call any - // native functions in this test. - unreachable!(); - } - } - - let module_constructor = |elements: UserDefinedElements| { - let program = program_with_default_env(); - let env_instance = program.module("env").unwrap(); - let native_env_instance = native_module(env_instance, elements).unwrap(); - let params = ExecutionParams::with_external("env".into(), native_env_instance); - - let module = module() - .with_import(ImportEntry::new("env".into(), "ext_global".into(), External::Global(GlobalType::new(ValueType::I32, false)))) - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::GetGlobal(0), - Opcode::End, - ])).build() - .build() - .build(); - program.add_module("main", module, Some(¶ms.externals))? - .execute_index(0, params.clone()) - }; - - // try to add module, exporting non-existant env' variable => error - { - assert!(module_constructor(UserDefinedElements { - executor: None, - globals: HashMap::new(), - functions: ::std::borrow::Cow::from(NO_SIGNATURES), - }).is_err()); - } - - // now add simple variable natively => ok - { - assert_eq!(module_constructor(UserDefinedElements { - executor: None, - globals: vec![("ext_global".into(), Arc::new(VariableInstance::new(false, VariableType::I32, RuntimeValue::I32(777)).unwrap()))].into_iter().collect(), - functions: ::std::borrow::Cow::from(NO_SIGNATURES), - }).unwrap().unwrap(), RuntimeValue::I32(777)); - } - - // now add 'getter+setter' variable natively => ok - { - assert_eq!(module_constructor(UserDefinedElements { - executor: None, - globals: vec![("ext_global".into(), Arc::new(VariableInstance::new_external_global(false, VariableType::I32, Box::new(MeasuredVariable { val: 345 })).unwrap()))].into_iter().collect(), - functions: ::std::borrow::Cow::from(NO_SIGNATURES), - }).unwrap().unwrap(), RuntimeValue::I32(345)); - } -} - -#[test] -fn native_custom_error() { - let program = program_with_default_env(); - let env_instance = program.module("env").unwrap(); - let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap(); - - let mut executor = FunctionExecutor { memory: env_memory.clone(), values: Vec::new() }; - let functions = UserDefinedElements { - executor: Some(&mut executor), - globals: HashMap::new(), - functions: ::std::borrow::Cow::from(SIGNATURES), - }; - let native_env_instance = native_module(env_instance, functions).unwrap(); - let params = ExecutionParams::with_external("env".into(), native_env_instance); - - let module = module() - .with_import(ImportEntry::new("env".into(), "err".into(), External::Function(0))) - .function() - .signature().param().i32().param().i32().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::GetLocal(1), - Opcode::Call(0), - Opcode::End, - ])).build() - .build() - .build(); - - let module_instance = program.add_module("main", module, Some(¶ms.externals)).unwrap(); - let user_error1 = match module_instance.execute_index( - 0, - params - .clone() - .add_argument(RuntimeValue::I32(7)) - .add_argument(RuntimeValue::I32(0)), - ) { - Err(Error::User(user_error)) => user_error, - result => panic!("Unexpected result {:?}", result), - }; - assert_eq!(user_error1.downcast_ref::().unwrap(), &UserErrorWithCode { error_code: 777 }); - - let user_error2 = match module_instance.execute_index( - 0, - params - .clone() - .add_argument(RuntimeValue::I32(7)) - .add_argument(RuntimeValue::I32(0)), - ) { - Err(Error::User(user_error)) => user_error, - result => panic!("Unexpected result {:?}", result), - }; - assert_eq!(user_error2.downcast_ref::().unwrap(), &UserErrorWithCode { error_code: 777 }); -} - -#[test] -fn env_native_export_entry_type_check() { - let program = program_with_default_env(); - let env_instance = program.module("env").unwrap(); - let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap(); - let mut function_executor = FunctionExecutor { - memory: env_memory, - values: Vec::new(), - }; - let native_env_instance = native_module(env_instance, UserDefinedElements { - executor: Some(&mut function_executor), - globals: vec![("ext_global".into(), Arc::new(VariableInstance::new(false, VariableType::I32, RuntimeValue::I32(1312)).unwrap()))].into_iter().collect(), - functions: ::std::borrow::Cow::from(SIGNATURES), - }).unwrap(); - - assert!(native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![ValueType::I32, ValueType::I32], Some(ValueType::I32))))).is_ok()); - match native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![], Some(ValueType::I32))))) { - Err(Error::Validation(_)) => { }, - result => panic!("Unexpected result {:?}", result), - } - match native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![ValueType::I32, ValueType::I32], None)))) { - Err(Error::Validation(_)) => { }, - result => panic!("Unexpected result {:?}", result), - } - match native_env_instance.export_entry("add", &ExportEntryType::Function(FunctionSignature::Module(&FunctionType::new(vec![ValueType::I32, ValueType::I32], Some(ValueType::I64))))) { - Err(Error::Validation(_)) => { }, - result => panic!("Unexpected result {:?}", result), - } - - assert!(native_env_instance.export_entry("ext_global", &ExportEntryType::Global(VariableType::I32)).is_ok()); - match native_env_instance.export_entry("ext_global", &ExportEntryType::Global(VariableType::F32)) { - Err(Error::Validation(_)) => { }, - result => panic!("Unexpected result {:?}", result), - } -} - -#[test] -fn memory_import_limits_initial() { - let core_module = module() - .memory().with_min(10).build() - .with_export(ExportEntry::new("memory".into(), Internal::Memory(0))) - .build(); - - let program = ProgramInstance::new(); - program.add_module("core", core_module, None).unwrap(); - - let test_cases = vec![ - (9, false), - (10, false), - (11, true), - ]; - - for test_case in test_cases { - let (import_initial, is_error) = test_case; - let client_module = module() - .with_import(ImportEntry::new("core".into(), "memory".into(), External::Memory(MemoryType::new(import_initial, None)))) - .build(); - match program.add_module("client", client_module, None).map(|_| ()) { - Ok(_) if !is_error => (), - Err(Error::Validation(ref actual_error)) - if is_error && actual_error == &format!("trying to import memory with initial=10 and import.initial={}", import_initial) => (), - x @ _ => panic!("unexpected result for test_case {:?}: {:?}", test_case, x), - } - } -} - -#[test] -fn memory_import_limits_maximum() { - #[derive(Debug, Clone, Copy, PartialEq)] - enum MaximumError { ValueMismatch, Ok }; - - let test_cases = vec![ - (None, Some(100), MaximumError::Ok), - (Some(100), None, MaximumError::Ok), - (Some(100), Some(98), MaximumError::ValueMismatch), - (Some(100), Some(100), MaximumError::Ok), - (Some(100), Some(101), MaximumError::Ok), - (None, None, MaximumError::Ok), - ]; - - let program = ProgramInstance::new(); - for test_case in test_cases { - let (core_maximum, client_maximum, expected_err) = test_case; - let core_module = module() - .memory().with_min(10).with_max(core_maximum).build() - .with_export(ExportEntry::new("memory".into(), Internal::Memory(0))) - .build(); - let client_module = module() - .with_import(ImportEntry::new("core".into(), "memory".into(), External::Memory(MemoryType::new(10, client_maximum)))) - .build(); - - program.add_module("core", core_module, None).unwrap(); - match program.add_module("client", client_module, None).map(|_| ()) { - Err(Error::Validation(actual_err)) => match expected_err { - MaximumError::ValueMismatch - if actual_err == format!("trying to import memory with maximum={} and import.maximum={}", core_maximum.unwrap_or_default(), client_maximum.unwrap_or_default()) => (), - _ => panic!("unexpected validation error for test_case {:?}: {}", test_case, actual_err), - }, - Ok(_) if expected_err == MaximumError::Ok => (), - x @ _ => panic!("unexpected result for test_case {:?}: {:?}", test_case, x), - } - } -} - -#[test] -fn table_import_limits_initial() { - let core_module = module() - .table().with_min(10).build() - .with_export(ExportEntry::new("table".into(), Internal::Table(0))) - .build(); - - let program = ProgramInstance::new(); - program.add_module("core", core_module, None).unwrap(); - - let test_cases = vec![ - (9, false), - (10, false), - (11, true), - ]; - - for test_case in test_cases { - let (import_initial, is_error) = test_case; - let client_module = module() - .with_import(ImportEntry::new("core".into(), "table".into(), External::Table(TableType::new(import_initial, None)))) - .build(); - match program.add_module("client", client_module, None).map(|_| ()) { - Ok(_) if !is_error => (), - Err(Error::Validation(ref actual_error)) - if is_error && actual_error == &format!("trying to import table with initial=10 and import.initial={}", import_initial) => (), - x @ _ => panic!("unexpected result for test_case {:?}: {:?}", test_case, x), - } - } -} - -#[test] -fn table_import_limits_maximum() { - #[derive(Debug, Clone, Copy, PartialEq)] - enum MaximumError { ValueMismatch, Ok }; - - let test_cases = vec![ - (None, Some(100), MaximumError::Ok), - (Some(100), None, MaximumError::Ok), - (Some(100), Some(98), MaximumError::ValueMismatch), - (Some(100), Some(100), MaximumError::Ok), - (Some(100), Some(101), MaximumError::Ok), - (None, None, MaximumError::Ok), - ]; - - let program = ProgramInstance::new(); - for test_case in test_cases { - let (core_maximum, client_maximum, expected_err) = test_case; - let core_module = module() - .table().with_min(10).with_max(core_maximum).build() - .with_export(ExportEntry::new("table".into(), Internal::Table(0))) - .build(); - let client_module = module() - .with_import(ImportEntry::new("core".into(), "table".into(), External::Table(TableType::new(10, client_maximum)))) - .build(); - - program.add_module("core", core_module, None).unwrap(); - match program.add_module("client", client_module, None).map(|_| ()) { - Err(Error::Validation(actual_err)) => match expected_err { - MaximumError::ValueMismatch - if actual_err == format!("trying to import table with maximum={} and import.maximum={}", core_maximum.unwrap_or_default(), client_maximum.unwrap_or_default()) => (), - _ => panic!("unexpected validation error for test_case {:?}: {}", test_case, actual_err), - }, - Ok(_) if expected_err == MaximumError::Ok => (), - x @ _ => panic!("unexpected result for test_case {:?}: {:?}", test_case, x), - } - } -} diff --git a/src/interpreter/tests/mod.rs b/src/interpreter/tests/mod.rs deleted file mode 100644 index b2bf1dc..0000000 --- a/src/interpreter/tests/mod.rs +++ /dev/null @@ -1,29 +0,0 @@ -mod basics; -mod wabt; -mod wasm; - -mod utils { - use elements::{Internal, ExportEntry, InitExpr, Opcode, ValueType, GlobalType, GlobalEntry}; - use interpreter::ProgramInstance; - use builder::module; - - pub fn program_with_default_env() -> ProgramInstance { - let program = ProgramInstance::new(); - let env_module = module() - .memory() - .with_min(256) // 256 pages. 256 * 64K = 16MB - .build() - .with_export(ExportEntry::new("memory".into(), Internal::Memory(0))) - .table() - .with_min(64) - .build() - .with_export(ExportEntry::new("table".into(), Internal::Table(0))) - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0), Opcode::End]))) - .with_export(ExportEntry::new("tableBase".into(), Internal::Global(0))) - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0), Opcode::End]))) - .with_export(ExportEntry::new("memoryBase".into(), Internal::Global(1))) - .build(); - program.add_module("env", env_module, None).unwrap(); - program - } -} diff --git a/src/interpreter/tests/wabt.rs b/src/interpreter/tests/wabt.rs deleted file mode 100644 index 7f15125..0000000 --- a/src/interpreter/tests/wabt.rs +++ /dev/null @@ -1,3213 +0,0 @@ -///! Tests from https://github.com/WebAssembly/wabt/tree/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp - -use std::sync::Arc; -use builder::module; -use elements::{ValueType, Opcodes, Opcode, BlockType, Local}; -use interpreter::{Error, ProgramInstance, ModuleInstanceInterface, ItemIndex}; -use interpreter::value::{RuntimeValue, TryInto}; - -fn make_function_i32(body: Opcodes) -> (ProgramInstance, Arc) { - let module = module() - .function() - .signature().param().i32().return_type().i32().build() - .body() - .with_locals(vec![Local::new(2, ValueType::I32)]) - .with_opcodes(body) - .build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - (program, module) -} - -fn run_function_i32(module: &Arc, arg: i32) -> Result { - module - .execute_index(0, vec![RuntimeValue::I32(arg)].into()) - .and_then(|r| r.unwrap().try_into()) -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/unreachable.txt -#[test] -fn unreachable() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::Unreachable, // trap - Opcode::End])); - - match run_function_i32(&module, 0) { - Err(Error::Trap(msg)) => { - assert_eq!(msg, "programmatic"); - }, - result => panic!("Unexpected result {:?}", result), - } -} - -#[test] -fn nop() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::Nop, // nop - Opcode::I32Const(1), // [1] - Opcode::Nop, // nop - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 1); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-block.txt -#[test] -fn expr_block() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::Block(BlockType::Value(ValueType::I32)), - Opcode::I32Const(10), // [10] - Opcode::Drop, - Opcode::I32Const(1), // [1] - Opcode::End, - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 1); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/loop.txt -#[test] -fn loop_test() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::Loop(BlockType::NoResult), // loop - Opcode::GetLocal(1), // [local1] - Opcode::GetLocal(0), // [local1, arg] - Opcode::I32Add, // [arg + local1] - Opcode::SetLocal(1), // [] + local1 = arg + local1 - Opcode::GetLocal(0), // [arg] - Opcode::I32Const(1), // [arg, 1] - Opcode::I32Add, // [arg + 1] - Opcode::SetLocal(0), // [] + arg = arg + 1 - Opcode::GetLocal(0), // [arg] - Opcode::I32Const(5), // [arg, 5] - Opcode::I32LtS, // [arg < 5] - Opcode::If(BlockType::NoResult), - Opcode::Br(1), // break loop - Opcode::End, - Opcode::End, // end loop - Opcode::GetLocal(1), // [local1] - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 10); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/if.txt#L3 -#[test] -fn if_1() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::I32Const(0), // [0] - Opcode::SetLocal(0), // [] + arg = 0 - Opcode::I32Const(1), // [1] - Opcode::If(BlockType::NoResult), // if 1 - Opcode::GetLocal(0), // [arg] - Opcode::I32Const(1), // [arg, 1] - Opcode::I32Add, // [arg + 1] - Opcode::SetLocal(0), // [] + arg = arg + 1 - Opcode::End, // end if - Opcode::I32Const(0), // [0] - Opcode::If(BlockType::NoResult), // if 0 - Opcode::GetLocal(0), // [arg] - Opcode::I32Const(1), // [arg, 1] - Opcode::I32Add, // [arg + 1] - Opcode::SetLocal(0), // [] + arg = arg + 1 - Opcode::End, // end if - Opcode::GetLocal(0), // [arg] - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 1); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/if.txt#L23 -#[test] -fn if_2() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::I32Const(1), // [1] - Opcode::If(BlockType::NoResult), // if 1 - Opcode::I32Const(1), // [1] - Opcode::SetLocal(0), // [] + arg = 1 - Opcode::Else, // else - Opcode::I32Const(2), // [2] - Opcode::SetLocal(0), // [] + arg = 2 - Opcode::End, // end if - Opcode::I32Const(0), // [0] - Opcode::If(BlockType::NoResult), // if 0 - Opcode::I32Const(4), // [4] - Opcode::SetLocal(1), // [] + local1 = 4 - Opcode::Else, // else - Opcode::I32Const(8), // [8] - Opcode::SetLocal(1), // [] + local1 = 8 - Opcode::End, // end if - Opcode::GetLocal(0), // [arg] - Opcode::GetLocal(1), // [arg, local1] - Opcode::I32Add, // [arg + local1] - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 9); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-if.txt -#[test] -fn expr_if() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::GetLocal(0), // [arg] - Opcode::I32Const(0), // [arg, 0] - Opcode::I32Eq, // [arg == 0] - Opcode::If(BlockType::Value(ValueType::I32)), // if arg == 0 - Opcode::I32Const(1), // [1] - Opcode::Else, // else - Opcode::I32Const(2), // [2] - Opcode::End, // end if - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 1); - assert_eq!(run_function_i32(&module, 1).unwrap(), 2); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/nested-if.txt -#[test] -fn nested_if() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::Block(BlockType::NoResult), - Opcode::I32Const(1), - Opcode::If(BlockType::NoResult), - Opcode::I32Const(2), - Opcode::Drop, - Opcode::I32Const(3), - Opcode::If(BlockType::NoResult), - Opcode::Br(2), - Opcode::End, - Opcode::End, - Opcode::End, - Opcode::I32Const(4), - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 4); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L4 -#[test] -fn br_0() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::Block(BlockType::NoResult), // mark block - Opcode::I32Const(1), // [1] - Opcode::If(BlockType::NoResult), // if 1 - Opcode::Br(1), // break from block - Opcode::End, // end if - Opcode::I32Const(1), // [1] - Opcode::SetLocal(0), // [] + arg = 1 - Opcode::End, // end block - Opcode::I32Const(1), // [1] - Opcode::SetLocal(1), // [] + local1 = 1 - Opcode::GetLocal(0), // [arg] - Opcode::I32Const(0), // [arg, 0] - Opcode::I32Eq, // [arg == 0] - Opcode::GetLocal(1), // [arg == 0, local1] - Opcode::I32Const(1), // [arg == 0, local1, 1] - Opcode::I32Eq, // [arg == 0, local1 == 1] - Opcode::I32Add, // [arg == 0 + local1 == 1] - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 2); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L26 -#[test] -fn br_1() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::Block(BlockType::NoResult), // block1 - Opcode::Block(BlockType::NoResult), // block2 - Opcode::I32Const(1), // [1] - Opcode::If(BlockType::NoResult), // if 1 - Opcode::Br(2), // break from block2 - Opcode::End, // end if - Opcode::I32Const(1), // [1] - Opcode::SetLocal(0), // [] + arg = 1 - Opcode::End, // end (block2) - Opcode::I32Const(1), // [1] - Opcode::SetLocal(1), // [] + local1 = 1 - Opcode::End, // end (block1) - Opcode::I32Const(1), // [1] - Opcode::SetLocal(2), // [] + local2 = 1 - Opcode::GetLocal(0), // [arg] - Opcode::I32Const(0), // [arg, 0] - Opcode::I32Eq, // [arg == 0] - Opcode::GetLocal(1), // [arg == 0, local1] - Opcode::I32Const(0), // [arg == 0, local1, 0] - Opcode::I32Eq, // [arg == 0, local1 == 0] - Opcode::I32Add, // [arg == 0 + local1 == 0] - Opcode::GetLocal(2), // [arg == 0 + local1 == 0, local2] - Opcode::I32Const(1), // [arg == 0 + local1 == 0, local2, 1] - Opcode::I32Eq, // [arg == 0 + local1 == 0, local2 == 1] - Opcode::I32Add, // [arg == 0 + local1 == 0 + local2 == 1] - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 3); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L56 -#[test] -fn br_2() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::Block(BlockType::NoResult), // block1 - Opcode::Block(BlockType::NoResult), // block2 - Opcode::I32Const(1), // [1] - Opcode::If(BlockType::NoResult), // if 1 - Opcode::Br(2), // break from block2 - Opcode::End, // end if - Opcode::I32Const(1), // [1] - Opcode::Return, // return 1 - Opcode::End, // end (block2) - Opcode::End, // end (block1) - Opcode::I32Const(2), // [2] - Opcode::Return, // return 2 - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 2); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L71 -#[test] -fn br_3() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::Block(BlockType::NoResult), // block1 - Opcode::Loop(BlockType::NoResult), // loop - Opcode::GetLocal(0), // [arg] - Opcode::I32Const(1), // [arg, 1] - Opcode::I32Add, // [arg + 1] - Opcode::SetLocal(0), // [] + arg = arg + 1 - Opcode::GetLocal(0), // [arg] - Opcode::I32Const(5), // [arg, 5] - Opcode::I32GeS, // [5 >= arg] - Opcode::If(BlockType::NoResult), // if 5 >= arg - Opcode::Br(2), // break from block1 - Opcode::End, // end - Opcode::GetLocal(0), // [arg] - Opcode::I32Const(4), // [arg, 4] - Opcode::I32Eq, // [arg == 4] - Opcode::If(BlockType::NoResult), // if arg == 4 - Opcode::Br(1), // break from loop - Opcode::End, // end - Opcode::GetLocal(0), // [arg] - Opcode::SetLocal(1), // [] + local1 = arg - Opcode::Br(0), // continue loop - Opcode::End, // end (loop) - Opcode::End, // end (block1) - Opcode::GetLocal(1), // [local1] - Opcode::Return, // return local1 - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 3); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-br.txt -#[test] -fn expr_br() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::Block(BlockType::Value(ValueType::I32)), // block1 - Opcode::GetLocal(0), // [arg] - Opcode::I32Const(0), // [arg, 0] - Opcode::I32Eq, // [arg == 0] - Opcode::If(BlockType::NoResult), // if arg == 0 - Opcode::I32Const(1), // [1] - Opcode::Br(1), // break from block1 - Opcode::End, // end (if) - Opcode::I32Const(2), // [2] - Opcode::End, // end (block1) - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 1); - assert_eq!(run_function_i32(&module, 1).unwrap(), 2); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/brif.txt -#[test] -fn brif() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::Block(BlockType::NoResult), // block1 - Opcode::GetLocal(0), // [arg] - Opcode::BrIf(0), // if arg != 0: break from block1 - Opcode::I32Const(1), // [1] - Opcode::Return, // return 1 - Opcode::End, // end (block1) - Opcode::I32Const(2), // [2] - Opcode::Return, // return 2 - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 1); - assert_eq!(run_function_i32(&module, 1).unwrap(), 2); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/brif-loop.txt -#[test] -fn brif_loop() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::Loop(BlockType::NoResult), // loop - Opcode::GetLocal(1), // [local1] - Opcode::I32Const(1), // [local1, 1] - Opcode::I32Add, // [local1 + 1] - Opcode::SetLocal(1), // [] + local1 = local1 + 1 - Opcode::GetLocal(1), // [local1] - Opcode::GetLocal(0), // [local1, arg] - Opcode::I32LtS, // [local1 < arg] - Opcode::BrIf(0), // break loop if local1 < arg - Opcode::End, // end (loop) - Opcode::GetLocal(1), // [local1] - Opcode::Return, // return - Opcode::End])); - - assert_eq!(run_function_i32(&module, 3).unwrap(), 3); - assert_eq!(run_function_i32(&module, 10).unwrap(), 10); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-brif.txt -#[test] -fn expr_brif() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::Loop(BlockType::NoResult), // loop - Opcode::GetLocal(1), // [local1] - Opcode::I32Const(1), // [local1, 1] - Opcode::I32Add, // [local1 + 1] - Opcode::SetLocal(1), // [] + local1 = local1 + 1 - Opcode::GetLocal(1), // [local1] - Opcode::GetLocal(0), // [local1, local0] - Opcode::I32LtS, // [local1 < local0] - Opcode::BrIf(0), // if local1 < local0: break from loop - Opcode::End, // end (loop) - Opcode::GetLocal(1), // [local1] - Opcode::End])); - - assert_eq!(run_function_i32(&module, 3).unwrap(), 3); - assert_eq!(run_function_i32(&module, 10).unwrap(), 10); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/brtable.txt -#[test] -fn brtable() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::Block(BlockType::NoResult), // block3 - Opcode::Block(BlockType::NoResult), // block2 - Opcode::Block(BlockType::NoResult), // block1 - Opcode::Block(BlockType::NoResult), // block0 - Opcode::GetLocal(0), // [arg] - Opcode::BrTable( - vec![0, 1, 2].into_boxed_slice(), 3), - // br_table - Opcode::End, // end (block0) - Opcode::I32Const(0), // [0] - Opcode::Return, // return 0 - Opcode::End, // end (block1) - Opcode::I32Const(1), // [1] - Opcode::Return, // return 1 - Opcode::End, // end (block2) - Opcode::End, // end (block3) - Opcode::I32Const(2), // [2] - Opcode::Return, // return 2 - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 0); - assert_eq!(run_function_i32(&module, 1).unwrap(), 1); - assert_eq!(run_function_i32(&module, 2).unwrap(), 2); - assert_eq!(run_function_i32(&module, 3).unwrap(), 2); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/return.txt -#[test] -fn return_test() { - let (_program, module) = make_function_i32(Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::I32Const(0), - Opcode::I32Eq, - Opcode::If(BlockType::NoResult), - Opcode::I32Const(1), - Opcode::Return, - Opcode::End, - Opcode::GetLocal(0), - Opcode::I32Const(1), - Opcode::I32Eq, - Opcode::If(BlockType::NoResult), - Opcode::I32Const(2), - Opcode::Return, - Opcode::End, - Opcode::I32Const(3), - Opcode::Return, - Opcode::End])); - - assert_eq!(run_function_i32(&module, 0).unwrap(), 1); - assert_eq!(run_function_i32(&module, 1).unwrap(), 2); - assert_eq!(run_function_i32(&module, 5).unwrap(), 3); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/return-void.txt -#[test] -fn return_void() { - let body = Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::I32Const(0), - Opcode::I32Eq, - Opcode::If(BlockType::NoResult), - Opcode::Return, - Opcode::End, - Opcode::I32Const(0), - Opcode::I32Const(1), - Opcode::I32Store(2, 0), - Opcode::End, - ]); - - let module = module() - .memory().build() - .function() - .signature().param().i32().build() - .body().with_opcodes(body).build() - .build() - .build(); - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - - module.execute_index(0, vec![RuntimeValue::I32(0)].into()).unwrap(); - let memory = module.memory(ItemIndex::IndexSpace(0)).unwrap(); - assert_eq!(memory.get(0, 4).unwrap(), vec![0, 0, 0, 0]); - - module.execute_index(0, vec![RuntimeValue::I32(1)].into()).unwrap(); - let memory = module.memory(ItemIndex::IndexSpace(0)).unwrap(); - assert_eq!(memory.get(0, 4).unwrap(), vec![1, 0, 0, 0]); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/call.txt#L3 -#[test] -fn call_1() { - let body1 = Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I64Const(2), - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - Opcode::F32Const(0x40400000), - Opcode::F64Const(0x4010000000000000), - Opcode::Call(1), - Opcode::End, - ]); - - let body2 = Opcodes::new(vec![ - Opcode::GetLocal(1), - Opcode::I32WrapI64, - Opcode::GetLocal(0), - Opcode::I32Add, - Opcode::GetLocal(2), - Opcode::I32TruncSF32, - Opcode::I32Add, - Opcode::GetLocal(3), - Opcode::I32TruncSF64, - Opcode::I32Add, - Opcode::Return, - Opcode::End, - ]); - - let module = module() - .memory().build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(body1).build() - .build() - .function() - .signature() - .param().i32() - .param().i64() - .param().f32() - .param().f64() - .return_type().i32() - .build() - .body().with_opcodes(body2).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(10)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/call.txt#L23 -#[test] -fn call_2() { - let body1 = Opcodes::new(vec![ - Opcode::I32Const(10), - Opcode::Call(1), - Opcode::End, - ]); - - let body2 = Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::I32Const(0), - Opcode::I32GtS, - Opcode::If(BlockType::Value(ValueType::I32)), - Opcode::GetLocal(0), - Opcode::GetLocal(0), - Opcode::I32Const(1), - Opcode::I32Sub, - Opcode::Call(1), - Opcode::I32Mul, - Opcode::Return, - Opcode::Else, - Opcode::I32Const(1), - Opcode::Return, - Opcode::End, - Opcode::End, - ]); - - let module = module() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(body1).build() - .build() - .function() - .signature() - .param().i32() - .return_type().i32() - .build() - .body().with_opcodes(body2).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(3628800)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/call-zero-args.txt -#[test] -fn call_zero_args() { - let body1 = Opcodes::new(vec![ - Opcode::I32Const(42), - Opcode::End, - ]); - - let body2 = Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::GetLocal(1), - Opcode::I32Add, - Opcode::End, - ]); - - let body3 = Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::Call(0), - Opcode::Call(1), - Opcode::End, - ]); - - let module = module() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(body1).build() - .build() - .function() - .signature() - .param().i32() - .param().i32() - .return_type().i32() - .build() - .body().with_opcodes(body2).build() - .build() - .function() - .body().with_opcodes(body3).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(43)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/callindirect.txt#L31 -#[test] -fn callindirect_1() { - let body1 = Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::End, - ]); - - let body2 = Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::End, - ]); - - let body3 = Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::CallIndirect(0, 0), - Opcode::End, - ]); - - let module = module() - .table() - .with_min(2) - .with_element(0, vec![0, 1]) - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(body1).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(body2).build() - .build() - .function() - .signature() - .param().i32() - .return_type().i32() - .build() - .body().with_opcodes(body3).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(2, vec![RuntimeValue::I32(0)].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(2, vec![RuntimeValue::I32(1)].into()).unwrap().unwrap(), RuntimeValue::I32(1)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/callindirect.txt#L39 -#[test] -fn callindirect_2() { - let body1 = Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::GetLocal(1), - Opcode::I32Add, - Opcode::End, - ]); - - let body2 = Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::GetLocal(1), - Opcode::I32Sub, - Opcode::End, - ]); - - let body3 = Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::I32Ctz, - Opcode::End, - ]); - - let body4 = Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::GetLocal(1), - Opcode::GetLocal(2), - Opcode::CallIndirect(0, 0), - Opcode::End, - ]); - - let module = module() - .table() - .with_min(3) - .with_element(0, vec![0, 1, 2]) - .build() - .function() - .signature() - .param().i32() - .param().i32() - .return_type().i32().build() - .body().with_opcodes(body1).build() - .build() - .function() - .signature() - .param().i32() - .param().i32() - .return_type().i32().build() - .body().with_opcodes(body2).build() - .build() - .function() - .signature() - .param().i32() - .return_type().i32().build() - .body().with_opcodes(body3).build() - .build() - .function() - .signature() - .param().i32() - .param().i32() - .param().i32() - .return_type().i32() - .build() - .body().with_opcodes(body4).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(0)].into()).unwrap().unwrap(), RuntimeValue::I32(14)); - assert_eq!(module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(1)].into()).unwrap().unwrap(), RuntimeValue::I32(6)); - match module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(2)].into()) { - Err(Error::Function(msg)) => { - assert_eq!( - &msg, - "expected indirect function with signature ([I32, I32]) -> Some(I32) when got with ([I32]) -> Some(I32)" - ); - } - result => panic!("Unexpected result {:?}", result), - } - match module.execute_index(3, vec![RuntimeValue::I32(10), RuntimeValue::I32(4), RuntimeValue::I32(3)].into()) { - Err(Error::Table(msg)) => { - assert_eq!( - &msg, - "trying to read table item with index 3 when there are only 3 items" - ) - }, - result => panic!("Unexpected result {:?}", result), - } -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/select.txt -#[test] -fn select() { - let body1 = Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(2), - Opcode::GetLocal(0), - Opcode::Select, - Opcode::End, - ]); - - let body2 = Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(2), - Opcode::GetLocal(0), - Opcode::Select, - Opcode::End, - ]); - - let body3 = Opcodes::new(vec![ - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - Opcode::F32Const(0x3F800000), - Opcode::F32Const(0x40000000), - Opcode::GetLocal(0), - Opcode::Select, - Opcode::End, - ]); - - let body4 = Opcodes::new(vec![ - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - Opcode::F64Const(0x3FF0000000000000), - Opcode::F64Const(0x4000000000000000), - Opcode::GetLocal(0), - Opcode::Select, - Opcode::End, - ]); - - let module = module() - .function() - .signature().param().i32().return_type().i32().build() - .body().with_opcodes(body1).build() - .build() - .function() - .signature().param().i32().return_type().i64().build() - .body().with_opcodes(body2).build() - .build() - .function() - .signature().param().i32().return_type().f32().build() - .body().with_opcodes(body3).build() - .build() - .function() - .signature().param().i32().return_type().f64().build() - .body().with_opcodes(body4).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![RuntimeValue::I32(0)].into()).unwrap().unwrap(), RuntimeValue::I32(2)); - assert_eq!(module.execute_index(0, vec![RuntimeValue::I32(1)].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(1, vec![RuntimeValue::I32(0)].into()).unwrap().unwrap(), RuntimeValue::I64(2)); - assert_eq!(module.execute_index(1, vec![RuntimeValue::I32(1)].into()).unwrap().unwrap(), RuntimeValue::I64(1)); - assert_eq!(module.execute_index(2, vec![RuntimeValue::I32(0)].into()).unwrap().unwrap(), RuntimeValue::F32(2f32)); - assert_eq!(module.execute_index(2, vec![RuntimeValue::I32(1)].into()).unwrap().unwrap(), RuntimeValue::F32(1f32)); - assert_eq!(module.execute_index(3, vec![RuntimeValue::I32(0)].into()).unwrap().unwrap(), RuntimeValue::F64(2f64)); - assert_eq!(module.execute_index(3, vec![RuntimeValue::I32(1)].into()).unwrap().unwrap(), RuntimeValue::F64(1f64)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/binary.txt#L3 -#[test] -fn binary_i32() { - let module = module() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(2), - Opcode::I32Add, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(20), - Opcode::I32Const(4), - Opcode::I32Sub, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(3), - Opcode::I32Const(7), - Opcode::I32Mul, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-4), - Opcode::I32Const(2), - Opcode::I32DivS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-4), - Opcode::I32Const(2), - Opcode::I32DivU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-5), - Opcode::I32Const(2), - Opcode::I32RemS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-5), - Opcode::I32Const(2), - Opcode::I32RemU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(11), - Opcode::I32Const(5), - Opcode::I32And, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(11), - Opcode::I32Const(5), - Opcode::I32Or, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(11), - Opcode::I32Const(5), - Opcode::I32Xor, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-100), - Opcode::I32Const(3), - Opcode::I32Shl, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-100), - Opcode::I32Const(3), - Opcode::I32ShrU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-100), - Opcode::I32Const(3), - Opcode::I32ShrS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-100), - Opcode::I32Const(3), - Opcode::I32Rotl, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-100), - Opcode::I32Const(3), - Opcode::I32Rotr, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(3)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(16)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(21)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-2)); // 4294967294 - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(2147483646)); - assert_eq!(module.execute_index(5, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1)); // 4294967295 - assert_eq!(module.execute_index(6, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(7, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(8, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(15)); - assert_eq!(module.execute_index(9, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(14)); - assert_eq!(module.execute_index(10, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-800)); // 4294966496 - assert_eq!(module.execute_index(11, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(536870899)); - assert_eq!(module.execute_index(12, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-13)); // 4294967283 - assert_eq!(module.execute_index(13, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-793)); // 4294966503 - assert_eq!(module.execute_index(14, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1610612749)); // 2684354547 -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/binary.txt#L65 -#[test] -fn binary_i64() { - let module = module() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(2), - Opcode::I64Add, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(20), - Opcode::I64Const(4), - Opcode::I64Sub, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(3), - Opcode::I64Const(7), - Opcode::I64Mul, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-4), - Opcode::I64Const(2), - Opcode::I64DivS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-4), - Opcode::I64Const(2), - Opcode::I64DivU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-5), - Opcode::I64Const(2), - Opcode::I64RemS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-5), - Opcode::I64Const(2), - Opcode::I64RemU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(11), - Opcode::I64Const(5), - Opcode::I64And, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(11), - Opcode::I64Const(5), - Opcode::I64Or, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(11), - Opcode::I64Const(5), - Opcode::I64Xor, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-100), - Opcode::I64Const(3), - Opcode::I64Shl, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-100), - Opcode::I64Const(3), - Opcode::I64ShrU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-100), - Opcode::I64Const(3), - Opcode::I64ShrS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-100), - Opcode::I64Const(3), - Opcode::I64Rotl, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-100), - Opcode::I64Const(3), - Opcode::I64Rotr, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(3)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(16)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(21)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-2)); // 18446744073709551614 - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(9223372036854775806)); - assert_eq!(module.execute_index(5, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-1)); // 18446744073709551615 - assert_eq!(module.execute_index(6, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(1)); - assert_eq!(module.execute_index(7, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(1)); - assert_eq!(module.execute_index(8, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(15)); - assert_eq!(module.execute_index(9, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(14)); - assert_eq!(module.execute_index(10, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-800)); // 18446744073709550816 - assert_eq!(module.execute_index(11, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(2305843009213693939)); - assert_eq!(module.execute_index(12, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-13)); // 18446744073709551603 - assert_eq!(module.execute_index(13, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-793)); // 18446744073709550823 - assert_eq!(module.execute_index(14, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-6917529027641081869)); // 11529215046068469747 -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/binary.txt#L3 -#[test] -fn binary_f32() { - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - let module = module() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x3FA00000), // 1.25 - Opcode::F32Const(0x40700000), // 3.75 - Opcode::F32Add, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x40900000), // 4.5 - Opcode::F32Const(0x461C4000), // 1e4 - Opcode::F32Sub, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x449A5000), // 1234.5 - Opcode::F32Const(0xC0DC0000), // -6.875 - Opcode::F32Mul, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x56B5E621), // 1e14 - Opcode::F32Const(0xC8435000), // -2e5 - Opcode::F32Div, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x00000000), // 0 - Opcode::F32Const(0x00000000), // 0 - Opcode::F32Min, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x00000000), // 0 - Opcode::F32Const(0x00000000), // 0 - Opcode::F32Max, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x00000000), // 0 - Opcode::F32Const(0x00000000), // 0 - Opcode::F32Copysign, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(5.000000)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(-9995.500000)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(-8487.187500)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(-500000000.000000)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(0.000000)); - assert_eq!(module.execute_index(5, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(0.000000)); - assert_eq!(module.execute_index(6, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(0.000000)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/binary.txt#L157 -#[test] -fn binary_f64() { - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - let module = module() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x41CD6F3458800000), // 987654321 - Opcode::F64Const(0x419D6F3454000000), // 123456789 - Opcode::F64Add, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x4C33A8A41D39B24E), // 1234e56 - Opcode::F64Const(0x44DD1DE3D2D5C713), // 5.5e23 - Opcode::F64Sub, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xC132C4B000000000), // -123e4 - Opcode::F64Const(0x416789FE40000000), // 12341234 - Opcode::F64Mul, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x6974E718D7D7625A), // 1e200 - Opcode::F64Const(0x4A511B0EC57E649A), // 1e50 - Opcode::F64Div, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x0000000000000000), // 0 - Opcode::F64Const(0x0000000000000000), // 0 - Opcode::F64Min, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x0000000000000000), // 0 - Opcode::F64Const(0x0000000000000000), // 0 - Opcode::F64Max, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x0000000000000000), // 0 - Opcode::F64Const(0x0000000000000000), // 0 - Opcode::F64Copysign, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(1111111110.000000)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(123400000000000007812762268812638756607430593436581896388608.000000)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(-15179717820000.000000)); - // in wabt result is 99999999999999998083559617243737459057312001403031879309116481015410011220367858297629826861622 - // but the actual (and correct) result is 1e150 - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(1e150)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(0.000000)); - assert_eq!(module.execute_index(5, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(0.000000)); - assert_eq!(module.execute_index(6, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(0.000000)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/cast.txt -#[test] -fn cast() { - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - let module = module() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0x40900000), - Opcode::F32ReinterpretI32, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xC0600000), - Opcode::I32ReinterpretF32, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(0x405f480000000000), - Opcode::F64ReinterpretI64, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x42099C82CC000000), - Opcode::I64ReinterpretF64, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(4.5)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1067450368)); // 3227516928 - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(125.125000)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(4758506566875873280)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/compare.txt#L3 -#[test] -fn compare_i32() { - let module = module() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I32Const(-1), - Opcode::I32Eq, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(-1), - Opcode::I32Eq, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(-1), - Opcode::I32Ne, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I32Const(-1), - Opcode::I32Ne, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I32Const(1), - Opcode::I32LtS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I32Const(-1), - Opcode::I32LtS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(-1), - Opcode::I32LtS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(-1), - Opcode::I32LtU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(1), - Opcode::I32LtU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I32Const(1), - Opcode::I32LtU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I32Const(1), - Opcode::I32LeS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I32Const(-1), - Opcode::I32LeS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(-1), - Opcode::I32LeS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(-1), - Opcode::I32LeU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(1), - Opcode::I32LeU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I32Const(1), - Opcode::I32LeU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I32Const(1), - Opcode::I32GtS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I32Const(-1), - Opcode::I32GtS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(-1), - Opcode::I32GtS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(-1), - Opcode::I32GtU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(1), - Opcode::I32GtU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I32Const(1), - Opcode::I32GtU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I32Const(1), - Opcode::I32GeS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I32Const(-1), - Opcode::I32GeS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(-1), - Opcode::I32GeS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(-1), - Opcode::I32GeU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::I32Const(1), - Opcode::I32GeU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I32Const(1), - Opcode::I32GeU, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(5, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(6, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(7, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(8, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(9, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(10, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(11, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(12, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(13, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(14, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(15, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(16, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(17, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(18, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(19, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(20, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(21, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(22, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(23, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(24, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(25, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(26, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(27, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/compare.txt#L123 -#[test] -fn compare_i64() { - let module = module() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I64Const(-1), - Opcode::I64Eq, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(-1), - Opcode::I64Eq, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(-1), - Opcode::I64Ne, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I64Const(-1), - Opcode::I64Ne, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I64Const(1), - Opcode::I64LtS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I64Const(-1), - Opcode::I64LtS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(-1), - Opcode::I64LtS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(-1), - Opcode::I64LtU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(1), - Opcode::I64LtU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I64Const(1), - Opcode::I64LtU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I64Const(1), - Opcode::I64LeS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I64Const(-1), - Opcode::I64LeS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(-1), - Opcode::I64LeS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(-1), - Opcode::I64LeU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(1), - Opcode::I64LeU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I64Const(1), - Opcode::I64LeU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I64Const(1), - Opcode::I64GtS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I64Const(-1), - Opcode::I64GtS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(-1), - Opcode::I64GtS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(-1), - Opcode::I64GtU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(1), - Opcode::I64GtU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I64Const(1), - Opcode::I64GtU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I64Const(1), - Opcode::I64GeS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I64Const(-1), - Opcode::I64GeS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(-1), - Opcode::I64GeS, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(-1), - Opcode::I64GeU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(1), - Opcode::I64Const(1), - Opcode::I64GeU, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I64Const(1), - Opcode::I64GeU, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(5, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(6, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(7, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(8, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(9, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(10, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(11, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(12, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(13, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(14, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(15, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(16, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(17, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(18, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(19, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(20, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(21, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(22, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(23, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(24, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(25, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(26, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(27, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/compare.txt#L246 -#[test] -fn compare_f32() { - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - let module = module() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Eq, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x3F800000), // 1 - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Eq, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x3F800000), // 1 - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Ne, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Ne, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Const(0x3F800000), // 1 - Opcode::F32Lt, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Lt, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x3F800000), // 1 - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Lt, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Const(0x3F800000), // 1 - Opcode::F32Le, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Le, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x3F800000), // 1 - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Le, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Const(0x3F800000), // 1 - Opcode::F32Gt, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Gt, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x3F800000), // 1 - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Gt, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Const(0x3F800000), // 1 - Opcode::F32Ge, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Ge, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x3F800000), // 1 - Opcode::F32Const(0xBF800000), // -1 - Opcode::F32Ge, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(5, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(6, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(7, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(8, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(9, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(10, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(11, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(12, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(13, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(14, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(15, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/compare.txt#L317 -#[test] -fn compare_f64() { - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - let module = module() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Eq, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x3FF0000000000000), // 1 - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Eq, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x3FF0000000000000), // 1 - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Ne, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Ne, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Const(0x3FF0000000000000), // 1 - Opcode::F64Lt, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Lt, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x3FF0000000000000), // 1 - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Lt, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Const(0x3FF0000000000000), // 1 - Opcode::F64Le, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Le, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x3FF0000000000000), // 1 - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Le, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Const(0x3FF0000000000000), // 1 - Opcode::F64Gt, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Gt, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x3FF0000000000000), // 1 - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Gt, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Const(0x3FF0000000000000), // 1 - Opcode::F64Ge, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Ge, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x3FF0000000000000), // 1 - Opcode::F64Const(0xBFF0000000000000), // -1 - Opcode::F64Ge, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(5, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(6, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(7, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(8, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(9, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(10, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(11, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(12, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(13, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(14, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(15, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/convert.txt#L3 -#[test] -fn convert_i32() { - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - let module = module() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(-1), - Opcode::I32WrapI64, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xC2C83F35), // -100.12345 - Opcode::I32TruncSF32, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x4F32D05E), // 3e9 - Opcode::I32TruncUF32, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xC05907E69AD42C3D), // -100.12345 - Opcode::I32TruncSF64, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x41E65A0BC0000000), // 3e9 - Opcode::I32TruncUF64, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1)); // 4294967295 - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-100)); // 4294967196 - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1294967296)); // 3000000000 - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-100)); // 4294967196 - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1294967296)); // 3000000000 -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/convert.txt#L21 -#[test] -fn convert_i64() { - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - let module = module() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I64ExtendUI32, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::I64ExtendSI32, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xC2C83F35), // -100.12345 - Opcode::I64TruncSF32, - Opcode::I64Const(-100), - Opcode::I64Eq, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x4F32D05E), // 3e9 - Opcode::I64TruncUF32, - Opcode::I64Const(3000000000), - Opcode::I64Eq, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xC05907E69AD42C3D), // -100.12345 - Opcode::I64TruncSF64, - Opcode::I64Const(-100), - Opcode::I64Eq, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x41E65A0BC0000000), // 3e9 - Opcode::I64TruncUF64, - Opcode::I64Const(3000000000), - Opcode::I64Eq, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(4294967295)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-1)); // 18446744073709551615 - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(5, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/convert.txt#L50 -#[test] -fn convert_f32() { - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - let module = module() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::F32ConvertSI32, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::F32ConvertUI32, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x41678C29DCCCCCCD), // 12345678.9 - Opcode::F32DemoteF64, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(0), - Opcode::F32ConvertSI64, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(0), - Opcode::F32ConvertUI64, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(-1.000000)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(4294967296.000000)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(12345679.000000)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(0.000000)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(0.000000)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/convert.txt#L50 -#[test] -fn convert_f64() { - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - let module = module() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::F64ConvertSI32, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(-1), - Opcode::F64ConvertUI32, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x4B3C614F), // 12345678.9 - Opcode::F64PromoteF32, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(0), - Opcode::F64ConvertSI64, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(0), - Opcode::F64ConvertUI64, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(-1.000000)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(4294967295.000000)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(12345679.000000)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(0.000000)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(0.000000)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/load.txt#L9 -#[test] -fn load_i32() { - let module = module() - .memory() - .with_data(0, vec![0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xce, 0x41, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x8f, 0x40, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]) - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I32Load8S(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I32Load16S(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I32Load(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I32Load8U(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I32Load16U(0, 0), - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(255)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(65535)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/load.txt#L26 -#[test] -fn load_i64() { - let module = module() - .memory() - .with_data(0, vec![0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xce, 0x41, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x8f, 0x40, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]) - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I64Load8S(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I64Load16S(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I64Load32S(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(16), - Opcode::I64Load(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I64Load8U(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I64Load16U(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I64Load32U(0, 0), - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-1)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-1)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-1)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-1)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(255)); - assert_eq!(module.execute_index(5, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(65535)); - assert_eq!(module.execute_index(6, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(4294967295)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/load.txt#L50 -#[test] -fn load_f32() { - let module = module() - .memory() - .with_data(0, vec![0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xce, 0x41, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x8f, 0x40, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]) - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(4), - Opcode::F32Load(0, 0), - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(25.750000)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/load.txt#L54 -#[test] -fn load_f64() { - let module = module() - .memory() - .with_data(0, vec![0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xce, 0x41, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x8f, 0x40, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]) - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(8), - Opcode::F64Load(0, 0), - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(1023.875000)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/store.txt#L5 -#[test] -fn store_i32() { - let module = module() - .memory().build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I32Const(0xfb), - Opcode::I32Store8(0, 0), - Opcode::I32Const(1), - Opcode::I32Const(0xfc), - Opcode::I32Store8(0, 0), - Opcode::I32Const(2), - Opcode::I32Const(0xfd), - Opcode::I32Store8(0, 0), - Opcode::I32Const(3), - Opcode::I32Const(0xfe), - Opcode::I32Store8(0, 0), - Opcode::I32Const(0), - Opcode::I32Load(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I32Const(0xcac9), - Opcode::I32Store16(0, 0), - Opcode::I32Const(2), - Opcode::I32Const(0xcccb), - Opcode::I32Store16(0, 0), - Opcode::I32Const(0), - Opcode::I32Load(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I32Const(-123456), - Opcode::I32Store(0, 0), - Opcode::I32Const(0), - Opcode::I32Load(0, 0), - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-16909061)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-859059511)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-123456)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/store.txt#L38 -#[test] -fn store_i64() { - let module = module() - .memory().build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I64Const(-1229782938247303429), - Opcode::I64Store8(0, 0), - Opcode::I32Const(1), - Opcode::I64Const(-1229782938247303428), - Opcode::I64Store8(0, 0), - Opcode::I32Const(2), - Opcode::I64Const(-1229782938247303427), - Opcode::I64Store8(0, 0), - Opcode::I32Const(3), - Opcode::I64Const(-1229782938247303426), - Opcode::I64Store8(0, 0), - Opcode::I32Const(0), - Opcode::I64Load(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I64Const(-1229782938247312695), - Opcode::I64Store16(0, 0), - Opcode::I32Const(2), - Opcode::I64Const(-1229782938247312181), - Opcode::I64Store16(0, 0), - Opcode::I32Const(0), - Opcode::I64Load(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I64Const(-123456), - Opcode::I64Store32(0, 0), - Opcode::I32Const(0), - Opcode::I64Load(0, 0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I64Const(-4981613551475109875), - Opcode::I64Store(0, 0), - Opcode::I32Const(0), - Opcode::I64Load(0, 0), - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(4278058235)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(3435907785)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(4294843840)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(-4981613551475109875)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/store.txt#L78 -#[test] -fn store_f32() { - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - let module = module() - .memory().build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::F32Const(0x3FC00000), // 1.5 - Opcode::F32Store(0, 0), - Opcode::I32Const(0), - Opcode::I32Load(0, 0), - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1069547520)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/store.txt#L85 -#[test] -fn store_f64() { - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - let module = module() - .memory().build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::F64Const(0xC08F460000000000), // -1000.75 - Opcode::F64Store(0, 0), - Opcode::I32Const(4), - Opcode::I32Load(0, 0), - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(-1064352256)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/unary.txt#L12 -#[test] -fn unary_i32() { - let module = module() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(100), - Opcode::I32Eqz, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(0), - Opcode::I32Eqz, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(128), - Opcode::I32Clz, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(128), - Opcode::I32Ctz, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I32Const(128), - Opcode::I32Popcnt, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(24)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(7)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/unary.txt#L29 -#[test] -fn unary_i64() { - let module = module() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(100), - Opcode::I64Eqz, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(0), - Opcode::I64Eqz, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(128), - Opcode::I64Clz, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(128), - Opcode::I64Ctz, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::I64Const(128), - Opcode::I64Popcnt, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(0)); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(56)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(7)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::I64(1)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/unary.txt#L46 -#[test] -fn unary_f32() { - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - let module = module() - .function() - .signature().param().f32().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::GetLocal(0), - Opcode::F32Ne, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x42C80000), // 100 - Opcode::F32Neg, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xC2C80000), // -100 - Opcode::F32Abs, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xC2C80000), // -100 - Opcode::F32Sqrt, - Opcode::Call(0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x42C80000), // 100 - Opcode::F32Sqrt, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xBF400000), // -0.75 - Opcode::F32Ceil, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xBF400000), // -0.75 - Opcode::F32Floor, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0xBF400000), // -0.75 - Opcode::F32Trunc, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x3FA00000), // 1.25 - Opcode::F32Nearest, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F32Const(0x3FE00000), // 1.75 - Opcode::F32Nearest, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(-100.000000)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(100.000000)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(10.000000)); - assert_eq!(module.execute_index(5, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(-0.000000)); - assert_eq!(module.execute_index(6, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(-1.000000)); - assert_eq!(module.execute_index(7, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(-0.000000)); - assert_eq!(module.execute_index(8, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(1.000000)); - assert_eq!(module.execute_index(9, vec![].into()).unwrap().unwrap(), RuntimeValue::F32(2.000000)); -} - -/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/unary.txt#L76 -#[test] -fn unary_f64() { - // f32 && f64 are serialized using binary32 && binary64 formats - // http://babbage.cs.qc.cuny.edu/IEEE-754/ - let module = module() - .function() - .signature().param().f64().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::GetLocal(0), - Opcode::GetLocal(0), - Opcode::F64Ne, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x4059000000000000), // 100 - Opcode::F64Neg, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xC059000000000000), // -100 - Opcode::F64Abs, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xC059000000000000 ), // -100 - Opcode::F64Sqrt, - Opcode::Call(0), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x4059000000000000), // 100 - Opcode::F64Sqrt, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xBFE8000000000000), // -0.75 - Opcode::F64Ceil, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xBFE8000000000000), // -0.75 - Opcode::F64Floor, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0xBFE8000000000000), // -0.75 - Opcode::F64Trunc, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x3FF4000000000000), // 1.25 - Opcode::F64Nearest, - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().f64().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::F64Const(0x3FFC000000000000), // 1.75 - Opcode::F64Nearest, - Opcode::End, - ])).build() - .build() - .build(); - - let program = ProgramInstance::new(); - let module = program.add_module("main", module, None).unwrap(); - assert_eq!(module.execute_index(1, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(-100.000000)); - assert_eq!(module.execute_index(2, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(100.000000)); - assert_eq!(module.execute_index(3, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(1)); - assert_eq!(module.execute_index(4, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(10.000000)); - assert_eq!(module.execute_index(5, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(-0.000000)); - assert_eq!(module.execute_index(6, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(-1.000000)); - assert_eq!(module.execute_index(7, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(-0.000000)); - assert_eq!(module.execute_index(8, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(1.000000)); - assert_eq!(module.execute_index(9, vec![].into()).unwrap().unwrap(), RuntimeValue::F64(2.000000)); -} diff --git a/src/interpreter/tests/wasm.rs b/src/interpreter/tests/wasm.rs deleted file mode 100644 index dc689f6..0000000 --- a/src/interpreter/tests/wasm.rs +++ /dev/null @@ -1,77 +0,0 @@ -use elements::deserialize_file; -use elements::Module; -use interpreter::ExecutionParams; -use interpreter::value::RuntimeValue; -use interpreter::module::{ModuleInstanceInterface, ItemIndex}; -use super::utils::program_with_default_env; - -#[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/cases/v1/inc_i32.wasm"; - - let program = program_with_default_env(); - - let module: Module = - deserialize_file(WASM_FILE).expect("Failed to deserialize module from buffer"); - let i32_val = 42; - // the functions expects a single i32 parameter - let args = vec![RuntimeValue::I32(i32_val)]; - let exp_retval = Some(RuntimeValue::I32(i32_val + 1)); - let execution_params = ExecutionParams::from(args); - - let module_result = program - .add_module("main", module, None); - - let module = module_result - .expect("Failed to initialize module"); - - let retval = module - .execute_export(FUNCTION_NAME, execution_params) - .expect(""); - assert_eq!(exp_retval, retval); -} - -#[test] -fn interpreter_accumulate_u8() { - // Name of function contained in WASM file (note the leading underline) - const FUNCTION_NAME: &'static str = "_accumulate_u8"; - // The WASM file containing the module and function - const WASM_FILE: &str = &"res/cases/v1/accumulate_u8.wasm"; - // The octet sequence being accumulated - const BUF: &[u8] = &[9,8,7,6,5,4,3,2,1]; - - let program = program_with_default_env(); - - // Load the module-structure from wasm-file and add to program - let module: Module = - deserialize_file(WASM_FILE).expect("Failed to deserialize module from buffer"); - let module = program - .add_module("main", module, None) - .expect("Failed to initialize module"); - - // => env module is created - let env_instance = program.module("env").unwrap(); - // => linear memory is created - let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap(); - - // Place the octet-sequence at index 0 in linear memory - let offset: u32 = 0; - let _ = env_memory.set(offset, BUF); - - // Set up the function argument list and invoke the function - let args = vec![RuntimeValue::I32(BUF.len() as i32), RuntimeValue::I32(offset as i32)]; - let execution_params = ExecutionParams::from(args); - let retval = module - .execute_export(FUNCTION_NAME, execution_params) - .expect("Failed to execute function"); - - // For verification, repeat accumulation using native code - let accu = BUF.into_iter().fold(0 as i32, |a, b| a + *b as i32); - let exp_retval: Option = Some(RuntimeValue::I32(accu)); - - // Verify calculation from WebAssembly runtime is identical to expected result - assert_eq!(exp_retval, retval); -} diff --git a/src/interpreter/validator.rs b/src/interpreter/validator.rs deleted file mode 100644 index a6304ab..0000000 --- a/src/interpreter/validator.rs +++ /dev/null @@ -1,809 +0,0 @@ -use std::u32; -use std::sync::Arc; -use std::collections::HashMap; -use elements::{Opcode, BlockType, ValueType}; -use interpreter::Error; -use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; -use interpreter::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, FunctionSignature}; -use common::stack::StackWithLimit; -use interpreter::variable::VariableType; - -/// Constant from wabt' validator.cc to skip alignment validation (not a part of spec). -const NATURAL_ALIGNMENT: u32 = 0xFFFFFFFF; - -/// Function validation context. -pub struct FunctionValidationContext<'a> { - /// Wasm module instance (in process of instantiation). - module_instance: &'a ModuleInstance, - /// Native externals. - externals: Option<&'a HashMap>>, - /// Current instruction position. - position: usize, - /// Local variables. - locals: &'a [ValueType], - /// Value stack. - value_stack: StackWithLimit, - /// Frame stack. - frame_stack: StackWithLimit, - /// Function return type. None if validating expression. - return_type: Option, - /// Labels positions. - labels: HashMap, -} - -/// Value type on the stack. -#[derive(Debug, Clone, Copy)] -pub enum StackValueType { - /// Any value type. - Any, - /// Any number of any values of any type. - AnyUnlimited, - /// Concrete value type. - Specific(ValueType), -} - -/// Control stack frame. -#[derive(Debug, Clone)] -pub struct BlockFrame { - /// Frame type. - pub frame_type: BlockFrameType, - /// A signature, which is a block signature type indicating the number and types of result values of the region. - pub block_type: BlockType, - /// A label for reference to block instruction. - pub begin_position: usize, - /// A label for reference from branch instructions. - pub branch_position: usize, - /// A label for reference from end instructions. - pub end_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. - pub value_stack_len: usize, -} - -/// Type of block frame. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum BlockFrameType { - /// Function frame. - Function, - /// Usual block frame. - Block, - /// Loop frame (branching to the beginning of block). - Loop, - /// True-subblock of if expression. - IfTrue, - /// False-subblock of if expression. - IfFalse, -} - -/// Function validator. -pub struct Validator; - -/// Instruction outcome. -#[derive(Debug, Clone)] -pub enum InstructionOutcome { - /// Continue with next instruction. - ValidateNextInstruction, - /// Unreachable instruction reached. - Unreachable, -} - -impl Validator { - pub fn validate_function(context: &mut FunctionValidationContext, block_type: BlockType, body: &[Opcode]) -> Result<(), Error> { - context.push_label(BlockFrameType::Function, block_type)?; - Validator::validate_function_block(context, body)?; - while !context.frame_stack.is_empty() { - context.pop_label()?; - } - - Ok(()) - } - - fn validate_function_block(context: &mut FunctionValidationContext, body: &[Opcode]) -> Result<(), Error> { - let body_len = body.len(); - if body_len == 0 { - return Err(Error::Validation("Non-empty function body expected".into())); - } - - loop { - let opcode = &body[context.position]; - match Validator::validate_instruction(context, opcode)? { - InstructionOutcome::ValidateNextInstruction => (), - InstructionOutcome::Unreachable => context.unreachable()?, - } - - context.position += 1; - if context.position >= body_len { - return Ok(()); - } - } - } - - fn validate_instruction(context: &mut FunctionValidationContext, opcode: &Opcode) -> Result { - debug!(target: "validator", "validating {:?}", opcode); - match opcode { - &Opcode::Unreachable => Ok(InstructionOutcome::Unreachable), - &Opcode::Nop => Ok(InstructionOutcome::ValidateNextInstruction), - &Opcode::Block(block_type) => Validator::validate_block(context, block_type), - &Opcode::Loop(block_type) => Validator::validate_loop(context, block_type), - &Opcode::If(block_type) => Validator::validate_if(context, block_type), - &Opcode::Else => Validator::validate_else(context), - &Opcode::End => Validator::validate_end(context), - &Opcode::Br(idx) => Validator::validate_br(context, idx), - &Opcode::BrIf(idx) => Validator::validate_br_if(context, idx), - &Opcode::BrTable(ref table, default) => Validator::validate_br_table(context, table, default), - &Opcode::Return => Validator::validate_return(context), - - &Opcode::Call(index) => Validator::validate_call(context, index), - &Opcode::CallIndirect(index, _reserved) => Validator::validate_call_indirect(context, index), - - &Opcode::Drop => Validator::validate_drop(context), - &Opcode::Select => Validator::validate_select(context), - - &Opcode::GetLocal(index) => Validator::validate_get_local(context, index), - &Opcode::SetLocal(index) => Validator::validate_set_local(context, index), - &Opcode::TeeLocal(index) => Validator::validate_tee_local(context, index), - &Opcode::GetGlobal(index) => Validator::validate_get_global(context, index), - &Opcode::SetGlobal(index) => Validator::validate_set_global(context, index), - - &Opcode::I32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::I32.into()), - &Opcode::I64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::I64.into()), - &Opcode::F32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::F32.into()), - &Opcode::F64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::F64.into()), - &Opcode::I32Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I32.into()), - &Opcode::I32Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I32.into()), - &Opcode::I32Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I32.into()), - &Opcode::I32Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I32.into()), - &Opcode::I64Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I64.into()), - &Opcode::I64Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I64.into()), - &Opcode::I64Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I64.into()), - &Opcode::I64Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I64.into()), - &Opcode::I64Load32S(align, _) => Validator::validate_load(context, align, 4, ValueType::I64.into()), - &Opcode::I64Load32U(align, _) => Validator::validate_load(context, align, 4, ValueType::I64.into()), - - &Opcode::I32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::I32.into()), - &Opcode::I64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::I64.into()), - &Opcode::F32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::F32.into()), - &Opcode::F64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::F64.into()), - &Opcode::I32Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I32.into()), - &Opcode::I32Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I32.into()), - &Opcode::I64Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I64.into()), - &Opcode::I64Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I64.into()), - &Opcode::I64Store32(align, _) => Validator::validate_store(context, align, 4, ValueType::I64.into()), - - &Opcode::CurrentMemory(_) => Validator::validate_current_memory(context), - &Opcode::GrowMemory(_) => Validator::validate_grow_memory(context), - - &Opcode::I32Const(_) => Validator::validate_const(context, ValueType::I32.into()), - &Opcode::I64Const(_) => Validator::validate_const(context, ValueType::I64.into()), - &Opcode::F32Const(_) => Validator::validate_const(context, ValueType::F32.into()), - &Opcode::F64Const(_) => Validator::validate_const(context, ValueType::F64.into()), - - &Opcode::I32Eqz => Validator::validate_testop(context, ValueType::I32.into()), - &Opcode::I32Eq => Validator::validate_relop(context, ValueType::I32.into()), - &Opcode::I32Ne => Validator::validate_relop(context, ValueType::I32.into()), - &Opcode::I32LtS => Validator::validate_relop(context, ValueType::I32.into()), - &Opcode::I32LtU => Validator::validate_relop(context, ValueType::I32.into()), - &Opcode::I32GtS => Validator::validate_relop(context, ValueType::I32.into()), - &Opcode::I32GtU => Validator::validate_relop(context, ValueType::I32.into()), - &Opcode::I32LeS => Validator::validate_relop(context, ValueType::I32.into()), - &Opcode::I32LeU => Validator::validate_relop(context, ValueType::I32.into()), - &Opcode::I32GeS => Validator::validate_relop(context, ValueType::I32.into()), - &Opcode::I32GeU => Validator::validate_relop(context, ValueType::I32.into()), - - &Opcode::I64Eqz => Validator::validate_testop(context, ValueType::I64.into()), - &Opcode::I64Eq => Validator::validate_relop(context, ValueType::I64.into()), - &Opcode::I64Ne => Validator::validate_relop(context, ValueType::I64.into()), - &Opcode::I64LtS => Validator::validate_relop(context, ValueType::I64.into()), - &Opcode::I64LtU => Validator::validate_relop(context, ValueType::I64.into()), - &Opcode::I64GtS => Validator::validate_relop(context, ValueType::I64.into()), - &Opcode::I64GtU => Validator::validate_relop(context, ValueType::I64.into()), - &Opcode::I64LeS => Validator::validate_relop(context, ValueType::I64.into()), - &Opcode::I64LeU => Validator::validate_relop(context, ValueType::I64.into()), - &Opcode::I64GeS => Validator::validate_relop(context, ValueType::I64.into()), - &Opcode::I64GeU => Validator::validate_relop(context, ValueType::I64.into()), - - &Opcode::F32Eq => Validator::validate_relop(context, ValueType::F32.into()), - &Opcode::F32Ne => Validator::validate_relop(context, ValueType::F32.into()), - &Opcode::F32Lt => Validator::validate_relop(context, ValueType::F32.into()), - &Opcode::F32Gt => Validator::validate_relop(context, ValueType::F32.into()), - &Opcode::F32Le => Validator::validate_relop(context, ValueType::F32.into()), - &Opcode::F32Ge => Validator::validate_relop(context, ValueType::F32.into()), - - &Opcode::F64Eq => Validator::validate_relop(context, ValueType::F64.into()), - &Opcode::F64Ne => Validator::validate_relop(context, ValueType::F64.into()), - &Opcode::F64Lt => Validator::validate_relop(context, ValueType::F64.into()), - &Opcode::F64Gt => Validator::validate_relop(context, ValueType::F64.into()), - &Opcode::F64Le => Validator::validate_relop(context, ValueType::F64.into()), - &Opcode::F64Ge => Validator::validate_relop(context, ValueType::F64.into()), - - &Opcode::I32Clz => Validator::validate_unop(context, ValueType::I32.into()), - &Opcode::I32Ctz => Validator::validate_unop(context, ValueType::I32.into()), - &Opcode::I32Popcnt => Validator::validate_unop(context, ValueType::I32.into()), - &Opcode::I32Add => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I32Sub => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I32Mul => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I32DivS => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I32DivU => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I32RemS => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I32RemU => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I32And => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I32Or => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I32Xor => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I32Shl => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I32ShrS => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I32ShrU => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I32Rotl => Validator::validate_binop(context, ValueType::I32.into()), - &Opcode::I32Rotr => Validator::validate_binop(context, ValueType::I32.into()), - - &Opcode::I64Clz => Validator::validate_unop(context, ValueType::I64.into()), - &Opcode::I64Ctz => Validator::validate_unop(context, ValueType::I64.into()), - &Opcode::I64Popcnt => Validator::validate_unop(context, ValueType::I64.into()), - &Opcode::I64Add => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::I64Sub => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::I64Mul => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::I64DivS => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::I64DivU => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::I64RemS => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::I64RemU => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::I64And => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::I64Or => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::I64Xor => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::I64Shl => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::I64ShrS => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::I64ShrU => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::I64Rotl => Validator::validate_binop(context, ValueType::I64.into()), - &Opcode::I64Rotr => Validator::validate_binop(context, ValueType::I64.into()), - - &Opcode::F32Abs => Validator::validate_unop(context, ValueType::F32.into()), - &Opcode::F32Neg => Validator::validate_unop(context, ValueType::F32.into()), - &Opcode::F32Ceil => Validator::validate_unop(context, ValueType::F32.into()), - &Opcode::F32Floor => Validator::validate_unop(context, ValueType::F32.into()), - &Opcode::F32Trunc => Validator::validate_unop(context, ValueType::F32.into()), - &Opcode::F32Nearest => Validator::validate_unop(context, ValueType::F32.into()), - &Opcode::F32Sqrt => Validator::validate_unop(context, ValueType::F32.into()), - &Opcode::F32Add => Validator::validate_binop(context, ValueType::F32.into()), - &Opcode::F32Sub => Validator::validate_binop(context, ValueType::F32.into()), - &Opcode::F32Mul => Validator::validate_binop(context, ValueType::F32.into()), - &Opcode::F32Div => Validator::validate_binop(context, ValueType::F32.into()), - &Opcode::F32Min => Validator::validate_binop(context, ValueType::F32.into()), - &Opcode::F32Max => Validator::validate_binop(context, ValueType::F32.into()), - &Opcode::F32Copysign => Validator::validate_binop(context, ValueType::F32.into()), - - &Opcode::F64Abs => Validator::validate_unop(context, ValueType::F64.into()), - &Opcode::F64Neg => Validator::validate_unop(context, ValueType::F64.into()), - &Opcode::F64Ceil => Validator::validate_unop(context, ValueType::F64.into()), - &Opcode::F64Floor => Validator::validate_unop(context, ValueType::F64.into()), - &Opcode::F64Trunc => Validator::validate_unop(context, ValueType::F64.into()), - &Opcode::F64Nearest => Validator::validate_unop(context, ValueType::F64.into()), - &Opcode::F64Sqrt => Validator::validate_unop(context, ValueType::F64.into()), - &Opcode::F64Add => Validator::validate_binop(context, ValueType::F64.into()), - &Opcode::F64Sub => Validator::validate_binop(context, ValueType::F64.into()), - &Opcode::F64Mul => Validator::validate_binop(context, ValueType::F64.into()), - &Opcode::F64Div => Validator::validate_binop(context, ValueType::F64.into()), - &Opcode::F64Min => Validator::validate_binop(context, ValueType::F64.into()), - &Opcode::F64Max => Validator::validate_binop(context, ValueType::F64.into()), - &Opcode::F64Copysign => Validator::validate_binop(context, ValueType::F64.into()), - - &Opcode::I32WrapI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::I32.into()), - &Opcode::I32TruncSF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()), - &Opcode::I32TruncUF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()), - &Opcode::I32TruncSF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I32.into()), - &Opcode::I32TruncUF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I32.into()), - &Opcode::I64ExtendSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::I64.into()), - &Opcode::I64ExtendUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::I64.into()), - &Opcode::I64TruncSF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I64.into()), - &Opcode::I64TruncUF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I64.into()), - &Opcode::I64TruncSF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()), - &Opcode::I64TruncUF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()), - &Opcode::F32ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()), - &Opcode::F32ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()), - &Opcode::F32ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F32.into()), - &Opcode::F32ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F32.into()), - &Opcode::F32DemoteF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::F32.into()), - &Opcode::F64ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F64.into()), - &Opcode::F64ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F64.into()), - &Opcode::F64ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()), - &Opcode::F64ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()), - &Opcode::F64PromoteF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::F64.into()), - - &Opcode::I32ReinterpretF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()), - &Opcode::I64ReinterpretF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()), - &Opcode::F32ReinterpretI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()), - &Opcode::F64ReinterpretI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()), - } - } - - fn validate_const(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result { - context.push_value(value_type)?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_unop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result { - context.pop_value(value_type)?; - context.push_value(value_type)?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_binop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result { - context.pop_value(value_type)?; - context.pop_value(value_type)?; - context.push_value(value_type)?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_testop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result { - context.pop_value(value_type)?; - context.push_value(ValueType::I32.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_relop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result { - context.pop_value(value_type)?; - context.pop_value(value_type)?; - context.push_value(ValueType::I32.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_cvtop(context: &mut FunctionValidationContext, value_type1: StackValueType, value_type2: StackValueType) -> Result { - context.pop_value(value_type1)?; - context.push_value(value_type2)?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_drop(context: &mut FunctionValidationContext) -> Result { - context.pop_any_value().map(|_| ())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_select(context: &mut FunctionValidationContext) -> Result { - context.pop_value(ValueType::I32.into())?; - let select_type = context.pop_any_value()?; - context.pop_value(select_type)?; - context.push_value(select_type)?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_get_local(context: &mut FunctionValidationContext, index: u32) -> Result { - let local_type = context.require_local(index)?; - context.push_value(local_type)?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_set_local(context: &mut FunctionValidationContext, index: u32) -> Result { - let local_type = context.require_local(index)?; - let value_type = context.pop_any_value()?; - if local_type != value_type { - return Err(Error::Validation(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type))); - } - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_tee_local(context: &mut FunctionValidationContext, index: u32) -> Result { - let local_type = context.require_local(index)?; - let value_type = context.tee_any_value()?; - if local_type != value_type { - return Err(Error::Validation(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type))); - } - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_get_global(context: &mut FunctionValidationContext, index: u32) -> Result { - let global_type = context.require_global(index, None)?; - context.push_value(global_type)?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_set_global(context: &mut FunctionValidationContext, index: u32) -> Result { - let global_type = context.require_global(index, Some(true))?; - let value_type = context.pop_any_value()?; - if global_type != value_type { - return Err(Error::Validation(format!("Trying to update global {} of type {:?} with value of type {:?}", index, global_type, value_type))); - } - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_load(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result { - if align != NATURAL_ALIGNMENT { - if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align { - return Err(Error::Validation(format!("Too large memory alignment 2^{} (expected at most {})", align, max_align))); - } - } - - context.pop_value(ValueType::I32.into())?; - context.require_memory(DEFAULT_MEMORY_INDEX)?; - context.push_value(value_type)?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_store(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result { - if align != NATURAL_ALIGNMENT { - if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align { - return Err(Error::Validation(format!("Too large memory alignment 2^{} (expected at most {})", align, max_align))); - } - } - - context.require_memory(DEFAULT_MEMORY_INDEX)?; - context.pop_value(value_type)?; - context.pop_value(ValueType::I32.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_block(context: &mut FunctionValidationContext, block_type: BlockType) -> Result { - context.push_label(BlockFrameType::Block, block_type).map(|_| InstructionOutcome::ValidateNextInstruction) - } - - fn validate_loop(context: &mut FunctionValidationContext, block_type: BlockType) -> Result { - context.push_label(BlockFrameType::Loop, block_type).map(|_| InstructionOutcome::ValidateNextInstruction) - } - - fn validate_if(context: &mut FunctionValidationContext, block_type: BlockType) -> Result { - context.pop_value(ValueType::I32.into())?; - context.push_label(BlockFrameType::IfTrue, block_type).map(|_| InstructionOutcome::ValidateNextInstruction) - } - - fn validate_else(context: &mut FunctionValidationContext) -> Result { - let block_type = { - let top_frame = context.top_label()?; - if top_frame.frame_type != BlockFrameType::IfTrue { - return Err(Error::Validation("Misplaced else instruction".into())); - } - top_frame.block_type - }; - context.pop_label()?; - - if let BlockType::Value(value_type) = block_type { - context.pop_value(value_type.into())?; - } - context.push_label(BlockFrameType::IfFalse, block_type).map(|_| InstructionOutcome::ValidateNextInstruction) - } - - fn validate_end(context: &mut FunctionValidationContext) -> Result { - { - let top_frame = context.top_label()?; - if top_frame.frame_type == BlockFrameType::IfTrue { - if top_frame.block_type != BlockType::NoResult { - return Err(Error::Validation(format!("If block without else required to have NoResult block type. But it have {:?} type", top_frame.block_type))); - } - } - } - - context.pop_label().map(|_| InstructionOutcome::ValidateNextInstruction) - } - - fn validate_br(context: &mut FunctionValidationContext, idx: u32) -> Result { - let (frame_type, frame_block_type) = { - let frame = context.require_label(idx)?; - (frame.frame_type, frame.block_type) - }; - if frame_type != BlockFrameType::Loop { - if let BlockType::Value(value_type) = frame_block_type { - context.tee_value(value_type.into())?; - } - } - Ok(InstructionOutcome::Unreachable) - } - - fn validate_br_if(context: &mut FunctionValidationContext, idx: u32) -> Result { - context.pop_value(ValueType::I32.into())?; - - let (frame_type, frame_block_type) = { - let frame = context.require_label(idx)?; - (frame.frame_type, frame.block_type) - }; - if frame_type != BlockFrameType::Loop { - if let BlockType::Value(value_type) = frame_block_type { - context.tee_value(value_type.into())?; - } - } - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_br_table(context: &mut FunctionValidationContext, table: &[u32], default: u32) -> Result { - let mut required_block_type = None; - - { - let default_block = context.require_label(default)?; - if default_block.frame_type != BlockFrameType::Loop { - required_block_type = Some(default_block.block_type); - } - - for label in table { - let label_block = context.require_label(*label)?; - if label_block.frame_type != BlockFrameType::Loop { - if let Some(required_block_type) = required_block_type { - if required_block_type != label_block.block_type { - return Err(Error::Validation(format!("Labels in br_table points to block of different types: {:?} and {:?}", required_block_type, label_block.block_type))); - } - } - required_block_type = Some(label_block.block_type); - } - } - } - - context.pop_value(ValueType::I32.into())?; - if let Some(required_block_type) = required_block_type { - if let BlockType::Value(value_type) = required_block_type { - context.tee_value(value_type.into())?; - } - } - - Ok(InstructionOutcome::Unreachable) - } - - fn validate_return(context: &mut FunctionValidationContext) -> Result { - if let BlockType::Value(value_type) = context.return_type()? { - context.tee_value(value_type.into())?; - } - Ok(InstructionOutcome::Unreachable) - } - - fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result { - let (argument_types, return_type) = context.require_function(idx)?; - for argument_type in argument_types.iter().rev() { - context.pop_value((*argument_type).into())?; - } - if let BlockType::Value(value_type) = return_type { - context.push_value(value_type.into())?; - } - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_call_indirect(context: &mut FunctionValidationContext, idx: u32) -> Result { - context.require_table(DEFAULT_TABLE_INDEX, VariableType::AnyFunc)?; - - context.pop_value(ValueType::I32.into())?; - let (argument_types, return_type) = context.require_function_type(idx)?; - for argument_type in argument_types.iter().rev() { - context.pop_value((*argument_type).into())?; - } - if let BlockType::Value(value_type) = return_type { - context.push_value(value_type.into())?; - } - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_current_memory(context: &mut FunctionValidationContext) -> Result { - context.require_memory(DEFAULT_MEMORY_INDEX)?; - context.push_value(ValueType::I32.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result { - context.require_memory(DEFAULT_MEMORY_INDEX)?; - context.pop_value(ValueType::I32.into())?; - context.push_value(ValueType::I32.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } -} - -impl<'a> FunctionValidationContext<'a> { - pub fn new( - module_instance: &'a ModuleInstance, - externals: Option<&'a HashMap>>, - locals: &'a [ValueType], - value_stack_limit: usize, - frame_stack_limit: usize, - function: FunctionSignature, - ) -> Self { - FunctionValidationContext { - module_instance: module_instance, - externals: externals, - position: 0, - locals: locals, - value_stack: StackWithLimit::with_limit(value_stack_limit), - frame_stack: StackWithLimit::with_limit(frame_stack_limit), - return_type: Some(function.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult)), - labels: HashMap::new(), - } - } - - pub fn push_value(&mut self, value_type: StackValueType) -> Result<(), Error> { - Ok(self.value_stack.push(value_type.into())?) - } - - pub fn pop_value(&mut self, value_type: StackValueType) -> Result<(), Error> { - self.check_stack_access()?; - match self.value_stack.pop()? { - StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()), - StackValueType::Any => Ok(()), - StackValueType::AnyUnlimited => { - self.value_stack.push(StackValueType::AnyUnlimited)?; - Ok(()) - }, - stack_value_type @ _ => Err(Error::Validation(format!("Expected value of type {:?} on top of stack. Got {:?}", value_type, stack_value_type))), - } - } - - pub fn tee_value(&mut self, value_type: StackValueType) -> Result<(), Error> { - self.check_stack_access()?; - match *self.value_stack.top()? { - StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()), - StackValueType::Any | StackValueType::AnyUnlimited => Ok(()), - stack_value_type @ _ => Err(Error::Validation(format!("Expected value of type {:?} on top of stack. Got {:?}", value_type, stack_value_type))), - } - } - - pub fn pop_any_value(&mut self) -> Result { - self.check_stack_access()?; - match self.value_stack.pop()? { - StackValueType::Specific(stack_value_type) => Ok(StackValueType::Specific(stack_value_type)), - StackValueType::Any => Ok(StackValueType::Any), - StackValueType::AnyUnlimited => { - self.value_stack.push(StackValueType::AnyUnlimited)?; - Ok(StackValueType::Any) - }, - } - } - - pub fn tee_any_value(&mut self) -> Result { - self.check_stack_access()?; - Ok(self.value_stack.top().map(Clone::clone)?) - } - - pub fn unreachable(&mut self) -> Result<(), Error> { - Ok(self.value_stack.push(StackValueType::AnyUnlimited)?) - } - - pub fn top_label(&self) -> Result<&BlockFrame, Error> { - Ok(self.frame_stack.top()?) - } - - pub fn push_label(&mut self, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), Error> { - Ok(self.frame_stack.push(BlockFrame { - frame_type: frame_type, - block_type: block_type, - begin_position: self.position, - branch_position: self.position, - end_position: self.position, - value_stack_len: self.value_stack.len(), - })?) - } - - pub fn pop_label(&mut self) -> Result { - let frame = self.frame_stack.pop()?; - let actual_value_type = if self.value_stack.len() > frame.value_stack_len { - Some(self.value_stack.pop()?) - } else { - None - }; - self.value_stack.resize(frame.value_stack_len, StackValueType::Any); - - match frame.block_type { - BlockType::NoResult if actual_value_type.map(|vt| vt.is_any_unlimited()).unwrap_or(true) => (), - BlockType::Value(required_value_type) if actual_value_type.map(|vt| vt == required_value_type).unwrap_or(false) => (), - _ => return Err(Error::Validation(format!("Expected block to return {:?} while it has returned {:?}", frame.block_type, actual_value_type))), - } - if !self.frame_stack.is_empty() { - self.labels.insert(frame.begin_position, self.position); - } - if let BlockType::Value(value_type) = frame.block_type { - self.push_value(value_type.into())?; - } - - Ok(InstructionOutcome::ValidateNextInstruction) - } - - pub fn require_label(&self, idx: u32) -> Result<&BlockFrame, Error> { - Ok(self.frame_stack.get(idx as usize)?) - } - - pub fn return_type(&self) -> Result { - self.return_type.ok_or(Error::Validation("Trying to return from expression".into())) - } - - pub fn require_local(&self, idx: u32) -> Result { - self.locals.get(idx as usize) - .cloned() - .map(Into::into) - .ok_or(Error::Validation(format!("Trying to access local with index {} when there are only {} locals", idx, self.locals.len()))) - } - - pub fn require_global(&self, idx: u32, mutability: Option) -> Result { - self.module_instance - .global(ItemIndex::IndexSpace(idx), None, self.externals.clone()) - .and_then(|g| match mutability { - Some(true) if !g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be mutable", idx))), - Some(false) if g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be immutable", idx))), - _ => match g.variable_type() { - VariableType::AnyFunc => Err(Error::Validation(format!("Expected global {} to have non-AnyFunc type", idx))), - VariableType::I32 => Ok(StackValueType::Specific(ValueType::I32)), - VariableType::I64 => Ok(StackValueType::Specific(ValueType::I64)), - VariableType::F32 => Ok(StackValueType::Specific(ValueType::F32)), - VariableType::F64 => Ok(StackValueType::Specific(ValueType::F64)), - } - }) - } - - pub fn require_memory(&self, idx: u32) -> Result<(), Error> { - self.module_instance - .memory(ItemIndex::IndexSpace(idx)) - .map(|_| ()) - } - - pub fn require_table(&self, idx: u32, variable_type: VariableType) -> Result<(), Error> { - self.module_instance - .table(ItemIndex::IndexSpace(idx)) - .and_then(|t| if t.variable_type() == variable_type { - Ok(()) - } else { - Err(Error::Validation(format!("Table {} has element type {:?} while {:?} expected", idx, t.variable_type(), variable_type))) - }) - } - - pub fn require_function(&self, idx: u32) -> Result<(Vec, BlockType), Error> { - self.module_instance.function_type(ItemIndex::IndexSpace(idx)) - .map(|ft| (ft.params().to_vec(), ft.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult))) - } - - pub fn require_function_type(&self, idx: u32) -> Result<(Vec, BlockType), Error> { - self.module_instance.function_type_by_index(idx) - .map(|ft| (ft.params().to_vec(), ft.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult))) - } - - pub fn function_labels(self) -> HashMap { - self.labels - } - - fn check_stack_access(&self) -> Result<(), Error> { - let value_stack_min = self.frame_stack.top().expect("at least 1 topmost block").value_stack_len; - if self.value_stack.len() > value_stack_min { - Ok(()) - } else { - Err(Error::Validation("Trying to access parent frame stack values.".into())) - } - } -} - -impl StackValueType { - pub fn is_any(&self) -> bool { - match self { - &StackValueType::Any => true, - _ => false, - } - } - - pub fn is_any_unlimited(&self) -> bool { - match self { - &StackValueType::AnyUnlimited => true, - _ => false, - } - } - - pub fn value_type(&self) -> ValueType { - match self { - &StackValueType::Any | &StackValueType::AnyUnlimited => 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) - } -} - -impl PartialEq for StackValueType { - fn eq(&self, other: &StackValueType) -> bool { - if self.is_any() || other.is_any() || self.is_any_unlimited() || other.is_any_unlimited() { - true - } else { - self.value_type() == other.value_type() - } - } -} - -impl PartialEq for StackValueType { - fn eq(&self, other: &ValueType) -> bool { - if self.is_any() || self.is_any_unlimited() { - true - } else { - self.value_type() == *other - } - } -} - -impl PartialEq for ValueType { - fn eq(&self, other: &StackValueType) -> bool { - other == self - } -} diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs deleted file mode 100644 index d9dcbf3..0000000 --- a/src/interpreter/value.rs +++ /dev/null @@ -1,664 +0,0 @@ -use std::{i32, i64, u32, u64, f32}; -use std::io; -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use interpreter::Error; -use interpreter::variable::VariableType; - -/// Runtime value. -#[derive(Debug, Clone, PartialEq)] -pub enum RuntimeValue { - /// Null value. - Null, - /// Reference to the function in the given module' function index space. - AnyFunc(String, u32), - /// 32b-length signed/unsigned int. - I32(i32), - /// 64b-length signed/unsigned int. - I64(i64), - /// 32b-length float. - F32(f32), - /// 64b-length float. - F64(f64), -} - -/// Try to convert into trait. -pub trait TryInto { - /// Try to convert self into other value. - fn try_into(self) -> Result; -} - -/// 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 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 extending with leading zeroes. -pub trait ExtendInto { - /// 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; -} - -/// Convert from and to little endian. -pub trait LittleEndianConvert where Self: Sized { - /// Convert to little endian buffer. - fn into_little_endian(self) -> Vec; - /// Convert from little endian buffer. - fn from_little_endian(buffer: Vec) -> 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; -} - -/// 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; -} - -/// 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; -} - -impl RuntimeValue { - /// Creates new default value of given type. - pub fn default(variable_type: VariableType) -> Self { - match variable_type { - VariableType::AnyFunc => RuntimeValue::AnyFunc("".into(), 0), - VariableType::I32 => RuntimeValue::I32(0), - VariableType::I64 => RuntimeValue::I64(0), - VariableType::F32 => RuntimeValue::F32(0f32), - VariableType::F64 => RuntimeValue::F64(0f64), - } - } - - /// 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)) - } - - /// Returns true if value is null. - pub fn is_null(&self) -> bool { - match *self { - RuntimeValue::Null => true, - _ => false, - } - } - - /// Get variable type for this value. - pub fn variable_type(&self) -> Option { - match *self { - RuntimeValue::Null => None, - RuntimeValue::AnyFunc(_, _) => Some(VariableType::AnyFunc), - RuntimeValue::I32(_) => Some(VariableType::I32), - RuntimeValue::I64(_) => Some(VariableType::I64), - RuntimeValue::F32(_) => Some(VariableType::F32), - RuntimeValue::F64(_) => Some(VariableType::F64), - } - } -} - -impl From for RuntimeValue { - fn from(val: i32) -> Self { - RuntimeValue::I32(val) - } -} - -impl From for RuntimeValue { - fn from(val: i64) -> Self { - RuntimeValue::I64(val) - } -} - -impl From for RuntimeValue { - fn from(val: f32) -> Self { - RuntimeValue::F32(val) - } -} - -impl From for RuntimeValue { - fn from(val: f64) -> Self { - RuntimeValue::F64(val) - } -} - -impl TryInto for RuntimeValue { - fn try_into(self) -> Result { - match self { - RuntimeValue::I32(val) => Ok(val != 0), - _ => Err(Error::Value(format!("32-bit int value expected"))), - } - } -} - -impl TryInto for RuntimeValue { - fn try_into(self) -> Result { - match self { - RuntimeValue::I32(val) => Ok(val), - _ => Err(Error::Value(format!("32-bit int value expected"))), - } - } -} - -impl TryInto for RuntimeValue { - fn try_into(self) -> Result { - match self { - RuntimeValue::I64(val) => Ok(val), - _ => Err(Error::Value(format!("64-bit int value expected"))), - } - } -} - -impl TryInto for RuntimeValue { - fn try_into(self) -> Result { - match self { - RuntimeValue::F32(val) => Ok(val), - _ => Err(Error::Value(format!("32-bit float value expected"))), - } - } -} - -impl TryInto for RuntimeValue { - fn try_into(self) -> Result { - match self { - RuntimeValue::F64(val) => Ok(val), - _ => Err(Error::Value(format!("64-bit float value expected"))), - } - } -} - -impl TryInto for RuntimeValue { - fn try_into(self) -> Result { - match self { - RuntimeValue::I32(val) => Ok(val as u32), - _ => Err(Error::Value(format!("32-bit int value expected"))), - } - } -} - -impl TryInto for RuntimeValue { - fn try_into(self) -> Result { - match self { - RuntimeValue::I64(val) => Ok(val as u64), - _ => Err(Error::Value(format!("64-bit int value expected"))), - } - } -} - -macro_rules! impl_wrap_into { - ($from: ident, $into: ident) => { - impl WrapInto<$into> for $from { - fn wrap_into(self) -> $into { - self as $into - } - } - } -} - -impl_wrap_into!(i32, i8); -impl_wrap_into!(i32, i16); -impl_wrap_into!(i64, i8); -impl_wrap_into!(i64, i16); -impl_wrap_into!(i64, i32); -impl_wrap_into!(i64, f32); -impl_wrap_into!(u64, f32); -// Casting from an f64 to an f32 will produce the closest possible value (rounding strategy unspecified) -// NOTE: currently this will cause Undefined Behavior if the value is finite but larger or smaller than the -// largest or smallest finite value representable by f32. This is a bug and will be fixed. -impl_wrap_into!(f64, f32); - -macro_rules! impl_try_truncate_into { - ($from: ident, $into: ident) => { - impl TryTruncateInto<$into, Error> for $from { - fn try_truncate_into(self) -> Result<$into, Error> { - // 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(Error::Value("invalid float value for this operation".into())); - } - - // range check - let result = self as $into; - if result as $from != self.trunc() { - return Err(Error::Value("invalid float value for this operation".into())); - } - - Ok(self as $into) - } - } - } -} - -impl_try_truncate_into!(f32, i32); -impl_try_truncate_into!(f32, i64); -impl_try_truncate_into!(f64, i32); -impl_try_truncate_into!(f64, i64); -impl_try_truncate_into!(f32, u32); -impl_try_truncate_into!(f32, u64); -impl_try_truncate_into!(f64, u32); -impl_try_truncate_into!(f64, u64); - -macro_rules! impl_extend_into { - ($from: ident, $into: ident) => { - impl ExtendInto<$into> for $from { - fn extend_into(self) -> $into { - self as $into - } - } - } -} - -impl_extend_into!(i8, i32); -impl_extend_into!(u8, i32); -impl_extend_into!(i16, i32); -impl_extend_into!(u16, i32); -impl_extend_into!(i8, i64); -impl_extend_into!(u8, i64); -impl_extend_into!(i16, i64); -impl_extend_into!(u16, i64); -impl_extend_into!(i32, i64); -impl_extend_into!(u32, i64); -impl_extend_into!(u32, u64); -impl_extend_into!(i32, f32); -impl_extend_into!(i32, f64); -impl_extend_into!(u32, f32); -impl_extend_into!(u32, f64); -impl_extend_into!(i64, f64); -impl_extend_into!(u64, f64); -impl_extend_into!(f32, f64); - -macro_rules! impl_transmute_into_self { - ($type: ident) => { - impl TransmuteInto<$type> for $type { - fn transmute_into(self) -> $type { - self - } - } - } -} - -impl_transmute_into_self!(i32); -impl_transmute_into_self!(i64); -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 - } - } - } -} - -impl_transmute_into_as!(i8, u8); -impl_transmute_into_as!(u8, i8); -impl_transmute_into_as!(i32, u32); -impl_transmute_into_as!(u32, i32); -impl_transmute_into_as!(i64, u64); -impl_transmute_into_as!(u64, i64); - -// TODO: rewrite these safely when `f32/f32::to_bits/from_bits` stabilized. -impl TransmuteInto for f32 { - fn transmute_into(self) -> i32 { unsafe { ::std::mem::transmute(self) } } -} - -impl TransmuteInto for f64 { - fn transmute_into(self) -> i64 { unsafe { ::std::mem::transmute(self) } } -} - -impl TransmuteInto for i32 { - fn transmute_into(self) -> f32 { f32_from_bits(self as _) } -} - -impl TransmuteInto for i64 { - fn transmute_into(self) -> f64 { f64_from_bits(self as _) } -} - -impl LittleEndianConvert for i8 { - fn into_little_endian(self) -> Vec { - vec![self as u8] - } - - fn from_little_endian(buffer: Vec) -> Result { - buffer.get(0) - .map(|v| *v as i8) - .ok_or(Error::Value("invalid little endian buffer".into())) - } -} - -impl LittleEndianConvert for u8 { - fn into_little_endian(self) -> Vec { - vec![self] - } - - fn from_little_endian(buffer: Vec) -> Result { - buffer.get(0) - .cloned() - .ok_or(Error::Value("invalid little endian buffer".into())) - } -} - -impl LittleEndianConvert for i16 { - fn into_little_endian(self) -> Vec { - let mut vec = Vec::with_capacity(2); - vec.write_i16::(self) - .expect("i16 is written without any errors"); - vec - } - - fn from_little_endian(buffer: Vec) -> Result { - io::Cursor::new(buffer).read_i16::() - .map_err(|e| Error::Value(e.to_string())) - } -} - -impl LittleEndianConvert for u16 { - fn into_little_endian(self) -> Vec { - let mut vec = Vec::with_capacity(2); - vec.write_u16::(self) - .expect("u16 is written without any errors"); - vec - } - - fn from_little_endian(buffer: Vec) -> Result { - io::Cursor::new(buffer).read_u16::() - .map_err(|e| Error::Value(e.to_string())) - } -} - -impl LittleEndianConvert for i32 { - fn into_little_endian(self) -> Vec { - let mut vec = Vec::with_capacity(4); - vec.write_i32::(self) - .expect("i32 is written without any errors"); - vec - } - - fn from_little_endian(buffer: Vec) -> Result { - io::Cursor::new(buffer).read_i32::() - .map_err(|e| Error::Value(e.to_string())) - } -} - -impl LittleEndianConvert for u32 { - fn into_little_endian(self) -> Vec { - let mut vec = Vec::with_capacity(4); - vec.write_u32::(self) - .expect("u32 is written without any errors"); - vec - } - - fn from_little_endian(buffer: Vec) -> Result { - io::Cursor::new(buffer).read_u32::() - .map_err(|e| Error::Value(e.to_string())) - } -} - -impl LittleEndianConvert for i64 { - fn into_little_endian(self) -> Vec { - let mut vec = Vec::with_capacity(8); - vec.write_i64::(self) - .expect("i64 is written without any errors"); - vec - } - - fn from_little_endian(buffer: Vec) -> Result { - io::Cursor::new(buffer).read_i64::() - .map_err(|e| Error::Value(e.to_string())) - } -} - -impl LittleEndianConvert for f32 { - fn into_little_endian(self) -> Vec { - let mut vec = Vec::with_capacity(4); - vec.write_f32::(self) - .expect("f32 is written without any errors"); - vec - } - - fn from_little_endian(buffer: Vec) -> Result { - io::Cursor::new(buffer).read_u32::() - .map(f32_from_bits) - .map_err(|e| Error::Value(e.to_string())) - } -} - -impl LittleEndianConvert for f64 { - fn into_little_endian(self) -> Vec { - let mut vec = Vec::with_capacity(8); - vec.write_f64::(self) - .expect("i64 is written without any errors"); - vec - } - - fn from_little_endian(buffer: Vec) -> Result { - io::Cursor::new(buffer).read_u64::() - .map(f64_from_bits) - .map_err(|e| Error::Value(e.to_string())) - } -} - -// Convert u32 to f32 safely, masking out sNAN -fn f32_from_bits(mut v: u32) -> f32 { - const EXP_MASK: u32 = 0x7F800000; - const QNAN_MASK: u32 = 0x00400000; - const FRACT_MASK: u32 = 0x007FFFFF; - - if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 { - // If we have a NaN value, we - // convert signaling NaN values to quiet NaN - // by setting the the highest bit of the fraction - // TODO: remove when https://github.com/BurntSushi/byteorder/issues/71 closed. - // or `f32::from_bits` stabilized. - v |= QNAN_MASK; - } - - unsafe { ::std::mem::transmute(v) } -} - -// Convert u64 to f64 safely, masking out sNAN -fn f64_from_bits(mut v: u64) -> f64 { - const EXP_MASK: u64 = 0x7FF0000000000000; - const QNAN_MASK: u64 = 0x0001000000000000; - const FRACT_MASK: u64 = 0x000FFFFFFFFFFFFF; - - if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 { - // If we have a NaN value, we - // convert signaling NaN values to quiet NaN - // by setting the the highest bit of the fraction - // TODO: remove when https://github.com/BurntSushi/byteorder/issues/71 closed. - // or `f64::from_bits` stabilized. - v |= QNAN_MASK; - } - - unsafe { ::std::mem::transmute(v) } -} - -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, Error> { - if other == 0 { Err(Error::Value("Division by zero".to_owned())) } - else { - let (result, overflow) = self.overflowing_div(other); - if overflow { - return Err(Error::Value("Attempt to divide with overflow".to_owned())); - } else { - Ok(result) - } - } - } - } - } -} - -impl_integer_arithmetic_ops!(i32); -impl_integer_arithmetic_ops!(u32); -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, Error> { Ok(self / other) } - } - } -} - -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, Error> { - if other == 0 { Err(Error::Value("Division by zero".to_owned())) } - else { Ok(self.wrapping_rem(other)) } - } - } - } -} - -impl_integer!(i32); -impl_integer!(u32); -impl_integer!(i64); -impl_integer!(u64); - -macro_rules! impl_float { - ($type: ident, $int_type: ident) => { - impl Float<$type> for $type { - fn abs(self) -> $type { self.abs() } - fn floor(self) -> $type { self.floor() } - fn ceil(self) -> $type { self.ceil() } - fn trunc(self) -> $type { self.trunc() } - fn round(self) -> $type { self.round() } - fn nearest(self) -> $type { - let round = self.round(); - if self.fract().abs() != 0.5 { - return round; - } - - use std::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 { self.sqrt() } - // This instruction corresponds to what is sometimes called "minNaN" in other languages. - fn min(self, other: $type) -> $type { - if self.is_nan() || other.is_nan() { - use std::$type; - return $type::NAN; - } - - 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() || other.is_nan() { - use std::$type; - return $type::NAN; - } - - self.max(other) - } - fn copysign(self, other: $type) -> $type { - use std::mem::size_of; - - if self.is_nan() { - return self; - } - - let sign_mask: $int_type = 1 << ((size_of::<$int_type>() << 3) - 1); - let self_int: $int_type = self.transmute_into(); - let other_int: $int_type = 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, i32); -impl_float!(f64, i64); diff --git a/src/interpreter/variable.rs b/src/interpreter/variable.rs deleted file mode 100644 index 7cd32ce..0000000 --- a/src/interpreter/variable.rs +++ /dev/null @@ -1,163 +0,0 @@ -use std::fmt; -use parking_lot::RwLock; -use elements::{GlobalType, ValueType, TableElementType}; -use interpreter::Error; -use interpreter::value::RuntimeValue; - -/// Variable type. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum VariableType { - /// Any func value. - AnyFunc, - /// i32 value. - I32, - /// i64 value. - I64, - /// f32 value. - F32, - /// f64 value. - F64, -} - -/// Externally stored variable value. -/// -/// WebAssembly specificaiton [requires][0] that if a global variable is immutable, then -/// it should remain unchanged. To comply with specification you should ensure this invariant holds. -/// -/// [0]: https://webassembly.github.io/spec/appendix/properties.html#global-instance -pub trait ExternalVariableValue { - /// Get variable value. - fn get(&self) -> RuntimeValue; - - /// Set variable value. - fn set(&mut self, value: RuntimeValue) -> Result<(), Error>; -} - -/// Variable instance. -#[derive(Debug)] -pub struct VariableInstance { - /// Is mutable? - is_mutable: bool, - /// Variable type. - variable_type: VariableType, - /// Global value. - value: RwLock, -} - -/// Enum variable value. -enum VariableValue { - /// Internal value. - Internal(RuntimeValue), - /// External value. - External(Box), -} - -impl VariableInstance { - /// New variable instance - pub fn new(is_mutable: bool, variable_type: VariableType, value: RuntimeValue) -> Result { - // TODO: there is nothing about null value in specification + there is nothing about initializing missing table elements? => runtime check for nulls - if !value.is_null() && value.variable_type() != Some(variable_type) { - return Err(Error::Variable(format!("trying to initialize variable of type {:?} with value of type {:?}", variable_type, value.variable_type()))); - } - - Ok(VariableInstance { - is_mutable: is_mutable, - variable_type: variable_type, - value: RwLock::new(VariableValue::Internal(value)), - }) - } - - /// New global variable - pub fn new_global(global_type: &GlobalType, value: RuntimeValue) -> Result { - Self::new(global_type.is_mutable(), global_type.content_type().into(), value) - } - - /// New global with externally stored value. - pub fn new_external_global(is_mutable: bool, variable_type: VariableType, value: Box) -> Result { - // TODO: there is nothing about null value in specification + there is nothing about initializing missing table elements? => runtime check for nulls - let current_value = value.get(); - if !current_value.is_null() && current_value.variable_type() != Some(variable_type) { - return Err(Error::Variable(format!("trying to initialize variable of type {:?} with value of type {:?}", variable_type, current_value.variable_type()))); - } - - Ok(VariableInstance { - is_mutable: is_mutable, - variable_type: variable_type, - value: RwLock::new(VariableValue::External(value)), - }) - } - - /// Is mutable - pub fn is_mutable(&self) -> bool { - self.is_mutable - } - - /// Get variable type. - pub fn variable_type(&self) -> VariableType { - self.variable_type - } - - /// Get the value of the variable instance - pub fn get(&self) -> RuntimeValue { - self.value.read().get() - } - - /// Set the value of the variable instance - pub fn set(&self, value: RuntimeValue) -> Result<(), Error> { - if !self.is_mutable { - return Err(Error::Variable("trying to update immutable variable".into())); - } - if value.variable_type() != Some(self.variable_type) { - return Err(Error::Variable(format!("trying to update variable of type {:?} with value of type {:?}", self.variable_type, value.variable_type()))); - } - - self.value.write().set(value) - } -} - -impl VariableValue { - fn get(&self) -> RuntimeValue { - match *self { - VariableValue::Internal(ref value) => value.clone(), - VariableValue::External(ref value) => value.get(), - } - } - - fn set(&mut self, new_value: RuntimeValue) -> Result<(), Error> { - match *self { - VariableValue::Internal(ref mut value) => { - *value = new_value; - Ok(()) - }, - VariableValue::External(ref mut value) => value.set(new_value), - } - } -} - -impl From for VariableType { - fn from(vt: ValueType) -> VariableType { - match vt { - ValueType::I32 => VariableType::I32, - ValueType::I64 => VariableType::I64, - ValueType::F32 => VariableType::F32, - ValueType::F64 => VariableType::F64, - } - } -} - -impl From for VariableType { - fn from(tt: TableElementType) -> VariableType { - match tt { - TableElementType::AnyFunc => VariableType::AnyFunc, - } - } -} - -impl fmt::Debug for VariableValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - VariableValue::Internal(ref value) => write!(f, "Variable.Internal({:?})", value), - VariableValue::External(ref value) => write!(f, "Variable.External({:?})", value.get()), - } - } -} diff --git a/src/lib.rs b/src/lib.rs index b829b3c..5f06a63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,16 +2,10 @@ #![warn(missing_docs)] -#[macro_use] -extern crate log; extern crate byteorder; -extern crate parking_lot; pub mod elements; pub mod builder; -pub mod interpreter; -mod validation; -mod common; pub use elements::{ Error as SerializationError, @@ -22,10 +16,3 @@ pub use elements::{ peek_size, }; -#[allow(deprecated)] -pub use interpreter::{ - ProgramInstance, - ModuleInstance, - ModuleInstanceInterface, - RuntimeValue, -}; diff --git a/src/validation/context.rs b/src/validation/context.rs deleted file mode 100644 index 23b7aa1..0000000 --- a/src/validation/context.rs +++ /dev/null @@ -1,82 +0,0 @@ -use elements::{MemoryType, TableType, GlobalType, Type}; -use elements::{BlockType, 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, -} - -impl ModuleContext { - pub fn memories(&self) -> &[MemoryType] { - &self.memories - } - - pub fn tables(&self) -> &[TableType] { - &self.tables - } - - pub fn globals(&self) -> &[GlobalType] { - &self.globals - } - - pub fn types(&self) -> &[Type] { - &self.types - } - - 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_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_type(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> { - let &Type::Function(ref 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)) - } - - 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) - } -} diff --git a/src/validation/func.rs b/src/validation/func.rs deleted file mode 100644 index 46daff2..0000000 --- a/src/validation/func.rs +++ /dev/null @@ -1,774 +0,0 @@ -use std::u32; -use std::iter::repeat; -use std::collections::HashMap; -use elements::{Opcode, BlockType, ValueType, TableElementType, Func, FuncBody}; -use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; -use validation::context::ModuleContext; - -use validation::Error; - -use common::stack::StackWithLimit; -use common::{BlockFrame, BlockFrameType}; - -/// Constant from wabt' validator.cc to skip alignment validation (not a part of spec). -const NATURAL_ALIGNMENT: u32 = 0xFFFFFFFF; - -/// Maximum number of entries in value stack. -const DEFAULT_VALUE_STACK_LIMIT: usize = 16384; -/// Maximum number of entries in frame stack. -const DEFAULT_FRAME_STACK_LIMIT: usize = 1024; - -/// Function validation context. -struct FunctionValidationContext<'a> { - /// Wasm module - module: &'a ModuleContext, - /// Current instruction position. - position: usize, - /// Local variables. - locals: &'a [ValueType], - /// Value stack. - value_stack: StackWithLimit, - /// Frame stack. - frame_stack: StackWithLimit, - /// Function return type. None if validating expression. - return_type: Option, - /// Labels positions. - labels: HashMap, -} - -/// Value type on the stack. -#[derive(Debug, Clone, Copy)] -enum StackValueType { - /// Any value type. - Any, - /// Any number of any values of any type. - AnyUnlimited, - /// Concrete value type. - Specific(ValueType), -} - -/// Function validator. -pub struct Validator; - -/// Instruction outcome. -#[derive(Debug, Clone)] -enum InstructionOutcome { - /// Continue with next instruction. - ValidateNextInstruction, - /// Unreachable instruction reached. - Unreachable, -} - -impl Validator { - pub fn validate_function( - module: &ModuleContext, - func: &Func, - body: &FuncBody, - ) -> Result<(), Error> { - let (params, result_ty) = module.require_function_type(func.type_ref())?; - - // locals = (params + vars) - let mut locals = params.to_vec(); - locals.extend( - body.locals() - .iter() - .flat_map(|l| repeat(l.value_type()) - .take(l.count() as usize) - ), - ); - - let mut context = FunctionValidationContext::new( - &module, - &locals, - DEFAULT_VALUE_STACK_LIMIT, - DEFAULT_FRAME_STACK_LIMIT, - result_ty, - ); - - context.push_label(BlockFrameType::Function, result_ty)?; - Validator::validate_function_block(&mut context, body.code().elements())?; - while !context.frame_stack.is_empty() { - context.pop_label()?; - } - - Ok(()) - } - - fn validate_function_block(context: &mut FunctionValidationContext, body: &[Opcode]) -> Result<(), Error> { - let body_len = body.len(); - if body_len == 0 { - return Err(Error("Non-empty function body expected".into())); - } - - loop { - let opcode = &body[context.position]; - match Validator::validate_instruction(context, opcode)? { - InstructionOutcome::ValidateNextInstruction => (), - InstructionOutcome::Unreachable => context.unreachable()?, - } - - context.position += 1; - if context.position == body_len { - return Ok(()); - } - } - } - - fn validate_instruction(context: &mut FunctionValidationContext, opcode: &Opcode) -> Result { - use self::Opcode::*; - debug!(target: "validator", "validating {:?}", opcode); - match *opcode { - Unreachable => Ok(InstructionOutcome::Unreachable), - Nop => Ok(InstructionOutcome::ValidateNextInstruction), - Block(block_type) => Validator::validate_block(context, block_type), - Loop(block_type) => Validator::validate_loop(context, block_type), - If(block_type) => Validator::validate_if(context, block_type), - Else => Validator::validate_else(context), - End => Validator::validate_end(context), - Br(idx) => Validator::validate_br(context, idx), - BrIf(idx) => Validator::validate_br_if(context, idx), - BrTable(ref table, default) => Validator::validate_br_table(context, table, default), - Return => Validator::validate_return(context), - - Call(index) => Validator::validate_call(context, index), - CallIndirect(index, _reserved) => Validator::validate_call_indirect(context, index), - - Drop => Validator::validate_drop(context), - Select => Validator::validate_select(context), - - GetLocal(index) => Validator::validate_get_local(context, index), - SetLocal(index) => Validator::validate_set_local(context, index), - TeeLocal(index) => Validator::validate_tee_local(context, index), - GetGlobal(index) => Validator::validate_get_global(context, index), - SetGlobal(index) => Validator::validate_set_global(context, index), - - I32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::I32), - I64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::I64), - F32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::F32), - F64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::F64), - I32Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I32), - I32Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I32), - I32Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I32), - I32Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I32), - I64Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I64), - I64Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I64), - I64Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I64), - I64Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I64), - I64Load32S(align, _) => Validator::validate_load(context, align, 4, ValueType::I64), - I64Load32U(align, _) => Validator::validate_load(context, align, 4, ValueType::I64), - - I32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::I32), - I64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::I64), - F32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::F32), - F64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::F64), - I32Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I32), - I32Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I32), - I64Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I64), - I64Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I64), - I64Store32(align, _) => Validator::validate_store(context, align, 4, ValueType::I64), - - CurrentMemory(_) => Validator::validate_current_memory(context), - GrowMemory(_) => Validator::validate_grow_memory(context), - - I32Const(_) => Validator::validate_const(context, ValueType::I32), - I64Const(_) => Validator::validate_const(context, ValueType::I64), - F32Const(_) => Validator::validate_const(context, ValueType::F32), - F64Const(_) => Validator::validate_const(context, ValueType::F64), - - I32Eqz => Validator::validate_testop(context, ValueType::I32), - I32Eq => Validator::validate_relop(context, ValueType::I32), - I32Ne => Validator::validate_relop(context, ValueType::I32), - I32LtS => Validator::validate_relop(context, ValueType::I32), - I32LtU => Validator::validate_relop(context, ValueType::I32), - I32GtS => Validator::validate_relop(context, ValueType::I32), - I32GtU => Validator::validate_relop(context, ValueType::I32), - I32LeS => Validator::validate_relop(context, ValueType::I32), - I32LeU => Validator::validate_relop(context, ValueType::I32), - I32GeS => Validator::validate_relop(context, ValueType::I32), - I32GeU => Validator::validate_relop(context, ValueType::I32), - - I64Eqz => Validator::validate_testop(context, ValueType::I64), - I64Eq => Validator::validate_relop(context, ValueType::I64), - I64Ne => Validator::validate_relop(context, ValueType::I64), - I64LtS => Validator::validate_relop(context, ValueType::I64), - I64LtU => Validator::validate_relop(context, ValueType::I64), - I64GtS => Validator::validate_relop(context, ValueType::I64), - I64GtU => Validator::validate_relop(context, ValueType::I64), - I64LeS => Validator::validate_relop(context, ValueType::I64), - I64LeU => Validator::validate_relop(context, ValueType::I64), - I64GeS => Validator::validate_relop(context, ValueType::I64), - I64GeU => Validator::validate_relop(context, ValueType::I64), - - F32Eq => Validator::validate_relop(context, ValueType::F32), - F32Ne => Validator::validate_relop(context, ValueType::F32), - F32Lt => Validator::validate_relop(context, ValueType::F32), - F32Gt => Validator::validate_relop(context, ValueType::F32), - F32Le => Validator::validate_relop(context, ValueType::F32), - F32Ge => Validator::validate_relop(context, ValueType::F32), - - F64Eq => Validator::validate_relop(context, ValueType::F64), - F64Ne => Validator::validate_relop(context, ValueType::F64), - F64Lt => Validator::validate_relop(context, ValueType::F64), - F64Gt => Validator::validate_relop(context, ValueType::F64), - F64Le => Validator::validate_relop(context, ValueType::F64), - F64Ge => Validator::validate_relop(context, ValueType::F64), - - I32Clz => Validator::validate_unop(context, ValueType::I32), - I32Ctz => Validator::validate_unop(context, ValueType::I32), - I32Popcnt => Validator::validate_unop(context, ValueType::I32), - I32Add => Validator::validate_binop(context, ValueType::I32), - I32Sub => Validator::validate_binop(context, ValueType::I32), - I32Mul => Validator::validate_binop(context, ValueType::I32), - I32DivS => Validator::validate_binop(context, ValueType::I32), - I32DivU => Validator::validate_binop(context, ValueType::I32), - I32RemS => Validator::validate_binop(context, ValueType::I32), - I32RemU => Validator::validate_binop(context, ValueType::I32), - I32And => Validator::validate_binop(context, ValueType::I32), - I32Or => Validator::validate_binop(context, ValueType::I32), - I32Xor => Validator::validate_binop(context, ValueType::I32), - I32Shl => Validator::validate_binop(context, ValueType::I32), - I32ShrS => Validator::validate_binop(context, ValueType::I32), - I32ShrU => Validator::validate_binop(context, ValueType::I32), - I32Rotl => Validator::validate_binop(context, ValueType::I32), - I32Rotr => Validator::validate_binop(context, ValueType::I32), - - I64Clz => Validator::validate_unop(context, ValueType::I64), - I64Ctz => Validator::validate_unop(context, ValueType::I64), - I64Popcnt => Validator::validate_unop(context, ValueType::I64), - I64Add => Validator::validate_binop(context, ValueType::I64), - I64Sub => Validator::validate_binop(context, ValueType::I64), - I64Mul => Validator::validate_binop(context, ValueType::I64), - I64DivS => Validator::validate_binop(context, ValueType::I64), - I64DivU => Validator::validate_binop(context, ValueType::I64), - I64RemS => Validator::validate_binop(context, ValueType::I64), - I64RemU => Validator::validate_binop(context, ValueType::I64), - I64And => Validator::validate_binop(context, ValueType::I64), - I64Or => Validator::validate_binop(context, ValueType::I64), - I64Xor => Validator::validate_binop(context, ValueType::I64), - I64Shl => Validator::validate_binop(context, ValueType::I64), - I64ShrS => Validator::validate_binop(context, ValueType::I64), - I64ShrU => Validator::validate_binop(context, ValueType::I64), - I64Rotl => Validator::validate_binop(context, ValueType::I64), - I64Rotr => Validator::validate_binop(context, ValueType::I64), - - F32Abs => Validator::validate_unop(context, ValueType::F32), - F32Neg => Validator::validate_unop(context, ValueType::F32), - F32Ceil => Validator::validate_unop(context, ValueType::F32), - F32Floor => Validator::validate_unop(context, ValueType::F32), - F32Trunc => Validator::validate_unop(context, ValueType::F32), - F32Nearest => Validator::validate_unop(context, ValueType::F32), - F32Sqrt => Validator::validate_unop(context, ValueType::F32), - F32Add => Validator::validate_binop(context, ValueType::F32), - F32Sub => Validator::validate_binop(context, ValueType::F32), - F32Mul => Validator::validate_binop(context, ValueType::F32), - F32Div => Validator::validate_binop(context, ValueType::F32), - F32Min => Validator::validate_binop(context, ValueType::F32), - F32Max => Validator::validate_binop(context, ValueType::F32), - F32Copysign => Validator::validate_binop(context, ValueType::F32), - - F64Abs => Validator::validate_unop(context, ValueType::F64), - F64Neg => Validator::validate_unop(context, ValueType::F64), - F64Ceil => Validator::validate_unop(context, ValueType::F64), - F64Floor => Validator::validate_unop(context, ValueType::F64), - F64Trunc => Validator::validate_unop(context, ValueType::F64), - F64Nearest => Validator::validate_unop(context, ValueType::F64), - F64Sqrt => Validator::validate_unop(context, ValueType::F64), - F64Add => Validator::validate_binop(context, ValueType::F64), - F64Sub => Validator::validate_binop(context, ValueType::F64), - F64Mul => Validator::validate_binop(context, ValueType::F64), - F64Div => Validator::validate_binop(context, ValueType::F64), - F64Min => Validator::validate_binop(context, ValueType::F64), - F64Max => Validator::validate_binop(context, ValueType::F64), - F64Copysign => Validator::validate_binop(context, ValueType::F64), - - I32WrapI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::I32), - I32TruncSF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32), - I32TruncUF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32), - I32TruncSF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I32), - I32TruncUF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I32), - I64ExtendSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::I64), - I64ExtendUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::I64), - I64TruncSF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I64), - I64TruncUF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I64), - I64TruncSF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64), - I64TruncUF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64), - F32ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32), - F32ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32), - F32ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F32), - F32ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F32), - F32DemoteF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::F32), - F64ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F64), - F64ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F64), - F64ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64), - F64ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64), - F64PromoteF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::F64), - - I32ReinterpretF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32), - I64ReinterpretF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64), - F32ReinterpretI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32), - F64ReinterpretI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64), - } - } - - fn validate_const(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { - context.push_value(value_type.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_unop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { - context.pop_value(value_type.into())?; - context.push_value(value_type.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_binop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { - context.pop_value(value_type.into())?; - context.pop_value(value_type.into())?; - context.push_value(value_type.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_testop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { - context.pop_value(value_type.into())?; - context.push_value(ValueType::I32.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_relop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { - context.pop_value(value_type.into())?; - context.pop_value(value_type.into())?; - context.push_value(ValueType::I32.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_cvtop(context: &mut FunctionValidationContext, value_type1: ValueType, value_type2: ValueType) -> Result { - context.pop_value(value_type1.into())?; - context.push_value(value_type2.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_drop(context: &mut FunctionValidationContext) -> Result { - context.pop_any_value().map(|_| ())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_select(context: &mut FunctionValidationContext) -> Result { - context.pop_value(ValueType::I32.into())?; - let select_type = context.pop_any_value()?; - context.pop_value(select_type)?; - context.push_value(select_type)?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_get_local(context: &mut FunctionValidationContext, index: u32) -> Result { - let local_type = context.require_local(index)?; - context.push_value(local_type)?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_set_local(context: &mut FunctionValidationContext, index: u32) -> Result { - let local_type = context.require_local(index)?; - let value_type = context.pop_any_value()?; - if local_type != value_type { - return Err(Error(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type))); - } - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_tee_local(context: &mut FunctionValidationContext, index: u32) -> Result { - let local_type = context.require_local(index)?; - let value_type = context.tee_any_value()?; - if local_type != value_type { - return Err(Error(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type))); - } - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_get_global(context: &mut FunctionValidationContext, index: u32) -> Result { - let global_type: StackValueType = { - let global = context.module.require_global(index, None)?; - global.content_type().into() - }; - context.push_value(global_type)?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_set_global(context: &mut FunctionValidationContext, index: u32) -> Result { - let global_type: StackValueType = { - let global = context.module.require_global(index, Some(true))?; - global.content_type().into() - }; - let value_type = context.pop_any_value()?; - 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(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_load(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: ValueType) -> Result { - if align != NATURAL_ALIGNMENT { - 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.pop_value(ValueType::I32.into())?; - context.module.require_memory(DEFAULT_MEMORY_INDEX)?; - context.push_value(value_type.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_store(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: ValueType) -> Result { - if align != NATURAL_ALIGNMENT { - 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)?; - context.pop_value(value_type.into())?; - context.pop_value(ValueType::I32.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_block(context: &mut FunctionValidationContext, block_type: BlockType) -> Result { - context.push_label(BlockFrameType::Block, block_type).map(|_| InstructionOutcome::ValidateNextInstruction) - } - - fn validate_loop(context: &mut FunctionValidationContext, block_type: BlockType) -> Result { - context.push_label(BlockFrameType::Loop, block_type).map(|_| InstructionOutcome::ValidateNextInstruction) - } - - fn validate_if(context: &mut FunctionValidationContext, block_type: BlockType) -> Result { - context.pop_value(ValueType::I32.into())?; - context.push_label(BlockFrameType::IfTrue, block_type).map(|_| InstructionOutcome::ValidateNextInstruction) - } - - fn validate_else(context: &mut FunctionValidationContext) -> Result { - let block_type = { - let top_frame = context.top_label()?; - if top_frame.frame_type != BlockFrameType::IfTrue { - return Err(Error("Misplaced else instruction".into())); - } - top_frame.block_type - }; - context.pop_label()?; - - if let BlockType::Value(value_type) = block_type { - context.pop_value(value_type.into())?; - } - context.push_label(BlockFrameType::IfFalse, block_type).map(|_| InstructionOutcome::ValidateNextInstruction) - } - - fn validate_end(context: &mut FunctionValidationContext) -> Result { - { - let top_frame = context.top_label()?; - if top_frame.frame_type == BlockFrameType::IfTrue { - if top_frame.block_type != BlockType::NoResult { - return Err(Error(format!("If block without else required to have NoResult block type. But it have {:?} type", top_frame.block_type))); - } - } - } - - context.pop_label().map(|_| InstructionOutcome::ValidateNextInstruction) - } - - fn validate_br(context: &mut FunctionValidationContext, idx: u32) -> Result { - let (frame_type, frame_block_type) = { - let frame = context.require_label(idx)?; - (frame.frame_type, frame.block_type) - }; - if frame_type != BlockFrameType::Loop { - if let BlockType::Value(value_type) = frame_block_type { - context.tee_value(value_type.into())?; - } - } - Ok(InstructionOutcome::Unreachable) - } - - fn validate_br_if(context: &mut FunctionValidationContext, idx: u32) -> Result { - context.pop_value(ValueType::I32.into())?; - - let (frame_type, frame_block_type) = { - let frame = context.require_label(idx)?; - (frame.frame_type, frame.block_type) - }; - if frame_type != BlockFrameType::Loop { - if let BlockType::Value(value_type) = frame_block_type { - context.tee_value(value_type.into())?; - } - } - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_br_table(context: &mut FunctionValidationContext, table: &[u32], default: u32) -> Result { - let mut required_block_type = None; - - { - let default_block = context.require_label(default)?; - if default_block.frame_type != BlockFrameType::Loop { - required_block_type = Some(default_block.block_type); - } - - for label in table { - let label_block = context.require_label(*label)?; - if label_block.frame_type != BlockFrameType::Loop { - if let Some(required_block_type) = required_block_type { - if required_block_type != label_block.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 = Some(label_block.block_type); - } - } - } - - context.pop_value(ValueType::I32.into())?; - if let Some(required_block_type) = required_block_type { - if let BlockType::Value(value_type) = required_block_type { - context.tee_value(value_type.into())?; - } - } - - Ok(InstructionOutcome::Unreachable) - } - - fn validate_return(context: &mut FunctionValidationContext) -> Result { - if let BlockType::Value(value_type) = context.return_type()? { - context.tee_value(value_type.into())?; - } - Ok(InstructionOutcome::Unreachable) - } - - fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result { - let (argument_types, return_type) = context.module.require_function(idx)?; - for argument_type in argument_types.iter().rev() { - context.pop_value((*argument_type).into())?; - } - if let BlockType::Value(value_type) = return_type { - context.push_value(value_type.into())?; - } - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_call_indirect(context: &mut FunctionValidationContext, idx: u32) -> Result { - { - 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() - ))); - } - } - - context.pop_value(ValueType::I32.into())?; - let (argument_types, return_type) = context.module.require_function_type(idx)?; - for argument_type in argument_types.iter().rev() { - context.pop_value((*argument_type).into())?; - } - if let BlockType::Value(value_type) = return_type { - context.push_value(value_type.into())?; - } - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_current_memory(context: &mut FunctionValidationContext) -> Result { - context.module.require_memory(DEFAULT_MEMORY_INDEX)?; - context.push_value(ValueType::I32.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result { - context.module.require_memory(DEFAULT_MEMORY_INDEX)?; - context.pop_value(ValueType::I32.into())?; - context.push_value(ValueType::I32.into())?; - Ok(InstructionOutcome::ValidateNextInstruction) - } -} - -impl<'a> FunctionValidationContext<'a> { - fn new( - module: &'a ModuleContext, - locals: &'a [ValueType], - value_stack_limit: usize, - frame_stack_limit: usize, - return_type: BlockType, - ) -> 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: Some(return_type), - labels: HashMap::new(), - } - } - - fn push_value(&mut self, value_type: StackValueType) -> Result<(), Error> { - Ok(self.value_stack.push(value_type.into())?) - } - - fn pop_value(&mut self, value_type: StackValueType) -> Result<(), Error> { - self.check_stack_access()?; - match self.value_stack.pop()? { - StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()), - StackValueType::Any => Ok(()), - StackValueType::AnyUnlimited => { - self.value_stack.push(StackValueType::AnyUnlimited)?; - Ok(()) - }, - stack_value_type @ _ => Err(Error(format!("Expected value of type {:?} on top of stack. Got {:?}", value_type, stack_value_type))), - } - } - - fn tee_value(&mut self, value_type: StackValueType) -> Result<(), Error> { - self.check_stack_access()?; - match *self.value_stack.top()? { - StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()), - StackValueType::Any | StackValueType::AnyUnlimited => Ok(()), - stack_value_type @ _ => Err(Error(format!("Expected value of type {:?} on top of stack. Got {:?}", value_type, stack_value_type))), - } - } - - fn pop_any_value(&mut self) -> Result { - self.check_stack_access()?; - match self.value_stack.pop()? { - StackValueType::Specific(stack_value_type) => Ok(StackValueType::Specific(stack_value_type)), - StackValueType::Any => Ok(StackValueType::Any), - StackValueType::AnyUnlimited => { - self.value_stack.push(StackValueType::AnyUnlimited)?; - Ok(StackValueType::Any) - }, - } - } - - fn tee_any_value(&mut self) -> Result { - self.check_stack_access()?; - Ok(self.value_stack.top().map(Clone::clone)?) - } - - fn unreachable(&mut self) -> Result<(), Error> { - Ok(self.value_stack.push(StackValueType::AnyUnlimited)?) - } - - fn top_label(&self) -> Result<&BlockFrame, Error> { - Ok(self.frame_stack.top()?) - } - - fn push_label(&mut self, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), Error> { - Ok(self.frame_stack.push(BlockFrame { - frame_type: frame_type, - block_type: block_type, - begin_position: self.position, - branch_position: self.position, - end_position: self.position, - value_stack_len: self.value_stack.len(), - })?) - } - - fn pop_label(&mut self) -> Result { - let frame = self.frame_stack.pop()?; - let actual_value_type = if self.value_stack.len() > frame.value_stack_len { - Some(self.value_stack.pop()?) - } else { - None - }; - self.value_stack.resize(frame.value_stack_len, StackValueType::Any); - - match frame.block_type { - BlockType::NoResult if actual_value_type.map(|vt| vt.is_any_unlimited()).unwrap_or(true) => (), - BlockType::Value(required_value_type) if actual_value_type.map(|vt| vt == required_value_type).unwrap_or(false) => (), - _ => return Err(Error(format!("Expected block to return {:?} while it has returned {:?}", frame.block_type, actual_value_type))), - } - if !self.frame_stack.is_empty() { - self.labels.insert(frame.begin_position, self.position); - } - if let BlockType::Value(value_type) = frame.block_type { - self.push_value(value_type.into())?; - } - - Ok(InstructionOutcome::ValidateNextInstruction) - } - - fn require_label(&self, idx: u32) -> Result<&BlockFrame, Error> { - Ok(self.frame_stack.get(idx as usize)?) - } - - fn return_type(&self) -> Result { - self.return_type.ok_or(Error("Trying to return from expression".into())) - } - - fn require_local(&self, idx: u32) -> Result { - self.locals.get(idx as usize) - .cloned() - .map(Into::into) - .ok_or(Error(format!("Trying to access local with index {} when there are only {} locals", idx, self.locals.len()))) - } - - fn check_stack_access(&self) -> Result<(), Error> { - let value_stack_min = self.frame_stack.top().expect("at least 1 topmost block").value_stack_len; - if self.value_stack.len() > value_stack_min { - Ok(()) - } else { - Err(Error("Trying to access parent frame stack values.".into())) - } - } -} - -impl StackValueType { - fn is_any(&self) -> bool { - match self { - &StackValueType::Any => true, - _ => false, - } - } - - fn is_any_unlimited(&self) -> bool { - match self { - &StackValueType::AnyUnlimited => true, - _ => false, - } - } - - fn value_type(&self) -> ValueType { - match self { - &StackValueType::Any | &StackValueType::AnyUnlimited => 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) - } -} - -impl PartialEq for StackValueType { - fn eq(&self, other: &StackValueType) -> bool { - if self.is_any() || other.is_any() || self.is_any_unlimited() || other.is_any_unlimited() { - true - } else { - self.value_type() == other.value_type() - } - } -} - -impl PartialEq for StackValueType { - fn eq(&self, other: &ValueType) -> bool { - if self.is_any() || self.is_any_unlimited() { - true - } else { - self.value_type() == *other - } - } -} - -impl PartialEq for ValueType { - fn eq(&self, other: &StackValueType) -> bool { - other == self - } -} diff --git a/src/validation/mod.rs b/src/validation/mod.rs deleted file mode 100644 index 34afc5a..0000000 --- a/src/validation/mod.rs +++ /dev/null @@ -1,326 +0,0 @@ -use std::error; -use std::fmt; -use elements::{ - BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, - Module, Opcode, ResizableLimits, TableType, ValueType, InitExpr -}; -use common::stack; -use self::context::ModuleContext; -use self::func::Validator; - -pub use self::module::ValidatedModule; - -mod context; -mod module; -mod func; - -#[cfg(test)] -mod tests; - -#[derive(Debug)] -pub struct Error(String); - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl error::Error for Error { - fn description(&self) -> &str { - &self.0 - } -} - -impl From for Error { - fn from(e: stack::Error) -> Error { - Error(format!("Stack: {}", e)) - } -} - -pub fn validate_module(module: &Module) -> Result { - let context = prepare_context(module)?; - - 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)))?; - Validator::validate_function(&context, function, function_body).map_err(|e| { - let Error(ref msg) = e; - Error(format!("Function #{} validation error: {}", index, msg)) - })?; - } - } - - // validate start section - if let Some(start_function) = module.start_section() { - let (params, return_ty) = context.require_function(start_function)?; - 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() { - for export in export_section.entries() { - 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(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) => { - memory_type.validate()?; - }, - External::Table(ref table_type) => { - table_type.validate()?; - }, - } - } - } - - // 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()))); - } - - // 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 = data_segment.offset().expr_const_type(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())?; - - let init_ty = element_segment.offset().expr_const_type(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)?; - } - } - } - - let ModuleContext { - types, - tables, - memories, - globals, - func_type_indexes, - } = context; - - Ok(ValidatedModule { - types, - tables, - memories, - globals, - func_type_indexes, - }) -} - -fn prepare_context(module: &Module) -> Result { - // Copy types from module as is. - let types = module - .type_section() - .map(|ts| ts.types().into_iter().cloned().collect()) - .unwrap_or_default(); - - // Fill elements with imported values. - let mut func_type_indexes = Vec::new(); - let mut tables = Vec::new(); - let mut memories = Vec::new(); - let mut globals = Vec::new(); - - for import_entry in module - .import_section() - .map(|i| i.entries()) - .unwrap_or_default() - { - match *import_entry.external() { - External::Function(idx) => func_type_indexes.push(idx), - External::Table(ref table) => tables.push(table.clone()), - External::Memory(ref memory) => memories.push(memory.clone()), - External::Global(ref global) => 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() { - func_type_indexes.push(func_entry.type_ref()); - } - } - if let Some(table_section) = module.table_section() { - for table_entry in table_section.entries() { - table_entry.validate()?; - tables.push(table_entry.clone()); - } - } - if let Some(mem_section) = module.memory_section() { - for mem_entry in mem_section.entries() { - mem_entry.validate()?; - memories.push(mem_entry.clone()); - } - } - if let Some(global_section) = module.global_section() { - // Validation of globals is defined over modified context C', which - // contains only imported globals. So we do globals validation - // in two passes, in first we validate globals and after all globals are validated - // add them in globals list. - for global_entry in global_section.entries() { - global_entry.validate(&globals)?; - } - - for global_entry in global_section.entries() { - globals.push(global_entry.global_type().clone()); - } - } - - Ok(ModuleContext { - types, - tables, - memories, - globals, - func_type_indexes, - }) -} - -impl ResizableLimits { - fn validate(&self) -> Result<(), Error> { - if let Some(maximum) = self.maximum() { - if self.initial() > maximum { - return Err(Error(format!( - "maximum limit {} is lesser than minimum {}", - maximum, - self.initial() - ))); - } - } - Ok(()) - } -} - -impl MemoryType { - fn validate(&self) -> Result<(), Error> { - self.limits().validate() - } -} - -impl TableType { - fn validate(&self) -> Result<(), Error> { - self.limits().validate() - } -} - -impl GlobalEntry { - fn validate(&self, globals: &[GlobalType]) -> Result<(), Error> { - let init = self.init_expr(); - let init_expr_ty = init.expr_const_type(globals)?; - if init_expr_ty != self.global_type().content_type() { - return Err(Error(format!( - "Trying to initialize variable of type {:?} with value of type {:?}", - self.global_type().content_type(), - init_expr_ty - ))); - } - Ok(()) - } -} - -impl InitExpr { - /// Returns type of this constant expression. - fn expr_const_type(&self, globals: &[GlobalType]) -> Result { - let code = self.code(); - if code.len() != 2 { - return Err(Error("Init expression should always be with length 2".into())); - } - let expr_ty: ValueType = match code[0] { - Opcode::I32Const(_) => ValueType::I32, - Opcode::I64Const(_) => ValueType::I64, - Opcode::F32Const(_) => ValueType::F32, - Opcode::F64Const(_) => ValueType::F64, - Opcode::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] != Opcode::End { - return Err(Error("Expression doesn't ends with `end` opcode".into())); - } - Ok(expr_ty) - } -} diff --git a/src/validation/module.rs b/src/validation/module.rs deleted file mode 100644 index 1e138a1..0000000 --- a/src/validation/module.rs +++ /dev/null @@ -1,10 +0,0 @@ -use elements::{MemoryType, TableType, GlobalType, Type}; - -#[derive(Debug)] -pub struct ValidatedModule { - pub memories: Vec, - pub tables: Vec, - pub globals: Vec, - pub types: Vec, - pub func_type_indexes: Vec, -} diff --git a/src/validation/tests.rs b/src/validation/tests.rs deleted file mode 100644 index 3e1f255..0000000 --- a/src/validation/tests.rs +++ /dev/null @@ -1,301 +0,0 @@ -use super::validate_module; -use builder::module; -use elements::{ - External, GlobalEntry, GlobalType, ImportEntry, InitExpr, MemoryType, - Opcode, Opcodes, TableType, ValueType, BlockType -}; - -#[test] -fn empty_is_valid() { - 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), - ]; - - 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); - - // 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); - } -} - -#[test] -fn global_init_const() { - let m = module() - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new( - vec![Opcode::I32Const(42), Opcode::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![Opcode::I32Const(42), Opcode::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![Opcode::GetGlobal(0), Opcode::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![Opcode::GetGlobal(0), Opcode::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![Opcode::GetGlobal(0), Opcode::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![Opcode::I32Const(0), Opcode::End]) - ) - ) - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::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![Opcode::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![Opcode::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![Opcode::Unreachable, Opcode::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 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_opcodes(Opcodes::new(vec![ - Opcode::Call(1), - Opcode::End, - ])).build() - .build() - .function() - .signature().return_type().i32().build() - .body().with_opcodes(Opcodes::new(vec![ - Opcode::Call(0), - Opcode::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 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_opcodes(Opcodes::new(vec![ - Opcode::I32Const(1), - Opcode::If(BlockType::NoResult), - Opcode::I32Const(1), - Opcode::If(BlockType::Value(ValueType::I32)), - Opcode::I32Const(1), - Opcode::Else, - Opcode::I32Const(2), - Opcode::End, - Opcode::Drop, - Opcode::End, - Opcode::End, - ])).build() - .build() - .build(); - validate_module(&m).unwrap(); -}