From d92bfa1b2cd150f3c5a5e7bc3789e56d91a5656f Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 1 Dec 2017 18:53:19 +0300 Subject: [PATCH] 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();