From 06277915dace59fa8d3202f5eb3db089e4dd1889 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Mon, 21 Jan 2019 17:04:31 +0300 Subject: [PATCH 01/35] some graph structure definition --- src/graph.rs | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + tests/diff.rs | 2 ++ 3 files changed, 100 insertions(+) create mode 100644 src/graph.rs diff --git a/src/graph.rs b/src/graph.rs new file mode 100644 index 0000000..e5079b1 --- /dev/null +++ b/src/graph.rs @@ -0,0 +1,97 @@ +//! Wasm binary graph format + +use parity_wasm::elements; +use std::cell::RefCell; +use std::rc::Rc; + +enum ImportedOrDeclared { + Imported(String, String), + Declared(T), +} + +type FuncOrigin = ImportedOrDeclared>; +type GlobalOrigin = ImportedOrDeclared>; +type MemoryOrigin = ImportedOrDeclared; +type TableOrigin = ImportedOrDeclared; + +type TypeRef = Rc>; +type FuncRef = Rc>; +type GlobalRef = Rc>; + +struct Func { + type_ref: TypeRef, + origin: FuncOrigin, +} + +struct Global { + content: elements::ValueType, + is_mut: bool, + origin: GlobalOrigin, +} + +enum Instruction { + Plain(elements::Instruction), + Call(FuncRef), +} + +struct Memory { + limits: elements::ResizableLimits, + origin: MemoryOrigin, +} + +struct Table { + origin: TableOrigin, +} + +struct DataSegment { + offset_expr: Vec, + data: Vec, +} + +struct ElementSegment { + offset_expr: Vec, + data: Vec, +} + +enum Export { + Func(FuncRef), + Global(GlobalRef), +} + +#[derive(Default)] +struct Module { + types: Vec, + funcs: Vec, + tables: Vec, + memories: Vec, + globals: Vec, + elements: Vec, + data: Vec, + exports: Vec, +} + +impl Module { + + + fn from_elements(module: &elements::Module) -> Self { + + let mut res = Module::default(); + + for section in module.sections() { + match section { + elements::Section::Type(type_section) => { + res.types = type_section.types().iter().cloned().map(|t| Rc::new(RefCell::new(t))).collect(); + }, + _ => continue, + } + } + + res + } + +} + +#[cfg(test)] +mod tests { + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 3d1c546..38f3710 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ mod symbols; mod ext; mod pack; mod runtime_type; +mod graph; pub mod stack_height; diff --git a/tests/diff.rs b/tests/diff.rs index 4200bb6..3c889d7 100644 --- a/tests/diff.rs +++ b/tests/diff.rs @@ -30,6 +30,7 @@ fn validate_wasm(binary: &[u8]) -> Result<(), wabt::Error> { } fn run_diff_test Vec>(test_dir: &str, name: &str, test: F) { + // FIXME: not going to work on windows? let mut fixture_path = PathBuf::from(concat!( env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/", @@ -37,6 +38,7 @@ fn run_diff_test Vec>(test_dir: &str, name: &str, test: fixture_path.push(test_dir); fixture_path.push(name); + // FIXME: not going to work on windows? let mut expected_path = PathBuf::from(concat!( env!("CARGO_MANIFEST_DIR"), "/tests/expectations/" From 80d80a37d9d1344f0146cfe08eec53107ae84b9e Mon Sep 17 00:00:00 2001 From: NikVolf Date: Mon, 21 Jan 2019 17:56:30 +0300 Subject: [PATCH 02/35] import rewiring --- src/graph.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index e5079b1..4ba046a 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -9,6 +9,18 @@ enum ImportedOrDeclared { Declared(T), } +impl ImportedOrDeclared { + fn imported(module: String, name: String) -> Self { + ImportedOrDeclared::Imported(module, name) + } +} + +impl From<&elements::ImportEntry> for ImportedOrDeclared { + fn from(v: &elements::ImportEntry) -> Self { + ImportedOrDeclared::Imported(v.module().to_owned(), v.field().to_owned()) + } +} + type FuncOrigin = ImportedOrDeclared>; type GlobalOrigin = ImportedOrDeclared>; type MemoryOrigin = ImportedOrDeclared; @@ -17,18 +29,32 @@ type TableOrigin = ImportedOrDeclared; type TypeRef = Rc>; type FuncRef = Rc>; type GlobalRef = Rc>; +type MemoryRef = Rc>; +type TableRef = Rc>; struct Func { type_ref: TypeRef, origin: FuncOrigin, } +impl Func { + fn into_ref(self) -> Rc> { + Rc::from(RefCell::from(self)) + } +} + struct Global { content: elements::ValueType, is_mut: bool, origin: GlobalOrigin, } +impl Global { + fn into_ref(self) -> Rc> { + Rc::from(RefCell::from(self)) + } +} + enum Instruction { Plain(elements::Instruction), Call(FuncRef), @@ -39,8 +65,21 @@ struct Memory { origin: MemoryOrigin, } +impl Memory { + fn into_ref(self) -> Rc> { + Rc::from(RefCell::from(self)) + } +} + struct Table { origin: TableOrigin, + limits: elements::ResizableLimits, +} + +impl Table { + fn into_ref(self) -> Rc> { + Rc::from(RefCell::from(self)) + } } struct DataSegment { @@ -56,14 +95,16 @@ struct ElementSegment { enum Export { Func(FuncRef), Global(GlobalRef), + Table(TableRef), + Memory(MemoryRef), } #[derive(Default)] struct Module { types: Vec, funcs: Vec, - tables: Vec
, - memories: Vec, + tables: Vec, + memory: Vec, globals: Vec, elements: Vec, data: Vec, @@ -72,7 +113,6 @@ struct Module { impl Module { - fn from_elements(module: &elements::Module) -> Self { let mut res = Module::default(); @@ -80,7 +120,44 @@ impl Module { for section in module.sections() { match section { elements::Section::Type(type_section) => { - res.types = type_section.types().iter().cloned().map(|t| Rc::new(RefCell::new(t))).collect(); + res.types = type_section + .types() + .iter() + .cloned() + .map(RefCell::<_>::from) + .map(Rc::<_>::from) + .collect(); + }, + elements::Section::Import(import_section) => { + for entry in import_section.entries() { + match *entry.external() { + elements::External::Function(f) => { + res.funcs.push(Func { + type_ref: res.types[f as usize].clone(), + origin: entry.into(), + }.into_ref()) + }, + elements::External::Memory(m) => { + res.memory.push(Memory { + limits: m.limits().clone(), + origin: entry.into(), + }.into_ref()) + }, + elements::External::Global(g) => { + res.globals.push(Global { + content: g.content_type(), + is_mut: g.is_mutable(), + origin: entry.into(), + }.into_ref()) + }, + elements::External::Table(t) => { + res.tables.push(Table { + limits: t.limits().clone(), + origin: entry.into(), + }.into_ref()) + }, + } + } }, _ => continue, } From db4070b96cde45f30340c02a54651d2987e9c82e Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 12:08:25 +0300 Subject: [PATCH 03/35] ref list impl --- src/lib.rs | 1 + src/ref_list.rs | 181 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 src/ref_list.rs diff --git a/src/lib.rs b/src/lib.rs index 38f3710..d41c4df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ mod ext; mod pack; mod runtime_type; mod graph; +mod ref_list; pub mod stack_height; diff --git a/src/ref_list.rs b/src/ref_list.rs new file mode 100644 index 0000000..b9a114c --- /dev/null +++ b/src/ref_list.rs @@ -0,0 +1,181 @@ + +use std::rc::Rc; +use std::cell::RefCell; + +#[derive(Debug)] +enum EntryOrigin { + Index(usize), + Detached, +} + +impl From for EntryOrigin { + fn from(v: usize) -> Self { + EntryOrigin::Index(v) + } +} + +#[derive(Debug)] +pub struct Entry { + val: T, + index: EntryOrigin, +} + +impl Entry { + fn new(val: T, index: usize) -> Entry { + Entry { + val: val, + index: EntryOrigin::Index(index), + } + } + + pub fn order(&self) -> Option { + match self.index { + EntryOrigin::Detached => None, + EntryOrigin::Index(idx) => Some(idx), + } + } +} + +impl ::std::ops::Deref for Entry { + type Target = T; + + fn deref(&self) -> &T { + &self.val + } +} + +struct EntryRef(Rc>>); + +impl Clone for EntryRef { + fn clone(&self) -> Self { + EntryRef(self.0.clone()) + } +} + +impl From> for EntryRef { + fn from(v: Entry) -> Self { + EntryRef(Rc::new(RefCell::new(v))) + } +} + +impl EntryRef { + fn read(&self) -> ::std::cell::Ref> { + self.0.borrow() + } + + fn write(&self) -> ::std::cell::RefMut> { + self.0.borrow_mut() + } + + fn order(&self) -> Option { + self.0.borrow().order() + } +} + +struct RefList { + items: Vec>, +} + +impl RefList { + + pub fn new() -> Self { RefList { items: Default::default() } } + + fn push(&mut self, t: T) -> EntryRef { + let idx = self.items.len(); + let val: EntryRef<_> = Entry::new(t, idx).into(); + self.items.push(val.clone()); + val + } + + pub fn begin_delete(&mut self) -> DeleteTransaction { + DeleteTransaction { + list: self, + deleted: Vec::new(), + } + } + + fn done_delete(&mut self, indices: &[usize]) { + + let mut index = 0; + + for idx in indices { + let mut detached = self.items.remove(*idx); + detached.write().index = EntryOrigin::Detached; + } + + for index in 0..self.items.len() { + let mut next_entry = self.items.get_mut(index).expect("Checked above; qed").write(); + let total_less = indices.iter() + .take_while(|x| **x < next_entry.order().expect("Items in the list always have order; qed")) + .count(); + match next_entry.index { + EntryOrigin::Detached => unreachable!("Items in the list always have order!"), + EntryOrigin::Index(ref mut idx) => { *idx -= total_less; }, + }; + } + } + + pub fn delete(&mut self, inddices: &[usize]) { + self.done_delete(inddices) + } + + pub fn delete_one(&mut self, index: usize) { + self.done_delete(&[index]) + } +} + +#[must_use] +pub struct DeleteTransaction<'a, T> { + list: &'a mut RefList, + deleted: Vec, +} + +impl<'a, T> DeleteTransaction<'a, T> { + pub fn push(mut self, idx: usize) -> Self { + let mut tx = self; + tx.deleted.push(idx); + tx + } + + pub fn done(mut self) { + let indices = self.deleted; + let list = self.list; + list.done_delete(&indices[..]); + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn order() { + let mut list = RefList::::new(); + let item10 = list.push(10); + let item20 = list.push(20); + let item30 = list.push(30); + + assert_eq!(item10.order(), Some(0usize)); + assert_eq!(item20.order(), Some(1)); + assert_eq!(item30.order(), Some(2)); + + assert_eq!(**item10.read(), 10); + assert_eq!(**item20.read(), 20); + assert_eq!(**item30.read(), 30); + } + + #[test] + fn delete() { + let mut list = RefList::::new(); + let item10 = list.push(10); + let item20 = list.push(20); + let item30 = list.push(30); + + list.begin_delete().push(1).done(); + + assert_eq!(item10.order(), Some(0)); + assert_eq!(item30.order(), Some(1)); + assert_eq!(item20.order(), None); + } +} \ No newline at end of file From 33ff0cbe1dd91cc834ef0e1cee5cca430ae8cb58 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 12:19:29 +0300 Subject: [PATCH 04/35] refactor to reflist --- src/graph.rs | 75 +++++++++++++------------------------------------ src/ref_list.rs | 34 ++++++++++++++++++---- 2 files changed, 47 insertions(+), 62 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 4ba046a..cab7e4c 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -1,8 +1,7 @@ //! Wasm binary graph format use parity_wasm::elements; -use std::cell::RefCell; -use std::rc::Rc; +use super::ref_list::{RefList, EntryRef}; enum ImportedOrDeclared { Imported(String, String), @@ -26,38 +25,20 @@ type GlobalOrigin = ImportedOrDeclared>; type MemoryOrigin = ImportedOrDeclared; type TableOrigin = ImportedOrDeclared; -type TypeRef = Rc>; -type FuncRef = Rc>; -type GlobalRef = Rc>; -type MemoryRef = Rc>; -type TableRef = Rc>; - struct Func { - type_ref: TypeRef, + type_ref: EntryRef, origin: FuncOrigin, } -impl Func { - fn into_ref(self) -> Rc> { - Rc::from(RefCell::from(self)) - } -} - struct Global { content: elements::ValueType, is_mut: bool, origin: GlobalOrigin, } -impl Global { - fn into_ref(self) -> Rc> { - Rc::from(RefCell::from(self)) - } -} - enum Instruction { Plain(elements::Instruction), - Call(FuncRef), + Call(EntryRef), } struct Memory { @@ -65,23 +46,11 @@ struct Memory { origin: MemoryOrigin, } -impl Memory { - fn into_ref(self) -> Rc> { - Rc::from(RefCell::from(self)) - } -} - struct Table { origin: TableOrigin, limits: elements::ResizableLimits, } -impl Table { - fn into_ref(self) -> Rc> { - Rc::from(RefCell::from(self)) - } -} - struct DataSegment { offset_expr: Vec, data: Vec, @@ -93,19 +62,19 @@ struct ElementSegment { } enum Export { - Func(FuncRef), - Global(GlobalRef), - Table(TableRef), - Memory(MemoryRef), + Func(EntryRef), + Global(EntryRef), + Table(EntryRef
), + Memory(EntryRef), } #[derive(Default)] struct Module { - types: Vec, - funcs: Vec, - tables: Vec, - memory: Vec, - globals: Vec, + types: RefList, + funcs: RefList, + tables: RefList
, + memory: RefList, + globals: RefList, elements: Vec, data: Vec, exports: Vec, @@ -120,43 +89,37 @@ impl Module { for section in module.sections() { match section { elements::Section::Type(type_section) => { - res.types = type_section - .types() - .iter() - .cloned() - .map(RefCell::<_>::from) - .map(Rc::<_>::from) - .collect(); + res.types = RefList::from_slice(type_section.types()); }, elements::Section::Import(import_section) => { for entry in import_section.entries() { match *entry.external() { elements::External::Function(f) => { res.funcs.push(Func { - type_ref: res.types[f as usize].clone(), + type_ref: res.types.get(f as usize).expect("validated; qed").clone(), origin: entry.into(), - }.into_ref()) + }); }, elements::External::Memory(m) => { res.memory.push(Memory { limits: m.limits().clone(), origin: entry.into(), - }.into_ref()) + }); }, elements::External::Global(g) => { res.globals.push(Global { content: g.content_type(), is_mut: g.is_mutable(), origin: entry.into(), - }.into_ref()) + }); }, elements::External::Table(t) => { res.tables.push(Table { limits: t.limits().clone(), origin: entry.into(), - }.into_ref()) + }); }, - } + }; } }, _ => continue, diff --git a/src/ref_list.rs b/src/ref_list.rs index b9a114c..c349393 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -44,7 +44,7 @@ impl ::std::ops::Deref for Entry { } } -struct EntryRef(Rc>>); +pub struct EntryRef(Rc>>); impl Clone for EntryRef { fn clone(&self) -> Self { @@ -72,15 +72,21 @@ impl EntryRef { } } -struct RefList { +pub struct RefList { items: Vec>, } +impl Default for RefList { + fn default() -> Self { + RefList { items: Default::default() } + } +} + impl RefList { - pub fn new() -> Self { RefList { items: Default::default() } } + pub fn new() -> Self { Self::default() } - fn push(&mut self, t: T) -> EntryRef { + pub fn push(&mut self, t: T) -> EntryRef { let idx = self.items.len(); let val: EntryRef<_> = Entry::new(t, idx).into(); self.items.push(val.clone()); @@ -94,6 +100,10 @@ impl RefList { } } + pub fn get(&self, idx: usize) -> Option> { + self.items.get(idx).cloned() + } + fn done_delete(&mut self, indices: &[usize]) { let mut index = 0; @@ -115,13 +125,25 @@ impl RefList { } } - pub fn delete(&mut self, inddices: &[usize]) { - self.done_delete(inddices) + pub fn delete(&mut self, indices: &[usize]) { + self.done_delete(indices) } pub fn delete_one(&mut self, index: usize) { self.done_delete(&[index]) } + + pub fn from_slice(list: &[T]) -> Self + where T: Clone + { + let mut res = Self::new(); + + for t in list { + res.push(t.clone()); + } + + res + } } #[must_use] From ba45e15567a1c9ad044937f25c3a8bd75a06026a Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 12:39:09 +0300 Subject: [PATCH 05/35] remove unused --- src/graph.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index cab7e4c..4a78ae6 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -8,12 +8,6 @@ enum ImportedOrDeclared { Declared(T), } -impl ImportedOrDeclared { - fn imported(module: String, name: String) -> Self { - ImportedOrDeclared::Imported(module, name) - } -} - impl From<&elements::ImportEntry> for ImportedOrDeclared { fn from(v: &elements::ImportEntry) -> Self { ImportedOrDeclared::Imported(v.module().to_owned(), v.field().to_owned()) From be40285a6743a8b84c6ca1eedd798ef24119217b Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 12:58:29 +0300 Subject: [PATCH 06/35] func and tests --- src/graph.rs | 30 ++++++++++++++++++++++++++++++ src/ref_list.rs | 4 ++++ 2 files changed, 34 insertions(+) diff --git a/src/graph.rs b/src/graph.rs index 4a78ae6..3dd74a9 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -116,6 +116,15 @@ impl Module { }; } }, + elements::Section::Function(function_section) => { + for f in function_section.entries() { + res.funcs.push(Func { + type_ref: res.types.get(f.type_ref() as usize).expect("validated; qed").clone(), + // code will be populated later + origin: ImportedOrDeclared::Declared(Vec::new()), + }); + }; + }, _ => continue, } } @@ -125,7 +134,28 @@ impl Module { } +fn parse(wasm: &[u8]) -> Module { + Module::from_elements(&::parity_wasm::deserialize_buffer(wasm).expect("failed to parse wasm")) +} + #[cfg(test)] mod tests { + extern crate wabt; + use parity_wasm; + + #[test] + fn smoky() { + let wasm = wabt::wat2wasm(r#" + (module + (type (func)) + (func (type 0)) + ) + "#).expect("Failed to read fixture"); + + let f = super::parse(&wasm[..]); + + assert_eq!(f.types.len(), 1); + assert_eq!(f.funcs.len(), 1); + } } \ No newline at end of file diff --git a/src/ref_list.rs b/src/ref_list.rs index c349393..87497e0 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -144,6 +144,10 @@ impl RefList { res } + + pub fn len(&self) -> usize { + self.items.len() + } } #[must_use] From dd9169e30f2e0901e6461e4f34d3262a3133d4a9 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 13:03:11 +0300 Subject: [PATCH 07/35] table and memory --- src/graph.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/graph.rs b/src/graph.rs index 3dd74a9..6fe278e 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -125,6 +125,22 @@ impl Module { }); }; }, + elements::Section::Table(table_section) => { + for t in table_section.entries() { + res.tables.push(Table { + limits: t.limits().clone(), + origin: ImportedOrDeclared::Declared(()), + }); + } + }, + elements::Section::Memory(table_section) => { + for t in table_section.entries() { + res.memory.push(Memory { + limits: t.limits().clone(), + origin: ImportedOrDeclared::Declared(()), + }); + } + }, _ => continue, } } @@ -150,6 +166,7 @@ mod tests { (module (type (func)) (func (type 0)) + (memory 0 1) ) "#).expect("Failed to read fixture"); @@ -157,5 +174,7 @@ mod tests { assert_eq!(f.types.len(), 1); assert_eq!(f.funcs.len(), 1); + assert_eq!(f.tables.len(), 0); + assert_eq!(f.memory.len(), 1); } } \ No newline at end of file From cf10b7d5d91f061be389e3c818c323e4b27bfd5a Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 14:31:21 +0300 Subject: [PATCH 08/35] exports and fix for no-std --- src/graph.rs | 41 +++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 4 ++++ src/ref_list.rs | 19 ++++++++++++++++--- 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 6fe278e..51fb4ff 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -2,6 +2,9 @@ use parity_wasm::elements; use super::ref_list::{RefList, EntryRef}; +use std::vec::Vec; +use std::borrow::ToOwned; +use std::string::String; enum ImportedOrDeclared { Imported(String, String), @@ -66,12 +69,13 @@ enum Export { struct Module { types: RefList, funcs: RefList, - tables: RefList
, memory: RefList, + tables: RefList
, globals: RefList, + start: Option>, + exports: Vec, elements: Vec, data: Vec, - exports: Vec, } impl Module { @@ -141,6 +145,34 @@ impl Module { }); } }, + elements::Section::Global(global_section) => { + for g in global_section.entries() { + res.globals.push(Global { + content: g.global_type().content_type(), + is_mut: g.global_type().is_mutable(), + // TODO: init expr + origin: ImportedOrDeclared::Declared(Vec::new()), + }); + } + }, + elements::Section::Export(export_section) => { + for e in export_section.entries() { + match e.internal() { + &elements::Internal::Function(func_idx) => { + res.exports.push(Export::Func(res.funcs.clone_ref(func_idx as usize))); + }, + &elements::Internal::Global(global_idx) => { + res.exports.push(Export::Global(res.globals.clone_ref(global_idx as usize))); + }, + &elements::Internal::Memory(mem_idx) => { + res.exports.push(Export::Memory(res.memory.clone_ref(mem_idx as usize))); + }, + &elements::Internal::Table(table_idx) => { + res.exports.push(Export::Table(res.tables.clone_ref(table_idx as usize))); + }, + } + } + }, _ => continue, } } @@ -167,6 +199,7 @@ mod tests { (type (func)) (func (type 0)) (memory 0 1) + (export "simple" (func 0)) ) "#).expect("Failed to read fixture"); @@ -176,5 +209,9 @@ mod tests { assert_eq!(f.funcs.len(), 1); assert_eq!(f.tables.len(), 0); assert_eq!(f.memory.len(), 1); + assert_eq!(f.exports.len(), 1); + + assert_eq!(f.types.get_ref(0).link_count(), 1); + assert_eq!(f.funcs.get_ref(0).link_count(), 1); } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index d41c4df..244fb50 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,10 @@ mod std { pub use core::*; pub use alloc::{vec, string, boxed, borrow}; + pub mod rc { + pub use alloc::rc::Rc; + } + pub mod collections { pub use alloc::collections::{BTreeMap, BTreeSet}; } diff --git a/src/ref_list.rs b/src/ref_list.rs index 87497e0..63adc63 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -1,6 +1,7 @@ use std::rc::Rc; use std::cell::RefCell; +use std::vec::Vec; #[derive(Debug)] enum EntryOrigin { @@ -59,17 +60,21 @@ impl From> for EntryRef { } impl EntryRef { - fn read(&self) -> ::std::cell::Ref> { + pub fn read(&self) -> ::std::cell::Ref> { self.0.borrow() } - fn write(&self) -> ::std::cell::RefMut> { + pub fn write(&self) -> ::std::cell::RefMut> { self.0.borrow_mut() } - fn order(&self) -> Option { + pub fn order(&self) -> Option { self.0.borrow().order() } + + pub fn link_count(&self) -> usize { + Rc::strong_count(&self.0) - 1 + } } pub struct RefList { @@ -148,6 +153,14 @@ impl RefList { pub fn len(&self) -> usize { self.items.len() } + + pub fn clone_ref(&self, idx: usize) -> EntryRef { + self.items[idx].clone() + } + + pub fn get_ref(&self, idx: usize) -> &EntryRef { + &self.items[idx] + } } #[must_use] From ed1c7b1b5134790b0d3c4480a7c255f4e98d5f25 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 14:42:57 +0300 Subject: [PATCH 09/35] better exports --- src/graph.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 51fb4ff..dcfc32e 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -58,13 +58,18 @@ struct ElementSegment { data: Vec, } -enum Export { +enum ExportLocal { Func(EntryRef), Global(EntryRef), Table(EntryRef
), Memory(EntryRef), } +struct Export { + name: String, + local: ExportLocal, +} + #[derive(Default)] struct Module { types: RefList, @@ -157,20 +162,22 @@ impl Module { }, elements::Section::Export(export_section) => { for e in export_section.entries() { - match e.internal() { + let local = match e.internal() { &elements::Internal::Function(func_idx) => { - res.exports.push(Export::Func(res.funcs.clone_ref(func_idx as usize))); + ExportLocal::Func(res.funcs.clone_ref(func_idx as usize)) }, &elements::Internal::Global(global_idx) => { - res.exports.push(Export::Global(res.globals.clone_ref(global_idx as usize))); + ExportLocal::Global(res.globals.clone_ref(global_idx as usize)) }, &elements::Internal::Memory(mem_idx) => { - res.exports.push(Export::Memory(res.memory.clone_ref(mem_idx as usize))); + ExportLocal::Memory(res.memory.clone_ref(mem_idx as usize)) }, &elements::Internal::Table(table_idx) => { - res.exports.push(Export::Table(res.tables.clone_ref(table_idx as usize))); + ExportLocal::Table(res.tables.clone_ref(table_idx as usize)) }, - } + }; + + res.exports.push(Export { local: local, name: e.field().to_owned() }) } }, _ => continue, From 86da6439d12f2cafc263576464577641bff02e11 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 15:15:17 +0300 Subject: [PATCH 10/35] data and elements --- src/graph.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index dcfc32e..89d090b 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -48,14 +48,20 @@ struct Table { limits: elements::ResizableLimits, } +enum SegmentLocation { + Passive, + Default(Vec), + WithIndex(u32, Vec), +} + struct DataSegment { - offset_expr: Vec, - data: Vec, + location: SegmentLocation, + value: Vec, } struct ElementSegment { - offset_expr: Vec, - data: Vec, + location: SegmentLocation, + value: Vec, } enum ExportLocal { @@ -180,6 +186,44 @@ impl Module { res.exports.push(Export { local: local, name: e.field().to_owned() }) } }, + elements::Section::Start(start_func) => { + res.start = Some(res.funcs.clone_ref(*start_func as usize)); + }, + elements::Section::Element(element_section) => { + for element_segment in element_section.entries() { + + // let location = if element_segment.passive() { + // SegmentLocation::Passive + // } else if element_segment.index() == 0 { + // // TODO: transform instructions + // SegmentLocation::Default(Vec::new()) + // } else { + // // TODO: transform instructions + // SegmentLocation::WithIndex(element_segment.index(), Vec::new()) + // }; + + // TODO: transform instructions + // TODO: update parity-wasm and uncomment the above + let location = SegmentLocation::Default(Vec::new()); + + res.elements.push(ElementSegment { + value: element_segment.members().to_vec(), + location: location, + }); + } + }, + elements::Section::Data(data_section) => { + for data_segment in data_section.entries() { + // TODO: transform instructions + // TODO: update parity-wasm and uncomment the above + let location = SegmentLocation::Default(Vec::new()); + + res.data.push(DataSegment { + value: data_segment.value().to_vec(), + location: location, + }); + } + } _ => continue, } } From d6c6cefcf16192a3278da3f097f418fba1e43ff9 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 16:11:04 +0300 Subject: [PATCH 11/35] generation - import --- src/graph.rs | 109 +++++++++++++++++++++++++++++++++++++++++++++++- src/ref_list.rs | 4 ++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/src/graph.rs b/src/graph.rs index 89d090b..7a43310 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -5,6 +5,7 @@ use super::ref_list::{RefList, EntryRef}; use std::vec::Vec; use std::borrow::ToOwned; use std::string::String; +use std::collections::BTreeMap; enum ImportedOrDeclared { Imported(String, String), @@ -87,12 +88,14 @@ struct Module { exports: Vec, elements: Vec, data: Vec, + other: BTreeMap, } impl Module { fn from_elements(module: &elements::Module) -> Self { + let mut idx = 0; let mut res = Module::default(); for section in module.sections() { @@ -224,13 +227,117 @@ impl Module { }); } } - _ => continue, + _ => { + res.other.insert(idx, section.clone()); + } } + idx += 1; } res } + fn generate(&self) -> elements::Module { + use self::ImportedOrDeclared::*; + + let mut idx = 0; + let mut sections = Vec::new(); + + custom_round(&self.other, &mut idx, &mut sections); + + let mut imports = Vec::new(); + + for func in self.funcs.iter() { + match func.read().origin { + Imported(ref module, ref field) => { + imports.push( + elements::ImportEntry::new( + module.to_owned(), + field.to_owned(), + elements::External::Function( + func.read().type_ref.order() + .expect("detached func encountered somehow!") as u32 + ), + ) + ) + }, + _ => continue, + } + } + + for global in self.globals.iter() { + match global.read().origin { + Imported(ref module, ref field) => { + imports.push( + elements::ImportEntry::new( + module.to_owned(), + field.to_owned(), + elements::External::Global( + elements::GlobalType::new( + global.read().content, + global.read().is_mut, + ) + ), + ) + ) + }, + _ => continue, + } + } + + for memory in self.memory.iter() { + match memory.read().origin { + Imported(ref module, ref field) => { + imports.push( + elements::ImportEntry::new( + module.to_owned(), + field.to_owned(), + elements::External::Memory( + elements::MemoryType::new( + memory.read().limits.initial(), + memory.read().limits.maximum(), + ) + ), + ) + ) + }, + _ => continue, + } + } + + for table in self.tables.iter() { + match table.read().origin { + Imported(ref module, ref field) => { + imports.push( + elements::ImportEntry::new( + module.to_owned(), + field.to_owned(), + elements::External::Table( + elements::TableType::new( + table.read().limits.initial(), + table.read().limits.maximum(), + ) + ), + ) + ) + }, + _ => continue, + } + } + + elements::Module::new(sections) + } +} + +fn custom_round( + map: &BTreeMap, + idx: &mut usize, + sections: &mut Vec, +) { + while let Some(other_section) = map.get(&idx) { + sections.push(other_section.clone()); + *idx += 1; + } } fn parse(wasm: &[u8]) -> Module { diff --git a/src/ref_list.rs b/src/ref_list.rs index 63adc63..82d04d9 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -161,6 +161,10 @@ impl RefList { pub fn get_ref(&self, idx: usize) -> &EntryRef { &self.items[idx] } + + pub fn iter(&self) -> std::slice::Iter> { + self.items.iter() + } } #[must_use] From 76b6743c64ff545edf48a35eeec59fd5085553fd Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 16:40:28 +0300 Subject: [PATCH 12/35] generation of more sections --- src/graph.rs | 252 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 182 insertions(+), 70 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 7a43310..22169b8 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -245,85 +245,197 @@ impl Module { custom_round(&self.other, &mut idx, &mut sections); - let mut imports = Vec::new(); + // TYPE SECTION (1) - for func in self.funcs.iter() { - match func.read().origin { - Imported(ref module, ref field) => { - imports.push( - elements::ImportEntry::new( - module.to_owned(), - field.to_owned(), - elements::External::Function( - func.read().type_ref.order() - .expect("detached func encountered somehow!") as u32 - ), + let mut type_section = elements::TypeSection::default(); + { + let mut types = type_section.types_mut(); + + for type_entry in self.types.iter() { + types.push(type_entry.read().clone()) + } + } + sections.push(elements::Section::Type(type_section)); + idx += 1; + + custom_round(&self.other, &mut idx, &mut sections); + + // IMPORT SECTION (2) + let mut import_section = elements::ImportSection::default(); + { + let mut imports = import_section.entries_mut(); + for func in self.funcs.iter() { + match func.read().origin { + Imported(ref module, ref field) => { + imports.push( + elements::ImportEntry::new( + module.to_owned(), + field.to_owned(), + elements::External::Function( + func.read().type_ref.order() + .expect("detached func encountered somehow!") as u32 + ), + ) ) - ) - }, - _ => continue, + }, + _ => continue, + } + } + + for global in self.globals.iter() { + match global.read().origin { + Imported(ref module, ref field) => { + imports.push( + elements::ImportEntry::new( + module.to_owned(), + field.to_owned(), + elements::External::Global( + elements::GlobalType::new( + global.read().content, + global.read().is_mut, + ) + ), + ) + ) + }, + _ => continue, + } + } + + for memory in self.memory.iter() { + match memory.read().origin { + Imported(ref module, ref field) => { + imports.push( + elements::ImportEntry::new( + module.to_owned(), + field.to_owned(), + elements::External::Memory( + elements::MemoryType::new( + memory.read().limits.initial(), + memory.read().limits.maximum(), + ) + ), + ) + ) + }, + _ => continue, + } + } + + for table in self.tables.iter() { + match table.read().origin { + Imported(ref module, ref field) => { + imports.push( + elements::ImportEntry::new( + module.to_owned(), + field.to_owned(), + elements::External::Table( + elements::TableType::new( + table.read().limits.initial(), + table.read().limits.maximum(), + ) + ), + ) + ) + }, + _ => continue, + } } } - for global in self.globals.iter() { - match global.read().origin { - Imported(ref module, ref field) => { - imports.push( - elements::ImportEntry::new( - module.to_owned(), - field.to_owned(), - elements::External::Global( - elements::GlobalType::new( - global.read().content, - global.read().is_mut, - ) - ), - ) - ) - }, - _ => continue, - } - } + sections.push(elements::Section::Import(import_section)); + idx += 1; - for memory in self.memory.iter() { - match memory.read().origin { - Imported(ref module, ref field) => { - imports.push( - elements::ImportEntry::new( - module.to_owned(), - field.to_owned(), - elements::External::Memory( - elements::MemoryType::new( - memory.read().limits.initial(), - memory.read().limits.maximum(), - ) - ), - ) - ) - }, - _ => continue, - } - } + custom_round(&self.other, &mut idx, &mut sections); - for table in self.tables.iter() { - match table.read().origin { - Imported(ref module, ref field) => { - imports.push( - elements::ImportEntry::new( - module.to_owned(), - field.to_owned(), - elements::External::Table( - elements::TableType::new( - table.read().limits.initial(), - table.read().limits.maximum(), - ) - ), - ) - ) - }, - _ => continue, + // FUNC SECTION (3) + let mut func_section = elements::FunctionSection::default(); + { + let mut funcs = func_section.entries_mut(); + + for func in self.funcs.iter() { + match func.read().origin { + Declared(_) => { + funcs.push(elements::Func::new( + func.read().type_ref.order() + .expect("detached func encountered somehow!") as u32 + )); + }, + _ => continue, + } } } + sections.push(elements::Section::Function(func_section)); + idx += 1; + + custom_round(&self.other, &mut idx, &mut sections); + + // TABLE SECTION (4) + let mut table_section = elements::TableSection::default(); + { + let mut tables = table_section.entries_mut(); + + for table in self.tables.iter() { + match table.read().origin { + Declared(_) => { + tables.push(elements::TableType::new( + table.read().limits.initial(), + table.read().limits.maximum(), + )); + }, + _ => continue, + } + } + } + sections.push(elements::Section::Table(table_section)); + idx += 1; + + custom_round(&self.other, &mut idx, &mut sections); + + // TABLE SECTION (4) + let mut memory_section = elements::MemorySection::default(); + { + let mut memories = memory_section.entries_mut(); + + for memory in self.memory.iter() { + match memory.read().origin { + Declared(_) => { + memories.push(elements::MemoryType::new( + memory.read().limits.initial(), + memory.read().limits.maximum(), + )); + }, + _ => continue, + } + } + } + sections.push(elements::Section::Memory(memory_section)); + idx += 1; + + custom_round(&self.other, &mut idx, &mut sections); + + // CODE SECTION (10) + let mut code_section = elements::CodeSection::default(); + { + let mut funcs = code_section.bodies_mut(); + + for func in self.funcs.iter() { + match func.read().origin { + Declared(_) => { + // TODO: generate body + funcs.push(elements::FuncBody::new( + Vec::new(), + elements::Instructions::empty(), + )); + }, + _ => continue, + } + } + } + sections.push(elements::Section::Code(code_section)); + idx += 1; + + custom_round(&self.other, &mut idx, &mut sections); elements::Module::new(sections) } From c520d334cdd2885e160c3c86d74fb53e68afe04e Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 17:14:37 +0300 Subject: [PATCH 13/35] ordering and filtering --- src/graph.rs | 268 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 182 insertions(+), 86 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 22169b8..677d4f5 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -245,24 +245,26 @@ impl Module { custom_round(&self.other, &mut idx, &mut sections); - // TYPE SECTION (1) + if self.types.len() > 0 { + // TYPE SECTION (1) + let mut type_section = elements::TypeSection::default(); + { + let mut types = type_section.types_mut(); - let mut type_section = elements::TypeSection::default(); - { - let mut types = type_section.types_mut(); - - for type_entry in self.types.iter() { - types.push(type_entry.read().clone()) + for type_entry in self.types.iter() { + types.push(type_entry.read().clone()) + } } - } - sections.push(elements::Section::Type(type_section)); - idx += 1; + sections.push(elements::Section::Type(type_section)); + idx += 1; - custom_round(&self.other, &mut idx, &mut sections); + custom_round(&self.other, &mut idx, &mut sections); + } // IMPORT SECTION (2) let mut import_section = elements::ImportSection::default(); - { + + let add = { let mut imports = import_section.entries_mut(); for func in self.funcs.iter() { match func.read().origin { @@ -341,101 +343,168 @@ impl Module { _ => continue, } } + imports.len() > 0 + }; + + if add { + sections.push(elements::Section::Import(import_section)); + idx += 1; + custom_round(&self.other, &mut idx, &mut sections); } - sections.push(elements::Section::Import(import_section)); - idx += 1; + if self.funcs.len() > 0 { + // FUNC SECTION (3) + let mut func_section = elements::FunctionSection::default(); + { + let mut funcs = func_section.entries_mut(); - custom_round(&self.other, &mut idx, &mut sections); - - // FUNC SECTION (3) - let mut func_section = elements::FunctionSection::default(); - { - let mut funcs = func_section.entries_mut(); - - for func in self.funcs.iter() { - match func.read().origin { - Declared(_) => { - funcs.push(elements::Func::new( - func.read().type_ref.order() - .expect("detached func encountered somehow!") as u32 - )); - }, - _ => continue, + for func in self.funcs.iter() { + match func.read().origin { + Declared(_) => { + funcs.push(elements::Func::new( + func.read().type_ref.order() + .expect("detached func encountered somehow!") as u32 + )); + }, + _ => continue, + } } } + sections.push(elements::Section::Function(func_section)); + idx += 1; + + custom_round(&self.other, &mut idx, &mut sections); } - sections.push(elements::Section::Function(func_section)); - idx += 1; - custom_round(&self.other, &mut idx, &mut sections); + if self.tables.len() > 0 { + // TABLE SECTION (4) + let mut table_section = elements::TableSection::default(); + { + let mut tables = table_section.entries_mut(); - // TABLE SECTION (4) - let mut table_section = elements::TableSection::default(); - { - let mut tables = table_section.entries_mut(); - - for table in self.tables.iter() { - match table.read().origin { - Declared(_) => { - tables.push(elements::TableType::new( - table.read().limits.initial(), - table.read().limits.maximum(), - )); - }, - _ => continue, + for table in self.tables.iter() { + match table.read().origin { + Declared(_) => { + tables.push(elements::TableType::new( + table.read().limits.initial(), + table.read().limits.maximum(), + )); + }, + _ => continue, + } } } + sections.push(elements::Section::Table(table_section)); + idx += 1; + + custom_round(&self.other, &mut idx, &mut sections); } - sections.push(elements::Section::Table(table_section)); - idx += 1; - custom_round(&self.other, &mut idx, &mut sections); + if self.memory.len() > 0 { + // MEMORY SECTION (5) + let mut memory_section = elements::MemorySection::default(); + { + let mut memories = memory_section.entries_mut(); - // TABLE SECTION (4) - let mut memory_section = elements::MemorySection::default(); - { - let mut memories = memory_section.entries_mut(); - - for memory in self.memory.iter() { - match memory.read().origin { - Declared(_) => { - memories.push(elements::MemoryType::new( - memory.read().limits.initial(), - memory.read().limits.maximum(), - )); - }, - _ => continue, + for memory in self.memory.iter() { + match memory.read().origin { + Declared(_) => { + memories.push(elements::MemoryType::new( + memory.read().limits.initial(), + memory.read().limits.maximum(), + )); + }, + _ => continue, + } } } + sections.push(elements::Section::Memory(memory_section)); + idx += 1; + + custom_round(&self.other, &mut idx, &mut sections); } - sections.push(elements::Section::Memory(memory_section)); - idx += 1; - custom_round(&self.other, &mut idx, &mut sections); + if self.globals.len() > 0 { + // GLOBAL SECTION (6) + let mut global_section = elements::GlobalSection::default(); + { + let mut globals = global_section.entries_mut(); - // CODE SECTION (10) - let mut code_section = elements::CodeSection::default(); - { - let mut funcs = code_section.bodies_mut(); - - for func in self.funcs.iter() { - match func.read().origin { - Declared(_) => { - // TODO: generate body - funcs.push(elements::FuncBody::new( - Vec::new(), - elements::Instructions::empty(), - )); - }, - _ => continue, + for global in self.globals.iter() { + match global.read().origin { + Declared(_) => { + globals.push(elements::GlobalEntry::new( + elements::GlobalType::new(global.read().content, global.read().is_mut), + // TODO: generate init expr + elements::InitExpr::empty(), + )); + }, + _ => continue, + } } } - } - sections.push(elements::Section::Code(code_section)); - idx += 1; + sections.push(elements::Section::Global(global_section)); + idx += 1; - custom_round(&self.other, &mut idx, &mut sections); + custom_round(&self.other, &mut idx, &mut sections); + } + + if self.exports.len() > 0 { + // EXPORT SECTION (6) + let mut export_section = elements::ExportSection::default(); + { + let mut exports = export_section.entries_mut(); + + for export in self.exports.iter() { + let internal = match export.local { + ExportLocal::Func(ref func_ref) => { + elements::Internal::Function(func_ref.order().expect("detached func ref") as u32) + }, + ExportLocal::Global(ref global_ref) => { + elements::Internal::Global(global_ref.order().expect("detached global ref") as u32) + }, + ExportLocal::Table(ref table_ref) => { + elements::Internal::Table(table_ref.order().expect("detached table ref") as u32) + }, + ExportLocal::Memory(ref memory_ref) => { + elements::Internal::Memory(memory_ref.order().expect("detached memory ref") as u32) + }, + _ => continue, + }; + + exports.push(elements::ExportEntry::new(export.name.to_owned(), internal)); + } + } + sections.push(elements::Section::Export(export_section)); + idx += 1; + + custom_round(&self.other, &mut idx, &mut sections); + } + + if self.funcs.len() > 0 { + // CODE SECTION (10) + let mut code_section = elements::CodeSection::default(); + { + let mut funcs = code_section.bodies_mut(); + + for func in self.funcs.iter() { + match func.read().origin { + Declared(_) => { + // TODO: generate body + funcs.push(elements::FuncBody::new( + Vec::new(), + elements::Instructions::empty(), + )); + }, + _ => continue, + } + } + } + sections.push(elements::Section::Code(code_section)); + idx += 1; + + custom_round(&self.other, &mut idx, &mut sections); + } elements::Module::new(sections) } @@ -456,6 +525,11 @@ fn parse(wasm: &[u8]) -> Module { Module::from_elements(&::parity_wasm::deserialize_buffer(wasm).expect("failed to parse wasm")) } +fn generate(f: &Module) -> Vec { + let pm = f.generate(); + ::parity_wasm::serialize(pm).expect("failed to generate wasm") +} + #[cfg(test)] mod tests { @@ -484,4 +558,26 @@ mod tests { assert_eq!(f.types.get_ref(0).link_count(), 1); assert_eq!(f.funcs.get_ref(0).link_count(), 1); } + + #[test] + #[ignore] + fn simple_round_trip() { + let wat = r#" + (module + (type (func)) + (func (type 0)) + (memory 0 1) + (export "simple" (func 0)) + ) + "#; + let wasm = wabt::wat2wasm(wat).expect("Failed to read fixture"); + + let f = super::parse(&wasm[..]); + let wasm_new = super::generate(&f); + + let wat_new = wabt::wasm2wat(&wasm_new).expect("Failed to generate expectation"); + + assert_eq!(&wat_new, wat); + } + } \ No newline at end of file From da5b2ca5f62bde4462985704ada56cdb2201c229 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 18:21:30 +0300 Subject: [PATCH 14/35] rest of sections generation --- src/graph.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 10 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 677d4f5..cabed75 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -249,7 +249,7 @@ impl Module { // TYPE SECTION (1) let mut type_section = elements::TypeSection::default(); { - let mut types = type_section.types_mut(); + let types = type_section.types_mut(); for type_entry in self.types.iter() { types.push(type_entry.read().clone()) @@ -265,7 +265,7 @@ impl Module { let mut import_section = elements::ImportSection::default(); let add = { - let mut imports = import_section.entries_mut(); + let imports = import_section.entries_mut(); for func in self.funcs.iter() { match func.read().origin { Imported(ref module, ref field) => { @@ -356,7 +356,7 @@ impl Module { // FUNC SECTION (3) let mut func_section = elements::FunctionSection::default(); { - let mut funcs = func_section.entries_mut(); + let funcs = func_section.entries_mut(); for func in self.funcs.iter() { match func.read().origin { @@ -380,7 +380,7 @@ impl Module { // TABLE SECTION (4) let mut table_section = elements::TableSection::default(); { - let mut tables = table_section.entries_mut(); + let tables = table_section.entries_mut(); for table in self.tables.iter() { match table.read().origin { @@ -404,7 +404,7 @@ impl Module { // MEMORY SECTION (5) let mut memory_section = elements::MemorySection::default(); { - let mut memories = memory_section.entries_mut(); + let memories = memory_section.entries_mut(); for memory in self.memory.iter() { match memory.read().origin { @@ -428,7 +428,7 @@ impl Module { // GLOBAL SECTION (6) let mut global_section = elements::GlobalSection::default(); { - let mut globals = global_section.entries_mut(); + let globals = global_section.entries_mut(); for global in self.globals.iter() { match global.read().origin { @@ -450,10 +450,10 @@ impl Module { } if self.exports.len() > 0 { - // EXPORT SECTION (6) + // EXPORT SECTION (7) let mut export_section = elements::ExportSection::default(); { - let mut exports = export_section.entries_mut(); + let exports = export_section.entries_mut(); for export in self.exports.iter() { let internal = match export.local { @@ -481,6 +481,42 @@ impl Module { custom_round(&self.other, &mut idx, &mut sections); } + if let Some(ref func_ref) = self.start { + // START SECTION (8) + sections.push(elements::Section::Start( + func_ref.order().expect("detached start func") as u32 + )); + } + + if self.elements.len() > 0 { + // START SECTION (9) + let mut element_section = elements::ElementSection::default(); + { + let element_segments = element_section.entries_mut(); + + for element in self.elements.iter() { + match element.location { + SegmentLocation::Default(ref offset_expr) => { + element_segments.push( + elements::ElementSegment::new( + 0, + // TODO: generate init expr + elements::InitExpr::empty(), + element.value.clone(), + ) + ); + }, + _ => unreachable!("Other segment location types are never added"), + } + } + } + + sections.push(elements::Section::Element(element_section)); + idx += 1; + + custom_round(&self.other, &mut idx, &mut sections); + } + if self.funcs.len() > 0 { // CODE SECTION (10) let mut code_section = elements::CodeSection::default(); @@ -506,6 +542,36 @@ impl Module { custom_round(&self.other, &mut idx, &mut sections); } + + if self.data.len() > 0 { + // DATA SECTION (11) + let mut data_section = elements::DataSection::default(); + { + let data_segments = data_section.entries_mut(); + + for data_entry in self.data.iter() { + match data_entry.location { + SegmentLocation::Default(ref offset_expr) => { + data_segments.push( + elements::DataSegment::new( + 0, + // TODO: generate init expr + elements::InitExpr::empty(), + data_entry.value.clone(), + ) + ); + }, + _ => unreachable!("Other segment location types are never added"), + } + } + } + + sections.push(elements::Section::Data(data_section)); + idx += 1; + + custom_round(&self.other, &mut idx, &mut sections); + } + elements::Module::new(sections) } } @@ -565,7 +631,7 @@ mod tests { let wat = r#" (module (type (func)) - (func (type 0)) + (import "env" "f1" (func (type 0))) (memory 0 1) (export "simple" (func 0)) ) @@ -577,7 +643,11 @@ mod tests { let wat_new = wabt::wasm2wat(&wasm_new).expect("Failed to generate expectation"); - assert_eq!(&wat_new, wat); + if &wasm_new[..] != &wasm[..] { + panic!( + "{}\n != \n{}", wat, wat_new + ); + } } } \ No newline at end of file From d60340762b216a82a942cfb0fb952e72ec6fb7f3 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 18:28:15 +0300 Subject: [PATCH 15/35] public api exposure and fix warnings --- src/graph.rs | 92 ++++++++++++++++++++++++------------------------- src/lib.rs | 2 ++ src/ref_list.rs | 7 ++-- 3 files changed, 49 insertions(+), 52 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index cabed75..ba48e98 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -7,7 +7,7 @@ use std::borrow::ToOwned; use std::string::String; use std::collections::BTreeMap; -enum ImportedOrDeclared { +pub enum ImportedOrDeclared { Imported(String, String), Declared(T), } @@ -18,82 +18,82 @@ impl From<&elements::ImportEntry> for ImportedOrDeclared { } } -type FuncOrigin = ImportedOrDeclared>; -type GlobalOrigin = ImportedOrDeclared>; -type MemoryOrigin = ImportedOrDeclared; -type TableOrigin = ImportedOrDeclared; +pub type FuncOrigin = ImportedOrDeclared>; +pub type GlobalOrigin = ImportedOrDeclared>; +pub type MemoryOrigin = ImportedOrDeclared; +pub type TableOrigin = ImportedOrDeclared; -struct Func { - type_ref: EntryRef, - origin: FuncOrigin, +pub struct Func { + pub type_ref: EntryRef, + pub origin: FuncOrigin, } -struct Global { - content: elements::ValueType, - is_mut: bool, - origin: GlobalOrigin, +pub struct Global { + pub content: elements::ValueType, + pub is_mut: bool, + pub origin: GlobalOrigin, } -enum Instruction { +pub enum Instruction { Plain(elements::Instruction), Call(EntryRef), } -struct Memory { - limits: elements::ResizableLimits, - origin: MemoryOrigin, +pub struct Memory { + pub limits: elements::ResizableLimits, + pub origin: MemoryOrigin, } -struct Table { - origin: TableOrigin, - limits: elements::ResizableLimits, +pub struct Table { + pub origin: TableOrigin, + pub limits: elements::ResizableLimits, } -enum SegmentLocation { +pub enum SegmentLocation { Passive, Default(Vec), WithIndex(u32, Vec), } -struct DataSegment { - location: SegmentLocation, - value: Vec, +pub struct DataSegment { + pub location: SegmentLocation, + pub value: Vec, } -struct ElementSegment { - location: SegmentLocation, - value: Vec, +pub struct ElementSegment { + pub location: SegmentLocation, + pub value: Vec, } -enum ExportLocal { +pub enum ExportLocal { Func(EntryRef), Global(EntryRef), Table(EntryRef
), Memory(EntryRef), } -struct Export { - name: String, - local: ExportLocal, +pub struct Export { + pub name: String, + pub local: ExportLocal, } #[derive(Default)] -struct Module { - types: RefList, - funcs: RefList, - memory: RefList, - tables: RefList
, - globals: RefList, - start: Option>, - exports: Vec, - elements: Vec, - data: Vec, - other: BTreeMap, +pub struct Module { + pub types: RefList, + pub funcs: RefList, + pub memory: RefList, + pub tables: RefList
, + pub globals: RefList, + pub start: Option>, + pub exports: Vec, + pub elements: Vec, + pub data: Vec, + pub other: BTreeMap, } impl Module { - fn from_elements(module: &elements::Module) -> Self { + pub fn from_elements(module: &elements::Module) -> Self { let mut idx = 0; let mut res = Module::default(); @@ -469,7 +469,6 @@ impl Module { ExportLocal::Memory(ref memory_ref) => { elements::Internal::Memory(memory_ref.order().expect("detached memory ref") as u32) }, - _ => continue, }; exports.push(elements::ExportEntry::new(export.name.to_owned(), internal)); @@ -521,7 +520,7 @@ impl Module { // CODE SECTION (10) let mut code_section = elements::CodeSection::default(); { - let mut funcs = code_section.bodies_mut(); + let funcs = code_section.bodies_mut(); for func in self.funcs.iter() { match func.read().origin { @@ -587,11 +586,11 @@ fn custom_round( } } -fn parse(wasm: &[u8]) -> Module { +pub fn parse(wasm: &[u8]) -> Module { Module::from_elements(&::parity_wasm::deserialize_buffer(wasm).expect("failed to parse wasm")) } -fn generate(f: &Module) -> Vec { +pub fn generate(f: &Module) -> Vec { let pm = f.generate(); ::parity_wasm::serialize(pm).expect("failed to generate wasm") } @@ -600,7 +599,6 @@ fn generate(f: &Module) -> Vec { mod tests { extern crate wabt; - use parity_wasm; #[test] fn smoky() { diff --git a/src/lib.rs b/src/lib.rs index 244fb50..2ab34dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,8 @@ pub use gas::inject_gas_counter; pub use ext::{externalize, externalize_mem, underscore_funcs, ununderscore_funcs, shrink_unknown_stack}; pub use pack::{pack_instance, Error as PackingError}; pub use runtime_type::inject_runtime_type; +pub use graph::{Module, parse, generate}; +pub use ref_list::{RefList, Entry, EntryRef, DeleteTransaction}; pub struct TargetRuntime { pub create_symbol: &'static str, diff --git a/src/ref_list.rs b/src/ref_list.rs index 82d04d9..df10547 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -110,9 +110,6 @@ impl RefList { } fn done_delete(&mut self, indices: &[usize]) { - - let mut index = 0; - for idx in indices { let mut detached = self.items.remove(*idx); detached.write().index = EntryOrigin::Detached; @@ -174,13 +171,13 @@ pub struct DeleteTransaction<'a, T> { } impl<'a, T> DeleteTransaction<'a, T> { - pub fn push(mut self, idx: usize) -> Self { + pub fn push(self, idx: usize) -> Self { let mut tx = self; tx.deleted.push(idx); tx } - pub fn done(mut self) { + pub fn done(self) { let indices = self.deleted; let list = self.list; list.done_delete(&indices[..]); From 48c1c6e72a5a3dec5e8535b5315378a81c47e62a Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 20:30:50 +0300 Subject: [PATCH 16/35] code mapping --- src/graph.rs | 84 ++++++++++++++++++++++++++++++++++++++++++------- src/ref_list.rs | 6 ++++ 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index ba48e98..6907144 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -18,11 +18,16 @@ impl From<&elements::ImportEntry> for ImportedOrDeclared { } } -pub type FuncOrigin = ImportedOrDeclared>; +pub type FuncOrigin = ImportedOrDeclared; pub type GlobalOrigin = ImportedOrDeclared>; pub type MemoryOrigin = ImportedOrDeclared; pub type TableOrigin = ImportedOrDeclared; +pub struct FuncBody { + pub locals: Vec, + pub code: Vec, +} + pub struct Func { pub type_ref: EntryRef, pub origin: FuncOrigin, @@ -37,6 +42,9 @@ pub struct Global { pub enum Instruction { Plain(elements::Instruction), Call(EntryRef), + CallIndirect(EntryRef, u8), + GetGlobal(EntryRef), + SetGlobal(EntryRef), } pub struct Memory { @@ -93,11 +101,41 @@ pub struct Module { impl Module { + fn map_instructions(&self, instructions: &[elements::Instruction]) -> Vec { + use parity_wasm::elements::Instruction::*; + instructions.iter().map(|instruction| match instruction { + Call(func_idx) => Instruction::Call(self.funcs.clone_ref(*func_idx as usize)), + CallIndirect(type_idx, arg2) => + Instruction::CallIndirect( + self.types.clone_ref(*type_idx as usize), + *arg2, + ), + SetGlobal(global_idx) => + Instruction::SetGlobal(self.globals.clone_ref(*global_idx as usize)), + GetGlobal(global_idx) => + Instruction::GetGlobal(self.globals.clone_ref(*global_idx as usize)), + other_instruction => Instruction::Plain(other_instruction.clone()), + }).collect() + } + + fn generate_instructions(&self, instructions: &[Instruction]) -> Vec { + use parity_wasm::elements::Instruction::*; + instructions.iter().map(|instruction| match instruction { + Instruction::Call(func_ref) => Call(func_ref.order().expect("detached instruction!") as u32), + Instruction::CallIndirect(type_ref, arg2) => CallIndirect(type_ref.order().expect("detached instruction!") as u32, *arg2), + Instruction::SetGlobal(global_ref) => SetGlobal(global_ref.order().expect("detached instruction!") as u32), + Instruction::GetGlobal(global_ref) => GetGlobal(global_ref.order().expect("detached instruction!") as u32), + Instruction::Plain(plain) => plain.clone(), + }).collect() + } + pub fn from_elements(module: &elements::Module) -> Self { let mut idx = 0; let mut res = Module::default(); + let mut imported_functions = 0; + for section in module.sections() { match section { elements::Section::Type(type_section) => { @@ -111,6 +149,7 @@ impl Module { type_ref: res.types.get(f as usize).expect("validated; qed").clone(), origin: entry.into(), }); + imported_functions += 1; }, elements::External::Memory(m) => { res.memory.push(Memory { @@ -138,8 +177,11 @@ impl Module { for f in function_section.entries() { res.funcs.push(Func { type_ref: res.types.get(f.type_ref() as usize).expect("validated; qed").clone(), - // code will be populated later - origin: ImportedOrDeclared::Declared(Vec::new()), + origin: ImportedOrDeclared::Declared(FuncBody { + locals: Vec::new(), + // code will be populated later + code: Vec::new(), + }), }); }; }, @@ -161,11 +203,11 @@ impl Module { }, elements::Section::Global(global_section) => { for g in global_section.entries() { + let init_code = res.map_instructions(g.init_expr().code()); res.globals.push(Global { content: g.global_type().content_type(), is_mut: g.global_type().is_mutable(), - // TODO: init expr - origin: ImportedOrDeclared::Declared(Vec::new()), + origin: ImportedOrDeclared::Declared(init_code), }); } }, @@ -205,9 +247,10 @@ impl Module { // SegmentLocation::WithIndex(element_segment.index(), Vec::new()) // }; - // TODO: transform instructions - // TODO: update parity-wasm and uncomment the above - let location = SegmentLocation::Default(Vec::new()); + // TODO: update parity-wasm and uncomment the above instead + let location = SegmentLocation::Default( + res.map_instructions(element_segment.offset().code()) + ); res.elements.push(ElementSegment { value: element_segment.members().to_vec(), @@ -215,18 +258,35 @@ impl Module { }); } }, + elements::Section::Code(code_section) => { + let mut idx = 0; + for func_body in code_section.bodies() { + let code = res.map_instructions(func_body.code().elements()); + + let mut func = res.funcs.get_ref(imported_functions + idx).write(); + match func.origin { + ImportedOrDeclared::Declared(ref mut body) => { + body.code = code; + body.locals = func_body.locals().iter().cloned().collect(); + }, + _ => unreachable!("All declared functions added after imported; qed"), + } + } + }, elements::Section::Data(data_section) => { for data_segment in data_section.entries() { - // TODO: transform instructions - // TODO: update parity-wasm and uncomment the above - let location = SegmentLocation::Default(Vec::new()); + // TODO: update parity-wasm and use the same logic as in + // commented element segment branch + let location = SegmentLocation::Default( + res.map_instructions(data_segment.offset().code()) + ); res.data.push(DataSegment { value: data_segment.value().to_vec(), location: location, }); } - } + }, _ => { res.other.insert(idx, section.clone()); } diff --git a/src/ref_list.rs b/src/ref_list.rs index df10547..4679fe7 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -45,6 +45,12 @@ impl ::std::ops::Deref for Entry { } } +impl ::std::ops::DerefMut for Entry { + fn deref_mut(&mut self) -> &mut T { + &mut self.val + } +} + pub struct EntryRef(Rc>>); impl Clone for EntryRef { From 4e871c65e2c4027eaa7a208393d2bb606e7b866b Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 20:37:36 +0300 Subject: [PATCH 17/35] generate instructions on module generation --- src/graph.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 6907144..66cf0de 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -240,10 +240,8 @@ impl Module { // let location = if element_segment.passive() { // SegmentLocation::Passive // } else if element_segment.index() == 0 { - // // TODO: transform instructions // SegmentLocation::Default(Vec::new()) // } else { - // // TODO: transform instructions // SegmentLocation::WithIndex(element_segment.index(), Vec::new()) // }; @@ -492,11 +490,10 @@ impl Module { for global in self.globals.iter() { match global.read().origin { - Declared(_) => { + Declared(ref init_code) => { globals.push(elements::GlobalEntry::new( elements::GlobalType::new(global.read().content, global.read().is_mut), - // TODO: generate init expr - elements::InitExpr::empty(), + elements::InitExpr::new(self.generate_instructions(&init_code[..])), )); }, _ => continue, @@ -559,8 +556,7 @@ impl Module { element_segments.push( elements::ElementSegment::new( 0, - // TODO: generate init expr - elements::InitExpr::empty(), + elements::InitExpr::new(self.generate_instructions(&offset_expr[..])), element.value.clone(), ) ); @@ -584,11 +580,10 @@ impl Module { for func in self.funcs.iter() { match func.read().origin { - Declared(_) => { - // TODO: generate body + Declared(ref body) => { funcs.push(elements::FuncBody::new( - Vec::new(), - elements::Instructions::empty(), + body.locals.clone(), + elements::Instructions::new(self.generate_instructions(&body.code[..])), )); }, _ => continue, @@ -614,8 +609,7 @@ impl Module { data_segments.push( elements::DataSegment::new( 0, - // TODO: generate init expr - elements::InitExpr::empty(), + elements::InitExpr::new(self.generate_instructions(&offset_expr[..])), data_entry.value.clone(), ) ); From c3833efca73db37860f8e7f20e95fd9de0d1f455 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 22 Jan 2019 20:39:51 +0300 Subject: [PATCH 18/35] fix for nightly --- src/ref_list.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ref_list.rs b/src/ref_list.rs index 4679fe7..a15f644 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -2,6 +2,7 @@ use std::rc::Rc; use std::cell::RefCell; use std::vec::Vec; +use std::slice; #[derive(Debug)] enum EntryOrigin { @@ -165,7 +166,7 @@ impl RefList { &self.items[idx] } - pub fn iter(&self) -> std::slice::Iter> { + pub fn iter(&self) -> slice::Iter> { self.items.iter() } } From 62ea903c3a00c00b84bb9ae462272e62b817c3cb Mon Sep 17 00:00:00 2001 From: NikVolf Date: Wed, 23 Jan 2019 13:47:44 +0300 Subject: [PATCH 19/35] add some docs --- src/graph.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++++-- src/ref_list.rs | 31 ++++++++++++++++++ 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 66cf0de..5a3db0d 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -7,8 +7,15 @@ use std::borrow::ToOwned; use std::string::String; use std::collections::BTreeMap; +/// Imported or declared variant of the same thing. +/// +/// In WebAssembly, function/global/memory/table instances can either be +/// imported or declared internally, forming united index space. +#[derive(Debug)] pub enum ImportedOrDeclared { + /// Variant for imported instances. Imported(String, String), + /// Variant for instances declared internally in the module. Declared(T), } @@ -18,74 +25,148 @@ impl From<&elements::ImportEntry> for ImportedOrDeclared { } } +/// Function origin (imported or internal). pub type FuncOrigin = ImportedOrDeclared; +/// Global origin (imported or internal). pub type GlobalOrigin = ImportedOrDeclared>; +/// Memory origin (imported or internal). pub type MemoryOrigin = ImportedOrDeclared; +/// Table origin (imported or internal). pub type TableOrigin = ImportedOrDeclared; +/// Function body. +/// +/// Function consist of declaration (signature, i.e. type reference) +/// and the actual code. This part is the actual code. +#[derive(Debug)] pub struct FuncBody { pub locals: Vec, pub code: Vec, } +/// Function declaration. +/// +/// As with other instances, functions can be either imported or declared +/// within the module - `origin` field is handling this. +#[derive(Debug)] pub struct Func { + /// Function signature/type reference. pub type_ref: EntryRef, + /// Where this function comes from (imported or declared). pub origin: FuncOrigin, } +/// Global declaration. +/// +/// As with other instances, globals can be either imported or declared +/// within the module - `origin` field is handling this. +#[derive(Debug)] pub struct Global { pub content: elements::ValueType, pub is_mut: bool, pub origin: GlobalOrigin, } +/// Instruction. +/// +/// Some instructions don't reference any entities within the WebAssembly module, +/// while others do. This enum is for tracking references when required. +#[derive(Debug)] pub enum Instruction { + /// WebAssembly instruction that does not reference any module entities. Plain(elements::Instruction), + /// Call instruction which references the function. Call(EntryRef), + /// Indirect call instruction which references function type (function signature). CallIndirect(EntryRef, u8), + /// get_global instruction which references the global. GetGlobal(EntryRef), + /// set_global instruction which references the global. SetGlobal(EntryRef), } +/// Memory instance decriptor. +/// +/// As with other similar instances, memory instances can be either imported +/// or declared within the module - `origin` field is handling this. +#[derive(Debug)] pub struct Memory { + /// Declared limits of the table instance. pub limits: elements::ResizableLimits, + /// Origin of the memory instance (internal or imported). pub origin: MemoryOrigin, } +/// Memory instance decriptor. +/// +/// As with other similar instances, memory instances can be either imported +/// or declared within the module - `origin` field is handling this. +#[derive(Debug)] pub struct Table { - pub origin: TableOrigin, + /// Declared limits of the table instance. pub limits: elements::ResizableLimits, + /// Origin of the table instance (internal or imported). + pub origin: TableOrigin, } +/// Segment location. +/// +/// Reserved for future use. Currenty only `Default` variant is supported. +#[derive(Debug)] pub enum SegmentLocation { + /// Not used currently. Passive, + /// Default segment location with index `0`. Default(Vec), + /// Not used currently. WithIndex(u32, Vec), } +/// Data segment of data section. +#[derive(Debug)] pub struct DataSegment { + /// Location of the segment in the linear memory. pub location: SegmentLocation, + /// Raw value of the data segment. pub value: Vec, } +/// Element segment of element section. +#[derive(Debug)] pub struct ElementSegment { + /// Location of the segment in the table space. pub location: SegmentLocation, + /// Raw value (function indices) of the element segment. pub value: Vec, } +/// Export entry reference. +/// +/// Module can export function, global, table or memory instance +/// under specific name (field). +#[derive(Debug)] pub enum ExportLocal { + /// Function reference. Func(EntryRef), + /// Global reference. Global(EntryRef), + /// Table reference. Table(EntryRef
), + /// Memory reference. Memory(EntryRef), } +/// Export entry description. +#[derive(Debug)] pub struct Export { + /// Name (field) of the export entry. pub name: String, + /// What entity is exported. pub local: ExportLocal, } -#[derive(Default)] +/// Module +#[derive(Debug, Default)] pub struct Module { pub types: RefList, pub funcs: RefList, diff --git a/src/ref_list.rs b/src/ref_list.rs index a15f644..6c5026e 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -84,6 +84,8 @@ impl EntryRef { } } +/// List that tracks references and indices. +#[derive(Debug)] pub struct RefList { items: Vec>, } @@ -96,8 +98,12 @@ impl Default for RefList { impl RefList { + /// New empty list. pub fn new() -> Self { Self::default() } + /// Push new element in the list. + /// + /// Returns refernce tracking entry. pub fn push(&mut self, t: T) -> EntryRef { let idx = self.items.len(); let val: EntryRef<_> = Entry::new(t, idx).into(); @@ -105,6 +111,12 @@ impl RefList { val } + /// Start deleting. + /// + /// Start deleting some entries in the list. Returns transaction + /// that can be populated with number of removed entries. + /// When transaction is finailized, all entries are deleted and + /// internal indices of other entries are updated. pub fn begin_delete(&mut self) -> DeleteTransaction { DeleteTransaction { list: self, @@ -112,6 +124,9 @@ impl RefList { } } + /// Get entry with index (checked). + /// + /// Can return None when index out of bounts. pub fn get(&self, idx: usize) -> Option> { self.items.get(idx).cloned() } @@ -134,14 +149,19 @@ impl RefList { } } + /// Delete several items. pub fn delete(&mut self, indices: &[usize]) { self.done_delete(indices) } + /// Delete one item. pub fn delete_one(&mut self, index: usize) { self.done_delete(&[index]) } + /// Initialize from slice. + /// + /// Slice members are cloned. pub fn from_slice(list: &[T]) -> Self where T: Clone { @@ -154,23 +174,32 @@ impl RefList { res } + /// Length of the list. pub fn len(&self) -> usize { self.items.len() } + /// Clone entry (reference counting object to item) by index. + /// + /// Will panic if index out of bounds. pub fn clone_ref(&self, idx: usize) -> EntryRef { self.items[idx].clone() } + /// Get reference to entry by index. + /// + /// Will panic if index out of bounds. pub fn get_ref(&self, idx: usize) -> &EntryRef { &self.items[idx] } + /// Iterate through entries. pub fn iter(&self) -> slice::Iter> { self.items.iter() } } +/// Delete transaction. #[must_use] pub struct DeleteTransaction<'a, T> { list: &'a mut RefList, @@ -178,12 +207,14 @@ pub struct DeleteTransaction<'a, T> { } impl<'a, T> DeleteTransaction<'a, T> { + /// Add new element to the delete list. pub fn push(self, idx: usize) -> Self { let mut tx = self; tx.deleted.push(idx); tx } + /// Commit transaction. pub fn done(self) { let indices = self.deleted; let list = self.list; From bb9832dba1a7346700aead71d9d5e2f309081d06 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Wed, 23 Jan 2019 13:57:26 +0300 Subject: [PATCH 20/35] more docs and warnings --- src/graph.rs | 15 +++++++++++++++ src/ref_list.rs | 14 +++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/graph.rs b/src/graph.rs index 5a3db0d..71a3670 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -1,5 +1,7 @@ //! Wasm binary graph format +#![warn(missing_docs)] + use parity_wasm::elements; use super::ref_list::{RefList, EntryRef}; use std::vec::Vec; @@ -168,15 +170,25 @@ pub struct Export { /// Module #[derive(Debug, Default)] pub struct Module { + /// Refence-tracking list of types. pub types: RefList, + /// Refence-tracking list of funcs. pub funcs: RefList, + /// Refence-tracking list of memory instances. pub memory: RefList, + /// Refence-tracking list of table instances. pub tables: RefList
, + /// Refence-tracking list of globals. pub globals: RefList, + /// Reference to start function. pub start: Option>, + /// References to exported objects. pub exports: Vec, + /// List of element segments. pub elements: Vec, + /// List of data segments. pub data: Vec, + /// Other module functions that are not decoded or processed. pub other: BTreeMap, } @@ -210,6 +222,7 @@ impl Module { }).collect() } + /// Initialize module from parity-wasm `Module`. pub fn from_elements(module: &elements::Module) -> Self { let mut idx = 0; @@ -721,10 +734,12 @@ fn custom_round( } } +/// New module from parity-wasm `Module` pub fn parse(wasm: &[u8]) -> Module { Module::from_elements(&::parity_wasm::deserialize_buffer(wasm).expect("failed to parse wasm")) } +/// Generate parity-wasm `Module` pub fn generate(f: &Module) -> Vec { let pm = f.generate(); ::parity_wasm::serialize(pm).expect("failed to generate wasm") diff --git a/src/ref_list.rs b/src/ref_list.rs index 6c5026e..739ab43 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -1,3 +1,4 @@ +#![warn(missing_docs)] use std::rc::Rc; use std::cell::RefCell; @@ -16,6 +17,7 @@ impl From for EntryOrigin { } } +/// Reference counting, link-handling object. #[derive(Debug)] pub struct Entry { val: T, @@ -23,13 +25,15 @@ pub struct Entry { } impl Entry { - fn new(val: T, index: usize) -> Entry { + /// New entity. + pub fn new(val: T, index: usize) -> Entry { Entry { val: val, index: EntryOrigin::Index(index), } } + /// Index of the element within the reference list. pub fn order(&self) -> Option { match self.index { EntryOrigin::Detached => None, @@ -52,6 +56,8 @@ impl ::std::ops::DerefMut for Entry { } } +/// Reference to the entry in the rerence list. +#[derive(Debug)] pub struct EntryRef(Rc>>); impl Clone for EntryRef { @@ -67,18 +73,24 @@ impl From> for EntryRef { } impl EntryRef { + /// Read the reference data. pub fn read(&self) -> ::std::cell::Ref> { self.0.borrow() } + /// Try to modify internal content of the referenced object. + /// + /// May panic if it is already borrowed. pub fn write(&self) -> ::std::cell::RefMut> { self.0.borrow_mut() } + /// Index of the element within the reference list. pub fn order(&self) -> Option { self.0.borrow().order() } + /// Number of active links to this entity. pub fn link_count(&self) -> usize { Rc::strong_count(&self.0) - 1 } From 75043814198cf9f29f79f6852d5e612a14db607c Mon Sep 17 00:00:00 2001 From: NikVolf Date: Wed, 23 Jan 2019 14:44:32 +0300 Subject: [PATCH 21/35] fix linking for elements --- src/graph.rs | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 71a3670..0202315 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -139,7 +139,7 @@ pub struct ElementSegment { /// Location of the segment in the table space. pub location: SegmentLocation, /// Raw value (function indices) of the element segment. - pub value: Vec, + pub value: Vec>, } /// Export entry reference. @@ -344,8 +344,13 @@ impl Module { res.map_instructions(element_segment.offset().code()) ); + let funcs_map = element_segment + .members().iter() + .map(|idx| res.funcs.clone_ref(*idx as usize)) + .collect::>>(); + res.elements.push(ElementSegment { - value: element_segment.members().to_vec(), + value: funcs_map, location: location, }); } @@ -647,11 +652,15 @@ impl Module { for element in self.elements.iter() { match element.location { SegmentLocation::Default(ref offset_expr) => { + let elements_map = element.value.iter() + .map(|f| f.order().expect("Detached func in element segment!") as u32) + .collect(); + element_segments.push( elements::ElementSegment::new( 0, elements::InitExpr::new(self.generate_instructions(&offset_expr[..])), - element.value.clone(), + elements_map, ) ); }, @@ -773,29 +782,5 @@ mod tests { assert_eq!(f.funcs.get_ref(0).link_count(), 1); } - #[test] - #[ignore] - fn simple_round_trip() { - let wat = r#" - (module - (type (func)) - (import "env" "f1" (func (type 0))) - (memory 0 1) - (export "simple" (func 0)) - ) - "#; - let wasm = wabt::wat2wasm(wat).expect("Failed to read fixture"); - - let f = super::parse(&wasm[..]); - let wasm_new = super::generate(&f); - - let wat_new = wabt::wasm2wat(&wasm_new).expect("Failed to generate expectation"); - - if &wasm_new[..] != &wasm[..] { - panic!( - "{}\n != \n{}", wat, wat_new - ); - } - } } \ No newline at end of file From f5890c2c7b135335ca985d00cbe624fecd9a70c3 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Wed, 23 Jan 2019 15:00:27 +0300 Subject: [PATCH 22/35] more complex test --- src/graph.rs | 88 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 16 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 0202315..882a653 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -759,27 +759,83 @@ mod tests { extern crate wabt; + use parity_wasm::elements; + + fn load_sample(wat: &'static str) -> super::Module { + super::parse(&wabt::wat2wasm(wat).expect("faled to parse wat!")[..]) + } + #[test] fn smoky() { - let wasm = wabt::wat2wasm(r#" - (module - (type (func)) - (func (type 0)) - (memory 0 1) - (export "simple" (func 0)) - ) - "#).expect("Failed to read fixture"); + let sample = load_sample(r#" +(module + (type (func)) + (func (type 0)) + (memory 0 1) + (export "simple" (func 0)) +) +"# + ); - let f = super::parse(&wasm[..]); + assert_eq!(sample.types.len(), 1); + assert_eq!(sample.funcs.len(), 1); + assert_eq!(sample.tables.len(), 0); + assert_eq!(sample.memory.len(), 1); + assert_eq!(sample.exports.len(), 1); - assert_eq!(f.types.len(), 1); - assert_eq!(f.funcs.len(), 1); - assert_eq!(f.tables.len(), 0); - assert_eq!(f.memory.len(), 1); - assert_eq!(f.exports.len(), 1); + assert_eq!(sample.types.get_ref(0).link_count(), 1); + assert_eq!(sample.funcs.get_ref(0).link_count(), 1); + } + + #[test] + fn table() { + let mut sample = load_sample(r#" +(module + (import "env" "foo" (func $foo)) + (func (param i32) + get_local 0 + i32.const 0 + call $i32.add + drop + ) + (func $i32.add (export "i32.add") (param i32 i32) (result i32) + get_local 0 + get_local 1 + i32.add + ) + (table 10 anyfunc) + + ;; Refer all types of functions: imported, defined not exported and defined exported. + (elem (i32.const 0) 0 1 2) +)"# + ); + + { + let element_func = &sample.elements[0].value[1]; + let rfunc = element_func.read(); + let rtype = &**rfunc.type_ref.read(); + let elements::Type::Function(ref ftype) = rtype; + + // it's func#1 in the function space + assert_eq!(rfunc.order(), Some(1)); + // it's type#1 + assert_eq!(ftype.params().len(), 1); + } + + sample.funcs.begin_delete().push(0).done(); + + { + let element_func = &sample.elements[0].value[1]; + let rfunc = element_func.read(); + let rtype = &**rfunc.type_ref.read(); + let elements::Type::Function(ref ftype) = rtype; + + /// import deleted so now it's func #0 + assert_eq!(rfunc.order(), Some(0)); + /// type should be the same, #1 + assert_eq!(ftype.params().len(), 1); + } - assert_eq!(f.types.get_ref(0).link_count(), 1); - assert_eq!(f.funcs.get_ref(0).link_count(), 1); } From 1bc4973e6e07aad086e8e6769ea81ec977583648 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 24 Jan 2019 12:05:20 +0300 Subject: [PATCH 23/35] insert api in ref_list --- src/graph.rs | 3 - src/ref_list.rs | 195 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 194 insertions(+), 4 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 882a653..8d933ab 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -835,8 +835,5 @@ mod tests { /// type should be the same, #1 assert_eq!(ftype.params().len(), 1); } - } - - } \ No newline at end of file diff --git a/src/ref_list.rs b/src/ref_list.rs index 739ab43..8de3813 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -33,6 +33,14 @@ impl Entry { } } + /// New detached entry. + pub fn new_detached(val: T) -> Entry { + Entry { + val: val, + index: EntryOrigin::Detached, + } + } + /// Index of the element within the reference list. pub fn order(&self) -> Option { match self.index { @@ -136,6 +144,33 @@ impl RefList { } } + /// Start inserting. + /// + /// Start inserting some entries in the list at he designated position. + /// Returns transaction that can be populated with some entries. + /// When transaction is finailized, all entries are inserted and + /// internal indices of other entries might be updated. + pub fn begin_insert(&mut self, at: usize) -> InsertTransaction { + InsertTransaction { + at: at, + list: self, + items: Vec::new(), + } + } + + /// Start inserting after the condition match (or at the end). + /// + /// Start inserting some entries in the list at he designated position. + /// Returns transaction that can be populated with some entries. + /// When transaction is finailized, all entries are inserted and + /// internal indices of other entries might be updated. + pub fn begin_insert_after(&mut self, mut f: F) -> InsertTransaction + where F : FnMut(&T) -> bool + { + let pos = self.items.iter().position(|rf| f(&**rf.read())).map(|x| x + 1).unwrap_or(self.items.len()); + self.begin_insert(pos) + } + /// Get entry with index (checked). /// /// Can return None when index out of bounts. @@ -161,6 +196,19 @@ impl RefList { } } + fn done_insert(&mut self, index: usize, mut items: Vec>) { + let mut offset = 0; + for item in items.drain(..) { + item.write().index = EntryOrigin::Index(index + offset); + self.items.insert(index + offset, item); + offset += 1; + } + + for idx in (index+offset)..self.items.len() { + self.get_ref(idx).write().index = EntryOrigin::Index(idx); + } + } + /// Delete several items. pub fn delete(&mut self, indices: &[usize]) { self.done_delete(indices) @@ -234,6 +282,32 @@ impl<'a, T> DeleteTransaction<'a, T> { } } +/// Insert transaction +#[must_use] +pub struct InsertTransaction<'a, T> { + at: usize, + list: &'a mut RefList, + items: Vec>, +} + +impl<'a, T> InsertTransaction<'a, T> { + /// Add new element to the delete list. + pub fn push(&mut self, val: T) -> EntryRef { + let val: EntryRef<_> = Entry::new_detached(val).into(); + self.items.push(val.clone()); + val + } + + /// Commit transaction. + pub fn done(self) { + let items = self.items; + let list = self.list; + let at = self.at; + list.done_insert(at, items); + } +} + + #[cfg(test)] mod tests { @@ -246,7 +320,7 @@ mod tests { let item20 = list.push(20); let item30 = list.push(30); - assert_eq!(item10.order(), Some(0usize)); + assert_eq!(item10.order(), Some(0)); assert_eq!(item20.order(), Some(1)); assert_eq!(item30.order(), Some(2)); @@ -268,4 +342,123 @@ mod tests { assert_eq!(item30.order(), Some(1)); assert_eq!(item20.order(), None); } + + #[test] + fn insert() { + let mut list = RefList::::new(); + let item10 = list.push(10); + let item20 = list.push(20); + let item30 = list.push(30); + + let mut insert_tx = list.begin_insert(2); + let item23 = insert_tx.push(23); + let item27 = insert_tx.push(27); + insert_tx.done(); + + assert_eq!(item10.order(), Some(0)); + assert_eq!(item20.order(), Some(1)); + assert_eq!(item23.order(), Some(2)); + assert_eq!(item27.order(), Some(3)); + assert_eq!(item30.order(), Some(4)); + } + + #[test] + fn insert_end() { + let mut list = RefList::::new(); + + let mut insert_tx = list.begin_insert(0); + let item0 = insert_tx.push(0); + insert_tx.done(); + + assert_eq!(item0.order(), Some(0)); + } + + #[test] + fn insert_end_more() { + let mut list = RefList::::new(); + let item0 = list.push(0); + + let mut insert_tx = list.begin_insert(1); + let item1 = insert_tx.push(1); + insert_tx.done(); + + assert_eq!(item0.order(), Some(0)); + assert_eq!(item1.order(), Some(1)); + } + + #[test] + fn insert_after() { + let mut list = RefList::::new(); + let item10 = list.push(10); + let item20 = list.push(20); + let item30 = list.push(30); + + let mut insert_tx = list.begin_insert_after(|i| *i == 20); + + let item23 = insert_tx.push(23); + let item27 = insert_tx.push(27); + insert_tx.done(); + + assert_eq!(item10.order(), Some(0)); + assert_eq!(item20.order(), Some(1)); + assert_eq!(item23.order(), Some(2)); + assert_eq!(item27.order(), Some(3)); + assert_eq!(item30.order(), Some(4)); + } + + #[test] + fn insert_after_none() { + let mut list = RefList::::new(); + let item10 = list.push(10); + let item20 = list.push(20); + let item30 = list.push(30); + + let mut insert_tx = list.begin_insert_after(|i| *i == 50); + + let item55 = insert_tx.push(23); + let item59 = insert_tx.push(27); + insert_tx.done(); + + assert_eq!(item10.order(), Some(0)); + assert_eq!(item20.order(), Some(1)); + assert_eq!(item30.order(), Some(2)); + assert_eq!(item55.order(), Some(3)); + assert_eq!(item59.order(), Some(4)); + } + + #[test] + fn insert_after_empty() { + let mut list = RefList::::new(); + + let mut insert_tx = list.begin_insert_after(|x| *x == 100); + let item0 = insert_tx.push(0); + insert_tx.done(); + + assert_eq!(item0.order(), Some(0)); + } + + #[test] + fn insert_more() { + let mut list = RefList::::new(); + let item10 = list.push(10); + let item20 = list.push(20); + let item30 = list.push(30); + let item40 = list.push(10); + let item50 = list.push(20); + let item60 = list.push(30); + + let mut insert_tx = list.begin_insert(3); + let item35 = insert_tx.push(23); + let item37 = insert_tx.push(27); + insert_tx.done(); + + assert_eq!(item10.order(), Some(0)); + assert_eq!(item20.order(), Some(1)); + assert_eq!(item30.order(), Some(2)); + assert_eq!(item35.order(), Some(3)); + assert_eq!(item37.order(), Some(4)); + assert_eq!(item40.order(), Some(5)); + assert_eq!(item50.order(), Some(6)); + assert_eq!(item60.order(), Some(7)); + } } \ No newline at end of file From 8413e562cd16fa093f14603e9069ad1613836d86 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 24 Jan 2019 12:37:03 +0300 Subject: [PATCH 24/35] more insert api and graph module logic upon --- src/graph.rs | 47 +++++++++++++++++++++++++++++++++++++++++- src/ref_list.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 8d933ab..b2578c9 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -765,6 +765,14 @@ mod tests { super::parse(&wabt::wat2wasm(wat).expect("faled to parse wat!")[..]) } + fn validate_sample(module: &super::Module) { + let binary = super::generate(module); + wabt::Module::read_binary(&binary, &Default::default()) + .expect("Wabt failed to read final binary") + .validate() + .expect("Invalid module"); + } + #[test] fn smoky() { let sample = load_sample(r#" @@ -789,7 +797,7 @@ mod tests { #[test] fn table() { - let mut sample = load_sample(r#" + let mut sample = load_sample(r#" (module (import "env" "foo" (func $foo)) (func (param i32) @@ -836,4 +844,41 @@ mod tests { assert_eq!(ftype.params().len(), 1); } } + + #[test] + fn new_import() { + let mut sample = load_sample(r#" +(module + (type (;0;) (func)) + (type (;1;) (func (param i32 i32) (result i32))) + (import "env" "foo" (func (type 1))) + (func (param i32) + get_local 0 + i32.const 0 + call 0 + drop + ) +)"# + ); + + { + let type_ref_0 = sample.types.clone_ref(0); + + let mut tx = sample.funcs.begin_insert_not_until( + |f| match f.origin { + super::ImportedOrDeclared::Imported(_, _) => false, + _ => true, + } + ); + + tx.push(super::Func { + type_ref: type_ref_0, + origin: super::ImportedOrDeclared::Imported("env".to_owned(), "bar".to_owned()), + }); + + tx.done(); + } + + validate_sample(&sample); + } } \ No newline at end of file diff --git a/src/ref_list.rs b/src/ref_list.rs index 8de3813..2d0d3f2 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -158,7 +158,7 @@ impl RefList { } } - /// Start inserting after the condition match (or at the end). + /// Start inserting after the condition first matches (or at the end). /// /// Start inserting some entries in the list at he designated position. /// Returns transaction that can be populated with some entries. @@ -171,6 +171,19 @@ impl RefList { self.begin_insert(pos) } + /// Start inserting after the condition first no longer true (or at the end). + /// + /// Start inserting some entries in the list at he designated position. + /// Returns transaction that can be populated with some entries. + /// When transaction is finailized, all entries are inserted and + /// internal indices of other entries might be updated. + pub fn begin_insert_not_until(&mut self, mut f: F) -> InsertTransaction + where F : FnMut(&T) -> bool + { + let pos = self.items.iter().take_while(|rf| f(&**rf.read())).count(); + self.begin_insert(pos) + } + /// Get entry with index (checked). /// /// Can return None when index out of bounts. @@ -406,6 +419,26 @@ mod tests { assert_eq!(item30.order(), Some(4)); } + #[test] + fn insert_not_until() { + let mut list = RefList::::new(); + let item10 = list.push(10); + let item20 = list.push(20); + let item30 = list.push(30); + + let mut insert_tx = list.begin_insert_not_until(|i| *i <= 20); + + let item23 = insert_tx.push(23); + let item27 = insert_tx.push(27); + insert_tx.done(); + + assert_eq!(item10.order(), Some(0)); + assert_eq!(item20.order(), Some(1)); + assert_eq!(item23.order(), Some(2)); + assert_eq!(item27.order(), Some(3)); + assert_eq!(item30.order(), Some(4)); + } + #[test] fn insert_after_none() { let mut list = RefList::::new(); @@ -426,6 +459,26 @@ mod tests { assert_eq!(item59.order(), Some(4)); } + #[test] + fn insert_not_until_none() { + let mut list = RefList::::new(); + let item10 = list.push(10); + let item20 = list.push(20); + let item30 = list.push(30); + + let mut insert_tx = list.begin_insert_not_until(|i| *i < 50); + + let item55 = insert_tx.push(23); + let item59 = insert_tx.push(27); + insert_tx.done(); + + assert_eq!(item10.order(), Some(0)); + assert_eq!(item20.order(), Some(1)); + assert_eq!(item30.order(), Some(2)); + assert_eq!(item55.order(), Some(3)); + assert_eq!(item59.order(), Some(4)); + } + #[test] fn insert_after_empty() { let mut list = RefList::::new(); From 0a78a1ab8dd8afc34b7a23e12203ae0ac1b8a9df Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 24 Jan 2019 14:35:05 +0300 Subject: [PATCH 25/35] complicate test --- src/graph.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index b2578c9..7411d70 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -368,6 +368,8 @@ impl Module { }, _ => unreachable!("All declared functions added after imported; qed"), } + + idx += 1; } }, elements::Section::Data(data_section) => { @@ -858,6 +860,10 @@ mod tests { call 0 drop ) + (func (type 0) + i32.const 0 + call 1 + ) )"# ); @@ -866,17 +872,19 @@ mod tests { let mut tx = sample.funcs.begin_insert_not_until( |f| match f.origin { - super::ImportedOrDeclared::Imported(_, _) => false, - _ => true, + super::ImportedOrDeclared::Imported(_, _) => true, + _ => false, } ); - tx.push(super::Func { + let new_import_func = tx.push(super::Func { type_ref: type_ref_0, origin: super::ImportedOrDeclared::Imported("env".to_owned(), "bar".to_owned()), }); tx.done(); + + assert_eq!(new_import_func.order(), Some(1)); } validate_sample(&sample); From cda99e70dace98b4ca3cbe29c77e4988d22e7652 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 24 Jan 2019 14:42:53 +0300 Subject: [PATCH 26/35] add much more complicated assertion --- src/graph.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/graph.rs b/src/graph.rs index 7411d70..1fa3099 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -869,6 +869,7 @@ mod tests { { let type_ref_0 = sample.types.clone_ref(0); + let declared_func_2 = sample.funcs.clone_ref(2); let mut tx = sample.funcs.begin_insert_not_until( |f| match f.origin { @@ -885,6 +886,20 @@ mod tests { tx.done(); assert_eq!(new_import_func.order(), Some(1)); + assert_eq!(declared_func_2.order(), Some(3)); + assert_eq!( + match &declared_func_2.read().origin { + super::ImportedOrDeclared::Declared(ref body) => { + match body.code[1] { + super::Instruction::Call(ref called_func) => called_func.order(), + _ => panic!("instruction #2 should be a call!"), + } + }, + _ => panic!("func #4 should be declared!"), + }, + Some(2), + "Call should be recalculated to 2" + ); } validate_sample(&sample); From d695703146145a6c7b805103602232d2c9cf73ad Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 24 Jan 2019 15:07:57 +0300 Subject: [PATCH 27/35] more complicated opt and delete tests --- src/graph.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++++- src/ref_list.rs | 41 ++++++++++++++++++++++++++---- 2 files changed, 102 insertions(+), 6 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 1fa3099..5cc65ea 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -895,7 +895,7 @@ mod tests { _ => panic!("instruction #2 should be a call!"), } }, - _ => panic!("func #4 should be declared!"), + _ => panic!("func #3 should be declared!"), }, Some(2), "Call should be recalculated to 2" @@ -904,4 +904,69 @@ mod tests { validate_sample(&sample); } + + #[test] + fn simple_opt() { + let mut sample = load_sample(r#" +(module + (type (;0;) (func)) + (type (;1;) (func (param i32 i32) (result i32))) + (type (;2;) (func (param i32 i32) (result i32))) + (type (;3;) (func (param i32 i32) (result i32))) + (import "env" "foo" (func (type 1))) + (import "env" "foo2" (func (type 2))) + (import "env" "foo3" (func (type 3))) + (func (type 0) + i32.const 1 + i32.const 1 + call 0 + drop + ) + (func (type 0) + i32.const 2 + i32.const 2 + call 1 + drop + ) + (func (type 0) + i32.const 3 + i32.const 3 + call 2 + drop + ) + (func (type 0) + call 3 + ) + +)"# + ); + + validate_sample(&sample); + + // we'll delete functions #4 and #5, nobody references it so it should be fine; + + sample.funcs.begin_delete().push(4).push(5).done(); + validate_sample(&sample); + + // now we'll delete functions #1 and #2 (imported and called from the deleted above), + // should also be fine + sample.funcs.begin_delete().push(1).push(2).done(); + validate_sample(&sample); + + // now the last declared function left should call another one before it (which is index #1) + let declared_func_2 = sample.funcs.clone_ref(2); + assert_eq!( + match &declared_func_2.read().origin { + super::ImportedOrDeclared::Declared(ref body) => { + match body.code[0] { + super::Instruction::Call(ref called_func) => called_func.order(), + ref wrong_instruction => panic!("instruction #2 should be a call but got {:?}!", wrong_instruction), + } + }, + _ => panic!("func #0 should be declared!"), + }, + Some(1), + "Call should be recalculated to 1" + ); + } } \ No newline at end of file diff --git a/src/ref_list.rs b/src/ref_list.rs index 2d0d3f2..8c35ed2 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -192,11 +192,6 @@ impl RefList { } fn done_delete(&mut self, indices: &[usize]) { - for idx in indices { - let mut detached = self.items.remove(*idx); - detached.write().index = EntryOrigin::Detached; - } - for index in 0..self.items.len() { let mut next_entry = self.items.get_mut(index).expect("Checked above; qed").write(); let total_less = indices.iter() @@ -207,6 +202,14 @@ impl RefList { EntryOrigin::Index(ref mut idx) => { *idx -= total_less; }, }; } + + let mut total_removed = 0; + for idx in indices { + let mut detached = self.items.remove(*idx - total_removed); + detached.write().index = EntryOrigin::Detached; + total_removed += 1; + } + } fn done_insert(&mut self, index: usize, mut items: Vec>) { @@ -356,6 +359,34 @@ mod tests { assert_eq!(item20.order(), None); } + #[test] + fn complex_delete() { + let mut list = RefList::::new(); + let item00 = list.push(0); + let item10 = list.push(10); + let item20 = list.push(20); + let item30 = list.push(30); + let item40 = list.push(40); + let item50 = list.push(50); + let item60 = list.push(60); + let item70 = list.push(70); + let item80 = list.push(80); + let item90 = list.push(90); + + list.begin_delete().push(1).push(2).push(4).push(6).done(); + + assert_eq!(item00.order(), Some(0)); + assert_eq!(item10.order(), None); + assert_eq!(item20.order(), None); + assert_eq!(item30.order(), Some(1)); + assert_eq!(item40.order(), None); + assert_eq!(item50.order(), Some(2)); + assert_eq!(item60.order(), None); + assert_eq!(item70.order(), Some(3)); + assert_eq!(item80.order(), Some(4)); + assert_eq!(item90.order(), Some(5)); + } + #[test] fn insert() { let mut list = RefList::::new(); From d8428327d5303e08d02c90ed2ee21ac4945767ab Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 24 Jan 2019 15:18:16 +0300 Subject: [PATCH 28/35] simpler imports --- src/graph.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 5cc65ea..17ebb50 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -4,10 +4,12 @@ use parity_wasm::elements; use super::ref_list::{RefList, EntryRef}; -use std::vec::Vec; -use std::borrow::ToOwned; -use std::string::String; -use std::collections::BTreeMap; +use std::{ + vec::Vec, + borrow::ToOwned, + string::String, + collections::BTreeMap, +}; /// Imported or declared variant of the same thing. /// From 3e635514e4f9fdf7f37a6287b98a22cfe595696e Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 24 Jan 2019 15:22:41 +0300 Subject: [PATCH 29/35] some reformatting --- src/ref_list.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ref_list.rs b/src/ref_list.rs index 8c35ed2..eae1ba1 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -167,7 +167,11 @@ impl RefList { pub fn begin_insert_after(&mut self, mut f: F) -> InsertTransaction where F : FnMut(&T) -> bool { - let pos = self.items.iter().position(|rf| f(&**rf.read())).map(|x| x + 1).unwrap_or(self.items.len()); + let pos = self + .items.iter() + .position(|rf| f(&**rf.read())).map(|x| x + 1) + .unwrap_or(self.items.len()); + self.begin_insert(pos) } @@ -209,7 +213,6 @@ impl RefList { detached.write().index = EntryOrigin::Detached; total_removed += 1; } - } fn done_insert(&mut self, index: usize, mut items: Vec>) { From 33785674dca8a6b2d2a74a5cdd248564dd728dd5 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 24 Jan 2019 15:32:14 +0300 Subject: [PATCH 30/35] simplify code --- src/ref_list.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ref_list.rs b/src/ref_list.rs index eae1ba1..12d2dbc 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -196,12 +196,12 @@ impl RefList { } fn done_delete(&mut self, indices: &[usize]) { - for index in 0..self.items.len() { - let mut next_entry = self.items.get_mut(index).expect("Checked above; qed").write(); + for mut entry in self.items.iter_mut() { + let mut entry = entry.write(); let total_less = indices.iter() - .take_while(|x| **x < next_entry.order().expect("Items in the list always have order; qed")) + .take_while(|x| **x < entry.order().expect("Items in the list always have order; qed")) .count(); - match next_entry.index { + match entry.index { EntryOrigin::Detached => unreachable!("Items in the list always have order!"), EntryOrigin::Index(ref mut idx) => { *idx -= total_less; }, }; From 728c93536749d37fa3d7efac279846c0c15f3b4f Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 24 Jan 2019 15:36:09 +0300 Subject: [PATCH 31/35] alter some tests to show correspondence --- src/ref_list.rs | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/ref_list.rs b/src/ref_list.rs index 12d2dbc..f33e882 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -335,14 +335,17 @@ mod tests { #[test] fn order() { let mut list = RefList::::new(); + let item00 = list.push(0); let item10 = list.push(10); let item20 = list.push(20); let item30 = list.push(30); - assert_eq!(item10.order(), Some(0)); - assert_eq!(item20.order(), Some(1)); - assert_eq!(item30.order(), Some(2)); + assert_eq!(item00.order(), Some(0)); + assert_eq!(item10.order(), Some(1)); + assert_eq!(item20.order(), Some(2)); + assert_eq!(item30.order(), Some(3)); + assert_eq!(**item00.read(), 0); assert_eq!(**item10.read(), 10); assert_eq!(**item20.read(), 20); assert_eq!(**item30.read(), 30); @@ -351,14 +354,18 @@ mod tests { #[test] fn delete() { let mut list = RefList::::new(); + let item00 = list.push(0); let item10 = list.push(10); let item20 = list.push(20); let item30 = list.push(30); - list.begin_delete().push(1).done(); + list.begin_delete().push(2).done(); - assert_eq!(item10.order(), Some(0)); - assert_eq!(item30.order(), Some(1)); + assert_eq!(item00.order(), Some(0)); + assert_eq!(item10.order(), Some(1)); + assert_eq!(item30.order(), Some(2)); + + // but this was detached assert_eq!(item20.order(), None); } @@ -393,20 +400,22 @@ mod tests { #[test] fn insert() { let mut list = RefList::::new(); + let item00 = list.push(0); let item10 = list.push(10); let item20 = list.push(20); let item30 = list.push(30); - let mut insert_tx = list.begin_insert(2); + let mut insert_tx = list.begin_insert(3); let item23 = insert_tx.push(23); let item27 = insert_tx.push(27); insert_tx.done(); - assert_eq!(item10.order(), Some(0)); - assert_eq!(item20.order(), Some(1)); - assert_eq!(item23.order(), Some(2)); - assert_eq!(item27.order(), Some(3)); - assert_eq!(item30.order(), Some(4)); + assert_eq!(item00.order(), Some(0)); + assert_eq!(item10.order(), Some(1)); + assert_eq!(item20.order(), Some(2)); + assert_eq!(item23.order(), Some(3)); + assert_eq!(item27.order(), Some(4)); + assert_eq!(item30.order(), Some(5)); } #[test] @@ -436,6 +445,7 @@ mod tests { #[test] fn insert_after() { let mut list = RefList::::new(); + let item00 = list.push(0); let item10 = list.push(10); let item20 = list.push(20); let item30 = list.push(30); @@ -446,11 +456,12 @@ mod tests { let item27 = insert_tx.push(27); insert_tx.done(); - assert_eq!(item10.order(), Some(0)); - assert_eq!(item20.order(), Some(1)); - assert_eq!(item23.order(), Some(2)); - assert_eq!(item27.order(), Some(3)); - assert_eq!(item30.order(), Some(4)); + assert_eq!(item00.order(), Some(0)); + assert_eq!(item10.order(), Some(1)); + assert_eq!(item20.order(), Some(2)); + assert_eq!(item23.order(), Some(3)); + assert_eq!(item27.order(), Some(4)); + assert_eq!(item30.order(), Some(5)); } #[test] From 91036c0affac3c4f83d839815fc12a615ba365fa Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 24 Jan 2019 16:04:00 +0300 Subject: [PATCH 32/35] avoid panics when creating representation --- src/graph.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 17ebb50..337882c 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -29,6 +29,15 @@ impl From<&elements::ImportEntry> for ImportedOrDeclared { } } +/// Error for this module +#[derive(Debug)] +pub enum Error { + /// Inconsistent source representation + InconsistentSource, + /// Format error + Format(elements::Error), +} + /// Function origin (imported or internal). pub type FuncOrigin = ImportedOrDeclared; /// Global origin (imported or internal). @@ -225,7 +234,7 @@ impl Module { } /// Initialize module from parity-wasm `Module`. - pub fn from_elements(module: &elements::Module) -> Self { + pub fn from_elements(module: &elements::Module) -> Result { let mut idx = 0; let mut res = Module::default(); @@ -242,7 +251,7 @@ impl Module { match *entry.external() { elements::External::Function(f) => { res.funcs.push(Func { - type_ref: res.types.get(f as usize).expect("validated; qed").clone(), + type_ref: res.types.get(f as usize).ok_or(Error::InconsistentSource)?.clone(), origin: entry.into(), }); imported_functions += 1; @@ -272,7 +281,8 @@ impl Module { elements::Section::Function(function_section) => { for f in function_section.entries() { res.funcs.push(Func { - type_ref: res.types.get(f.type_ref() as usize).expect("validated; qed").clone(), + type_ref: res.types.get(f.type_ref() as usize) + .ok_or(Error::InconsistentSource)?.clone(), origin: ImportedOrDeclared::Declared(FuncBody { locals: Vec::new(), // code will be populated later @@ -368,7 +378,7 @@ impl Module { body.code = code; body.locals = func_body.locals().iter().cloned().collect(); }, - _ => unreachable!("All declared functions added after imported; qed"), + _ => { return Err(Error::InconsistentSource); } } idx += 1; @@ -395,7 +405,7 @@ impl Module { idx += 1; } - res + Ok(res) } fn generate(&self) -> elements::Module { @@ -748,8 +758,8 @@ fn custom_round( } /// New module from parity-wasm `Module` -pub fn parse(wasm: &[u8]) -> Module { - Module::from_elements(&::parity_wasm::deserialize_buffer(wasm).expect("failed to parse wasm")) +pub fn parse(wasm: &[u8]) -> Result { + Module::from_elements(&::parity_wasm::deserialize_buffer(wasm).map_err(Error::Format)?) } /// Generate parity-wasm `Module` @@ -767,6 +777,7 @@ mod tests { fn load_sample(wat: &'static str) -> super::Module { super::parse(&wabt::wat2wasm(wat).expect("faled to parse wat!")[..]) + .expect("error making representation") } fn validate_sample(module: &super::Module) { From ad83ad17ee589e5fd58c2ed4860bb064b326e93c Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 24 Jan 2019 16:10:39 +0300 Subject: [PATCH 33/35] avoid panic when generating format --- src/graph.rs | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index 337882c..dfef028 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -36,6 +36,8 @@ pub enum Error { InconsistentSource, /// Format error Format(elements::Error), + /// Detached entry + DetachedEntry, } /// Function origin (imported or internal). @@ -408,7 +410,7 @@ impl Module { Ok(res) } - fn generate(&self) -> elements::Module { + fn generate(&self) -> Result { use self::ImportedOrDeclared::*; let mut idx = 0; @@ -445,8 +447,7 @@ impl Module { module.to_owned(), field.to_owned(), elements::External::Function( - func.read().type_ref.order() - .expect("detached func encountered somehow!") as u32 + func.read().type_ref.order().ok_or(Error::DetachedEntry)? as u32 ), ) ) @@ -533,8 +534,7 @@ impl Module { match func.read().origin { Declared(_) => { funcs.push(elements::Func::new( - func.read().type_ref.order() - .expect("detached func encountered somehow!") as u32 + func.read().type_ref.order().ok_or(Error::DetachedEntry)? as u32 )); }, _ => continue, @@ -628,16 +628,16 @@ impl Module { for export in self.exports.iter() { let internal = match export.local { ExportLocal::Func(ref func_ref) => { - elements::Internal::Function(func_ref.order().expect("detached func ref") as u32) + elements::Internal::Function(func_ref.order().ok_or(Error::DetachedEntry)? as u32) }, ExportLocal::Global(ref global_ref) => { - elements::Internal::Global(global_ref.order().expect("detached global ref") as u32) + elements::Internal::Global(global_ref.order().ok_or(Error::DetachedEntry)? as u32) }, ExportLocal::Table(ref table_ref) => { - elements::Internal::Table(table_ref.order().expect("detached table ref") as u32) + elements::Internal::Table(table_ref.order().ok_or(Error::DetachedEntry)? as u32) }, ExportLocal::Memory(ref memory_ref) => { - elements::Internal::Memory(memory_ref.order().expect("detached memory ref") as u32) + elements::Internal::Memory(memory_ref.order().ok_or(Error::DetachedEntry)? as u32) }, }; @@ -653,7 +653,7 @@ impl Module { if let Some(ref func_ref) = self.start { // START SECTION (8) sections.push(elements::Section::Start( - func_ref.order().expect("detached start func") as u32 + func_ref.order().ok_or(Error::DetachedEntry)? as u32 )); } @@ -666,9 +666,10 @@ impl Module { for element in self.elements.iter() { match element.location { SegmentLocation::Default(ref offset_expr) => { - let elements_map = element.value.iter() - .map(|f| f.order().expect("Detached func in element segment!") as u32) - .collect(); + let mut elements_map = Vec::new(); + for f in element.value.iter() { + elements_map.push(f.order().ok_or(Error::DetachedEntry)? as u32); + } element_segments.push( elements::ElementSegment::new( @@ -742,7 +743,7 @@ impl Module { custom_round(&self.other, &mut idx, &mut sections); } - elements::Module::new(sections) + Ok(elements::Module::new(sections)) } } @@ -763,9 +764,9 @@ pub fn parse(wasm: &[u8]) -> Result { } /// Generate parity-wasm `Module` -pub fn generate(f: &Module) -> Vec { - let pm = f.generate(); - ::parity_wasm::serialize(pm).expect("failed to generate wasm") +pub fn generate(f: &Module) -> Result, Error> { + let pm = f.generate()?; + ::parity_wasm::serialize(pm).map_err(Error::Format) } #[cfg(test)] @@ -781,7 +782,7 @@ mod tests { } fn validate_sample(module: &super::Module) { - let binary = super::generate(module); + let binary = super::generate(module).expect("Failed to generate binary"); wabt::Module::read_binary(&binary, &Default::default()) .expect("Wabt failed to read final binary") .validate() From 5b2cd9c4c65c8bc5735bcdf2a45e0491d07d74ed Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 24 Jan 2019 16:21:52 +0300 Subject: [PATCH 34/35] add example --- examples/opt_imports.rs | 28 ++++++++++++++++++++++++++++ src/graph.rs | 3 ++- src/lib.rs | 2 +- 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 examples/opt_imports.rs diff --git a/examples/opt_imports.rs b/examples/opt_imports.rs new file mode 100644 index 0000000..c17f356 --- /dev/null +++ b/examples/opt_imports.rs @@ -0,0 +1,28 @@ +extern crate pwasm_utils as utils; + +use std::env; + +fn main() { + let args = env::args().collect::>(); + if args.len() != 3 { + println!("Usage: {} input_file.wasm output_file.wasm", args[0]); + return; + } + + // Loading module + let mut module = utils::Module::from_elements( + &parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed") + ).expect("Failed to parse parity-wasm format"); + + let mut delete_types = Vec::new(); + for type_ in module.types.iter() { + if type_.link_count() == 0 { + delete_types.push(type_.order().expect("type in list should have index")); + } + } + module.types.delete(&delete_types[..]); + + parity_wasm::serialize_to_file(&args[2], + module.generate().expect("Failed to generate valid format") + ).expect("Module serialization to succeed") +} diff --git a/src/graph.rs b/src/graph.rs index dfef028..e7bbdc5 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -410,7 +410,8 @@ impl Module { Ok(res) } - fn generate(&self) -> Result { + /// Generate raw format representation. + pub fn generate(&self) -> Result { use self::ImportedOrDeclared::*; let mut idx = 0; diff --git a/src/lib.rs b/src/lib.rs index 2679120..02abcc7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,7 +32,7 @@ pub use gas::inject_gas_counter; pub use optimizer::{optimize, Error as OptimizerError}; pub use pack::{pack_instance, Error as PackingError}; pub use runtime_type::inject_runtime_type; -pub use graph::{Module, parse, generate}; +pub use graph::{Module, parse as graph_parse, generate as graph_generate}; pub use ref_list::{RefList, Entry, EntryRef, DeleteTransaction}; pub struct TargetSymbols { From 38e0f254b020432f35ab973487d6a2b65b47c3b1 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Sun, 27 Jan 2019 12:14:14 +0300 Subject: [PATCH 35/35] use indoc! --- Cargo.toml | 1 + src/graph.rs | 153 +++++++++++++++++++++++++-------------------------- src/lib.rs | 5 +- 3 files changed, 79 insertions(+), 80 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 68df429..7229173 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ byteorder = { version = "1", default-features = false } tempdir = "0.3" wabt = "0.2" diff = "0.1.11" +indoc = "0.3" [features] default = ["std"] diff --git a/src/graph.rs b/src/graph.rs index e7bbdc5..a37a082 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -792,15 +792,13 @@ mod tests { #[test] fn smoky() { - let sample = load_sample(r#" -(module - (type (func)) - (func (type 0)) - (memory 0 1) - (export "simple" (func 0)) -) -"# - ); + let sample = load_sample(indoc!(r#" + (module + (type (func)) + (func (type 0)) + (memory 0 1) + (export "simple" (func 0)))"# + )); assert_eq!(sample.types.len(), 1); assert_eq!(sample.funcs.len(), 1); @@ -814,26 +812,26 @@ mod tests { #[test] fn table() { - let mut sample = load_sample(r#" -(module - (import "env" "foo" (func $foo)) - (func (param i32) - get_local 0 - i32.const 0 - call $i32.add - drop - ) - (func $i32.add (export "i32.add") (param i32 i32) (result i32) - get_local 0 - get_local 1 - i32.add - ) - (table 10 anyfunc) + let mut sample = load_sample(indoc!(r#" + (module + (import "env" "foo" (func $foo)) + (func (param i32) + get_local 0 + i32.const 0 + call $i32.add + drop + ) + (func $i32.add (export "i32.add") (param i32 i32) (result i32) + get_local 0 + get_local 1 + i32.add + ) + (table 10 anyfunc) - ;; Refer all types of functions: imported, defined not exported and defined exported. - (elem (i32.const 0) 0 1 2) -)"# - ); + ;; Refer all types of functions: imported, defined not exported and defined exported. + (elem (i32.const 0) 0 1 2) + )"# + )); { let element_func = &sample.elements[0].value[1]; @@ -864,23 +862,23 @@ mod tests { #[test] fn new_import() { - let mut sample = load_sample(r#" -(module - (type (;0;) (func)) - (type (;1;) (func (param i32 i32) (result i32))) - (import "env" "foo" (func (type 1))) - (func (param i32) - get_local 0 - i32.const 0 - call 0 - drop - ) - (func (type 0) - i32.const 0 - call 1 - ) -)"# - ); + let mut sample = load_sample(indoc!(r#" + (module + (type (;0;) (func)) + (type (;1;) (func (param i32 i32) (result i32))) + (import "env" "foo" (func (type 1))) + (func (param i32) + get_local 0 + i32.const 0 + call 0 + drop + ) + (func (type 0) + i32.const 0 + call 1 + ) + )"# + )); { let type_ref_0 = sample.types.clone_ref(0); @@ -922,39 +920,38 @@ mod tests { #[test] fn simple_opt() { - let mut sample = load_sample(r#" -(module - (type (;0;) (func)) - (type (;1;) (func (param i32 i32) (result i32))) - (type (;2;) (func (param i32 i32) (result i32))) - (type (;3;) (func (param i32 i32) (result i32))) - (import "env" "foo" (func (type 1))) - (import "env" "foo2" (func (type 2))) - (import "env" "foo3" (func (type 3))) - (func (type 0) - i32.const 1 - i32.const 1 - call 0 - drop - ) - (func (type 0) - i32.const 2 - i32.const 2 - call 1 - drop - ) - (func (type 0) - i32.const 3 - i32.const 3 - call 2 - drop - ) - (func (type 0) - call 3 - ) - -)"# - ); + let mut sample = load_sample(indoc!(r#" + (module + (type (;0;) (func)) + (type (;1;) (func (param i32 i32) (result i32))) + (type (;2;) (func (param i32 i32) (result i32))) + (type (;3;) (func (param i32 i32) (result i32))) + (import "env" "foo" (func (type 1))) + (import "env" "foo2" (func (type 2))) + (import "env" "foo3" (func (type 3))) + (func (type 0) + i32.const 1 + i32.const 1 + call 0 + drop + ) + (func (type 0) + i32.const 2 + i32.const 2 + call 1 + drop + ) + (func (type 0) + i32.const 3 + i32.const 3 + call 2 + drop + ) + (func (type 0) + call 3 + ) + )"# + )); validate_sample(&sample); diff --git a/src/lib.rs b/src/lib.rs index 02abcc7..6f034ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,8 +7,9 @@ extern crate alloc; extern crate byteorder; extern crate parity_wasm; -#[macro_use] -extern crate log; +#[macro_use] extern crate log; +#[cfg(test)] #[macro_use] extern crate indoc; + pub mod rules;