Skip to content

Commit 9801046

Browse files
authored
Print custom sections by default in wasmprinter (#1449)
* Print custom sections by default in `wasmprinter` The annotations proposal for WebAssembly added `@custom` syntax to the text format of WebAssembly and while `wast` has supported parsing it for quite some time now the `wasmprinter` half has not printed it. This was mostly just for historical reasons, but this decision ends up causing confusion for folks when they round-trip a binary through the text format and are surprised when the result is different. An example of this is that the `component-type*` custom sections for components are lost when roundtripping and that can break the componentization process. This commit updates to instead print these sections by default. That does mean that many modules coming out of compilers may have lots of DWARF information now printed, but this feels like a better default than always ignoring them entirely. It's worth noting here that `wasmprinter` and `wast` try to round-trip the structure of a module but won't ever round trip a module byte-for-byte. For example overlong LEB encodings will never be reproduced. This means that while this commit will roundtrip custom sections it won't guarantee that the custom section continues to work because the offsets within the module when converted back to binary may still be different. * Fix some mutation tests to work on 32 and 64-bit * Normalize WIT comments to `\n` from `\r\n`
1 parent bb37002 commit 9801046

56 files changed

Lines changed: 222 additions & 34 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/wasm-mutate/src/info.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -317,18 +317,12 @@ impl<'a> ModuleInfo<'a> {
317317
assert!(src_idx < self.raw_sections.len());
318318
assert!(dest_idx < self.raw_sections.len());
319319
assert_ne!(src_idx, dest_idx);
320+
let mut sections = self.raw_sections.clone();
321+
sections.swap(src_idx, dest_idx);
320322
let mut module = wasm_encoder::Module::new();
321-
self.raw_sections.iter().enumerate().for_each(|(i, s)| {
322-
if src_idx < dest_idx && i == dest_idx {
323-
module.section(&self.raw_sections[src_idx]);
324-
}
325-
if i != src_idx {
326-
module.section(s);
327-
}
328-
if dest_idx < src_idx && i == dest_idx {
329-
module.section(&self.raw_sections[src_idx]);
330-
}
331-
});
323+
for section in sections {
324+
module.section(&section);
325+
}
332326
module
333327
}
334328

crates/wasm-mutate/src/mutators/custom.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ mod tests {
162162
AddCustomSectionMutator,
163163
r#"
164164
(module
165-
(@custom "a" "b")
165+
(@custom "" "")
166166
)
167167
"#,
168168
);
@@ -224,13 +224,13 @@ mod tests {
224224
crate::mutators::match_mutation(
225225
r#"
226226
(module
227-
(@custom "name" "data")
227+
(@custom "custom-name" "data")
228228
)
229229
"#,
230230
CustomSectionMutator,
231231
r#"
232232
(module
233-
(@custom "n" "data")
233+
(@custom "custom-nam" "data")
234234
)
235235
"#,
236236
);
@@ -241,15 +241,15 @@ mod tests {
241241
crate::mutators::match_mutation(
242242
r#"
243243
(module
244-
(@custom "name" "data")
245244
(@custom "name2" "data")
245+
(@custom "name3" "data")
246246
)
247247
"#,
248248
ReorderCustomSectionMutator,
249249
r#"
250250
(module
251+
(@custom "name3" "data")
251252
(@custom "name2" "data")
252-
(@custom "name" "data")
253253
)
254254
"#,
255255
)

crates/wasmprinter/src/lib.rs

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ struct State {
124124
name: Option<Naming>,
125125
core: CoreState,
126126
component: ComponentState,
127+
custom_section_place: Option<&'static str>,
127128
}
128129

129130
impl State {
@@ -133,6 +134,7 @@ impl State {
133134
name: None,
134135
core: CoreState::default(),
135136
component: ComponentState::default(),
137+
custom_section_place: None,
136138
}
137139
}
138140
}
@@ -351,6 +353,7 @@ impl Printer {
351353
match encoding {
352354
Encoding::Module => {
353355
states.push(State::new(Encoding::Module));
356+
states.last_mut().unwrap().custom_section_place = Some("before first");
354357
if states.len() > 1 {
355358
self.start_group("core module");
356359
} else {
@@ -414,7 +417,8 @@ impl Printer {
414417
continue;
415418
}
416419
let cur = self.result.len();
417-
let err = match self.print_custom_section(c.clone()) {
420+
let state = states.last().unwrap();
421+
let err = match self.print_custom_section(state, c.clone()) {
418422
Ok(()) => continue,
419423
Err(e) => e,
420424
};
@@ -429,13 +433,19 @@ impl Printer {
429433
self.result.push_str(line);
430434
}
431435
self.newline(c.range().end);
436+
self.print_raw_custom_section(state, c)?;
437+
}
438+
Payload::TypeSection(s) => {
439+
self.update_custom_section_place(&mut states, "after type");
440+
self.print_types(states.last_mut().unwrap(), s)?;
432441
}
433-
Payload::TypeSection(s) => self.print_types(states.last_mut().unwrap(), s)?,
434442
Payload::ImportSection(s) => {
443+
self.update_custom_section_place(&mut states, "after import");
435444
Self::ensure_module(&states)?;
436445
self.print_imports(states.last_mut().unwrap(), s)?
437446
}
438447
Payload::FunctionSection(reader) => {
448+
self.update_custom_section_place(&mut states, "after func");
439449
Self::ensure_module(&states)?;
440450
if mem::replace(&mut code_printed, true) {
441451
bail!("function section appeared twice in module");
@@ -446,39 +456,47 @@ impl Printer {
446456
self.print_code(states.last_mut().unwrap(), &code, reader)?;
447457
}
448458
Payload::TableSection(s) => {
459+
self.update_custom_section_place(&mut states, "after table");
449460
Self::ensure_module(&states)?;
450461
self.print_tables(states.last_mut().unwrap(), s)?
451462
}
452463
Payload::MemorySection(s) => {
464+
self.update_custom_section_place(&mut states, "after memory");
453465
Self::ensure_module(&states)?;
454466
self.print_memories(states.last_mut().unwrap(), s)?
455467
}
456468
Payload::TagSection(s) => {
469+
self.update_custom_section_place(&mut states, "after tag");
457470
Self::ensure_module(&states)?;
458471
self.print_tags(states.last_mut().unwrap(), s)?
459472
}
460473
Payload::GlobalSection(s) => {
474+
self.update_custom_section_place(&mut states, "after global");
461475
Self::ensure_module(&states)?;
462476
self.print_globals(states.last_mut().unwrap(), s)?
463477
}
464478
Payload::ExportSection(s) => {
479+
self.update_custom_section_place(&mut states, "after export");
465480
Self::ensure_module(&states)?;
466481
self.print_exports(states.last().unwrap(), s)?
467482
}
468483
Payload::StartSection { func, range } => {
484+
self.update_custom_section_place(&mut states, "after start");
469485
Self::ensure_module(&states)?;
470486
self.newline(range.start);
471487
self.start_group("start ");
472488
self.print_idx(&states.last().unwrap().core.func_names, func)?;
473489
self.end_group();
474490
}
475491
Payload::ElementSection(s) => {
492+
self.update_custom_section_place(&mut states, "after element");
476493
Self::ensure_module(&states)?;
477494
self.print_elems(states.last_mut().unwrap(), s)?;
478495
}
479496
// printed with the `Function` section, so we
480497
// skip this section
481498
Payload::CodeSectionStart { size, .. } => {
499+
self.update_custom_section_place(&mut states, "after code");
482500
Self::ensure_module(&states)?;
483501
bytes = &bytes[size as usize..];
484502
parser.skip_section();
@@ -489,6 +507,7 @@ impl Printer {
489507
// not part of the text format
490508
}
491509
Payload::DataSection(s) => {
510+
self.update_custom_section_place(&mut states, "after data");
492511
Self::ensure_module(&states)?;
493512
self.print_data(states.last_mut().unwrap(), s)?;
494513
}
@@ -577,6 +596,14 @@ impl Printer {
577596
Ok(())
578597
}
579598

599+
fn update_custom_section_place(&self, states: &mut Vec<State>, place: &'static str) {
600+
if let Some(last) = states.last_mut() {
601+
if let Some(prev) = &mut last.custom_section_place {
602+
*prev = place;
603+
}
604+
}
605+
}
606+
580607
fn start_group(&mut self, name: &str) {
581608
self.result.push('(');
582609
self.result.push_str(name);
@@ -2661,8 +2688,15 @@ impl Printer {
26612688
self.result.push(to_hex(byte & 0xf));
26622689
}
26632690

2664-
fn print_custom_section(&mut self, section: CustomSectionReader<'_>) -> Result<()> {
2691+
fn print_custom_section(
2692+
&mut self,
2693+
state: &State,
2694+
section: CustomSectionReader<'_>,
2695+
) -> Result<()> {
26652696
match section.name() {
2697+
// For now `wasmprinter` has invented syntax for `producers` and
2698+
// `dylink.0` below to use in tests. Note that this syntax is not
2699+
// official at this time.
26662700
"producers" => {
26672701
self.newline(section.range().start);
26682702
self.print_producers_section(ProducersSectionReader::new(
@@ -2677,10 +2711,35 @@ impl Printer {
26772711
section.data_offset(),
26782712
))
26792713
}
2680-
_ => Ok(()),
2714+
2715+
// These are parsed during `read_names_and_code` and are part of
2716+
// printing elsewhere, so don't print them.
2717+
"name" | "component-name" | "metadata.code.branch_hint" => Ok(()),
2718+
2719+
// Unknown custom sections get a `@custom` annotation printed.
2720+
_ => self.print_raw_custom_section(state, section),
26812721
}
26822722
}
26832723

2724+
fn print_raw_custom_section(
2725+
&mut self,
2726+
state: &State,
2727+
section: CustomSectionReader<'_>,
2728+
) -> Result<()> {
2729+
self.newline(section.range().start);
2730+
self.start_group("@custom ");
2731+
self.print_str(section.name())?;
2732+
if let Some(place) = state.custom_section_place {
2733+
self.result.push_str(" (");
2734+
self.result.push_str(place);
2735+
self.result.push_str(")");
2736+
}
2737+
self.result.push_str(" ");
2738+
self.print_bytes(section.data())?;
2739+
self.end_group();
2740+
Ok(())
2741+
}
2742+
26842743
fn print_producers_section(&mut self, section: ProducersSectionReader<'_>) -> Result<()> {
26852744
self.start_group("@producers");
26862745
for field in section {

crates/wit-component/src/encoding.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2127,7 +2127,7 @@ impl ComponentEncoder {
21272127
}
21282128
}
21292129

2130-
#[cfg(test)]
2130+
#[cfg(all(test, feature = "dummy-module"))]
21312131
mod test {
21322132
use crate::{dummy_module, embed_component_metadata};
21332133

crates/wit-component/tests/interfaces/console.wat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
)
1212
)
1313
(export (;1;) "console" (type 0))
14+
(@custom "package-docs" "\00{}")
1415
(@producers
1516
(processed-by "wit-component" "$CARGO_PKG_VERSION")
1617
)

crates/wit-component/tests/interfaces/diamond-disambiguate.wat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
)
6464
)
6565
(export (;5;) "w1" (type 4))
66+
(@custom "package-docs" "\00{}")
6667
(@producers
6768
(processed-by "wit-component" "$CARGO_PKG_VERSION")
6869
)

crates/wit-component/tests/interfaces/diamond.wat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
)
101101
)
102102
(export (;7;) "w3" (type 6))
103+
(@custom "package-docs" "\00{}")
103104
(@producers
104105
(processed-by "wit-component" "$CARGO_PKG_VERSION")
105106
)

crates/wit-component/tests/interfaces/doc-comments.wat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
)
6767
)
6868
(export (;5;) "coverage-world" (type 4))
69+
(@custom "package-docs" "\00{\22docs\22:\22package docs;\22,\22worlds\22:{\22coverage-world\22:{\22docs\22:\22world docs\22,\22interfaces\22:{\22i\22:{\22docs\22:\22world inline interface docs\22,\22funcs\22:{\22f\22:\22inline interface func docs\22},\22types\22:{\22t\22:{\22docs\22:\22inline interface typedef docs\22}}}},\22types\22:{\22t\22:{\22docs\22:\22world typedef docs\22}},\22funcs\22:{\22imp\22:\22world func import docs\22,\22exp\22:\22world func export docs\22}}},\22interfaces\22:{\22coverage-iface\22:{\22docs\22:\22interface docs\22,\22funcs\22:{\22[constructor]res\22:\22constructor docs\22,\22[method]res.m\22:\22method docs\22,\22[static]res.s\22:\22static func docs\22,\22f\22:\22interface func docs\22},\22types\22:{\22t\22:{\22docs\22:\22basic typedef docs\22},\22r\22:{\22docs\22:\22record typedef docs\22,\22items\22:{\22f1\22:\22record field docs\22}},\22fl\22:{\22items\22:{\22f1\22:\22flag docs\22}},\22v\22:{\22items\22:{\22c1\22:\22variant case docs\22}},\22e\22:{\22items\22:{\22c1\22:\22enum case docs\22}}}},\22other-comment-forms\22:{\22docs\22:\22other comment forms\5cn multi-line block\22,\22funcs\22:{\22multiple-lines-split\22:\22one doc line\5cnnon-doc in the middle\5cnanother doc line\22,\22mixed-forms\22:\22mixed forms; line doc\5cnplus block doc\5cn multi-line\22}}}}")
6970
(@producers
7071
(processed-by "wit-component" "$CARGO_PKG_VERSION")
7172
)

crates/wit-component/tests/interfaces/empty.wat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
)
4444
)
4545
(export (;5;) "actually-empty-world" (type 4))
46+
(@custom "package-docs" "\00{}")
4647
(@producers
4748
(processed-by "wit-component" "$CARGO_PKG_VERSION")
4849
)

crates/wit-component/tests/interfaces/export-other-packages-interface.wat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
)
1717
)
1818
(export (;1;) "foo" (type 0))
19+
(@custom "package-docs" "\00{}")
1920
(@producers
2021
(processed-by "wit-component" "$CARGO_PKG_VERSION")
2122
)

0 commit comments

Comments
 (0)