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/mod.rs b/src/interpreter/mod.rs index d382871..802c0d0 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -130,9 +130,7 @@ impl From for Error { } } -mod validator; mod native; -mod imports; mod memory; mod module; mod program; @@ -147,10 +145,9 @@ mod store; mod tests; pub use self::memory::MemoryInstance; -pub use self::module::{ModuleInstance, ModuleInstanceInterface, - ItemIndex, ExportEntryType, CallerContext, ExecutionParams, FunctionSignature}; +pub use self::module::{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}; +pub use self::native::{UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor}; diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index 3361c47..e92c637 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -5,12 +5,9 @@ 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::{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; @@ -23,11 +20,9 @@ const DEFAULT_FRAME_STACK_LIMIT: usize = 1024; /// Execution context. #[derive(Clone)] -pub struct ExecutionParams<'a> { +pub struct ExecutionParams { /// Arguments. pub args: Vec, - /// Execution-local external modules. - pub externals: HashMap>, } /// Export type. @@ -50,34 +45,6 @@ pub enum FunctionSignature<'a> { 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 { @@ -89,26 +56,6 @@ pub enum ItemIndex { 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 @@ -129,17 +76,7 @@ pub struct InternalFunction<'a> { 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, - } - } - +impl ExecutionParams { /// Add argument. pub fn add_argument(mut self, arg: RuntimeValue) -> Self { self.args.push(arg); @@ -147,493 +84,22 @@ impl<'a> ExecutionParams<'a> { } } -impl<'a> Default for ExecutionParams<'a> { +impl Default for ExecutionParams { fn default() -> Self { ExecutionParams { args: Vec::default(), - externals: HashMap::default(), } } } -impl<'a> From> for ExecutionParams<'a> { - fn from(args: Vec) -> ExecutionParams<'a> { +impl<'a> From> for ExecutionParams { + fn from(args: Vec) -> ExecutionParams { ExecutionParams { - args: args, - externals: HashMap::new(), + args: args } } } -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(|mt| MemoryInstance::new(mt).map(Arc::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).map(Arc::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(), &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); - 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> { - panic!("TODO") - // 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> { - // TODO: - panic!() - // 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) -> Self { @@ -664,34 +130,6 @@ pub fn check_limits(limits: &ResizableLimits) -> Result<(), Error> { 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 { diff --git a/src/interpreter/native.rs b/src/interpreter/native.rs index 4c4b079..b21452b 100644 --- a/src/interpreter/native.rs +++ b/src/interpreter/native.rs @@ -5,7 +5,7 @@ use std::borrow::Cow; use parking_lot::RwLock; use elements::{Internal, ValueType}; use interpreter::Error; -use interpreter::module::{ModuleInstanceInterface, ExecutionParams, ItemIndex, +use interpreter::module::{ExecutionParams, ItemIndex, CallerContext, ExportEntryType, InternalFunction, FunctionSignature}; use interpreter::memory::MemoryInstance; use interpreter::table::TableInstance; @@ -80,8 +80,6 @@ pub struct UserDefinedElements { /// Native module instance. pub struct NativeModuleInstance { - /// Underlying module reference. - base: Arc, /// User function executor. executor: RwLock>, /// By-name functions index. @@ -94,196 +92,6 @@ pub struct NativeModuleInstance { 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, ExecutionParams, ExportEntryType, -/// FunctionSignature, ItemIndex, ModuleInstance, -/// ModuleInstanceInterface, ProgramInstance, RuntimeValue, -/// UserDefinedElements, UserError, UserFunctionDescriptor, -/// 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::()? as u32; -/// let a = context.value_stack.pop_as::()? as u32; -/// 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() diff --git a/src/interpreter/program.rs b/src/interpreter/program.rs index 267b685..f1178b3 100644 --- a/src/interpreter/program.rs +++ b/src/interpreter/program.rs @@ -27,7 +27,7 @@ impl ProgramInstance { module: Module, start_exec_params: ExecutionParams, ) -> Result { - let extern_vals = Vec::new(); + let mut extern_vals = Vec::new(); for import_entry in module.import_section().map(|s| s.entries()).unwrap_or(&[]) { let module = self.modules[import_entry.module()]; let extern_val = module diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index b7ff72f..a32f114 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -493,15 +493,14 @@ impl<'store> Interpreter<'store> { ) -> Result { let global = context.module().resolve_global(&self.store, index); let val = self.store.read_global(global); - context.value_stack_mut().push(val).map_err(Into::into)?; + context.value_stack_mut().push(val)?; Ok(InstructionOutcome::RunNextInstruction) } fn run_set_global<'a>(&mut self, context: &mut FunctionContext, index: u32) -> Result { let val = context .value_stack_mut() - .pop() - .map_err(Into::into)?; + .pop()?; let global = context.module().resolve_global(&self.store, index); self.store.write_global(global, val)?; @@ -516,7 +515,7 @@ impl<'store> Interpreter<'store> { .resolve(self.store); let b = m.get(address, mem::size_of::())?; let n = T::from_little_endian(b)?; - context.value_stack_mut().push(n.into()).map_err(Into::into)?; + context.value_stack_mut().push(n.into())?; Ok(InstructionOutcome::RunNextInstruction) } @@ -583,8 +582,7 @@ impl<'store> Interpreter<'store> { let s = m.size(); context .value_stack_mut() - .push(RuntimeValue::I32(s as i32)) - .map_err(Into::into)?; + .push(RuntimeValue::I32(s as i32))?; Ok(InstructionOutcome::RunNextInstruction) } @@ -596,8 +594,7 @@ impl<'store> Interpreter<'store> { let m = m.grow(pages)?; context .value_stack_mut() - .push(RuntimeValue::I32(m as i32)) - .map_err(Into::into)?; + .push(RuntimeValue::I32(m as i32))?; Ok(InstructionOutcome::RunNextInstruction) } @@ -980,7 +977,7 @@ impl<'store> Interpreter<'store> { } impl<'a> FunctionContext { - pub fn new(store: &Store, function: FuncId, value_stack_limit: usize, frame_stack_limit: usize, function_type: &FunctionSignature, args: Vec) -> Self { + pub fn new<'store>(store: &'store Store, function: FuncId, value_stack_limit: usize, frame_stack_limit: usize, function_type: &FunctionSignature, args: Vec) -> Self { let func_instance = function.resolve(store); let module = match *func_instance { FuncInstance::Defined { module, .. } => module, diff --git a/src/interpreter/store.rs b/src/interpreter/store.rs index 3712c35..08061b6 100644 --- a/src/interpreter/store.rs +++ b/src/interpreter/store.rs @@ -263,7 +263,7 @@ impl Store { instance: &mut ModuleInstance, module_id: ModuleId, ) -> Result<(), Error> { - let aux_data = validate_module(module)?; + let mut aux_data = validate_module(module)?; for extern_val in extern_vals { match *extern_val { @@ -440,14 +440,16 @@ impl Store { } fn invoke(&mut self, func: FuncId, params: ExecutionParams) -> Result, Error> { - let ExecutionParams { args, externals } = params; + let ExecutionParams { args } = params; let mut args = StackWithLimit::with_data(args, DEFAULT_VALUE_STACK_LIMIT); let outer = CallerContext::topmost(&mut args); - let func_type = func.resolve(self).func_type().resolve(self); - let func_signature = FunctionSignature::Module(func_type); - let args = prepare_function_args(&func_signature, outer.value_stack)?; - let inner = FunctionContext::new(self, func, outer.value_stack_limit, outer.frame_stack_limit, &func_signature, args); - let interpreter = Interpreter::new(self); + let inner = { + let func_type = func.resolve(self).func_type().resolve(self); + let func_signature = FunctionSignature::Module(func_type); + let args = prepare_function_args(&func_signature, outer.value_stack)?; + FunctionContext::new(self, func, outer.value_stack_limit, outer.frame_stack_limit, &func_signature, args) + }; + let mut interpreter = Interpreter::new(self); interpreter.run_function(inner) } @@ -462,8 +464,8 @@ impl Store { } pub fn read_global(&self, global: GlobalId) -> RuntimeValue { - let global_instance = self.globals.get_mut(global.0 as usize).expect("ID should be always valid"); - global_instance.val + let global_instance = self.globals.get(global.0 as usize).expect("ID should be always valid"); + global_instance.val.clone() } } diff --git a/src/interpreter/validator.rs b/src/interpreter/validator.rs deleted file mode 100644 index 4995354..0000000 --- a/src/interpreter/validator.rs +++ /dev/null @@ -1,808 +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::I32WarpI64 => 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: &Vec, 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::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/lib.rs b/src/lib.rs index 732fdfd..8c61c33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,7 +24,5 @@ pub use elements::{ pub use interpreter::{ ProgramInstance, - ModuleInstance, - ModuleInstanceInterface, RuntimeValue, };