diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index cb6e11b..ff6e078 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -39,14 +39,8 @@ pub enum ExportEntryType { Global(VariableType), } -pub struct InstantiationParams { - -} - /// Module instance API. pub trait ModuleInstanceInterface { - /// Run instantiation-time procedures (validation and start function [if any] call). Module is not completely validated until this call. - //fn instantiate<'a>(&self, is_user_module: bool, externals: Option<&'a HashMap>>) -> Result<(), Error>; /// Execute function with the given index. fn execute_index(&self, index: u32, params: ExecutionParams) -> Result, Error>; /// Execute function with the given export name. @@ -214,6 +208,7 @@ impl ModuleInstance { }) } + /// Run instantiation-time procedures (validation). Module is not completely validated until this call. pub fn instantiate<'a>(&mut self, is_user_module: bool, externals: Option<&'a HashMap>>) -> Result<(), Error> { // validate start section if let Some(start_function) = self.module.start_section() { @@ -319,24 +314,27 @@ impl ModuleInstance { 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.module, - &self.imports, - &locals, - DEFAULT_VALUE_STACK_LIMIT, - DEFAULT_FRAME_STACK_LIMIT, - &function_type); - 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 - } - })?; - self.functions_labels.insert(index as u32, context.function_labels()); + let function_labels = { + let mut context = FunctionValidationContext::new( + self, + &locals, + DEFAULT_VALUE_STACK_LIMIT, + DEFAULT_FRAME_STACK_LIMIT, + &function_type); + + 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); } } @@ -369,6 +367,7 @@ impl ModuleInstance { 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() { diff --git a/src/interpreter/table.rs b/src/interpreter/table.rs index 73cb41f..98c7b52 100644 --- a/src/interpreter/table.rs +++ b/src/interpreter/table.rs @@ -29,6 +29,11 @@ impl TableInstance { })) } + /// Get variable type for this table. + pub fn variable_type(&self) -> VariableType { + self.variable_type + } + /// Get the specific value in the table pub fn get(&self, offset: u32) -> Result { let buffer = self.buffer.read(); diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs index 0a89392..84ddfa4 100644 --- a/src/interpreter/tests/basics.rs +++ b/src/interpreter/tests/basics.rs @@ -6,9 +6,8 @@ use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, Global InitExpr, ValueType, BlockType, Opcodes, Opcode, FunctionType}; use interpreter::Error; use interpreter::env_native::{env_native_module, UserFunction, UserFunctions, UserFunctionExecutor, UserFunctionDescriptor}; -use interpreter::imports::ModuleImports; use interpreter::memory::MemoryInstance; -use interpreter::module::{ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams, ExportEntryType}; +use interpreter::module::{ModuleInstance, ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams, ExportEntryType}; use interpreter::program::ProgramInstance; use interpreter::validator::{FunctionValidationContext, Validator}; use interpreter::value::RuntimeValue; @@ -226,9 +225,8 @@ fn env_native_export_entry_type_check() { #[test] fn if_else_with_return_type_validation() { - let module = module().build(); - let imports = ModuleImports::new(Weak::default(), None); - let mut context = FunctionValidationContext::new(&module, &imports, &[], 1024, 1024, &FunctionType::default()); + let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap(); + let mut context = FunctionValidationContext::new(&module_instance, &[], 1024, 1024, &FunctionType::default()); Validator::validate_function(&mut context, BlockType::NoResult, &[ Opcode::I32Const(1), diff --git a/src/interpreter/validator.rs b/src/interpreter/validator.rs index 3313c0f..9c7260a 100644 --- a/src/interpreter/validator.rs +++ b/src/interpreter/validator.rs @@ -1,10 +1,9 @@ use std::u32; -use std::collections::{HashMap, VecDeque}; -use elements::{Module, Opcode, BlockType, FunctionType, ValueType, External, Type}; +use std::collections::HashMap; +use elements::{Opcode, BlockType, FunctionType, ValueType}; use interpreter::Error; use interpreter::runner::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; -use interpreter::imports::ModuleImports; -use interpreter::module::ItemIndex; +use interpreter::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex}; use interpreter::stack::StackWithLimit; use interpreter::variable::VariableType; @@ -13,10 +12,8 @@ const NATURAL_ALIGNMENT: u32 = 0xFFFFFFFF; /// Function validation context. pub struct FunctionValidationContext<'a> { - /// Wasm module. - module: &'a Module, - /// Module imports. - imports: &'a ModuleImports, + /// Wasm module instance (in process of instantiation). + module_instance: &'a ModuleInstance, /// Current instruction position. position: usize, /// Local variables. @@ -571,16 +568,14 @@ impl Validator { impl<'a> FunctionValidationContext<'a> { pub fn new( - module: &'a Module, - imports: &'a ModuleImports, + module_instance: &'a ModuleInstance, locals: &'a [ValueType], value_stack_limit: usize, frame_stack_limit: usize, function: &FunctionType, ) -> Self { FunctionValidationContext { - module: module, - imports: imports, + module_instance: module_instance, position: 0, locals: locals, value_stack: StackWithLimit::with_limit(value_stack_limit), @@ -692,100 +687,45 @@ impl<'a> FunctionValidationContext<'a> { } pub fn require_global(&self, idx: u32, mutability: Option) -> Result { - match self.imports.parse_global_index(ItemIndex::IndexSpace(idx)) { - ItemIndex::IndexSpace(_) => unreachable!("parse_global_index is intended to resolve this"), - ItemIndex::Internal(internal_idx) => self.module - .global_section().ok_or(Error::Validation(format!("Trying to access internal global {} in module without global section", internal_idx))) - .and_then(|s| s.entries().get(internal_idx as usize).ok_or(Error::Validation(format!("Trying to access internal global {} in module with {} globals", internal_idx, s.entries().len())))) - .and_then(|g| match mutability { - Some(true) if !g.global_type().is_mutable() => Err(Error::Validation(format!("Expected internal global {} to be mutable", internal_idx))), - Some(false) if g.global_type().is_mutable() => Err(Error::Validation(format!("Expected internal global {} to be immutable", internal_idx))), - _ => Ok(g), - }) - .map(|g| g.global_type().content_type().into()), - ItemIndex::External(external_idx) => self.module - .import_section().ok_or(Error::Validation(format!("Trying to access external global {} in module without import section", external_idx))) - .and_then(|s| s.entries().get(external_idx as usize).ok_or(Error::Validation(format!("Trying to access external global with index {} in module with {}-entries import section", external_idx, s.entries().len())))) - .and_then(|e| match e.external() { - &External::Global(ref g) => { - match mutability { - Some(true) if !g.is_mutable() => Err(Error::Validation(format!("Expected external global {} to be mutable", external_idx))), - Some(false) if g.is_mutable() => Err(Error::Validation(format!("Expected external global {} to be immutable", external_idx))), - _ => Ok(g.content_type().into()), - } - }, - _ => Err(Error::Validation(format!("Import entry {} expected to import global", external_idx))) - }), - } + self.module_instance + .global(ItemIndex::IndexSpace(idx), None) + .and_then(|g| match mutability { + Some(true) if !g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be mutable", idx))), + Some(false) if g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be immutable", idx))), + _ => match g.variable_type() { + VariableType::AnyFunc => Err(Error::Validation(format!("Expected global {} to have non-AnyFunc type", idx))), + VariableType::I32 => Ok(StackValueType::Specific(ValueType::I32)), + VariableType::I64 => Ok(StackValueType::Specific(ValueType::I64)), + VariableType::F32 => Ok(StackValueType::Specific(ValueType::F32)), + VariableType::F64 => Ok(StackValueType::Specific(ValueType::F64)), + } + }) } pub fn require_memory(&self, idx: u32) -> Result<(), Error> { - match self.imports.parse_memory_index(ItemIndex::IndexSpace(idx)) { - ItemIndex::IndexSpace(_) => unreachable!("parse_memory_index is intended to resolve this"), - ItemIndex::Internal(internal_idx) => self.module - .memory_section().ok_or(Error::Validation(format!("Trying to access internal memory {} in module without memory section", internal_idx))) - .and_then(|s| s.entries().get(internal_idx as usize).ok_or(Error::Validation(format!("Trying to access internal memory {} in module with {} memory regions", internal_idx, s.entries().len())))) - .map(|_| ()), - ItemIndex::External(external_idx) => self.module - .import_section().ok_or(Error::Validation(format!("Trying to access external memory {} in module without import section", external_idx))) - .and_then(|s| s.entries().get(external_idx as usize).ok_or(Error::Validation(format!("Trying to access external memory with index {} in module with {}-entries import section", external_idx, s.entries().len())))) - .and_then(|e| match e.external() { - &External::Memory(_) => Ok(()), - _ => Err(Error::Validation(format!("Import entry {} expected to import memory", external_idx))) - }), - } + self.module_instance + .memory(ItemIndex::IndexSpace(idx)) + .map(|_| ()) } pub fn require_table(&self, idx: u32, variable_type: VariableType) -> Result<(), Error> { - match self.imports.parse_table_index(ItemIndex::IndexSpace(idx)) { - ItemIndex::IndexSpace(_) => unreachable!("parse_table_index is intended to resolve this"), - ItemIndex::Internal(internal_idx) => self.module - .table_section().ok_or(Error::Validation(format!("Trying to access internal table {} in module without table section", internal_idx))) - .and_then(|s| s.entries().get(internal_idx as usize).ok_or(Error::Validation(format!("Trying to access internal table {} in module with {} tables", internal_idx, s.entries().len())))) - .and_then(|t| if variable_type == t.elem_type().into() { - Ok(()) - } else { - Err(Error::Validation(format!("Internal table {} has element type {:?} while {:?} expected", internal_idx, t.elem_type(), variable_type))) - }), - ItemIndex::External(external_idx) => self.module - .import_section().ok_or(Error::Validation(format!("Trying to access external table {} in module without import section", external_idx))) - .and_then(|s| s.entries().get(external_idx as usize).ok_or(Error::Validation(format!("Trying to access external table with index {} in module with {}-entries import section", external_idx, s.entries().len())))) - .and_then(|e| match e.external() { - &External::Table(ref t) => if variable_type == t.elem_type().into() { - Ok(()) - } else { - Err(Error::Validation(format!("External table {} has element type {:?} while {:?} expected", external_idx, t.elem_type(), variable_type))) - }, - _ => Err(Error::Validation(format!("Import entry {} expected to import table", external_idx))) - }), - } + 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> { - match self.imports.parse_function_index(ItemIndex::IndexSpace(idx)) { - ItemIndex::IndexSpace(_) => unreachable!("parse_function_index is intended to resolve this"), - ItemIndex::Internal(internal_idx) => self.module - .function_section().ok_or(Error::Validation(format!("Trying to access internal function {} in module without function section", internal_idx))) - .and_then(|s| s.entries().get(internal_idx as usize).map(|f| f.type_ref()).ok_or(Error::Validation(format!("Trying to access internal function {} in module with {} functions", internal_idx, s.entries().len())))) - .and_then(|tidx| self.require_function_type(tidx)), - ItemIndex::External(external_idx) => self.module - .import_section().ok_or(Error::Validation(format!("Trying to access external function {} in module without import section", external_idx))) - .and_then(|s| s.entries().get(external_idx as usize).ok_or(Error::Validation(format!("Trying to access external function with index {} in module with {}-entries import section", external_idx, s.entries().len())))) - .and_then(|e| match e.external() { - &External::Function(tidx) => Ok(tidx), - _ => Err(Error::Validation(format!("Import entry {} expected to import function", external_idx))) - }) - .and_then(|tidx| self.require_function_type(tidx)), - } + 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 - .type_section().ok_or(Error::Validation(format!("Trying to access internal function {} in module without type section", idx))) - .and_then(|ts| match ts.types().get(idx as usize) { - Some(&Type::Function(ref function_type)) => Ok((function_type.params().to_vec(), function_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult))), - _ => Err(Error::Validation(format!("Trying to access internal function {} with wrong type", idx))), - }) + 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 {