//! Wasm binary graph format 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; enum ImportedOrDeclared { Imported(String, String), Declared(T), } 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; type TableOrigin = ImportedOrDeclared; struct Func { type_ref: EntryRef, origin: FuncOrigin, } struct Global { content: elements::ValueType, is_mut: bool, origin: GlobalOrigin, } enum Instruction { Plain(elements::Instruction), Call(EntryRef), } struct Memory { limits: elements::ResizableLimits, origin: MemoryOrigin, } struct Table { origin: TableOrigin, limits: elements::ResizableLimits, } enum SegmentLocation { Passive, Default(Vec), WithIndex(u32, Vec), } struct DataSegment { location: SegmentLocation, value: Vec, } struct ElementSegment { location: SegmentLocation, value: Vec, } enum ExportLocal { Func(EntryRef), Global(EntryRef), Table(EntryRef), Memory(EntryRef), } struct Export { name: String, 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, } impl Module { fn from_elements(module: &elements::Module) -> Self { let mut idx = 0; let mut res = Module::default(); for section in module.sections() { match section { elements::Section::Type(type_section) => { 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.get(f as usize).expect("validated; qed").clone(), origin: entry.into(), }); }, elements::External::Memory(m) => { res.memory.push(Memory { limits: m.limits().clone(), origin: entry.into(), }); }, elements::External::Global(g) => { res.globals.push(Global { content: g.content_type(), is_mut: g.is_mutable(), origin: entry.into(), }); }, elements::External::Table(t) => { res.tables.push(Table { limits: t.limits().clone(), origin: entry.into(), }); }, }; } }, 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()), }); }; }, 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(()), }); } }, 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() { let local = match e.internal() { &elements::Internal::Function(func_idx) => { ExportLocal::Func(res.funcs.clone_ref(func_idx as usize)) }, &elements::Internal::Global(global_idx) => { ExportLocal::Global(res.globals.clone_ref(global_idx as usize)) }, &elements::Internal::Memory(mem_idx) => { ExportLocal::Memory(res.memory.clone_ref(mem_idx as usize)) }, &elements::Internal::Table(table_idx) => { ExportLocal::Table(res.tables.clone_ref(table_idx as usize)) }, }; 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, }); } } _ => { 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); if self.types.len() > 0 { // TYPE SECTION (1) 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 add = { 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, } } 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, } } imports.len() > 0 }; if add { sections.push(elements::Section::Import(import_section)); idx += 1; custom_round(&self.other, &mut idx, &mut sections); } if self.funcs.len() > 0 { // 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); } if self.tables.len() > 0 { // 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); } if self.memory.len() > 0 { // MEMORY SECTION (5) 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); } if self.globals.len() > 0 { // GLOBAL SECTION (6) let mut global_section = elements::GlobalSection::default(); { let mut globals = global_section.entries_mut(); 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::Global(global_section)); idx += 1; 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) } } 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 { 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 { extern crate wabt; use parity_wasm; #[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 f = super::parse(&wasm[..]); 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!(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); } }