Skip to content

Commit 40d3637

Browse files
authored
wasmprinter: Add the Printer::offsets_and_lines method (#955)
This gives you the line-by-line WAT disassembly for the given Wasm bytes, along with the binary offset associated with each line.
1 parent 6b85ae2 commit 40d3637

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

crates/wasmprinter/src/lib.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ pub struct Printer {
4444
print_offsets: bool,
4545
printers: HashMap<String, Box<dyn FnMut(&mut Printer, usize, &[u8]) -> Result<()>>>,
4646
result: String,
47+
/// The `i`th line in `result` is at offset `lines[i]`.
48+
lines: Vec<usize>,
49+
/// The binary offset for the `i`th line is `line_offsets[i]`.
50+
line_offsets: Vec<Option<usize>>,
4751
nesting: u32,
4852
line: usize,
4953
group_lines: Vec<usize>,
@@ -160,6 +164,29 @@ impl Printer {
160164
Ok(mem::take(&mut self.result))
161165
}
162166

167+
/// Get the line-by-line WAT disassembly for the given Wasm, along with the
168+
/// binary offsets for each line.
169+
pub fn offsets_and_lines<'a>(
170+
&'a mut self,
171+
wasm: &[u8],
172+
) -> Result<impl Iterator<Item = (Option<usize>, &'a str)> + 'a> {
173+
self.print_contents(wasm)?;
174+
175+
let end = self.result.len();
176+
let result = &self.result;
177+
178+
let mut offsets = self.line_offsets.iter().copied();
179+
let mut lines = self.lines.iter().copied().peekable();
180+
181+
Ok(std::iter::from_fn(move || {
182+
let offset = offsets.next()?;
183+
let i = lines.next()?;
184+
let j = lines.peek().copied().unwrap_or(end);
185+
let line = &result[i..j];
186+
Some((offset, line))
187+
}))
188+
}
189+
163190
fn read_names_and_code<'a>(
164191
&mut self,
165192
mut bytes: &'a [u8],
@@ -233,6 +260,11 @@ impl Printer {
233260
}
234261

235262
fn print_contents(&mut self, mut bytes: &[u8]) -> Result<()> {
263+
self.lines.clear();
264+
self.lines.push(0);
265+
self.line_offsets.clear();
266+
self.line_offsets.push(Some(0));
267+
236268
let mut expected = None;
237269
let mut states: Vec<State> = Vec::new();
238270
let mut parser = Parser::new(0);
@@ -1009,6 +1041,10 @@ impl Printer {
10091041

10101042
fn print_newline(&mut self, offset: Option<usize>) {
10111043
self.result.push('\n');
1044+
1045+
self.lines.push(self.result.len());
1046+
self.line_offsets.push(offset);
1047+
10121048
if self.print_offsets {
10131049
match offset {
10141050
Some(offset) => write!(self.result, "(;@{offset:<6x};)").unwrap(),

crates/wasmprinter/tests/all.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,33 @@ fn label_shadowing_locals() {
234234
MODULE.replace(" ", "").trim()
235235
);
236236
}
237+
238+
#[test]
239+
fn offsets_and_lines_smoke_test() {
240+
const MODULE: &str = r#"
241+
(;@0 ;) (module
242+
(;@b ;) (type (;0;) (func (param i32) (result i32)))
243+
(;@1f ;) (func (;0;) (type 0) (param i32) (result i32)
244+
(;@20 ;) local.get 0
245+
)
246+
(;@17 ;) (export "f" (func 0))
247+
)
248+
"#;
249+
let bytes = wat::parse_str(MODULE).unwrap();
250+
251+
let mut printer = wasmprinter::Printer::new();
252+
let actual: Vec<_> = printer.offsets_and_lines(&bytes).unwrap().collect();
253+
254+
#[rustfmt::skip]
255+
let expected = vec![
256+
(Some(0), "(module\n"),
257+
(Some(0xb), " (type (;0;) (func (param i32) (result i32)))\n"),
258+
(Some(0x1f), " (func (;0;) (type 0) (param i32) (result i32)\n"),
259+
(Some(0x20), " local.get 0\n"),
260+
(None, " )\n"),
261+
(Some(0x17), " (export \"f\" (func 0))\n"),
262+
(None, ")"),
263+
];
264+
265+
assert_eq!(actual, expected);
266+
}

0 commit comments

Comments
 (0)