From 0eb881f4872a936154dacf3003cf7f54689f31d5 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 21 Apr 2017 14:35:12 +0300 Subject: [PATCH] initial interpreter commit --- Cargo.toml | 1 + src/elements/export_entry.rs | 1 + src/elements/module.rs | 10 +- src/elements/ops.rs | 6 +- src/interpreter/imports.rs | 144 +++++ src/interpreter/memory.rs | 87 +++ src/interpreter/mod.rs | 49 ++ src/interpreter/module.rs | 213 +++++++ src/interpreter/program.rs | 54 ++ src/interpreter/runner.rs | 1119 ++++++++++++++++++++++++++++++++++ src/interpreter/table.rs | 26 + src/interpreter/value.rs | 415 +++++++++++++ src/interpreter/variable.rs | 72 +++ src/lib.rs | 2 + 14 files changed, 2195 insertions(+), 4 deletions(-) create mode 100644 src/interpreter/imports.rs create mode 100644 src/interpreter/memory.rs create mode 100644 src/interpreter/mod.rs create mode 100644 src/interpreter/module.rs create mode 100644 src/interpreter/program.rs create mode 100644 src/interpreter/runner.rs create mode 100644 src/interpreter/table.rs create mode 100644 src/interpreter/value.rs create mode 100644 src/interpreter/variable.rs diff --git a/Cargo.toml b/Cargo.toml index 2a8e9fc..243cd74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,4 @@ exclude = [ "res/*" ] [dependencies] byteorder = "1.0" +parking_lot = "0.4" diff --git a/src/elements/export_entry.rs b/src/elements/export_entry.rs index b119ac4..535ae26 100644 --- a/src/elements/export_entry.rs +++ b/src/elements/export_entry.rs @@ -2,6 +2,7 @@ use std::io; use super::{Deserialize, Serialize, Error, VarUint7, VarUint32}; /// Internal reference of the exported entry. +#[derive(Clone, Copy)] pub enum Internal { /// Function reference. Function(u32), diff --git a/src/elements/module.rs b/src/elements/module.rs index cf87296..da7782f 100644 --- a/src/elements/module.rs +++ b/src/elements/module.rs @@ -1,7 +1,7 @@ use std::io; use super::{Deserialize, Serialize, Error, Uint32}; use super::section::{ - Section, CodeSection, TypeSection, ImportSection, FunctionsSection, + Section, CodeSection, TypeSection, ImportSection, ExportSection, FunctionsSection, GlobalSection, TableSection, ElementSection, DataSection, MemorySection, }; @@ -82,6 +82,14 @@ impl Module { None } + /// Exports section, if any. + pub fn export_section(&self) -> Option<&ExportSection> { + for section in self.sections() { + if let &Section::Export(ref export_section) = section { return Some(export_section); } + } + None + } + /// Table section, if any. pub fn table_section(&self) -> Option<&TableSection> { for section in self.sections() { diff --git a/src/elements/ops.rs b/src/elements/ops.rs index d73b26c..34e0452 100644 --- a/src/elements/ops.rs +++ b/src/elements/ops.rs @@ -199,7 +199,7 @@ pub enum Opcode { I32Or, I32Xor, I32Shl, - I32ShlS, + I32ShrS, I32ShrU, I32Rotl, I32Rotr, @@ -485,7 +485,7 @@ impl Deserialize for Opcode { 0x72 => I32Or, 0x73 => I32Xor, 0x74 => I32Shl, - 0x75 => I32ShlS, + 0x75 => I32ShrS, 0x76 => I32ShrU, 0x77 => I32Rotl, 0x78 => I32Rotr, @@ -805,7 +805,7 @@ impl Serialize for Opcode { I32Or => op!(writer, 0x72), I32Xor => op!(writer, 0x73), I32Shl => op!(writer, 0x74), - I32ShlS => op!(writer, 0x75), + I32ShrS => op!(writer, 0x75), I32ShrU => op!(writer, 0x76), I32Rotl => op!(writer, 0x77), I32Rotr => op!(writer, 0x78), diff --git a/src/interpreter/imports.rs b/src/interpreter/imports.rs new file mode 100644 index 0000000..6f2063d --- /dev/null +++ b/src/interpreter/imports.rs @@ -0,0 +1,144 @@ +use std::sync::{Arc, Weak}; +use elements::{ImportSection, ImportEntry, External, Internal}; +use interpreter::Error; +use interpreter::memory::MemoryInstance; +use interpreter::module::{ModuleInstance, ItemIndex}; +use interpreter::program::ProgramInstanceEssence; +use interpreter::table::TableInstance; +use interpreter::variable::VariableInstance; + +// TODO: cache Internal-s to fasten access +/// 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, + } + } + + /// 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(index), + }, + 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(index), + }, + 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(index), + }, + 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(index), + }, + index @ _ => index, + } + } + + /// Get module reference. + pub fn module(&self, name: &str) -> Result, Error> { + 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 table reference. + pub fn table(&self, import: &ImportEntry) -> Result, Error> { + let (module, export) = self.external_export(import)?; + 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(&self, import: &ImportEntry) -> Result, Error> { + let (module, export) = self.external_export(import)?; + 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(&self, import: &ImportEntry) -> Result, Error> { + let (module, export) = self.external_export(import)?; + if let Internal::Global(external_index) = export { + return module.global(ItemIndex::Internal(external_index)); + } + + Err(Error::Program(format!("wrong import {} from module {} (expecting global)", import.field(), import.module()))) + } + + fn external_export(&self, import: &ImportEntry) -> Result<(Arc, Internal), Error> { + self.module(import.module()) + .and_then(|m| m.module().export_section() + .ok_or(Error::Program(format!("trying to import from module {} without export section", import.module()))) + .and_then(|s| s.entries().iter() + .find(|e| e.field() == import.field()) + .map(|e| e.internal()) + .ok_or(Error::Program(format!("unresolved import {} from module {}", import.field(), import.module()))) + .map(|export| (m.clone(), *export)))) + } +} diff --git a/src/interpreter/memory.rs b/src/interpreter/memory.rs new file mode 100644 index 0000000..8c1321d --- /dev/null +++ b/src/interpreter/memory.rs @@ -0,0 +1,87 @@ +use std::u32; +use std::sync::Arc; +use parking_lot::RwLock; +use elements::MemoryType; +use interpreter::Error; + +/// Linear memory page size. +const LINEAR_MEMORY_PAGE_SIZE: u32 = 65536; + +/// Linear memory instance. +pub struct MemoryInstance { + /// Linear memory buffer. + buffer: RwLock>, + /// Maximum buffer size. + maximum_size: u32, +} + +impl MemoryInstance { + /// Create new linear memory instance. + pub fn new(memory_type: &MemoryType) -> Result, Error> { + let memory = MemoryInstance { + buffer: RwLock::new(Vec::new()), // TODO: with_capacity + maximum_size: memory_type.limits().maximum() + .map(|s| s.saturating_mul(LINEAR_MEMORY_PAGE_SIZE)) + .unwrap_or(u32::MAX), + }; + if memory.grow(memory_type.limits().initial())? == u32::MAX { + return Err(Error::Memory(format!("error initializing {}-pages linear memory region", memory_type.limits().initial()))); + } + Ok(Arc::new(memory)) + } + + /// 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 begin = offset as usize; + let end = match begin.checked_add(size) { + Some(end) => end, + None => return Err(Error::Memory(format!("trying to read memory block of size {} from offset {}", size, offset))), + }; + + let buffer = self.buffer.read(); + if buffer.len() <= end { + return Err(Error::Memory(format!("trying to read region [{}..{}] in memory [0..{}]", begin, end, buffer.len()))); + } + + Ok(buffer[begin..end].to_vec()) + } + + /// Set data at given offset. + pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error> { + let size = value.len(); + let begin = offset as usize; + let end = match begin.checked_add(size) { + Some(end) => end, + None => return Err(Error::Memory(format!("trying to update memory block of size {} from offset {}", size, offset))), + }; + + let mut buffer = self.buffer.write(); + if buffer.len() <= end { + return Err(Error::Memory(format!("trying to update region [{}..{}] in memory [0..{}]", begin, end, buffer.len()))); + } + + let mut mut_buffer = buffer.as_mut_slice(); + mut_buffer[begin..end].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 pages.checked_mul(LINEAR_MEMORY_PAGE_SIZE).and_then(|bytes| old_size.checked_add(bytes)) { + None => Ok(u32::MAX), + Some(new_size) if new_size > self.maximum_size => Ok(u32::MAX), + Some(new_size) => { + buffer.extend(vec![0; (new_size - old_size) as usize]); + Ok(old_size / LINEAR_MEMORY_PAGE_SIZE) + }, + } + } +} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs new file mode 100644 index 0000000..6fd02f5 --- /dev/null +++ b/src/interpreter/mod.rs @@ -0,0 +1,49 @@ +#![allow(dead_code, unused_variables, missing_docs)] + +#[derive(Debug, Clone, PartialEq)] +pub enum Error { + Program(String), + Initialization(String), + Function(String), + Table(String), + Memory(String), + Variable(String), + Global(String), + Local(String), + ValueStack(String), + FrameStack(String), + Value(String), + Interpreter(String), + Trap, + NotImplemented, +} + +impl Into for Error { + fn into(self) -> String { + match self { + Error::Program(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::ValueStack(s) => s, + Error::FrameStack(s) => s, + Error::Interpreter(s) => s, + Error::Value(s) => s, + Error::Trap => "trap".into(), + Error::NotImplemented => "not implemented".into(), + } + } +} + +mod imports; +mod memory; +mod module; +mod program; +mod runner; +mod table; +mod value; +mod variable; diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs new file mode 100644 index 0000000..ad06008 --- /dev/null +++ b/src/interpreter/module.rs @@ -0,0 +1,213 @@ +use std::sync::{Arc, Weak}; +use elements::{Module, InitExpr, Opcode, Type}; +use interpreter::Error; +use interpreter::imports::ModuleImports; +use interpreter::memory::MemoryInstance; +use interpreter::program::ProgramInstanceEssence; +use interpreter::runner::Interpreter; +use interpreter::table::TableInstance; +use interpreter::value::{RuntimeValue, TryInto}; +use interpreter::variable::VariableInstance; + +/// 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 item index (i.e. index of item in export section). + External(u32), +} + +/// Module instance. +pub struct ModuleInstance { + /// Module. + module: Module, + /// Module imports. + imports: ModuleImports, + /// Tables. + tables: Vec>, + /// Linear memory regions. + memory: Vec>, + /// Globals. + globals: Vec>, +} + +impl ModuleInstance { + /// Instantiate given module within program context. + pub fn new(program: Weak, module: Module) -> Result { + // TODO: missing validation step + + // load entries from import section + let imports = ModuleImports::new(program, module.import_section()); + + // instantiate linear memory regions, if any + let mut memory = match module.memory_section() { + Some(memory_section) => memory_section.entries() + .iter() + .map(MemoryInstance::new) + .collect::, _>>()?, + None => Vec::new(), + }; + + // instantiate tables, if any + let mut tables = match module.table_section() { + Some(table_section) => table_section.entries() + .iter() + .map(TableInstance::new) + .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()) + .map_err(|e| Error::Initialization(e.into())) + .and_then(|v| VariableInstance::new(g.global_type(), v)) + }) + .collect::, _>>()?, + None => Vec::new(), + }; + + // use data section to initialize linear memory regions + if let Some(data_section) = module.data_section() { + for (data_segement_index, data_segment) in data_section.entries().iter().enumerate() { + let offset: u32 = get_initializer(data_segment.offset())?.try_into()?; + memory + .get_mut(data_segment.index() as usize) + .ok_or(Error::Initialization(format!("DataSegment {} initializes non-existant MemoryInstance {}", data_segement_index, data_segment.index()))) + .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) = module.elements_section() { + for (element_segment_index, element_segment) in element_section.entries().iter().enumerate() { + let offset: u32 = get_initializer(element_segment.offset())?.try_into()?; + tables + .get_mut(element_segment.index() as usize) + .ok_or(Error::Initialization(format!("ElementSegment {} initializes non-existant Table {}", element_segment_index, element_segment.index()))) + .and_then(|m| m.set(offset, element_segment.members()).map_err(|e| Error::Initialization(e.into())))?; + } + } + + Ok(ModuleInstance { + module: module, + imports: imports, + memory: memory, + tables: tables, + globals: globals, + }) + } + + /// Get module description reference. + pub fn module(&self) -> &Module { + &self.module + } + + /// Get table reference. + pub 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(e)), + } + } + + /// Get memory reference. + pub 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(e)), + } + } + + /// Get global reference. + pub fn global(&self, index: ItemIndex) -> 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(e)), + } + } + + /// Call function with given index in functions index space. + pub fn call_function(&self, index: ItemIndex) -> Result, Error> { + // each functions has its own value stack + // but there's global stack limit + match self.imports.parse_function_index(index) { + ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"), + ItemIndex::Internal(index) => { + // TODO: cache + // internal index = index of function in functions section && index of code in code section + // get function type index + let function_type_index = self.module + .functions_section() + .ok_or(Error::Function(format!("trying to call function with index {} in module without function section", index))) + .and_then(|s| s.entries() + .get(index as usize) + .ok_or(Error::Function(format!("trying to call function with index {} in module with {} functions", index, s.entries().len()))))? + .type_ref(); + // function type index = index of function type in types index + // get function type + let item_type = self.module + .type_section() + .ok_or(Error::Function(format!("trying to call function with index {} in module without types section", index))) + .and_then(|s| s.types() + .get(function_type_index as usize) + .ok_or(Error::Function(format!("trying to call function with type index {} in module with {} types", index, s.types().len()))))?; + let function_type = match item_type { + &Type::Function(ref function_type) => function_type, + }; + // get function body + let function_body = self.module + .code_section() + .ok_or(Error::Function(format!("trying to call function with index {} in module without code section", index))) + .and_then(|s| s.bodies() + .get(index as usize) + .ok_or(Error::Function(format!("trying to call function with index {} in module with {} functions codes", index, s.bodies().len()))))?; + + // TODO: args, locals + Interpreter::run_function(function_type, function_body.code().elements(), &vec![]) + }, + 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| self.imports.module(e.module())) + .and_then(|m| m.call_function(ItemIndex::Internal(index))), + } + } +} + +fn get_initializer(expr: &InitExpr) -> Result { + let first_opcode = expr.code().get(0).ok_or(Error::Initialization(format!("empty instantiation-time initializer")))?; + match first_opcode { + // TODO: &Opcode::GetGlobal(index) => self.get_global(index), + &Opcode::I32Const(val) => Ok(RuntimeValue::I32(val)), + &Opcode::I64Const(val) => Ok(RuntimeValue::I64(val)), + &Opcode::F32Const(val) => Ok(RuntimeValue::F32(val as f32)), // TODO + &Opcode::F64Const(val) => Ok(RuntimeValue::F64(val as f64)), // TODO + _ => Err(Error::Initialization(format!("not-supported {:?} instruction in instantiation-time initializer", first_opcode))), + } +} diff --git a/src/interpreter/program.rs b/src/interpreter/program.rs new file mode 100644 index 0000000..e030e61 --- /dev/null +++ b/src/interpreter/program.rs @@ -0,0 +1,54 @@ +use std::sync::Arc; +use std::collections::HashMap; +use std::collections::hash_map::Entry; +use parking_lot::RwLock; +use elements::Module; +use interpreter::Error; +use interpreter::module::ModuleInstance; + +/// 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. + pub fn add_module(&self, name: &str, module: Module) -> Result<(), Error> { + let mut modules = self.essence.modules.write(); + match modules.entry(name.into()) { + Entry::Occupied(_) => Err(Error::Program(format!("module {} already instantiated", name))), + Entry::Vacant(entry) => { + entry.insert(Arc::new(ModuleInstance::new(Arc::downgrade(&self.essence), module)?)); + Ok(()) + }, + } + } +} + +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 new file mode 100644 index 0000000..6e93b30 --- /dev/null +++ b/src/interpreter/runner.rs @@ -0,0 +1,1119 @@ +// TODO: WebAssembly code must be validated before it can be instantiated and executed. +// WebAssembly is designed to allow decoding and validation to be performed in a single linear pass through a WebAssembly module, +// and to enable many parts of decoding and validation to be performed concurrently. +// => Interpreter is written in assumption that code has been validated +// (important https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#code-section) + +// Externals: +// to call function: list of imported functions + list of functions +// to access globals: list of imported globals + list of globals +// to access linear memory: list of imported regions + list of regions + +use std::sync::Weak; +use std::mem; +use std::ops; +use std::u32; +use std::collections::VecDeque; +use super::super::elements::{Module, Opcode, BlockType, FunctionType}; +use interpreter::Error; +use interpreter::module::{ModuleInstance, ItemIndex}; +use interpreter::value::{RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto, TransmuteInto, + ArithmeticOps, Integer, Float}; + +const DEFAULT_MEMORY_INDEX: u32 = 0; + +pub struct Interpreter; + +/// Function execution context. +struct FunctionContext<'a> { + /// Module instance. + module: &'a mut ModuleInstance, + /// Values stack. + value_stack: &'a mut VecDeque, + /// Blocks frames stack. + frame_stack: &'a mut VecDeque, + /// Local function variables. + locals: Vec, + /// Current instruction position. + position: usize, +} + +#[derive(Debug, Clone)] +enum InstructionOutcome { + /// Continue with current instruction. + RunInstruction, + /// Continue with next instruction. + RunNextInstruction, + /// Pop given number of stack frames. + PopFrame(usize), + /// Return from current function block. + Return, +} + +#[derive(Debug, Clone)] +struct BlockFrame { + // A label for reference from branch instructions. + position: usize, + // A limit integer value, which is an index into the value stack indicating where to reset it to on a branch to that label. + value_limit: usize, + // A signature, which is a block signature type indicating the number and types of result values of the region. + signature: BlockType, +} + +impl Interpreter { + pub fn run_function(function: &FunctionType, body: &[Opcode], args: &[RuntimeValue]) -> Result, Error> { + // prepare execution context + let mut module = ModuleInstance::new(Weak::default(), Module::default()).unwrap(); + let mut value_stack = VecDeque::new(); + let mut frame_stack = VecDeque::new(); + let mut context = FunctionContext::new(&mut module, &mut value_stack, &mut frame_stack, function, body, args)?; + + let block_type = match function.return_type() { + Some(value_type) => BlockType::Value(value_type), + None => BlockType::NoResult, + }; + Interpreter::execute_block(&mut context, block_type.clone(), body)?; + match block_type { + BlockType::Value(_) => Ok(Some(context.pop_value()?)), + BlockType::NoResult => Ok(None), + } + } + + fn run_instruction(context: &mut FunctionContext, opcode: &Opcode) -> Result { + match opcode { + &Opcode::Unreachable => Interpreter::run_unreachable(context), + &Opcode::Nop => Interpreter::run_nop(context), + &Opcode::Block(block_type, ref ops) => Interpreter::run_block(context, block_type, ops.elements()), + &Opcode::Loop(block_type, ref ops) => Interpreter::run_loop(context, block_type, ops.elements()), + &Opcode::If(block_type, ref ops) => Interpreter::run_if(context, block_type, ops.elements()), + &Opcode::Else => Interpreter::run_else(context), + &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(offset, align) => Interpreter::run_load::(context, offset, align), + &Opcode::I64Load(offset, align) => Interpreter::run_load::(context, offset, align), + &Opcode::F32Load(offset, align) => Interpreter::run_load::(context, offset, align), + &Opcode::F64Load(offset, align) => Interpreter::run_load::(context, offset, align), + &Opcode::I32Load8S(offset, align) => Interpreter::run_load_extend::(context, offset, align), + &Opcode::I32Load8U(offset, align) => Interpreter::run_load_extend::(context, offset, align), + &Opcode::I32Load16S(offset, align) => Interpreter::run_load_extend::(context, offset, align), + &Opcode::I32Load16U(offset, align) => Interpreter::run_load_extend::(context, offset, align), + &Opcode::I64Load8S(offset, align) => Interpreter::run_load_extend::(context, offset, align), + &Opcode::I64Load8U(offset, align) => Interpreter::run_load_extend::(context, offset, align), + &Opcode::I64Load16S(offset, align) => Interpreter::run_load_extend::(context, offset, align), + &Opcode::I64Load16U(offset, align) => Interpreter::run_load_extend::(context, offset, align), + &Opcode::I64Load32S(offset, align) => Interpreter::run_load_extend::(context, offset, align), + &Opcode::I64Load32U(offset, align) => Interpreter::run_load_extend::(context, offset, align), + + &Opcode::I32Store(offset, align) => Interpreter::run_store::(context, offset, align), + &Opcode::I64Store(offset, align) => Interpreter::run_store::(context, offset, align), + &Opcode::F32Store(offset, align) => Interpreter::run_store::(context, offset, align), + &Opcode::F64Store(offset, align) => Interpreter::run_store::(context, offset, align), + &Opcode::I32Store8(offset, align) => Interpreter::run_store_wrap::(context, offset, align), + &Opcode::I32Store16(offset, align) => Interpreter::run_store_wrap::(context, offset, align), + &Opcode::I64Store8(offset, align) => Interpreter::run_store_wrap::(context, offset, align), + &Opcode::I64Store16(offset, align) => Interpreter::run_store_wrap::(context, offset, align), + &Opcode::I64Store32(offset, align) => Interpreter::run_store_wrap::(context, offset, align), + + &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), + &Opcode::I32ShrS => Interpreter::run_shr::(context), + &Opcode::I32ShrU => Interpreter::run_shr::(context), + &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), + &Opcode::I64ShrS => Interpreter::run_shr::(context), + &Opcode::I64ShrU => Interpreter::run_shr::(context), + &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::I32WarpI64 => 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(context: &mut FunctionContext) -> Result { + Err(Error::Trap) + } + + fn run_nop(context: &mut FunctionContext) -> Result { + Ok(InstructionOutcome::RunNextInstruction) + } + + fn run_block(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result { + let frame_position = context.position + 1; + context.push_frame(frame_position, block_type.clone())?; + Interpreter::execute_block(context, block_type, body) + } + + fn run_loop(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result { + let frame_position = context.position; + context.push_frame(frame_position, block_type.clone())?; + Interpreter::execute_block(context, block_type, body) + } + + fn run_if(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result { + let body_len = body.len(); + let else_index = body.iter().position(|op| *op == Opcode::Else).unwrap_or(body_len - 1); + let (begin_index, end_index) = if context.pop_value_as()? { + (0, else_index + 1) + } else { + (else_index + 1, body_len) + }; + + if begin_index != end_index { + let frame_position = context.position + 1; + context.push_frame(frame_position, block_type.clone())?; + Interpreter::execute_block(context, block_type, &body[begin_index..end_index]) + } else { + Ok(InstructionOutcome::RunNextInstruction) + } + } + + fn run_else(context: &mut FunctionContext) -> Result { + Ok(InstructionOutcome::PopFrame(0)) + } + + fn run_end(context: &mut FunctionContext) -> Result { + Ok(InstructionOutcome::PopFrame(0)) + } + + fn run_br(context: &mut FunctionContext, label_idx: u32) -> Result { + Ok(InstructionOutcome::PopFrame(label_idx as usize)) + } + + fn run_br_if(context: &mut FunctionContext, label_idx: u32) -> Result { + if context.pop_value_as()? { + Ok(InstructionOutcome::PopFrame(label_idx as usize)) + } else { + Ok(InstructionOutcome::RunNextInstruction) + } + } + + fn run_br_table(context: &mut FunctionContext, table: &Vec, default: u32) -> Result { + let index: u32 = context.pop_value_as()?; + Ok(InstructionOutcome::PopFrame(table.get(index as usize).cloned().unwrap_or(default) as usize)) + } + + fn run_return(context: &mut FunctionContext) -> Result { + Ok(InstructionOutcome::Return) + } + + fn run_call(context: &mut FunctionContext, func_idx: u32) -> Result { + Err(Error::NotImplemented) + } + + fn run_call_indirect(context: &mut FunctionContext, type_idx: u32) -> Result { + Err(Error::NotImplemented) + } + + fn run_drop(context: &mut FunctionContext) -> Result { + context + .pop_value() + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_select(context: &mut FunctionContext) -> Result { + context + .pop_value_triple() + .and_then(|(left, mid, right)| + match (left, mid, right.try_into()) { + (left, mid, Ok(condition)) => Ok((left, mid, condition)), + _ => Err(Error::ValueStack("expected to get int value from stack".into())) + } + ) + .map(|(left, mid, condition)| if condition { left } else { mid }) + .map(|val| context.push_value(val)) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_get_local(context: &mut FunctionContext, index: u32) -> Result { + context.get_local(index as usize) + .map(|value| value.clone()) + .map(|value| context.push_value(value)) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_set_local(context: &mut FunctionContext, index: u32) -> Result { + let arg = context.pop_value()?; + context.set_local(index as usize, arg) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_tee_local(context: &mut FunctionContext, index: u32) -> Result { + let arg = context.top_value()?.clone(); + context.set_local(index as usize, arg) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_get_global(context: &mut FunctionContext, index: u32) -> Result { + context.module() + .global(ItemIndex::IndexSpace(index)) + .and_then(|g| context.push_value(g.get())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_set_global(context: &mut FunctionContext, index: u32) -> Result { + context + .pop_value() + .and_then(|v| context.module().global(ItemIndex::IndexSpace(index)).and_then(|g| g.set(v))) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_load(context: &mut FunctionContext, offset: u32, align: u32) -> Result + where RuntimeValue: From { + context.module() + .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) + .and_then(|m| m.get(effective_address(offset, align)?, 4)) + .map(|b| from_little_endian_bytes::(&b)) + .and_then(|n| context.push_value(n.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_load_extend(context: &mut FunctionContext, offset: u32, align: u32) -> Result + where T: ExtendInto, RuntimeValue: From { + let stack_value: U = context.module() + .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) + .and_then(|m| m.get(effective_address(offset, align)?, mem::size_of::())) + .map(|b| from_little_endian_bytes::(&b)) + .map(|v| v.extend_into())?; + context + .push_value(stack_value.into()) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_store(context: &mut FunctionContext, offset: u32, align: u32) -> Result + where RuntimeValue: TryInto { + let stack_value = context + .pop_value_as::() + .map(|n| to_little_endian_bytes::(n))?; + context.module() + .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) + .and_then(|m| m.set(effective_address(offset, align)?, &stack_value)) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_store_wrap(context: &mut FunctionContext, offset: u32, align: u32) -> Result + where RuntimeValue: TryInto, T: WrapInto { + let stack_value: T = context.pop_value().and_then(|v| v.try_into())?; + let stack_value = to_little_endian_bytes::(stack_value.wrap_into()); + context.module() + .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) + .and_then(|m| m.set(effective_address(offset, align)?, &stack_value)) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_current_memory(context: &mut FunctionContext) -> Result { + context.module() + .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) + .map(|m| m.size()) + .and_then(|s| context.push_value(RuntimeValue::I64(s as i64))) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_grow_memory(context: &mut FunctionContext) -> Result { + let pages: u32 = context.pop_value_as()?; + context.module() + .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) + .and_then(|m| m.grow(pages)) + .and_then(|m| context.push_value(RuntimeValue::I32(m as i32))) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_const(context: &mut FunctionContext, val: RuntimeValue) -> Result { + context + .push_value(val) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_eqz(context: &mut FunctionContext) -> Result + where RuntimeValue: TryInto, T: PartialEq + Default { + context + .pop_value_as::() + .map(|v| RuntimeValue::I32(if v == Default::default() { 1 } else { 0 })) + .and_then(|v| context.push_value(v)) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_eq(context: &mut FunctionContext) -> Result + where RuntimeValue: TryInto, T: PartialEq { + context + .pop_value_pair_as::() + .map(|(left, right)| RuntimeValue::I32(if left == right { 1 } else { 0 })) + .and_then(|v| context.push_value(v)) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_ne(context: &mut FunctionContext) -> Result + where RuntimeValue: TryInto, T: PartialEq { + context + .pop_value_pair_as::() + .map(|(left, right)| RuntimeValue::I32(if left != right { 1 } else { 0 })) + .and_then(|v| context.push_value(v)) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_lt(context: &mut FunctionContext) -> Result + where RuntimeValue: TryInto, T: PartialOrd { + context + .pop_value_pair_as::() + .map(|(left, right)| RuntimeValue::I32(if left < right { 1 } else { 0 })) + .and_then(|v| context.push_value(v)) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_gt(context: &mut FunctionContext) -> Result + where RuntimeValue: TryInto, T: PartialOrd { + context + .pop_value_pair_as::() + .map(|(left, right)| RuntimeValue::I32(if left > right { 1 } else { 0 })) + .and_then(|v| context.push_value(v)) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_lte(context: &mut FunctionContext) -> Result + where RuntimeValue: TryInto, T: PartialOrd { + context + .pop_value_pair_as::() + .map(|(left, right)| RuntimeValue::I32(if left <= right { 1 } else { 0 })) + .and_then(|v| context.push_value(v)) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_gte(context: &mut FunctionContext) -> Result + where RuntimeValue: TryInto, T: PartialOrd { + context + .pop_value_pair_as::() + .map(|(left, right)| RuntimeValue::I32(if left >= right { 1 } else { 0 })) + .and_then(|v| context.push_value(v)) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_clz(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Integer { + context + .pop_value_as::() + .map(|v| v.leading_zeros()) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_ctz(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Integer { + context + .pop_value_as::() + .map(|v| v.trailing_zeros()) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_popcnt(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Integer { + context + .pop_value_as::() + .map(|v| v.count_ones()) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_add(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: ArithmeticOps { + context + .pop_value_pair_as::() + .map(|(left, right)| left.add(right)) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_sub(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: ArithmeticOps { + context + .pop_value_pair_as::() + .map(|(left, right)| left.sub(right)) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_mul(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: ArithmeticOps { + context + .pop_value_pair_as::() + .map(|(left, right)| left.mul(right)) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_div(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: TransmuteInto, U: ArithmeticOps + TransmuteInto { + context + .pop_value_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.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_rem(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: TransmuteInto, U: Integer + TransmuteInto { + context + .pop_value_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.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_and(context: &mut FunctionContext) -> Result + where RuntimeValue: From<::Output> + TryInto, T: ops::BitAnd { + context + .pop_value_pair_as::() + .map(|(left, right)| left.bitand(right)) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_or(context: &mut FunctionContext) -> Result + where RuntimeValue: From<::Output> + TryInto, T: ops::BitOr { + context + .pop_value_pair_as::() + .map(|(left, right)| left.bitor(right)) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_xor(context: &mut FunctionContext) -> Result + where RuntimeValue: From<::Output> + TryInto, T: ops::BitXor { + context + .pop_value_pair_as::() + .map(|(left, right)| left.bitxor(right)) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_shl(context: &mut FunctionContext) -> Result + where RuntimeValue: From<>::Output> + TryInto, T: ops::Shl { + context + .pop_value_pair_as::() + .map(|(left, right)| left.shl(right)) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_shr(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: TransmuteInto, U: ops::Shr, >::Output: TransmuteInto { + context + .pop_value_pair_as::() + .map(|(left, right)| (left.transmute_into(), right.transmute_into())) + .map(|(left, right)| left.shr(right)) + .map(|v| v.transmute_into()) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_rotl(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Integer { + context + .pop_value_pair_as::() + .map(|(left, right)| left.rotl(right)) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_rotr(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Integer { + context + .pop_value_pair_as::() + .map(|(left, right)| left.rotr(right)) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_abs(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { + context + .pop_value_as::() + .map(|v| v.abs()) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_neg(context: &mut FunctionContext) -> Result + where RuntimeValue: From<::Output> + TryInto, T: ops::Neg { + context + .pop_value_as::() + .map(|v| v.neg()) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_ceil(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { + context + .pop_value_as::() + .map(|v| v.ceil()) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_floor(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { + context + .pop_value_as::() + .map(|v| v.floor()) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_trunc(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { + context + .pop_value_as::() + .map(|v| v.trunc()) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_nearest(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { + context + .pop_value_as::() + .map(|v| v.round()) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_sqrt(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { + context + .pop_value_as::() + .map(|v| v.sqrt()) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_min(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { + context + .pop_value_pair_as::() + .map(|(left, right)| left.min(right)) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_max(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { + context + .pop_value_pair_as::() + .map(|(left, right)| left.max(right)) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_copysign(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: Float { + Err(Error::NotImplemented) + } + + fn run_wrap(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: WrapInto { + context + .pop_value_as::() + .map(|v| v.wrap_into()) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_trunc_to_int(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: TryTruncateInto, U: TransmuteInto, { + context + .pop_value_as::() + .and_then(|v| v.try_truncate_into()) + .map(|v| v.transmute_into()) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_extend(context: &mut FunctionContext) -> Result + where RuntimeValue: From + TryInto, T: ExtendInto, U: TransmuteInto { + context + .pop_value_as::() + .map(|v| v.extend_into()) + .map(|v| v.transmute_into()) + .map(|v| context.push_value(v.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn run_reinterpret(context: &mut FunctionContext) -> Result + where RuntimeValue: From, RuntimeValue: TryInto, T: TransmuteInto { + context + .pop_value_as::() + .map(TransmuteInto::transmute_into) + .and_then(|val| context.push_value(val.into())) + .map(|_| InstructionOutcome::RunNextInstruction) + } + + fn execute_block(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result { + debug_assert!(!context.frame_stack.is_empty()); + + // run instructions + context.position = 0; + loop { + // TODO: blocks ends with end => it should work with + // If the current position is now past the end of the sequence, function return + // execution is initiated and execution of the function is thereafter complete. + // if context.position == body_len { + // return Ok(InstructionOutcome::Next); + // } + let instruction = &body[context.position]; +println!("=== RUNNING {:?}", instruction); + match Interpreter::run_instruction(context, instruction)? { + InstructionOutcome::RunInstruction => (), + InstructionOutcome::RunNextInstruction => context.position += 1, + InstructionOutcome::PopFrame(index) => { + context.pop_frame()?; + if index != 0 { + return Ok(InstructionOutcome::PopFrame(index - 1)); + } else { + return Ok(InstructionOutcome::RunInstruction); + } + }, + InstructionOutcome::Return => return Ok(InstructionOutcome::Return), + } + } + } +} + +impl<'a> FunctionContext<'a> { + pub fn new(module: &'a mut ModuleInstance, value_stack: &'a mut VecDeque, frame_stack: &'a mut VecDeque, function: &FunctionType, body: &[Opcode], args: &[RuntimeValue]) -> Result { + let mut context = FunctionContext { + module: module, + // The value stack begins empty. + value_stack: value_stack, + // The control-flow stack begins with an entry holding a label bound to the last instruction in + // the instruction sequence, a limit value of zero, and a signature corresponding to the function's + // return types: + // - If the function's return type sequence is empty, its signature is void. + // - If the function's return type sequence has exactly one element, the signature is that element. + frame_stack: frame_stack, + // The value of each incoming argument is copied to the local with the corresponding index, and the rest of the locals are initialized to all-zeros bit-pattern values. + locals: Vec::from(args), + // The current position starts at the first instruction in the function body. + position: 0, + }; + context.push_frame(body.len() - 1, match function.return_type() { + Some(value_type) => BlockType::Value(value_type), + None => BlockType::NoResult, + })?; + Ok(context) + } + + pub fn module(&mut self) -> &mut ModuleInstance { + self.module + } + + pub fn set_local(&mut self, index: usize, value: RuntimeValue) -> Result { + self.locals.get_mut(index) + .map(|local| *local = value) + .map(|_| InstructionOutcome::RunNextInstruction) + .ok_or(Error::Local(format!("expected to have local with index {}", index))) + } + + pub fn get_local(&mut self, index: usize) -> Result<&RuntimeValue, Error> { + self.locals.get(index) + .ok_or(Error::Local(format!("expected to have local with index {}", index))) + } + + pub fn push_value(&mut self, value: RuntimeValue) -> Result<(), Error> { + self.value_stack.push_back(value); + Ok(()) + } + + pub fn top_value(&mut self) -> Result { + self.value_stack + .back() + .cloned() + .ok_or(Error::ValueStack("non-empty value stack expected".into() )) + } + + pub fn pop_value(&mut self) -> Result { + self.value_stack + .pop_back() + .ok_or(Error::ValueStack("non-empty value stack expected".into())) + } + + pub fn pop_value_as(&mut self) -> Result + where RuntimeValue: TryInto { + self.pop_value() + .and_then(TryInto::try_into) + } + + pub fn pop_value_pair(&mut self) -> Result<(RuntimeValue, RuntimeValue), Error> { + let right = self.pop_value()?; + let left = self.pop_value()?; + Ok((left, right)) + } + + pub fn pop_value_pair_as(&mut self) -> Result<(T, T), Error> + where RuntimeValue: TryInto { + let right = self.pop_value_as()?; + let left = self.pop_value_as()?; + Ok((left, right)) + } + + pub fn pop_value_triple(&mut self) -> Result<(RuntimeValue, RuntimeValue, RuntimeValue), Error> { + let right = self.pop_value()?; + let mid = self.pop_value()?; + let left = self.pop_value()?; + Ok((left, mid, right)) + } + + pub fn push_frame(&mut self, position: usize, signature: BlockType) -> Result<(), Error> { + self.frame_stack.push_back(BlockFrame { + position: position, + value_limit: self.value_stack.len(), + signature: signature, + }); + Ok(()) + } + + pub fn pop_frame(&mut self) -> Result<(), Error> { + let frame = match self.frame_stack.pop_back() { + Some(frame) => frame, + None => return Err(Error::FrameStack("non-empty frame stack expected".into())), + }; + + if frame.value_limit > self.value_stack.len() { + return Err(Error::FrameStack("non-empty frame stack expected".into())); + } + let frame_value = match frame.signature { + BlockType::Value(_) => Some(self.pop_value()?), + BlockType::NoResult => None, + }; + self.value_stack.resize(frame.value_limit, RuntimeValue::I32(0)); + self.position = frame.position; + if let Some(frame_value) = frame_value { + self.push_value(frame_value)?; + } + + Ok(()) + } +} + +impl BlockFrame { + pub fn invalid() -> Self { + BlockFrame { + position: usize::max_value(), + value_limit: usize::max_value(), + signature: BlockType::NoResult, + } + } +} + +fn effective_address(offset: u32, align: u32) -> Result { + if align == 0 { + Ok(offset) + } else { + 1u32.checked_shl(align - 1) + .and_then(|align| align.checked_add(offset)) + .ok_or(Error::Interpreter("invalid memory alignment".into())) + } +} + +fn to_little_endian_bytes(number: T) -> Vec { + unimplemented!() +} + +fn from_little_endian_bytes(buffer: &[u8]) -> T { + unimplemented!() +} + +#[cfg(test)] +mod tests { + use super::super::super::elements::{ValueType, Opcodes, Opcode, BlockType, FunctionType}; + use interpreter::Error; + use interpreter::runner::Interpreter; + use interpreter::value::{RuntimeValue, TryInto}; + + fn run_function_i32(body: &Opcodes, arg: i32) -> Result { + let function_type = FunctionType::new(vec![ValueType::I32], Some(ValueType::I32)); + Interpreter::run_function(&function_type, body.elements(), &[RuntimeValue::I32(arg)]) + .map(|v| v.unwrap().try_into().unwrap()) + } + + #[test] + fn trap() { + let body = Opcodes::new(vec![ + Opcode::Unreachable, // trap + Opcode::End]); + + assert_eq!(run_function_i32(&body, 0).unwrap_err(), Error::Trap); + } + + #[test] + fn nop() { + let body = Opcodes::new(vec![ + Opcode::Nop, // nop + Opcode::I32Const(20), // 20 + Opcode::Nop, // nop + Opcode::End]); + + assert_eq!(run_function_i32(&body, 0).unwrap(), 20); + } + + #[test] + fn if_then() { + let body = Opcodes::new(vec![ + Opcode::I32Const(20), // 20 + Opcode::GetLocal(0), // read argument + Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0 + Opcodes::new(vec![ + Opcode::I32Const(10), // 10 + Opcode::End, // end + ])), + Opcode::End]); + + assert_eq!(run_function_i32(&body, 0).unwrap(), 20); + assert_eq!(run_function_i32(&body, 1).unwrap(), 10); + } + + #[test] + fn if_then_else() { + let body = Opcodes::new(vec![ + Opcode::GetLocal(0), // read argument + Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0 + Opcodes::new(vec![ + Opcode::I32Const(10), // 10 + Opcode::Else, // else + Opcode::I32Const(20), // 20 + Opcode::End, // end + ])), + Opcode::End]); + + assert_eq!(run_function_i32(&body, 0).unwrap(), 20); + assert_eq!(run_function_i32(&body, 1).unwrap(), 10); + } + + #[test] + fn return_from_if() { + let body = Opcodes::new(vec![ + Opcode::GetLocal(0), // read argument + Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0 + Opcodes::new(vec![ + Opcode::I32Const(20), // 20 + Opcode::Return, // return + Opcode::End, + ])), + Opcode::I32Const(10), // 10 + Opcode::End]); + + assert_eq!(run_function_i32(&body, 0).unwrap(), 10); + assert_eq!(run_function_i32(&body, 1).unwrap(), 20); + } + + #[test] + fn block() { + let body = Opcodes::new(vec![ + Opcode::Block(BlockType::Value(ValueType::I32), // mark block + Opcodes::new(vec![ + Opcode::I32Const(10), // 10 + Opcode::End, + ])), + Opcode::End]); + + assert_eq!(run_function_i32(&body, 0).unwrap(), 10); + } + + #[test] + fn loop_block() { + // TODO: test +/* + let body = Opcodes::new(vec![ + Opcode::I32Const(2), // 2 + Opcode::Loop(BlockType::Value(ValueType::I32), // start loop + Opcodes::new(vec![ + Opcode::GetLocal(0), // read argument + Opcode::I32Const(1), // 1 + Opcode::I32Sub, // argument-- + Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0 + Opcodes::new(vec![ + Opcode::I32Const(2), // 2 + Opcode::I32Mul, // prev_val * 2 + Opcode::Br(1), // branch to loop + Opcode::End, // end (if) + ])), + Opcode::End, // end (loop) + ])), + Opcode::End]); // end (fun) + + assert_eq!(run_function_i32(&body, 2).unwrap(), 10); +*/ + } + + #[test] + fn branch() { + // TODO + } + + #[test] + fn branch_if() { + // TODO + } + + #[test] + fn branch_table() { + // TODO + } + + #[test] + fn drop() { + // TODO + } + + #[test] + fn select() { + // TODO + } +} diff --git a/src/interpreter/table.rs b/src/interpreter/table.rs new file mode 100644 index 0000000..370887b --- /dev/null +++ b/src/interpreter/table.rs @@ -0,0 +1,26 @@ +use std::u32; +use std::sync::Arc; +use elements::TableType; +use interpreter::Error; +use interpreter::value::RuntimeValue; + +/// Table instance. +pub struct TableInstance { + /// Table memory buffer. + buffer: Vec, + /// Maximum buffer size. + maximum_size: u32, +} + +impl TableInstance { + pub fn new(table_type: &TableType) -> Result, Error> { + Ok(Arc::new(TableInstance { + buffer: vec![RuntimeValue::Null; table_type.limits().initial() as usize], + maximum_size: table_type.limits().maximum().unwrap_or(u32::MAX), + })) + } + + pub fn set(&self, offset: u32, value: &[u32]) -> Result { + unimplemented!() + } +} diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs new file mode 100644 index 0000000..1c9091d --- /dev/null +++ b/src/interpreter/value.rs @@ -0,0 +1,415 @@ +use std::{i32, i64, u32, u64, f32}; +use std::mem; +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 same module. + AnyFunc(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; +} + +/// 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) -> T; +} + +/// 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) -> T; +} + +/// 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; + /// 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; +} + +impl RuntimeValue { + pub fn decode_f32(val: u32) -> Self { + RuntimeValue::F32(unsafe { mem::transmute(val) }) + } + + pub fn decode_f64(val: u64) -> Self { + RuntimeValue::F64(unsafe { mem::transmute(val) }) + } + + pub fn is_null(&self) -> bool { + match *self { + RuntimeValue::Null => true, + _ => false, + } + } + + pub fn as_any_func_index(&self) -> Option { + match *self { + RuntimeValue::AnyFunc(idx) => Some(idx), + _ => None, + } + } + + 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::F32(val) => Some(val as f64), + 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(unsafe { + mem::transmute(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(unsafe { + mem::transmute(val) + }), + _ => 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> { + if !self.is_normal() { + return Err(Error::Value("invalid float value for this operation".into())); + } + + let truncated = self.trunc(); + if truncated < $into::MIN as $from || truncated > $into::MAX as $from { + return Err(Error::Value("invalid float value for this operation".into())); + } + + Ok(truncated 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 { + ($from: ident, $into: ident) => { + impl TransmuteInto<$into> for $from { + fn transmute_into(self) -> $into { + unsafe { + mem::transmute(self) + } + } + } + } +} + +impl_transmute_into!(i32, u32); +impl_transmute_into!(u32, i32); +impl_transmute_into!(i32, f32); +impl_transmute_into!(f32, i32); +impl_transmute_into!(i64, u64); +impl_transmute_into!(u64, i64); +impl_transmute_into!(i64, f64); +impl_transmute_into!(f64, i64); + +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) -> $type { self.wrapping_div(other) } + } + } +} + +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) -> $type { 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) -> $type { self.wrapping_rem(other) } + } + } +} + +impl_integer!(i32); +impl_integer!(u32); +impl_integer!(i64); +impl_integer!(u64); + +macro_rules! impl_float { + ($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 sqrt(self) -> $type { self.sqrt() } + // TODO + // This instruction corresponds to what is sometimes called "minNaN" in other languages. + // This differs from the IEEE 754-2008 minNum operation in that it returns a NaN if either operand is a NaN, and in that the behavior when the operands are zeros of differing signs is fully specified. + // This differs from the common x $type { self.min(other) } + // TODO + // This instruction corresponds to what is sometimes called "maxNaN" in other languages. + // This differs from the IEEE 754-2008 maxNum operation in that it returns a NaN if either operand is a NaN, and in that the behavior when the operands are zeros of differing signs is fully specified. + // This differs from the common x>y?x:y expansion in its handling of negative zero and NaN values. + fn max(self, other: $type) -> $type { self.max(other) } + } + } +} + +impl_float!(f32); +impl_float!(f64); diff --git a/src/interpreter/variable.rs b/src/interpreter/variable.rs new file mode 100644 index 0000000..4e13763 --- /dev/null +++ b/src/interpreter/variable.rs @@ -0,0 +1,72 @@ +use std::sync::Arc; +use parking_lot::RwLock; +use elements::{GlobalType, ValueType}; +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, +} + +/// Variable instance. +pub struct VariableInstance { + /// Is mutable? + is_mutable: bool, + /// Variable type. + variable_type: VariableType, + /// Global value. + value: RwLock, +} + +impl VariableInstance { + pub fn new(global_type: &GlobalType, value: RuntimeValue) -> Result, Error> { + let variable_type: VariableType = global_type.content_type().into(); + 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(Arc::new(VariableInstance { + is_mutable: global_type.is_mutable(), + variable_type: variable_type, + value: RwLock::new(value), + })) + } + + pub fn get(&self) -> RuntimeValue { + self.value.read().clone() + } + + 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() = value; + Ok(()) + } +} + +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, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index b5f2918..4ce4b2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,9 +3,11 @@ #![warn(missing_docs)] extern crate byteorder; +extern crate parking_lot; pub mod elements; pub mod builder; +pub mod interpreter; pub use elements::{ Error as SerializationError,