mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-22 19:21:59 +00:00
Refactor
This commit is contained in:
@ -366,8 +366,6 @@ impl ModuleInstance {
|
|||||||
return Err(Error::Validation(format!("length of function section is {}, while len of code section is {}", 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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// use data section to initialize linear memory regions
|
// use data section to initialize linear memory regions
|
||||||
if let Some(data_section) = self.module.data_section() {
|
if let Some(data_section) = self.module.data_section() {
|
||||||
for (data_segment_index, data_segment) in data_section.entries().iter().enumerate() {
|
for (data_segment_index, data_segment) in data_section.entries().iter().enumerate() {
|
||||||
|
89
src/validation/context.rs
Normal file
89
src/validation/context.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use elements::{MemoryType, TableType, GlobalType, Type};
|
||||||
|
use elements::{Opcode, BlockType, ValueType, TableElementType};
|
||||||
|
use validation::Error;
|
||||||
|
|
||||||
|
pub struct ModuleContext {
|
||||||
|
pub memories: Vec<MemoryType>,
|
||||||
|
pub tables: Vec<TableType>,
|
||||||
|
pub globals: Vec<GlobalType>,
|
||||||
|
pub types: Vec<Type>,
|
||||||
|
pub func_type_indexes: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleContext {
|
||||||
|
pub fn memories(&self) -> &[MemoryType] {
|
||||||
|
&self.memories
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tables(&self) -> &[TableType] {
|
||||||
|
&self.tables
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn globals(&self) -> &[GlobalType] {
|
||||||
|
&self.globals
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn types(&self) -> &[Type] {
|
||||||
|
&self.types
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ use std::collections::HashMap;
|
|||||||
use elements::{Opcode, BlockType, ValueType, TableElementType, Func, FuncBody};
|
use elements::{Opcode, BlockType, ValueType, TableElementType, Func, FuncBody};
|
||||||
use elements::{FunctionType, Type};
|
use elements::{FunctionType, Type};
|
||||||
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
|
||||||
use validation::module::ModuleContext;
|
use validation::context::ModuleContext;
|
||||||
|
|
||||||
use validation::Error;
|
use validation::Error;
|
||||||
|
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
#![allow(unused, missing_docs)]
|
#![allow(unused, missing_docs)]
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::iter::repeat;
|
||||||
|
use elements::{BlockType, External, FunctionType, GlobalEntry, GlobalType, MemoryType, Module,
|
||||||
|
Opcode, ResizableLimits, TableType, Type, ValueType};
|
||||||
|
use common::stack;
|
||||||
|
use self::context::ModuleContext;
|
||||||
|
use self::func::{FunctionValidationContext, Validator};
|
||||||
|
|
||||||
|
mod context;
|
||||||
mod module;
|
mod module;
|
||||||
mod func;
|
mod func;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
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);
|
pub struct Error(String);
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
@ -34,10 +36,7 @@ pub fn validate_module(module: &Module) -> Result<(), Error> {
|
|||||||
.function_section()
|
.function_section()
|
||||||
.map(|s| s.entries().len())
|
.map(|s| s.entries().len())
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let code_section_len = module
|
let code_section_len = module.code_section().map(|s| s.bodies().len()).unwrap_or(0);
|
||||||
.code_section()
|
|
||||||
.map(|s| s.bodies().len())
|
|
||||||
.unwrap_or(0);
|
|
||||||
if function_section_len != code_section_len {
|
if function_section_len != code_section_len {
|
||||||
return Err(Error(format!(
|
return Err(Error(format!(
|
||||||
"length of function section is {}, while len of code section is {}",
|
"length of function section is {}, while len of code section is {}",
|
||||||
@ -47,19 +46,31 @@ pub fn validate_module(module: &Module) -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// validate every function body in user modules
|
// validate every function body in user modules
|
||||||
if function_section_len != 0 { // tests use invalid code
|
if function_section_len != 0 {
|
||||||
let function_section = module.function_section().expect("function_section_len != 0; qed");
|
// tests use invalid code
|
||||||
let code_section = module.code_section().expect("function_section_len != 0; function_section_len == code_section_len; qed");
|
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
|
// check every function body
|
||||||
for (index, function) in function_section.entries().iter().enumerate() {
|
for (index, function) in function_section.entries().iter().enumerate() {
|
||||||
let function_labels = {
|
let function_labels = {
|
||||||
let function_body = code_section.bodies().get(index as usize).ok_or(Error(format!("Missing body for function {}", index)))?;
|
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| {
|
Validator::validate_function(&context, function, function_body).map_err(|e| {
|
||||||
let Error(ref msg) = e;
|
let Error(ref msg) = e;
|
||||||
Error(format!("Function #{} validation error: {}", index, msg))
|
Error(format!("Function #{} validation error: {}", index, msg))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
// TODO: pepyakin
|
||||||
// context.function_labels()
|
// context.function_labels()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: pepyakin
|
||||||
// self.functions_labels.insert(index as u32, function_labels);
|
// self.functions_labels.insert(index as u32, function_labels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,8 +79,8 @@ pub fn validate_module(module: &Module) -> Result<(), Error> {
|
|||||||
|
|
||||||
fn prepare_context(module: &Module) -> Result<ModuleContext, Error> {
|
fn prepare_context(module: &Module) -> Result<ModuleContext, Error> {
|
||||||
// TODO: Validate start
|
// TODO: Validate start
|
||||||
// TODO: Validate imports
|
// TODO: Validate imports
|
||||||
// TODO: Validate exports
|
// TODO: Validate exports
|
||||||
|
|
||||||
// Copy types from module as is.
|
// Copy types from module as is.
|
||||||
let types = module
|
let types = module
|
||||||
@ -113,44 +124,7 @@ fn prepare_context(module: &Module) -> Result<ModuleContext, Error> {
|
|||||||
}
|
}
|
||||||
if let Some(global_section) = module.global_section() {
|
if let Some(global_section) = module.global_section() {
|
||||||
for global_entry in global_section.entries() {
|
for global_entry in global_section.entries() {
|
||||||
let init = global_entry.init_expr().code();
|
global_entry.validate(&globals)?;
|
||||||
if init.len() != 2 {
|
|
||||||
return Err(Error(format!("Init expression should always be with length 2")));
|
|
||||||
}
|
|
||||||
let init_expr_ty: ValueType = match init[0] {
|
|
||||||
Opcode::I32Const(_) => ValueType::I32,
|
|
||||||
Opcode::I64Const(_) => ValueType::I64,
|
|
||||||
Opcode::F32Const(_) => ValueType::F32,
|
|
||||||
Opcode::F64Const(_) => ValueType::F64,
|
|
||||||
Opcode::GetGlobal(idx) => {
|
|
||||||
match globals.get(idx as usize) {
|
|
||||||
Some(target_global) => {
|
|
||||||
if target_global.is_mutable() {
|
|
||||||
return Err(Error(
|
|
||||||
format!("Global {} is mutable", idx)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
target_global.content_type()
|
|
||||||
},
|
|
||||||
None => return Err(Error(
|
|
||||||
format!("Global {} doesn't exists", idx)
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => return Err(Error(format!("Non constant opcode in init expr"))),
|
|
||||||
};
|
|
||||||
if init_expr_ty != global_entry.global_type().content_type() {
|
|
||||||
return Err(Error(
|
|
||||||
format!(
|
|
||||||
"Trying to initialize variable of type {:?} with value of type {:?}",
|
|
||||||
global_entry.global_type().content_type(),
|
|
||||||
init_expr_ty
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if init[1] != Opcode::End {
|
|
||||||
return Err(Error(format!("Expression doesn't ends with `end` opcode")));
|
|
||||||
}
|
|
||||||
globals.push(global_entry.global_type().clone());
|
globals.push(global_entry.global_type().clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,3 +164,45 @@ impl TableType {
|
|||||||
self.limits().validate()
|
self.limits().validate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GlobalEntry {
|
||||||
|
fn validate(&self, globals_sofar: &[GlobalType]) -> Result<(), Error> {
|
||||||
|
let init = self.init_expr().code();
|
||||||
|
if init.len() != 2 {
|
||||||
|
return Err(Error(
|
||||||
|
format!("Init expression should always be with length 2"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let init_expr_ty: ValueType = match init[0] {
|
||||||
|
Opcode::I32Const(_) => ValueType::I32,
|
||||||
|
Opcode::I64Const(_) => ValueType::I64,
|
||||||
|
Opcode::F32Const(_) => ValueType::F32,
|
||||||
|
Opcode::F64Const(_) => ValueType::F64,
|
||||||
|
Opcode::GetGlobal(idx) => match globals_sofar.get(idx as usize) {
|
||||||
|
Some(target_global) => {
|
||||||
|
if target_global.is_mutable() {
|
||||||
|
return Err(Error(format!("Global {} is mutable", idx)));
|
||||||
|
}
|
||||||
|
target_global.content_type()
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(Error(
|
||||||
|
format!("Global {} doesn't exists or not yet defined", idx),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => return Err(Error(format!("Non constant opcode in init expr"))),
|
||||||
|
};
|
||||||
|
if init_expr_ty != self.global_type().content_type() {
|
||||||
|
return Err(Error(format!(
|
||||||
|
"Trying to initialize variable of type {:?} with value of type {:?}",
|
||||||
|
self.global_type().content_type(),
|
||||||
|
init_expr_ty
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if init[1] != Opcode::End {
|
||||||
|
return Err(Error(format!("Expression doesn't ends with `end` opcode")));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
use elements::{MemoryType, TableType, GlobalType, Type};
|
|
||||||
use elements::{Opcode, BlockType, ValueType, TableElementType};
|
|
||||||
use validation::Error;
|
|
||||||
|
|
||||||
pub struct ModuleContext {
|
|
||||||
pub memories: Vec<MemoryType>,
|
|
||||||
pub tables: Vec<TableType>,
|
|
||||||
pub globals: Vec<GlobalType>,
|
|
||||||
pub types: Vec<Type>,
|
|
||||||
pub func_type_indexes: Vec<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleContext {
|
|
||||||
pub fn memories(&self) -> &[MemoryType] {
|
|
||||||
&self.memories
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tables(&self) -> &[TableType] {
|
|
||||||
&self.tables
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn globals(&self) -> &[GlobalType] {
|
|
||||||
&self.globals
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn types(&self) -> &[Type] {
|
|
||||||
&self.types
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user