From 41d6f48df482ca1eb614c9b69f9e9e6217f93216 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 27 Jun 2017 13:45:33 +0300 Subject: [PATCH 01/21] example for module() --- src/builder/code.rs | 2 +- src/builder/module.rs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/builder/code.rs b/src/builder/code.rs index b76d941..b3cf39f 100644 --- a/src/builder/code.rs +++ b/src/builder/code.rs @@ -346,7 +346,7 @@ mod tests { .build() .bind(); - assert_eq!(result.len(), 1); + assert_eq!(result.len(), 1); } #[test] diff --git a/src/builder/module.rs b/src/builder/module.rs index c63b5a1..c69d5dd 100644 --- a/src/builder/module.rs +++ b/src/builder/module.rs @@ -442,6 +442,22 @@ impl Invoke for ModuleBuilder } /// Start new module builder +/// # Examples +/// +/// ``` +/// use parity_wasm::builder; +/// +/// let module = builder::module() +/// .function() +/// .signature().param().i32().build() +/// .body().build() +/// .build() +/// .build(); +/// +/// assert_eq!(module.type_section().expect("type section to exist").types().len(), 1); +/// assert_eq!(module.function_section().expect("function section to exist").entries().len(), 1); +/// assert_eq!(module.code_section().expect("code section to exist").bodies().len(), 1); +/// ``` pub fn module() -> ModuleBuilder { ModuleBuilder::new() } From 447c3db90def4661e7a032d43d749f123f7a549e Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 27 Jun 2017 13:54:51 +0300 Subject: [PATCH 02/21] import doc example --- src/builder/module.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/builder/module.rs b/src/builder/module.rs index c69d5dd..8e8d690 100644 --- a/src/builder/module.rs +++ b/src/builder/module.rs @@ -301,6 +301,20 @@ impl ModuleBuilder where F: Invoke { } /// Import entry builder + /// # Examples + /// ``` + /// use parity_wasm::builder::module; + /// + /// let module = module() + /// .import() + /// .module("env") + /// .field("memory") + /// .external().memory(256, Some(256)) + /// .build() + /// .build(); + /// + /// assert_eq!(module.import_section().expect("import section to exist").entries().len(), 1); + /// ``` pub fn import(self) -> import::ImportBuilder { import::ImportBuilder::with_callback(self) } From c72dfae3545353513d4e1c3f99246afea8f85c40 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 27 Jun 2017 14:12:57 +0300 Subject: [PATCH 03/21] export example --- src/builder/module.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/builder/module.rs b/src/builder/module.rs index 8e8d690..9c987a0 100644 --- a/src/builder/module.rs +++ b/src/builder/module.rs @@ -332,6 +332,24 @@ impl ModuleBuilder where F: Invoke { } /// Export entry builder + /// # Examples + /// ``` + /// use parity_wasm::builder::module; + /// use parity_wasm::elements::Opcode::*; + /// + /// let module = module() + /// .global() + /// .value_type().i32() + /// .init_expr(I32Const(0)) + /// .build() + /// .export() + /// .field("_zero") + /// .internal().global(0) + /// .build() + /// .build(); + /// + /// assert_eq!(module.export_section().expect("import section to exist").entries().len(), 1); + /// ``` pub fn export(self) -> export::ExportBuilder { export::ExportBuilder::with_callback(self) } From 216f8ae87138bfe6d86edd1d560600632e327c4e Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 27 Jun 2017 15:21:10 +0300 Subject: [PATCH 04/21] fixed test lags --- src/interpreter/memory.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/memory.rs b/src/interpreter/memory.rs index 171c3ab..4afafe2 100644 --- a/src/interpreter/memory.rs +++ b/src/interpreter/memory.rs @@ -49,7 +49,7 @@ impl MemoryInstance { .ok_or(Error::Memory(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)))?; let memory = MemoryInstance { - buffer: RwLock::new(Vec::with_capacity(initial_size as usize)), + buffer: RwLock::new(vec![0; initial_size as usize]), maximum_size: maximum_size, }; if memory.grow(memory_type.limits().initial())? == u32::MAX { From b82592c03edcfd4964997bc9c3253d4bed201088 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 27 Jun 2017 15:26:10 +0300 Subject: [PATCH 05/21] removed grow() call --- src/interpreter/memory.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/interpreter/memory.rs b/src/interpreter/memory.rs index 4afafe2..c6a9a0d 100644 --- a/src/interpreter/memory.rs +++ b/src/interpreter/memory.rs @@ -52,9 +52,6 @@ impl MemoryInstance { buffer: RwLock::new(vec![0; initial_size as usize]), maximum_size: maximum_size, }; - if memory.grow(memory_type.limits().initial())? == u32::MAX { - return Err(Error::Memory(format!("error initializing {}-bytes linear memory region", initial_size))); - } Ok(Arc::new(memory)) } From 84ad965596b8d0a88596352042df6e6b68cacd41 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 27 Jun 2017 18:36:27 +0300 Subject: [PATCH 06/21] global example --- src/builder/module.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/builder/module.rs b/src/builder/module.rs index 9c987a0..742b05c 100644 --- a/src/builder/module.rs +++ b/src/builder/module.rs @@ -348,13 +348,27 @@ impl ModuleBuilder where F: Invoke { /// .build() /// .build(); /// - /// assert_eq!(module.export_section().expect("import section to exist").entries().len(), 1); + /// assert_eq!(module.export_section().expect("export section to exist").entries().len(), 1); /// ``` pub fn export(self) -> export::ExportBuilder { export::ExportBuilder::with_callback(self) } /// Glboal entry builder + /// # Examples + /// ``` + /// use parity_wasm::builder::module; + /// use parity_wasm::elements::Opcode::*; + /// + /// let module = module() + /// .global() + /// .value_type().i32() + /// .init_expr(I32Const(0)) + /// .build() + /// .build(); + /// + /// assert_eq!(module.global_section().expect("global section to exist").entries().len(), 1); + /// ``` pub fn global(self) -> global::GlobalBuilder { global::GlobalBuilder::with_callback(self) } From aad4dfa67e442fa25ab6aac7101b20e3776bd6bf Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 28 Jun 2017 11:03:01 +0300 Subject: [PATCH 07/21] cache internals --- benches/interpreter.rs | 54 +++++++++++++++++++++++++++++++++ src/interpreter/imports.rs | 1 - src/interpreter/mod.rs | 2 +- src/interpreter/module.rs | 61 +++++++++++++++++++++++--------------- 4 files changed, 92 insertions(+), 26 deletions(-) create mode 100644 benches/interpreter.rs diff --git a/benches/interpreter.rs b/benches/interpreter.rs new file mode 100644 index 0000000..e1711f5 --- /dev/null +++ b/benches/interpreter.rs @@ -0,0 +1,54 @@ +#![feature(test)] + +extern crate test; +extern crate parity_wasm; + +use test::Bencher; +use parity_wasm::builder::module; +use parity_wasm::elements::{ExportEntry, Internal, ImportEntry, External, Opcodes, Opcode}; +use parity_wasm::interpreter::{ProgramInstance, ModuleInstanceInterface, RuntimeValue}; + +#[bench] +fn export_entry_performance(b: &mut Bencher) { + // create module with 1000 functions + const NUM_FUNCTIONS: u32 = 1000; + let mut callee_module = module(); + for i in 0..NUM_FUNCTIONS { + callee_module = callee_module + .with_export(ExportEntry::new(format!("func{}", i), Internal::Function(i))) + .function() + .signature().return_type().i32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::I32Const(i as i32), + Opcode::End, + ])).build() + .build(); + } + let callee_module = callee_module.build(); + + // create module which call one of 1000 functions + let caller_module = module() + .with_import(ImportEntry::new("callee_module".into(), "func500".into(), External::Function(0))) + .function() + .signature().return_type().i32().build() + .body().with_opcodes(Opcodes::new(vec![ + Opcode::Call(0), + Opcode::I32Const(1000), + Opcode::I32Add, + Opcode::End, + ])).build() + .build() + .build(); + + // add both modules to program + let program = ProgramInstance::new().unwrap(); + program.add_module("callee_module", callee_module, None).unwrap(); + let caller_module = program.add_module("caller_module", caller_module, None).unwrap(); + + // run bench + b.iter(|| + assert_eq!(caller_module.execute_index(1, vec![].into()).unwrap(), Some(RuntimeValue::I32(1500))) + ); + + // test export_entry_performance ... bench: 3,497 ns/iter (+/- 200) +} diff --git a/src/interpreter/imports.rs b/src/interpreter/imports.rs index a2df35b..1f7912b 100644 --- a/src/interpreter/imports.rs +++ b/src/interpreter/imports.rs @@ -8,7 +8,6 @@ use interpreter::program::ProgramInstanceEssence; use interpreter::table::TableInstance; use interpreter::variable::{VariableInstance, VariableType}; -// TODO: cache Internal-s to fasten access /// Module imports. pub struct ModuleImports { /// Program instance. diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 3e0892f..1d59c06 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -74,7 +74,7 @@ mod variable; mod tests; pub use self::memory::MemoryInstance; -pub use self::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, ExportEntryType, CallerContext, ExecutionParams}; +pub use self::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, ExportEntryType, CallerContext, ExecutionParams, FunctionSignature}; pub use self::table::TableInstance; pub use self::program::ProgramInstance; pub use self::value::RuntimeValue; diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index 29b5613..12e34fa 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -98,6 +98,8 @@ pub struct ModuleInstance { functions_labels: HashMap>, /// Module imports. imports: ModuleImports, + /// Module exports. + exports: HashMap>, /// Tables. tables: Vec>, /// Linear memory regions. @@ -211,6 +213,7 @@ impl ModuleInstance { name: name, module: module, imports: imports, + exports: HashMap::new(), functions_labels: HashMap::new(), memory: memory, tables: tables, @@ -236,19 +239,27 @@ impl ModuleInstance { if let Some(export_section) = self.module.export_section() { for export in export_section.entries() { match export.internal() { - &Internal::Function(function_index) => - self.require_function(ItemIndex::IndexSpace(function_index)).map(|_| ())?, - &Internal::Global(global_index) => + &Internal::Function(function_index) => { + self.require_function(ItemIndex::IndexSpace(function_index)).map(|_| ())?; + self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Function(function_index)); + }, + &Internal::Global(global_index) => { self.global(ItemIndex::IndexSpace(global_index), None) .and_then(|g| if g.is_mutable() { Err(Error::Validation(format!("trying to export mutable global {}", export.field()))) } else { Ok(()) - })?, - &Internal::Memory(memory_index) => - self.memory(ItemIndex::IndexSpace(memory_index)).map(|_| ())?, - &Internal::Table(table_index) => - self.table(ItemIndex::IndexSpace(table_index)).map(|_| ())?, + })?; + self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Global(global_index)); + }, + &Internal::Memory(memory_index) => { + self.memory(ItemIndex::IndexSpace(memory_index)).map(|_| ())?; + self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Memory(memory_index)); + }, + &Internal::Table(table_index) => { + self.table(ItemIndex::IndexSpace(table_index)).map(|_| ())?; + self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Table(table_index)); + }, } } } @@ -422,15 +433,15 @@ impl ModuleInstanceInterface for ModuleInstance { } fn execute_export(&self, name: &str, params: ExecutionParams) -> Result, Error> { - let index = self.module.export_section() - .ok_or(Error::Function("missing export section".into())) - .and_then(|s| s.entries().iter() - .find(|e| e.field() == name && match e.internal() { - &Internal::Function(_) => true, + let index = self.exports.get(name) + .ok_or(Error::Function(format!("missing exports with name {}", name))) + .and_then(|l| l.iter() + .find(|i| match i { + &&Internal::Function(_) => true, _ => false, }) - .ok_or(Error::Function(format!("missing export section exported function with name {}", name))) - .map(|e| match e.internal() { + .ok_or(Error::Function(format!("missing exported function with name {}", name))) + .map(|i| match i { &Internal::Function(index) => index, _ => unreachable!(), // checked couple of lines above }) @@ -439,24 +450,24 @@ impl ModuleInstanceInterface for ModuleInstance { } fn export_entry<'a>(&self, name: &str, required_type: &ExportEntryType) -> Result { - self.module.export_section() - .ok_or(Error::Program(format!("trying to import {} from module without export section", name))) - .and_then(|s| s.entries().iter() - .find(|e| e.field() == name && match required_type { + self.exports.get(name) + .ok_or(Error::Function(format!("missing exports with name {}", name))) + .and_then(|l| l.iter() + .find(|i| match required_type { &ExportEntryType::Any => true, - &ExportEntryType::Global(global_type) => match e.internal() { - &Internal::Global(global_index) => self.global(ItemIndex::IndexSpace(global_index), Some(global_type)).map(|_| true).unwrap_or(false), + &ExportEntryType::Global(global_type) => match i { + &&Internal::Global(global_index) => self.global(ItemIndex::IndexSpace(global_index), Some(global_type)).map(|_| true).unwrap_or(false), _ => false, }, - &ExportEntryType::Function(ref required_type) => match e.internal() { - &Internal::Function(function_index) => + &ExportEntryType::Function(ref required_type) => match i { + &&Internal::Function(function_index) => self.function_type(ItemIndex::IndexSpace(function_index)) .map(|ft| ft == *required_type) .unwrap_or(false), _ => false, }, }) - .map(|e| *e.internal()) + .map(|i| *i) .ok_or(Error::Program(format!("unresolved import {}", name)))) } @@ -653,6 +664,7 @@ fn get_initializer(expr: &InitExpr, module: &Module, imports: &ModuleImports, ex } impl<'a> FunctionSignature<'a> { + /// Get return type of this function. pub fn return_type(&self) -> Option { match self { &FunctionSignature::Module(ft) => ft.return_type(), @@ -660,6 +672,7 @@ impl<'a> FunctionSignature<'a> { } } + /// Get parameters of this function. pub fn params(&self) -> &[ValueType] { match self { &FunctionSignature::Module(ft) => ft.params(), From d096e040525d6eb91e9931217117b069a3b2285d Mon Sep 17 00:00:00 2001 From: fro Date: Tue, 11 Jul 2017 19:12:01 +0300 Subject: [PATCH 08/21] assert global value persistence --- src/interpreter/tests/basics.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs index ee54797..f9bc24e 100644 --- a/src/interpreter/tests/basics.rs +++ b/src/interpreter/tests/basics.rs @@ -97,6 +97,7 @@ fn global_get_set() { let program = ProgramInstance::new().unwrap(); let module = program.add_module("main", module, None).unwrap(); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(50)); + assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(58)); } const SIGNATURE_I32_I32: &'static [ValueType] = &[ValueType::I32, ValueType::I32]; From e9e5972b3807eee31deaa0e024b884b3dc09e292 Mon Sep 17 00:00:00 2001 From: fro Date: Wed, 12 Jul 2017 19:22:23 +0300 Subject: [PATCH 09/21] example of interpret with multiple aguments and fn name --- examples/interpret_mul_args.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 examples/interpret_mul_args.rs diff --git a/examples/interpret_mul_args.rs b/examples/interpret_mul_args.rs new file mode 100644 index 0000000..3c2feee --- /dev/null +++ b/examples/interpret_mul_args.rs @@ -0,0 +1,30 @@ +extern crate parity_wasm; + +use std::env::args; + +use parity_wasm::{interpreter, ModuleInstanceInterface, RuntimeValue}; + +fn main() { + let args: Vec<_> = args().collect(); + if args.len() < 3 { + println!("Usage: {} [...]", args[0]); + return; + } + let func_name = &args[2]; + let (_, program_args) = args.split_at(3); + let program_args: Vec<_> = program_args.iter().enumerate() + .map(|(i, arg)| RuntimeValue::I32(arg.parse().expect(&format!("Invalid i32 arg at index {}", i)))) + .collect(); + + let program = parity_wasm::ProgramInstance::with_env_params( + interpreter::EnvParams { + total_stack: 128*1024, + total_memory: 2*1024*1024, + allow_memory_growth: false, + } + ).expect("Failed to load program"); + let module = parity_wasm::deserialize_file(&args[1]).expect("Failed to load module"); + let module = program.add_module("main", module, None).expect("Failed to initialize module"); + + println!("Result: {:?}", module.execute_export(func_name, program_args.into())); +} From 43a81e661a96614fde6719fb86b25ebfbb9ac8c7 Mon Sep 17 00:00:00 2001 From: fro Date: Thu, 13 Jul 2017 15:07:13 +0300 Subject: [PATCH 10/21] type section added in info example --- examples/info.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/info.rs b/examples/info.rs index c5e8cc9..796bf8c 100644 --- a/examples/info.rs +++ b/examples/info.rs @@ -25,6 +25,9 @@ fn main() { &Section::Function(ref function_section) => { println!(" Functions: {}", function_section.entries().len()); }, + &Section::Type(ref type_section) => { + println!(" Types: {}", type_section.types().len()); + }, &Section::Global(ref globals_section) => { println!(" Globals: {}", globals_section.entries().len()); }, From bf12f12af53cf79688df9f9df9acb7ea3f9a93f0 Mon Sep 17 00:00:00 2001 From: fro Date: Thu, 13 Jul 2017 20:25:05 +0300 Subject: [PATCH 11/21] rename interpret_mul_args -> invoke cli arguments parsing according to function type --- examples/interpret_mul_args.rs | 30 ---------------- examples/invoke.rs | 62 ++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 30 deletions(-) delete mode 100644 examples/interpret_mul_args.rs create mode 100644 examples/invoke.rs diff --git a/examples/interpret_mul_args.rs b/examples/interpret_mul_args.rs deleted file mode 100644 index 3c2feee..0000000 --- a/examples/interpret_mul_args.rs +++ /dev/null @@ -1,30 +0,0 @@ -extern crate parity_wasm; - -use std::env::args; - -use parity_wasm::{interpreter, ModuleInstanceInterface, RuntimeValue}; - -fn main() { - let args: Vec<_> = args().collect(); - if args.len() < 3 { - println!("Usage: {} [...]", args[0]); - return; - } - let func_name = &args[2]; - let (_, program_args) = args.split_at(3); - let program_args: Vec<_> = program_args.iter().enumerate() - .map(|(i, arg)| RuntimeValue::I32(arg.parse().expect(&format!("Invalid i32 arg at index {}", i)))) - .collect(); - - let program = parity_wasm::ProgramInstance::with_env_params( - interpreter::EnvParams { - total_stack: 128*1024, - total_memory: 2*1024*1024, - allow_memory_growth: false, - } - ).expect("Failed to load program"); - let module = parity_wasm::deserialize_file(&args[1]).expect("Failed to load module"); - let module = program.add_module("main", module, None).expect("Failed to initialize module"); - - println!("Result: {:?}", module.execute_export(func_name, program_args.into())); -} diff --git a/examples/invoke.rs b/examples/invoke.rs new file mode 100644 index 0000000..277f2c2 --- /dev/null +++ b/examples/invoke.rs @@ -0,0 +1,62 @@ +extern crate parity_wasm; + +use std::env::args; + +use parity_wasm::{interpreter, ModuleInstanceInterface, RuntimeValue}; +use parity_wasm::elements::ExportEntry; +use parity_wasm::elements::ExportSection; +use parity_wasm::elements::FunctionSection; +use parity_wasm::elements::TypeSection; +use parity_wasm::elements::Internal; +use parity_wasm::elements::Type; +use parity_wasm::elements::FunctionType; +use parity_wasm::elements::ValueType; + +fn main() { + let args: Vec<_> = args().collect(); + if args.len() < 3 { + println!("Usage: {} [...]", args[0]); + return; + } + let func_name = &args[2]; + let (_, program_args) = args.split_at(3); + + let program = parity_wasm::ProgramInstance::with_env_params( + interpreter::EnvParams { + total_stack: 128*1024, + total_memory: 2*1024*1024, + allow_memory_growth: false, + } + ).expect("Failed to load program"); + + let module_def = parity_wasm::deserialize_file(&args[1]).expect("Failed to load module"); + let execution_params = { + let export_section: &ExportSection = module_def.export_section().expect("No export section found"); + let function_section: &FunctionSection = module_def.function_section().expect("No function section found"); + let type_section: &TypeSection = module_def.type_section().expect("No type section found"); + let found_entry: &ExportEntry = export_section.entries().iter() + .find(|entry| func_name.eq(entry.field())).unwrap(); + let function_index = match found_entry.internal() { + &Internal::Function(index) => index, + _ => panic!("Founded export is not a function"), + }; + let func_type_ref: u32 = function_section.entries()[function_index as usize].type_ref(); + let type_: &Type = &type_section.types()[func_type_ref as usize]; + let function_type: &FunctionType = match type_ { + &Type::Function(ref func_type) => func_type, + }; + + let args: Vec = function_type.params().iter().enumerate().map(|(i, value)| match value { + &ValueType::I32 => RuntimeValue::I32(program_args[i].parse::().unwrap()), + &ValueType::I64 => RuntimeValue::I64(program_args[i].parse::().unwrap()), + &ValueType::F32 => RuntimeValue::F32(program_args[i].parse::().unwrap()), + &ValueType::F64 => RuntimeValue::F64(program_args[i].parse::().unwrap()), + }).collect(); + + interpreter::ExecutionParams::from(args) + }; + + let module_def = program.add_module("main", module_def, None).expect("Failed to initialize module"); + + println!("Result: {:?}", module_def.execute_export(func_name, execution_params)); +} From 1b8ecf11a58fa926c1c98934fde8a49b35214d0c Mon Sep 17 00:00:00 2001 From: fro Date: Mon, 17 Jul 2017 14:51:59 +0300 Subject: [PATCH 12/21] various fixes, code cleanup fix: exclude imports from function index --- examples/invoke.rs | 53 +++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/examples/invoke.rs b/examples/invoke.rs index 277f2c2..8e0b67c 100644 --- a/examples/invoke.rs +++ b/examples/invoke.rs @@ -3,14 +3,7 @@ extern crate parity_wasm; use std::env::args; use parity_wasm::{interpreter, ModuleInstanceInterface, RuntimeValue}; -use parity_wasm::elements::ExportEntry; -use parity_wasm::elements::ExportSection; -use parity_wasm::elements::FunctionSection; -use parity_wasm::elements::TypeSection; -use parity_wasm::elements::Internal; -use parity_wasm::elements::Type; -use parity_wasm::elements::FunctionType; -use parity_wasm::elements::ValueType; +use parity_wasm::elements::{Internal, Type, FunctionType, ValueType}; fn main() { let args: Vec<_> = args().collect(); @@ -27,36 +20,44 @@ fn main() { total_memory: 2*1024*1024, allow_memory_growth: false, } - ).expect("Failed to load program"); + ).expect("Program instance to load"); - let module_def = parity_wasm::deserialize_file(&args[1]).expect("Failed to load module"); + let module = parity_wasm::deserialize_file(&args[1]).expect("File to be deserialized"); let execution_params = { - let export_section: &ExportSection = module_def.export_section().expect("No export section found"); - let function_section: &FunctionSection = module_def.function_section().expect("No function section found"); - let type_section: &TypeSection = module_def.type_section().expect("No type section found"); - let found_entry: &ExportEntry = export_section.entries().iter() - .find(|entry| func_name.eq(entry.field())).unwrap(); - let function_index = match found_entry.internal() { - &Internal::Function(index) => index, + let export_section = module.export_section().expect("No export section found"); + let function_section = module.function_section().expect("No function section found"); + let type_section = module.type_section().expect("No type section found"); + + let found_entry= export_section.entries().iter() + .find(|entry| func_name == entry.field()).expect(&format!("No export with name {} found", func_name)); + + // Function index with imported functions + let function_index: usize = match found_entry.internal() { + &Internal::Function(index) => index as usize, _ => panic!("Founded export is not a function"), }; - let func_type_ref: u32 = function_section.entries()[function_index as usize].type_ref(); - let type_: &Type = &type_section.types()[func_type_ref as usize]; - let function_type: &FunctionType = match type_ { + let import_section_len: usize = match module.import_section() { + Some(import) => import.entries().len(), + None => 0, + }; + + let function_index_in_section = function_index - import_section_len; + let func_type_ref: usize = function_section.entries()[function_index_in_section].type_ref() as usize; + let function_type: &FunctionType = match &type_section.types()[func_type_ref] { &Type::Function(ref func_type) => func_type, }; let args: Vec = function_type.params().iter().enumerate().map(|(i, value)| match value { - &ValueType::I32 => RuntimeValue::I32(program_args[i].parse::().unwrap()), - &ValueType::I64 => RuntimeValue::I64(program_args[i].parse::().unwrap()), - &ValueType::F32 => RuntimeValue::F32(program_args[i].parse::().unwrap()), - &ValueType::F64 => RuntimeValue::F64(program_args[i].parse::().unwrap()), + &ValueType::I32 => RuntimeValue::I32(program_args[i].parse::().expect(&format!("Can't parse arg #{} as i32", program_args[i]))), + &ValueType::I64 => RuntimeValue::I64(program_args[i].parse::().expect(&format!("Can't parse arg #{} as i64", program_args[i]))), + &ValueType::F32 => RuntimeValue::F32(program_args[i].parse::().expect(&format!("Can't parse arg #{} as f32", program_args[i]))), + &ValueType::F64 => RuntimeValue::F64(program_args[i].parse::().expect(&format!("Can't parse arg #{} as f64", program_args[i]))), }).collect(); interpreter::ExecutionParams::from(args) }; - let module_def = program.add_module("main", module_def, None).expect("Failed to initialize module"); + let module = program.add_module("main", module, None).expect("Failed to initialize module"); - println!("Result: {:?}", module_def.execute_export(func_name, execution_params)); + println!("Result: {:?}", module.execute_export(func_name, execution_params).expect("")); } From bd0c0f70e77d38edfede2ec9c7e70f24b6c78514 Mon Sep 17 00:00:00 2001 From: fro Date: Mon, 17 Jul 2017 16:59:40 +0300 Subject: [PATCH 13/21] only function imports should be excluded from function_index --- examples/invoke.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/invoke.rs b/examples/invoke.rs index 8e0b67c..4972de6 100644 --- a/examples/invoke.rs +++ b/examples/invoke.rs @@ -3,7 +3,8 @@ extern crate parity_wasm; use std::env::args; use parity_wasm::{interpreter, ModuleInstanceInterface, RuntimeValue}; -use parity_wasm::elements::{Internal, Type, FunctionType, ValueType}; +use parity_wasm::elements::{Internal, External, Type, FunctionType, ValueType}; + fn main() { let args: Vec<_> = args().collect(); @@ -28,7 +29,7 @@ fn main() { let function_section = module.function_section().expect("No function section found"); let type_section = module.type_section().expect("No type section found"); - let found_entry= export_section.entries().iter() + let found_entry = export_section.entries().iter() .find(|entry| func_name == entry.field()).expect(&format!("No export with name {} found", func_name)); // Function index with imported functions @@ -37,9 +38,14 @@ fn main() { _ => panic!("Founded export is not a function"), }; let import_section_len: usize = match module.import_section() { - Some(import) => import.entries().len(), + Some(import) => + import.entries().iter().filter(|entry| match entry.external() { + &External::Function(_) => true, + _ => false, + }).collect::>().len(), None => 0, }; + println!("{:?}", import_section_len); let function_index_in_section = function_index - import_section_len; let func_type_ref: usize = function_section.entries()[function_index_in_section].type_ref() as usize; From ac36045bedaa7039c21dd491c15a3f89ea8f4041 Mon Sep 17 00:00:00 2001 From: fro Date: Mon, 17 Jul 2017 17:02:54 +0300 Subject: [PATCH 14/21] only function imports should be excluded from function index in order to get a correct index in the function section --- examples/invoke.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/invoke.rs b/examples/invoke.rs index 4972de6..74d09c5 100644 --- a/examples/invoke.rs +++ b/examples/invoke.rs @@ -45,7 +45,6 @@ fn main() { }).collect::>().len(), None => 0, }; - println!("{:?}", import_section_len); let function_index_in_section = function_index - import_section_len; let func_type_ref: usize = function_section.entries()[function_index_in_section].type_ref() as usize; From 0b48ed50ad2bf34190a4c121477feb676892bd1c Mon Sep 17 00:00:00 2001 From: fro Date: Mon, 17 Jul 2017 20:16:46 +0300 Subject: [PATCH 15/21] a little fix --- examples/invoke.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/invoke.rs b/examples/invoke.rs index 74d09c5..22a47e1 100644 --- a/examples/invoke.rs +++ b/examples/invoke.rs @@ -42,7 +42,7 @@ fn main() { import.entries().iter().filter(|entry| match entry.external() { &External::Function(_) => true, _ => false, - }).collect::>().len(), + }).count(), None => 0, }; From f9832b046bf529c02794323ab2bc2ef5cff7fde2 Mon Sep 17 00:00:00 2001 From: fro Date: Thu, 20 Jul 2017 18:31:15 +0300 Subject: [PATCH 16/21] additional module info --- examples/info.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/info.rs b/examples/info.rs index 796bf8c..58c5927 100644 --- a/examples/info.rs +++ b/examples/info.rs @@ -15,23 +15,25 @@ fn main() { println!("Module sections: {}", module.sections().len()); for section in module.sections() { - match section { - &Section::Import(ref import_section) => { + match *section { + Section::Import(ref import_section) => { println!(" Imports: {}", import_section.entries().len()); + import_section.entries().iter().map(|e| println!(" {}.{}", e.module(), e.field())).count(); }, - &Section::Export(ref exports_section) => { + Section::Export(ref exports_section) => { println!(" Exports: {}", exports_section.entries().len()); + exports_section.entries().iter().map(|e| println!(" {}", e.field())).count(); }, - &Section::Function(ref function_section) => { + Section::Function(ref function_section) => { println!(" Functions: {}", function_section.entries().len()); }, - &Section::Type(ref type_section) => { + Section::Type(ref type_section) => { println!(" Types: {}", type_section.types().len()); }, - &Section::Global(ref globals_section) => { + Section::Global(ref globals_section) => { println!(" Globals: {}", globals_section.entries().len()); }, - &Section::Data(ref data_section) if data_section.entries().len() > 0 => { + Section::Data(ref data_section) if data_section.entries().len() > 0 => { let data = &data_section.entries()[0]; println!(" Data size: {}", data.value().len()); }, From 72ad104b94b257761055b84e729fc4e0e9d8a60b Mon Sep 17 00:00:00 2001 From: fro Date: Fri, 21 Jul 2017 13:44:18 +0300 Subject: [PATCH 17/21] Test i64 and u64 types serealization/deserialization --- src/elements/primitives.rs | 66 +++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/elements/primitives.rs b/src/elements/primitives.rs index 305f597..0f96a2c 100644 --- a/src/elements/primitives.rs +++ b/src/elements/primitives.rs @@ -559,7 +559,7 @@ impl, T: IntoIterator> Serialize f mod tests { use super::super::{deserialize_buffer, Serialize}; - use super::{CountedList, VarInt7, VarUint32, VarInt32}; + use super::{CountedList, VarInt7, VarUint32, VarInt32, VarInt64, VarUint64}; fn varuint32_ser_test(val: u32, expected: Vec) { let mut buf = Vec::new(); @@ -594,6 +594,40 @@ mod tests { varint32_de_test(dt.clone(), val); varint32_ser_test(val, dt); } + + fn varuint64_ser_test(val: u64, expected: Vec) { + let mut buf = Vec::new(); + let v1: VarUint64 = val.into(); + v1.serialize(&mut buf).expect("to be serialized ok"); + assert_eq!(expected, buf); + } + + fn varuint64_de_test(dt: Vec, expected: u64) { + let val: VarUint64 = super::super::deserialize_buffer(dt).expect("buf to be serialized"); + assert_eq!(expected, val.into()); + } + + fn varuint64_serde_test(dt: Vec, val: u64) { + varuint64_de_test(dt.clone(), val); + varuint64_ser_test(val, dt); + } + + fn varint64_ser_test(val: i64, expected: Vec) { + let mut buf = Vec::new(); + let v1: VarInt64 = val.into(); + v1.serialize(&mut buf).expect("to be serialized ok"); + assert_eq!(expected, buf); + } + + fn varint64_de_test(dt: Vec, expected: i64) { + let val: VarInt64 = super::super::deserialize_buffer(dt).expect("buf to be serialized"); + assert_eq!(expected, val.into()); + } + + fn varint64_serde_test(dt: Vec, val: i64) { + varint64_de_test(dt.clone(), val); + varint64_ser_test(val, dt); + } #[test] fn varuint32_0() { @@ -625,6 +659,36 @@ mod tests { varint32_serde_test(vec![0x80, 0x40], -8192); } + #[test] + fn varuint64_0() { + varuint64_serde_test(vec![0u8; 1], 0); + } + + #[test] + fn varuint64_1() { + varuint64_serde_test(vec![1u8; 1], 1); + } + + #[test] + fn varuint64_135() { + varuint64_serde_test(vec![135u8, 0x01], 135); + } + + #[test] + fn varuint64_8192() { + varuint64_serde_test(vec![0x80, 0x40], 8192); + } + + #[test] + fn varint64_8192() { + varint64_serde_test(vec![0x80, 0xc0, 0x00], 8192); + } + + #[test] + fn varint64_neg_8192() { + varint64_serde_test(vec![0x80, 0x40], -8192); + } + #[test] fn counted_list() { let payload = vec![ From 35601d02b99077c7cd6ee8125a25d663de4f64f3 Mon Sep 17 00:00:00 2001 From: fro Date: Sat, 22 Jul 2017 18:35:19 +0300 Subject: [PATCH 18/21] fix indentation --- src/elements/primitives.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/elements/primitives.rs b/src/elements/primitives.rs index 0f96a2c..57f638b 100644 --- a/src/elements/primitives.rs +++ b/src/elements/primitives.rs @@ -659,7 +659,7 @@ mod tests { varint32_serde_test(vec![0x80, 0x40], -8192); } - #[test] + #[test] fn varuint64_0() { varuint64_serde_test(vec![0u8; 1], 0); } From a5da7d604ba4ad8bcbb8dba2f85018c50c2e1e68 Mon Sep 17 00:00:00 2001 From: fro Date: Tue, 25 Jul 2017 14:31:17 +0300 Subject: [PATCH 19/21] global import breaks with Function("missing exports with name STACKTOP") --- src/interpreter/tests/basics.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs index f9bc24e..326b7b7 100644 --- a/src/interpreter/tests/basics.rs +++ b/src/interpreter/tests/basics.rs @@ -210,6 +210,39 @@ fn single_program_different_modules() { assert_eq!(executor.values, vec![7, 57, 42]); } +#[test] +fn import_global() { + // create new program + let program = ProgramInstance::new().unwrap(); + // => env module is created + let env_instance = program.module("env").unwrap(); + // => linear memory is created + let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap(); + + // create native env module executor + let mut executor = FunctionExecutor { + memory: env_memory.clone(), + values: Vec::new(), + }; + { + let functions: UserFunctions = UserFunctions { + executor: &mut executor, + 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); + + let module = module() + .with_import(ImportEntry::new("env".into(), "STACKTOP".into(), External::Global(GlobalType::new(ValueType::I32, false)))) + .build(); + + // load module + program.add_module("main", module, Some(¶ms.externals)).unwrap(); + } + +} + + #[test] fn env_native_export_entry_type_check() { let program = ProgramInstance::new().unwrap(); From 2f73f0283116bc93bb166cfeddbeefcb0a262408 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 25 Jul 2017 15:26:31 +0300 Subject: [PATCH 20/21] instantiate env module --- src/interpreter/env.rs | 5 ++-- src/interpreter/module.rs | 50 ++++++++++++++++----------------- src/interpreter/tests/basics.rs | 32 ++++----------------- 3 files changed, 32 insertions(+), 55 deletions(-) diff --git a/src/interpreter/env.rs b/src/interpreter/env.rs index 651fc6c..e7f54d5 100644 --- a/src/interpreter/env.rs +++ b/src/interpreter/env.rs @@ -81,7 +81,8 @@ pub struct EnvModuleInstance { impl EnvModuleInstance { pub fn new(params: EnvParams, module: Module) -> Result { - let instance = ModuleInstance::new(Weak::default(), "env".into(), module)?; + let mut instance = ModuleInstance::new(Weak::default(), "env".into(), module)?; + instance.instantiate(false, None)?; Ok(EnvModuleInstance { _params: params, @@ -186,7 +187,7 @@ pub fn env_module(params: EnvParams) -> Result { .with_export(ExportEntry::new("DYNAMIC_BASE".into(), Internal::Global(INDEX_GLOBAL_DYNAMIC_BASE))) .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack) as i32)]))) .with_export(ExportEntry::new("DYNAMICTOP_PTR".into(), Internal::Global(INDEX_GLOBAL_DYNAMICTOP_PTR))) - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, params.allow_memory_growth), InitExpr::new(vec![Opcode::I32Const(params.total_memory as i32)]))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, params.allow_memory_growth), InitExpr::new(vec![Opcode::I32Const(params.total_memory as i32)]))) .with_export(ExportEntry::new("TOTAL_MEMORY".into(), Internal::Global(INDEX_GLOBAL_TOTAL_MEMORY))) .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(0)]))) .with_export(ExportEntry::new("ABORT".into(), Internal::Global(INDEX_GLOBAL_ABORT))) diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index 12e34fa..f490710 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -235,32 +235,30 @@ impl ModuleInstance { } // validate export section - if is_user_module { // TODO: env module exports STACKTOP global, which is mutable => check is failed - if let Some(export_section) = self.module.export_section() { - for export in export_section.entries() { - match export.internal() { - &Internal::Function(function_index) => { - self.require_function(ItemIndex::IndexSpace(function_index)).map(|_| ())?; - self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Function(function_index)); - }, - &Internal::Global(global_index) => { - self.global(ItemIndex::IndexSpace(global_index), None) - .and_then(|g| if g.is_mutable() { - Err(Error::Validation(format!("trying to export mutable global {}", export.field()))) - } else { - Ok(()) - })?; - self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Global(global_index)); - }, - &Internal::Memory(memory_index) => { - self.memory(ItemIndex::IndexSpace(memory_index)).map(|_| ())?; - self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Memory(memory_index)); - }, - &Internal::Table(table_index) => { - self.table(ItemIndex::IndexSpace(table_index)).map(|_| ())?; - self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Table(table_index)); - }, - } + if let Some(export_section) = self.module.export_section() { + for export in export_section.entries() { + match export.internal() { + &Internal::Function(function_index) => { + self.require_function(ItemIndex::IndexSpace(function_index)).map(|_| ())?; + self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Function(function_index)); + }, + &Internal::Global(global_index) => { + self.global(ItemIndex::IndexSpace(global_index), None) + .and_then(|g| if g.is_mutable() && is_user_module { + Err(Error::Validation(format!("trying to export mutable global {}", export.field()))) + } else { + Ok(()) + })?; + self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Global(global_index)); + }, + &Internal::Memory(memory_index) => { + self.memory(ItemIndex::IndexSpace(memory_index)).map(|_| ())?; + self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Memory(memory_index)); + }, + &Internal::Table(table_index) => { + self.table(ItemIndex::IndexSpace(table_index)).map(|_| ())?; + self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Table(table_index)); + }, } } } diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs index 326b7b7..87b0fb2 100644 --- a/src/interpreter/tests/basics.rs +++ b/src/interpreter/tests/basics.rs @@ -211,38 +211,16 @@ fn single_program_different_modules() { } #[test] -fn import_global() { - // create new program +fn import_env_mutable_global() { let program = ProgramInstance::new().unwrap(); - // => env module is created - let env_instance = program.module("env").unwrap(); - // => linear memory is created - let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap(); - // create native env module executor - let mut executor = FunctionExecutor { - memory: env_memory.clone(), - values: Vec::new(), - }; - { - let functions: UserFunctions = UserFunctions { - executor: &mut executor, - 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); - - let module = module() - .with_import(ImportEntry::new("env".into(), "STACKTOP".into(), External::Global(GlobalType::new(ValueType::I32, false)))) - .build(); - - // load module - program.add_module("main", module, Some(¶ms.externals)).unwrap(); - } + let module = module() + .with_import(ImportEntry::new("env".into(), "STACKTOP".into(), External::Global(GlobalType::new(ValueType::I32, false)))) + .build(); + program.add_module("main", module, None).unwrap(); } - #[test] fn env_native_export_entry_type_check() { let program = ProgramInstance::new().unwrap(); From c23e5dae5442e9470a3c026179eef54dacfd2147 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 25 Jul 2017 15:42:41 +0300 Subject: [PATCH 21/21] bump version --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2f53b26..3cd355f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parity-wasm" -version = "0.12.1" +version = "0.12.2" authors = ["Nikolay Volf ", "Svyatoslav Nikolsky "] license = "MIT/Apache-2.0" readme = "README.md" @@ -8,7 +8,7 @@ repository = "https://github.com/nikvolf/parity-wasm" homepage = "https://github.com/nikvolf/parity-wasm" documentation = "https://nikvolf.github.io/parity-wasm/parity_wasm/" description = "WebAssembly binary format serialization/deserialization/interpreter" -keywords = ["wasm", "webassembly", "bytecode", "serde", "interpreter"] +keywords = ["wasm", "webassembly", "bytecode", "serde", "interpreter"] exclude = [ "res/*", "spec/*" ] [dependencies]