mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-24 20:21:59 +00:00
use ModuleInstance methods for validation
This commit is contained in:
@ -39,14 +39,8 @@ pub enum ExportEntryType {
|
|||||||
Global(VariableType),
|
Global(VariableType),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InstantiationParams {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Module instance API.
|
/// Module instance API.
|
||||||
pub trait ModuleInstanceInterface {
|
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<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<(), Error>;
|
|
||||||
/// Execute function with the given index.
|
/// Execute function with the given index.
|
||||||
fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error>;
|
fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error>;
|
||||||
/// Execute function with the given export name.
|
/// 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<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<(), Error> {
|
pub fn instantiate<'a>(&mut self, is_user_module: bool, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<(), Error> {
|
||||||
// validate start section
|
// validate start section
|
||||||
if let Some(start_function) = self.module.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 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();
|
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)));
|
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);
|
let function_labels = {
|
||||||
Validator::validate_function(&mut context, block_type, function_body.code().elements())
|
let mut context = FunctionValidationContext::new(
|
||||||
.map_err(|e| {
|
self,
|
||||||
if let Error::Validation(msg) = e {
|
&locals,
|
||||||
Error::Validation(format!("Function #{} validation error: {}", index, msg))
|
DEFAULT_VALUE_STACK_LIMIT,
|
||||||
} else {
|
DEFAULT_FRAME_STACK_LIMIT,
|
||||||
e
|
&function_type);
|
||||||
}
|
|
||||||
})?;
|
let block_type = function_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult);
|
||||||
self.functions_labels.insert(index as u32, context.function_labels());
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run start function [if any].
|
||||||
pub fn run_start_function(&self) -> Result<(), Error> {
|
pub fn run_start_function(&self) -> Result<(), Error> {
|
||||||
// execute start function (if any)
|
// execute start function (if any)
|
||||||
if let Some(start_function) = self.module.start_section() {
|
if let Some(start_function) = self.module.start_section() {
|
||||||
|
@ -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
|
/// Get the specific value in the table
|
||||||
pub fn get(&self, offset: u32) -> Result<RuntimeValue, Error> {
|
pub fn get(&self, offset: u32) -> Result<RuntimeValue, Error> {
|
||||||
let buffer = self.buffer.read();
|
let buffer = self.buffer.read();
|
||||||
|
@ -6,9 +6,8 @@ use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, Global
|
|||||||
InitExpr, ValueType, BlockType, Opcodes, Opcode, FunctionType};
|
InitExpr, ValueType, BlockType, Opcodes, Opcode, FunctionType};
|
||||||
use interpreter::Error;
|
use interpreter::Error;
|
||||||
use interpreter::env_native::{env_native_module, UserFunction, UserFunctions, UserFunctionExecutor, UserFunctionDescriptor};
|
use interpreter::env_native::{env_native_module, UserFunction, UserFunctions, UserFunctionExecutor, UserFunctionDescriptor};
|
||||||
use interpreter::imports::ModuleImports;
|
|
||||||
use interpreter::memory::MemoryInstance;
|
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::program::ProgramInstance;
|
||||||
use interpreter::validator::{FunctionValidationContext, Validator};
|
use interpreter::validator::{FunctionValidationContext, Validator};
|
||||||
use interpreter::value::RuntimeValue;
|
use interpreter::value::RuntimeValue;
|
||||||
@ -226,9 +225,8 @@ fn env_native_export_entry_type_check() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_else_with_return_type_validation() {
|
fn if_else_with_return_type_validation() {
|
||||||
let module = module().build();
|
let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap();
|
||||||
let imports = ModuleImports::new(Weak::default(), None);
|
let mut context = FunctionValidationContext::new(&module_instance, &[], 1024, 1024, &FunctionType::default());
|
||||||
let mut context = FunctionValidationContext::new(&module, &imports, &[], 1024, 1024, &FunctionType::default());
|
|
||||||
|
|
||||||
Validator::validate_function(&mut context, BlockType::NoResult, &[
|
Validator::validate_function(&mut context, BlockType::NoResult, &[
|
||||||
Opcode::I32Const(1),
|
Opcode::I32Const(1),
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
use std::u32;
|
use std::u32;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::HashMap;
|
||||||
use elements::{Module, Opcode, BlockType, FunctionType, ValueType, External, Type};
|
use elements::{Opcode, BlockType, FunctionType, ValueType};
|
||||||
use interpreter::Error;
|
use interpreter::Error;
|
||||||
use interpreter::runner::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
use interpreter::runner::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
||||||
use interpreter::imports::ModuleImports;
|
use interpreter::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex};
|
||||||
use interpreter::module::ItemIndex;
|
|
||||||
use interpreter::stack::StackWithLimit;
|
use interpreter::stack::StackWithLimit;
|
||||||
use interpreter::variable::VariableType;
|
use interpreter::variable::VariableType;
|
||||||
|
|
||||||
@ -13,10 +12,8 @@ const NATURAL_ALIGNMENT: u32 = 0xFFFFFFFF;
|
|||||||
|
|
||||||
/// Function validation context.
|
/// Function validation context.
|
||||||
pub struct FunctionValidationContext<'a> {
|
pub struct FunctionValidationContext<'a> {
|
||||||
/// Wasm module.
|
/// Wasm module instance (in process of instantiation).
|
||||||
module: &'a Module,
|
module_instance: &'a ModuleInstance,
|
||||||
/// Module imports.
|
|
||||||
imports: &'a ModuleImports,
|
|
||||||
/// Current instruction position.
|
/// Current instruction position.
|
||||||
position: usize,
|
position: usize,
|
||||||
/// Local variables.
|
/// Local variables.
|
||||||
@ -571,16 +568,14 @@ impl Validator {
|
|||||||
|
|
||||||
impl<'a> FunctionValidationContext<'a> {
|
impl<'a> FunctionValidationContext<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
module: &'a Module,
|
module_instance: &'a ModuleInstance,
|
||||||
imports: &'a ModuleImports,
|
|
||||||
locals: &'a [ValueType],
|
locals: &'a [ValueType],
|
||||||
value_stack_limit: usize,
|
value_stack_limit: usize,
|
||||||
frame_stack_limit: usize,
|
frame_stack_limit: usize,
|
||||||
function: &FunctionType,
|
function: &FunctionType,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
FunctionValidationContext {
|
FunctionValidationContext {
|
||||||
module: module,
|
module_instance: module_instance,
|
||||||
imports: imports,
|
|
||||||
position: 0,
|
position: 0,
|
||||||
locals: locals,
|
locals: locals,
|
||||||
value_stack: StackWithLimit::with_limit(value_stack_limit),
|
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<bool>) -> Result<StackValueType, Error> {
|
pub fn require_global(&self, idx: u32, mutability: Option<bool>) -> Result<StackValueType, Error> {
|
||||||
match self.imports.parse_global_index(ItemIndex::IndexSpace(idx)) {
|
self.module_instance
|
||||||
ItemIndex::IndexSpace(_) => unreachable!("parse_global_index is intended to resolve this"),
|
.global(ItemIndex::IndexSpace(idx), None)
|
||||||
ItemIndex::Internal(internal_idx) => self.module
|
.and_then(|g| match mutability {
|
||||||
.global_section().ok_or(Error::Validation(format!("Trying to access internal global {} in module without global section", internal_idx)))
|
Some(true) if !g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be mutable", 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()))))
|
Some(false) if g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be immutable", idx))),
|
||||||
.and_then(|g| match mutability {
|
_ => match g.variable_type() {
|
||||||
Some(true) if !g.global_type().is_mutable() => Err(Error::Validation(format!("Expected internal global {} to be mutable", internal_idx))),
|
VariableType::AnyFunc => Err(Error::Validation(format!("Expected global {} to have non-AnyFunc type", idx))),
|
||||||
Some(false) if g.global_type().is_mutable() => Err(Error::Validation(format!("Expected internal global {} to be immutable", internal_idx))),
|
VariableType::I32 => Ok(StackValueType::Specific(ValueType::I32)),
|
||||||
_ => Ok(g),
|
VariableType::I64 => Ok(StackValueType::Specific(ValueType::I64)),
|
||||||
})
|
VariableType::F32 => Ok(StackValueType::Specific(ValueType::F32)),
|
||||||
.map(|g| g.global_type().content_type().into()),
|
VariableType::F64 => Ok(StackValueType::Specific(ValueType::F64)),
|
||||||
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)))
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn require_memory(&self, idx: u32) -> Result<(), Error> {
|
pub fn require_memory(&self, idx: u32) -> Result<(), Error> {
|
||||||
match self.imports.parse_memory_index(ItemIndex::IndexSpace(idx)) {
|
self.module_instance
|
||||||
ItemIndex::IndexSpace(_) => unreachable!("parse_memory_index is intended to resolve this"),
|
.memory(ItemIndex::IndexSpace(idx))
|
||||||
ItemIndex::Internal(internal_idx) => self.module
|
.map(|_| ())
|
||||||
.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)))
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn require_table(&self, idx: u32, variable_type: VariableType) -> Result<(), Error> {
|
pub fn require_table(&self, idx: u32, variable_type: VariableType) -> Result<(), Error> {
|
||||||
match self.imports.parse_table_index(ItemIndex::IndexSpace(idx)) {
|
self.module_instance
|
||||||
ItemIndex::IndexSpace(_) => unreachable!("parse_table_index is intended to resolve this"),
|
.table(ItemIndex::IndexSpace(idx))
|
||||||
ItemIndex::Internal(internal_idx) => self.module
|
.and_then(|t| if t.variable_type() == variable_type {
|
||||||
.table_section().ok_or(Error::Validation(format!("Trying to access internal table {} in module without table section", internal_idx)))
|
Ok(())
|
||||||
.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()))))
|
} else {
|
||||||
.and_then(|t| if variable_type == t.elem_type().into() {
|
Err(Error::Validation(format!("Table {} has element type {:?} while {:?} expected", idx, t.variable_type(), variable_type)))
|
||||||
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)))
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn require_function(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
|
pub fn require_function(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
|
||||||
match self.imports.parse_function_index(ItemIndex::IndexSpace(idx)) {
|
self.module_instance.function_type(ItemIndex::IndexSpace(idx))
|
||||||
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index is intended to resolve this"),
|
.map(|ft| (ft.params().to_vec(), ft.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult)))
|
||||||
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)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn require_function_type(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
|
pub fn require_function_type(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
|
||||||
self.module
|
self.module_instance.function_type_by_index(idx)
|
||||||
.type_section().ok_or(Error::Validation(format!("Trying to access internal function {} in module without type section", idx)))
|
.map(|ft| (ft.params().to_vec(), ft.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult)))
|
||||||
.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))),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn function_labels(self) -> HashMap<usize, usize> {
|
pub fn function_labels(self) -> HashMap<usize, usize> {
|
||||||
|
Reference in New Issue
Block a user