Implement functions checking

This commit is contained in:
Sergey Pepyakin
2017-12-01 19:27:36 +03:00
parent d92bfa1b2c
commit 4cf6bf6868
4 changed files with 144 additions and 105 deletions

View File

@ -366,41 +366,7 @@ impl ModuleInstance {
return Err(Error::Validation(format!("length of function section is {}, while len of code section is {}", function_section_len, code_section_len)));
}
// TODO: pepyakin reimplement
// // 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() {

View File

@ -1,7 +1,8 @@
use std::u32;
use std::sync::Arc;
use std::iter::repeat;
use std::collections::HashMap;
use elements::{Opcode, BlockType, ValueType, TableElementType};
use elements::{Opcode, BlockType, ValueType, TableElementType, Func, FuncBody};
use elements::{FunctionType, Type};
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
use validation::module::ModuleContext;
@ -14,6 +15,11 @@ use common::{BlockFrame, BlockFrameType};
/// Constant from wabt' validator.cc to skip alignment validation (not a part of spec).
const NATURAL_ALIGNMENT: u32 = 0xFFFFFFFF;
/// Maximum number of entries in value stack.
const DEFAULT_VALUE_STACK_LIMIT: usize = 16384;
/// Maximum number of entries in frame stack.
const DEFAULT_FRAME_STACK_LIMIT: usize = 1024;
/// Function validation context.
pub struct FunctionValidationContext<'a> {
/// Wasm module
@ -56,9 +62,31 @@ pub enum InstructionOutcome {
}
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)?;
pub fn validate_function(
module: &ModuleContext,
func: &Func,
body: &FuncBody,
) -> Result<(), Error> {
let (params, result_ty) = module.require_function(func.type_ref())?;
// locals = (params + vars)
let mut locals = params;
locals.extend(
body.locals()
.iter()
.flat_map(|l| repeat(l.value_type()).take(l.count() as usize)),
);
let mut context = FunctionValidationContext::new(
&module,
&locals,
DEFAULT_VALUE_STACK_LIMIT,
DEFAULT_FRAME_STACK_LIMIT,
result_ty,
);
context.push_label(BlockFrameType::Function, result_ty)?;
Validator::validate_function_block(&mut context, body.code().elements())?;
while !context.frame_stack.is_empty() {
context.pop_label()?;
}
@ -378,7 +406,7 @@ impl Validator {
}
context.pop_value(ValueType::I32.into())?;
context.require_memory(DEFAULT_MEMORY_INDEX)?;
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
context.push_value(value_type)?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
@ -390,7 +418,7 @@ impl Validator {
}
}
context.require_memory(DEFAULT_MEMORY_INDEX)?;
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
context.pop_value(value_type)?;
context.pop_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
@ -506,7 +534,7 @@ impl Validator {
}
fn validate_call(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
let (argument_types, return_type) = context.require_function(idx)?;
let (argument_types, return_type) = context.module.require_function(idx)?;
for argument_type in argument_types.iter().rev() {
context.pop_value((*argument_type).into())?;
}
@ -517,10 +545,10 @@ impl Validator {
}
fn validate_call_indirect(context: &mut FunctionValidationContext, idx: u32) -> Result<InstructionOutcome, Error> {
context.require_table(DEFAULT_TABLE_INDEX, TableElementType::AnyFunc)?;
context.module.require_table(DEFAULT_TABLE_INDEX, TableElementType::AnyFunc)?;
context.pop_value(ValueType::I32.into())?;
let (argument_types, return_type) = context.require_function_type(idx)?;
let (argument_types, return_type) = context.module.require_function_type(idx)?;
for argument_type in argument_types.iter().rev() {
context.pop_value((*argument_type).into())?;
}
@ -531,13 +559,13 @@ impl Validator {
}
fn validate_current_memory(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.require_memory(DEFAULT_MEMORY_INDEX)?;
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
context.push_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
}
fn validate_grow_memory(context: &mut FunctionValidationContext) -> Result<InstructionOutcome, Error> {
context.require_memory(DEFAULT_MEMORY_INDEX)?;
context.module.require_memory(DEFAULT_MEMORY_INDEX)?;
context.pop_value(ValueType::I32.into())?;
context.push_value(ValueType::I32.into())?;
Ok(InstructionOutcome::ValidateNextInstruction)
@ -550,7 +578,7 @@ impl<'a> FunctionValidationContext<'a> {
locals: &'a [ValueType],
value_stack_limit: usize,
frame_stack_limit: usize,
func_type: &'a FunctionType,
return_type: BlockType,
) -> Self {
FunctionValidationContext {
module: module,
@ -558,7 +586,7 @@ impl<'a> FunctionValidationContext<'a> {
locals: locals,
value_stack: StackWithLimit::with_limit(value_stack_limit),
frame_stack: StackWithLimit::with_limit(frame_stack_limit),
return_type: Some(func_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult)),
return_type: Some(return_type),
labels: HashMap::new(),
}
}
@ -693,62 +721,6 @@ impl<'a> FunctionValidationContext<'a> {
})
}
pub fn require_memory(&self, idx: u32) -> Result<(), Error> {
if self.module.memories().get(idx as usize).is_none() {
return Err(Error(format!("Memory at index {} doesn't exists", idx)));
}
Ok(())
}
pub fn require_table(&self, idx: u32, expected_type: TableElementType) -> Result<(), Error> {
let table = match self.module.tables().get(idx as usize) {
Some(table) => table,
None => {
return Err(Error(format!("Table at index {} doesn't exists", idx)));
}
};
if table.elem_type() != expected_type {
return Err(Error(format!(
"Table {} has element type {:?} while {:?} expected",
idx,
table.elem_type(),
expected_type
)));
}
Ok(())
}
pub fn require_function(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
let ty_idx = match self.module.func_type_indexes().get(idx as usize) {
Some(ty_idx) => *ty_idx,
None => {
return Err(Error(
format!("Function at index {} doesn't exists", idx),
));
}
};
self.require_function_type(ty_idx)
}
pub fn require_function_type(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
let ty = match self.module.types().get(idx as usize) {
Some(&Type::Function(ref func_ty)) => func_ty,
None => {
return Err(Error(
format!("Type at index {} doesn't exists", idx),
));
}
};
let params = ty.params().to_vec();
let return_ty = ty.return_type()
.map(BlockType::Value)
.unwrap_or(BlockType::NoResult);
Ok((params, return_ty))
}
pub fn function_labels(self) -> HashMap<usize, usize> {
self.labels
}

View File

@ -4,9 +4,11 @@ mod module;
mod func;
use std::fmt;
use elements::{Module, ResizableLimits, MemoryType, TableType, GlobalType, FunctionType, External, Opcode, ValueType};
use std::iter::repeat;
use elements::{Module, ResizableLimits, MemoryType, TableType, GlobalType, FunctionType, External, Opcode, ValueType, BlockType, Type};
use common::stack;
use self::module::ModuleContext;
use self::func::{Validator, FunctionValidationContext};
pub struct Error(String);
@ -23,10 +25,49 @@ impl From<stack::Error> for Error {
}
pub fn validate_module(module: &Module) -> Result<(), Error> {
prepare_context(module).map(|_| ())
let context = prepare_context(module)?;
let function_section_len = module
.function_section()
.map(|s| s.entries().len())
.unwrap_or(0);
let code_section_len = module
.code_section()
.map(|s| s.bodies().len())
.unwrap_or(0);
if function_section_len != code_section_len {
return Err(Error(format!(
"length of function section is {}, while len of code section is {}",
function_section_len,
code_section_len
)));
}
// validate every function body in user modules
if function_section_len != 0 { // tests use invalid code
let function_section = module.function_section().expect("function_section_len != 0; qed");
let code_section = module.code_section().expect("function_section_len != 0; function_section_len == code_section_len; qed");
// check every function body
for (index, function) in function_section.entries().iter().enumerate() {
let function_labels = {
let function_body = code_section.bodies().get(index as usize).ok_or(Error(format!("Missing body for function {}", index)))?;
Validator::validate_function(&context, function, function_body).map_err(|e| {
let Error(ref msg) = e;
Error(format!("Function #{} validation error: {}", index, msg))
})?;
// context.function_labels()
};
// self.functions_labels.insert(index as u32, function_labels);
}
}
Ok(())
}
fn prepare_context(module: &Module) -> Result<ModuleContext, Error> {
// TODO: Validate start
// TODO: Validate imports
// TODO: Validate exports
// Copy types from module as is.
let types = module
.type_section()
@ -34,6 +75,8 @@ fn prepare_context(module: &Module) -> Result<ModuleContext, Error> {
.unwrap_or_default();
// Fill elements with imported values.
// TODO: Use Func::type_ref?
let mut func_type_indexes = Vec::new();
let mut tables = Vec::new();
let mut memories = Vec::new();

View File

@ -1,4 +1,6 @@
use elements::{MemoryType, TableType, GlobalType, Type};
use elements::{Opcode, BlockType, ValueType, TableElementType};
use validation::Error;
pub struct ModuleContext {
pub memories: Vec<MemoryType>,
@ -28,4 +30,60 @@ impl ModuleContext {
pub fn func_type_indexes(&self) -> &[u32] {
&self.func_type_indexes
}
pub fn require_memory(&self, idx: u32) -> Result<(), Error> {
if self.memories().get(idx as usize).is_none() {
return Err(Error(format!("Memory at index {} doesn't exists", idx)));
}
Ok(())
}
pub fn require_table(&self, idx: u32, expected_type: TableElementType) -> Result<(), Error> {
let table = match self.tables().get(idx as usize) {
Some(table) => table,
None => {
return Err(Error(format!("Table at index {} doesn't exists", idx)));
}
};
if table.elem_type() != expected_type {
return Err(Error(format!(
"Table {} has element type {:?} while {:?} expected",
idx,
table.elem_type(),
expected_type
)));
}
Ok(())
}
pub fn require_function(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
let ty_idx = match self.func_type_indexes().get(idx as usize) {
Some(ty_idx) => *ty_idx,
None => {
return Err(Error(
format!("Function at index {} doesn't exists", idx),
));
}
};
self.require_function_type(ty_idx)
}
pub fn require_function_type(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
let ty = match self.types().get(idx as usize) {
Some(&Type::Function(ref func_ty)) => func_ty,
None => {
return Err(Error(
format!("Type at index {} doesn't exists", idx),
));
}
};
let params = ty.params().to_vec();
let return_ty = ty.return_type()
.map(BlockType::Value)
.unwrap_or(BlockType::NoResult);
Ok((params, return_ty))
}
}