#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
extern crate failure;
extern crate parity_wasm;
use parity_wasm::elements::{Deserialize, FuncBody, ImportEntry, Internal, Module, Section,
Serialize, VarUint32, VarUint7};
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt;
use std::iter;
use std::io;
use std::slice;
use std::str;
#[derive(Clone, Debug)]
pub struct Options {
pub imports: bool,
pub exports: bool,
pub privates: bool,
pub sizes: bool,
}
impl Default for Options {
fn default() -> Options {
Options {
imports: true,
exports: true,
privates: true,
sizes: false,
}
}
}
impl Options {
pub fn nothing() -> Options {
Options {
imports: false,
exports: false,
privates: false,
sizes: false,
}
}
}
pub fn symbols<R>(opts: Options, reader: &mut R) -> Result<Symbols, failure::Error>
where
R: io::Read,
{
let module = Module::deserialize(reader)?;
Ok(Symbols { opts, module })
}
pub struct Symbols {
opts: Options,
module: Module,
}
impl fmt::Debug for Symbols {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Symbols")
.field("opts", &self.opts)
.field("module", &"...")
.finish()
}
}
fn decode_name_map<'a>(
mut bytes: &'a [u8],
num_imports: usize,
) -> Result<HashMap<u32, Cow<'a, str>>, failure::Error> {
while !bytes.is_empty() {
let name_type = u8::from(VarUint7::deserialize(&mut bytes)?);
let name_payload_len = u32::from(VarUint32::deserialize(&mut bytes)?);
let (these_bytes, rest) = bytes.split_at(name_payload_len as usize);
if name_type == 1 {
bytes = these_bytes;
} else {
bytes = rest;
continue;
}
let count = u32::from(VarUint32::deserialize(&mut bytes)?);
let mut names = HashMap::with_capacity(count as usize);
for _ in 0..count {
let index =
u32::from(VarUint32::deserialize(&mut bytes)?).saturating_sub(num_imports as u32);
let name_len = u32::from(VarUint32::deserialize(&mut bytes)?);
let (name, rest) = bytes.split_at(name_len as usize);
bytes = rest;
let name = String::from_utf8_lossy(name);
names.insert(index, name);
}
return Ok(names);
}
return Ok(Default::default());
}
impl Symbols {
pub fn iter(&self) -> SymbolsIter {
let exports = self.module
.export_section()
.map_or(HashMap::new(), |section| {
section
.entries()
.iter()
.filter_map(|entry| match *entry.internal() {
Internal::Function(idx) => Some((idx, entry.field())),
_ => None,
})
.collect()
});
let num_imports = self.module
.import_section()
.map_or(0, |imports| imports.entries().len());
let names = self.module
.sections()
.iter()
.filter_map(|section| match *section {
Section::Custom(ref custom) if custom.name() == "name" => Some(custom),
_ => None,
})
.next()
.and_then(|name_section| decode_name_map(name_section.payload(), num_imports).ok());
SymbolsIter {
symbols: self,
state: SymbolsIterState::new(self),
exports,
names,
}
}
}
#[derive(Debug)]
pub struct SymbolsIter<'a> {
symbols: &'a Symbols,
state: SymbolsIterState<'a>,
exports: HashMap<u32, &'a str>,
names: Option<HashMap<u32, Cow<'a, str>>>,
}
#[derive(Debug)]
enum SymbolsIterState<'a> {
Imports(slice::Iter<'a, ImportEntry>),
Functions(iter::Enumerate<slice::Iter<'a, FuncBody>>),
Finished,
}
impl<'a> SymbolsIterState<'a> {
fn new(symbols: &'a Symbols) -> SymbolsIterState<'a> {
SymbolsIterState::Imports(if let Some(section) = symbols.module.import_section() {
section.entries().iter()
} else {
[].iter()
})
}
}
fn function_size(index: usize, module: &Module) -> Option<usize> {
module
.code_section()
.and_then(|section| section.bodies().iter().nth(index))
.and_then(|body| {
let mut encoded = vec![];
if let Err(_) = body.code().clone().serialize(&mut encoded) {
return None;
}
Some(encoded.len())
})
}
impl<'a> Iterator for SymbolsIter<'a> {
type Item = Symbol<'a>;
fn next(&mut self) -> Option<Symbol<'a>> {
loop {
self.state = match self.state {
SymbolsIterState::Finished => return None,
SymbolsIterState::Imports(ref mut imports) => match (
self.symbols.opts.imports,
imports.next(),
) {
(true, Some(import)) => {
return Some(Symbol::Import {
name: import.field(),
})
}
(false, _) | (true, None) => SymbolsIterState::Functions(
if let Some(section) = self.symbols.module.code_section() {
section.bodies().iter().enumerate()
} else {
[].iter().enumerate()
},
),
},
SymbolsIterState::Functions(ref mut functions) => {
let (i, function) = match functions.next() {
Some(next) => next,
_ => break,
};
match (i, function, self.exports.get(&(i as u32))) {
(i, _, Some(export)) if self.symbols.opts.exports => {
return Some(Symbol::Export {
name: export,
size: if self.symbols.opts.sizes {
function_size(i, &self.symbols.module)
} else {
None
},
});
}
(i, _function, None) if self.symbols.opts.privates => {
let i = i as u32;
let name = self.names.as_ref().and_then(|names| names.get(&i).cloned());
return Some(Symbol::Private {
index: i,
name,
size: if self.symbols.opts.sizes {
function_size(i as usize, &self.symbols.module)
} else {
None
},
});
}
_ => {
continue;
}
}
}
};
}
self.state = SymbolsIterState::Finished;
None
}
}
#[derive(Clone, Debug)]
pub enum Symbol<'a> {
Import {
name: &'a str,
},
Export {
name: &'a str,
size: Option<usize>,
},
Private {
index: u32,
name: Option<Cow<'a, str>>,
size: Option<usize>,
},
}
impl<'a> fmt::Display for Symbol<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Symbol::Import { name } | Symbol::Export { name, .. } => f.write_str(name),
Symbol::Private {
name: Some(ref name),
..
} => f.write_str(&name),
Symbol::Private { index, .. } => write!(f, "function[{}]", index),
}
}
}