From 990cf7fbf2686742c1211ed193f16d5b5186a99f Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 08:58:25 +0300 Subject: [PATCH 01/43] Mem validation --- src/validation/mod.rs | 71 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 src/validation/mod.rs diff --git a/src/validation/mod.rs b/src/validation/mod.rs new file mode 100644 index 0000000..2caa7f6 --- /dev/null +++ b/src/validation/mod.rs @@ -0,0 +1,71 @@ +use elements::{Module, ResizableLimits, MemoryType}; + +pub struct Error(pub String); + +pub fn validate_module(module: &Module) -> Result<(), Error> { + if let Some(mem_section) = module.memory_section() { + mem_section + .entries() + .iter() + .map(MemoryType::validate) + .collect::>()? + } + + Ok(()) +} + +impl ResizableLimits { + fn validate(&self) -> Result<(), Error> { + if let Some(maximum) = self.maximum() { + if self.initial() >= maximum { + return Err(Error(format!( + "maximum limit {} is lesser than minimum {}", + maximum, + self.initial() + ))); + } + } + Ok(()) + } +} + +impl MemoryType { + fn validate(&self) -> Result<(), Error> { + self.limits().validate() + } +} + +#[cfg(test)] +mod tests { + use super::validate_module; + use builder::module; + use elements::{BlockType, ExportEntry, External, FunctionType, GlobalEntry, GlobalType, + ImportEntry, InitExpr, Internal, MemoryType, Opcode, Opcodes, TableType, + ValueType}; + + #[test] + fn empty_is_valid() { + let module = module().build(); + assert!(validate_module(&module).is_ok()); + } + + #[test] + fn mem_limits() { + // min > max + let m = module() + .memory() + .with_min(10) + .with_max(Some(9)) + .build() + .build(); + assert!(validate_module(&m).is_err()); + + // mod is always valid without max. + let m = module() + .memory() + .with_min(10) + .build() + .build(); + assert!(validate_module(&m).is_ok()); + } +} From e46690d64aaae2805a0c9dcde91b0967f8bdfcd5 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 09:00:42 +0300 Subject: [PATCH 02/43] Fix condition. --- src/lib.rs | 1 + src/validation/mod.rs | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f96cd2f..2f94d29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ extern crate parking_lot; pub mod elements; pub mod builder; pub mod interpreter; +pub mod validation; pub use elements::{ Error as SerializationError, diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 2caa7f6..8415f3b 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -17,7 +17,7 @@ pub fn validate_module(module: &Module) -> Result<(), Error> { impl ResizableLimits { fn validate(&self) -> Result<(), Error> { if let Some(maximum) = self.maximum() { - if self.initial() >= maximum { + if self.initial() > maximum { return Err(Error(format!( "maximum limit {} is lesser than minimum {}", maximum, @@ -60,6 +60,15 @@ mod tests { .build(); assert!(validate_module(&m).is_err()); + // min = max + let m = module() + .memory() + .with_min(10) + .with_max(Some(10)) + .build() + .build(); + assert!(validate_module(&m).is_ok()); + // mod is always valid without max. let m = module() .memory() From 28199fdac8953c60cfe2167ac3e9d17e0d9de4dc Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 09:05:33 +0300 Subject: [PATCH 03/43] Add table validation. --- src/validation/mod.rs | 47 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 8415f3b..47344a5 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -1,4 +1,4 @@ -use elements::{Module, ResizableLimits, MemoryType}; +use elements::{Module, ResizableLimits, MemoryType, TableType}; pub struct Error(pub String); @@ -11,6 +11,14 @@ pub fn validate_module(module: &Module) -> Result<(), Error> { .collect::>()? } + if let Some(table_section) = module.table_section() { + table_section + .entries() + .iter() + .map(TableType::validate) + .collect::>()? + } + Ok(()) } @@ -35,6 +43,12 @@ impl MemoryType { } } +impl TableType { + fn validate(&self) -> Result<(), Error> { + self.limits().validate() + } +} + #[cfg(test)] mod tests { use super::validate_module; @@ -69,7 +83,7 @@ mod tests { .build(); assert!(validate_module(&m).is_ok()); - // mod is always valid without max. + // mem is always valid without max. let m = module() .memory() .with_min(10) @@ -77,4 +91,33 @@ mod tests { .build(); assert!(validate_module(&m).is_ok()); } + + #[test] + fn table_limits() { + // min > max + let m = module() + .table() + .with_min(10) + .with_max(Some(9)) + .build() + .build(); + assert!(validate_module(&m).is_err()); + + // min = max + let m = module() + .table() + .with_min(10) + .with_max(Some(10)) + .build() + .build(); + assert!(validate_module(&m).is_ok()); + + // table is always valid without max. + let m = module() + .table() + .with_min(10) + .build() + .build(); + assert!(validate_module(&m).is_ok()); + } } From 12a1bcfe04f55bbf0f3ad80990978c2118c4dc7f Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 09:07:56 +0300 Subject: [PATCH 04/43] Reorganize --- src/validation/mod.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 47344a5..a22d606 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -1,15 +1,11 @@ +#![allow(unused, missing_docs)] + use elements::{Module, ResizableLimits, MemoryType, TableType}; pub struct Error(pub String); pub fn validate_module(module: &Module) -> Result<(), Error> { - if let Some(mem_section) = module.memory_section() { - mem_section - .entries() - .iter() - .map(MemoryType::validate) - .collect::>()? - } + // TODO: Functions if let Some(table_section) = module.table_section() { table_section @@ -19,6 +15,14 @@ pub fn validate_module(module: &Module) -> Result<(), Error> { .collect::>()? } + if let Some(mem_section) = module.memory_section() { + mem_section + .entries() + .iter() + .map(MemoryType::validate) + .collect::>()? + } + Ok(()) } From 040cbb50565b5736f9425dc3f189f7eb7d24152f Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 15:35:01 +0300 Subject: [PATCH 05/43] Validator extracted. --- src/common/mod.rs | 40 ++ src/common/stack.rs | 106 ++++ src/interpreter/mod.rs | 14 +- src/interpreter/module.rs | 74 +-- src/interpreter/runner.rs | 89 ++-- src/interpreter/stack.rs | 117 +---- src/interpreter/tests/basics.rs | 20 - src/interpreter/validator.rs | 4 +- src/lib.rs | 1 + src/validation/func.rs | 824 ++++++++++++++++++++++++++++++++ src/validation/mod.rs | 41 +- src/validation/module.rs | 26 + 12 files changed, 1161 insertions(+), 195 deletions(-) create mode 100644 src/common/mod.rs create mode 100644 src/common/stack.rs create mode 100644 src/validation/func.rs create mode 100644 src/validation/module.rs diff --git a/src/common/mod.rs b/src/common/mod.rs new file mode 100644 index 0000000..50f72ae --- /dev/null +++ b/src/common/mod.rs @@ -0,0 +1,40 @@ +use elements::BlockType; + +pub mod stack; + +/// Index of default linear memory. +pub const DEFAULT_MEMORY_INDEX: u32 = 0; +/// Index of default table. +pub const DEFAULT_TABLE_INDEX: u32 = 0; + +/// 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, +} diff --git a/src/common/stack.rs b/src/common/stack.rs new file mode 100644 index 0000000..532dca7 --- /dev/null +++ b/src/common/stack.rs @@ -0,0 +1,106 @@ + +use std::collections::VecDeque; +use std::fmt; + +#[derive(Debug)] +pub struct Error(String); + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +/// Stack with limit. +#[derive(Debug)] +pub struct StackWithLimit where T: Clone { + /// Stack values. + values: VecDeque, + /// Stack limit (maximal stack len). + limit: usize, +} + +impl StackWithLimit where T: Clone { + pub fn with_data(data: Vec, limit: usize) -> Self { + StackWithLimit { + values: data.into_iter().collect(), + limit: limit + } + } + + pub fn with_limit(limit: usize) -> Self { + StackWithLimit { + values: VecDeque::new(), + limit: limit + } + } + + pub fn is_empty(&self) -> bool { + self.values.is_empty() + } + + pub fn len(&self) -> usize { + self.values.len() + } + + pub fn limit(&self) -> usize { + self.limit + } + + pub fn values(&self) -> &VecDeque { + &self.values + } + + pub fn top(&self) -> Result<&T, Error> { + self.values + .back() + .ok_or(Error("non-empty stack expected".into())) + } + + pub fn top_mut(&mut self) -> Result<&mut T, Error> { + self.values + .back_mut() + .ok_or(Error("non-empty stack expected".into())) + } + + pub fn get(&self, index: usize) -> Result<&T, Error> { + if index >= self.values.len() { + return Err(Error(format!("trying to get value at position {} on stack of size {}", index, self.values.len()))); + } + + Ok(self.values.get(self.values.len() - 1 - index).expect("checked couple of lines above")) + } + + pub fn push(&mut self, value: T) -> Result<(), Error> { + if self.values.len() >= self.limit { + return Err(Error(format!("exceeded stack limit {}", self.limit))); + } + + self.values.push_back(value); + Ok(()) + } + + pub fn push_penultimate(&mut self, value: T) -> Result<(), Error> { + if self.values.is_empty() { + return Err(Error("trying to insert penultimate element into empty stack".into())); + } + self.push(value)?; + + let last_index = self.values.len() - 1; + let penultimate_index = last_index - 1; + self.values.swap(last_index, penultimate_index); + + Ok(()) + } + + pub fn pop(&mut self) -> Result { + self.values + .pop_back() + .ok_or(Error("non-empty stack expected".into())) + } + + pub fn resize(&mut self, new_size: usize, dummy: T) { + debug_assert!(new_size <= self.values.len()); + self.values.resize(new_size, dummy); + } +} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 5749939..f53ed6e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,6 +1,7 @@ //! WebAssembly interpreter module. use std::any::TypeId; +use validation; /// Custom user error. pub trait UserError: 'static + ::std::fmt::Display + ::std::fmt::Debug { @@ -116,6 +117,18 @@ impl From for Error where U: UserError + Sized { } } +impl From for Error { + fn from(e: validation::Error) -> Self { + Error::Validation(e.to_string()) + } +} + +impl From<::common::stack::Error> for Error { + fn from(e: ::common::stack::Error) -> Self { + Error::Stack(e.to_string()) + } +} + mod native; mod imports; mod memory; @@ -124,7 +137,6 @@ mod program; mod runner; mod stack; mod table; -mod validator; mod value; mod variable; diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index 876d566..6289392 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -1,19 +1,17 @@ use std::collections::HashMap; -use std::iter::repeat; use std::sync::{Arc, Weak}; use std::fmt; -use elements::{Module, InitExpr, Opcode, Type, FunctionType, Internal, External, BlockType, ResizableLimits, Local, ValueType}; +use elements::{Module, InitExpr, Opcode, Type, FunctionType, Internal, External, ResizableLimits, Local, ValueType}; use interpreter::Error; use interpreter::native::UserFunctionDescriptor; use interpreter::imports::ModuleImports; use interpreter::memory::MemoryInstance; use interpreter::program::ProgramInstanceEssence; use interpreter::runner::{Interpreter, FunctionContext, prepare_function_args}; -use interpreter::stack::StackWithLimit; use interpreter::table::TableInstance; -use interpreter::validator::{Validator, FunctionValidationContext}; use interpreter::value::{RuntimeValue, TryInto}; use interpreter::variable::{VariableInstance, VariableType}; +use common::stack::StackWithLimit; /// Maximum number of entries in value stack. const DEFAULT_VALUE_STACK_LIMIT: usize = 16384; @@ -368,40 +366,41 @@ impl ModuleInstance { 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))); + // 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 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); - } - } + // 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() { @@ -670,7 +669,8 @@ impl<'a> CallerContext<'a> { pub fn check_limits(limits: &ResizableLimits) -> Result<(), Error> { if let Some(maximum) = limits.maximum() { if maximum < limits.initial() { - return Err(Error::Validation(format!("maximum limit {} is lesser than minimum {}", maximum, limits.initial()))); + panic!() + // return Err(Error::Validation(format!("maximum limit {} is lesser than minimum {}", maximum, limits.initial()))); } } diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index 23b2485..5e5f38b 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -8,18 +8,13 @@ use std::collections::{HashMap, VecDeque}; use elements::{Opcode, BlockType, Local}; use interpreter::Error; use interpreter::module::{ModuleInstanceInterface, CallerContext, ItemIndex, InternalFunctionReference, FunctionSignature}; -use interpreter::stack::StackWithLimit; use interpreter::value::{ RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto, ArithmeticOps, Integer, Float, LittleEndianConvert, TransmuteInto, }; -use interpreter::validator::{BlockFrame, BlockFrameType}; use interpreter::variable::VariableInstance; - -/// Index of default linear memory. -pub const DEFAULT_MEMORY_INDEX: u32 = 0; -/// Index of default table. -pub const DEFAULT_TABLE_INDEX: u32 = 0; +use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, BlockFrame, BlockFrameType}; +use common::stack::{StackWithLimit}; /// Function interpreter. pub struct Interpreter; @@ -439,6 +434,7 @@ impl Interpreter { context .value_stack_mut() .pop() + .map_err(Into::into) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -479,7 +475,7 @@ impl Interpreter { fn run_get_global<'a>(context: &mut FunctionContext, index: u32) -> Result, Error> { context.module() .global(ItemIndex::IndexSpace(index), None, Some(context.externals)) - .and_then(|g| context.value_stack_mut().push(g.get())) + .and_then(|g| context.value_stack_mut().push(g.get()).map_err(Into::into)) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -487,6 +483,7 @@ impl Interpreter { context .value_stack_mut() .pop() + .map_err(Into::into) .and_then(|v| context.module().global(ItemIndex::IndexSpace(index), None, Some(context.externals)).and_then(|g| g.set(v))) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -498,7 +495,7 @@ impl Interpreter { .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) .and_then(|m| m.get(address, mem::size_of::())) .and_then(|b| T::from_little_endian(b)) - .and_then(|n| context.value_stack_mut().push(n.into())) + .and_then(|n| context.value_stack_mut().push(n.into()).map_err(Into::into)) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -513,6 +510,7 @@ impl Interpreter { context .value_stack_mut() .push(stack_value.into()) + .map_err(Into::into) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -529,9 +527,21 @@ impl Interpreter { .map(|_| InstructionOutcome::RunNextInstruction) } - fn run_store_wrap<'a, T, U>(context: &mut FunctionContext, _align: u32, offset: u32) -> Result, Error> - where RuntimeValue: TryInto, T: WrapInto, U: LittleEndianConvert { - let stack_value: T = context.value_stack_mut().pop().and_then(|v| v.try_into())?; + fn run_store_wrap<'a, T, U>( + context: &mut FunctionContext, + _align: u32, + offset: u32, + ) -> Result, Error> + where + RuntimeValue: TryInto, + T: WrapInto, + U: LittleEndianConvert, + { + let stack_value: T = context + .value_stack_mut() + .pop() + .map_err(Into::into) + .and_then(|v| v.try_into())?; let stack_value = stack_value.wrap_into().into_little_endian(); let address = effective_address(offset, context.value_stack_mut().pop_as::()?)?; context.module() @@ -541,19 +551,31 @@ impl Interpreter { } fn run_current_memory<'a>(context: &mut FunctionContext) -> Result, Error> { - context.module() + context + .module() .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) .map(|m| m.size()) - .and_then(|s| context.value_stack_mut().push(RuntimeValue::I32(s as i32))) + .and_then(|s| { + context + .value_stack_mut() + .push(RuntimeValue::I32(s as i32)) + .map_err(Into::into) + }) .map(|_| InstructionOutcome::RunNextInstruction) } fn run_grow_memory<'a>(context: &mut FunctionContext) -> Result, Error> { let pages: u32 = context.value_stack_mut().pop_as()?; - context.module() + context + .module() .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) .and_then(|m| m.grow(pages)) - .and_then(|m| context.value_stack_mut().push(RuntimeValue::I32(m as i32))) + .and_then(|m| { + context + .value_stack_mut() + .push(RuntimeValue::I32(m as i32)) + .map_err(Into::into) + }) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -561,6 +583,7 @@ impl Interpreter { context .value_stack_mut() .push(val) + .map_err(Into::into) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -570,7 +593,7 @@ impl Interpreter { .value_stack_mut() .pop_as::() .map(|v| RuntimeValue::I32(if v == Default::default() { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v)) + .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -580,7 +603,7 @@ impl Interpreter { .value_stack_mut() .pop_pair_as::() .map(|(left, right)| RuntimeValue::I32(if left == right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v)) + .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -590,7 +613,7 @@ impl Interpreter { .value_stack_mut() .pop_pair_as::() .map(|(left, right)| RuntimeValue::I32(if left != right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v)) + .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -600,7 +623,7 @@ impl Interpreter { .value_stack_mut() .pop_pair_as::() .map(|(left, right)| RuntimeValue::I32(if left < right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v)) + .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -610,7 +633,7 @@ impl Interpreter { .value_stack_mut() .pop_pair_as::() .map(|(left, right)| RuntimeValue::I32(if left > right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v)) + .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -620,7 +643,7 @@ impl Interpreter { .value_stack_mut() .pop_pair_as::() .map(|(left, right)| RuntimeValue::I32(if left <= right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v)) + .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -630,7 +653,7 @@ impl Interpreter { .value_stack_mut() .pop_pair_as::() .map(|(left, right)| RuntimeValue::I32(if left >= right { 1 } else { 0 })) - .and_then(|v| context.value_stack_mut().push(v)) + .and_then(|v| context.value_stack_mut().push(v).map_err(Into::into)) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -911,11 +934,18 @@ impl Interpreter { .map(|_| InstructionOutcome::RunNextInstruction) } - fn run_extend<'a, T, U, V>(context: &mut FunctionContext) -> Result, Error> - where RuntimeValue: From + TryInto, T: ExtendInto, U: TransmuteInto { + fn run_extend<'a, T, U, V>( + context: &mut FunctionContext, + ) -> Result, Error> + where + RuntimeValue: From + TryInto, + T: ExtendInto, + U: TransmuteInto, + { context .value_stack_mut() .pop_as::() + .map_err(Error::into) .map(|v| v.extend_into()) .map(|v| v.transmute_into()) .map(|v| context.value_stack_mut().push(v.into())) @@ -928,7 +958,7 @@ impl Interpreter { .value_stack_mut() .pop_as::() .map(TransmuteInto::transmute_into) - .and_then(|val| context.value_stack_mut().push(val.into())) + .and_then(|val| context.value_stack_mut().push(val.into()).map_err(Into::into)) .map(|_| InstructionOutcome::RunNextInstruction) } } @@ -1038,19 +1068,18 @@ impl<'a> FunctionContext<'a> { BlockFrameType::Function => usize::MAX, _ => labels[&begin_position] + 1, }; - self.frame_stack.push(BlockFrame { + Ok(self.frame_stack.push(BlockFrame { frame_type: frame_type, block_type: block_type, begin_position: begin_position, branch_position: branch_position, end_position: end_position, value_stack_len: self.value_stack.len(), - }) + })?) } pub fn discard_frame(&mut self) -> Result<(), Error> { - self.frame_stack.pop() - .map(|_| ()) + Ok(self.frame_stack.pop().map(|_| ())?) } pub fn pop_frame(&mut self, is_branch: bool) -> Result<(), Error> { diff --git a/src/interpreter/stack.rs b/src/interpreter/stack.rs index 77f5fb7..ae81933 100644 --- a/src/interpreter/stack.rs +++ b/src/interpreter/stack.rs @@ -1,121 +1,32 @@ -use std::collections::VecDeque; -use interpreter::Error; +use interpreter::{Error as InterpreterError}; use interpreter::value::{RuntimeValue, TryInto}; - -/// Stack with limit. -#[derive(Debug)] -pub struct StackWithLimit where T: Clone { - /// Stack values. - values: VecDeque, - /// Stack limit (maximal stack len). - limit: usize, -} - -impl StackWithLimit where T: Clone { - pub fn with_data(data: Vec, limit: usize) -> Self { - StackWithLimit { - values: data.into_iter().collect(), - limit: limit - } - } - - pub fn with_limit(limit: usize) -> Self { - StackWithLimit { - values: VecDeque::new(), - limit: limit - } - } - - pub fn is_empty(&self) -> bool { - self.values.is_empty() - } - - pub fn len(&self) -> usize { - self.values.len() - } - - pub fn limit(&self) -> usize { - self.limit - } - - pub fn values(&self) -> &VecDeque { - &self.values - } - - pub fn top(&self) -> Result<&T, Error> { - self.values - .back() - .ok_or(Error::Stack("non-empty stack expected".into())) - } - - pub fn top_mut(&mut self) -> Result<&mut T, Error> { - self.values - .back_mut() - .ok_or(Error::Stack("non-empty stack expected".into())) - } - - pub fn get(&self, index: usize) -> Result<&T, Error> { - if index >= self.values.len() { - return Err(Error::Stack(format!("trying to get value at position {} on stack of size {}", index, self.values.len()))); - } - - Ok(self.values.get(self.values.len() - 1 - index).expect("checked couple of lines above")) - } - - pub fn push(&mut self, value: T) -> Result<(), Error> { - if self.values.len() >= self.limit { - return Err(Error::Stack(format!("exceeded stack limit {}", self.limit))); - } - - self.values.push_back(value); - Ok(()) - } - - pub fn push_penultimate(&mut self, value: T) -> Result<(), Error> { - if self.values.is_empty() { - return Err(Error::Stack("trying to insert penultimate element into empty stack".into())); - } - self.push(value)?; - - let last_index = self.values.len() - 1; - let penultimate_index = last_index - 1; - self.values.swap(last_index, penultimate_index); - - Ok(()) - } - - pub fn pop(&mut self) -> Result { - self.values - .pop_back() - .ok_or(Error::Stack("non-empty stack expected".into())) - } - - pub fn resize(&mut self, new_size: usize, dummy: T) { - debug_assert!(new_size <= self.values.len()); - self.values.resize(new_size, dummy); - } -} +use common::stack::StackWithLimit; impl StackWithLimit { - pub fn pop_as(&mut self) -> Result - where RuntimeValue: TryInto { - self.pop().and_then(TryInto::try_into) + pub fn pop_as(&mut self) -> Result + where + RuntimeValue: TryInto, + { + let value = self.pop()?; + TryInto::try_into(value) } - pub fn pop_pair(&mut self) -> Result<(RuntimeValue, RuntimeValue), Error> { + pub fn pop_pair(&mut self) -> Result<(RuntimeValue, RuntimeValue), InterpreterError> { let right = self.pop()?; let left = self.pop()?; Ok((left, right)) } - pub fn pop_pair_as(&mut self) -> Result<(T, T), Error> - where RuntimeValue: TryInto { + pub fn pop_pair_as(&mut self) -> Result<(T, T), InterpreterError> + where + RuntimeValue: TryInto, + { let right = self.pop_as()?; let left = self.pop_as()?; Ok((left, right)) } - pub fn pop_triple(&mut self) -> Result<(RuntimeValue, RuntimeValue, RuntimeValue), Error> { + pub fn pop_triple(&mut self) -> Result<(RuntimeValue, RuntimeValue, RuntimeValue), InterpreterError> { let right = self.pop()?; let mid = self.pop()?; let left = self.pop()?; diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs index 3efc20f..6eeecef 100644 --- a/src/interpreter/tests/basics.rs +++ b/src/interpreter/tests/basics.rs @@ -472,26 +472,6 @@ fn env_native_export_entry_type_check() { } } -#[test] -fn if_else_with_return_type_validation() { - let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap(); - let mut context = FunctionValidationContext::new(&module_instance, None, &[], 1024, 1024, FunctionSignature::Module(&FunctionType::default())); - - Validator::validate_function(&mut context, BlockType::NoResult, &[ - Opcode::I32Const(1), - Opcode::If(BlockType::NoResult), - Opcode::I32Const(1), - Opcode::If(BlockType::Value(ValueType::I32)), - Opcode::I32Const(1), - Opcode::Else, - Opcode::I32Const(2), - Opcode::End, - Opcode::Drop, - Opcode::End, - Opcode::End, - ]).unwrap(); -} - #[test] fn memory_import_limits_initial() { let core_module = module() diff --git a/src/interpreter/validator.rs b/src/interpreter/validator.rs index 85c0b3a..4662658 100644 --- a/src/interpreter/validator.rs +++ b/src/interpreter/validator.rs @@ -3,9 +3,9 @@ use std::sync::Arc; use std::collections::HashMap; use elements::{Opcode, BlockType, ValueType}; use interpreter::Error; -use interpreter::runner::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; +use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; use interpreter::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, FunctionSignature}; -use interpreter::stack::StackWithLimit; +use common::stack::StackWithLimit; use interpreter::variable::VariableType; /// Constant from wabt' validator.cc to skip alignment validation (not a part of spec). diff --git a/src/lib.rs b/src/lib.rs index 2f94d29..b68662e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ pub mod elements; pub mod builder; pub mod interpreter; pub mod validation; +mod common; pub use elements::{ Error as SerializationError, diff --git a/src/validation/func.rs b/src/validation/func.rs new file mode 100644 index 0000000..e459c0c --- /dev/null +++ b/src/validation/func.rs @@ -0,0 +1,824 @@ +use std::u32; +use std::sync::Arc; +use std::collections::HashMap; +use elements::{Opcode, BlockType, ValueType, TableElementType}; +use elements::{FunctionType, Type}; +use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; +use validation::module::ValidatedModule; + +use validation::Error; + +use common::stack::StackWithLimit; +use common::{BlockFrame, BlockFrameType}; + +/// 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 + module: &'a ValidatedModule, + /// 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), +} + +/// 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("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(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(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(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(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(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("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(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(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, TableElementType::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: &'a ValidatedModule, + locals: &'a [ValueType], + value_stack_limit: usize, + frame_stack_limit: usize, + func_type: &'a FunctionType, + ) -> Self { + FunctionValidationContext { + module: module, + position: 0, + 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)), + 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(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(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(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("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(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 { + let global = match self.module.globals().get(idx as usize) { + Some(global) => global, + None => { + return Err(Error(format!("Global at index {} doesn't exists", idx))); + } + }; + + if let Some(expected_mutable) = mutability { + if expected_mutable && !global.is_mutable() { + return Err(Error(format!("Expected global {} to be mutable", idx))); + } + if !expected_mutable && global.is_mutable() { + return Err(Error(format!("Expected global {} to be immutable", idx))); + } + } + + Ok(match global.content_type() { + ValueType::I32 => StackValueType::Specific(ValueType::I32), + ValueType::I64 => StackValueType::Specific(ValueType::I64), + ValueType::F32 => StackValueType::Specific(ValueType::F32), + ValueType::F64 => StackValueType::Specific(ValueType::F64), + }) + } + + 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, BlockType), Error> { + let ty = match self.module.function_types().get(idx as usize) { + Some(&Type::Function(ref func_ty)) => func_ty, + None => { + return Err(Error( + format!("Function 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 require_function_type(&self, idx: u32) -> Result<(Vec, 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 { + 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("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/validation/mod.rs b/src/validation/mod.rs index a22d606..5cc680d 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -1,8 +1,25 @@ #![allow(unused, missing_docs)] -use elements::{Module, ResizableLimits, MemoryType, TableType}; +mod module; +mod func; -pub struct Error(pub String); +use std::fmt; +use elements::{Module, ResizableLimits, MemoryType, TableType}; +use common::stack; + +pub struct Error(String); + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From for Error { + fn from(e: stack::Error) -> Error { + Error(format!("Stack: {}", e)) + } +} pub fn validate_module(module: &Module) -> Result<(), Error> { // TODO: Functions @@ -124,4 +141,24 @@ mod tests { .build(); assert!(validate_module(&m).is_ok()); } + + // #[test] + // fn if_else_with_return_type_validation() { + // let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap(); + // let mut context = FunctionValidationContext::new(&module_instance, None, &[], 1024, 1024, FunctionSignature::Module(&FunctionType::default())); + + // Validator::validate_function(&mut context, BlockType::NoResult, &[ + // Opcode::I32Const(1), + // Opcode::If(BlockType::NoResult), + // Opcode::I32Const(1), + // Opcode::If(BlockType::Value(ValueType::I32)), + // Opcode::I32Const(1), + // Opcode::Else, + // Opcode::I32Const(2), + // Opcode::End, + // Opcode::Drop, + // Opcode::End, + // Opcode::End, + // ]).unwrap(); + // } } diff --git a/src/validation/module.rs b/src/validation/module.rs new file mode 100644 index 0000000..abe65aa --- /dev/null +++ b/src/validation/module.rs @@ -0,0 +1,26 @@ +use elements::{MemoryType, TableType, GlobalType, Type}; + +pub struct ValidatedModule { +} + +impl ValidatedModule { + pub fn memories(&self) -> &[MemoryType] { + unimplemented!(); + } + + pub fn tables(&self) -> &[TableType] { + unimplemented!(); + } + + pub fn globals(&self) -> &[GlobalType] { + unimplemented!(); + } + + pub fn types(&self) -> &[Type] { + unimplemented!(); + } + + pub fn function_types(&self) -> &[Type] { + unimplemented!(); + } +} From 13920517ad9489983551871383015c283587dfd2 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 15:36:43 +0300 Subject: [PATCH 06/43] Fix tests. --- src/interpreter/module.rs | 3 +-- src/interpreter/tests/basics.rs | 7 +++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index 6289392..ef5e264 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -669,8 +669,7 @@ impl<'a> CallerContext<'a> { pub fn check_limits(limits: &ResizableLimits) -> Result<(), Error> { if let Some(maximum) = limits.maximum() { if maximum < limits.initial() { - panic!() - // return Err(Error::Validation(format!("maximum limit {} is lesser than minimum {}", maximum, limits.initial()))); + return Err(Error::Validation(format!("maximum limit {} is lesser than minimum {}", maximum, limits.initial()))); } } diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs index 6eeecef..333413a 100644 --- a/src/interpreter/tests/basics.rs +++ b/src/interpreter/tests/basics.rs @@ -1,16 +1,15 @@ ///! Basic tests for instructions/constructions, missing in wabt tests -use std::sync::{Arc, Weak}; +use std::sync::Arc; use std::cell::RefCell; use std::collections::HashMap; use builder::module; use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType, - InitExpr, ValueType, BlockType, Opcodes, Opcode, FunctionType, TableType, MemoryType}; + InitExpr, ValueType, Opcodes, Opcode, FunctionType, TableType, MemoryType}; use interpreter::{Error, UserError, ProgramInstance}; use interpreter::native::{native_module, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor}; use interpreter::memory::MemoryInstance; -use interpreter::module::{ModuleInstance, ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams, ExportEntryType, FunctionSignature}; -use interpreter::validator::{FunctionValidationContext, Validator}; +use interpreter::module::{ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams, ExportEntryType, FunctionSignature}; use interpreter::value::{RuntimeValue, TryInto}; use interpreter::variable::{VariableInstance, ExternalVariableValue, VariableType}; use super::utils::program_with_default_env; From 39f33cc32f1739cf21e7718735d7257d26517140 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 16:38:03 +0300 Subject: [PATCH 07/43] Clean --- src/interpreter/validator.rs | 809 ----------------------------------- 1 file changed, 809 deletions(-) delete mode 100644 src/interpreter/validator.rs diff --git a/src/interpreter/validator.rs b/src/interpreter/validator.rs deleted file mode 100644 index 4662658..0000000 --- a/src/interpreter/validator.rs +++ /dev/null @@ -1,809 +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> { - 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()?; - self.value_stack.top().map(Clone::clone) - } - - pub fn unreachable(&mut self) -> Result<(), Error> { - self.value_stack.push(StackValueType::AnyUnlimited) - } - - pub fn top_label(&self) -> Result<&BlockFrame, Error> { - self.frame_stack.top() - } - - pub fn push_label(&mut self, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), Error> { - 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> { - 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::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> { - 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 - } -} From 859d9855996fec1ac4b7171ada695644e23805c6 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 16:38:12 +0300 Subject: [PATCH 08/43] Module fill context --- src/validation/func.rs | 17 ++++++---------- src/validation/mod.rs | 42 +++++++++++++++++++++++++++++++++++++--- src/validation/module.rs | 21 ++++++++++++-------- 3 files changed, 58 insertions(+), 22 deletions(-) diff --git a/src/validation/func.rs b/src/validation/func.rs index e459c0c..9388b30 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; use elements::{Opcode, BlockType, ValueType, TableElementType}; use elements::{FunctionType, Type}; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; -use validation::module::ValidatedModule; +use validation::module::ModuleContext; use validation::Error; @@ -17,7 +17,7 @@ const NATURAL_ALIGNMENT: u32 = 0xFFFFFFFF; /// Function validation context. pub struct FunctionValidationContext<'a> { /// Wasm module - module: &'a ValidatedModule, + module: &'a ModuleContext, /// Current instruction position. position: usize, /// Local variables. @@ -546,7 +546,7 @@ impl Validator { impl<'a> FunctionValidationContext<'a> { pub fn new( - module: &'a ValidatedModule, + module: &'a ModuleContext, locals: &'a [ValueType], value_stack_limit: usize, frame_stack_limit: usize, @@ -721,20 +721,15 @@ impl<'a> FunctionValidationContext<'a> { } pub fn require_function(&self, idx: u32) -> Result<(Vec, BlockType), Error> { - let ty = match self.module.function_types().get(idx as usize) { - Some(&Type::Function(ref func_ty)) => func_ty, + 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), )); } }; - - let params = ty.params().to_vec(); - let return_ty = ty.return_type() - .map(BlockType::Value) - .unwrap_or(BlockType::NoResult); - Ok((params, return_ty)) + self.require_function_type(ty_idx) } pub fn require_function_type(&self, idx: u32) -> Result<(Vec, BlockType), Error> { diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 5cc680d..959358c 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -4,8 +4,9 @@ mod module; mod func; use std::fmt; -use elements::{Module, ResizableLimits, MemoryType, TableType}; +use elements::{Module, ResizableLimits, MemoryType, TableType, GlobalType, External}; use common::stack; +use self::module::ModuleContext; pub struct Error(String); @@ -22,8 +23,6 @@ impl From for Error { } pub fn validate_module(module: &Module) -> Result<(), Error> { - // TODO: Functions - if let Some(table_section) = module.table_section() { table_section .entries() @@ -43,6 +42,43 @@ pub fn validate_module(module: &Module) -> Result<(), Error> { Ok(()) } +fn prepare_context(module: &Module) -> ModuleContext { + // Copy types from module as is. + let types = module + .type_section() + .map(|ts| ts.types().into_iter().cloned().collect()) + .unwrap_or_default(); + + // Fill elements with imported values. + let mut func_type_indexes = Vec::new(); + let mut tables = Vec::new(); + let mut memories = Vec::new(); + let mut globals = Vec::new(); + + for import_entry in module + .import_section() + .map(|i| i.entries()) + .unwrap_or_default() + { + match import_entry.external() { + &External::Function(idx) => func_type_indexes.push(idx), + &External::Table(ref table) => tables.push(table.clone()), + &External::Memory(ref memory) => memories.push(memory.clone()), + &External::Global(ref global) => globals.push(global.clone()), + } + } + + // Concatenate elements with defined in the module. + + ModuleContext { + types, + tables, + memories, + globals, + func_type_indexes, + } +} + impl ResizableLimits { fn validate(&self) -> Result<(), Error> { if let Some(maximum) = self.maximum() { diff --git a/src/validation/module.rs b/src/validation/module.rs index abe65aa..af11f45 100644 --- a/src/validation/module.rs +++ b/src/validation/module.rs @@ -1,26 +1,31 @@ use elements::{MemoryType, TableType, GlobalType, Type}; -pub struct ValidatedModule { +pub struct ModuleContext { + pub memories: Vec, + pub tables: Vec, + pub globals: Vec, + pub types: Vec, + pub func_type_indexes: Vec, } -impl ValidatedModule { +impl ModuleContext { pub fn memories(&self) -> &[MemoryType] { - unimplemented!(); + &self.memories } pub fn tables(&self) -> &[TableType] { - unimplemented!(); + &self.tables } pub fn globals(&self) -> &[GlobalType] { - unimplemented!(); + &self.globals } pub fn types(&self) -> &[Type] { - unimplemented!(); + &self.types } - pub fn function_types(&self) -> &[Type] { - unimplemented!(); + pub fn func_type_indexes(&self) -> &[u32] { + &self.func_type_indexes } } From d92bfa1b2cd150f3c5a5e7bc3789e56d91a5656f Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 18:53:19 +0300 Subject: [PATCH 09/43] Global validation --- src/validation/mod.rs | 194 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 171 insertions(+), 23 deletions(-) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 959358c..04c34f3 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -4,7 +4,7 @@ mod module; mod func; use std::fmt; -use elements::{Module, ResizableLimits, MemoryType, TableType, GlobalType, External}; +use elements::{Module, ResizableLimits, MemoryType, TableType, GlobalType, FunctionType, External, Opcode, ValueType}; use common::stack; use self::module::ModuleContext; @@ -23,26 +23,10 @@ impl From for Error { } pub fn validate_module(module: &Module) -> Result<(), Error> { - if let Some(table_section) = module.table_section() { - table_section - .entries() - .iter() - .map(TableType::validate) - .collect::>()? - } - - if let Some(mem_section) = module.memory_section() { - mem_section - .entries() - .iter() - .map(MemoryType::validate) - .collect::>()? - } - - Ok(()) + prepare_context(module).map(|_| ()) } -fn prepare_context(module: &Module) -> ModuleContext { +fn prepare_context(module: &Module) -> Result { // Copy types from module as is. let types = module .type_section() @@ -69,14 +53,69 @@ fn prepare_context(module: &Module) -> ModuleContext { } // Concatenate elements with defined in the module. + if let Some(table_section) = module.table_section() { + for table_entry in table_section.entries() { + table_entry.validate()?; + tables.push(table_entry.clone()); + } + } + if let Some(mem_section) = module.memory_section() { + for mem_entry in mem_section.entries() { + mem_entry.validate()?; + memories.push(mem_entry.clone()); + } + } + if let Some(global_section) = module.global_section() { + for global_entry in global_section.entries() { + let init = global_entry.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.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()); + } + } - ModuleContext { + Ok(ModuleContext { types, tables, memories, globals, func_type_indexes, - } + }) } impl ResizableLimits { @@ -140,7 +179,7 @@ mod tests { .build(); assert!(validate_module(&m).is_ok()); - // mem is always valid without max. + // mem is always valid without max let m = module() .memory() .with_min(10) @@ -169,7 +208,7 @@ mod tests { .build(); assert!(validate_module(&m).is_ok()); - // table is always valid without max. + // table is always valid without max let m = module() .table() .with_min(10) @@ -178,6 +217,115 @@ mod tests { assert!(validate_module(&m).is_ok()); } + #[test] + fn global_init_const() { + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new( + vec![Opcode::I32Const(42), Opcode::End] + ) + ) + ) + .build(); + assert!(validate_module(&m).is_ok()); + + // without delimiting End opcode + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::I32Const(42)]) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); + + // init expr type differs from declared global type + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I64, true), + InitExpr::new(vec![Opcode::I32Const(42), Opcode::End]) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); + } + + #[test] + fn global_init_global() { + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, false), + InitExpr::new(vec![Opcode::I32Const(0), Opcode::End]) + ) + ) + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End]) + ) + ) + .build(); + assert!(validate_module(&m).is_ok()); + + // get_global can reference only previously defined globals + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End]) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); + + // get_global can reference only const globals + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::I32Const(0), Opcode::End]) + ) + ) + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End]) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); + } + + #[test] + fn global_init_misc() { + // empty init expr + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::End]) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); + + // not an constant opcode used + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::Unreachable, Opcode::End]) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); + } + // #[test] // fn if_else_with_return_type_validation() { // let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap(); From 4cf6bf6868422bb5823b00ac30a7e607975febce Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 19:27:36 +0300 Subject: [PATCH 10/43] Implement functions checking --- src/interpreter/module.rs | 34 ------------ src/validation/func.rs | 110 ++++++++++++++------------------------ src/validation/mod.rs | 47 +++++++++++++++- src/validation/module.rs | 58 ++++++++++++++++++++ 4 files changed, 144 insertions(+), 105 deletions(-) diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index ef5e264..0ff1c2b 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -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() { diff --git a/src/validation/func.rs b/src/validation/func.rs index 9388b30..0960e8c 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -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 { - 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 { - 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 { - 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 { - 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, 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, 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 { self.labels } diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 04c34f3..165ab70 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -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 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 { + // 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 { .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(); diff --git a/src/validation/module.rs b/src/validation/module.rs index af11f45..812c4f4 100644 --- a/src/validation/module.rs +++ b/src/validation/module.rs @@ -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, @@ -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, 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, 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)) + } } From 907b914fbade24879ce407cef1a3b0cbcfde427f Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 19:30:15 +0300 Subject: [PATCH 11/43] Move tests in separate file --- src/validation/mod.rs | 205 +--------------------------------------- src/validation/tests.rs | 198 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+), 202 deletions(-) create mode 100644 src/validation/tests.rs diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 165ab70..578ded6 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -3,6 +3,9 @@ mod module; mod func; +#[cfg(test)] +mod tests; + use std::fmt; use std::iter::repeat; use elements::{Module, ResizableLimits, MemoryType, TableType, GlobalType, FunctionType, External, Opcode, ValueType, BlockType, Type}; @@ -187,205 +190,3 @@ impl TableType { self.limits().validate() } } - -#[cfg(test)] -mod tests { - use super::validate_module; - use builder::module; - use elements::{BlockType, ExportEntry, External, FunctionType, GlobalEntry, GlobalType, - ImportEntry, InitExpr, Internal, MemoryType, Opcode, Opcodes, TableType, - ValueType}; - - #[test] - fn empty_is_valid() { - let module = module().build(); - assert!(validate_module(&module).is_ok()); - } - - #[test] - fn mem_limits() { - // min > max - let m = module() - .memory() - .with_min(10) - .with_max(Some(9)) - .build() - .build(); - assert!(validate_module(&m).is_err()); - - // min = max - let m = module() - .memory() - .with_min(10) - .with_max(Some(10)) - .build() - .build(); - assert!(validate_module(&m).is_ok()); - - // mem is always valid without max - let m = module() - .memory() - .with_min(10) - .build() - .build(); - assert!(validate_module(&m).is_ok()); - } - - #[test] - fn table_limits() { - // min > max - let m = module() - .table() - .with_min(10) - .with_max(Some(9)) - .build() - .build(); - assert!(validate_module(&m).is_err()); - - // min = max - let m = module() - .table() - .with_min(10) - .with_max(Some(10)) - .build() - .build(); - assert!(validate_module(&m).is_ok()); - - // table is always valid without max - let m = module() - .table() - .with_min(10) - .build() - .build(); - assert!(validate_module(&m).is_ok()); - } - - #[test] - fn global_init_const() { - let m = module() - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new( - vec![Opcode::I32Const(42), Opcode::End] - ) - ) - ) - .build(); - assert!(validate_module(&m).is_ok()); - - // without delimiting End opcode - let m = module() - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Opcode::I32Const(42)]) - ) - ) - .build(); - assert!(validate_module(&m).is_err()); - - // init expr type differs from declared global type - let m = module() - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I64, true), - InitExpr::new(vec![Opcode::I32Const(42), Opcode::End]) - ) - ) - .build(); - assert!(validate_module(&m).is_err()); - } - - #[test] - fn global_init_global() { - let m = module() - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, false), - InitExpr::new(vec![Opcode::I32Const(0), Opcode::End]) - ) - ) - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End]) - ) - ) - .build(); - assert!(validate_module(&m).is_ok()); - - // get_global can reference only previously defined globals - let m = module() - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End]) - ) - ) - .build(); - assert!(validate_module(&m).is_err()); - - // get_global can reference only const globals - let m = module() - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Opcode::I32Const(0), Opcode::End]) - ) - ) - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End]) - ) - ) - .build(); - assert!(validate_module(&m).is_err()); - } - - #[test] - fn global_init_misc() { - // empty init expr - let m = module() - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Opcode::End]) - ) - ) - .build(); - assert!(validate_module(&m).is_err()); - - // not an constant opcode used - let m = module() - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Opcode::Unreachable, Opcode::End]) - ) - ) - .build(); - assert!(validate_module(&m).is_err()); - } - - // #[test] - // fn if_else_with_return_type_validation() { - // let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap(); - // let mut context = FunctionValidationContext::new(&module_instance, None, &[], 1024, 1024, FunctionSignature::Module(&FunctionType::default())); - - // Validator::validate_function(&mut context, BlockType::NoResult, &[ - // Opcode::I32Const(1), - // Opcode::If(BlockType::NoResult), - // Opcode::I32Const(1), - // Opcode::If(BlockType::Value(ValueType::I32)), - // Opcode::I32Const(1), - // Opcode::Else, - // Opcode::I32Const(2), - // Opcode::End, - // Opcode::Drop, - // Opcode::End, - // Opcode::End, - // ]).unwrap(); - // } -} diff --git a/src/validation/tests.rs b/src/validation/tests.rs new file mode 100644 index 0000000..d316b08 --- /dev/null +++ b/src/validation/tests.rs @@ -0,0 +1,198 @@ +use super::validate_module; +use builder::module; +use elements::{BlockType, ExportEntry, External, FunctionType, GlobalEntry, GlobalType, + ImportEntry, InitExpr, Internal, MemoryType, Opcode, Opcodes, TableType, + ValueType}; + +#[test] +fn empty_is_valid() { + let module = module().build(); + assert!(validate_module(&module).is_ok()); +} + +#[test] +fn mem_limits() { + // min > max + let m = module() + .memory() + .with_min(10) + .with_max(Some(9)) + .build() + .build(); + assert!(validate_module(&m).is_err()); + + // min = max + let m = module() + .memory() + .with_min(10) + .with_max(Some(10)) + .build() + .build(); + assert!(validate_module(&m).is_ok()); + + // mem is always valid without max + let m = module() + .memory() + .with_min(10) + .build() + .build(); + assert!(validate_module(&m).is_ok()); +} + +#[test] +fn table_limits() { + // min > max + let m = module() + .table() + .with_min(10) + .with_max(Some(9)) + .build() + .build(); + assert!(validate_module(&m).is_err()); + + // min = max + let m = module() + .table() + .with_min(10) + .with_max(Some(10)) + .build() + .build(); + assert!(validate_module(&m).is_ok()); + + // table is always valid without max + let m = module() + .table() + .with_min(10) + .build() + .build(); + assert!(validate_module(&m).is_ok()); +} + +#[test] +fn global_init_const() { + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new( + vec![Opcode::I32Const(42), Opcode::End] + ) + ) + ) + .build(); + assert!(validate_module(&m).is_ok()); + + // without delimiting End opcode + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::I32Const(42)]) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); + + // init expr type differs from declared global type + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I64, true), + InitExpr::new(vec![Opcode::I32Const(42), Opcode::End]) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); +} + +#[test] +fn global_init_global() { + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, false), + InitExpr::new(vec![Opcode::I32Const(0), Opcode::End]) + ) + ) + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End]) + ) + ) + .build(); + assert!(validate_module(&m).is_ok()); + + // get_global can reference only previously defined globals + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End]) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); + + // get_global can reference only const globals + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::I32Const(0), Opcode::End]) + ) + ) + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End]) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); +} + +#[test] +fn global_init_misc() { + // empty init expr + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::End]) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); + + // not an constant opcode used + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::Unreachable, Opcode::End]) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); +} + +// #[test] +// fn if_else_with_return_type_validation() { +// let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap(); +// let mut context = FunctionValidationContext::new(&module_instance, None, &[], 1024, 1024, FunctionSignature::Module(&FunctionType::default())); + +// Validator::validate_function(&mut context, BlockType::NoResult, &[ +// Opcode::I32Const(1), +// Opcode::If(BlockType::NoResult), +// Opcode::I32Const(1), +// Opcode::If(BlockType::Value(ValueType::I32)), +// Opcode::I32Const(1), +// Opcode::Else, +// Opcode::I32Const(2), +// Opcode::End, +// Opcode::Drop, +// Opcode::End, +// Opcode::End, +// ]).unwrap(); +// } From 0feed2fa2197153d143242eeb3cdf8a7f8341a74 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 19:42:34 +0300 Subject: [PATCH 12/43] Refactor --- src/interpreter/module.rs | 2 - src/validation/context.rs | 89 +++++++++++++++++++++++++++ src/validation/func.rs | 2 +- src/validation/mod.rs | 126 +++++++++++++++++++++----------------- src/validation/module.rs | 89 --------------------------- 5 files changed, 161 insertions(+), 147 deletions(-) create mode 100644 src/validation/context.rs diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index 0ff1c2b..3aca8e4 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -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))); } - - // 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() { diff --git a/src/validation/context.rs b/src/validation/context.rs new file mode 100644 index 0000000..812c4f4 --- /dev/null +++ b/src/validation/context.rs @@ -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, + pub tables: Vec, + pub globals: Vec, + pub types: Vec, + pub func_type_indexes: Vec, +} + +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, 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, 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)) + } +} diff --git a/src/validation/func.rs b/src/validation/func.rs index 0960e8c..b713734 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use elements::{Opcode, BlockType, ValueType, TableElementType, Func, FuncBody}; use elements::{FunctionType, Type}; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; -use validation::module::ModuleContext; +use validation::context::ModuleContext; use validation::Error; diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 578ded6..1e339b9 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -1,18 +1,20 @@ #![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 func; #[cfg(test)] 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); impl fmt::Display for Error { @@ -34,10 +36,7 @@ pub fn validate_module(module: &Module) -> Result<(), Error> { .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); + 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 {}", @@ -47,19 +46,31 @@ pub fn validate_module(module: &Module) -> Result<(), Error> { } // 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"); + 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)))?; + 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)) })?; + + // TODO: pepyakin // context.function_labels() }; + + // TODO: pepyakin // 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 { // TODO: Validate start - // TODO: Validate imports - // TODO: Validate exports + // TODO: Validate imports + // TODO: Validate exports // Copy types from module as is. let types = module @@ -113,44 +124,7 @@ fn prepare_context(module: &Module) -> Result { } if let Some(global_section) = module.global_section() { for global_entry in global_section.entries() { - let init = global_entry.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.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"))); - } + global_entry.validate(&globals)?; globals.push(global_entry.global_type().clone()); } } @@ -190,3 +164,45 @@ impl TableType { 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(()) + } +} diff --git a/src/validation/module.rs b/src/validation/module.rs index 812c4f4..e69de29 100644 --- a/src/validation/module.rs +++ b/src/validation/module.rs @@ -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, - pub tables: Vec, - pub globals: Vec, - pub types: Vec, - pub func_type_indexes: Vec, -} - -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, 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, 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)) - } -} From 41e74eb8893c0ec587f03a6cea076429ee71c95d Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 19:57:11 +0300 Subject: [PATCH 13/43] Return ValidatedModule --- src/validation/mod.rs | 25 +++++++++++++++++++++---- src/validation/module.rs | 11 +++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 1e339b9..04fbacd 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -8,6 +8,8 @@ use common::stack; use self::context::ModuleContext; use self::func::{FunctionValidationContext, Validator}; +pub use self::module::ValidatedModule; + mod context; mod module; mod func; @@ -29,7 +31,7 @@ impl From for Error { } } -pub fn validate_module(module: &Module) -> Result<(), Error> { +pub fn validate_module(module: &Module) -> Result { let context = prepare_context(module)?; let function_section_len = module @@ -74,13 +76,28 @@ pub fn validate_module(module: &Module) -> Result<(), Error> { // self.functions_labels.insert(index as u32, function_labels); } } - Ok(()) + + let ModuleContext { + types, + tables, + memories, + globals, + func_type_indexes, + } = context; + + Ok(ValidatedModule { + types, + tables, + memories, + globals, + func_type_indexes, + }) } fn prepare_context(module: &Module) -> Result { // TODO: Validate start - // TODO: Validate imports - // TODO: Validate exports + // TODO: Validate imports + // TODO: Validate exports // Copy types from module as is. let types = module diff --git a/src/validation/module.rs b/src/validation/module.rs index e69de29..649d2d3 100644 --- a/src/validation/module.rs +++ b/src/validation/module.rs @@ -0,0 +1,11 @@ +use elements::{MemoryType, TableType, GlobalType, Type}; +use elements::{Opcode, BlockType, ValueType, TableElementType}; +use validation::Error; + +pub struct ValidatedModule { + pub memories: Vec, + pub tables: Vec, + pub globals: Vec, + pub types: Vec, + pub func_type_indexes: Vec, +} From 5b921b6cf992106e4fefde5e53f99396f636a012 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 20:06:43 +0300 Subject: [PATCH 14/43] Reimpl vanilla validator --- src/interpreter/mod.rs | 1 + src/interpreter/module.rs | 39 +- src/interpreter/validator.rs | 809 +++++++++++++++++++++++++++++++++++ 3 files changed, 848 insertions(+), 1 deletion(-) create mode 100644 src/interpreter/validator.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index f53ed6e..1fe782f 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -129,6 +129,7 @@ impl From<::common::stack::Error> for Error { } } +mod validator; mod native; mod imports; mod memory; diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index 3aca8e4..ae7c12e 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -1,7 +1,8 @@ use std::collections::HashMap; +use std::iter::repeat; use std::sync::{Arc, Weak}; use std::fmt; -use elements::{Module, InitExpr, Opcode, Type, FunctionType, Internal, External, ResizableLimits, Local, ValueType}; +use elements::{Module, InitExpr, Opcode, Type, FunctionType, Internal, External, ResizableLimits, Local, ValueType, BlockType}; use interpreter::Error; use interpreter::native::UserFunctionDescriptor; use interpreter::imports::ModuleImports; @@ -9,6 +10,7 @@ use interpreter::memory::MemoryInstance; use interpreter::program::ProgramInstanceEssence; use interpreter::runner::{Interpreter, 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; @@ -366,6 +368,41 @@ impl ModuleInstance { 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() { diff --git a/src/interpreter/validator.rs b/src/interpreter/validator.rs new file mode 100644 index 0000000..a6db4b3 --- /dev/null +++ b/src/interpreter/validator.rs @@ -0,0 +1,809 @@ +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::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> { + 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 + } +} From 29613aef9c33ad03cb0ab2487a38951ba04d3f54 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 20:08:42 +0300 Subject: [PATCH 15/43] Use new validation. --- src/interpreter/module.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index ae7c12e..f7be5ed 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -232,6 +232,8 @@ impl ModuleInstance { /// 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))?; From 7ea00b975bd36ffc62ae40eda637909a57e8dab3 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 20:27:33 +0300 Subject: [PATCH 16/43] Fix tests. --- src/interpreter/tests/basics.rs | 2 +- src/interpreter/tests/mod.rs | 4 ++-- src/validation/func.rs | 2 +- src/validation/mod.rs | 5 +++++ 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs index 333413a..3361182 100644 --- a/src/interpreter/tests/basics.rs +++ b/src/interpreter/tests/basics.rs @@ -82,7 +82,7 @@ fn wrong_import() { #[test] fn global_get_set() { let module = module() - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(42)]))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(42), Opcode::End]))) .function() .signature().return_type().i32().build() .body().with_opcodes(Opcodes::new(vec![ diff --git a/src/interpreter/tests/mod.rs b/src/interpreter/tests/mod.rs index 8842ad9..b2bf1dc 100644 --- a/src/interpreter/tests/mod.rs +++ b/src/interpreter/tests/mod.rs @@ -18,9 +18,9 @@ mod utils { .with_min(64) .build() .with_export(ExportEntry::new("table".into(), Internal::Table(0))) - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0)]))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0), Opcode::End]))) .with_export(ExportEntry::new("tableBase".into(), Internal::Global(0))) - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0)]))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0), Opcode::End]))) .with_export(ExportEntry::new("memoryBase".into(), Internal::Global(1))) .build(); program.add_module("env", env_module, None).unwrap(); diff --git a/src/validation/func.rs b/src/validation/func.rs index b713734..7c51d65 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -67,7 +67,7 @@ impl Validator { func: &Func, body: &FuncBody, ) -> Result<(), Error> { - let (params, result_ty) = module.require_function(func.type_ref())?; + let (params, result_ty) = module.require_function_type(func.type_ref())?; // locals = (params + vars) let mut locals = params; diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 04fbacd..b0ed35d 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -127,6 +127,11 @@ fn prepare_context(module: &Module) -> Result { } // Concatenate elements with defined in the module. + if let Some(function_section) = module.function_section() { + for func_entry in function_section.entries() { + func_type_indexes.push(func_entry.type_ref()); + } + } if let Some(table_section) = module.table_section() { for table_entry in table_section.entries() { table_entry.validate()?; From 251d4d68dbf8ea415023650ab29c5074116a3759 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Mon, 4 Dec 2017 17:36:35 +0100 Subject: [PATCH 17/43] Validate start_section --- src/validation/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index b0ed35d..02af39a 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -77,6 +77,14 @@ pub fn validate_module(module: &Module) -> Result { } } + // validate start section + if let Some(start_function) = module.start_section() { + let (params, return_ty) = context.require_function(start_function)?; + if return_ty != BlockType::NoResult || params.len() != 0 { + return Err(Error("start function expected to have type [] -> []".into())); + } + } + let ModuleContext { types, tables, @@ -95,7 +103,6 @@ pub fn validate_module(module: &Module) -> Result { } fn prepare_context(module: &Module) -> Result { - // TODO: Validate start // TODO: Validate imports // TODO: Validate exports From 71c97098854c03c4701f03532b67839ad04e2300 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Mon, 4 Dec 2017 18:00:51 +0100 Subject: [PATCH 18/43] get_global can only use imported globals. --- src/validation/mod.rs | 7 ++++++ src/validation/tests.rs | 49 ++++++++++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 02af39a..e8ac12b 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -152,8 +152,15 @@ fn prepare_context(module: &Module) -> Result { } } if let Some(global_section) = module.global_section() { + // Validation of globals is defined over modified context C', which + // contains only imported globals. So we do globals validation + // in two passes, in first we validate globals and after all globals are validated + // add them in globals list. for global_entry in global_section.entries() { global_entry.validate(&globals)?; + } + + for global_entry in global_section.entries() { globals.push(global_entry.global_type().clone()); } } diff --git a/src/validation/tests.rs b/src/validation/tests.rs index d316b08..cd4f0b7 100644 --- a/src/validation/tests.rs +++ b/src/validation/tests.rs @@ -82,17 +82,6 @@ fn global_init_const() { .build(); assert!(validate_module(&m).is_ok()); - // without delimiting End opcode - let m = module() - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Opcode::I32Const(42)]) - ) - ) - .build(); - assert!(validate_module(&m).is_err()); - // init expr type differs from declared global type let m = module() .with_global( @@ -108,10 +97,11 @@ fn global_init_const() { #[test] fn global_init_global() { let m = module() - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, false), - InitExpr::new(vec![Opcode::I32Const(0), Opcode::End]) + .with_import( + ImportEntry::new( + "env".into(), + "ext_global".into(), + External::Global(GlobalType::new(ValueType::I32, false)) ) ) .with_global( @@ -136,9 +126,27 @@ fn global_init_global() { // get_global can reference only const globals let m = module() + .with_import( + ImportEntry::new( + "env".into(), + "ext_global".into(), + External::Global(GlobalType::new(ValueType::I32, true)) + ) + ) .with_global( GlobalEntry::new( GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::GetGlobal(0), Opcode::End]) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); + + // get_global in init_expr can only refer to imported globals. + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0), Opcode::End]) ) ) @@ -154,6 +162,17 @@ fn global_init_global() { #[test] fn global_init_misc() { + // without delimiting End opcode + let m = module() + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::I32Const(42)]) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); + // empty init expr let m = module() .with_global( From 4984eca6d62309bb7f0c0539b43393c667e42c16 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 11:39:51 +0100 Subject: [PATCH 19/43] Clean --- src/validation/mod.rs | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index e8ac12b..450e2d0 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -58,22 +58,14 @@ pub fn validate_module(module: &Module) -> Result { .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)) - })?; - - // TODO: pepyakin - // context.function_labels() - }; - - // TODO: pepyakin - // self.functions_labels.insert(index as u32, 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)) + })?; } } From ad3f88227463171c8aae2894297ffda3a5c3e3ad Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 12:03:06 +0100 Subject: [PATCH 20/43] Validate globals --- src/validation/context.rs | 37 ++++++++++++++++++++++++++----------- src/validation/func.rs | 21 ++++++++++++++++++--- src/validation/mod.rs | 29 +++++++++++++++++++++++++---- 3 files changed, 69 insertions(+), 18 deletions(-) diff --git a/src/validation/context.rs b/src/validation/context.rs index 812c4f4..08bbef0 100644 --- a/src/validation/context.rs +++ b/src/validation/context.rs @@ -38,7 +38,7 @@ impl ModuleContext { Ok(()) } - pub fn require_table(&self, idx: u32, expected_type: TableElementType) -> Result<(), Error> { + pub fn require_table(&self, idx: u32) -> Result<&TableType, Error> { let table = match self.tables().get(idx as usize) { Some(table) => table, None => { @@ -46,16 +46,7 @@ impl ModuleContext { } }; - if table.elem_type() != expected_type { - return Err(Error(format!( - "Table {} has element type {:?} while {:?} expected", - idx, - table.elem_type(), - expected_type - ))); - } - - Ok(()) + Ok(table) } pub fn require_function(&self, idx: u32) -> Result<(Vec, BlockType), Error> { @@ -86,4 +77,28 @@ impl ModuleContext { .unwrap_or(BlockType::NoResult); Ok((params, return_ty)) } + + pub fn require_global( + &self, + idx: u32, + mutability: Option, + ) -> Result<&GlobalType, Error> { + let global = match self.globals().get(idx as usize) { + Some(global) => global, + None => { + return Err(Error(format!("Global at index {} doesn't exists", idx))); + } + }; + + if let Some(expected_mutable) = mutability { + if expected_mutable && !global.is_mutable() { + return Err(Error(format!("Expected global {} to be mutable", idx))); + } + if !expected_mutable && global.is_mutable() { + return Err(Error(format!("Expected global {} to be immutable", idx))); + } + } + + Ok(global) + } } diff --git a/src/validation/func.rs b/src/validation/func.rs index 7c51d65..d6b43f0 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -384,13 +384,19 @@ impl Validator { } fn validate_get_global(context: &mut FunctionValidationContext, index: u32) -> Result { - let global_type = context.require_global(index, None)?; + let global_type: StackValueType = { + let global = context.module.require_global(index, None)?; + global.content_type().into() + }; 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 global_type: StackValueType = { + let global = context.module.require_global(index, Some(true))?; + global.content_type().into() + }; let value_type = context.pop_any_value()?; if global_type != value_type { return Err(Error(format!("Trying to update global {} of type {:?} with value of type {:?}", index, global_type, value_type))); @@ -545,7 +551,16 @@ impl Validator { } fn validate_call_indirect(context: &mut FunctionValidationContext, idx: u32) -> Result { - context.module.require_table(DEFAULT_TABLE_INDEX, TableElementType::AnyFunc)?; + { + let table = context.module.require_table(DEFAULT_TABLE_INDEX)?; + if table.elem_type() != TableElementType::AnyFunc { + return Err(Error(format!( + "Table {} has element type {:?} while `anyfunc` expected", + idx, + table.elem_type() + ))); + } + } context.pop_value(ValueType::I32.into())?; let (argument_types, return_type) = context.module.require_function_type(idx)?; diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 450e2d0..00b84c2 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -2,8 +2,8 @@ use std::fmt; use std::iter::repeat; -use elements::{BlockType, External, FunctionType, GlobalEntry, GlobalType, MemoryType, Module, - Opcode, ResizableLimits, TableType, Type, ValueType}; +use elements::{BlockType, External, FunctionType, GlobalEntry, GlobalType, Internal, MemoryType, + Module, Opcode, ResizableLimits, TableType, Type, ValueType}; use common::stack; use self::context::ModuleContext; use self::func::{FunctionValidationContext, Validator}; @@ -73,7 +73,29 @@ pub fn validate_module(module: &Module) -> Result { if let Some(start_function) = module.start_section() { let (params, return_ty) = context.require_function(start_function)?; if return_ty != BlockType::NoResult || params.len() != 0 { - return Err(Error("start function expected to have type [] -> []".into())); + return Err(Error( + "start function expected to have type [] -> []".into(), + )); + } + } + + // validate export section + if let Some(export_section) = module.export_section() { + for export in export_section.entries() { + match export.internal() { + &Internal::Function(function_index) => { + context.require_function(function_index)?; + } + &Internal::Global(global_index) => { + context.require_global(global_index, Some(false))?; + } + &Internal::Memory(memory_index) => { + context.require_memory(memory_index)?; + } + &Internal::Table(table_index) => { + context.require_table(table_index)?; + } + } } } @@ -96,7 +118,6 @@ pub fn validate_module(module: &Module) -> Result { fn prepare_context(module: &Module) -> Result { // TODO: Validate imports - // TODO: Validate exports // Copy types from module as is. let types = module From b20eaec695ff0112af8ba75e6039ad351099cd21 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 12:06:41 +0100 Subject: [PATCH 21/43] Make require_function[_type] to return slice --- src/validation/context.rs | 6 +++--- src/validation/func.rs | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/validation/context.rs b/src/validation/context.rs index 08bbef0..48801c4 100644 --- a/src/validation/context.rs +++ b/src/validation/context.rs @@ -49,7 +49,7 @@ impl ModuleContext { Ok(table) } - pub fn require_function(&self, idx: u32) -> Result<(Vec, BlockType), Error> { + pub fn require_function(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> { let ty_idx = match self.func_type_indexes().get(idx as usize) { Some(ty_idx) => *ty_idx, None => { @@ -61,7 +61,7 @@ impl ModuleContext { self.require_function_type(ty_idx) } - pub fn require_function_type(&self, idx: u32) -> Result<(Vec, BlockType), Error> { + pub fn require_function_type(&self, idx: u32) -> Result<(&[ValueType], BlockType), Error> { let ty = match self.types().get(idx as usize) { Some(&Type::Function(ref func_ty)) => func_ty, None => { @@ -71,7 +71,7 @@ impl ModuleContext { } }; - let params = ty.params().to_vec(); + let params = ty.params(); let return_ty = ty.return_type() .map(BlockType::Value) .unwrap_or(BlockType::NoResult); diff --git a/src/validation/func.rs b/src/validation/func.rs index d6b43f0..1f4711c 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -70,11 +70,13 @@ impl Validator { let (params, result_ty) = module.require_function_type(func.type_ref())?; // locals = (params + vars) - let mut locals = params; + let mut locals = params.to_vec(); locals.extend( body.locals() .iter() - .flat_map(|l| repeat(l.value_type()).take(l.count() as usize)), + .flat_map(|l| repeat(l.value_type()) + .take(l.count() as usize) + ), ); let mut context = FunctionValidationContext::new( From 991fb8fcd87ecbf5524831321c73de6936c2ed6f Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 12:07:39 +0100 Subject: [PATCH 22/43] Clean --- src/validation/func.rs | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/src/validation/func.rs b/src/validation/func.rs index 1f4711c..85c5b1d 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -709,35 +709,6 @@ impl<'a> FunctionValidationContext<'a> { .ok_or(Error(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 { - let global = match self.module.globals().get(idx as usize) { - Some(global) => global, - None => { - return Err(Error(format!("Global at index {} doesn't exists", idx))); - } - }; - - if let Some(expected_mutable) = mutability { - if expected_mutable && !global.is_mutable() { - return Err(Error(format!("Expected global {} to be mutable", idx))); - } - if !expected_mutable && global.is_mutable() { - return Err(Error(format!("Expected global {} to be immutable", idx))); - } - } - - Ok(match global.content_type() { - ValueType::I32 => StackValueType::Specific(ValueType::I32), - ValueType::I64 => StackValueType::Specific(ValueType::I64), - ValueType::F32 => StackValueType::Specific(ValueType::F32), - ValueType::F64 => StackValueType::Specific(ValueType::F64), - }) - } - pub fn function_labels(self) -> HashMap { self.labels } From 8afb7b645013ba6ba97c40b18f993781acfe2c89 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 12:12:54 +0100 Subject: [PATCH 23/43] Add checks for tables and memories count. --- src/validation/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 00b84c2..f5eb686 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -99,6 +99,17 @@ pub fn validate_module(module: &Module) -> Result { } } + + // there must be no greater than 1 table in tables index space + if context.tables().len() > 1 { + return Err(Error(format!("too many tables in index space: {}", context.tables().len()))); + } + + // there must be no greater than 1 linear memory in memory index space + if context.memories().len() > 1 { + return Err(Error(format!("too many memory regions in index space: {}", context.memories().len()))); + } + let ModuleContext { types, tables, From 71b8b933bfda91a0cc2766062f6aa6ca906de0e4 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 12:26:42 +0100 Subject: [PATCH 24/43] Clean --- src/validation/func.rs | 40 +++++++++++++++--------------- src/validation/mod.rs | 2 +- src/validation/tests.rs | 54 +++++++++++++++++++++++++++++++++-------- 3 files changed, 65 insertions(+), 31 deletions(-) diff --git a/src/validation/func.rs b/src/validation/func.rs index 85c5b1d..63fc01e 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -21,7 +21,7 @@ const DEFAULT_VALUE_STACK_LIMIT: usize = 16384; const DEFAULT_FRAME_STACK_LIMIT: usize = 1024; /// Function validation context. -pub struct FunctionValidationContext<'a> { +struct FunctionValidationContext<'a> { /// Wasm module module: &'a ModuleContext, /// Current instruction position. @@ -40,7 +40,7 @@ pub struct FunctionValidationContext<'a> { /// Value type on the stack. #[derive(Debug, Clone, Copy)] -pub enum StackValueType { +enum StackValueType { /// Any value type. Any, /// Any number of any values of any type. @@ -54,7 +54,7 @@ pub struct Validator; /// Instruction outcome. #[derive(Debug, Clone)] -pub enum InstructionOutcome { +enum InstructionOutcome { /// Continue with next instruction. ValidateNextInstruction, /// Unreachable instruction reached. @@ -590,7 +590,7 @@ impl Validator { } impl<'a> FunctionValidationContext<'a> { - pub fn new( + fn new( module: &'a ModuleContext, locals: &'a [ValueType], value_stack_limit: usize, @@ -608,11 +608,11 @@ impl<'a> FunctionValidationContext<'a> { } } - pub fn push_value(&mut self, value_type: StackValueType) -> Result<(), Error> { + 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> { + 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(()), @@ -625,7 +625,7 @@ impl<'a> FunctionValidationContext<'a> { } } - pub fn tee_value(&mut self, value_type: StackValueType) -> Result<(), Error> { + 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(()), @@ -634,7 +634,7 @@ impl<'a> FunctionValidationContext<'a> { } } - pub fn pop_any_value(&mut self) -> Result { + 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)), @@ -646,20 +646,20 @@ impl<'a> FunctionValidationContext<'a> { } } - pub fn tee_any_value(&mut self) -> Result { + 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> { + fn unreachable(&mut self) -> Result<(), Error> { Ok(self.value_stack.push(StackValueType::AnyUnlimited)?) } - pub fn top_label(&self) -> Result<&BlockFrame, Error> { + 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> { + 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, @@ -670,7 +670,7 @@ impl<'a> FunctionValidationContext<'a> { })?) } - pub fn pop_label(&mut self) -> Result { + 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()?) @@ -694,22 +694,22 @@ impl<'a> FunctionValidationContext<'a> { Ok(InstructionOutcome::ValidateNextInstruction) } - pub fn require_label(&self, idx: u32) -> Result<&BlockFrame, Error> { + fn require_label(&self, idx: u32) -> Result<&BlockFrame, Error> { Ok(self.frame_stack.get(idx as usize)?) } - pub fn return_type(&self) -> Result { + fn return_type(&self) -> Result { self.return_type.ok_or(Error("Trying to return from expression".into())) } - pub fn require_local(&self, idx: u32) -> Result { + fn require_local(&self, idx: u32) -> Result { self.locals.get(idx as usize) .cloned() .map(Into::into) .ok_or(Error(format!("Trying to access local with index {} when there are only {} locals", idx, self.locals.len()))) } - pub fn function_labels(self) -> HashMap { + fn function_labels(self) -> HashMap { self.labels } @@ -724,21 +724,21 @@ impl<'a> FunctionValidationContext<'a> { } impl StackValueType { - pub fn is_any(&self) -> bool { + fn is_any(&self) -> bool { match self { &StackValueType::Any => true, _ => false, } } - pub fn is_any_unlimited(&self) -> bool { + fn is_any_unlimited(&self) -> bool { match self { &StackValueType::AnyUnlimited => true, _ => false, } } - pub fn value_type(&self) -> ValueType { + fn value_type(&self) -> ValueType { match self { &StackValueType::Any | &StackValueType::AnyUnlimited => unreachable!("must be checked by caller"), &StackValueType::Specific(value_type) => value_type, diff --git a/src/validation/mod.rs b/src/validation/mod.rs index f5eb686..c490417 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -6,7 +6,7 @@ use elements::{BlockType, External, FunctionType, GlobalEntry, GlobalType, Inter Module, Opcode, ResizableLimits, TableType, Type, ValueType}; use common::stack; use self::context::ModuleContext; -use self::func::{FunctionValidationContext, Validator}; +use self::func::Validator; pub use self::module::ValidatedModule; diff --git a/src/validation/tests.rs b/src/validation/tests.rs index cd4f0b7..5df2778 100644 --- a/src/validation/tests.rs +++ b/src/validation/tests.rs @@ -175,27 +175,61 @@ fn global_init_misc() { // empty init expr let m = module() - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Opcode::End]) - ) + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::End]) ) + ) .build(); assert!(validate_module(&m).is_err()); // not an constant opcode used let m = module() - .with_global( - GlobalEntry::new( - GlobalType::new(ValueType::I32, true), - InitExpr::new(vec![Opcode::Unreachable, Opcode::End]) - ) + .with_global( + GlobalEntry::new( + GlobalType::new(ValueType::I32, true), + InitExpr::new(vec![Opcode::Unreachable, Opcode::End]) ) + ) .build(); assert!(validate_module(&m).is_err()); } +#[test] +fn module_limits_validity() { + // module cannot contain more than 1 memory atm. + let m = module() + .with_import( + ImportEntry::new( + "core".into(), + "memory".into(), + External::Memory(MemoryType::new(10, None)) + ) + ) + .memory() + .with_min(10) + .build() + .build(); + assert!(validate_module(&m).is_err()); + + // module cannot contain more than 1 table atm. + let m = module() + .with_import( + ImportEntry::new( + "core".into(), + "table".into(), + External::Table(TableType::new(10, None)) + ) + ) + .table() + .with_min(10) + .build() + .build(); + assert!(validate_module(&m).is_err()); +} + +// TODO: pepyakin // #[test] // fn if_else_with_return_type_validation() { // let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap(); From 056ac258cdb05e88351be730a73c4df4e1e472bc Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 12:41:14 +0100 Subject: [PATCH 25/43] Test recursive calls --- src/validation/tests.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/validation/tests.rs b/src/validation/tests.rs index 5df2778..460d276 100644 --- a/src/validation/tests.rs +++ b/src/validation/tests.rs @@ -229,6 +229,28 @@ fn module_limits_validity() { assert!(validate_module(&m).is_err()); } +#[test] +fn funcs() { + // recursive function calls is legal. + let m = module() + .function() + .signature().return_type().i32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::Call(1), + Opcode::End, + ])).build() + .build() + .function() + .signature().return_type().i32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::Call(0), + Opcode::End, + ])).build() + .build() + .build(); + assert!(validate_module(&m).is_ok()); +} + // TODO: pepyakin // #[test] // fn if_else_with_return_type_validation() { From 11e4cd6ba116efe54451de81a616dabd06c641a7 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 12:57:42 +0100 Subject: [PATCH 26/43] Import section validation --- src/validation/mod.rs | 27 +++++++++++++++++++++++++-- src/validation/tests.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index c490417..af8ab91 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -99,6 +99,27 @@ pub fn validate_module(module: &Module) -> Result { } } + // validate import section + if let Some(import_section) = module.import_section() { + for import in import_section.entries() { + match import.external() { + &External::Function(function_type_index) => { + context.require_function(function_type_index)?; + }, + &External::Global(ref global_type) => { + if global_type.is_mutable() { + return Err(Error(format!("trying to import mutable global {}", import.field()))); + } + }, + &External::Memory(ref memory_type) => { + memory_type.validate()?; + }, + &External::Table(ref table_type) => { + table_type.validate()?; + }, + } + } + } // there must be no greater than 1 table in tables index space if context.tables().len() > 1 { @@ -110,6 +131,10 @@ pub fn validate_module(module: &Module) -> Result { return Err(Error(format!("too many memory regions in index space: {}", context.memories().len()))); } + + // TODO: data section + // TODO: element section + let ModuleContext { types, tables, @@ -128,8 +153,6 @@ pub fn validate_module(module: &Module) -> Result { } fn prepare_context(module: &Module) -> Result { - // TODO: Validate imports - // Copy types from module as is. let types = module .type_section() diff --git a/src/validation/tests.rs b/src/validation/tests.rs index 460d276..43f4f58 100644 --- a/src/validation/tests.rs +++ b/src/validation/tests.rs @@ -251,6 +251,33 @@ fn funcs() { assert!(validate_module(&m).is_ok()); } +#[test] +fn globals() { + // import immutable global is legal. + let m = module() + .with_import( + ImportEntry::new( + "env".into(), + "ext_global".into(), + External::Global(GlobalType::new(ValueType::I32, false)) + ) + ) + .build(); + assert!(validate_module(&m).is_ok()); + + // import mutable global is invalid. + let m = module() + .with_import( + ImportEntry::new( + "env".into(), + "ext_global".into(), + External::Global(GlobalType::new(ValueType::I32, true)) + ) + ) + .build(); + assert!(validate_module(&m).is_err()); +} + // TODO: pepyakin // #[test] // fn if_else_with_return_type_validation() { From e41e0f98ef1b719f9200bf7d9f6ce8f856535a9e Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 14:11:00 +0100 Subject: [PATCH 27/43] Refactor --- src/validation/mod.rs | 49 ++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index af8ab91..393175b 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -3,7 +3,7 @@ use std::fmt; use std::iter::repeat; use elements::{BlockType, External, FunctionType, GlobalEntry, GlobalType, Internal, MemoryType, - Module, Opcode, ResizableLimits, TableType, Type, ValueType}; + Module, Opcode, ResizableLimits, TableType, Type, ValueType, InitExpr}; use common::stack; use self::context::ModuleContext; use self::func::Validator; @@ -249,19 +249,44 @@ impl TableType { } impl GlobalEntry { - fn validate(&self, globals_sofar: &[GlobalType]) -> Result<(), Error> { - let init = self.init_expr().code(); - if init.len() != 2 { + fn validate(&self, globals: &[GlobalType]) -> Result<(), Error> { + let init = self.init_expr(); + init.validate(globals)?; + let init_expr_ty = init.expr_const_type(globals)?; + 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 + ))); + } + Ok(()) + } +} + +impl InitExpr { + fn validate(&self, globals: &[GlobalType]) -> Result<(), Error> { + let code = self.code(); + if code.len() != 2 { return Err(Error( format!("Init expression should always be with length 2"), )); } - let init_expr_ty: ValueType = match init[0] { + let _ = self.expr_const_type(globals)?; + if code[1] != Opcode::End { + return Err(Error(format!("Expression doesn't ends with `end` opcode"))); + } + Ok(()) + } + + fn expr_const_type(&self, globals: &[GlobalType]) -> Result { + let code = self.code(); + let expr_ty: ValueType = match code[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) { + 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))); @@ -276,16 +301,6 @@ impl GlobalEntry { }, _ => 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(()) + Ok(expr_ty) } } From 74f4c7c49a899867773f412e3b85ba7aa0e182b7 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 16:31:07 +0100 Subject: [PATCH 28/43] Emit Opcode::End for init exprs --- src/builder/memory.rs | 5 ++++- src/builder/table.rs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/builder/memory.rs b/src/builder/memory.rs index 4c87c27..aed5473 100644 --- a/src/builder/memory.rs +++ b/src/builder/memory.rs @@ -45,7 +45,10 @@ impl MemoryBuilder where F: Invoke { pub fn with_data(mut self, index: u32, values: Vec) -> Self { self.memory.data.push(MemoryDataDefinition { - offset: elements::InitExpr::new(vec![elements::Opcode::I32Const(index as i32)]), + offset: elements::InitExpr::new(vec![ + elements::Opcode::I32Const(index as i32), + elements::Opcode::End, + ]), values: values, }); self diff --git a/src/builder/table.rs b/src/builder/table.rs index 95705af..3706870 100644 --- a/src/builder/table.rs +++ b/src/builder/table.rs @@ -45,7 +45,10 @@ impl TableBuilder where F: Invoke { pub fn with_element(mut self, index: u32, values: Vec) -> Self { self.table.elements.push(TableEntryDefinition { - offset: elements::InitExpr::new(vec![elements::Opcode::I32Const(index as i32)]), + offset: elements::InitExpr::new(vec![ + elements::Opcode::I32Const(index as i32), + elements::Opcode::End, + ]), values: values, }); self From d30e4052cbd7699789983dbe4484b6aa1373b2b4 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 16:31:16 +0100 Subject: [PATCH 29/43] Add data section validation --- src/validation/mod.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 393175b..9ab2ff0 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -132,7 +132,17 @@ pub fn validate_module(module: &Module) -> Result { } - // TODO: data section + // use data section to initialize linear memory regions + if let Some(data_section) = module.data_section() { + for data_segment in data_section.entries() { + context.require_memory(data_segment.index())?; + let init_ty = data_segment.offset().expr_const_type(context.globals())?; + if init_ty != ValueType::I32 { + return Err(Error(format!("segment offset should return I32"))); + } + } + } + // TODO: element section let ModuleContext { @@ -251,7 +261,6 @@ impl TableType { impl GlobalEntry { fn validate(&self, globals: &[GlobalType]) -> Result<(), Error> { let init = self.init_expr(); - init.validate(globals)?; let init_expr_ty = init.expr_const_type(globals)?; if init_expr_ty != self.global_type().content_type() { return Err(Error(format!( @@ -265,22 +274,14 @@ impl GlobalEntry { } impl InitExpr { - fn validate(&self, globals: &[GlobalType]) -> Result<(), Error> { + /// Returns type of this constant expression. + fn expr_const_type(&self, globals: &[GlobalType]) -> Result { let code = self.code(); if code.len() != 2 { return Err(Error( - format!("Init expression should always be with length 2"), + format!("Init expression should always be with length 2") )); } - let _ = self.expr_const_type(globals)?; - if code[1] != Opcode::End { - return Err(Error(format!("Expression doesn't ends with `end` opcode"))); - } - Ok(()) - } - - fn expr_const_type(&self, globals: &[GlobalType]) -> Result { - let code = self.code(); let expr_ty: ValueType = match code[0] { Opcode::I32Const(_) => ValueType::I32, Opcode::I64Const(_) => ValueType::I64, @@ -301,6 +302,9 @@ impl InitExpr { }, _ => return Err(Error(format!("Non constant opcode in init expr"))), }; + if code[1] != Opcode::End { + return Err(Error(format!("Expression doesn't ends with `end` opcode"))); + } Ok(expr_ty) } } From b146c2114751b8328be0e0a8ae3a4c48a97f16c7 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 16:51:26 +0100 Subject: [PATCH 30/43] validate element section --- src/validation/mod.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 9ab2ff0..008bc02 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -131,7 +131,6 @@ pub fn validate_module(module: &Module) -> Result { return Err(Error(format!("too many memory regions in index space: {}", context.memories().len()))); } - // use data section to initialize linear memory regions if let Some(data_section) = module.data_section() { for data_segment in data_section.entries() { @@ -143,7 +142,21 @@ pub fn validate_module(module: &Module) -> Result { } } - // TODO: element section + // use element section to fill tables + if let Some(element_section) = module.elements_section() { + for element_segment in element_section.entries() { + context.require_table(element_segment.index())?; + + let init_ty = element_segment.offset().expr_const_type(context.globals())?; + if init_ty != ValueType::I32 { + return Err(Error(format!("segment offset should return I32"))); + } + + for function_index in element_segment.members() { + context.require_function(*function_index)?; + } + } + } let ModuleContext { types, From ba6018957a5fc02aed8b2a941db66c93bde4ffc5 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 17:06:39 +0100 Subject: [PATCH 31/43] Refactor limits test --- src/validation/tests.rs | 100 ++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/src/validation/tests.rs b/src/validation/tests.rs index 43f4f58..e55acb5 100644 --- a/src/validation/tests.rs +++ b/src/validation/tests.rs @@ -11,61 +11,59 @@ fn empty_is_valid() { } #[test] -fn mem_limits() { - // min > max - let m = module() - .memory() - .with_min(10) - .with_max(Some(9)) - .build() - .build(); - assert!(validate_module(&m).is_err()); +fn limits() { + let test_cases = vec![ + // min > max + (10, Some(9), false), + // min = max + (10, Some(10), true), + // table/memory is always valid without max + (10, None, true), + ]; - // min = max - let m = module() - .memory() - .with_min(10) - .with_max(Some(10)) - .build() - .build(); - assert!(validate_module(&m).is_ok()); + for (min, max, is_valid) in test_cases { + // defined table + let m = module() + .table() + .with_min(min) + .with_max(max) + .build() + .build(); + assert_eq!(validate_module(&m).is_ok(), is_valid); - // mem is always valid without max - let m = module() - .memory() - .with_min(10) - .build() - .build(); - assert!(validate_module(&m).is_ok()); -} + // imported table + let m = module() + .with_import( + ImportEntry::new( + "core".into(), + "table".into(), + External::Table(TableType::new(min, max)) + ) + ) + .build(); + assert_eq!(validate_module(&m).is_ok(), is_valid); -#[test] -fn table_limits() { - // min > max - let m = module() - .table() - .with_min(10) - .with_max(Some(9)) - .build() - .build(); - assert!(validate_module(&m).is_err()); + // defined memory + let m = module() + .memory() + .with_min(min) + .with_max(max) + .build() + .build(); + assert_eq!(validate_module(&m).is_ok(), is_valid); - // min = max - let m = module() - .table() - .with_min(10) - .with_max(Some(10)) - .build() - .build(); - assert!(validate_module(&m).is_ok()); - - // table is always valid without max - let m = module() - .table() - .with_min(10) - .build() - .build(); - assert!(validate_module(&m).is_ok()); + // imported table + let m = module() + .with_import( + ImportEntry::new( + "core".into(), + "memory".into(), + External::Memory(MemoryType::new(min, max)) + ) + ) + .build(); + assert_eq!(validate_module(&m).is_ok(), is_valid); + } } #[test] From 42142f6513434096f41f5d7999d41b7af648a2ed Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 17:10:40 +0100 Subject: [PATCH 32/43] Clean --- src/validation/context.rs | 2 +- src/validation/func.rs | 6 ------ src/validation/mod.rs | 7 +++---- src/validation/module.rs | 2 -- src/validation/tests.rs | 5 ++--- 5 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/validation/context.rs b/src/validation/context.rs index 48801c4..0de49de 100644 --- a/src/validation/context.rs +++ b/src/validation/context.rs @@ -1,5 +1,5 @@ use elements::{MemoryType, TableType, GlobalType, Type}; -use elements::{Opcode, BlockType, ValueType, TableElementType}; +use elements::{BlockType, ValueType}; use validation::Error; pub struct ModuleContext { diff --git a/src/validation/func.rs b/src/validation/func.rs index 63fc01e..e9fb976 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -1,9 +1,7 @@ use std::u32; -use std::sync::Arc; use std::iter::repeat; use std::collections::HashMap; use elements::{Opcode, BlockType, ValueType, TableElementType, Func, FuncBody}; -use elements::{FunctionType, Type}; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; use validation::context::ModuleContext; @@ -709,10 +707,6 @@ impl<'a> FunctionValidationContext<'a> { .ok_or(Error(format!("Trying to access local with index {} when there are only {} locals", idx, self.locals.len()))) } - 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 { diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 008bc02..16f4402 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -1,9 +1,8 @@ -#![allow(unused, missing_docs)] +#![allow(missing_docs)] use std::fmt; -use std::iter::repeat; -use elements::{BlockType, External, FunctionType, GlobalEntry, GlobalType, Internal, MemoryType, - Module, Opcode, ResizableLimits, TableType, Type, ValueType, InitExpr}; +use elements::{BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, + Module, Opcode, ResizableLimits, TableType, ValueType, InitExpr}; use common::stack; use self::context::ModuleContext; use self::func::Validator; diff --git a/src/validation/module.rs b/src/validation/module.rs index 649d2d3..e601664 100644 --- a/src/validation/module.rs +++ b/src/validation/module.rs @@ -1,6 +1,4 @@ use elements::{MemoryType, TableType, GlobalType, Type}; -use elements::{Opcode, BlockType, ValueType, TableElementType}; -use validation::Error; pub struct ValidatedModule { pub memories: Vec, diff --git a/src/validation/tests.rs b/src/validation/tests.rs index e55acb5..6cd49e9 100644 --- a/src/validation/tests.rs +++ b/src/validation/tests.rs @@ -1,8 +1,7 @@ use super::validate_module; use builder::module; -use elements::{BlockType, ExportEntry, External, FunctionType, GlobalEntry, GlobalType, - ImportEntry, InitExpr, Internal, MemoryType, Opcode, Opcodes, TableType, - ValueType}; +use elements::{External, GlobalEntry, GlobalType, ImportEntry, InitExpr, MemoryType, + Opcode, Opcodes, TableType, ValueType}; #[test] fn empty_is_valid() { From e9ec5e8aa805611e5ca5588784ddf581b6939d7a Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 17:25:32 +0100 Subject: [PATCH 33/43] Clean --- src/validation/mod.rs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 16f4402..437ed9a 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -81,17 +81,17 @@ pub fn validate_module(module: &Module) -> Result { // validate export section if let Some(export_section) = module.export_section() { for export in export_section.entries() { - match export.internal() { - &Internal::Function(function_index) => { + match *export.internal() { + Internal::Function(function_index) => { context.require_function(function_index)?; } - &Internal::Global(global_index) => { + Internal::Global(global_index) => { context.require_global(global_index, Some(false))?; } - &Internal::Memory(memory_index) => { + Internal::Memory(memory_index) => { context.require_memory(memory_index)?; } - &Internal::Table(table_index) => { + Internal::Table(table_index) => { context.require_table(table_index)?; } } @@ -101,19 +101,19 @@ pub fn validate_module(module: &Module) -> Result { // validate import section if let Some(import_section) = module.import_section() { for import in import_section.entries() { - match import.external() { - &External::Function(function_type_index) => { + match *import.external() { + External::Function(function_type_index) => { context.require_function(function_type_index)?; }, - &External::Global(ref global_type) => { + External::Global(ref global_type) => { if global_type.is_mutable() { return Err(Error(format!("trying to import mutable global {}", import.field()))); } }, - &External::Memory(ref memory_type) => { + External::Memory(ref memory_type) => { memory_type.validate()?; }, - &External::Table(ref table_type) => { + External::Table(ref table_type) => { table_type.validate()?; }, } @@ -136,7 +136,7 @@ pub fn validate_module(module: &Module) -> Result { context.require_memory(data_segment.index())?; let init_ty = data_segment.offset().expr_const_type(context.globals())?; if init_ty != ValueType::I32 { - return Err(Error(format!("segment offset should return I32"))); + return Err(Error("segment offset should return I32".into())); } } } @@ -148,7 +148,7 @@ pub fn validate_module(module: &Module) -> Result { let init_ty = element_segment.offset().expr_const_type(context.globals())?; if init_ty != ValueType::I32 { - return Err(Error(format!("segment offset should return I32"))); + return Err(Error("segment offset should return I32".into())); } for function_index in element_segment.members() { @@ -290,9 +290,7 @@ impl InitExpr { fn expr_const_type(&self, globals: &[GlobalType]) -> Result { let code = self.code(); if code.len() != 2 { - return Err(Error( - format!("Init expression should always be with length 2") - )); + return Err(Error("Init expression should always be with length 2".into())); } let expr_ty: ValueType = match code[0] { Opcode::I32Const(_) => ValueType::I32, @@ -312,10 +310,10 @@ impl InitExpr { )) } }, - _ => return Err(Error(format!("Non constant opcode in init expr"))), + _ => return Err(Error("Non constant opcode in init expr".into())), }; if code[1] != Opcode::End { - return Err(Error(format!("Expression doesn't ends with `end` opcode"))); + return Err(Error("Expression doesn't ends with `end` opcode".into())); } Ok(expr_ty) } From 93b61bc2dca58f6f95d281b09f89b0717652d6f5 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 17:43:17 +0100 Subject: [PATCH 34/43] Old test uncommented --- src/validation/context.rs | 1 + src/validation/mod.rs | 3 +-- src/validation/module.rs | 1 + src/validation/tests.rs | 44 ++++++++++++++++++++------------------- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/validation/context.rs b/src/validation/context.rs index 0de49de..4815a5a 100644 --- a/src/validation/context.rs +++ b/src/validation/context.rs @@ -2,6 +2,7 @@ use elements::{MemoryType, TableType, GlobalType, Type}; use elements::{BlockType, ValueType}; use validation::Error; +#[derive(Default, Debug)] pub struct ModuleContext { pub memories: Vec, pub tables: Vec, diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 437ed9a..bfcc325 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -16,6 +16,7 @@ mod func; #[cfg(test)] mod tests; +#[derive(Debug)] pub struct Error(String); impl fmt::Display for Error { @@ -182,8 +183,6 @@ fn prepare_context(module: &Module) -> Result { .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(); diff --git a/src/validation/module.rs b/src/validation/module.rs index e601664..1e138a1 100644 --- a/src/validation/module.rs +++ b/src/validation/module.rs @@ -1,5 +1,6 @@ use elements::{MemoryType, TableType, GlobalType, Type}; +#[derive(Debug)] pub struct ValidatedModule { pub memories: Vec, pub tables: Vec, diff --git a/src/validation/tests.rs b/src/validation/tests.rs index 6cd49e9..1ad6448 100644 --- a/src/validation/tests.rs +++ b/src/validation/tests.rs @@ -1,7 +1,7 @@ use super::validate_module; use builder::module; use elements::{External, GlobalEntry, GlobalType, ImportEntry, InitExpr, MemoryType, - Opcode, Opcodes, TableType, ValueType}; + Opcode, Opcodes, TableType, ValueType, BlockType}; #[test] fn empty_is_valid() { @@ -275,23 +275,25 @@ fn globals() { assert!(validate_module(&m).is_err()); } -// TODO: pepyakin -// #[test] -// fn if_else_with_return_type_validation() { -// let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap(); -// let mut context = FunctionValidationContext::new(&module_instance, None, &[], 1024, 1024, FunctionSignature::Module(&FunctionType::default())); - -// Validator::validate_function(&mut context, BlockType::NoResult, &[ -// Opcode::I32Const(1), -// Opcode::If(BlockType::NoResult), -// Opcode::I32Const(1), -// Opcode::If(BlockType::Value(ValueType::I32)), -// Opcode::I32Const(1), -// Opcode::Else, -// Opcode::I32Const(2), -// Opcode::End, -// Opcode::Drop, -// Opcode::End, -// Opcode::End, -// ]).unwrap(); -// } +#[test] +fn if_else_with_return_type_validation() { + let m = module() + .function() + .signature().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I32Const(1), + Opcode::If(BlockType::NoResult), + Opcode::I32Const(1), + Opcode::If(BlockType::Value(ValueType::I32)), + Opcode::I32Const(1), + Opcode::Else, + Opcode::I32Const(2), + Opcode::End, + Opcode::Drop, + Opcode::End, + Opcode::End, + ])).build() + .build() + .build(); + validate_module(&m).unwrap(); +} From 9c442f6be1bb1d1ece9d340b8fd9a0cfeb298d45 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 18:12:10 +0100 Subject: [PATCH 35/43] Fixes --- src/interpreter/mod.rs | 5 +++-- src/interpreter/runner.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 1fe782f..a2234b5 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -2,6 +2,7 @@ use std::any::TypeId; use validation; +use common; /// Custom user error. pub trait UserError: 'static + ::std::fmt::Display + ::std::fmt::Debug { @@ -123,8 +124,8 @@ impl From for Error { } } -impl From<::common::stack::Error> for Error { - fn from(e: ::common::stack::Error) -> Self { +impl From for Error { + fn from(e: common::stack::Error) -> Self { Error::Stack(e.to_string()) } } diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index 5e5f38b..c29c82f 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -14,7 +14,7 @@ use interpreter::value::{ }; use interpreter::variable::VariableInstance; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX, BlockFrame, BlockFrameType}; -use common::stack::{StackWithLimit}; +use common::stack::StackWithLimit; /// Function interpreter. pub struct Interpreter; From 15b93038f99fe09974c601fb3653b16e85b5273f Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 18:54:23 +0100 Subject: [PATCH 36/43] Fix matches --- src/validation/func.rs | 346 ++++++++++++++++++++--------------------- src/validation/mod.rs | 10 +- 2 files changed, 178 insertions(+), 178 deletions(-) diff --git a/src/validation/func.rs b/src/validation/func.rs index e9fb976..2d9f78b 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -116,196 +116,196 @@ impl Validator { 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), + 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::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::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::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::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::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::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::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::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::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::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::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::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::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::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::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::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()), + 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()), } } diff --git a/src/validation/mod.rs b/src/validation/mod.rs index bfcc325..7597586 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -193,11 +193,11 @@ fn prepare_context(module: &Module) -> Result { .map(|i| i.entries()) .unwrap_or_default() { - match import_entry.external() { - &External::Function(idx) => func_type_indexes.push(idx), - &External::Table(ref table) => tables.push(table.clone()), - &External::Memory(ref memory) => memories.push(memory.clone()), - &External::Global(ref global) => globals.push(global.clone()), + match *import_entry.external() { + External::Function(idx) => func_type_indexes.push(idx), + External::Table(ref table) => tables.push(table.clone()), + External::Memory(ref memory) => memories.push(memory.clone()), + External::Global(ref global) => globals.push(global.clone()), } } From 8bd984b10fb16bd173ea70eb432865436106c1f3 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 18:54:28 +0100 Subject: [PATCH 37/43] Fix imports --- src/validation/mod.rs | 6 ++++-- src/validation/tests.rs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 7597586..a01941b 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -1,8 +1,10 @@ #![allow(missing_docs)] use std::fmt; -use elements::{BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, - Module, Opcode, ResizableLimits, TableType, ValueType, InitExpr}; +use elements::{ + BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, + Module, Opcode, ResizableLimits, TableType, ValueType, InitExpr +}; use common::stack; use self::context::ModuleContext; use self::func::Validator; diff --git a/src/validation/tests.rs b/src/validation/tests.rs index 1ad6448..3e1f255 100644 --- a/src/validation/tests.rs +++ b/src/validation/tests.rs @@ -1,7 +1,9 @@ use super::validate_module; use builder::module; -use elements::{External, GlobalEntry, GlobalType, ImportEntry, InitExpr, MemoryType, - Opcode, Opcodes, TableType, ValueType, BlockType}; +use elements::{ + External, GlobalEntry, GlobalType, ImportEntry, InitExpr, MemoryType, + Opcode, Opcodes, TableType, ValueType, BlockType +}; #[test] fn empty_is_valid() { From bef4a644fdacef8ea9f19f41ad292c7ffa7a8165 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 18:56:17 +0100 Subject: [PATCH 38/43] Fix braces --- src/interpreter/runner.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index c29c82f..2842249 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -555,12 +555,12 @@ impl Interpreter { .module() .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) .map(|m| m.size()) - .and_then(|s| { + .and_then(|s| context .value_stack_mut() .push(RuntimeValue::I32(s as i32)) .map_err(Into::into) - }) + ) .map(|_| InstructionOutcome::RunNextInstruction) } @@ -570,12 +570,12 @@ impl Interpreter { .module() .memory(ItemIndex::IndexSpace(DEFAULT_MEMORY_INDEX)) .and_then(|m| m.grow(pages)) - .and_then(|m| { + .and_then(|m| context .value_stack_mut() .push(RuntimeValue::I32(m as i32)) .map_err(Into::into) - }) + ) .map(|_| InstructionOutcome::RunNextInstruction) } From 7ad604b5ad044eec7c25f81e447749f41a909f54 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 19:32:26 +0100 Subject: [PATCH 39/43] ok_or_else fixes --- src/validation/context.rs | 51 +++++++++++---------------------------- 1 file changed, 14 insertions(+), 37 deletions(-) diff --git a/src/validation/context.rs b/src/validation/context.rs index 4815a5a..23b7aa1 100644 --- a/src/validation/context.rs +++ b/src/validation/context.rs @@ -40,37 +40,22 @@ impl ModuleContext { } pub fn require_table(&self, idx: u32) -> Result<&TableType, 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))); - } - }; - - Ok(table) + self.tables() + .get(idx as usize) + .ok_or_else(|| Error(format!("Table at index {} doesn't exists", idx))) } pub fn require_function(&self, idx: u32) -> Result<(&[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) + let ty_idx = self.func_type_indexes() + .get(idx as usize) + .ok_or_else(|| Error(format!("Function at index {} doesn't exists", idx)))?; + self.require_function_type(*ty_idx) } pub fn require_function_type(&self, idx: u32) -> Result<(&[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 &Type::Function(ref ty) = self.types() + .get(idx as usize) + .ok_or_else(|| Error(format!("Type at index {} doesn't exists", idx)))?; let params = ty.params(); let return_ty = ty.return_type() @@ -79,17 +64,10 @@ impl ModuleContext { Ok((params, return_ty)) } - pub fn require_global( - &self, - idx: u32, - mutability: Option, - ) -> Result<&GlobalType, Error> { - let global = match self.globals().get(idx as usize) { - Some(global) => global, - None => { - return Err(Error(format!("Global at index {} doesn't exists", idx))); - } - }; + pub fn require_global(&self, idx: u32, mutability: Option) -> Result<&GlobalType, Error> { + let global = self.globals() + .get(idx as usize) + .ok_or_else(|| Error(format!("Global at index {} doesn't exists", idx)))?; if let Some(expected_mutable) = mutability { if expected_mutable && !global.is_mutable() { @@ -99,7 +77,6 @@ impl ModuleContext { return Err(Error(format!("Expected global {} to be immutable", idx))); } } - Ok(global) } } From 3b86a8e3351d14704e498937b4636affc5efef0d Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 19:34:38 +0100 Subject: [PATCH 40/43] Fix break condition. --- src/validation/func.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/validation/func.rs b/src/validation/func.rs index 2d9f78b..97850c2 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -108,7 +108,7 @@ impl Validator { } context.position += 1; - if context.position >= body_len { + if context.position == body_len { return Ok(()); } } From dea1f3cac0bbafdc25430cbddf4c2a9168092454 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 19:44:40 +0100 Subject: [PATCH 41/43] Oops --- src/lib.rs | 2 +- src/validation/mod.rs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b68662e..732fdfd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ extern crate parking_lot; pub mod elements; pub mod builder; pub mod interpreter; -pub mod validation; +mod validation; mod common; pub use elements::{ diff --git a/src/validation/mod.rs b/src/validation/mod.rs index a01941b..af29adf 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -1,5 +1,3 @@ -#![allow(missing_docs)] - use std::fmt; use elements::{ BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, From 7c7e3bd051a3d6a8788b923f249d49e03bb015f5 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 19:45:06 +0100 Subject: [PATCH 42/43] use self::Opcode::* --- src/validation/func.rs | 345 +++++++++++++++++++++-------------------- 1 file changed, 173 insertions(+), 172 deletions(-) diff --git a/src/validation/func.rs b/src/validation/func.rs index 97850c2..cab67c7 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -115,197 +115,198 @@ impl Validator { } fn validate_instruction(context: &mut FunctionValidationContext, opcode: &Opcode) -> Result { + use self::Opcode::*; 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), + Unreachable => Ok(InstructionOutcome::Unreachable), + Nop => Ok(InstructionOutcome::ValidateNextInstruction), + Block(block_type) => Validator::validate_block(context, block_type), + Loop(block_type) => Validator::validate_loop(context, block_type), + If(block_type) => Validator::validate_if(context, block_type), + Else => Validator::validate_else(context), + End => Validator::validate_end(context), + Br(idx) => Validator::validate_br(context, idx), + BrIf(idx) => Validator::validate_br_if(context, idx), + BrTable(ref table, default) => Validator::validate_br_table(context, table, default), + Return => Validator::validate_return(context), - Opcode::Call(index) => Validator::validate_call(context, index), - Opcode::CallIndirect(index, _reserved) => Validator::validate_call_indirect(context, index), + Call(index) => Validator::validate_call(context, index), + CallIndirect(index, _reserved) => Validator::validate_call_indirect(context, index), - Opcode::Drop => Validator::validate_drop(context), - Opcode::Select => Validator::validate_select(context), + Drop => Validator::validate_drop(context), + 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), + GetLocal(index) => Validator::validate_get_local(context, index), + SetLocal(index) => Validator::validate_set_local(context, index), + TeeLocal(index) => Validator::validate_tee_local(context, index), + GetGlobal(index) => Validator::validate_get_global(context, index), + 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()), + I32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::I32.into()), + I64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::I64.into()), + F32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::F32.into()), + F64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::F64.into()), + I32Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I32.into()), + I32Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I32.into()), + I32Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I32.into()), + I32Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I32.into()), + I64Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I64.into()), + I64Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I64.into()), + I64Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I64.into()), + I64Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I64.into()), + I64Load32S(align, _) => Validator::validate_load(context, align, 4, ValueType::I64.into()), + 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()), + I32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::I32.into()), + I64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::I64.into()), + F32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::F32.into()), + F64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::F64.into()), + I32Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I32.into()), + I32Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I32.into()), + I64Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I64.into()), + I64Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I64.into()), + 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), + CurrentMemory(_) => Validator::validate_current_memory(context), + 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()), + I32Const(_) => Validator::validate_const(context, ValueType::I32.into()), + I64Const(_) => Validator::validate_const(context, ValueType::I64.into()), + F32Const(_) => Validator::validate_const(context, ValueType::F32.into()), + 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()), + I32Eqz => Validator::validate_testop(context, ValueType::I32.into()), + I32Eq => Validator::validate_relop(context, ValueType::I32.into()), + I32Ne => Validator::validate_relop(context, ValueType::I32.into()), + I32LtS => Validator::validate_relop(context, ValueType::I32.into()), + I32LtU => Validator::validate_relop(context, ValueType::I32.into()), + I32GtS => Validator::validate_relop(context, ValueType::I32.into()), + I32GtU => Validator::validate_relop(context, ValueType::I32.into()), + I32LeS => Validator::validate_relop(context, ValueType::I32.into()), + I32LeU => Validator::validate_relop(context, ValueType::I32.into()), + I32GeS => Validator::validate_relop(context, ValueType::I32.into()), + 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()), + I64Eqz => Validator::validate_testop(context, ValueType::I64.into()), + I64Eq => Validator::validate_relop(context, ValueType::I64.into()), + I64Ne => Validator::validate_relop(context, ValueType::I64.into()), + I64LtS => Validator::validate_relop(context, ValueType::I64.into()), + I64LtU => Validator::validate_relop(context, ValueType::I64.into()), + I64GtS => Validator::validate_relop(context, ValueType::I64.into()), + I64GtU => Validator::validate_relop(context, ValueType::I64.into()), + I64LeS => Validator::validate_relop(context, ValueType::I64.into()), + I64LeU => Validator::validate_relop(context, ValueType::I64.into()), + I64GeS => Validator::validate_relop(context, ValueType::I64.into()), + 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()), + F32Eq => Validator::validate_relop(context, ValueType::F32.into()), + F32Ne => Validator::validate_relop(context, ValueType::F32.into()), + F32Lt => Validator::validate_relop(context, ValueType::F32.into()), + F32Gt => Validator::validate_relop(context, ValueType::F32.into()), + F32Le => Validator::validate_relop(context, ValueType::F32.into()), + 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()), + F64Eq => Validator::validate_relop(context, ValueType::F64.into()), + F64Ne => Validator::validate_relop(context, ValueType::F64.into()), + F64Lt => Validator::validate_relop(context, ValueType::F64.into()), + F64Gt => Validator::validate_relop(context, ValueType::F64.into()), + F64Le => Validator::validate_relop(context, ValueType::F64.into()), + 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()), + I32Clz => Validator::validate_unop(context, ValueType::I32.into()), + I32Ctz => Validator::validate_unop(context, ValueType::I32.into()), + I32Popcnt => Validator::validate_unop(context, ValueType::I32.into()), + I32Add => Validator::validate_binop(context, ValueType::I32.into()), + I32Sub => Validator::validate_binop(context, ValueType::I32.into()), + I32Mul => Validator::validate_binop(context, ValueType::I32.into()), + I32DivS => Validator::validate_binop(context, ValueType::I32.into()), + I32DivU => Validator::validate_binop(context, ValueType::I32.into()), + I32RemS => Validator::validate_binop(context, ValueType::I32.into()), + I32RemU => Validator::validate_binop(context, ValueType::I32.into()), + I32And => Validator::validate_binop(context, ValueType::I32.into()), + I32Or => Validator::validate_binop(context, ValueType::I32.into()), + I32Xor => Validator::validate_binop(context, ValueType::I32.into()), + I32Shl => Validator::validate_binop(context, ValueType::I32.into()), + I32ShrS => Validator::validate_binop(context, ValueType::I32.into()), + I32ShrU => Validator::validate_binop(context, ValueType::I32.into()), + I32Rotl => Validator::validate_binop(context, ValueType::I32.into()), + 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()), + I64Clz => Validator::validate_unop(context, ValueType::I64.into()), + I64Ctz => Validator::validate_unop(context, ValueType::I64.into()), + I64Popcnt => Validator::validate_unop(context, ValueType::I64.into()), + I64Add => Validator::validate_binop(context, ValueType::I64.into()), + I64Sub => Validator::validate_binop(context, ValueType::I64.into()), + I64Mul => Validator::validate_binop(context, ValueType::I64.into()), + I64DivS => Validator::validate_binop(context, ValueType::I64.into()), + I64DivU => Validator::validate_binop(context, ValueType::I64.into()), + I64RemS => Validator::validate_binop(context, ValueType::I64.into()), + I64RemU => Validator::validate_binop(context, ValueType::I64.into()), + I64And => Validator::validate_binop(context, ValueType::I64.into()), + I64Or => Validator::validate_binop(context, ValueType::I64.into()), + I64Xor => Validator::validate_binop(context, ValueType::I64.into()), + I64Shl => Validator::validate_binop(context, ValueType::I64.into()), + I64ShrS => Validator::validate_binop(context, ValueType::I64.into()), + I64ShrU => Validator::validate_binop(context, ValueType::I64.into()), + I64Rotl => Validator::validate_binop(context, ValueType::I64.into()), + 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()), + F32Abs => Validator::validate_unop(context, ValueType::F32.into()), + F32Neg => Validator::validate_unop(context, ValueType::F32.into()), + F32Ceil => Validator::validate_unop(context, ValueType::F32.into()), + F32Floor => Validator::validate_unop(context, ValueType::F32.into()), + F32Trunc => Validator::validate_unop(context, ValueType::F32.into()), + F32Nearest => Validator::validate_unop(context, ValueType::F32.into()), + F32Sqrt => Validator::validate_unop(context, ValueType::F32.into()), + F32Add => Validator::validate_binop(context, ValueType::F32.into()), + F32Sub => Validator::validate_binop(context, ValueType::F32.into()), + F32Mul => Validator::validate_binop(context, ValueType::F32.into()), + F32Div => Validator::validate_binop(context, ValueType::F32.into()), + F32Min => Validator::validate_binop(context, ValueType::F32.into()), + F32Max => Validator::validate_binop(context, ValueType::F32.into()), + 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()), + F64Abs => Validator::validate_unop(context, ValueType::F64.into()), + F64Neg => Validator::validate_unop(context, ValueType::F64.into()), + F64Ceil => Validator::validate_unop(context, ValueType::F64.into()), + F64Floor => Validator::validate_unop(context, ValueType::F64.into()), + F64Trunc => Validator::validate_unop(context, ValueType::F64.into()), + F64Nearest => Validator::validate_unop(context, ValueType::F64.into()), + F64Sqrt => Validator::validate_unop(context, ValueType::F64.into()), + F64Add => Validator::validate_binop(context, ValueType::F64.into()), + F64Sub => Validator::validate_binop(context, ValueType::F64.into()), + F64Mul => Validator::validate_binop(context, ValueType::F64.into()), + F64Div => Validator::validate_binop(context, ValueType::F64.into()), + F64Min => Validator::validate_binop(context, ValueType::F64.into()), + F64Max => Validator::validate_binop(context, ValueType::F64.into()), + 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()), + I32WarpI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::I32.into()), + I32TruncSF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()), + I32TruncUF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()), + I32TruncSF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I32.into()), + I32TruncUF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I32.into()), + I64ExtendSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::I64.into()), + I64ExtendUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::I64.into()), + I64TruncSF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I64.into()), + I64TruncUF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I64.into()), + I64TruncSF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()), + I64TruncUF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()), + F32ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()), + F32ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()), + F32ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F32.into()), + F32ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F32.into()), + F32DemoteF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::F32.into()), + F64ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F64.into()), + F64ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F64.into()), + F64ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()), + F64ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()), + 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()), + I32ReinterpretF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()), + I64ReinterpretF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()), + F32ReinterpretI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()), + F64ReinterpretI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()), } } From f6891e6de96d9149920224db75347ec75aaadc25 Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 5 Dec 2017 19:57:07 +0100 Subject: [PATCH 43/43] clean func --- src/validation/func.rs | 342 ++++++++++++++++++++--------------------- 1 file changed, 171 insertions(+), 171 deletions(-) diff --git a/src/validation/func.rs b/src/validation/func.rs index cab67c7..6920b42 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -142,208 +142,208 @@ impl Validator { GetGlobal(index) => Validator::validate_get_global(context, index), SetGlobal(index) => Validator::validate_set_global(context, index), - I32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::I32.into()), - I64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::I64.into()), - F32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::F32.into()), - F64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::F64.into()), - I32Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I32.into()), - I32Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I32.into()), - I32Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I32.into()), - I32Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I32.into()), - I64Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I64.into()), - I64Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I64.into()), - I64Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I64.into()), - I64Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I64.into()), - I64Load32S(align, _) => Validator::validate_load(context, align, 4, ValueType::I64.into()), - I64Load32U(align, _) => Validator::validate_load(context, align, 4, ValueType::I64.into()), + I32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::I32), + I64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::I64), + F32Load(align, _) => Validator::validate_load(context, align, 4, ValueType::F32), + F64Load(align, _) => Validator::validate_load(context, align, 8, ValueType::F64), + I32Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I32), + I32Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I32), + I32Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I32), + I32Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I32), + I64Load8S(align, _) => Validator::validate_load(context, align, 1, ValueType::I64), + I64Load8U(align, _) => Validator::validate_load(context, align, 1, ValueType::I64), + I64Load16S(align, _) => Validator::validate_load(context, align, 2, ValueType::I64), + I64Load16U(align, _) => Validator::validate_load(context, align, 2, ValueType::I64), + I64Load32S(align, _) => Validator::validate_load(context, align, 4, ValueType::I64), + I64Load32U(align, _) => Validator::validate_load(context, align, 4, ValueType::I64), - I32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::I32.into()), - I64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::I64.into()), - F32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::F32.into()), - F64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::F64.into()), - I32Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I32.into()), - I32Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I32.into()), - I64Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I64.into()), - I64Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I64.into()), - I64Store32(align, _) => Validator::validate_store(context, align, 4, ValueType::I64.into()), + I32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::I32), + I64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::I64), + F32Store(align, _) => Validator::validate_store(context, align, 4, ValueType::F32), + F64Store(align, _) => Validator::validate_store(context, align, 8, ValueType::F64), + I32Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I32), + I32Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I32), + I64Store8(align, _) => Validator::validate_store(context, align, 1, ValueType::I64), + I64Store16(align, _) => Validator::validate_store(context, align, 2, ValueType::I64), + I64Store32(align, _) => Validator::validate_store(context, align, 4, ValueType::I64), CurrentMemory(_) => Validator::validate_current_memory(context), GrowMemory(_) => Validator::validate_grow_memory(context), - I32Const(_) => Validator::validate_const(context, ValueType::I32.into()), - I64Const(_) => Validator::validate_const(context, ValueType::I64.into()), - F32Const(_) => Validator::validate_const(context, ValueType::F32.into()), - F64Const(_) => Validator::validate_const(context, ValueType::F64.into()), + I32Const(_) => Validator::validate_const(context, ValueType::I32), + I64Const(_) => Validator::validate_const(context, ValueType::I64), + F32Const(_) => Validator::validate_const(context, ValueType::F32), + F64Const(_) => Validator::validate_const(context, ValueType::F64), - I32Eqz => Validator::validate_testop(context, ValueType::I32.into()), - I32Eq => Validator::validate_relop(context, ValueType::I32.into()), - I32Ne => Validator::validate_relop(context, ValueType::I32.into()), - I32LtS => Validator::validate_relop(context, ValueType::I32.into()), - I32LtU => Validator::validate_relop(context, ValueType::I32.into()), - I32GtS => Validator::validate_relop(context, ValueType::I32.into()), - I32GtU => Validator::validate_relop(context, ValueType::I32.into()), - I32LeS => Validator::validate_relop(context, ValueType::I32.into()), - I32LeU => Validator::validate_relop(context, ValueType::I32.into()), - I32GeS => Validator::validate_relop(context, ValueType::I32.into()), - I32GeU => Validator::validate_relop(context, ValueType::I32.into()), + I32Eqz => Validator::validate_testop(context, ValueType::I32), + I32Eq => Validator::validate_relop(context, ValueType::I32), + I32Ne => Validator::validate_relop(context, ValueType::I32), + I32LtS => Validator::validate_relop(context, ValueType::I32), + I32LtU => Validator::validate_relop(context, ValueType::I32), + I32GtS => Validator::validate_relop(context, ValueType::I32), + I32GtU => Validator::validate_relop(context, ValueType::I32), + I32LeS => Validator::validate_relop(context, ValueType::I32), + I32LeU => Validator::validate_relop(context, ValueType::I32), + I32GeS => Validator::validate_relop(context, ValueType::I32), + I32GeU => Validator::validate_relop(context, ValueType::I32), - I64Eqz => Validator::validate_testop(context, ValueType::I64.into()), - I64Eq => Validator::validate_relop(context, ValueType::I64.into()), - I64Ne => Validator::validate_relop(context, ValueType::I64.into()), - I64LtS => Validator::validate_relop(context, ValueType::I64.into()), - I64LtU => Validator::validate_relop(context, ValueType::I64.into()), - I64GtS => Validator::validate_relop(context, ValueType::I64.into()), - I64GtU => Validator::validate_relop(context, ValueType::I64.into()), - I64LeS => Validator::validate_relop(context, ValueType::I64.into()), - I64LeU => Validator::validate_relop(context, ValueType::I64.into()), - I64GeS => Validator::validate_relop(context, ValueType::I64.into()), - I64GeU => Validator::validate_relop(context, ValueType::I64.into()), + I64Eqz => Validator::validate_testop(context, ValueType::I64), + I64Eq => Validator::validate_relop(context, ValueType::I64), + I64Ne => Validator::validate_relop(context, ValueType::I64), + I64LtS => Validator::validate_relop(context, ValueType::I64), + I64LtU => Validator::validate_relop(context, ValueType::I64), + I64GtS => Validator::validate_relop(context, ValueType::I64), + I64GtU => Validator::validate_relop(context, ValueType::I64), + I64LeS => Validator::validate_relop(context, ValueType::I64), + I64LeU => Validator::validate_relop(context, ValueType::I64), + I64GeS => Validator::validate_relop(context, ValueType::I64), + I64GeU => Validator::validate_relop(context, ValueType::I64), - F32Eq => Validator::validate_relop(context, ValueType::F32.into()), - F32Ne => Validator::validate_relop(context, ValueType::F32.into()), - F32Lt => Validator::validate_relop(context, ValueType::F32.into()), - F32Gt => Validator::validate_relop(context, ValueType::F32.into()), - F32Le => Validator::validate_relop(context, ValueType::F32.into()), - F32Ge => Validator::validate_relop(context, ValueType::F32.into()), + F32Eq => Validator::validate_relop(context, ValueType::F32), + F32Ne => Validator::validate_relop(context, ValueType::F32), + F32Lt => Validator::validate_relop(context, ValueType::F32), + F32Gt => Validator::validate_relop(context, ValueType::F32), + F32Le => Validator::validate_relop(context, ValueType::F32), + F32Ge => Validator::validate_relop(context, ValueType::F32), - F64Eq => Validator::validate_relop(context, ValueType::F64.into()), - F64Ne => Validator::validate_relop(context, ValueType::F64.into()), - F64Lt => Validator::validate_relop(context, ValueType::F64.into()), - F64Gt => Validator::validate_relop(context, ValueType::F64.into()), - F64Le => Validator::validate_relop(context, ValueType::F64.into()), - F64Ge => Validator::validate_relop(context, ValueType::F64.into()), + F64Eq => Validator::validate_relop(context, ValueType::F64), + F64Ne => Validator::validate_relop(context, ValueType::F64), + F64Lt => Validator::validate_relop(context, ValueType::F64), + F64Gt => Validator::validate_relop(context, ValueType::F64), + F64Le => Validator::validate_relop(context, ValueType::F64), + F64Ge => Validator::validate_relop(context, ValueType::F64), - I32Clz => Validator::validate_unop(context, ValueType::I32.into()), - I32Ctz => Validator::validate_unop(context, ValueType::I32.into()), - I32Popcnt => Validator::validate_unop(context, ValueType::I32.into()), - I32Add => Validator::validate_binop(context, ValueType::I32.into()), - I32Sub => Validator::validate_binop(context, ValueType::I32.into()), - I32Mul => Validator::validate_binop(context, ValueType::I32.into()), - I32DivS => Validator::validate_binop(context, ValueType::I32.into()), - I32DivU => Validator::validate_binop(context, ValueType::I32.into()), - I32RemS => Validator::validate_binop(context, ValueType::I32.into()), - I32RemU => Validator::validate_binop(context, ValueType::I32.into()), - I32And => Validator::validate_binop(context, ValueType::I32.into()), - I32Or => Validator::validate_binop(context, ValueType::I32.into()), - I32Xor => Validator::validate_binop(context, ValueType::I32.into()), - I32Shl => Validator::validate_binop(context, ValueType::I32.into()), - I32ShrS => Validator::validate_binop(context, ValueType::I32.into()), - I32ShrU => Validator::validate_binop(context, ValueType::I32.into()), - I32Rotl => Validator::validate_binop(context, ValueType::I32.into()), - I32Rotr => Validator::validate_binop(context, ValueType::I32.into()), + I32Clz => Validator::validate_unop(context, ValueType::I32), + I32Ctz => Validator::validate_unop(context, ValueType::I32), + I32Popcnt => Validator::validate_unop(context, ValueType::I32), + I32Add => Validator::validate_binop(context, ValueType::I32), + I32Sub => Validator::validate_binop(context, ValueType::I32), + I32Mul => Validator::validate_binop(context, ValueType::I32), + I32DivS => Validator::validate_binop(context, ValueType::I32), + I32DivU => Validator::validate_binop(context, ValueType::I32), + I32RemS => Validator::validate_binop(context, ValueType::I32), + I32RemU => Validator::validate_binop(context, ValueType::I32), + I32And => Validator::validate_binop(context, ValueType::I32), + I32Or => Validator::validate_binop(context, ValueType::I32), + I32Xor => Validator::validate_binop(context, ValueType::I32), + I32Shl => Validator::validate_binop(context, ValueType::I32), + I32ShrS => Validator::validate_binop(context, ValueType::I32), + I32ShrU => Validator::validate_binop(context, ValueType::I32), + I32Rotl => Validator::validate_binop(context, ValueType::I32), + I32Rotr => Validator::validate_binop(context, ValueType::I32), - I64Clz => Validator::validate_unop(context, ValueType::I64.into()), - I64Ctz => Validator::validate_unop(context, ValueType::I64.into()), - I64Popcnt => Validator::validate_unop(context, ValueType::I64.into()), - I64Add => Validator::validate_binop(context, ValueType::I64.into()), - I64Sub => Validator::validate_binop(context, ValueType::I64.into()), - I64Mul => Validator::validate_binop(context, ValueType::I64.into()), - I64DivS => Validator::validate_binop(context, ValueType::I64.into()), - I64DivU => Validator::validate_binop(context, ValueType::I64.into()), - I64RemS => Validator::validate_binop(context, ValueType::I64.into()), - I64RemU => Validator::validate_binop(context, ValueType::I64.into()), - I64And => Validator::validate_binop(context, ValueType::I64.into()), - I64Or => Validator::validate_binop(context, ValueType::I64.into()), - I64Xor => Validator::validate_binop(context, ValueType::I64.into()), - I64Shl => Validator::validate_binop(context, ValueType::I64.into()), - I64ShrS => Validator::validate_binop(context, ValueType::I64.into()), - I64ShrU => Validator::validate_binop(context, ValueType::I64.into()), - I64Rotl => Validator::validate_binop(context, ValueType::I64.into()), - I64Rotr => Validator::validate_binop(context, ValueType::I64.into()), + I64Clz => Validator::validate_unop(context, ValueType::I64), + I64Ctz => Validator::validate_unop(context, ValueType::I64), + I64Popcnt => Validator::validate_unop(context, ValueType::I64), + I64Add => Validator::validate_binop(context, ValueType::I64), + I64Sub => Validator::validate_binop(context, ValueType::I64), + I64Mul => Validator::validate_binop(context, ValueType::I64), + I64DivS => Validator::validate_binop(context, ValueType::I64), + I64DivU => Validator::validate_binop(context, ValueType::I64), + I64RemS => Validator::validate_binop(context, ValueType::I64), + I64RemU => Validator::validate_binop(context, ValueType::I64), + I64And => Validator::validate_binop(context, ValueType::I64), + I64Or => Validator::validate_binop(context, ValueType::I64), + I64Xor => Validator::validate_binop(context, ValueType::I64), + I64Shl => Validator::validate_binop(context, ValueType::I64), + I64ShrS => Validator::validate_binop(context, ValueType::I64), + I64ShrU => Validator::validate_binop(context, ValueType::I64), + I64Rotl => Validator::validate_binop(context, ValueType::I64), + I64Rotr => Validator::validate_binop(context, ValueType::I64), - F32Abs => Validator::validate_unop(context, ValueType::F32.into()), - F32Neg => Validator::validate_unop(context, ValueType::F32.into()), - F32Ceil => Validator::validate_unop(context, ValueType::F32.into()), - F32Floor => Validator::validate_unop(context, ValueType::F32.into()), - F32Trunc => Validator::validate_unop(context, ValueType::F32.into()), - F32Nearest => Validator::validate_unop(context, ValueType::F32.into()), - F32Sqrt => Validator::validate_unop(context, ValueType::F32.into()), - F32Add => Validator::validate_binop(context, ValueType::F32.into()), - F32Sub => Validator::validate_binop(context, ValueType::F32.into()), - F32Mul => Validator::validate_binop(context, ValueType::F32.into()), - F32Div => Validator::validate_binop(context, ValueType::F32.into()), - F32Min => Validator::validate_binop(context, ValueType::F32.into()), - F32Max => Validator::validate_binop(context, ValueType::F32.into()), - F32Copysign => Validator::validate_binop(context, ValueType::F32.into()), + F32Abs => Validator::validate_unop(context, ValueType::F32), + F32Neg => Validator::validate_unop(context, ValueType::F32), + F32Ceil => Validator::validate_unop(context, ValueType::F32), + F32Floor => Validator::validate_unop(context, ValueType::F32), + F32Trunc => Validator::validate_unop(context, ValueType::F32), + F32Nearest => Validator::validate_unop(context, ValueType::F32), + F32Sqrt => Validator::validate_unop(context, ValueType::F32), + F32Add => Validator::validate_binop(context, ValueType::F32), + F32Sub => Validator::validate_binop(context, ValueType::F32), + F32Mul => Validator::validate_binop(context, ValueType::F32), + F32Div => Validator::validate_binop(context, ValueType::F32), + F32Min => Validator::validate_binop(context, ValueType::F32), + F32Max => Validator::validate_binop(context, ValueType::F32), + F32Copysign => Validator::validate_binop(context, ValueType::F32), - F64Abs => Validator::validate_unop(context, ValueType::F64.into()), - F64Neg => Validator::validate_unop(context, ValueType::F64.into()), - F64Ceil => Validator::validate_unop(context, ValueType::F64.into()), - F64Floor => Validator::validate_unop(context, ValueType::F64.into()), - F64Trunc => Validator::validate_unop(context, ValueType::F64.into()), - F64Nearest => Validator::validate_unop(context, ValueType::F64.into()), - F64Sqrt => Validator::validate_unop(context, ValueType::F64.into()), - F64Add => Validator::validate_binop(context, ValueType::F64.into()), - F64Sub => Validator::validate_binop(context, ValueType::F64.into()), - F64Mul => Validator::validate_binop(context, ValueType::F64.into()), - F64Div => Validator::validate_binop(context, ValueType::F64.into()), - F64Min => Validator::validate_binop(context, ValueType::F64.into()), - F64Max => Validator::validate_binop(context, ValueType::F64.into()), - F64Copysign => Validator::validate_binop(context, ValueType::F64.into()), + F64Abs => Validator::validate_unop(context, ValueType::F64), + F64Neg => Validator::validate_unop(context, ValueType::F64), + F64Ceil => Validator::validate_unop(context, ValueType::F64), + F64Floor => Validator::validate_unop(context, ValueType::F64), + F64Trunc => Validator::validate_unop(context, ValueType::F64), + F64Nearest => Validator::validate_unop(context, ValueType::F64), + F64Sqrt => Validator::validate_unop(context, ValueType::F64), + F64Add => Validator::validate_binop(context, ValueType::F64), + F64Sub => Validator::validate_binop(context, ValueType::F64), + F64Mul => Validator::validate_binop(context, ValueType::F64), + F64Div => Validator::validate_binop(context, ValueType::F64), + F64Min => Validator::validate_binop(context, ValueType::F64), + F64Max => Validator::validate_binop(context, ValueType::F64), + F64Copysign => Validator::validate_binop(context, ValueType::F64), - I32WarpI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::I32.into()), - I32TruncSF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()), - I32TruncUF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()), - I32TruncSF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I32.into()), - I32TruncUF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I32.into()), - I64ExtendSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::I64.into()), - I64ExtendUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::I64.into()), - I64TruncSF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I64.into()), - I64TruncUF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I64.into()), - I64TruncSF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()), - I64TruncUF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()), - F32ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()), - F32ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()), - F32ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F32.into()), - F32ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F32.into()), - F32DemoteF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::F32.into()), - F64ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F64.into()), - F64ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F64.into()), - F64ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()), - F64ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()), - F64PromoteF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::F64.into()), + I32WarpI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::I32), + I32TruncSF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32), + I32TruncUF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32), + I32TruncSF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I32), + I32TruncUF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I32), + I64ExtendSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::I64), + I64ExtendUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::I64), + I64TruncSF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I64), + I64TruncUF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I64), + I64TruncSF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64), + I64TruncUF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64), + F32ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32), + F32ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32), + F32ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F32), + F32ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F32), + F32DemoteF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::F32), + F64ConvertSI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F64), + F64ConvertUI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F64), + F64ConvertSI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64), + F64ConvertUI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64), + F64PromoteF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::F64), - I32ReinterpretF32 => Validator::validate_cvtop(context, ValueType::F32.into(), ValueType::I32.into()), - I64ReinterpretF64 => Validator::validate_cvtop(context, ValueType::F64.into(), ValueType::I64.into()), - F32ReinterpretI32 => Validator::validate_cvtop(context, ValueType::I32.into(), ValueType::F32.into()), - F64ReinterpretI64 => Validator::validate_cvtop(context, ValueType::I64.into(), ValueType::F64.into()), + I32ReinterpretF32 => Validator::validate_cvtop(context, ValueType::F32, ValueType::I32), + I64ReinterpretF64 => Validator::validate_cvtop(context, ValueType::F64, ValueType::I64), + F32ReinterpretI32 => Validator::validate_cvtop(context, ValueType::I32, ValueType::F32), + F64ReinterpretI64 => Validator::validate_cvtop(context, ValueType::I64, ValueType::F64), } } - fn validate_const(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result { - context.push_value(value_type)?; + fn validate_const(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { + context.push_value(value_type.into())?; Ok(InstructionOutcome::ValidateNextInstruction) } - fn validate_unop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result { - context.pop_value(value_type)?; - context.push_value(value_type)?; + fn validate_unop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { + context.pop_value(value_type.into())?; + context.push_value(value_type.into())?; 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)?; + fn validate_binop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { + context.pop_value(value_type.into())?; + context.pop_value(value_type.into())?; + context.push_value(value_type.into())?; Ok(InstructionOutcome::ValidateNextInstruction) } - fn validate_testop(context: &mut FunctionValidationContext, value_type: StackValueType) -> Result { - context.pop_value(value_type)?; + fn validate_testop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { + context.pop_value(value_type.into())?; 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)?; + fn validate_relop(context: &mut FunctionValidationContext, value_type: ValueType) -> Result { + context.pop_value(value_type.into())?; + context.pop_value(value_type.into())?; 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)?; + fn validate_cvtop(context: &mut FunctionValidationContext, value_type1: ValueType, value_type2: ValueType) -> Result { + context.pop_value(value_type1.into())?; + context.push_value(value_type2.into())?; Ok(InstructionOutcome::ValidateNextInstruction) } @@ -405,7 +405,7 @@ impl Validator { Ok(InstructionOutcome::ValidateNextInstruction) } - fn validate_load(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result { + fn validate_load(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: ValueType) -> Result { if align != NATURAL_ALIGNMENT { if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align { return Err(Error(format!("Too large memory alignment 2^{} (expected at most {})", align, max_align))); @@ -414,11 +414,11 @@ impl Validator { context.pop_value(ValueType::I32.into())?; context.module.require_memory(DEFAULT_MEMORY_INDEX)?; - context.push_value(value_type)?; + context.push_value(value_type.into())?; Ok(InstructionOutcome::ValidateNextInstruction) } - fn validate_store(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: StackValueType) -> Result { + fn validate_store(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: ValueType) -> Result { if align != NATURAL_ALIGNMENT { if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align { return Err(Error(format!("Too large memory alignment 2^{} (expected at most {})", align, max_align))); @@ -426,7 +426,7 @@ impl Validator { } context.module.require_memory(DEFAULT_MEMORY_INDEX)?; - context.pop_value(value_type)?; + context.pop_value(value_type.into())?; context.pop_value(ValueType::I32.into())?; Ok(InstructionOutcome::ValidateNextInstruction) }