Skip to content

Commit 7a7815f

Browse files
authored
Refactor wasmprinter to rely less on a String output (#1592)
* Refactor how operand immediates are printed Delegate the space-before-immediate to printing the immediate itself rather than forcing a space between each immediate. Avoids the need to `pop()` or otherwise test if space is already present in situations where immediates are omitted. * Add a test exercising labels * Update how operators are printed in `wasmprinter` Previously operators were printed to a temporary buffer and then depending on what happened it would conditionally go into the actual module or instead be discarded (e.g. the last `end`). This commit refactors to instead avoid a temporary buffer entirely and unconditionally emit instructions to the real output. This is done by restructuring the printing slightly by having "before_op" and "after_op" hooks while printing which are used to manage nesting/newlines instead of the outer loop in the main printer. This is combined with a few other minor things such as a `is_end_then_eof` method which is used to avoid printing the final `end`. One minor test update happened here with a `delegate` instruction from the legacy exception-handling proposal which I think is a bugfix given my read of the older proposal. * Review comments
1 parent 9340ed2 commit 7a7815f

File tree

8 files changed

+205
-196
lines changed

8 files changed

+205
-196
lines changed

crates/wasmparser/src/binary_reader.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,6 +1712,12 @@ impl<'a> BinaryReader<'a> {
17121712
self.visit_operator(&mut OperatorFactory::new())
17131713
}
17141714

1715+
/// Returns whether there is an `end` opcode followed by eof remaining in
1716+
/// this reader.
1717+
pub fn is_end_then_eof(&self) -> bool {
1718+
self.remaining_buffer() == &[0x0b]
1719+
}
1720+
17151721
fn read_lane_index(&mut self, max: u8) -> Result<u8> {
17161722
let index = self.read_u8()?;
17171723
if index >= max {

crates/wasmparser/src/readers/core/operators.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,12 @@ impl<'a> OperatorsReader<'a> {
209209
pub fn get_binary_reader(&self) -> BinaryReader<'a> {
210210
self.reader.clone()
211211
}
212+
213+
/// Returns whether there is an `end` opcode followed by eof remaining in
214+
/// this reader.
215+
pub fn is_end_then_eof(&self) -> bool {
216+
self.reader.is_end_then_eof()
217+
}
212218
}
213219

214220
impl<'a> IntoIterator for OperatorsReader<'a> {

crates/wasmprinter/src/lib.rs

Lines changed: 13 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,9 +1281,9 @@ impl Printer {
12811281

12821282
let nesting_start = self.nesting;
12831283

1284-
let mut buf = String::new();
1285-
let mut op_printer = operator::PrintOperator::new(self, state);
1286-
while !body.eof() {
1284+
let mut op_printer =
1285+
operator::PrintOperator::new(self, state, operator::OperatorSeparator::Newline);
1286+
while !body.is_end_then_eof() {
12871287
// Branch hints are stored in increasing order of their body offset
12881288
// so print them whenever their instruction comes up.
12891289
if let Some(((hint_offset, hint), rest)) = branch_hints.split_first() {
@@ -1299,48 +1299,8 @@ impl Printer {
12991299
}
13001300
}
13011301

1302-
let offset = body.original_position();
1303-
mem::swap(&mut buf, &mut op_printer.printer.result);
1304-
let op_kind = body.visit_operator(&mut op_printer)??;
1305-
mem::swap(&mut buf, &mut op_printer.printer.result);
1306-
1307-
match op_kind {
1308-
// The final `end` in a reader is not printed, it's implied
1309-
// in the text format.
1310-
operator::OpKind::End if body.eof() => break,
1311-
1312-
// When we start a block we newline to the current
1313-
// indentation, then we increase the indentation so further
1314-
// instructions are tabbed over.
1315-
operator::OpKind::BlockStart => {
1316-
op_printer.printer.newline(offset);
1317-
op_printer.printer.nesting += 1;
1318-
}
1319-
1320-
// `else`/`catch` are special in that it's printed at
1321-
// the previous indentation, but it doesn't actually change
1322-
// our nesting level.
1323-
operator::OpKind::BlockMid => {
1324-
op_printer.printer.nesting -= 1;
1325-
op_printer.printer.newline(offset);
1326-
op_printer.printer.nesting += 1;
1327-
}
1328-
1329-
// Exiting a block prints `end` at the previous indentation
1330-
// level.
1331-
operator::OpKind::End | operator::OpKind::Delegate
1332-
if op_printer.printer.nesting > nesting_start =>
1333-
{
1334-
op_printer.printer.nesting -= 1;
1335-
op_printer.printer.newline(offset);
1336-
}
1337-
1338-
// .. otherwise everything else just has a normal newline
1339-
// out in front.
1340-
_ => op_printer.printer.newline(offset),
1341-
}
1342-
op_printer.printer.result.push_str(&buf);
1343-
buf.truncate(0);
1302+
op_printer.op_offset = body.original_position();
1303+
body.visit_operator(&mut op_printer)??;
13441304
}
13451305

13461306
// If this was an invalid function body then the nesting may not
@@ -1596,40 +1556,14 @@ impl Printer {
15961556
explicit: &str,
15971557
) -> Result<()> {
15981558
self.start_group("");
1599-
let mut prev = mem::take(&mut self.result);
16001559
let mut reader = expr.get_operators_reader();
1601-
let mut op_printer = operator::PrintOperator::new(self, state);
1602-
let mut first_op = None;
1603-
for i in 0.. {
1604-
if reader.eof() {
1605-
break;
1606-
}
1607-
match reader.visit_operator(&mut op_printer)?? {
1608-
operator::OpKind::End if reader.eof() => {}
16091560

1610-
_ if i == 0 => first_op = Some(mem::take(&mut op_printer.printer.result)),
1611-
1612-
_ => {
1613-
if i == 1 {
1614-
prev.push_str(explicit);
1615-
prev.push(' ');
1616-
prev.push_str(&first_op.take().unwrap());
1617-
}
1618-
prev.push(' ');
1619-
prev.push_str(&op_printer.printer.result);
1620-
}
1621-
}
1622-
1623-
op_printer.printer.result.truncate(0);
1561+
if reader.read().is_ok() && !reader.is_end_then_eof() {
1562+
write!(self.result, "{explicit} ")?;
16241563
}
16251564

1626-
// If `first_op` is still set here then it means we don't need to print
1627-
// an expression with `explicit` as the leading token, instead we can
1628-
// print the single operator.
1629-
if let Some(op) = first_op {
1630-
prev.push_str(&op);
1631-
}
1632-
self.result = prev;
1565+
self.print_const_expr(state, expr)?;
1566+
16331567
self.end_group();
16341568
Ok(())
16351569
}
@@ -1639,23 +1573,16 @@ impl Printer {
16391573
let mut reader = expr.get_operators_reader();
16401574
let mut first = true;
16411575

1642-
let mut result = mem::take(&mut self.result);
1643-
let mut op_printer = operator::PrintOperator::new(self, state);
1644-
while !reader.eof() {
1576+
let mut op_printer =
1577+
operator::PrintOperator::new(self, state, operator::OperatorSeparator::None);
1578+
while !reader.is_end_then_eof() {
16451579
if first {
16461580
first = false;
16471581
} else {
16481582
op_printer.printer.result.push(' ');
16491583
}
1650-
match reader.visit_operator(&mut op_printer)?? {
1651-
operator::OpKind::End if reader.eof() => {}
1652-
_ => {
1653-
result.push_str(&op_printer.printer.result);
1654-
op_printer.printer.result.truncate(0);
1655-
}
1656-
}
1584+
reader.visit_operator(&mut op_printer)??;
16571585
}
1658-
self.result = result;
16591586
Ok(())
16601587
}
16611588

0 commit comments

Comments
 (0)