Skip to main content

wasmprinter/
lib.rs

1//! A crate to convert a WebAssembly binary to its textual representation in the
2//! WebAssembly Text Format (WAT).
3//!
4//! This crate is intended for developer toolchains and debugging, supporting
5//! human-readable versions of a wasm binary. This can also be useful when
6//! developing wasm toolchain support in Rust for various purposes like testing
7//! and debugging and such.
8
9#![deny(missing_docs)]
10#![cfg_attr(docsrs, feature(doc_cfg))]
11
12use anyhow::{Context, Result, anyhow, bail};
13use operator::{OpPrinter, OperatorSeparator, OperatorState, PrintOperator, PrintOperatorFolded};
14use std::collections::{HashMap, HashSet};
15use std::fmt;
16use std::io;
17use std::marker;
18use std::mem;
19use std::path::Path;
20use wasmparser::*;
21
22const MAX_LOCALS: u32 = 50000;
23const MAX_NESTING_TO_PRINT: u32 = 50;
24const MAX_WASM_FUNCTIONS: u32 = 1_000_000;
25const MAX_WASM_FUNCTION_SIZE: u32 = 128 * 1024;
26
27#[cfg(feature = "component-model")]
28mod component;
29#[cfg(feature = "validate")]
30mod operand_stack;
31#[cfg(not(feature = "validate"))]
32mod operand_stack_disabled;
33#[cfg(not(feature = "validate"))]
34use operand_stack_disabled as operand_stack;
35mod operator;
36mod print;
37
38pub use self::print::*;
39
40/// Reads a WebAssembly `file` from the filesystem and then prints it into an
41/// in-memory `String`.
42pub fn print_file(file: impl AsRef<Path>) -> Result<String> {
43    let file = file.as_ref();
44    let contents = std::fs::read(file).context(format!("failed to read `{}`", file.display()))?;
45    print_bytes(contents)
46}
47
48/// Prints an in-memory `wasm` binary blob into an in-memory `String` which is
49/// its textual representation.
50pub fn print_bytes(wasm: impl AsRef<[u8]>) -> Result<String> {
51    let mut dst = String::new();
52    Config::new().print(wasm.as_ref(), &mut PrintFmtWrite(&mut dst))?;
53    Ok(dst)
54}
55
56/// Configuration used to print a WebAssembly binary.
57///
58/// This structure is used to control the overal structure of how wasm binaries
59/// are printed and tweaks various ways that configures the output.
60#[derive(Debug)]
61pub struct Config {
62    print_offsets: bool,
63    print_skeleton: bool,
64    name_unnamed: bool,
65    fold_instructions: bool,
66    indent_text: String,
67    print_operand_stack: bool,
68}
69
70impl Default for Config {
71    fn default() -> Self {
72        Self {
73            print_offsets: false,
74            print_skeleton: false,
75            name_unnamed: false,
76            fold_instructions: false,
77            indent_text: "  ".to_string(),
78            print_operand_stack: false,
79        }
80    }
81}
82
83/// This structure is the actual structure that prints WebAssembly binaries.
84struct Printer<'cfg, 'env> {
85    config: &'cfg Config,
86    result: &'cfg mut (dyn Print + 'env),
87    nesting: u32,
88    line: usize,
89    group_lines: Vec<usize>,
90    code_section_hints: Vec<(u32, Vec<(usize, BranchHint)>)>,
91}
92
93#[derive(Default)]
94struct CoreState {
95    types: Vec<Option<SubType>>,
96    funcs: u32,
97    func_to_type: Vec<Option<u32>>,
98    memories: u32,
99    tags: u32,
100    tag_to_type: Vec<Option<u32>>,
101    globals: u32,
102    tables: u32,
103    #[cfg(feature = "component-model")]
104    modules: u32,
105    #[cfg(feature = "component-model")]
106    instances: u32,
107    func_names: NamingMap<u32, NameFunc>,
108    local_names: NamingMap<(u32, u32), NameLocal>,
109    label_names: NamingMap<(u32, u32), NameLabel>,
110    type_names: NamingMap<u32, NameType>,
111    field_names: NamingMap<(u32, u32), NameField>,
112    tag_names: NamingMap<u32, NameTag>,
113    table_names: NamingMap<u32, NameTable>,
114    memory_names: NamingMap<u32, NameMemory>,
115    global_names: NamingMap<u32, NameGlobal>,
116    element_names: NamingMap<u32, NameElem>,
117    data_names: NamingMap<u32, NameData>,
118    #[cfg(feature = "component-model")]
119    module_names: NamingMap<u32, NameModule>,
120    #[cfg(feature = "component-model")]
121    instance_names: NamingMap<u32, NameInstance>,
122}
123
124/// A map of index-to-name for tracking what are the contents of the name
125/// section.
126///
127/// The type parameter `T` is either `u32` for most index-based maps or a `(u32,
128/// u32)` for label/local maps where there are two levels of indices.
129///
130/// The type parameter `K` is a static description/namespace for what kind of
131/// item is contained within this map. That's used by some helper methods to
132/// synthesize reasonable names automatically.
133struct NamingMap<T, K> {
134    index_to_name: HashMap<T, Naming>,
135    _marker: marker::PhantomData<K>,
136}
137
138impl<T, K> Default for NamingMap<T, K> {
139    fn default() -> NamingMap<T, K> {
140        NamingMap {
141            index_to_name: HashMap::new(),
142            _marker: marker::PhantomData,
143        }
144    }
145}
146
147#[derive(Default)]
148#[cfg(feature = "component-model")]
149struct ComponentState {
150    types: u32,
151    funcs: u32,
152    instances: u32,
153    components: u32,
154    values: u32,
155    type_names: NamingMap<u32, NameType>,
156    func_names: NamingMap<u32, NameFunc>,
157    component_names: NamingMap<u32, NameComponent>,
158    instance_names: NamingMap<u32, NameInstance>,
159    value_names: NamingMap<u32, NameValue>,
160}
161
162struct State {
163    encoding: Encoding,
164    name: Option<Naming>,
165    core: CoreState,
166    #[cfg(feature = "component-model")]
167    component: ComponentState,
168    custom_section_place: Option<(&'static str, usize)>,
169    // `custom_section_place` stores the text representation of the location where
170    // a custom section should be serialized in the binary format.
171    // The tuple elements are a str (e.g. "after elem") and the line number
172    // where the custom section place was set. `update_custom_section_place` won't
173    // update the custom section place unless the line number changes; this prevents
174    // printing a place "after xxx" where the xxx section doesn't appear in the text format
175    // (e.g. because it was present but empty in the binary format).
176}
177
178impl State {
179    fn new(encoding: Encoding) -> Self {
180        Self {
181            encoding,
182            name: None,
183            core: CoreState::default(),
184            #[cfg(feature = "component-model")]
185            component: ComponentState::default(),
186            custom_section_place: None,
187        }
188    }
189}
190
191struct Naming {
192    name: String,
193    kind: NamingKind,
194}
195
196enum NamingKind {
197    DollarName,
198    DollarQuotedName,
199    SyntheticPrefix(String),
200}
201
202impl Config {
203    /// Creates a new [`Config`] object that's ready to start printing wasm
204    /// binaries to strings.
205    pub fn new() -> Self {
206        Self::default()
207    }
208
209    /// Whether or not to print binary offsets of each item as comments in the
210    /// text format whenever a newline is printed.
211    pub fn print_offsets(&mut self, print: bool) -> &mut Self {
212        self.print_offsets = print;
213        self
214    }
215
216    /// Whether or not to print only a "skeleton" which skips function bodies,
217    /// data segment contents, element segment contents, etc.
218    pub fn print_skeleton(&mut self, print: bool) -> &mut Self {
219        self.print_skeleton = print;
220        self
221    }
222
223    /// Assign names to all unnamed items.
224    ///
225    /// If enabled then any previously unnamed item will have a name synthesized
226    /// that looks like `$#func10` for example. The leading `#` indicates that
227    /// it's `wasmprinter`-generated. The `func` is the namespace of the name
228    /// and provides extra context about the item when referenced. The 10 is the
229    /// local index of the item.
230    ///
231    /// Note that if the resulting text output is converted back to binary the
232    /// resulting `name` custom section will not be the same as before.
233    pub fn name_unnamed(&mut self, enable: bool) -> &mut Self {
234        self.name_unnamed = enable;
235        self
236    }
237
238    /// Print instructions in folded form where possible.
239    ///
240    /// This will cause printing to favor the s-expression (parenthesized) form
241    /// of WebAssembly instructions. For example this output would be generated
242    /// for a simple `add` function:
243    ///
244    /// ```wasm
245    /// (module
246    ///     (func $foo (param i32 i32) (result i32)
247    ///         (i32.add
248    ///             (local.get 0)
249    ///             (local.get 1))
250    ///     )
251    /// )
252    /// ```
253    pub fn fold_instructions(&mut self, enable: bool) -> &mut Self {
254        self.fold_instructions = enable;
255        self
256    }
257
258    /// Print the operand stack types within function bodies,
259    /// flagging newly pushed operands when color output is enabled. E.g.:
260    ///
261    /// ```wasm
262    /// (module
263    ///   (type (;0;) (func))
264    ///   (func (;0;) (type 0)
265    ///     i32.const 4
266    ///     ;; [i32]
267    ///     i32.const 5
268    ///     ;; [i32 i32]
269    ///     i32.add
270    ///     ;; [i32]
271    ///     drop
272    ///     ;; []
273    ///   )
274    /// )
275    /// ```
276    #[cfg(feature = "validate")]
277    pub fn print_operand_stack(&mut self, enable: bool) -> &mut Self {
278        self.print_operand_stack = enable;
279        self
280    }
281
282    /// Select the string to use when indenting.
283    ///
284    /// The indent allowed here are arbitrary and unchecked. You should enter
285    /// blank text like `" "` or `"\t"`, rather than something like `"(;;)"`.
286    ///
287    /// The default setting is double spaces `" "`
288    pub fn indent_text(&mut self, text: impl Into<String>) -> &mut Self {
289        self.indent_text = text.into();
290        self
291    }
292
293    /// Print a WebAssembly binary.
294    ///
295    /// This function takes an entire `wasm` binary blob and prints it to the
296    /// `result` in the WebAssembly Text Format.
297    pub fn print(&self, wasm: &[u8], result: &mut impl Print) -> Result<()> {
298        Printer {
299            config: self,
300            result,
301            code_section_hints: Vec::new(),
302            group_lines: Vec::new(),
303            line: 0,
304            nesting: 0,
305        }
306        .print_contents(wasm)
307    }
308
309    /// Get the line-by-line WAT disassembly for the given Wasm, along with the
310    /// binary offsets for each line.
311    pub fn offsets_and_lines<'a>(
312        &self,
313        wasm: &[u8],
314        storage: &'a mut String,
315    ) -> Result<impl Iterator<Item = (Option<usize>, &'a str)> + 'a> {
316        struct TrackingPrint<'a> {
317            dst: &'a mut String,
318            lines: Vec<usize>,
319            line_offsets: Vec<Option<usize>>,
320        }
321
322        impl Print for TrackingPrint<'_> {
323            fn write_str(&mut self, s: &str) -> io::Result<()> {
324                self.dst.push_str(s);
325                Ok(())
326            }
327            fn start_line(&mut self, offset: Option<usize>) {
328                self.lines.push(self.dst.len());
329                self.line_offsets.push(offset);
330            }
331        }
332
333        let mut output = TrackingPrint {
334            dst: storage,
335            lines: Vec::new(),
336            line_offsets: Vec::new(),
337        };
338        self.print(wasm, &mut output)?;
339
340        let TrackingPrint {
341            dst,
342            lines,
343            line_offsets,
344        } = output;
345        let end = dst.len();
346        let dst = &dst[..];
347        let mut offsets = line_offsets.into_iter();
348        let mut lines = lines.into_iter().peekable();
349
350        Ok(std::iter::from_fn(move || {
351            let offset = offsets.next()?;
352            let i = lines.next()?;
353            let j = lines.peek().copied().unwrap_or(end);
354            let line = &dst[i..j];
355            Some((offset, line))
356        }))
357    }
358}
359
360impl Printer<'_, '_> {
361    fn read_names<'a>(
362        &mut self,
363        mut bytes: &'a [u8],
364        mut parser: Parser,
365        state: &mut State,
366    ) -> Result<()> {
367        loop {
368            let payload = match parser.parse(bytes, true)? {
369                Chunk::NeedMoreData(_) => unreachable!(),
370                Chunk::Parsed { payload, consumed } => {
371                    bytes = &bytes[consumed..];
372                    payload
373                }
374            };
375
376            match payload {
377                Payload::CodeSectionStart { size, .. } => {
378                    if size as usize > bytes.len() {
379                        bail!("invalid code section size");
380                    }
381                    bytes = &bytes[size as usize..];
382                    parser.skip_section();
383                }
384                #[cfg(feature = "component-model")]
385                Payload::ModuleSection {
386                    unchecked_range: range,
387                    ..
388                }
389                | Payload::ComponentSection {
390                    unchecked_range: range,
391                    ..
392                } => {
393                    let offset = range.end - range.start;
394                    if offset > bytes.len() {
395                        bail!("invalid module or component section range");
396                    }
397                    bytes = &bytes[offset..];
398                }
399
400                Payload::CustomSection(c) => {
401                    // Ignore any error associated with the name sections.
402                    match c.as_known() {
403                        KnownCustom::Name(reader) => {
404                            drop(self.register_names(state, reader));
405                        }
406                        #[cfg(feature = "component-model")]
407                        KnownCustom::ComponentName(reader) => {
408                            drop(self.register_component_names(state, reader));
409                        }
410                        KnownCustom::BranchHints(reader) => {
411                            drop(self.register_branch_hint_section(reader));
412                        }
413                        _ => {}
414                    }
415                }
416
417                Payload::End(_) => break,
418                _ => {}
419            }
420        }
421
422        Ok(())
423    }
424
425    fn ensure_module(states: &[State]) -> Result<()> {
426        if !matches!(states.last().unwrap().encoding, Encoding::Module) {
427            bail!("a module section was encountered when parsing a component");
428        }
429
430        Ok(())
431    }
432
433    #[cfg(feature = "component-model")]
434    fn ensure_component(states: &[State]) -> Result<()> {
435        if !matches!(states.last().unwrap().encoding, Encoding::Component) {
436            bail!("a component section was encountered when parsing a module");
437        }
438
439        Ok(())
440    }
441
442    fn print_contents(&mut self, mut bytes: &[u8]) -> Result<()> {
443        self.result.start_line(Some(0));
444
445        let mut expected = None;
446        let mut states: Vec<State> = Vec::new();
447        let mut parser = Parser::new(0);
448        #[cfg(feature = "component-model")]
449        let mut parsers = Vec::new();
450
451        let mut validator = if self.config.print_operand_stack {
452            operand_stack::Validator::new()
453        } else {
454            None
455        };
456
457        loop {
458            let payload = match parser.parse(bytes, true)? {
459                Chunk::NeedMoreData(_) => unreachable!(),
460                Chunk::Parsed { payload, consumed } => {
461                    bytes = &bytes[consumed..];
462                    payload
463                }
464            };
465            if let Some(validator) = &mut validator {
466                match validator.payload(&payload) {
467                    Ok(()) => {}
468                    Err(e) => {
469                        self.newline_unknown_pos()?;
470                        write!(self.result, ";; module or component is invalid: {e}")?;
471                    }
472                }
473            }
474            match payload {
475                Payload::Version { encoding, .. } => {
476                    if let Some(e) = expected {
477                        if encoding != e {
478                            bail!("incorrect encoding for nested module or component");
479                        }
480                        expected = None;
481                    }
482
483                    assert!(states.last().map(|s| s.encoding) != Some(Encoding::Module));
484
485                    match encoding {
486                        Encoding::Module => {
487                            states.push(State::new(Encoding::Module));
488                            states.last_mut().unwrap().custom_section_place =
489                                Some(("before first", self.line));
490                            if states.len() > 1 {
491                                self.start_group("core module")?;
492                            } else {
493                                self.start_group("module")?;
494                            }
495
496                            #[cfg(feature = "component-model")]
497                            if states.len() > 1 {
498                                let parent = &states[states.len() - 2];
499                                self.result.write_str(" ")?;
500                                self.print_name(&parent.core.module_names, parent.core.modules)?;
501                            }
502                        }
503                        Encoding::Component => {
504                            #[cfg(feature = "component-model")]
505                            {
506                                states.push(State::new(Encoding::Component));
507                                self.start_group("component")?;
508
509                                if states.len() > 1 {
510                                    let parent = &states[states.len() - 2];
511                                    self.result.write_str(" ")?;
512                                    self.print_name(
513                                        &parent.component.component_names,
514                                        parent.component.components,
515                                    )?;
516                                }
517                            }
518                            #[cfg(not(feature = "component-model"))]
519                            {
520                                bail!(
521                                    "support for printing components disabled \
522                                     at compile-time"
523                                );
524                            }
525                        }
526                    }
527
528                    let len = states.len();
529                    let state = states.last_mut().unwrap();
530
531                    // First up try to find the `name` subsection which we'll use to print
532                    // pretty names everywhere.
533                    self.read_names(bytes, parser.clone(), state)?;
534
535                    if len == 1 {
536                        if let Some(name) = state.name.as_ref() {
537                            self.result.write_str(" ")?;
538                            name.write(self)?;
539                        }
540                    }
541                }
542                Payload::CustomSection(c) => {
543                    // If the custom printing trait handles this section, keep
544                    // going after that.
545                    let printed =
546                        self.result
547                            .print_custom_section(c.name(), c.data_offset(), c.data())?;
548                    if printed {
549                        self.update_custom_section_line(&mut states);
550                        continue;
551                    }
552
553                    // If this wasn't handled specifically above then try to
554                    // print the known custom builtin sections. If this fails
555                    // because the custom section is malformed then print the
556                    // raw contents instead.
557                    let state = states.last().unwrap();
558                    let start = self.nesting;
559                    match c.as_known() {
560                        KnownCustom::Unknown => self.print_raw_custom_section(state, c.clone())?,
561                        _ => {
562                            match (Printer {
563                                config: self.config,
564                                result: &mut PrintFmtWrite(String::new()),
565                                nesting: 0,
566                                line: 0,
567                                group_lines: Vec::new(),
568                                code_section_hints: Vec::new(),
569                            })
570                            .print_known_custom_section(c.clone())
571                            {
572                                Ok(true) => {
573                                    self.print_known_custom_section(c.clone())?;
574                                }
575                                Ok(false) => self.print_raw_custom_section(state, c.clone())?,
576                                Err(e) if !e.is::<BinaryReaderError>() => return Err(e),
577                                Err(e) => {
578                                    let msg = format!(
579                                        "failed to parse custom section `{}`: {e}",
580                                        c.name()
581                                    );
582                                    for line in msg.lines() {
583                                        self.newline(c.data_offset())?;
584                                        write!(self.result, ";; {line}")?;
585                                    }
586                                    self.print_raw_custom_section(state, c.clone())?
587                                }
588                            }
589                        }
590                    }
591                    assert!(self.nesting == start);
592                    self.update_custom_section_line(&mut states);
593                }
594                Payload::TypeSection(s) => {
595                    self.print_types(states.last_mut().unwrap(), s)?;
596                    self.update_custom_section_place(&mut states, "after type");
597                }
598                Payload::ImportSection(s) => {
599                    Self::ensure_module(&states)?;
600                    self.print_imports(states.last_mut().unwrap(), s)?;
601                    self.update_custom_section_place(&mut states, "after import");
602                }
603                Payload::FunctionSection(reader) => {
604                    Self::ensure_module(&states)?;
605                    if reader.count() > MAX_WASM_FUNCTIONS {
606                        bail!(
607                            "module contains {} functions which exceeds the limit of {}",
608                            reader.count(),
609                            MAX_WASM_FUNCTIONS
610                        );
611                    }
612                    for ty in reader {
613                        states.last_mut().unwrap().core.func_to_type.push(Some(ty?))
614                    }
615                    self.update_custom_section_place(&mut states, "after func");
616                }
617                Payload::TableSection(s) => {
618                    Self::ensure_module(&states)?;
619                    self.print_tables(states.last_mut().unwrap(), s)?;
620                    self.update_custom_section_place(&mut states, "after table");
621                }
622                Payload::MemorySection(s) => {
623                    Self::ensure_module(&states)?;
624                    self.print_memories(states.last_mut().unwrap(), s)?;
625                    self.update_custom_section_place(&mut states, "after memory");
626                }
627                Payload::TagSection(s) => {
628                    Self::ensure_module(&states)?;
629                    self.print_tags(states.last_mut().unwrap(), s)?;
630                    self.update_custom_section_place(&mut states, "after tag");
631                }
632                Payload::GlobalSection(s) => {
633                    Self::ensure_module(&states)?;
634                    self.print_globals(states.last_mut().unwrap(), s)?;
635                    self.update_custom_section_place(&mut states, "after global");
636                }
637                Payload::ExportSection(s) => {
638                    Self::ensure_module(&states)?;
639                    self.print_exports(states.last().unwrap(), s)?;
640                    self.update_custom_section_place(&mut states, "after export");
641                }
642                Payload::StartSection { func, range } => {
643                    Self::ensure_module(&states)?;
644                    self.newline(range.start)?;
645                    self.start_group("start ")?;
646                    self.print_idx(&states.last().unwrap().core.func_names, func)?;
647                    self.end_group()?;
648                    self.update_custom_section_place(&mut states, "after start");
649                }
650                Payload::ElementSection(s) => {
651                    Self::ensure_module(&states)?;
652                    self.print_elems(states.last_mut().unwrap(), s)?;
653                    self.update_custom_section_place(&mut states, "after elem");
654                }
655                Payload::CodeSectionStart { .. } => {
656                    Self::ensure_module(&states)?;
657                }
658                Payload::CodeSectionEntry(body) => {
659                    self.print_code_section_entry(
660                        states.last_mut().unwrap(),
661                        &body,
662                        validator.as_mut().and_then(|v| v.next_func()),
663                    )?;
664                    self.update_custom_section_place(&mut states, "after code");
665                }
666                Payload::DataCountSection { .. } => {
667                    Self::ensure_module(&states)?;
668                    // not part of the text format
669                }
670                Payload::DataSection(s) => {
671                    Self::ensure_module(&states)?;
672                    self.print_data(states.last_mut().unwrap(), s)?;
673                    self.update_custom_section_place(&mut states, "after data");
674                }
675
676                #[cfg(feature = "component-model")]
677                Payload::ModuleSection {
678                    parser: inner,
679                    unchecked_range: range,
680                } => {
681                    Self::ensure_component(&states)?;
682                    expected = Some(Encoding::Module);
683                    parsers.push(parser);
684                    parser = inner;
685                    self.newline(range.start)?;
686                }
687                #[cfg(feature = "component-model")]
688                Payload::InstanceSection(s) => {
689                    Self::ensure_component(&states)?;
690                    self.print_instances(states.last_mut().unwrap(), s)?;
691                }
692                #[cfg(feature = "component-model")]
693                Payload::CoreTypeSection(s) => self.print_core_types(&mut states, s)?,
694                #[cfg(feature = "component-model")]
695                Payload::ComponentSection {
696                    parser: inner,
697                    unchecked_range: range,
698                } => {
699                    Self::ensure_component(&states)?;
700                    expected = Some(Encoding::Component);
701                    parsers.push(parser);
702                    parser = inner;
703                    self.newline(range.start)?;
704                }
705                #[cfg(feature = "component-model")]
706                Payload::ComponentInstanceSection(s) => {
707                    Self::ensure_component(&states)?;
708                    self.print_component_instances(states.last_mut().unwrap(), s)?;
709                }
710                #[cfg(feature = "component-model")]
711                Payload::ComponentAliasSection(s) => {
712                    Self::ensure_component(&states)?;
713                    self.print_component_aliases(&mut states, s)?;
714                }
715                #[cfg(feature = "component-model")]
716                Payload::ComponentTypeSection(s) => {
717                    Self::ensure_component(&states)?;
718                    self.print_component_types(&mut states, s)?;
719                }
720                #[cfg(feature = "component-model")]
721                Payload::ComponentCanonicalSection(s) => {
722                    Self::ensure_component(&states)?;
723                    self.print_canonical_functions(states.last_mut().unwrap(), s)?;
724                }
725                #[cfg(feature = "component-model")]
726                Payload::ComponentStartSection { start, range } => {
727                    Self::ensure_component(&states)?;
728                    self.print_component_start(states.last_mut().unwrap(), range.start, start)?;
729                }
730                #[cfg(feature = "component-model")]
731                Payload::ComponentImportSection(s) => {
732                    Self::ensure_component(&states)?;
733                    self.print_component_imports(states.last_mut().unwrap(), s)?;
734                }
735                #[cfg(feature = "component-model")]
736                Payload::ComponentExportSection(s) => {
737                    Self::ensure_component(&states)?;
738                    self.print_component_exports(states.last_mut().unwrap(), s)?;
739                }
740
741                Payload::End(offset) => {
742                    self.end_group_at_pos(offset)?; // close the `module` or `component` group
743
744                    #[cfg(feature = "component-model")]
745                    {
746                        let state = states.pop().unwrap();
747                        if let Some(parent) = states.last_mut() {
748                            match state.encoding {
749                                Encoding::Module => {
750                                    parent.core.modules += 1;
751                                }
752                                Encoding::Component => {
753                                    parent.component.components += 1;
754                                }
755                            }
756                            parser = parsers.pop().unwrap();
757
758                            continue;
759                        }
760                    }
761                    self.result.newline()?;
762                    break;
763                }
764
765                other => match other.as_section() {
766                    Some((id, _)) => bail!("found unknown section `{}`", id),
767                    None => bail!("found unknown payload"),
768                },
769            }
770        }
771
772        Ok(())
773    }
774
775    fn update_custom_section_place(&self, states: &mut Vec<State>, place: &'static str) {
776        if let Some(last) = states.last_mut() {
777            if let Some((prev, prev_line)) = &mut last.custom_section_place {
778                if *prev_line != self.line {
779                    *prev = place;
780                    *prev_line = self.line;
781                }
782            }
783        }
784    }
785
786    fn update_custom_section_line(&self, states: &mut Vec<State>) {
787        if let Some(last) = states.last_mut() {
788            if let Some((_, prev_line)) = &mut last.custom_section_place {
789                *prev_line = self.line;
790            }
791        }
792    }
793
794    fn start_group(&mut self, name: &str) -> Result<()> {
795        write!(self.result, "(")?;
796        self.result.start_keyword()?;
797        write!(self.result, "{name}")?;
798        self.result.reset_color()?;
799        self.nesting += 1;
800        self.group_lines.push(self.line);
801        Ok(())
802    }
803
804    fn end_group(&mut self) -> Result<()> {
805        self.nesting -= 1;
806        if let Some(line) = self.group_lines.pop() {
807            if line != self.line {
808                self.newline_unknown_pos()?;
809            }
810        }
811        self.result.write_str(")")?;
812        Ok(())
813    }
814
815    fn end_group_at_pos(&mut self, offset: usize) -> Result<()> {
816        self.nesting -= 1;
817        let start_group_line = self.group_lines.pop();
818        if self.config.print_offsets {
819            self.newline(offset)?;
820        } else if let Some(line) = start_group_line {
821            if line != self.line {
822                self.newline(offset)?;
823            }
824        }
825        self.result.write_str(")")?;
826        Ok(())
827    }
828
829    fn register_names(&mut self, state: &mut State, names: NameSectionReader<'_>) -> Result<()> {
830        fn indirect_name_map<K>(
831            into: &mut NamingMap<(u32, u32), K>,
832            names: IndirectNameMap<'_>,
833            name: &str,
834        ) -> Result<()> {
835            for indirect in names {
836                let indirect = indirect?;
837                let mut used = match name {
838                    // labels can be shadowed, so maintaining the used names is not useful.
839                    "label" => None,
840                    "local" | "field" => Some(HashSet::new()),
841                    _ => unimplemented!("{name} is an unknown type of indirect names"),
842                };
843                for naming in indirect.names {
844                    let naming = naming?;
845                    into.index_to_name.insert(
846                        (indirect.index, naming.index),
847                        Naming::new(naming.name, naming.index, name, used.as_mut()),
848                    );
849                }
850            }
851            Ok(())
852        }
853
854        for section in names {
855            match section? {
856                Name::Module { name, .. } => {
857                    let name = Naming::new(name, 0, "module", None);
858                    state.name = Some(name);
859                }
860                Name::Function(n) => name_map(&mut state.core.func_names, n, "func")?,
861                Name::Local(n) => indirect_name_map(&mut state.core.local_names, n, "local")?,
862                Name::Label(n) => indirect_name_map(&mut state.core.label_names, n, "label")?,
863                Name::Type(n) => name_map(&mut state.core.type_names, n, "type")?,
864                Name::Table(n) => name_map(&mut state.core.table_names, n, "table")?,
865                Name::Memory(n) => name_map(&mut state.core.memory_names, n, "memory")?,
866                Name::Global(n) => name_map(&mut state.core.global_names, n, "global")?,
867                Name::Element(n) => name_map(&mut state.core.element_names, n, "elem")?,
868                Name::Data(n) => name_map(&mut state.core.data_names, n, "data")?,
869                Name::Field(n) => indirect_name_map(&mut state.core.field_names, n, "field")?,
870                Name::Tag(n) => name_map(&mut state.core.tag_names, n, "tag")?,
871                Name::Unknown { .. } => (),
872            }
873        }
874        Ok(())
875    }
876
877    fn print_rec(
878        &mut self,
879        state: &mut State,
880        offset: Option<usize>,
881        rec: RecGroup,
882        is_component: bool,
883    ) -> Result<()> {
884        if rec.is_explicit_rec_group() {
885            if is_component {
886                self.start_group("core rec")?;
887            } else {
888                self.start_group("rec")?;
889            }
890            for ty in rec.into_types() {
891                match offset {
892                    Some(offset) => self.newline(offset + 2)?,
893                    None => self.newline_unknown_pos()?,
894                }
895                self.print_type(state, ty, false)?;
896            }
897            self.end_group()?; // `rec`
898        } else {
899            assert_eq!(rec.types().len(), 1);
900            let ty = rec.into_types().next().unwrap();
901            self.print_type(state, ty, is_component)?;
902        }
903        Ok(())
904    }
905
906    fn print_type(&mut self, state: &mut State, ty: SubType, is_component: bool) -> Result<()> {
907        if is_component {
908            self.start_group("core type ")?;
909        } else {
910            self.start_group("type ")?;
911        }
912        let ty_idx = state.core.types.len() as u32;
913        self.print_name(&state.core.type_names, ty_idx)?;
914        self.result.write_str(" ")?;
915        self.print_sub(state, &ty, ty_idx)?;
916        self.end_group()?; // `type`
917        state.core.types.push(Some(ty));
918        Ok(())
919    }
920
921    fn print_sub(&mut self, state: &State, ty: &SubType, ty_idx: u32) -> Result<u32> {
922        let r = if !ty.is_final || !ty.supertype_idx.is_none() {
923            self.start_group("sub")?;
924            self.print_sub_type(state, ty)?;
925            let r = self.print_composite(state, &ty.composite_type, ty_idx)?;
926            self.end_group()?; // `sub`
927            r
928        } else {
929            self.print_composite(state, &ty.composite_type, ty_idx)?
930        };
931        Ok(r)
932    }
933
934    fn print_composite(&mut self, state: &State, ty: &CompositeType, ty_idx: u32) -> Result<u32> {
935        if ty.shared {
936            self.start_group("shared")?;
937            self.result.write_str(" ")?;
938        }
939        if let Some(idx) = ty.describes_idx {
940            self.start_group("describes")?;
941            self.result.write_str(" ")?;
942            self.print_idx(&state.core.type_names, idx.as_module_index().unwrap())?;
943            self.end_group()?;
944            self.result.write_str(" ")?;
945        }
946        if let Some(idx) = ty.descriptor_idx {
947            self.start_group("descriptor")?;
948            self.result.write_str(" ")?;
949            self.print_idx(&state.core.type_names, idx.as_module_index().unwrap())?;
950            self.end_group()?;
951            self.result.write_str(" ")?;
952        }
953        let r = match &ty.inner {
954            CompositeInnerType::Func(ty) => {
955                self.start_group("func")?;
956                let r = self.print_func_type(state, ty, None)?;
957                self.end_group()?; // `func`
958                r
959            }
960            CompositeInnerType::Array(ty) => {
961                self.start_group("array")?;
962                let r = self.print_array_type(state, ty)?;
963                self.end_group()?; // `array`
964                r
965            }
966            CompositeInnerType::Struct(ty) => {
967                self.start_group("struct")?;
968                let r = self.print_struct_type(state, ty, ty_idx)?;
969                self.end_group()?; // `struct`
970                r
971            }
972            CompositeInnerType::Cont(ty) => {
973                self.start_group("cont")?;
974                let r = self.print_cont_type(state, ty)?;
975                self.end_group()?; // `cont`
976                r
977            }
978        };
979        if ty.shared {
980            self.end_group()?; // `shared`
981        }
982        Ok(r)
983    }
984
985    fn print_types(&mut self, state: &mut State, parser: TypeSectionReader<'_>) -> Result<()> {
986        for ty in parser.into_iter_with_offsets() {
987            let (offset, rec_group) = ty?;
988            self.newline(offset)?;
989            self.print_rec(state, Some(offset), rec_group, false)?;
990        }
991        Ok(())
992    }
993
994    fn print_core_functype_idx(
995        &mut self,
996        state: &State,
997        idx: u32,
998        names_for: Option<u32>,
999    ) -> Result<Option<u32>> {
1000        self.print_core_type_ref(state, idx)?;
1001
1002        match state.core.types.get(idx as usize) {
1003            Some(Some(SubType {
1004                composite_type:
1005                    CompositeType {
1006                        inner: CompositeInnerType::Func(ty),
1007                        shared: false,
1008                        descriptor_idx: None,
1009                        describes_idx: None,
1010                    },
1011                ..
1012            })) => self.print_func_type(state, ty, names_for).map(Some),
1013            Some(Some(_)) | Some(None) | None => Ok(None),
1014        }
1015    }
1016
1017    /// Returns the number of parameters, useful for local index calculations
1018    /// later.
1019    fn print_func_type(
1020        &mut self,
1021        state: &State,
1022        ty: &FuncType,
1023        names_for: Option<u32>,
1024    ) -> Result<u32> {
1025        if !ty.params().is_empty() {
1026            self.result.write_str(" ")?;
1027        }
1028
1029        let mut params = NamedLocalPrinter::new("param");
1030        // Note that named parameters must be alone in a `param` block, so
1031        // we need to be careful to terminate previous param blocks and open
1032        // a new one if that's the case with a named parameter.
1033        for (i, param) in ty.params().iter().enumerate() {
1034            params.start_local(names_for, i as u32, self, state)?;
1035            self.print_valtype(state, *param)?;
1036            params.end_local(self)?;
1037        }
1038        params.finish(self)?;
1039        if !ty.results().is_empty() {
1040            self.result.write_str(" ")?;
1041            self.start_group("result")?;
1042            for result in ty.results().iter() {
1043                self.result.write_str(" ")?;
1044                self.print_valtype(state, *result)?;
1045            }
1046            self.end_group()?;
1047        }
1048        Ok(ty.params().len() as u32)
1049    }
1050
1051    fn print_field_type(
1052        &mut self,
1053        state: &State,
1054        ty: &FieldType,
1055        ty_field_idx: Option<(u32, u32)>,
1056    ) -> Result<u32> {
1057        self.result.write_str(" ")?;
1058        if let Some(idxs @ (_, field_idx)) = ty_field_idx {
1059            match state.core.field_names.index_to_name.get(&idxs) {
1060                Some(name) => {
1061                    name.write_identifier(self)?;
1062                    self.result.write_str(" ")?;
1063                }
1064                None if self.config.name_unnamed => write!(self.result, "$#field{field_idx} ")?,
1065                None => {}
1066            }
1067        }
1068        if ty.mutable {
1069            self.result.write_str("(mut ")?;
1070        }
1071        self.print_storage_type(state, ty.element_type)?;
1072        if ty.mutable {
1073            self.result.write_str(")")?;
1074        }
1075        Ok(0)
1076    }
1077
1078    fn print_array_type(&mut self, state: &State, ty: &ArrayType) -> Result<u32> {
1079        self.print_field_type(state, &ty.0, None)
1080    }
1081
1082    fn print_struct_type(&mut self, state: &State, ty: &StructType, ty_idx: u32) -> Result<u32> {
1083        for (field_index, field) in ty.fields.iter().enumerate() {
1084            self.result.write_str(" (field")?;
1085            self.print_field_type(state, field, Some((ty_idx, field_index as u32)))?;
1086            self.result.write_str(")")?;
1087        }
1088        Ok(0)
1089    }
1090
1091    fn print_cont_type(&mut self, state: &State, ct: &ContType) -> Result<u32> {
1092        self.result.write_str(" ")?;
1093        self.print_idx(&state.core.type_names, ct.0.as_module_index().unwrap())?;
1094        Ok(0)
1095    }
1096
1097    fn print_sub_type(&mut self, state: &State, ty: &SubType) -> Result<u32> {
1098        self.result.write_str(" ")?;
1099        if ty.is_final {
1100            self.result.write_str("final ")?;
1101        }
1102        if let Some(idx) = ty.supertype_idx {
1103            self.print_idx(&state.core.type_names, idx.as_module_index().unwrap())?;
1104            self.result.write_str(" ")?;
1105        }
1106        Ok(0)
1107    }
1108
1109    fn print_storage_type(&mut self, state: &State, ty: StorageType) -> Result<()> {
1110        match ty {
1111            StorageType::I8 => self.result.write_str("i8")?,
1112            StorageType::I16 => self.result.write_str("i16")?,
1113            StorageType::Val(val_type) => self.print_valtype(state, val_type)?,
1114        }
1115        Ok(())
1116    }
1117
1118    fn print_valtype(&mut self, state: &State, ty: ValType) -> Result<()> {
1119        match ty {
1120            ValType::I32 => self.print_type_keyword("i32")?,
1121            ValType::I64 => self.print_type_keyword("i64")?,
1122            ValType::F32 => self.print_type_keyword("f32")?,
1123            ValType::F64 => self.print_type_keyword("f64")?,
1124            ValType::V128 => self.print_type_keyword("v128")?,
1125            ValType::Ref(rt) => self.print_reftype(state, rt)?,
1126        }
1127        Ok(())
1128    }
1129
1130    fn print_valtypes(&mut self, state: &State, tys: Vec<ValType>) -> Result<()> {
1131        for ty in tys {
1132            self.result.write_str(" ")?;
1133            self.print_valtype(state, ty)?;
1134        }
1135        Ok(())
1136    }
1137
1138    fn print_reftype(&mut self, state: &State, ty: RefType) -> Result<()> {
1139        if ty.is_nullable() {
1140            match ty.as_non_null() {
1141                RefType::FUNC => self.print_type_keyword("funcref")?,
1142                RefType::EXTERN => self.print_type_keyword("externref")?,
1143                RefType::I31 => self.print_type_keyword("i31ref")?,
1144                RefType::ANY => self.print_type_keyword("anyref")?,
1145                RefType::NONE => self.print_type_keyword("nullref")?,
1146                RefType::NOEXTERN => self.print_type_keyword("nullexternref")?,
1147                RefType::NOFUNC => self.print_type_keyword("nullfuncref")?,
1148                RefType::EQ => self.print_type_keyword("eqref")?,
1149                RefType::STRUCT => self.print_type_keyword("structref")?,
1150                RefType::ARRAY => self.print_type_keyword("arrayref")?,
1151                RefType::EXN => self.print_type_keyword("exnref")?,
1152                RefType::NOEXN => self.print_type_keyword("nullexnref")?,
1153                _ => {
1154                    self.start_group("ref")?;
1155                    self.result.write_str(" null ")?;
1156                    self.print_heaptype(state, ty.heap_type())?;
1157                    self.end_group()?;
1158                }
1159            }
1160        } else {
1161            self.start_group("ref ")?;
1162            self.print_heaptype(state, ty.heap_type())?;
1163            self.end_group()?;
1164        }
1165        Ok(())
1166    }
1167
1168    fn print_heaptype(&mut self, state: &State, ty: HeapType) -> Result<()> {
1169        match ty {
1170            HeapType::Concrete(i) => {
1171                self.print_idx(&state.core.type_names, i.as_module_index().unwrap())?;
1172            }
1173            HeapType::Exact(i) => {
1174                self.start_group("exact ")?;
1175                self.print_idx(&state.core.type_names, i.as_module_index().unwrap())?;
1176                self.end_group()?;
1177            }
1178            HeapType::Abstract { shared, ty } => {
1179                use AbstractHeapType::*;
1180                if shared {
1181                    self.start_group("shared ")?;
1182                }
1183                match ty {
1184                    Func => self.print_type_keyword("func")?,
1185                    Extern => self.print_type_keyword("extern")?,
1186                    Any => self.print_type_keyword("any")?,
1187                    None => self.print_type_keyword("none")?,
1188                    NoExtern => self.print_type_keyword("noextern")?,
1189                    NoFunc => self.print_type_keyword("nofunc")?,
1190                    Eq => self.print_type_keyword("eq")?,
1191                    Struct => self.print_type_keyword("struct")?,
1192                    Array => self.print_type_keyword("array")?,
1193                    I31 => self.print_type_keyword("i31")?,
1194                    Exn => self.print_type_keyword("exn")?,
1195                    NoExn => self.print_type_keyword("noexn")?,
1196                    Cont => self.print_type_keyword("cont")?,
1197                    NoCont => self.print_type_keyword("nocont")?,
1198                }
1199                if shared {
1200                    self.end_group()?;
1201                }
1202            }
1203        }
1204        Ok(())
1205    }
1206
1207    fn print_type_keyword(&mut self, keyword: &str) -> Result<()> {
1208        self.result.start_type()?;
1209        self.result.write_str(keyword)?;
1210        self.result.reset_color()?;
1211        Ok(())
1212    }
1213
1214    fn print_imports(&mut self, state: &mut State, parser: ImportSectionReader<'_>) -> Result<()> {
1215        let update_state = |state: &mut State, ty: TypeRef| match ty {
1216            TypeRef::Func(idx) | TypeRef::FuncExact(idx) => {
1217                debug_assert!(state.core.func_to_type.len() == state.core.funcs as usize);
1218                state.core.funcs += 1;
1219                state.core.func_to_type.push(Some(idx))
1220            }
1221            TypeRef::Table(_) => state.core.tables += 1,
1222            TypeRef::Memory(_) => state.core.memories += 1,
1223            TypeRef::Tag(TagType {
1224                kind: _,
1225                func_type_idx: idx,
1226            }) => {
1227                debug_assert!(state.core.tag_to_type.len() == state.core.tags as usize);
1228                state.core.tags += 1;
1229                state.core.tag_to_type.push(Some(idx))
1230            }
1231            TypeRef::Global(_) => state.core.globals += 1,
1232        };
1233
1234        for imports in parser.into_iter_with_offsets() {
1235            let (offset, imports) = imports?;
1236            self.newline(offset)?;
1237            match imports {
1238                Imports::Single(_, import) => {
1239                    self.print_import(state, &import, true)?;
1240                    update_state(state, import.ty);
1241                }
1242                Imports::Compact1 { module, items } => {
1243                    self.start_group("import ")?;
1244                    self.print_str(module)?;
1245                    for res in items.into_iter_with_offsets() {
1246                        let (offset, item) = res?;
1247                        self.newline(offset)?;
1248                        self.start_group("item ")?;
1249                        self.print_str(item.name)?;
1250                        self.result.write_str(" ")?;
1251                        self.print_import_ty(state, &item.ty, true)?;
1252                        self.end_group()?;
1253                        update_state(state, item.ty);
1254                    }
1255                    self.end_group()?;
1256                }
1257                Imports::Compact2 { module, ty, names } => {
1258                    self.start_group("import ")?;
1259                    self.print_str(module)?;
1260                    for res in names.into_iter_with_offsets() {
1261                        let (offset, item) = res?;
1262                        self.newline(offset)?;
1263                        self.start_group("item ")?;
1264                        self.print_str(item)?;
1265                        self.end_group()?;
1266                        update_state(state, ty);
1267                    }
1268                    self.newline(offset)?;
1269                    self.print_import_ty(state, &ty, false)?;
1270                    self.end_group()?;
1271                }
1272            }
1273        }
1274        Ok(())
1275    }
1276
1277    fn print_import(&mut self, state: &State, import: &Import<'_>, index: bool) -> Result<()> {
1278        self.start_group("import ")?;
1279        self.print_str(import.module)?;
1280        self.result.write_str(" ")?;
1281        self.print_str(import.name)?;
1282        self.result.write_str(" ")?;
1283        self.print_import_ty(state, &import.ty, index)?;
1284        self.end_group()?;
1285        Ok(())
1286    }
1287
1288    fn print_import_ty(&mut self, state: &State, ty: &TypeRef, index: bool) -> Result<()> {
1289        match ty {
1290            TypeRef::Func(f) => {
1291                self.start_group("func ")?;
1292                if index {
1293                    self.print_name(&state.core.func_names, state.core.funcs)?;
1294                    self.result.write_str(" ")?;
1295                }
1296                self.print_core_type_ref(state, *f)?;
1297            }
1298            TypeRef::FuncExact(f) => {
1299                self.start_group("func ")?;
1300                if index {
1301                    self.print_name(&state.core.func_names, state.core.funcs)?;
1302                    self.result.write_str(" ")?;
1303                }
1304                self.start_group("exact ")?;
1305                self.print_core_type_ref(state, *f)?;
1306                self.end_group()?;
1307            }
1308            TypeRef::Table(f) => self.print_table_type(state, f, index)?,
1309            TypeRef::Memory(f) => self.print_memory_type(state, f, index)?,
1310            TypeRef::Tag(f) => self.print_tag_type(state, f, index)?,
1311            TypeRef::Global(f) => self.print_global_type(state, f, index)?,
1312        }
1313        self.end_group()?;
1314        Ok(())
1315    }
1316
1317    fn print_table_type(&mut self, state: &State, ty: &TableType, index: bool) -> Result<()> {
1318        self.start_group("table ")?;
1319        if index {
1320            self.print_name(&state.core.table_names, state.core.tables)?;
1321            self.result.write_str(" ")?;
1322        }
1323        if ty.shared {
1324            self.print_type_keyword("shared ")?;
1325        }
1326        if ty.table64 {
1327            self.print_type_keyword("i64 ")?;
1328        }
1329        self.print_limits(ty.initial, ty.maximum)?;
1330        self.result.write_str(" ")?;
1331        self.print_reftype(state, ty.element_type)?;
1332        Ok(())
1333    }
1334
1335    fn print_memory_type(&mut self, state: &State, ty: &MemoryType, index: bool) -> Result<()> {
1336        self.start_group("memory ")?;
1337        if index {
1338            self.print_name(&state.core.memory_names, state.core.memories)?;
1339            self.result.write_str(" ")?;
1340        }
1341        if ty.memory64 {
1342            self.print_type_keyword("i64 ")?;
1343        }
1344        self.print_limits(ty.initial, ty.maximum)?;
1345        if ty.shared {
1346            self.print_type_keyword(" shared")?;
1347        }
1348        if let Some(p) = ty.page_size_log2 {
1349            let p = 1_u64
1350                .checked_shl(p)
1351                .ok_or_else(|| anyhow!("left shift overflow").context("invalid page size"))?;
1352
1353            self.result.write_str(" ")?;
1354            self.start_group("pagesize ")?;
1355            write!(self.result, "{p:#x}")?;
1356            self.end_group()?;
1357        }
1358        Ok(())
1359    }
1360
1361    fn print_tag_type(&mut self, state: &State, ty: &TagType, index: bool) -> Result<()> {
1362        self.start_group("tag ")?;
1363        if index {
1364            self.print_name(&state.core.tag_names, state.core.tags)?;
1365            self.result.write_str(" ")?;
1366        }
1367        self.print_core_functype_idx(state, ty.func_type_idx, None)?;
1368        Ok(())
1369    }
1370
1371    fn print_limits<T>(&mut self, initial: T, maximum: Option<T>) -> Result<()>
1372    where
1373        T: fmt::Display,
1374    {
1375        self.result.start_literal()?;
1376        write!(self.result, "{initial}")?;
1377        if let Some(max) = maximum {
1378            write!(self.result, " {max}")?;
1379        }
1380        self.result.reset_color()?;
1381        Ok(())
1382    }
1383
1384    fn print_global_type(&mut self, state: &State, ty: &GlobalType, index: bool) -> Result<()> {
1385        self.start_group("global ")?;
1386        if index {
1387            self.print_name(&state.core.global_names, state.core.globals)?;
1388            self.result.write_str(" ")?;
1389        }
1390        if ty.shared || ty.mutable {
1391            self.result.write_str("(")?;
1392            if ty.shared {
1393                self.print_type_keyword("shared ")?;
1394            }
1395            if ty.mutable {
1396                self.print_type_keyword("mut ")?;
1397            }
1398            self.print_valtype(state, ty.content_type)?;
1399            self.result.write_str(")")?;
1400        } else {
1401            self.print_valtype(state, ty.content_type)?;
1402        }
1403        Ok(())
1404    }
1405
1406    fn print_tables(&mut self, state: &mut State, parser: TableSectionReader<'_>) -> Result<()> {
1407        for table in parser.into_iter_with_offsets() {
1408            let (offset, table) = table?;
1409            self.newline(offset)?;
1410            self.print_table_type(state, &table.ty, true)?;
1411            match &table.init {
1412                TableInit::RefNull => {}
1413                TableInit::Expr(expr) => {
1414                    self.result.write_str(" ")?;
1415                    self.print_const_expr(state, expr, self.config.fold_instructions)?;
1416                }
1417            }
1418            self.end_group()?;
1419            state.core.tables += 1;
1420        }
1421        Ok(())
1422    }
1423
1424    fn print_memories(&mut self, state: &mut State, parser: MemorySectionReader<'_>) -> Result<()> {
1425        for memory in parser.into_iter_with_offsets() {
1426            let (offset, memory) = memory?;
1427            self.newline(offset)?;
1428            self.print_memory_type(state, &memory, true)?;
1429            self.end_group()?;
1430            state.core.memories += 1;
1431        }
1432        Ok(())
1433    }
1434
1435    fn print_tags(&mut self, state: &mut State, parser: TagSectionReader<'_>) -> Result<()> {
1436        for tag in parser.into_iter_with_offsets() {
1437            let (offset, tag) = tag?;
1438            self.newline(offset)?;
1439            self.print_tag_type(state, &tag, true)?;
1440            self.end_group()?;
1441            debug_assert!(state.core.tag_to_type.len() == state.core.tags as usize);
1442            state.core.tags += 1;
1443            state.core.tag_to_type.push(Some(tag.func_type_idx));
1444        }
1445        Ok(())
1446    }
1447
1448    fn print_globals(&mut self, state: &mut State, parser: GlobalSectionReader<'_>) -> Result<()> {
1449        for global in parser.into_iter_with_offsets() {
1450            let (offset, global) = global?;
1451            self.newline(offset)?;
1452            self.print_global_type(state, &global.ty, true)?;
1453            self.result.write_str(" ")?;
1454            self.print_const_expr(state, &global.init_expr, self.config.fold_instructions)?;
1455            self.end_group()?;
1456            state.core.globals += 1;
1457        }
1458        Ok(())
1459    }
1460
1461    fn print_code_section_entry(
1462        &mut self,
1463        state: &mut State,
1464        body: &FunctionBody<'_>,
1465        validator: Option<operand_stack::FuncValidator>,
1466    ) -> Result<()> {
1467        self.newline(body.get_binary_reader().original_position())?;
1468        self.start_group("func ")?;
1469        let func_idx = state.core.funcs;
1470        self.print_name(&state.core.func_names, func_idx)?;
1471        self.result.write_str(" ")?;
1472        let ty = match state.core.func_to_type.get(func_idx as usize) {
1473            Some(Some(x)) => *x,
1474            _ => panic!("invalid function type"),
1475        };
1476        let params = self
1477            .print_core_functype_idx(state, ty, Some(func_idx))?
1478            .unwrap_or(0);
1479
1480        // Hints are stored on `self` in reverse order of function index so
1481        // check the last one and see if it matches this function.
1482        let hints = match self.code_section_hints.last() {
1483            Some((f, _)) if *f == func_idx => {
1484                let (_, hints) = self.code_section_hints.pop().unwrap();
1485                hints
1486            }
1487            _ => Vec::new(),
1488        };
1489
1490        if self.config.print_skeleton {
1491            self.result.write_str(" ...")?;
1492            self.end_group()?;
1493        } else {
1494            let end_pos =
1495                self.print_func_body(state, func_idx, params, &body, &hints, validator)?;
1496            self.end_group_at_pos(end_pos)?;
1497        }
1498
1499        state.core.funcs += 1;
1500        Ok(())
1501    }
1502
1503    fn print_func_body(
1504        &mut self,
1505        state: &mut State,
1506        func_idx: u32,
1507        params: u32,
1508        body: &FunctionBody<'_>,
1509        branch_hints: &[(usize, BranchHint)],
1510        mut validator: Option<operand_stack::FuncValidator>,
1511    ) -> Result<usize> {
1512        let mut first = true;
1513        let mut local_idx = 0;
1514        let mut locals = NamedLocalPrinter::new("local");
1515        let mut reader = body.get_binary_reader();
1516        let func_start = reader.original_position();
1517        for _ in 0..reader.read_var_u32()? {
1518            let offset = reader.original_position();
1519            let cnt = reader.read_var_u32()?;
1520            let ty = reader.read()?;
1521            if MAX_LOCALS
1522                .checked_sub(local_idx)
1523                .and_then(|s| s.checked_sub(cnt))
1524                .is_none()
1525            {
1526                bail!("function exceeds the maximum number of locals that can be printed");
1527            }
1528            for _ in 0..cnt {
1529                if first {
1530                    self.newline(offset)?;
1531                    first = false;
1532                }
1533                locals.start_local(Some(func_idx), params + local_idx, self, state)?;
1534                self.print_valtype(state, ty)?;
1535                locals.end_local(self)?;
1536                local_idx += 1;
1537            }
1538        }
1539        locals.finish(self)?;
1540
1541        if let Some(f) = &mut validator {
1542            if let Err(e) = f.read_locals(body.get_binary_reader()) {
1543                validator = None;
1544                self.newline_unknown_pos()?;
1545                write!(self.result, ";; locals are invalid: {e}")?;
1546            }
1547        }
1548
1549        let nesting_start = self.nesting;
1550        let fold_instructions = self.config.fold_instructions;
1551        let mut operator_state = OperatorState::new(self, OperatorSeparator::Newline);
1552
1553        let end_pos = if fold_instructions {
1554            let mut folded_printer = PrintOperatorFolded::new(self, state, &mut operator_state);
1555            folded_printer.set_offset(func_start);
1556            folded_printer.begin_function(func_idx)?;
1557            Self::print_operators(
1558                &mut reader,
1559                branch_hints,
1560                func_start,
1561                &mut folded_printer,
1562                validator,
1563            )?
1564        } else {
1565            let mut flat_printer = PrintOperator::new(self, state, &mut operator_state);
1566            Self::print_operators(
1567                &mut reader,
1568                branch_hints,
1569                func_start,
1570                &mut flat_printer,
1571                validator,
1572            )?
1573        };
1574
1575        // If this was an invalid function body then the nesting may not
1576        // have reset back to normal. Fix that up here and forcibly insert
1577        // a newline as well in case the last instruction was something
1578        // like an `if` which has a comment after it which could interfere
1579        // with the closing paren printed for the func.
1580        if self.nesting != nesting_start {
1581            self.nesting = nesting_start;
1582            self.newline(reader.original_position())?;
1583        }
1584
1585        Ok(end_pos)
1586    }
1587
1588    fn print_operators<'a, O: OpPrinter>(
1589        body: &mut BinaryReader<'a>,
1590        mut branch_hints: &[(usize, BranchHint)],
1591        func_start: usize,
1592        op_printer: &mut O,
1593        mut validator: Option<operand_stack::FuncValidator>,
1594    ) -> Result<usize> {
1595        let mut ops = OperatorsReader::new(body.clone());
1596        while !ops.eof() {
1597            if ops.is_end_then_eof() {
1598                let mut annotation = None;
1599                if let Some(f) = &mut validator {
1600                    match f.visit_operator(&ops, true) {
1601                        Ok(()) => {}
1602                        Err(_) => {
1603                            annotation = Some(String::from("type mismatch at end of expression"))
1604                        }
1605                    }
1606                }
1607
1608                let end_pos = ops.original_position();
1609                ops.read()?; // final "end" opcode terminates instruction sequence
1610                ops.finish()?;
1611                op_printer.finalize(annotation.as_deref())?;
1612                return Ok(end_pos);
1613            }
1614
1615            // Branch hints are stored in increasing order of their body offset
1616            // so print them whenever their instruction comes up.
1617            if let Some(((hint_offset, hint), rest)) = branch_hints.split_first() {
1618                if hint.func_offset == (ops.original_position() - func_start) as u32 {
1619                    branch_hints = rest;
1620                    op_printer.branch_hint(*hint_offset, hint.taken)?;
1621                }
1622            }
1623            let mut annotation = None;
1624            if let Some(f) = &mut validator {
1625                let result = f
1626                    .visit_operator(&ops, false)
1627                    .map_err(anyhow::Error::from)
1628                    .and_then(|()| f.visualize_operand_stack(op_printer.use_color()));
1629                match result {
1630                    Ok(s) => annotation = Some(s),
1631                    Err(_) => {
1632                        validator = None;
1633                        annotation = Some(String::from("(invalid)"));
1634                    }
1635                }
1636            }
1637            op_printer.set_offset(ops.original_position());
1638            op_printer.visit_operator(&mut ops, annotation.as_deref())?;
1639        }
1640        ops.finish()?; // for the error message
1641        bail!("unexpected end of operators");
1642    }
1643
1644    fn newline(&mut self, offset: usize) -> Result<()> {
1645        self.print_newline(Some(offset))
1646    }
1647
1648    fn newline_unknown_pos(&mut self) -> Result<()> {
1649        self.print_newline(None)
1650    }
1651
1652    fn print_newline(&mut self, offset: Option<usize>) -> Result<()> {
1653        self.result.newline()?;
1654        self.result.start_line(offset);
1655
1656        if self.config.print_offsets {
1657            match offset {
1658                Some(offset) => {
1659                    self.result.start_comment()?;
1660                    write!(self.result, "(;@{offset:<6x};)")?;
1661                    self.result.reset_color()?;
1662                }
1663                None => self.result.write_str("           ")?,
1664            }
1665        }
1666        self.line += 1;
1667
1668        // Clamp the maximum nesting size that we print at something somewhat
1669        // reasonable to avoid generating hundreds of megabytes of whitespace
1670        // for small-ish modules that have deep-ish nesting.
1671        for _ in 0..self.nesting.min(MAX_NESTING_TO_PRINT) {
1672            self.result.write_str(&self.config.indent_text)?;
1673        }
1674        Ok(())
1675    }
1676
1677    fn print_exports(&mut self, state: &State, data: ExportSectionReader) -> Result<()> {
1678        for export in data.into_iter_with_offsets() {
1679            let (offset, export) = export?;
1680            self.newline(offset)?;
1681            self.print_export(state, &export)?;
1682        }
1683        Ok(())
1684    }
1685
1686    fn print_export(&mut self, state: &State, export: &Export) -> Result<()> {
1687        self.start_group("export ")?;
1688        self.print_str(export.name)?;
1689        self.result.write_str(" ")?;
1690        self.print_external_kind(state, export.kind, export.index)?;
1691        self.end_group()?; // export
1692        Ok(())
1693    }
1694
1695    fn print_external_kind(&mut self, state: &State, kind: ExternalKind, index: u32) -> Result<()> {
1696        match kind {
1697            ExternalKind::Func | ExternalKind::FuncExact => {
1698                self.start_group("func ")?;
1699                self.print_idx(&state.core.func_names, index)?;
1700            }
1701            ExternalKind::Table => {
1702                self.start_group("table ")?;
1703                self.print_idx(&state.core.table_names, index)?;
1704            }
1705            ExternalKind::Global => {
1706                self.start_group("global ")?;
1707                self.print_idx(&state.core.global_names, index)?;
1708            }
1709            ExternalKind::Memory => {
1710                self.start_group("memory ")?;
1711                self.print_idx(&state.core.memory_names, index)?;
1712            }
1713            ExternalKind::Tag => {
1714                self.start_group("tag ")?;
1715                write!(self.result, "{index}")?;
1716            }
1717        }
1718        self.end_group()?;
1719        Ok(())
1720    }
1721
1722    fn print_core_type_ref(&mut self, state: &State, idx: u32) -> Result<()> {
1723        self.start_group("type ")?;
1724        self.print_idx(&state.core.type_names, idx)?;
1725        self.end_group()?;
1726        Ok(())
1727    }
1728
1729    // Note: in the text format, modules can use identifiers that are defined anywhere, but
1730    // components can only use previously-defined identifiers. In the binary format,
1731    // invalid components can make forward references to an index that appears in the name section;
1732    // these can be printed but the output won't parse.
1733    fn print_idx<K>(&mut self, names: &NamingMap<u32, K>, idx: u32) -> Result<()>
1734    where
1735        K: NamingNamespace,
1736    {
1737        self._print_idx(&names.index_to_name, idx, K::desc())
1738    }
1739
1740    fn _print_idx(&mut self, names: &HashMap<u32, Naming>, idx: u32, desc: &str) -> Result<()> {
1741        self.result.start_name()?;
1742        match names.get(&idx) {
1743            Some(name) => name.write_identifier(self)?,
1744            None if self.config.name_unnamed => write!(self.result, "$#{desc}{idx}")?,
1745            None => write!(self.result, "{idx}")?,
1746        }
1747        self.result.reset_color()?;
1748        Ok(())
1749    }
1750
1751    fn print_local_idx(&mut self, state: &State, func: u32, idx: u32) -> Result<()> {
1752        self.result.start_name()?;
1753        match state.core.local_names.index_to_name.get(&(func, idx)) {
1754            Some(name) => name.write_identifier(self)?,
1755            None if self.config.name_unnamed => write!(self.result, "$#local{idx}")?,
1756            None => write!(self.result, "{idx}")?,
1757        }
1758        self.result.reset_color()?;
1759        Ok(())
1760    }
1761
1762    fn print_field_idx(&mut self, state: &State, ty: u32, idx: u32) -> Result<()> {
1763        self.result.start_name()?;
1764        match state.core.field_names.index_to_name.get(&(ty, idx)) {
1765            Some(name) => name.write_identifier(self)?,
1766            None if self.config.name_unnamed => write!(self.result, "$#field{idx}")?,
1767            None => write!(self.result, "{idx}")?,
1768        }
1769        self.result.reset_color()?;
1770        Ok(())
1771    }
1772
1773    fn print_name<K>(&mut self, names: &NamingMap<u32, K>, cur_idx: u32) -> Result<()>
1774    where
1775        K: NamingNamespace,
1776    {
1777        self._print_name(&names.index_to_name, cur_idx, K::desc())
1778    }
1779
1780    fn _print_name(
1781        &mut self,
1782        names: &HashMap<u32, Naming>,
1783        cur_idx: u32,
1784        desc: &str,
1785    ) -> Result<()> {
1786        self.result.start_name()?;
1787        match names.get(&cur_idx) {
1788            Some(name) => {
1789                name.write(self)?;
1790                self.result.write_str(" ")?;
1791            }
1792            None if self.config.name_unnamed => {
1793                write!(self.result, "$#{desc}{cur_idx} ")?;
1794            }
1795            None => {}
1796        }
1797        write!(self.result, "(;{cur_idx};)")?;
1798        self.result.reset_color()?;
1799        Ok(())
1800    }
1801
1802    fn print_elems(&mut self, state: &mut State, data: ElementSectionReader) -> Result<()> {
1803        for (i, elem) in data.into_iter_with_offsets().enumerate() {
1804            let (offset, mut elem) = elem?;
1805            self.newline(offset)?;
1806            self.start_group("elem ")?;
1807            self.print_name(&state.core.element_names, i as u32)?;
1808            match &mut elem.kind {
1809                ElementKind::Passive => {}
1810                ElementKind::Declared => self.result.write_str(" declare")?,
1811                ElementKind::Active {
1812                    table_index,
1813                    offset_expr,
1814                } => {
1815                    if let Some(table_index) = *table_index {
1816                        self.result.write_str(" ")?;
1817                        self.start_group("table ")?;
1818                        self.print_idx(&state.core.table_names, table_index)?;
1819                        self.end_group()?;
1820                    }
1821                    self.result.write_str(" ")?;
1822                    self.print_const_expr_sugar(state, offset_expr, "offset")?;
1823                }
1824            }
1825            self.result.write_str(" ")?;
1826
1827            if self.config.print_skeleton {
1828                self.result.write_str("...")?;
1829            } else {
1830                match elem.items {
1831                    ElementItems::Functions(reader) => {
1832                        self.result.write_str("func")?;
1833                        for idx in reader {
1834                            self.result.write_str(" ")?;
1835                            self.print_idx(&state.core.func_names, idx?)?
1836                        }
1837                    }
1838                    ElementItems::Expressions(ty, reader) => {
1839                        self.print_reftype(state, ty)?;
1840                        for expr in reader {
1841                            self.result.write_str(" ")?;
1842                            self.print_const_expr_sugar(state, &expr?, "item")?
1843                        }
1844                    }
1845                }
1846            }
1847            self.end_group()?;
1848        }
1849        Ok(())
1850    }
1851
1852    fn print_data(&mut self, state: &mut State, data: DataSectionReader) -> Result<()> {
1853        for (i, data) in data.into_iter_with_offsets().enumerate() {
1854            let (offset, data) = data?;
1855            self.newline(offset)?;
1856            self.start_group("data ")?;
1857            self.print_name(&state.core.data_names, i as u32)?;
1858            self.result.write_str(" ")?;
1859            match &data.kind {
1860                DataKind::Passive => {}
1861                DataKind::Active {
1862                    memory_index,
1863                    offset_expr,
1864                } => {
1865                    if *memory_index != 0 {
1866                        self.start_group("memory ")?;
1867                        self.print_idx(&state.core.memory_names, *memory_index)?;
1868                        self.end_group()?;
1869                        self.result.write_str(" ")?;
1870                    }
1871                    self.print_const_expr_sugar(state, offset_expr, "offset")?;
1872                    self.result.write_str(" ")?;
1873                }
1874            }
1875            if self.config.print_skeleton {
1876                self.result.write_str("...")?;
1877            } else {
1878                self.print_bytes(data.data)?;
1879            }
1880            self.end_group()?;
1881        }
1882        Ok(())
1883    }
1884
1885    /// Prints the operators of `expr` space-separated, taking into account that
1886    /// if there's only one operator in `expr` then instead of `(explicit ...)`
1887    /// the printing can be `(...)`.
1888    fn print_const_expr_sugar(
1889        &mut self,
1890        state: &mut State,
1891        expr: &ConstExpr,
1892        explicit: &str,
1893    ) -> Result<()> {
1894        self.start_group("")?;
1895        let mut reader = expr.get_operators_reader();
1896
1897        if reader.read().is_ok() && !reader.is_end_then_eof() {
1898            write!(self.result, "{explicit} ")?;
1899            self.print_const_expr(state, expr, self.config.fold_instructions)?;
1900        } else {
1901            self.print_const_expr(state, expr, false)?;
1902        }
1903
1904        self.end_group()?;
1905        Ok(())
1906    }
1907
1908    /// Prints the operators of `expr` space-separated.
1909    fn print_const_expr(&mut self, state: &mut State, expr: &ConstExpr, fold: bool) -> Result<()> {
1910        let mut reader = expr.get_binary_reader();
1911        let mut operator_state = OperatorState::new(self, OperatorSeparator::NoneThenSpace);
1912
1913        if fold {
1914            let mut folded_printer = PrintOperatorFolded::new(self, state, &mut operator_state);
1915            folded_printer.begin_const_expr();
1916            Self::print_operators(&mut reader, &[], 0, &mut folded_printer, None)?;
1917        } else {
1918            let mut op_printer = PrintOperator::new(self, state, &mut operator_state);
1919            Self::print_operators(&mut reader, &[], 0, &mut op_printer, None)?;
1920        }
1921
1922        Ok(())
1923    }
1924
1925    fn print_str(&mut self, name: &str) -> Result<()> {
1926        self.result.start_literal()?;
1927        self.result.write_str("\"")?;
1928        self.print_str_contents(name)?;
1929        self.result.write_str("\"")?;
1930        self.result.reset_color()?;
1931        Ok(())
1932    }
1933
1934    fn print_str_contents(&mut self, name: &str) -> Result<()> {
1935        for c in name.chars() {
1936            let v = c as u32;
1937            if (0x20..0x7f).contains(&v) && c != '"' && c != '\\' && v < 0xff {
1938                write!(self.result, "{c}")?;
1939            } else {
1940                write!(self.result, "\\u{{{v:x}}}",)?;
1941            }
1942        }
1943        Ok(())
1944    }
1945
1946    fn print_bytes(&mut self, bytes: &[u8]) -> Result<()> {
1947        self.result.start_literal()?;
1948        self.result.write_str("\"")?;
1949        for byte in bytes {
1950            if *byte >= 0x20 && *byte < 0x7f && *byte != b'"' && *byte != b'\\' {
1951                write!(self.result, "{}", *byte as char)?;
1952            } else {
1953                self.hex_byte(*byte)?;
1954            }
1955        }
1956        self.result.write_str("\"")?;
1957        self.result.reset_color()?;
1958        Ok(())
1959    }
1960
1961    fn hex_byte(&mut self, byte: u8) -> Result<()> {
1962        write!(self.result, "\\{byte:02x}")?;
1963        Ok(())
1964    }
1965
1966    fn print_known_custom_section(&mut self, section: CustomSectionReader<'_>) -> Result<bool> {
1967        match section.as_known() {
1968            // For now `wasmprinter` has invented syntax for `producers` and
1969            // `dylink.0` below to use in tests. Note that this syntax is not
1970            // official at this time.
1971            KnownCustom::Producers(s) => {
1972                self.newline(section.range().start)?;
1973                self.print_producers_section(s)?;
1974                Ok(true)
1975            }
1976            KnownCustom::Dylink0(s) => {
1977                self.newline(section.range().start)?;
1978                self.print_dylink0_section(s)?;
1979                Ok(true)
1980            }
1981
1982            // These are parsed during `read_names` and are part of
1983            // printing elsewhere, so don't print them.
1984            KnownCustom::Name(_) | KnownCustom::BranchHints(_) => Ok(true),
1985            #[cfg(feature = "component-model")]
1986            KnownCustom::ComponentName(_) => Ok(true),
1987
1988            _ => Ok(false),
1989        }
1990    }
1991
1992    fn print_raw_custom_section(
1993        &mut self,
1994        state: &State,
1995        section: CustomSectionReader<'_>,
1996    ) -> Result<()> {
1997        self.newline(section.range().start)?;
1998        self.start_group("@custom ")?;
1999        self.print_str(section.name())?;
2000        if let Some((place, _)) = state.custom_section_place {
2001            write!(self.result, " ({place})")?;
2002        }
2003        self.result.write_str(" ")?;
2004        if self.config.print_skeleton {
2005            self.result.write_str("...")?;
2006        } else {
2007            self.print_bytes(section.data())?;
2008        }
2009        self.end_group()?;
2010        Ok(())
2011    }
2012
2013    fn print_producers_section(&mut self, section: ProducersSectionReader<'_>) -> Result<()> {
2014        self.start_group("@producers")?;
2015        for field in section {
2016            let field = field?;
2017            for value in field.values.into_iter_with_offsets() {
2018                let (offset, value) = value?;
2019                self.newline(offset)?;
2020                self.start_group(field.name)?;
2021                self.result.write_str(" ")?;
2022                self.print_str(value.name)?;
2023                self.result.write_str(" ")?;
2024                self.print_str(value.version)?;
2025                self.end_group()?;
2026            }
2027        }
2028        self.end_group()?;
2029        Ok(())
2030    }
2031
2032    fn print_dylink0_section(&mut self, mut section: Dylink0SectionReader<'_>) -> Result<()> {
2033        self.start_group("@dylink.0")?;
2034        loop {
2035            let start = section.original_position();
2036            let next = match section.next() {
2037                Some(Ok(next)) => next,
2038                Some(Err(e)) => return Err(e.into()),
2039                None => break,
2040            };
2041            match next {
2042                Dylink0Subsection::MemInfo(info) => {
2043                    self.newline(start)?;
2044                    self.start_group("mem-info")?;
2045                    if info.memory_size > 0 || info.memory_alignment > 0 {
2046                        write!(
2047                            self.result,
2048                            " (memory {} {})",
2049                            info.memory_size, info.memory_alignment
2050                        )?;
2051                    }
2052                    if info.table_size > 0 || info.table_alignment > 0 {
2053                        write!(
2054                            self.result,
2055                            " (table {} {})",
2056                            info.table_size, info.table_alignment
2057                        )?;
2058                    }
2059                    self.end_group()?;
2060                }
2061                Dylink0Subsection::Needed(needed) => {
2062                    self.newline(start)?;
2063                    self.start_group("needed")?;
2064                    for s in needed {
2065                        self.result.write_str(" ")?;
2066                        self.print_str(s)?;
2067                    }
2068                    self.end_group()?;
2069                }
2070                Dylink0Subsection::ExportInfo(info) => {
2071                    for info in info {
2072                        self.newline(start)?;
2073                        self.start_group("export-info ")?;
2074                        self.print_str(info.name)?;
2075                        self.print_dylink0_flags(info.flags)?;
2076                        self.end_group()?;
2077                    }
2078                }
2079                Dylink0Subsection::ImportInfo(info) => {
2080                    for info in info {
2081                        self.newline(start)?;
2082                        self.start_group("import-info ")?;
2083                        self.print_str(info.module)?;
2084                        self.result.write_str(" ")?;
2085                        self.print_str(info.field)?;
2086                        self.print_dylink0_flags(info.flags)?;
2087                        self.end_group()?;
2088                    }
2089                }
2090                Dylink0Subsection::RuntimePath(runtime_path) => {
2091                    self.newline(start)?;
2092                    self.start_group("runtime-path")?;
2093                    for s in runtime_path {
2094                        self.result.write_str(" ")?;
2095                        self.print_str(s)?;
2096                    }
2097                    self.end_group()?;
2098                }
2099                Dylink0Subsection::Unknown { ty, .. } => {
2100                    bail!("don't know how to print dylink.0 subsection id {ty}");
2101                }
2102            }
2103        }
2104        self.end_group()?;
2105        Ok(())
2106    }
2107
2108    fn print_dylink0_flags(&mut self, mut flags: SymbolFlags) -> Result<()> {
2109        macro_rules! print_flag {
2110            ($($name:ident = $text:tt)*) => ({$(
2111                if flags.contains(SymbolFlags::$name) {
2112                    flags.remove(SymbolFlags::$name);
2113                    write!(self.result, concat!(" ", $text))?;
2114                }
2115            )*})
2116        }
2117        // N.B.: Keep in sync with `parse_sym_flags` in `crates/wast/src/core/custom.rs`.
2118        print_flag! {
2119            BINDING_WEAK = "binding-weak"
2120            BINDING_LOCAL = "binding-local"
2121            VISIBILITY_HIDDEN = "visibility-hidden"
2122            UNDEFINED = "undefined"
2123            EXPORTED = "exported"
2124            EXPLICIT_NAME = "explicit-name"
2125            NO_STRIP = "no-strip"
2126            TLS = "tls"
2127            ABSOLUTE = "absolute"
2128        }
2129        if !flags.is_empty() {
2130            write!(self.result, " {flags:#x}")?;
2131        }
2132        Ok(())
2133    }
2134
2135    fn register_branch_hint_section(&mut self, section: BranchHintSectionReader<'_>) -> Result<()> {
2136        self.code_section_hints.clear();
2137        for func in section {
2138            let func = func?;
2139            if self.code_section_hints.len() >= MAX_WASM_FUNCTIONS as usize {
2140                bail!("found too many hints");
2141            }
2142            if func.hints.count() >= MAX_WASM_FUNCTION_SIZE {
2143                bail!("found too many hints");
2144            }
2145            let hints = func
2146                .hints
2147                .into_iter_with_offsets()
2148                .collect::<wasmparser::Result<Vec<_>>>()?;
2149            self.code_section_hints.push((func.func, hints));
2150        }
2151        self.code_section_hints.reverse();
2152        Ok(())
2153    }
2154}
2155
2156struct NamedLocalPrinter {
2157    group_name: &'static str,
2158    in_group: bool,
2159    end_group_after_local: bool,
2160    first: bool,
2161}
2162
2163impl NamedLocalPrinter {
2164    fn new(group_name: &'static str) -> NamedLocalPrinter {
2165        NamedLocalPrinter {
2166            group_name,
2167            in_group: false,
2168            end_group_after_local: false,
2169            first: true,
2170        }
2171    }
2172
2173    fn start_local(
2174        &mut self,
2175        func: Option<u32>,
2176        local: u32,
2177        dst: &mut Printer,
2178        state: &State,
2179    ) -> Result<()> {
2180        let name = state
2181            .core
2182            .local_names
2183            .index_to_name
2184            .get(&(func.unwrap_or(u32::MAX), local));
2185
2186        // Named locals must be in their own group, so if we have a name we need
2187        // to terminate the previous group.
2188        if name.is_some() && self.in_group {
2189            dst.end_group()?;
2190            self.in_group = false;
2191        }
2192
2193        if self.first {
2194            self.first = false;
2195        } else {
2196            dst.result.write_str(" ")?;
2197        }
2198
2199        // Next we either need a separator if we're already in a group or we
2200        // need to open a group for our new local.
2201        if !self.in_group {
2202            dst.start_group(self.group_name)?;
2203            dst.result.write_str(" ")?;
2204            self.in_group = true;
2205        }
2206
2207        // Print the optional name if given...
2208        match name {
2209            Some(name) => {
2210                name.write(dst)?;
2211                dst.result.write_str(" ")?;
2212                self.end_group_after_local = true;
2213            }
2214            None if dst.config.name_unnamed && func.is_some() => {
2215                write!(dst.result, "$#local{local} ")?;
2216                self.end_group_after_local = true;
2217            }
2218            None => {
2219                self.end_group_after_local = false;
2220            }
2221        }
2222        Ok(())
2223    }
2224
2225    fn end_local(&mut self, dst: &mut Printer) -> Result<()> {
2226        if self.end_group_after_local {
2227            dst.end_group()?;
2228            self.end_group_after_local = false;
2229            self.in_group = false;
2230        }
2231        Ok(())
2232    }
2233    fn finish(self, dst: &mut Printer) -> Result<()> {
2234        if self.in_group {
2235            dst.end_group()?;
2236        }
2237        Ok(())
2238    }
2239}
2240
2241macro_rules! print_float {
2242    ($name:ident $float:ident $uint:ident $sint:ident $exp_bits:tt) => {
2243        fn $name(&mut self, mut bits: $uint) -> Result<()> {
2244            // Calculate a few constants
2245            let int_width = mem::size_of::<$uint>() * 8;
2246            let exp_width = $exp_bits;
2247            let mantissa_width = int_width - 1 - exp_width;
2248            let bias = (1 << (exp_width - 1)) - 1;
2249            let max_exp = (1 as $sint) << (exp_width - 1);
2250            let min_exp = -max_exp + 1;
2251
2252            // Handle `NaN` and infinity specially
2253            let f = $float::from_bits(bits);
2254            if bits >> (int_width - 1) != 0 {
2255                bits ^= 1 << (int_width - 1);
2256                self.result.write_str("-")?;
2257            }
2258            if f.is_infinite() {
2259                self.result.start_literal()?;
2260                self.result.write_str("inf ")?;
2261                self.result.start_comment()?;
2262                write!(self.result, "(;={f};)")?;
2263                self.result.reset_color()?;
2264                return Ok(());
2265            }
2266            if f.is_nan() {
2267                let payload = bits & ((1 << mantissa_width) - 1);
2268                self.result.start_literal()?;
2269                if payload == 1 << (mantissa_width - 1) {
2270                    self.result.write_str("nan ")?;
2271                    self.result.start_comment()?;
2272                    write!(self.result, "(;={f};)")?;
2273                } else {
2274                    write!(self.result, "nan:{:#x} ", payload)?;
2275                    self.result.start_comment()?;
2276                    write!(self.result, "(;={f};)")?;
2277                }
2278                self.result.reset_color()?;
2279                return Ok(());
2280            }
2281
2282            // Figure out our exponent, but keep in mine that it's in an
2283            // integer width that may not be supported. As a result we do a few
2284            // tricks here:
2285            //
2286            // * Make the MSB the top bit of the exponent, then shift the
2287            //   exponent to the bottom. This means we now have a signed
2288            //   integer in `$sint` width representing the whole exponent.
2289            // * Do the arithmetic for the exponent (subtract)
2290            // * Next we only care about the lowest `$exp_bits` bits of the
2291            //   result, but we do care about the sign. Use sign-carrying of
2292            //   the signed integer shifts to shift it left then shift it back.
2293            //
2294            // Overall this should do basic arithmetic for `$exp_bits` bit
2295            // numbers and get the result back as a signed integer with `$sint`
2296            // bits in `exponent` representing the same decimal value.
2297            let mut exponent = (((bits << 1) as $sint) >> (mantissa_width + 1)).wrapping_sub(bias);
2298            exponent = (exponent << (int_width - exp_width)) >> (int_width - exp_width);
2299            let mut fraction = bits & ((1 << mantissa_width) - 1);
2300            self.result.start_literal()?;
2301            self.result.write_str("0x")?;
2302            if bits == 0 {
2303                self.result.write_str("0p+0")?;
2304            } else {
2305                self.result.write_str("1")?;
2306                if fraction > 0 {
2307                    fraction <<= (int_width - mantissa_width);
2308
2309                    // Apparently the subnormal is handled here. I don't know
2310                    // what a subnormal is. If someone else does, please let me
2311                    // know!
2312                    if exponent == min_exp {
2313                        let leading = fraction.leading_zeros();
2314                        if (leading as usize) < int_width - 1 {
2315                            fraction <<= leading + 1;
2316                        } else {
2317                            fraction = 0;
2318                        }
2319                        exponent -= leading as $sint;
2320                    }
2321
2322                    self.result.write_str(".")?;
2323                    while fraction > 0 {
2324                        write!(self.result, "{:x}", fraction >> (int_width - 4))?;
2325                        fraction <<= 4;
2326                    }
2327                }
2328                write!(self.result, "p{:+}", exponent)?;
2329            }
2330            self.result.start_comment()?;
2331            write!(self.result, " (;={};)", f)?;
2332            self.result.reset_color()?;
2333            Ok(())
2334        }
2335    };
2336}
2337
2338impl Printer<'_, '_> {
2339    print_float!(print_f32 f32 u32 i32 8);
2340    print_float!(print_f64 f64 u64 i64 11);
2341}
2342
2343impl Naming {
2344    fn new<'a>(
2345        name: &'a str,
2346        index: u32,
2347        group: &str,
2348        used: Option<&mut HashSet<&'a str>>,
2349    ) -> Naming {
2350        let mut kind = NamingKind::DollarName;
2351        if name.chars().any(|c| !is_idchar(c)) {
2352            kind = NamingKind::DollarQuotedName;
2353        }
2354
2355        // If the `name` provided can't be used as the raw identifier for the
2356        // item that it's describing then a synthetic name must be made. The
2357        // rules here which generate a name are:
2358        //
2359        // * Empty identifiers are not allowed
2360        // * Identifiers have a fixed set of valid characters
2361        // * For wasmprinter's purposes we "reserve" identifiers with the `#`
2362        //   prefix, which is in theory rare to encounter in practice.
2363        // * If the name has already been used for some other item and cannot
2364        //   be reused (e.g. because shadowing in this context is not possible).
2365        //
2366        // If any of these conditions match then we generate a unique identifier
2367        // based on `name` but not it exactly. By factoring in the `group`,
2368        // `index`, and `name` we get a guaranteed unique identifier (due to the
2369        // leading `#` prefix that we reserve and factoring in of the item
2370        // index) while preserving human readability at least somewhat (the
2371        // valid identifier characters of `name` still appear in the returned
2372        // name).
2373        if name.is_empty()
2374            || name.starts_with('#')
2375            || used.map(|set| !set.insert(name)).unwrap_or(false)
2376        {
2377            kind = NamingKind::SyntheticPrefix(format!("#{group}{index}"));
2378        }
2379        return Naming {
2380            kind,
2381            name: name.to_string(),
2382        };
2383
2384        // See https://webassembly.github.io/spec/core/text/values.html#text-id
2385        fn is_idchar(c: char) -> bool {
2386            matches!(
2387                c,
2388                '0'..='9'
2389                | 'a'..='z'
2390                | 'A'..='Z'
2391                | '!'
2392                | '#'
2393                | '$'
2394                | '%'
2395                | '&'
2396                | '\''
2397                | '*'
2398                | '+'
2399                | '-'
2400                | '.'
2401                | '/'
2402                | ':'
2403                | '<'
2404                | '='
2405                | '>'
2406                | '?'
2407                | '@'
2408                | '\\'
2409                | '^'
2410                | '_'
2411                | '`'
2412                | '|'
2413                | '~'
2414            )
2415        }
2416    }
2417
2418    fn write_identifier(&self, printer: &mut Printer<'_, '_>) -> Result<()> {
2419        match &self.kind {
2420            NamingKind::DollarName => {
2421                printer.result.write_str("$")?;
2422                printer.result.write_str(&self.name)?;
2423            }
2424            NamingKind::DollarQuotedName => {
2425                printer.result.write_str("$\"")?;
2426                printer.print_str_contents(&self.name)?;
2427                printer.result.write_str("\"")?;
2428            }
2429            NamingKind::SyntheticPrefix(prefix) => {
2430                printer.result.write_str("$\"")?;
2431                printer.result.write_str(&prefix)?;
2432                printer.result.write_str(" ")?;
2433                printer.print_str_contents(&self.name)?;
2434                printer.result.write_str("\"")?;
2435            }
2436        }
2437        Ok(())
2438    }
2439
2440    fn write(&self, dst: &mut Printer<'_, '_>) -> Result<()> {
2441        self.write_identifier(dst)?;
2442        match &self.kind {
2443            NamingKind::DollarName | NamingKind::DollarQuotedName => {}
2444
2445            NamingKind::SyntheticPrefix(_) => {
2446                dst.result.write_str(" ")?;
2447                dst.start_group("@name \"")?;
2448                dst.print_str_contents(&self.name)?;
2449                dst.result.write_str("\"")?;
2450                dst.end_group()?;
2451            }
2452        }
2453        Ok(())
2454    }
2455}
2456
2457/// Helper trait for the `NamingMap` type's `K` type parameter.
2458trait NamingNamespace {
2459    fn desc() -> &'static str;
2460}
2461
2462macro_rules! naming_namespaces {
2463    ($(struct $name:ident => $desc:tt)*) => ($(
2464        struct $name;
2465
2466        impl NamingNamespace for $name {
2467            fn desc() -> &'static str { $desc }
2468        }
2469    )*)
2470}
2471
2472naming_namespaces! {
2473    struct NameFunc => "func"
2474    struct NameGlobal => "global"
2475    struct NameMemory => "memory"
2476    struct NameLocal => "local"
2477    struct NameLabel => "label"
2478    struct NameTable => "table"
2479    struct NameType => "type"
2480    struct NameField => "field"
2481    struct NameData => "data"
2482    struct NameElem => "elem"
2483    struct NameTag => "tag"
2484}
2485
2486#[cfg(feature = "component-model")]
2487naming_namespaces! {
2488    struct NameModule => "module"
2489    struct NameInstance => "instance"
2490    struct NameValue => "value"
2491    struct NameComponent => "component"
2492}
2493
2494fn name_map<K>(into: &mut NamingMap<u32, K>, names: NameMap<'_>, name: &str) -> Result<()> {
2495    let mut used = HashSet::new();
2496    for naming in names {
2497        let naming = naming?;
2498        into.index_to_name.insert(
2499            naming.index,
2500            Naming::new(naming.name, naming.index, name, Some(&mut used)),
2501        );
2502    }
2503    Ok(())
2504}