99 lines
3.3 KiB
Rust
99 lines
3.3 KiB
Rust
|
|
//! Disassembly calling function for `wasm32` targets.
|
||
|
|
use wasm_bindgen::prelude::*;
|
||
|
|
|
||
|
|
use ::*;
|
||
|
|
|
||
|
|
#[wasm_bindgen(module = "child_process")]
|
||
|
|
extern "C" {
|
||
|
|
#[wasm_bindgen(js_name = execSync)]
|
||
|
|
fn exec_sync(cmd: &str) -> Buffer;
|
||
|
|
}
|
||
|
|
|
||
|
|
#[wasm_bindgen(module = "buffer")]
|
||
|
|
extern "C" {
|
||
|
|
type Buffer;
|
||
|
|
#[wasm_bindgen(method, js_name = toString)]
|
||
|
|
fn to_string(this: &Buffer) -> String;
|
||
|
|
}
|
||
|
|
|
||
|
|
#[wasm_bindgen]
|
||
|
|
extern "C" {
|
||
|
|
#[wasm_bindgen(js_namespace = require)]
|
||
|
|
fn resolve(module: &str) -> String;
|
||
|
|
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||
|
|
fn js_console_log(s: &str);
|
||
|
|
}
|
||
|
|
|
||
|
|
// println! doesn't work on wasm32 right now, so shadow the compiler's println!
|
||
|
|
// macro with our own shim that redirects to `console.log`.
|
||
|
|
#[allow(unused)]
|
||
|
|
macro_rules! println {
|
||
|
|
($($args:tt)*) => (js_console_log(&format!($($args)*)))
|
||
|
|
}
|
||
|
|
|
||
|
|
pub(crate) fn disassemble_myself() -> HashMap<String, Vec<Function>> {
|
||
|
|
use std::path::Path;
|
||
|
|
// Our wasm module in the wasm-bindgen test harness is called
|
||
|
|
// "wasm-bindgen-test_bg". When running in node this is actually a shim JS
|
||
|
|
// file. Ask node where that JS file is, and then we use that with a wasm
|
||
|
|
// extension to find the wasm file itself.
|
||
|
|
let js_shim = resolve("wasm-bindgen-test_bg");
|
||
|
|
let js_shim = Path::new(&js_shim).with_extension("wasm");
|
||
|
|
|
||
|
|
// Execute `wasm2wat` synchronously, waiting for and capturing all of its
|
||
|
|
// output.
|
||
|
|
let output =
|
||
|
|
exec_sync(&format!("wasm2wat {}", js_shim.display())).to_string();
|
||
|
|
|
||
|
|
let mut ret: HashMap<String, Vec<Function>> = HashMap::new();
|
||
|
|
let mut lines = output.lines().map(|s| s.trim());
|
||
|
|
while let Some(line) = lines.next() {
|
||
|
|
// If we found the table of function pointers, fill in the known
|
||
|
|
// address for all our `Function` instances
|
||
|
|
if line.starts_with("(elem") {
|
||
|
|
for (i, name) in line.split_whitespace().skip(3).enumerate() {
|
||
|
|
let name = name.trim_right_matches(")");
|
||
|
|
for f in ret.get_mut(name).expect("ret.get_mut(name) failed") {
|
||
|
|
f.addr = Some(i + 1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// If this isn't a function, we don't care about it.
|
||
|
|
if !line.starts_with("(func ") {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
let mut function = Function {
|
||
|
|
instrs: Vec::new(),
|
||
|
|
addr: None,
|
||
|
|
};
|
||
|
|
|
||
|
|
// Empty functions will end in `))` so there's nothing to do, otherwise
|
||
|
|
// we'll have a bunch of following lines which are instructions.
|
||
|
|
//
|
||
|
|
// Lines that have an imbalanced `)` mark the end of a function.
|
||
|
|
if !line.ends_with("))") {
|
||
|
|
while let Some(line) = lines.next() {
|
||
|
|
function.instrs.push(Instruction {
|
||
|
|
parts: line
|
||
|
|
.split_whitespace()
|
||
|
|
.map(|s| s.to_string())
|
||
|
|
.collect(),
|
||
|
|
});
|
||
|
|
if !line.starts_with("(") && line.ends_with(")") {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// The second element here split on whitespace should be the name of
|
||
|
|
// the function, skipping the type/params/results
|
||
|
|
ret.entry(line.split_whitespace().nth(1).unwrap().to_string())
|
||
|
|
.or_insert(Vec::new())
|
||
|
|
.push(function);
|
||
|
|
}
|
||
|
|
return ret;
|
||
|
|
}
|