Skip to content

Commit d514f70

Browse files
authored
Add --name-unnamed to wasm-tools print (#1404)
Sometimes I've wanted to twiddle with wasm files but I don't enjoy having to update a bunch of indices. This option can be used to automatically assign a name to everything in a file to more easily make the file human editable.
1 parent dbf9666 commit d514f70

File tree

3 files changed

+176
-52
lines changed

3 files changed

+176
-52
lines changed

crates/wasmprinter/src/lib.rs

Lines changed: 151 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use anyhow::{bail, Context, Result};
1212
use std::collections::{HashMap, HashSet};
1313
use std::fmt::{self, Write};
14+
use std::marker;
1415
use std::mem;
1516
use std::path::Path;
1617
use wasmparser::*;
@@ -54,6 +55,7 @@ pub struct Printer {
5455
line: usize,
5556
group_lines: Vec<usize>,
5657
code_section_hints: Vec<(u32, Vec<(usize, BranchHint)>)>,
58+
name_unnamed: bool,
5759
}
5860

5961
#[derive(Default)]
@@ -66,18 +68,41 @@ struct CoreState {
6668
tables: u32,
6769
modules: u32,
6870
instances: u32,
69-
func_names: HashMap<u32, Naming>,
70-
local_names: HashMap<(u32, u32), Naming>,
71-
label_names: HashMap<(u32, u32), Naming>,
72-
type_names: HashMap<u32, Naming>,
73-
tag_names: HashMap<u32, Naming>,
74-
table_names: HashMap<u32, Naming>,
75-
memory_names: HashMap<u32, Naming>,
76-
global_names: HashMap<u32, Naming>,
77-
element_names: HashMap<u32, Naming>,
78-
data_names: HashMap<u32, Naming>,
79-
module_names: HashMap<u32, Naming>,
80-
instance_names: HashMap<u32, Naming>,
71+
func_names: NamingMap<u32, NameFunc>,
72+
local_names: NamingMap<(u32, u32), NameLocal>,
73+
label_names: NamingMap<(u32, u32), NameLabel>,
74+
type_names: NamingMap<u32, NameType>,
75+
tag_names: NamingMap<u32, NameTag>,
76+
table_names: NamingMap<u32, NameTable>,
77+
memory_names: NamingMap<u32, NameMemory>,
78+
global_names: NamingMap<u32, NameGlobal>,
79+
element_names: NamingMap<u32, NameElem>,
80+
data_names: NamingMap<u32, NameData>,
81+
module_names: NamingMap<u32, NameModule>,
82+
instance_names: NamingMap<u32, NameInstance>,
83+
}
84+
85+
/// A map of index-to-name for tracking what are the contents of the name
86+
/// section.
87+
///
88+
/// The type parameter `T` is either `u32` for most index-based maps or a `(u32,
89+
/// u32)` for label/local maps where there are two levels of indices.
90+
///
91+
/// The type parameter `K` is a static description/namespace for what kind of
92+
/// item is contained within this map. That's used by some helper methods to
93+
/// synthesize reasonable names automatically.
94+
struct NamingMap<T, K> {
95+
index_to_name: HashMap<T, Naming>,
96+
_marker: marker::PhantomData<K>,
97+
}
98+
99+
impl<T, K> Default for NamingMap<T, K> {
100+
fn default() -> NamingMap<T, K> {
101+
NamingMap {
102+
index_to_name: HashMap::new(),
103+
_marker: marker::PhantomData,
104+
}
105+
}
81106
}
82107

83108
#[derive(Default)]
@@ -87,11 +112,11 @@ struct ComponentState {
87112
instances: u32,
88113
components: u32,
89114
values: u32,
90-
type_names: HashMap<u32, Naming>,
91-
func_names: HashMap<u32, Naming>,
92-
component_names: HashMap<u32, Naming>,
93-
instance_names: HashMap<u32, Naming>,
94-
value_names: HashMap<u32, Naming>,
115+
type_names: NamingMap<u32, NameType>,
116+
func_names: NamingMap<u32, NameFunc>,
117+
component_names: NamingMap<u32, NameComponent>,
118+
instance_names: NamingMap<u32, NameInstance>,
119+
value_names: NamingMap<u32, NameValue>,
95120
}
96121

97122
struct State {
@@ -136,6 +161,20 @@ impl Printer {
136161
self.print_skeleton = print;
137162
}
138163

164+
/// Assign names to all unnamed items.
165+
///
166+
/// If enabled then any previously unnamed item will have a name synthesized
167+
/// that looks like `$#func10` for example. The leading `#` indicates that
168+
/// it's `wasmprinter`-generated. The `func` is the namespace of the name
169+
/// and provides extra context about the item when referenced. The 10 is the
170+
/// local index of the item.
171+
///
172+
/// Note that if the resulting text output is converted back to binary the
173+
/// resulting `name` custom section will not be the same as before.
174+
pub fn name_unnamed(&mut self, enable: bool) {
175+
self.name_unnamed = enable;
176+
}
177+
139178
/// Registers a custom `printer` function to get invoked whenever a custom
140179
/// section of name `section` is seen.
141180
///
@@ -552,8 +591,8 @@ impl Printer {
552591
}
553592

554593
fn register_names(&mut self, state: &mut State, names: NameSectionReader<'_>) -> Result<()> {
555-
fn indirect_name_map(
556-
into: &mut HashMap<(u32, u32), Naming>,
594+
fn indirect_name_map<K>(
595+
into: &mut NamingMap<(u32, u32), K>,
557596
names: IndirectNameMap<'_>,
558597
name: &str,
559598
) -> Result<()> {
@@ -567,7 +606,7 @@ impl Printer {
567606
};
568607
for naming in indirect.names {
569608
let naming = naming?;
570-
into.insert(
609+
into.index_to_name.insert(
571610
(indirect.index, naming.index),
572611
Naming::new(naming.name, naming.index, name, used.as_mut()),
573612
);
@@ -811,8 +850,7 @@ impl Printer {
811850
// we need to be careful to terminate previous param blocks and open
812851
// a new one if that's the case with a named parameter.
813852
for (i, param) in ty.params().iter().enumerate() {
814-
let name = names_for.and_then(|n| state.core.local_names.get(&(n, i as u32)));
815-
params.start_local(name, &mut self.result);
853+
params.start_local(names_for.unwrap_or(u32::MAX), i as u32, self, state);
816854
self.print_valtype(*param)?;
817855
params.end_local(&mut self.result);
818856
}
@@ -1171,8 +1209,7 @@ impl Printer {
11711209
self.newline(offset);
11721210
first = false;
11731211
}
1174-
let name = state.core.local_names.get(&(func_idx, params + local_idx));
1175-
locals.start_local(name, &mut self.result);
1212+
locals.start_local(func_idx, params + local_idx, self, state);
11761213
self.print_valtype(ty)?;
11771214
locals.end_local(&mut self.result);
11781215
local_idx += 1;
@@ -1345,28 +1382,55 @@ impl Printer {
13451382
Ok(())
13461383
}
13471384

1348-
fn print_idx(&mut self, names: &HashMap<u32, Naming>, idx: u32) -> Result<()> {
1385+
fn print_idx<K>(&mut self, names: &NamingMap<u32, K>, idx: u32) -> Result<()>
1386+
where
1387+
K: NamingNamespace,
1388+
{
1389+
self._print_idx(&names.index_to_name, idx, K::desc())
1390+
}
1391+
1392+
fn _print_idx(&mut self, names: &HashMap<u32, Naming>, idx: u32, desc: &str) -> Result<()> {
13491393
match names.get(&idx) {
13501394
Some(name) => write!(self.result, "${}", name.identifier())?,
1351-
None => write!(self.result, "{}", idx)?,
1395+
None if self.name_unnamed => write!(self.result, "$#{desc}{idx}")?,
1396+
None => write!(self.result, "{idx}")?,
13521397
}
13531398
Ok(())
13541399
}
13551400

13561401
fn print_local_idx(&mut self, state: &State, func: u32, idx: u32) -> Result<()> {
1357-
match state.core.local_names.get(&(func, idx)) {
1402+
match state.core.local_names.index_to_name.get(&(func, idx)) {
13581403
Some(name) => write!(self.result, "${}", name.identifier())?,
1404+
None if self.name_unnamed => write!(self.result, "$#local{idx}")?,
13591405
None => write!(self.result, "{}", idx)?,
13601406
}
13611407
Ok(())
13621408
}
13631409

1364-
fn print_name(&mut self, names: &HashMap<u32, Naming>, cur_idx: u32) -> Result<()> {
1365-
if let Some(name) = names.get(&cur_idx) {
1366-
name.write(&mut self.result);
1367-
self.result.push(' ');
1410+
fn print_name<K>(&mut self, names: &NamingMap<u32, K>, cur_idx: u32) -> Result<()>
1411+
where
1412+
K: NamingNamespace,
1413+
{
1414+
self._print_name(&names.index_to_name, cur_idx, K::desc())
1415+
}
1416+
1417+
fn _print_name(
1418+
&mut self,
1419+
names: &HashMap<u32, Naming>,
1420+
cur_idx: u32,
1421+
desc: &str,
1422+
) -> Result<()> {
1423+
match names.get(&cur_idx) {
1424+
Some(name) => {
1425+
name.write(&mut self.result);
1426+
self.result.push(' ');
1427+
}
1428+
None if self.name_unnamed => {
1429+
write!(self.result, "$#{desc}{cur_idx} ")?;
1430+
}
1431+
None => {}
13681432
}
1369-
write!(self.result, "(;{};)", cur_idx)?;
1433+
write!(self.result, "(;{cur_idx};)")?;
13701434
Ok(())
13711435
}
13721436

@@ -2764,35 +2828,46 @@ impl NamedLocalPrinter {
27642828
}
27652829
}
27662830

2767-
fn start_local(&mut self, name: Option<&Naming>, dst: &mut String) {
2831+
fn start_local(&mut self, func: u32, local: u32, dst: &mut Printer, state: &State) {
2832+
let name = state.core.local_names.index_to_name.get(&(func, local));
2833+
27682834
// Named locals must be in their own group, so if we have a name we need
27692835
// to terminate the previous group.
27702836
if name.is_some() && self.in_group {
2771-
dst.push(')');
2837+
dst.result.push(')');
27722838
self.in_group = false;
27732839
}
27742840

27752841
if self.first {
27762842
self.first = false;
27772843
} else {
2778-
dst.push(' ');
2844+
dst.result.push(' ');
27792845
}
27802846

27812847
// Next we either need a separator if we're already in a group or we
27822848
// need to open a group for our new local.
27832849
if !self.in_group {
2784-
dst.push('(');
2785-
dst.push_str(self.group_name);
2786-
dst.push(' ');
2850+
dst.result.push('(');
2851+
dst.result.push_str(self.group_name);
2852+
dst.result.push(' ');
27872853
self.in_group = true;
27882854
}
27892855

27902856
// Print the optional name if given...
2791-
if let Some(name) = name {
2792-
name.write(dst);
2793-
dst.push(' ');
2857+
match name {
2858+
Some(name) => {
2859+
name.write(&mut dst.result);
2860+
dst.result.push(' ');
2861+
self.end_group_after_local = true;
2862+
}
2863+
None if dst.name_unnamed => {
2864+
dst.result.push_str(&format!("$#local{local} "));
2865+
self.end_group_after_local = true;
2866+
}
2867+
None => {
2868+
self.end_group_after_local = false;
2869+
}
27942870
}
2795-
self.end_group_after_local = name.is_some();
27962871
}
27972872

27982873
fn end_local(&mut self, dst: &mut String) {
@@ -3019,11 +3094,43 @@ impl Naming {
30193094
}
30203095
}
30213096

3022-
fn name_map(into: &mut HashMap<u32, Naming>, names: NameMap<'_>, name: &str) -> Result<()> {
3097+
/// Helper trait for the `NamingMap` type's `K` type parameter.
3098+
trait NamingNamespace {
3099+
fn desc() -> &'static str;
3100+
}
3101+
3102+
macro_rules! naming_namespaces {
3103+
($(struct $name:ident => $desc:tt)*) => ($(
3104+
struct $name;
3105+
3106+
impl NamingNamespace for $name {
3107+
fn desc() -> &'static str { $desc }
3108+
}
3109+
)*)
3110+
}
3111+
3112+
naming_namespaces! {
3113+
struct NameFunc => "func"
3114+
struct NameModule => "module"
3115+
struct NameInstance => "instance"
3116+
struct NameGlobal => "global"
3117+
struct NameMemory => "memory"
3118+
struct NameLocal => "local"
3119+
struct NameLabel => "label"
3120+
struct NameTable => "table"
3121+
struct NameValue => "value"
3122+
struct NameType => "type"
3123+
struct NameData => "data"
3124+
struct NameElem => "elem"
3125+
struct NameComponent => "component"
3126+
struct NameTag => "tag"
3127+
}
3128+
3129+
fn name_map<K>(into: &mut NamingMap<u32, K>, names: NameMap<'_>, name: &str) -> Result<()> {
30233130
let mut used = HashSet::new();
30243131
for naming in names {
30253132
let naming = naming?;
3026-
into.insert(
3133+
into.index_to_name.insert(
30273134
naming.index,
30283135
Naming::new(naming.name, naming.index, name, Some(&mut used)),
30293136
);

crates/wasmprinter/src/operator.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,18 @@ impl<'a, 'b> PrintOperator<'a, 'b> {
6868
}
6969

7070
let key = (self.state.core.funcs, self.label);
71-
let has_name = if let Some(name) = self.state.core.label_names.get(&key) {
72-
self.printer.result.push_str(" ");
73-
name.write(&mut self.printer.result);
74-
true
75-
} else {
76-
false
71+
let has_name = match self.state.core.label_names.index_to_name.get(&key) {
72+
Some(name) => {
73+
self.printer.result.push_str(" ");
74+
name.write(&mut self.printer.result);
75+
true
76+
}
77+
None if self.printer.name_unnamed => {
78+
let depth = self.cur_depth();
79+
write!(self.result(), " $#label{depth}")?;
80+
true
81+
}
82+
None => false,
7783
};
7884
match ty {
7985
BlockType::Empty => {}
@@ -127,7 +133,7 @@ impl<'a, 'b> PrintOperator<'a, 'b> {
127133
.and_then(|idx| self.label_indices.get(idx as usize).copied())
128134
.and_then(|label_idx| {
129135
let key = (self.state.core.funcs, label_idx);
130-
self.state.core.label_names.get(&key)
136+
self.state.core.label_names.index_to_name.get(&key)
131137
});
132138

133139
// This is a bit tricky, but if there's a shallower label than
@@ -139,7 +145,7 @@ impl<'a, 'b> PrintOperator<'a, 'b> {
139145
let name = name.and_then(|name| {
140146
for other_label in self.label_indices[i as usize..].iter() {
141147
let key = (self.state.core.funcs, *other_label);
142-
if let Some(other) = self.state.core.label_names.get(&key) {
148+
if let Some(other) = self.state.core.label_names.index_to_name.get(&key) {
143149
if name.name == other.name {
144150
return None;
145151
}
@@ -151,6 +157,8 @@ impl<'a, 'b> PrintOperator<'a, 'b> {
151157
match name {
152158
Some(name) => name.write(&mut self.printer.result),
153159

160+
None if self.printer.name_unnamed => write!(self.result(), "$#label{i}")?,
161+
154162
// If this label has some name also print its pseudo-name as
155163
// `@N` to help match things up in the text format.
156164
None => write!(self.result(), "{depth} (;@{i};)")?,

src/bin/wasm-tools/print.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ pub struct Opts {
1717
/// replaced with "..." instead of printing their actual contents.
1818
#[clap(long)]
1919
skeleton: bool,
20+
21+
/// Ensure all wasm items have `$`-based names, even if they don't have an
22+
/// entry in the `name` section.
23+
///
24+
/// This option, when enabled, will synthesize names for any item which
25+
/// doesn't previously have a name.
26+
#[clap(long)]
27+
name_unnamed: bool,
2028
}
2129

2230
impl Opts {
@@ -29,6 +37,7 @@ impl Opts {
2937
let mut printer = wasmprinter::Printer::new();
3038
printer.print_offsets(self.print_offsets);
3139
printer.print_skeleton(self.skeleton);
40+
printer.name_unnamed(self.name_unnamed);
3241
let wat = printer.print(&wasm)?;
3342
self.io.output(wasm_tools::Output::Wat(&wat))?;
3443
Ok(())

0 commit comments

Comments
 (0)