mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-05-30 14:11:21 +00:00
This commit restructures some of the internals of `wasm-gc` now that I've actually got a better grasp on the wasm format and what all the ownership edges look like. This shouldn't actually result in any user-facing changes, but should make us be a bit more compatible with operations in the future. Memories/tables/elements/segments are no longer considered automatic roots but rather need to be rooted by something else to prevent a gc. For example an element section is gc'd along with a table if the table is never referenced, along with data segments as well if the memory isn't referenced. Additionally all index sets now don't contained offseted indices, but rather everything is always stored relative to the "index space" to ensure consistency. This should make it a bit easier to add future items to gc!
839 lines
26 KiB
Rust
839 lines
26 KiB
Rust
//! NO GUARANTEES ARE MADE ABOUT THE BACKWARDS COMPATIBILITY OF THIS LIBRARY
|
|
//!
|
|
//! Internal library of `wasm-gc` to remove unreferenced items from a wasm
|
|
//! executable.
|
|
|
|
extern crate parity_wasm;
|
|
#[macro_use]
|
|
extern crate log;
|
|
extern crate rustc_demangle;
|
|
|
|
use std::mem;
|
|
use std::collections::HashSet;
|
|
|
|
use parity_wasm::elements::*;
|
|
|
|
use bitvec::BitSet;
|
|
|
|
mod bitvec;
|
|
|
|
pub struct Config {
|
|
demangle: bool,
|
|
keep_debug: bool,
|
|
}
|
|
|
|
impl Config {
|
|
/// Creates a blank slate of configuration, ready to gc wasm files.
|
|
pub fn new() -> Config {
|
|
Config {
|
|
demangle: true,
|
|
keep_debug: false,
|
|
}
|
|
}
|
|
|
|
/// Configures whether or not this will demangle symbols as part of the gc
|
|
/// pass.
|
|
pub fn demangle(&mut self, demangle: bool) -> &mut Self {
|
|
self.demangle = demangle;
|
|
self
|
|
}
|
|
|
|
/// Configures whether or not debug sections will be preserved.
|
|
pub fn keep_debug(&mut self, keep_debug: bool) -> &mut Self {
|
|
self.keep_debug = keep_debug;
|
|
self
|
|
}
|
|
|
|
pub fn run(&mut self, module: &mut Module) {
|
|
run(self, module);
|
|
}
|
|
}
|
|
|
|
fn run(config: &mut Config, module: &mut Module) {
|
|
let analysis = {
|
|
let mut cx = LiveContext::new(&module);
|
|
cx.blacklist.insert("rust_eh_personality");
|
|
|
|
// always treat memory as a root
|
|
cx.add_memory(0);
|
|
|
|
// All exports are a root
|
|
if let Some(section) = module.export_section() {
|
|
for (i, entry) in section.entries().iter().enumerate() {
|
|
if cx.blacklist.contains(entry.field()) {
|
|
continue
|
|
}
|
|
cx.add_export_entry(entry, i as u32);
|
|
}
|
|
}
|
|
|
|
// Pessimistically assume all passive data and table segments are
|
|
// required
|
|
if let Some(section) = module.data_section() {
|
|
for entry in section.entries() {
|
|
if entry.passive() {
|
|
cx.add_data_segment(entry);
|
|
}
|
|
}
|
|
}
|
|
if let Some(elements) = module.elements_section() {
|
|
for seg in elements.entries() {
|
|
if seg.passive() {
|
|
cx.add_element_segment(seg);
|
|
}
|
|
}
|
|
}
|
|
|
|
// The start function is also a root
|
|
if let Some(i) = module.start_section() {
|
|
cx.add_function(i);
|
|
}
|
|
|
|
cx.analysis
|
|
};
|
|
|
|
let cx = RemapContext::new(&module, &analysis, config);
|
|
for i in (0..module.sections().len()).rev() {
|
|
let retain = match module.sections_mut()[i] {
|
|
Section::Unparsed { .. } => {
|
|
info!("unparsed section");
|
|
continue
|
|
}
|
|
Section::Custom(ref s) => {
|
|
if !cx.config.keep_debug && s.name().starts_with(".debug_") {
|
|
false
|
|
} else {
|
|
info!("skipping custom section: {}", s.name());
|
|
continue
|
|
}
|
|
}
|
|
Section::Reloc(..) => {
|
|
info!("skipping reloc section");
|
|
continue
|
|
}
|
|
Section::Type(ref mut s) => cx.remap_type_section(s),
|
|
Section::Import(ref mut s) => cx.remap_import_section(s),
|
|
Section::Function(ref mut s) => cx.remap_function_section(s),
|
|
Section::Table(ref mut s) => cx.remap_table_section(s),
|
|
Section::Memory(ref mut s) => cx.remap_memory_section(s),
|
|
Section::Global(ref mut s) => cx.remap_global_section(s),
|
|
Section::Export(ref mut s) => cx.remap_export_section(s),
|
|
Section::Start(ref mut i) => { cx.remap_function_idx(i); true }
|
|
Section::Element(ref mut s) => cx.remap_element_section(s),
|
|
Section::Code(ref mut s) => cx.remap_code_section(s),
|
|
Section::Data(ref mut s) => cx.remap_data_section(s),
|
|
Section::Name(ref mut s) => { cx.remap_name_section(s); true }
|
|
};
|
|
if !retain {
|
|
debug!("remove empty section");
|
|
module.sections_mut().remove(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct Analysis {
|
|
tables: BitSet,
|
|
memories: BitSet,
|
|
globals: BitSet,
|
|
types: BitSet,
|
|
imports: BitSet,
|
|
exports: BitSet,
|
|
functions: BitSet,
|
|
imported_functions: u32,
|
|
imported_globals: u32,
|
|
imported_memories: u32,
|
|
imported_tables: u32,
|
|
}
|
|
|
|
struct LiveContext<'a> {
|
|
blacklist: HashSet<&'static str>,
|
|
function_section: Option<&'a FunctionSection>,
|
|
type_section: Option<&'a TypeSection>,
|
|
data_section: Option<&'a DataSection>,
|
|
element_section: Option<&'a ElementSection>,
|
|
code_section: Option<&'a CodeSection>,
|
|
table_section: Option<&'a TableSection>,
|
|
global_section: Option<&'a GlobalSection>,
|
|
import_section: Option<&'a ImportSection>,
|
|
analysis: Analysis,
|
|
}
|
|
|
|
impl<'a> LiveContext<'a> {
|
|
fn new(module: &'a Module) -> LiveContext<'a> {
|
|
let (mut tables, mut memories, mut globals, mut functions) = (0, 0, 0, 0);
|
|
if let Some(imports) = module.import_section() {
|
|
for entry in imports.entries() {
|
|
match entry.external() {
|
|
External::Memory(_) => memories += 1,
|
|
External::Table(_) => tables += 1,
|
|
External::Function(_) => functions += 1,
|
|
External::Global(_) => globals += 1,
|
|
}
|
|
}
|
|
}
|
|
|
|
LiveContext {
|
|
blacklist: HashSet::new(),
|
|
function_section: module.function_section(),
|
|
type_section: module.type_section(),
|
|
data_section: module.data_section(),
|
|
element_section: module.elements_section(),
|
|
code_section: module.code_section(),
|
|
table_section: module.table_section(),
|
|
global_section: module.global_section(),
|
|
import_section: module.import_section(),
|
|
analysis: Analysis {
|
|
imported_functions: functions,
|
|
imported_globals: globals,
|
|
imported_tables: tables,
|
|
imported_memories: memories,
|
|
..Analysis::default()
|
|
},
|
|
}
|
|
}
|
|
|
|
fn add_function(&mut self, idx: u32) {
|
|
if !self.analysis.functions.insert(idx) {
|
|
return
|
|
}
|
|
debug!("adding function: {}", idx);
|
|
|
|
if idx < self.analysis.imported_functions {
|
|
let imports = self.import_section.unwrap();
|
|
let (i, import) = imports.entries()
|
|
.iter()
|
|
.enumerate()
|
|
.filter(|&(_, i)| {
|
|
match *i.external() {
|
|
External::Function(_) => true,
|
|
_ => false,
|
|
}
|
|
})
|
|
.skip(idx as usize)
|
|
.next()
|
|
.expect("expected an imported function with this index");
|
|
let i = i as u32;
|
|
return self.add_import_entry(import, i);
|
|
}
|
|
let idx = idx - self.analysis.imported_functions;
|
|
let functions = self.function_section.expect("no functions section");
|
|
self.add_type(functions.entries()[idx as usize].type_ref());
|
|
let codes = self.code_section.expect("no codes section");
|
|
self.add_func_body(&codes.bodies()[idx as usize]);
|
|
}
|
|
|
|
fn add_table(&mut self, idx: u32) {
|
|
if !self.analysis.tables.insert(idx) {
|
|
return
|
|
}
|
|
debug!("adding table: {}", idx);
|
|
|
|
// Add all element segments that initialize this table
|
|
if let Some(elements) = self.element_section {
|
|
for entry in elements.entries().iter().filter(|d| !d.passive()) {
|
|
if entry.index() == idx {
|
|
self.add_element_segment(entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
if idx < self.analysis.imported_tables {
|
|
let imports = self.import_section.unwrap();
|
|
let (i, import) = imports.entries()
|
|
.iter()
|
|
.enumerate()
|
|
.filter(|&(_, i)| {
|
|
match *i.external() {
|
|
External::Table(_) => true,
|
|
_ => false,
|
|
}
|
|
})
|
|
.skip(idx as usize)
|
|
.next()
|
|
.expect("expected an imported table with this index");
|
|
let i = i as u32;
|
|
return self.add_import_entry(import, i);
|
|
}
|
|
let idx = idx - self.analysis.imported_tables;
|
|
let tables = self.table_section.expect("no table section");
|
|
let table = &tables.entries()[idx as usize];
|
|
drop(table);
|
|
}
|
|
|
|
fn add_memory(&mut self, idx: u32) {
|
|
if !self.analysis.memories.insert(idx) {
|
|
return
|
|
}
|
|
debug!("adding memory: {}", idx);
|
|
|
|
// Add all data segments that initialize this memory
|
|
if let Some(data) = self.data_section {
|
|
for entry in data.entries().iter().filter(|d| !d.passive()) {
|
|
if entry.index() == idx {
|
|
self.add_data_segment(entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ... and add the import if it's an imported memory ..
|
|
if idx < self.analysis.imported_memories {
|
|
let imports = self.import_section.unwrap();
|
|
let (i, import) = imports.entries()
|
|
.iter()
|
|
.enumerate()
|
|
.filter(|&(_, i)| {
|
|
match *i.external() {
|
|
External::Memory(_) => true,
|
|
_ => false,
|
|
}
|
|
})
|
|
.skip(idx as usize)
|
|
.next()
|
|
.expect("expected an imported memory with this index");
|
|
let i = i as u32;
|
|
return self.add_import_entry(import, i);
|
|
}
|
|
|
|
// ... and if it's not imported that's it!
|
|
}
|
|
|
|
fn add_global(&mut self, idx: u32) {
|
|
if !self.analysis.globals.insert(idx) {
|
|
return
|
|
}
|
|
debug!("adding global: {}", idx);
|
|
|
|
if idx < self.analysis.imported_globals {
|
|
let imports = self.import_section.unwrap();
|
|
let (i, import) = imports.entries()
|
|
.iter()
|
|
.enumerate()
|
|
.filter(|&(_, i)| {
|
|
match *i.external() {
|
|
External::Global(_) => true,
|
|
_ => false,
|
|
}
|
|
})
|
|
.skip(idx as usize)
|
|
.next()
|
|
.expect("expected an imported global with this index");
|
|
let i = i as u32;
|
|
return self.add_import_entry(import, i);
|
|
}
|
|
|
|
let idx = idx - self.analysis.imported_globals;
|
|
let globals = self.global_section.expect("no global section");
|
|
let global = &globals.entries()[idx as usize];
|
|
self.add_global_type(global.global_type());
|
|
self.add_init_expr(global.init_expr());
|
|
}
|
|
|
|
fn add_global_type(&mut self, t: &GlobalType) {
|
|
self.add_value_type(&t.content_type());
|
|
}
|
|
|
|
fn add_init_expr(&mut self, t: &InitExpr) {
|
|
for opcode in t.code() {
|
|
self.add_opcode(opcode);
|
|
}
|
|
}
|
|
|
|
fn add_type(&mut self, idx: u32) {
|
|
if !self.analysis.types.insert(idx) {
|
|
return
|
|
}
|
|
let types = self.type_section.expect("no types section");
|
|
match types.types()[idx as usize] {
|
|
Type::Function(ref f) => {
|
|
for param in f.params() {
|
|
self.add_value_type(param);
|
|
}
|
|
if let Some(ref ret) = f.return_type() {
|
|
self.add_value_type(ret);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn add_value_type(&mut self, value: &ValueType) {
|
|
match *value {
|
|
ValueType::I32 => {}
|
|
ValueType::I64 => {}
|
|
ValueType::F32 => {}
|
|
ValueType::F64 => {}
|
|
ValueType::V128 => {}
|
|
}
|
|
}
|
|
|
|
fn add_func_body(&mut self, body: &FuncBody) {
|
|
for local in body.locals() {
|
|
self.add_value_type(&local.value_type());
|
|
}
|
|
self.add_opcodes(body.code());
|
|
}
|
|
|
|
fn add_opcodes(&mut self, code: &Instructions) {
|
|
for opcode in code.elements() {
|
|
self.add_opcode(opcode);
|
|
}
|
|
}
|
|
|
|
fn add_opcode(&mut self, code: &Instruction) {
|
|
match *code {
|
|
Instruction::Block(ref b) |
|
|
Instruction::Loop(ref b) |
|
|
Instruction::If(ref b) => self.add_block_type(b),
|
|
Instruction::Call(f) => self.add_function(f),
|
|
Instruction::CallIndirect(t, _) => {
|
|
self.add_type(t);
|
|
self.add_table(0);
|
|
}
|
|
Instruction::GetGlobal(i) |
|
|
Instruction::SetGlobal(i) => self.add_global(i),
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
fn add_block_type(&mut self, bt: &BlockType) {
|
|
match *bt {
|
|
BlockType::Value(ref v) => self.add_value_type(v),
|
|
BlockType::NoResult => {}
|
|
}
|
|
}
|
|
|
|
fn add_export_entry(&mut self, entry: &ExportEntry, idx: u32) {
|
|
if !self.analysis.exports.insert(idx) {
|
|
return
|
|
}
|
|
match *entry.internal() {
|
|
Internal::Function(i) => self.add_function(i),
|
|
Internal::Table(i) => self.add_table(i),
|
|
Internal::Memory(i) => self.add_memory(i),
|
|
Internal::Global(i) => self.add_global(i),
|
|
}
|
|
}
|
|
|
|
fn add_import_entry(&mut self, entry: &ImportEntry, idx: u32) {
|
|
if !self.analysis.imports.insert(idx) {
|
|
return
|
|
}
|
|
debug!("adding import: {}", idx);
|
|
match *entry.external() {
|
|
External::Function(i) => self.add_type(i),
|
|
External::Table(_) => {}
|
|
External::Memory(_) => {}
|
|
External::Global(g) => self.add_value_type(&g.content_type()),
|
|
}
|
|
}
|
|
|
|
fn add_data_segment(&mut self, data: &DataSegment) {
|
|
if let Some(offset) = data.offset() {
|
|
self.add_memory(data.index());
|
|
self.add_init_expr(offset);
|
|
}
|
|
}
|
|
|
|
fn add_element_segment(&mut self, seg: &ElementSegment) {
|
|
for member in seg.members() {
|
|
self.add_function(*member);
|
|
}
|
|
if let Some(offset) = seg.offset() {
|
|
self.add_table(seg.index());
|
|
self.add_init_expr(offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct RemapContext<'a> {
|
|
analysis: &'a Analysis,
|
|
config: &'a Config,
|
|
functions: Vec<u32>,
|
|
globals: Vec<u32>,
|
|
types: Vec<u32>,
|
|
tables: Vec<u32>,
|
|
memories: Vec<u32>,
|
|
}
|
|
|
|
impl<'a> RemapContext<'a> {
|
|
fn new(m: &Module, analysis: &'a Analysis, config: &'a Config) -> RemapContext<'a> {
|
|
let mut nfunctions = 0;
|
|
let mut functions = Vec::new();
|
|
let mut nglobals = 0;
|
|
let mut globals = Vec::new();
|
|
let mut types = Vec::new();
|
|
let mut ntables = 0;
|
|
let mut tables = Vec::new();
|
|
let mut nmemories = 0;
|
|
let mut memories = Vec::new();
|
|
|
|
if let Some(s) = m.type_section() {
|
|
let mut removed = 0;
|
|
for i in 0..(s.types().len() as u32) {
|
|
if analysis.types.contains(&i) {
|
|
types.push(i - removed);
|
|
} else {
|
|
debug!("gc type {}", i);
|
|
types.push(u32::max_value());
|
|
removed += 1;
|
|
}
|
|
}
|
|
}
|
|
if let Some(s) = m.import_section() {
|
|
for (i, import) in s.entries().iter().enumerate() {
|
|
let (dst, ndst) = match *import.external() {
|
|
External::Function(_) => (&mut functions, &mut nfunctions),
|
|
External::Table(_) => (&mut tables, &mut ntables),
|
|
External::Memory(_) => (&mut memories, &mut nmemories),
|
|
External::Global(_) => (&mut globals, &mut nglobals),
|
|
};
|
|
if analysis.imports.contains(&(i as u32)) {
|
|
dst.push(*ndst);
|
|
*ndst += 1;
|
|
} else {
|
|
debug!("gc import {}", i);
|
|
dst.push(u32::max_value());
|
|
}
|
|
}
|
|
}
|
|
if let Some(s) = m.function_section() {
|
|
for i in 0..(s.entries().len() as u32) {
|
|
if analysis.functions.contains(&(i + analysis.imported_functions)) {
|
|
functions.push(nfunctions);
|
|
nfunctions += 1;
|
|
} else {
|
|
debug!("gc function {}", i);
|
|
functions.push(u32::max_value());
|
|
}
|
|
}
|
|
}
|
|
if let Some(s) = m.global_section() {
|
|
for i in 0..(s.entries().len() as u32) {
|
|
if analysis.globals.contains(&(i + analysis.imported_globals)) {
|
|
globals.push(nglobals);
|
|
nglobals += 1;
|
|
} else {
|
|
debug!("gc global {}", i);
|
|
globals.push(u32::max_value());
|
|
}
|
|
}
|
|
}
|
|
if let Some(s) = m.table_section() {
|
|
for i in 0..(s.entries().len() as u32) {
|
|
if analysis.tables.contains(&(i + analysis.imported_tables)) {
|
|
tables.push(ntables);
|
|
ntables += 1;
|
|
} else {
|
|
debug!("gc table {}", i);
|
|
tables.push(u32::max_value());
|
|
}
|
|
}
|
|
}
|
|
if let Some(s) = m.memory_section() {
|
|
for i in 0..(s.entries().len() as u32) {
|
|
if analysis.memories.contains(&(i + analysis.imported_memories)) {
|
|
memories.push(nmemories);
|
|
nmemories += 1;
|
|
} else {
|
|
debug!("gc memory {}", i);
|
|
memories.push(u32::max_value());
|
|
}
|
|
}
|
|
}
|
|
|
|
RemapContext {
|
|
analysis,
|
|
functions,
|
|
globals,
|
|
memories,
|
|
tables,
|
|
types,
|
|
config,
|
|
}
|
|
}
|
|
|
|
fn retain<T>(&self, set: &BitSet, list: &mut Vec<T>, name: &str, offset: u32) {
|
|
for i in (0..list.len()).rev().map(|x| x as u32) {
|
|
if !set.contains(&(i + offset)) {
|
|
debug!("removing {} {}", name, i);
|
|
list.remove(i as usize);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn remap_type_section(&self, s: &mut TypeSection) -> bool {
|
|
self.retain(&self.analysis.types, s.types_mut(), "type", 0);
|
|
for t in s.types_mut() {
|
|
self.remap_type(t);
|
|
}
|
|
s.types().len() > 0
|
|
}
|
|
|
|
fn remap_type(&self, t: &mut Type) {
|
|
match *t {
|
|
Type::Function(ref mut t) => self.remap_function_type(t),
|
|
}
|
|
}
|
|
|
|
fn remap_function_type(&self, t: &mut FunctionType) {
|
|
for param in t.params_mut() {
|
|
self.remap_value_type(param);
|
|
}
|
|
if let Some(m) = t.return_type_mut().as_mut() {
|
|
self.remap_value_type(m);
|
|
}
|
|
}
|
|
|
|
fn remap_value_type(&self, t: &mut ValueType) {
|
|
match t {
|
|
ValueType::I32 => {}
|
|
ValueType::I64 => {}
|
|
ValueType::F32 => {}
|
|
ValueType::F64 => {}
|
|
ValueType::V128 => {}
|
|
}
|
|
}
|
|
|
|
fn remap_import_section(&self, s: &mut ImportSection) -> bool {
|
|
self.retain(&self.analysis.imports, s.entries_mut(), "import", 0);
|
|
for i in s.entries_mut() {
|
|
self.remap_import_entry(i);
|
|
}
|
|
s.entries().len() > 0
|
|
}
|
|
|
|
fn remap_import_entry(&self, s: &mut ImportEntry) {
|
|
debug!("remap import entry {:?}", s);
|
|
match *s.external_mut() {
|
|
External::Function(ref mut f) => self.remap_type_idx(f),
|
|
External::Table(_) => {}
|
|
External::Memory(_) => {}
|
|
External::Global(_) => {}
|
|
}
|
|
}
|
|
|
|
fn remap_function_section(&self, s: &mut FunctionSection) -> bool {
|
|
self.retain(
|
|
&self.analysis.functions,
|
|
s.entries_mut(),
|
|
"function",
|
|
self.analysis.imported_functions,
|
|
);
|
|
for f in s.entries_mut() {
|
|
self.remap_func(f);
|
|
}
|
|
s.entries().len() > 0
|
|
}
|
|
|
|
fn remap_func(&self, f: &mut Func) {
|
|
self.remap_type_idx(f.type_ref_mut());
|
|
}
|
|
|
|
fn remap_table_section(&self, s: &mut TableSection) -> bool {
|
|
self.retain(
|
|
&self.analysis.tables,
|
|
s.entries_mut(),
|
|
"table",
|
|
self.analysis.imported_tables,
|
|
);
|
|
for t in s.entries_mut() {
|
|
drop(t); // TODO
|
|
}
|
|
s.entries().len() > 0
|
|
}
|
|
|
|
fn remap_memory_section(&self, s: &mut MemorySection) -> bool {
|
|
self.retain(
|
|
&self.analysis.memories,
|
|
s.entries_mut(),
|
|
"memory",
|
|
self.analysis.imported_memories,
|
|
);
|
|
for m in s.entries_mut() {
|
|
drop(m); // TODO
|
|
}
|
|
s.entries().len() > 0
|
|
}
|
|
|
|
fn remap_global_section(&self, s: &mut GlobalSection) -> bool {
|
|
self.retain(
|
|
&self.analysis.globals,
|
|
s.entries_mut(),
|
|
"global",
|
|
self.analysis.imported_globals,
|
|
);
|
|
for g in s.entries_mut() {
|
|
self.remap_global_entry(g);
|
|
}
|
|
s.entries().len() > 0
|
|
}
|
|
|
|
fn remap_global_entry(&self, s: &mut GlobalEntry) {
|
|
self.remap_global_type(s.global_type_mut());
|
|
self.remap_init_expr(s.init_expr_mut());
|
|
}
|
|
|
|
fn remap_global_type(&self, s: &mut GlobalType) {
|
|
drop(s);
|
|
}
|
|
|
|
fn remap_init_expr(&self, s: &mut InitExpr) {
|
|
for code in s.code_mut() {
|
|
self.remap_instruction(code);
|
|
}
|
|
}
|
|
|
|
fn remap_export_section(&self, s: &mut ExportSection) -> bool {
|
|
self.retain(&self.analysis.exports, s.entries_mut(), "export", 0);
|
|
for s in s.entries_mut() {
|
|
self.remap_export_entry(s);
|
|
}
|
|
s.entries().len() > 0
|
|
}
|
|
|
|
fn remap_export_entry(&self, s: &mut ExportEntry) {
|
|
match *s.internal_mut() {
|
|
Internal::Function(ref mut i) => self.remap_function_idx(i),
|
|
Internal::Table(ref mut i) => self.remap_table_idx(i),
|
|
Internal::Memory(ref mut i) => self.remap_memory_idx(i),
|
|
Internal::Global(ref mut i) => self.remap_global_idx(i),
|
|
}
|
|
}
|
|
|
|
fn remap_element_section(&self, s: &mut ElementSection) -> bool {
|
|
for s in s.entries_mut() {
|
|
self.remap_element_segment(s);
|
|
}
|
|
true
|
|
}
|
|
|
|
fn remap_element_segment(&self, s: &mut ElementSegment) {
|
|
let mut i = s.index();
|
|
self.remap_table_idx(&mut i);
|
|
assert_eq!(s.index(), i);
|
|
for m in s.members_mut() {
|
|
self.remap_function_idx(m);
|
|
}
|
|
if let Some(offset) = s.offset_mut() {
|
|
self.remap_init_expr(offset);
|
|
}
|
|
}
|
|
|
|
fn remap_code_section(&self, s: &mut CodeSection) -> bool {
|
|
self.retain(
|
|
&self.analysis.functions,
|
|
s.bodies_mut(),
|
|
"code",
|
|
self.analysis.imported_functions,
|
|
);
|
|
for s in s.bodies_mut() {
|
|
self.remap_func_body(s);
|
|
}
|
|
s.bodies().len() > 0
|
|
}
|
|
|
|
fn remap_func_body(&self, b: &mut FuncBody) {
|
|
self.remap_code(b.code_mut());
|
|
}
|
|
|
|
fn remap_code(&self, c: &mut Instructions) {
|
|
for i in c.elements_mut() {
|
|
self.remap_instruction(i);
|
|
}
|
|
}
|
|
|
|
fn remap_instruction(&self, i: &mut Instruction) {
|
|
match *i {
|
|
Instruction::Block(ref mut b) |
|
|
Instruction::Loop(ref mut b) |
|
|
Instruction::If(ref mut b) => self.remap_block_type(b),
|
|
Instruction::Call(ref mut f) => self.remap_function_idx(f),
|
|
Instruction::CallIndirect(ref mut t, _) => self.remap_type_idx(t),
|
|
Instruction::GetGlobal(ref mut i) |
|
|
Instruction::SetGlobal(ref mut i) => self.remap_global_idx(i),
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
fn remap_block_type(&self, bt: &mut BlockType) {
|
|
match *bt {
|
|
BlockType::Value(ref mut v) => self.remap_value_type(v),
|
|
BlockType::NoResult => {}
|
|
}
|
|
}
|
|
|
|
fn remap_data_section(&self, s: &mut DataSection) -> bool {
|
|
for data in s.entries_mut() {
|
|
self.remap_data_segment(data);
|
|
}
|
|
true
|
|
}
|
|
|
|
fn remap_data_segment(&self, segment: &mut DataSegment) {
|
|
let mut i = segment.index();
|
|
self.remap_memory_idx(&mut i);
|
|
assert_eq!(segment.index(), i);
|
|
if let Some(offset) = segment.offset_mut() {
|
|
self.remap_init_expr(offset);
|
|
}
|
|
}
|
|
|
|
fn remap_type_idx(&self, i: &mut u32) {
|
|
*i = self.types[*i as usize];
|
|
assert!(*i != u32::max_value());
|
|
}
|
|
|
|
fn remap_function_idx(&self, i: &mut u32) {
|
|
*i = self.functions[*i as usize];
|
|
assert!(*i != u32::max_value());
|
|
}
|
|
|
|
fn remap_global_idx(&self, i: &mut u32) {
|
|
trace!("global {} => {}", *i, self.globals[*i as usize]);
|
|
*i = self.globals[*i as usize];
|
|
assert!(*i != u32::max_value());
|
|
}
|
|
|
|
fn remap_table_idx(&self, i: &mut u32) {
|
|
*i = self.tables[*i as usize];
|
|
assert!(*i != u32::max_value());
|
|
}
|
|
|
|
fn remap_memory_idx(&self, i: &mut u32) {
|
|
*i = self.memories[*i as usize];
|
|
assert!(*i != u32::max_value());
|
|
}
|
|
|
|
fn remap_name_section(&self, s: &mut NameSection) {
|
|
match *s {
|
|
NameSection::Module(_) => {}
|
|
NameSection::Function(ref mut f) => {
|
|
let map = f.names_mut();
|
|
let new = IndexMap::with_capacity(map.len());
|
|
for (idx, name) in mem::replace(map, new) {
|
|
let new_idx = self.functions[idx as usize];
|
|
let name = if self.config.demangle {
|
|
rustc_demangle::demangle(&name).to_string()
|
|
} else {
|
|
name
|
|
};
|
|
if new_idx != u32::max_value() {
|
|
map.insert(new_idx, name);
|
|
}
|
|
}
|
|
}
|
|
NameSection::Local(ref mut l) => {
|
|
let map = l.local_names_mut();
|
|
let new = IndexMap::with_capacity(map.len());
|
|
for (idx, value) in mem::replace(map, new) {
|
|
let new_idx = self.functions[idx as usize];
|
|
if new_idx != u32::max_value() {
|
|
map.insert(new_idx, value);
|
|
}
|
|
}
|
|
}
|
|
NameSection::Unparsed { .. } => {}
|
|
}
|
|
}
|
|
}
|