diff --git a/Cargo.toml b/Cargo.toml index b6c0a41..00f9c6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parity-wasm" -version = "0.8.5" +version = "0.10.0" authors = ["Nikolay Volf ", "Svyatoslav Nikolsky "] license = "MIT/Apache-2.0" readme = "README.md" @@ -12,5 +12,6 @@ keywords = ["wasm", "webassembly", "bytecode", "serde", "interpreter"] exclude = [ "res/*", "spec/*" ] [dependencies] +log = "0.3" byteorder = "1.0" parking_lot = "0.4" \ No newline at end of file diff --git a/src/builder/data.rs b/src/builder/data.rs new file mode 100644 index 0000000..46398e5 --- /dev/null +++ b/src/builder/data.rs @@ -0,0 +1,49 @@ +use super::invoke::{Identity, Invoke}; +use elements; + +pub struct DataSegmentBuilder { + callback: F, + // todo: add mapper once multiple memory refs possible + mem_index: u32, + offset: elements::InitExpr, + value: Vec, +} + +impl DataSegmentBuilder { + pub fn new() -> Self { + DataSegmentBuilder::with_callback(Identity) + } +} + +impl DataSegmentBuilder { + pub fn with_callback(callback: F) -> Self { + DataSegmentBuilder { + callback: callback, + mem_index: 0, + offset: elements::InitExpr::empty(), + value: Vec::new(), + } + } + + pub fn offset(mut self, opcode: elements::Opcode) -> Self { + self.offset = elements::InitExpr::new(vec![opcode, elements::Opcode::End]); + self + } + + pub fn value(mut self, value: Vec) -> Self { + self.value = value; + self + } +} + +impl DataSegmentBuilder where F: Invoke { + pub fn build(self) -> F::Result { + self.callback.invoke( + elements::DataSegment::new( + self.mem_index, + self.offset, + self.value, + ) + ) + } +} \ No newline at end of file diff --git a/src/builder/global.rs b/src/builder/global.rs index 2cae4ba..9c666f3 100644 --- a/src/builder/global.rs +++ b/src/builder/global.rs @@ -56,7 +56,6 @@ impl GlobalBuilder where F: Invoke { } } - impl Invoke for GlobalBuilder { type Result = Self; fn invoke(self, the_type: elements::ValueType) -> Self { diff --git a/src/builder/mod.rs b/src/builder/mod.rs index 7d7437e..d590a75 100644 --- a/src/builder/mod.rs +++ b/src/builder/mod.rs @@ -9,6 +9,7 @@ mod memory; mod table; mod export; mod global; +mod data; pub use self::module::{module, from_module, ModuleBuilder}; pub use self::code::{signatures, signature, function}; diff --git a/src/builder/module.rs b/src/builder/module.rs index d4ef09d..c63b5a1 100644 --- a/src/builder/module.rs +++ b/src/builder/module.rs @@ -2,7 +2,7 @@ use super::invoke::{Invoke, Identity}; use super::code::{self, SignaturesBuilder, FunctionBuilder}; use super::memory::{self, MemoryBuilder}; use super::table::{self, TableBuilder}; -use super::{import, export, global}; +use super::{import, export, global, data}; use elements; /// Module builder @@ -327,6 +327,17 @@ impl ModuleBuilder where F: Invoke { global::GlobalBuilder::with_callback(self) } + /// Add data segment to the builder + pub fn with_data_segment(mut self, segment: elements::DataSegment) -> Self { + self.module.data.entries_mut().push(segment); + self + } + + /// Data entry builder + pub fn data(self) -> data::DataSegmentBuilder { + data::DataSegmentBuilder::with_callback(self) + } + /// Build module (final step) pub fn build(self) -> F::Result { self.callback.invoke(self.module.into()) @@ -420,6 +431,16 @@ impl Invoke for ModuleBuilder } } +impl Invoke for ModuleBuilder + where F: Invoke +{ + type Result = Self; + + fn invoke(self, segment: elements::DataSegment) -> Self { + self.with_data_segment(segment) + } +} + /// Start new module builder pub fn module() -> ModuleBuilder { ModuleBuilder::new() @@ -472,4 +493,16 @@ mod tests { assert_eq!(module.global_section().expect("global section to exist").entries().len(), 1); } + + #[test] + fn data() { + let module = module() + .data() + .offset(::elements::Opcode::I32Const(16)) + .value(vec![0u8, 15, 10, 5, 25]) + .build() + .build(); + + assert_eq!(module.data_section().expect("data section to exist").entries().len(), 1); + } } diff --git a/src/elements/ops.rs b/src/elements/ops.rs index 4346f6d..b80b29e 100644 --- a/src/elements/ops.rs +++ b/src/elements/ops.rs @@ -122,6 +122,8 @@ pub enum Opcode { GetGlobal(u32), SetGlobal(u32), + // All store/load opcodes operate with 'memory immediates' + // which represented here as (flag, offset) tuple I32Load(u32, u32), I64Load(u32, u32), F32Load(u32, u32), @@ -145,6 +147,7 @@ pub enum Opcode { I64Store8(u32, u32), I64Store16(u32, u32), I64Store32(u32, u32), + CurrentMemory(bool), GrowMemory(bool), @@ -649,97 +652,97 @@ impl Serialize for Opcode { SetGlobal(index) => op!(writer, 0x24, { VarUint32::from(index).serialize(writer)?; }), - I32Load(from, to) => op!(writer, 0x28, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I32Load(flags, offset) => op!(writer, 0x28, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I64Load(from, to) => op!(writer, 0x29, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I64Load(flags, offset) => op!(writer, 0x29, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - F32Load(from, to) => op!(writer, 0x2a, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + F32Load(flags, offset) => op!(writer, 0x2a, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - F64Load(from, to) => op!(writer, 0x2b, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + F64Load(flags, offset) => op!(writer, 0x2b, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I32Load8S(from, to) => op!(writer, 0x2c, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I32Load8S(flags, offset) => op!(writer, 0x2c, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I32Load8U(from, to) => op!(writer, 0x2d, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I32Load8U(flags, offset) => op!(writer, 0x2d, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I32Load16S(from, to) => op!(writer, 0x2e, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I32Load16S(flags, offset) => op!(writer, 0x2e, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I32Load16U(from, to) => op!(writer, 0x2f, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I32Load16U(flags, offset) => op!(writer, 0x2f, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I64Load8S(from, to) => op!(writer, 0x30, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I64Load8S(flags, offset) => op!(writer, 0x30, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I64Load8U(from, to) => op!(writer, 0x31, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I64Load8U(flags, offset) => op!(writer, 0x31, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I64Load16S(from, to) => op!(writer, 0x32, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I64Load16S(flags, offset) => op!(writer, 0x32, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I64Load16U(from, to) => op!(writer, 0x33, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I64Load16U(flags, offset) => op!(writer, 0x33, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I64Load32S(from, to) => op!(writer, 0x34, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I64Load32S(flags, offset) => op!(writer, 0x34, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I64Load32U(from, to) => op!(writer, 0x35, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I64Load32U(flags, offset) => op!(writer, 0x35, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I32Store(from, to) => op!(writer, 0x36, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I32Store(flags, offset) => op!(writer, 0x36, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I64Store(from, to) => op!(writer, 0x37, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I64Store(flags, offset) => op!(writer, 0x37, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - F32Store(from, to) => op!(writer, 0x38, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + F32Store(flags, offset) => op!(writer, 0x38, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - F64Store(from, to) => op!(writer, 0x39, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + F64Store(flags, offset) => op!(writer, 0x39, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I32Store8(from, to) => op!(writer, 0x3a, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I32Store8(flags, offset) => op!(writer, 0x3a, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I32Store16(from, to) => op!(writer, 0x3b, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I32Store16(flags, offset) => op!(writer, 0x3b, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I64Store8(from, to) => op!(writer, 0x3c, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I64Store8(flags, offset) => op!(writer, 0x3c, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I64Store16(from, to) => op!(writer, 0x3d, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I64Store16(flags, offset) => op!(writer, 0x3d, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), - I64Store32(from, to) => op!(writer, 0x3e, { - VarUint32::from(from).serialize(writer)?; - VarUint32::from(to).serialize(writer)?; + I64Store32(flags, offset) => op!(writer, 0x3e, { + VarUint32::from(flags).serialize(writer)?; + VarUint32::from(offset).serialize(writer)?; }), CurrentMemory(flag) => op!(writer, 0x3f, { VarUint1::from(flag).serialize(writer)?; diff --git a/src/elements/primitives.rs b/src/elements/primitives.rs index ee98a76..305f597 100644 --- a/src/elements/primitives.rs +++ b/src/elements/primitives.rs @@ -427,7 +427,6 @@ impl Deserialize for VarUint1 { fn deserialize(reader: &mut R) -> Result { let mut u8buf = [0u8; 1]; reader.read_exact(&mut u8buf)?; - // todo check range match u8buf[0] { 0 => Ok(VarUint1(false)), 1 => Ok(VarUint1(true)), diff --git a/src/interpreter/env_native.rs b/src/interpreter/env_native.rs index b4b18db..3bca979 100644 --- a/src/interpreter/env_native.rs +++ b/src/interpreter/env_native.rs @@ -1,5 +1,6 @@ use std::sync::Arc; use std::collections::HashMap; +use std::borrow::Cow; use parking_lot::RwLock; use elements::{FunctionType, Internal, ValueType}; use interpreter::Error; @@ -19,20 +20,67 @@ pub trait UserFunctionExecutor { fn execute(&mut self, name: &str, context: CallerContext) -> Result, Error>; } +/// User function descriptor +#[derive(Clone)] +pub enum UserFunctionDescriptor { + /// Static function definition + Static(&'static str, &'static [ValueType]), + /// Dynamic heap function definition + Heap(String, Vec), +} + /// User function type. +#[derive(Clone)] pub struct UserFunction { - /// User function name. - pub name: String, - /// User function parameters (for signature matching). - pub params: Vec, - /// User function return type (for signature matching). + /// Descriptor with variable-length definitions + pub desc: UserFunctionDescriptor, + /// Return type of the signature pub result: Option, } +impl UserFunction { + /// New function with statically known params + pub fn statik(name: &'static str, params: &'static [ValueType], result: Option) -> Self { + UserFunction { + desc: UserFunctionDescriptor::Static(name, params), + result: result, + } + } + + /// New function with statically unknown params + pub fn heap(name: String, params: Vec, result: Option) -> Self { + UserFunction { + desc: UserFunctionDescriptor::Heap(name, params), + result: result, + } + } + + /// Name of the function + pub fn name(&self) -> &str { + match self.desc { + UserFunctionDescriptor::Static(name, _) => name, + UserFunctionDescriptor::Heap(ref name, _) => name, + } + } + + /// Arguments of the function + pub fn params(&self) -> &[ValueType] { + match self.desc { + UserFunctionDescriptor::Static(_, params) => params, + UserFunctionDescriptor::Heap(_, ref params) => params, + } + } + + /// Return type of the function + pub fn result(&self) -> Option { + self.result + } +} + /// Set of user-defined functions pub struct UserFunctions<'a> { /// Functions list. - pub functions: Vec, + pub functions: Cow<'static, [UserFunction]>, /// Functions executor. pub executor: &'a mut UserFunctionExecutor, } @@ -46,7 +94,7 @@ pub struct NativeModuleInstance<'a> { /// By-name functions index. by_name: HashMap, /// User functions list. - functions: Vec, + functions: Cow<'static, [UserFunction]>, } impl<'a> NativeModuleInstance<'a> { @@ -55,7 +103,7 @@ impl<'a> NativeModuleInstance<'a> { Ok(NativeModuleInstance { env: env, executor: RwLock::new(functions.executor), - by_name: functions.functions.iter().enumerate().map(|(i, f)| (f.name.clone(), i as u32)).collect(), + by_name: functions.functions.iter().enumerate().map(|(i, f)| (f.name().to_owned(), i as u32)).collect(), functions: functions.functions, }) } @@ -108,7 +156,7 @@ impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> { self.functions .get((index - NATIVE_INDEX_FUNC_MIN) as usize) .ok_or(Error::Native(format!("missing native env function with index {}", index))) - .map(|f| FunctionType::new(f.params.clone(), f.result.clone())) + .map(|f| FunctionType::new(f.params().to_vec(), f.result().clone())) } fn function_type_by_index(&self, type_index: u32) -> Result { @@ -135,7 +183,7 @@ impl<'a> ModuleInstanceInterface for NativeModuleInstance<'a> { self.functions .get((index - NATIVE_INDEX_FUNC_MIN) as usize) .ok_or(Error::Native(format!("trying to call native function with index {}", index))) - .and_then(|f| self.executor.write().execute(&f.name, outer)) + .and_then(|f| self.executor.write().execute(&f.name(), outer)) } } diff --git a/src/interpreter/memory.rs b/src/interpreter/memory.rs index 7aadcae..43a921f 100644 --- a/src/interpreter/memory.rs +++ b/src/interpreter/memory.rs @@ -18,6 +18,22 @@ pub struct MemoryInstance { maximum_size: u32, } +struct CheckedRegion<'a, B: 'a> where B: ::std::ops::Deref> { + buffer: &'a B, + offset: usize, + size: usize, +} + +impl<'a, B: 'a> CheckedRegion<'a, B> where B: ::std::ops::Deref> { + fn range(&self) -> ::std::ops::Range { + self.offset..self.offset+self.size + } + + fn slice(&self) -> &[u8] { + &self.buffer[self.range()] + } +} + impl MemoryInstance { /// Create new linear memory instance. pub fn new(memory_type: &MemoryType) -> Result, Error> { @@ -48,36 +64,18 @@ impl MemoryInstance { /// Get data at given offset. pub fn get(&self, offset: u32, size: usize) -> Result, Error> { - let begin = offset as usize; - let end = match begin.checked_add(size) { - Some(end) => end, - None => return Err(Error::Memory(format!("trying to read memory block of size {} from offset {}", size, offset))), - }; - let buffer = self.buffer.read(); - if buffer.len() < end { - return Err(Error::Memory(format!("trying to read region [{}..{}] in memory [0..{}]", begin, end, buffer.len()))); - } + let region = self.checked_region(&buffer, offset as usize, size)?; - Ok(buffer[begin..end].to_vec()) + Ok(region.slice().to_vec()) } /// Set data at given offset. pub fn set(&self, offset: u32, value: &[u8]) -> Result<(), Error> { - let size = value.len(); - let begin = offset as usize; - let end = match begin.checked_add(size) { - Some(end) => end, - None => return Err(Error::Memory(format!("trying to update memory block of size {} from offset {}", size, offset))), - }; - let mut buffer = self.buffer.write(); - if buffer.len() < end { - return Err(Error::Memory(format!("trying to update region [{}..{}] in memory [0..{}]", begin, end, buffer.len()))); - } + let range = self.checked_region(&buffer, offset as usize, value.len())?.range(); - let mut mut_buffer = buffer.as_mut_slice(); - mut_buffer[begin..end].copy_from_slice(value); + buffer[range].copy_from_slice(value); Ok(()) } @@ -96,4 +94,46 @@ impl MemoryInstance { }, } } + + fn checked_region<'a, B>(&self, buffer: &'a B, offset: usize, size: usize) -> Result, Error> + where B: ::std::ops::Deref> + { + let end = offset.checked_add(size) + .ok_or(Error::Memory(format!("trying to access memory block of size {} from offset {}", size, offset)))?; + + if end > buffer.len() { + return Err(Error::Memory(format!("trying to access region [{}..{}] in memory [0..{}]", offset, end, buffer.len()))); + } + + Ok(CheckedRegion { + buffer: buffer, + offset: offset, + size: size, + }) + } + + /// Copy memory region + pub fn copy(&self, src_offset: usize, dst_offset: usize, len: usize) -> Result<(), Error> { + let buffer = self.buffer.write(); + + let read_region = self.checked_region(&buffer, src_offset, len)?; + let write_region = self.checked_region(&buffer, dst_offset, len)?; + + unsafe { ::std::ptr::copy( + buffer[read_region.range()].as_ptr(), + buffer[write_region.range()].as_ptr() as *mut _, + len, + )} + + Ok(()) + } + + /// Zero memory region + pub fn zero(&self, offset: usize, len: usize) -> Result<(), Error> { + let mut buffer = self.buffer.write(); + + let range = self.checked_region(&buffer, offset, len)?.range(); + for val in &mut buffer[range] { *val = 0 } + Ok(()) + } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 7d3331a..33155bd 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -79,5 +79,5 @@ pub use self::table::TableInstance; pub use self::program::ProgramInstance; pub use self::value::RuntimeValue; pub use self::variable::VariableInstance; -pub use self::env_native::{env_native_module, UserFunctions, UserFunction, UserFunctionExecutor}; +pub use self::env_native::{env_native_module, UserFunctions, UserFunction, UserFunctionExecutor, UserFunctionDescriptor}; pub use self::env::EnvParams; \ No newline at end of file diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index a100a1f..b470a28 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -41,7 +41,7 @@ pub enum ExportEntryType { /// Module instance API. pub trait ModuleInstanceInterface { - /// Run instantiation-time procedures (validation and start function call). Module is not completely validated until this call. + /// Run instantiation-time procedures (validation and start function [if any] call). Module is not completely validated until this call. fn instantiate<'a>(&self, is_user_module: bool, externals: Option<&'a HashMap>>) -> Result<(), Error>; /// Execute function with the given index. fn execute_index(&self, index: u32, params: ExecutionParams) -> Result, Error>; @@ -335,9 +335,23 @@ impl ModuleInstanceInterface for ModuleInstance { 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.module, &self.imports, &locals, DEFAULT_VALUE_STACK_LIMIT, DEFAULT_FRAME_STACK_LIMIT, &function_type); + let mut context = FunctionValidationContext::new( + &self.module, + &self.imports, + &locals, + DEFAULT_VALUE_STACK_LIMIT, + DEFAULT_FRAME_STACK_LIMIT, + &function_type); + let block_type = function_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult); 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 + } + })?; } } diff --git a/src/interpreter/runner.rs b/src/interpreter/runner.rs index fed497a..d8c6bc6 100644 --- a/src/interpreter/runner.rs +++ b/src/interpreter/runner.rs @@ -187,8 +187,6 @@ impl Interpreter { let instruction = &body_stack.back().expect("TODO")[top_frame.begin_position]; let block_body = Interpreter::into_block(instruction, top_frame.frame_type)?; body_stack.push_back(block_body); - //body_stack.insert(block_body.len() - 1, block_body); - //function_context.frame_stack_mut().push_penultimate(block_frame)?; }, InstructionOutcome::ExecuteCall(func_ref) => return Ok(RunResult::NestedCall(function_context.nested(func_ref)?)), InstructionOutcome::End if !function_context.frame_stack().is_empty() => { @@ -206,6 +204,7 @@ impl Interpreter { loop { let instruction = &body[context.position]; + debug!(target: "interpreter", "running {:?}", instruction); match Interpreter::run_instruction(context, instruction)? { InstructionOutcome::RunInstruction => (), InstructionOutcome::RunNextInstruction => context.position += 1, diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs index e8e9f35..2931529 100644 --- a/src/interpreter/tests/basics.rs +++ b/src/interpreter/tests/basics.rs @@ -1,14 +1,16 @@ ///! Basic tests for instructions/constructions, missing in wabt tests -use std::sync::Arc; +use std::sync::{Arc, Weak}; use builder::module; use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType, - InitExpr, ValueType, Opcodes, Opcode}; + InitExpr, ValueType, BlockType, Opcodes, Opcode, FunctionType}; use interpreter::Error; -use interpreter::env_native::{env_native_module, UserFunction, UserFunctions, UserFunctionExecutor}; +use interpreter::env_native::{env_native_module, UserFunction, UserFunctions, UserFunctionExecutor, UserFunctionDescriptor}; +use interpreter::imports::ModuleImports; use interpreter::memory::MemoryInstance; use interpreter::module::{ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams}; use interpreter::program::ProgramInstance; +use interpreter::validator::{FunctionValidationContext, Validator}; use interpreter::value::RuntimeValue; #[test] @@ -121,6 +123,25 @@ fn global_get_set() { assert_eq!(module.execute_index(2, vec![].into()).unwrap_err(), Error::Variable("trying to update variable of type I32 with value of type Some(I64)".into())); } +const SIGNATURE_I32: &'static [ValueType] = &[ValueType::I32]; + +const SIGNATURES: &'static [UserFunction] = &[ + UserFunction { + desc: UserFunctionDescriptor::Static( + "add", + SIGNATURE_I32, + ), + result: Some(ValueType::I32), + }, + UserFunction { + desc: UserFunctionDescriptor::Static( + "sub", + SIGNATURE_I32, + ), + result: Some(ValueType::I32), + }, +]; + #[test] fn single_program_different_modules() { // user function executor @@ -168,15 +189,7 @@ fn single_program_different_modules() { { let functions: UserFunctions = UserFunctions { executor: &mut executor, - functions: vec![UserFunction { - name: "add".into(), - params: vec![ValueType::I32], - result: Some(ValueType::I32), - }, UserFunction { - name: "sub".into(), - params: vec![ValueType::I32], - result: Some(ValueType::I32), - }], + functions: ::std::borrow::Cow::from(SIGNATURES), }; let native_env_instance = Arc::new(env_native_module(env_instance, functions).unwrap()); let params = ExecutionParams::with_external("env".into(), native_env_instance); @@ -215,3 +228,26 @@ fn single_program_different_modules() { assert_eq!(executor.memory.get(0, 1).unwrap()[0], 42); assert_eq!(executor.values, vec![7, 57, 42]); } + +#[test] +fn if_else_with_return_type_validation() { + let module = module().build(); + let imports = ModuleImports::new(Weak::default(), None); + let mut context = FunctionValidationContext::new(&module, &imports, &[], 1024, 1024, &FunctionType::default()); + + Validator::validate_block(&mut context, false, BlockType::NoResult, &[ + Opcode::I32Const(1), + Opcode::If(BlockType::NoResult, Opcodes::new(vec![ + Opcode::I32Const(1), + Opcode::If(BlockType::Value(ValueType::I32), Opcodes::new(vec![ + Opcode::I32Const(1), + Opcode::Else, + Opcode::I32Const(2), + Opcode::End, + ])), + Opcode::Drop, + Opcode::End, + ])), + Opcode::End, + ], Opcode::End).unwrap(); +} diff --git a/src/interpreter/validator.rs b/src/interpreter/validator.rs index fbaa185..a326f43 100644 --- a/src/interpreter/validator.rs +++ b/src/interpreter/validator.rs @@ -130,7 +130,7 @@ impl Validator { } pub fn validate_instruction<'a>(context: &mut FunctionValidationContext, opcode: &'a Opcode) -> Result, Error> { - // println!("=== VALIDATING {:?}: {:?}", opcode, context.value_stack); + debug!(target: "validator", "validating {:?}", opcode); match opcode { &Opcode::Unreachable => Ok(InstructionOutcome::Unreachable), &Opcode::Nop => Ok(InstructionOutcome::ValidateNextInstruction), @@ -581,7 +581,14 @@ impl Validator { } impl<'a> FunctionValidationContext<'a> { - pub fn new(module: &'a Module, imports: &'a ModuleImports, locals: &'a [ValueType], value_stack_limit: usize, frame_stack_limit: usize, function: &FunctionType) -> Self { + pub fn new( + module: &'a Module, + imports: &'a ModuleImports, + locals: &'a [ValueType], + value_stack_limit: usize, + frame_stack_limit: usize, + function: &FunctionType, + ) -> Self { FunctionValidationContext { module: module, imports: imports, diff --git a/src/lib.rs b/src/lib.rs index d0443f7..680de78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,8 @@ #![warn(missing_docs)] +#[macro_use] +extern crate log; extern crate byteorder; extern crate parking_lot;