wasm-utils/src/graph.rs

487 lines
12 KiB
Rust
Raw Normal View History

2019-01-21 17:04:31 +03:00
//! Wasm binary graph format
use parity_wasm::elements;
2019-01-22 12:19:29 +03:00
use super::ref_list::{RefList, EntryRef};
2019-01-22 14:31:21 +03:00
use std::vec::Vec;
use std::borrow::ToOwned;
use std::string::String;
2019-01-22 16:11:04 +03:00
use std::collections::BTreeMap;
2019-01-21 17:04:31 +03:00
enum ImportedOrDeclared<T=()> {
Imported(String, String),
Declared(T),
}
2019-01-21 17:56:30 +03:00
impl<T> From<&elements::ImportEntry> for ImportedOrDeclared<T> {
fn from(v: &elements::ImportEntry) -> Self {
ImportedOrDeclared::Imported(v.module().to_owned(), v.field().to_owned())
}
}
2019-01-21 17:04:31 +03:00
type FuncOrigin = ImportedOrDeclared<Vec<Instruction>>;
type GlobalOrigin = ImportedOrDeclared<Vec<Instruction>>;
type MemoryOrigin = ImportedOrDeclared;
type TableOrigin = ImportedOrDeclared;
struct Func {
2019-01-22 12:19:29 +03:00
type_ref: EntryRef<elements::Type>,
2019-01-21 17:04:31 +03:00
origin: FuncOrigin,
}
struct Global {
content: elements::ValueType,
is_mut: bool,
origin: GlobalOrigin,
}
enum Instruction {
Plain(elements::Instruction),
2019-01-22 12:19:29 +03:00
Call(EntryRef<Func>),
2019-01-21 17:04:31 +03:00
}
struct Memory {
limits: elements::ResizableLimits,
origin: MemoryOrigin,
}
struct Table {
origin: TableOrigin,
2019-01-21 17:56:30 +03:00
limits: elements::ResizableLimits,
}
2019-01-22 15:15:17 +03:00
enum SegmentLocation {
Passive,
Default(Vec<Instruction>),
WithIndex(u32, Vec<Instruction>),
}
2019-01-21 17:04:31 +03:00
struct DataSegment {
2019-01-22 15:15:17 +03:00
location: SegmentLocation,
value: Vec<u8>,
2019-01-21 17:04:31 +03:00
}
struct ElementSegment {
2019-01-22 15:15:17 +03:00
location: SegmentLocation,
value: Vec<u32>,
2019-01-21 17:04:31 +03:00
}
2019-01-22 14:42:57 +03:00
enum ExportLocal {
2019-01-22 12:19:29 +03:00
Func(EntryRef<Func>),
Global(EntryRef<Global>),
Table(EntryRef<Table>),
Memory(EntryRef<Memory>),
2019-01-21 17:04:31 +03:00
}
2019-01-22 14:42:57 +03:00
struct Export {
name: String,
local: ExportLocal,
}
2019-01-21 17:04:31 +03:00
#[derive(Default)]
struct Module {
2019-01-22 12:19:29 +03:00
types: RefList<elements::Type>,
funcs: RefList<Func>,
memory: RefList<Memory>,
2019-01-22 14:31:21 +03:00
tables: RefList<Table>,
2019-01-22 12:19:29 +03:00
globals: RefList<Global>,
2019-01-22 14:31:21 +03:00
start: Option<EntryRef<Func>>,
exports: Vec<Export>,
2019-01-21 17:04:31 +03:00
elements: Vec<ElementSegment>,
data: Vec<DataSegment>,
2019-01-22 16:11:04 +03:00
other: BTreeMap<usize, elements::Section>,
2019-01-21 17:04:31 +03:00
}
impl Module {
fn from_elements(module: &elements::Module) -> Self {
2019-01-22 16:11:04 +03:00
let mut idx = 0;
2019-01-21 17:04:31 +03:00
let mut res = Module::default();
for section in module.sections() {
match section {
elements::Section::Type(type_section) => {
2019-01-22 12:19:29 +03:00
res.types = RefList::from_slice(type_section.types());
2019-01-21 17:56:30 +03:00
},
elements::Section::Import(import_section) => {
for entry in import_section.entries() {
match *entry.external() {
elements::External::Function(f) => {
res.funcs.push(Func {
2019-01-22 12:19:29 +03:00
type_ref: res.types.get(f as usize).expect("validated; qed").clone(),
2019-01-21 17:56:30 +03:00
origin: entry.into(),
2019-01-22 12:19:29 +03:00
});
2019-01-21 17:56:30 +03:00
},
elements::External::Memory(m) => {
res.memory.push(Memory {
limits: m.limits().clone(),
origin: entry.into(),
2019-01-22 12:19:29 +03:00
});
2019-01-21 17:56:30 +03:00
},
elements::External::Global(g) => {
res.globals.push(Global {
content: g.content_type(),
is_mut: g.is_mutable(),
origin: entry.into(),
2019-01-22 12:19:29 +03:00
});
2019-01-21 17:56:30 +03:00
},
elements::External::Table(t) => {
res.tables.push(Table {
limits: t.limits().clone(),
origin: entry.into(),
2019-01-22 12:19:29 +03:00
});
2019-01-21 17:56:30 +03:00
},
2019-01-22 12:19:29 +03:00
};
2019-01-21 17:56:30 +03:00
}
2019-01-21 17:04:31 +03:00
},
2019-01-22 12:58:29 +03:00
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()),
});
};
},
2019-01-22 13:03:11 +03:00
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(()),
});
}
},
2019-01-22 14:31:21 +03:00
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() {
2019-01-22 14:42:57 +03:00
let local = match e.internal() {
2019-01-22 14:31:21 +03:00
&elements::Internal::Function(func_idx) => {
2019-01-22 14:42:57 +03:00
ExportLocal::Func(res.funcs.clone_ref(func_idx as usize))
2019-01-22 14:31:21 +03:00
},
&elements::Internal::Global(global_idx) => {
2019-01-22 14:42:57 +03:00
ExportLocal::Global(res.globals.clone_ref(global_idx as usize))
2019-01-22 14:31:21 +03:00
},
&elements::Internal::Memory(mem_idx) => {
2019-01-22 14:42:57 +03:00
ExportLocal::Memory(res.memory.clone_ref(mem_idx as usize))
2019-01-22 14:31:21 +03:00
},
&elements::Internal::Table(table_idx) => {
2019-01-22 14:42:57 +03:00
ExportLocal::Table(res.tables.clone_ref(table_idx as usize))
2019-01-22 14:31:21 +03:00
},
2019-01-22 14:42:57 +03:00
};
res.exports.push(Export { local: local, name: e.field().to_owned() })
2019-01-22 14:31:21 +03:00
}
},
2019-01-22 15:15:17 +03:00
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,
});
}
}
2019-01-22 16:11:04 +03:00
_ => {
res.other.insert(idx, section.clone());
}
2019-01-21 17:04:31 +03:00
}
2019-01-22 16:11:04 +03:00
idx += 1;
2019-01-21 17:04:31 +03:00
}
res
}
2019-01-22 16:11:04 +03:00
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);
2019-01-22 16:40:28 +03:00
// 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())
2019-01-22 16:11:04 +03:00
}
}
2019-01-22 16:40:28 +03:00
sections.push(elements::Section::Type(type_section));
idx += 1;
custom_round(&self.other, &mut idx, &mut sections);
2019-01-22 16:11:04 +03:00
2019-01-22 16:40:28 +03:00
// 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
),
)
2019-01-22 16:11:04 +03:00
)
2019-01-22 16:40:28 +03:00
},
_ => continue,
}
2019-01-22 16:11:04 +03:00
}
2019-01-22 16:40:28 +03:00
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,
)
),
)
2019-01-22 16:11:04 +03:00
)
2019-01-22 16:40:28 +03:00
},
_ => continue,
}
2019-01-22 16:11:04 +03:00
}
2019-01-22 16:40:28 +03:00
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(),
)
),
)
2019-01-22 16:11:04 +03:00
)
2019-01-22 16:40:28 +03:00
},
_ => 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,
}
2019-01-22 16:11:04 +03:00
}
}
2019-01-22 16:40:28 +03:00
sections.push(elements::Section::Import(import_section));
idx += 1;
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,
}
}
}
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);
2019-01-22 16:11:04 +03:00
elements::Module::new(sections)
}
}
fn custom_round(
map: &BTreeMap<usize, elements::Section>,
idx: &mut usize,
sections: &mut Vec<elements::Section>,
) {
while let Some(other_section) = map.get(&idx) {
sections.push(other_section.clone());
*idx += 1;
}
2019-01-21 17:04:31 +03:00
}
2019-01-22 12:58:29 +03:00
fn parse(wasm: &[u8]) -> Module {
Module::from_elements(&::parity_wasm::deserialize_buffer(wasm).expect("failed to parse wasm"))
}
2019-01-21 17:04:31 +03:00
#[cfg(test)]
mod tests {
2019-01-22 12:58:29 +03:00
extern crate wabt;
use parity_wasm;
#[test]
fn smoky() {
let wasm = wabt::wat2wasm(r#"
(module
(type (func))
(func (type 0))
2019-01-22 13:03:11 +03:00
(memory 0 1)
2019-01-22 14:31:21 +03:00
(export "simple" (func 0))
2019-01-22 12:58:29 +03:00
)
"#).expect("Failed to read fixture");
let f = super::parse(&wasm[..]);
assert_eq!(f.types.len(), 1);
assert_eq!(f.funcs.len(), 1);
2019-01-22 13:03:11 +03:00
assert_eq!(f.tables.len(), 0);
assert_eq!(f.memory.len(), 1);
2019-01-22 14:31:21 +03:00
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);
2019-01-22 12:58:29 +03:00
}
2019-01-21 17:04:31 +03:00
}