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_auto_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()?; // 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.newline(offset)?;
762                    if self.config.print_offsets {
763                        self.result.newline()?;
764                    }
765                    break;
766                }
767
768                other => match other.as_section() {
769                    Some((id, _)) => bail!("found unknown section `{}`", id),
770                    None => bail!("found unknown payload"),
771                },
772            }
773        }
774
775        Ok(())
776    }
777
778    fn update_custom_section_place(&self, states: &mut Vec<State>, place: &'static str) {
779        if let Some(last) = states.last_mut() {
780            if let Some((prev, prev_line)) = &mut last.custom_section_place {
781                if *prev_line != self.line {
782                    *prev = place;
783                    *prev_line = self.line;
784                }
785            }
786        }
787    }
788
789    fn update_custom_section_line(&self, states: &mut Vec<State>) {
790        if let Some(last) = states.last_mut() {
791            if let Some((_, prev_line)) = &mut last.custom_section_place {
792                *prev_line = self.line;
793            }
794        }
795    }
796
797    fn start_group(&mut self, name: &str) -> Result<()> {
798        write!(self.result, "(")?;
799        self.result.start_keyword()?;
800        write!(self.result, "{name}")?;
801        self.result.reset_color()?;
802        self.nesting += 1;
803        self.group_lines.push(self.line);
804        Ok(())
805    }
806
807    fn end_group(&mut self) -> Result<()> {
808        self.nesting -= 1;
809        if let Some(line) = self.group_lines.pop() {
810            if line != self.line {
811                self.newline_unknown_pos()?;
812            }
813        }
814        self.result.write_str(")")?;
815        Ok(())
816    }
817
818    fn register_names(&mut self, state: &mut State, names: NameSectionReader<'_>) -> Result<()> {
819        fn indirect_name_map<K>(
820            into: &mut NamingMap<(u32, u32), K>,
821            names: IndirectNameMap<'_>,
822            name: &str,
823        ) -> Result<()> {
824            for indirect in names {
825                let indirect = indirect?;
826                let mut used = match name {
827                    // labels can be shadowed, so maintaining the used names is not useful.
828                    "label" => None,
829                    "local" | "field" => Some(HashSet::new()),
830                    _ => unimplemented!("{name} is an unknown type of indirect names"),
831                };
832                for naming in indirect.names {
833                    let naming = naming?;
834                    into.index_to_name.insert(
835                        (indirect.index, naming.index),
836                        Naming::new(naming.name, naming.index, name, used.as_mut()),
837                    );
838                }
839            }
840            Ok(())
841        }
842
843        for section in names {
844            match section? {
845                Name::Module { name, .. } => {
846                    let name = Naming::new(name, 0, "module", None);
847                    state.name = Some(name);
848                }
849                Name::Function(n) => name_map(&mut state.core.func_names, n, "func")?,
850                Name::Local(n) => indirect_name_map(&mut state.core.local_names, n, "local")?,
851                Name::Label(n) => indirect_name_map(&mut state.core.label_names, n, "label")?,
852                Name::Type(n) => name_map(&mut state.core.type_names, n, "type")?,
853                Name::Table(n) => name_map(&mut state.core.table_names, n, "table")?,
854                Name::Memory(n) => name_map(&mut state.core.memory_names, n, "memory")?,
855                Name::Global(n) => name_map(&mut state.core.global_names, n, "global")?,
856                Name::Element(n) => name_map(&mut state.core.element_names, n, "elem")?,
857                Name::Data(n) => name_map(&mut state.core.data_names, n, "data")?,
858                Name::Field(n) => indirect_name_map(&mut state.core.field_names, n, "field")?,
859                Name::Tag(n) => name_map(&mut state.core.tag_names, n, "tag")?,
860                Name::Unknown { .. } => (),
861            }
862        }
863        Ok(())
864    }
865
866    fn print_rec(
867        &mut self,
868        state: &mut State,
869        offset: Option<usize>,
870        rec: RecGroup,
871        is_component: bool,
872    ) -> Result<()> {
873        if rec.is_explicit_rec_group() {
874            if is_component {
875                self.start_group("core rec")?;
876            } else {
877                self.start_group("rec")?;
878            }
879            for ty in rec.into_types() {
880                match offset {
881                    Some(offset) => self.newline(offset + 2)?,
882                    None => self.newline_unknown_pos()?,
883                }
884                self.print_type(state, ty, false)?;
885            }
886            self.end_group()?; // `rec`
887        } else {
888            assert_eq!(rec.types().len(), 1);
889            let ty = rec.into_types().next().unwrap();
890            self.print_type(state, ty, is_component)?;
891        }
892        Ok(())
893    }
894
895    fn print_type(&mut self, state: &mut State, ty: SubType, is_component: bool) -> Result<()> {
896        if is_component {
897            self.start_group("core type ")?;
898        } else {
899            self.start_group("type ")?;
900        }
901        let ty_idx = state.core.types.len() as u32;
902        self.print_name(&state.core.type_names, ty_idx)?;
903        self.result.write_str(" ")?;
904        self.print_sub(state, &ty, ty_idx)?;
905        self.end_group()?; // `type`
906        state.core.types.push(Some(ty));
907        Ok(())
908    }
909
910    fn print_sub(&mut self, state: &State, ty: &SubType, ty_idx: u32) -> Result<u32> {
911        let r = if !ty.is_final || !ty.supertype_idx.is_none() {
912            self.start_group("sub")?;
913            self.print_sub_type(state, ty)?;
914            let r = self.print_composite(state, &ty.composite_type, ty_idx)?;
915            self.end_group()?; // `sub`
916            r
917        } else {
918            self.print_composite(state, &ty.composite_type, ty_idx)?
919        };
920        Ok(r)
921    }
922
923    fn print_composite(&mut self, state: &State, ty: &CompositeType, ty_idx: u32) -> Result<u32> {
924        if ty.shared {
925            self.start_group("shared")?;
926            self.result.write_str(" ")?;
927        }
928        let r = match &ty.inner {
929            CompositeInnerType::Func(ty) => {
930                self.start_group("func")?;
931                let r = self.print_func_type(state, ty, None)?;
932                self.end_group()?; // `func`
933                r
934            }
935            CompositeInnerType::Array(ty) => {
936                self.start_group("array")?;
937                let r = self.print_array_type(state, ty)?;
938                self.end_group()?; // `array`
939                r
940            }
941            CompositeInnerType::Struct(ty) => {
942                self.start_group("struct")?;
943                let r = self.print_struct_type(state, ty, ty_idx)?;
944                self.end_group()?; // `struct`
945                r
946            }
947            CompositeInnerType::Cont(ty) => {
948                self.start_group("cont")?;
949                let r = self.print_cont_type(state, ty)?;
950                self.end_group()?; // `cont`
951                r
952            }
953        };
954        if ty.shared {
955            self.end_group()?; // `shared`
956        }
957        Ok(r)
958    }
959
960    fn print_types(&mut self, state: &mut State, parser: TypeSectionReader<'_>) -> Result<()> {
961        for ty in parser.into_iter_with_offsets() {
962            let (offset, rec_group) = ty?;
963            self.newline(offset)?;
964            self.print_rec(state, Some(offset), rec_group, false)?;
965        }
966        Ok(())
967    }
968
969    fn print_core_functype_idx(
970        &mut self,
971        state: &State,
972        idx: u32,
973        names_for: Option<u32>,
974    ) -> Result<Option<u32>> {
975        self.print_core_type_ref(state, idx)?;
976
977        match state.core.types.get(idx as usize) {
978            Some(Some(SubType {
979                composite_type:
980                    CompositeType {
981                        inner: CompositeInnerType::Func(ty),
982                        shared: false,
983                    },
984                ..
985            })) => self.print_func_type(state, ty, names_for).map(Some),
986            Some(Some(_)) | Some(None) | None => Ok(None),
987        }
988    }
989
990    /// Returns the number of parameters, useful for local index calculations
991    /// later.
992    fn print_func_type(
993        &mut self,
994        state: &State,
995        ty: &FuncType,
996        names_for: Option<u32>,
997    ) -> Result<u32> {
998        if !ty.params().is_empty() {
999            self.result.write_str(" ")?;
1000        }
1001
1002        let mut params = NamedLocalPrinter::new("param");
1003        // Note that named parameters must be alone in a `param` block, so
1004        // we need to be careful to terminate previous param blocks and open
1005        // a new one if that's the case with a named parameter.
1006        for (i, param) in ty.params().iter().enumerate() {
1007            params.start_local(names_for, i as u32, self, state)?;
1008            self.print_valtype(state, *param)?;
1009            params.end_local(self)?;
1010        }
1011        params.finish(self)?;
1012        if !ty.results().is_empty() {
1013            self.result.write_str(" ")?;
1014            self.start_group("result")?;
1015            for result in ty.results().iter() {
1016                self.result.write_str(" ")?;
1017                self.print_valtype(state, *result)?;
1018            }
1019            self.end_group()?;
1020        }
1021        Ok(ty.params().len() as u32)
1022    }
1023
1024    fn print_field_type(
1025        &mut self,
1026        state: &State,
1027        ty: &FieldType,
1028        ty_field_idx: Option<(u32, u32)>,
1029    ) -> Result<u32> {
1030        self.result.write_str(" ")?;
1031        if let Some(idxs @ (_, field_idx)) = ty_field_idx {
1032            match state.core.field_names.index_to_name.get(&idxs) {
1033                Some(name) => {
1034                    name.write_identifier(self)?;
1035                    self.result.write_str(" ")?;
1036                }
1037                None if self.config.name_unnamed => write!(self.result, "$#field{field_idx} ")?,
1038                None => {}
1039            }
1040        }
1041        if ty.mutable {
1042            self.result.write_str("(mut ")?;
1043        }
1044        self.print_storage_type(state, ty.element_type)?;
1045        if ty.mutable {
1046            self.result.write_str(")")?;
1047        }
1048        Ok(0)
1049    }
1050
1051    fn print_array_type(&mut self, state: &State, ty: &ArrayType) -> Result<u32> {
1052        self.print_field_type(state, &ty.0, None)
1053    }
1054
1055    fn print_struct_type(&mut self, state: &State, ty: &StructType, ty_idx: u32) -> Result<u32> {
1056        for (field_index, field) in ty.fields.iter().enumerate() {
1057            self.result.write_str(" (field")?;
1058            self.print_field_type(state, field, Some((ty_idx, field_index as u32)))?;
1059            self.result.write_str(")")?;
1060        }
1061        Ok(0)
1062    }
1063
1064    fn print_cont_type(&mut self, state: &State, ct: &ContType) -> Result<u32> {
1065        self.result.write_str(" ")?;
1066        self.print_idx(&state.core.type_names, ct.0.as_module_index().unwrap())?;
1067        Ok(0)
1068    }
1069
1070    fn print_sub_type(&mut self, state: &State, ty: &SubType) -> Result<u32> {
1071        self.result.write_str(" ")?;
1072        if ty.is_final {
1073            self.result.write_str("final ")?;
1074        }
1075        if let Some(idx) = ty.supertype_idx {
1076            self.print_idx(&state.core.type_names, idx.as_module_index().unwrap())?;
1077            self.result.write_str(" ")?;
1078        }
1079        Ok(0)
1080    }
1081
1082    fn print_storage_type(&mut self, state: &State, ty: StorageType) -> Result<()> {
1083        match ty {
1084            StorageType::I8 => self.result.write_str("i8")?,
1085            StorageType::I16 => self.result.write_str("i16")?,
1086            StorageType::Val(val_type) => self.print_valtype(state, val_type)?,
1087        }
1088        Ok(())
1089    }
1090
1091    fn print_valtype(&mut self, state: &State, ty: ValType) -> Result<()> {
1092        match ty {
1093            ValType::I32 => self.print_type_keyword("i32")?,
1094            ValType::I64 => self.print_type_keyword("i64")?,
1095            ValType::F32 => self.print_type_keyword("f32")?,
1096            ValType::F64 => self.print_type_keyword("f64")?,
1097            ValType::V128 => self.print_type_keyword("v128")?,
1098            ValType::Ref(rt) => self.print_reftype(state, rt)?,
1099        }
1100        Ok(())
1101    }
1102
1103    fn print_valtypes(&mut self, state: &State, tys: Vec<ValType>) -> Result<()> {
1104        for ty in tys {
1105            self.result.write_str(" ")?;
1106            self.print_valtype(state, ty)?;
1107        }
1108        Ok(())
1109    }
1110
1111    fn print_reftype(&mut self, state: &State, ty: RefType) -> Result<()> {
1112        if ty.is_nullable() {
1113            match ty.as_non_null() {
1114                RefType::FUNC => self.print_type_keyword("funcref")?,
1115                RefType::EXTERN => self.print_type_keyword("externref")?,
1116                RefType::I31 => self.print_type_keyword("i31ref")?,
1117                RefType::ANY => self.print_type_keyword("anyref")?,
1118                RefType::NONE => self.print_type_keyword("nullref")?,
1119                RefType::NOEXTERN => self.print_type_keyword("nullexternref")?,
1120                RefType::NOFUNC => self.print_type_keyword("nullfuncref")?,
1121                RefType::EQ => self.print_type_keyword("eqref")?,
1122                RefType::STRUCT => self.print_type_keyword("structref")?,
1123                RefType::ARRAY => self.print_type_keyword("arrayref")?,
1124                RefType::EXN => self.print_type_keyword("exnref")?,
1125                RefType::NOEXN => self.print_type_keyword("nullexnref")?,
1126                _ => {
1127                    self.start_group("ref")?;
1128                    self.result.write_str(" null ")?;
1129                    self.print_heaptype(state, ty.heap_type())?;
1130                    self.end_group()?;
1131                }
1132            }
1133        } else {
1134            self.start_group("ref ")?;
1135            self.print_heaptype(state, ty.heap_type())?;
1136            self.end_group()?;
1137        }
1138        Ok(())
1139    }
1140
1141    fn print_heaptype(&mut self, state: &State, ty: HeapType) -> Result<()> {
1142        match ty {
1143            HeapType::Concrete(i) => {
1144                self.print_idx(&state.core.type_names, i.as_module_index().unwrap())?;
1145            }
1146            HeapType::Abstract { shared, ty } => {
1147                use AbstractHeapType::*;
1148                if shared {
1149                    self.start_group("shared ")?;
1150                }
1151                match ty {
1152                    Func => self.print_type_keyword("func")?,
1153                    Extern => self.print_type_keyword("extern")?,
1154                    Any => self.print_type_keyword("any")?,
1155                    None => self.print_type_keyword("none")?,
1156                    NoExtern => self.print_type_keyword("noextern")?,
1157                    NoFunc => self.print_type_keyword("nofunc")?,
1158                    Eq => self.print_type_keyword("eq")?,
1159                    Struct => self.print_type_keyword("struct")?,
1160                    Array => self.print_type_keyword("array")?,
1161                    I31 => self.print_type_keyword("i31")?,
1162                    Exn => self.print_type_keyword("exn")?,
1163                    NoExn => self.print_type_keyword("noexn")?,
1164                    Cont => self.print_type_keyword("cont")?,
1165                    NoCont => self.print_type_keyword("nocont")?,
1166                }
1167                if shared {
1168                    self.end_group()?;
1169                }
1170            }
1171        }
1172        Ok(())
1173    }
1174
1175    fn print_type_keyword(&mut self, keyword: &str) -> Result<()> {
1176        self.result.start_type()?;
1177        self.result.write_str(keyword)?;
1178        self.result.reset_color()?;
1179        Ok(())
1180    }
1181
1182    fn print_imports(&mut self, state: &mut State, parser: ImportSectionReader<'_>) -> Result<()> {
1183        for import in parser.into_iter_with_offsets() {
1184            let (offset, import) = import?;
1185            self.newline(offset)?;
1186            self.print_import(state, &import, true)?;
1187            match import.ty {
1188                TypeRef::Func(idx) => {
1189                    debug_assert!(state.core.func_to_type.len() == state.core.funcs as usize);
1190                    state.core.funcs += 1;
1191                    state.core.func_to_type.push(Some(idx))
1192                }
1193                TypeRef::Table(_) => state.core.tables += 1,
1194                TypeRef::Memory(_) => state.core.memories += 1,
1195                TypeRef::Tag(TagType {
1196                    kind: _,
1197                    func_type_idx: idx,
1198                }) => {
1199                    debug_assert!(state.core.tag_to_type.len() == state.core.tags as usize);
1200                    state.core.tags += 1;
1201                    state.core.tag_to_type.push(Some(idx))
1202                }
1203                TypeRef::Global(_) => state.core.globals += 1,
1204            }
1205        }
1206        Ok(())
1207    }
1208
1209    fn print_import(&mut self, state: &State, import: &Import<'_>, index: bool) -> Result<()> {
1210        self.start_group("import ")?;
1211        self.print_str(import.module)?;
1212        self.result.write_str(" ")?;
1213        self.print_str(import.name)?;
1214        self.result.write_str(" ")?;
1215        self.print_import_ty(state, &import.ty, index)?;
1216        self.end_group()?;
1217        Ok(())
1218    }
1219
1220    fn print_import_ty(&mut self, state: &State, ty: &TypeRef, index: bool) -> Result<()> {
1221        match ty {
1222            TypeRef::Func(f) => {
1223                self.start_group("func ")?;
1224                if index {
1225                    self.print_name(&state.core.func_names, state.core.funcs)?;
1226                    self.result.write_str(" ")?;
1227                }
1228                self.print_core_type_ref(state, *f)?;
1229            }
1230            TypeRef::Table(f) => self.print_table_type(state, f, index)?,
1231            TypeRef::Memory(f) => self.print_memory_type(state, f, index)?,
1232            TypeRef::Tag(f) => self.print_tag_type(state, f, index)?,
1233            TypeRef::Global(f) => self.print_global_type(state, f, index)?,
1234        }
1235        self.end_group()?;
1236        Ok(())
1237    }
1238
1239    fn print_table_type(&mut self, state: &State, ty: &TableType, index: bool) -> Result<()> {
1240        self.start_group("table ")?;
1241        if index {
1242            self.print_name(&state.core.table_names, state.core.tables)?;
1243            self.result.write_str(" ")?;
1244        }
1245        if ty.shared {
1246            self.print_type_keyword("shared ")?;
1247        }
1248        if ty.table64 {
1249            self.print_type_keyword("i64 ")?;
1250        }
1251        self.print_limits(ty.initial, ty.maximum)?;
1252        self.result.write_str(" ")?;
1253        self.print_reftype(state, ty.element_type)?;
1254        Ok(())
1255    }
1256
1257    fn print_memory_type(&mut self, state: &State, ty: &MemoryType, index: bool) -> Result<()> {
1258        self.start_group("memory ")?;
1259        if index {
1260            self.print_name(&state.core.memory_names, state.core.memories)?;
1261            self.result.write_str(" ")?;
1262        }
1263        if ty.memory64 {
1264            self.print_type_keyword("i64 ")?;
1265        }
1266        self.print_limits(ty.initial, ty.maximum)?;
1267        if ty.shared {
1268            self.print_type_keyword(" shared")?;
1269        }
1270        if let Some(p) = ty.page_size_log2 {
1271            let p = 1_u64
1272                .checked_shl(p)
1273                .ok_or_else(|| anyhow!("left shift overflow").context("invalid page size"))?;
1274
1275            self.result.write_str(" ")?;
1276            self.start_group("pagesize ")?;
1277            write!(self.result, "{p:#x}")?;
1278            self.end_group()?;
1279        }
1280        Ok(())
1281    }
1282
1283    fn print_tag_type(&mut self, state: &State, ty: &TagType, index: bool) -> Result<()> {
1284        self.start_group("tag ")?;
1285        if index {
1286            self.print_name(&state.core.tag_names, state.core.tags)?;
1287            self.result.write_str(" ")?;
1288        }
1289        self.print_core_functype_idx(state, ty.func_type_idx, None)?;
1290        Ok(())
1291    }
1292
1293    fn print_limits<T>(&mut self, initial: T, maximum: Option<T>) -> Result<()>
1294    where
1295        T: fmt::Display,
1296    {
1297        self.result.start_literal()?;
1298        write!(self.result, "{initial}")?;
1299        if let Some(max) = maximum {
1300            write!(self.result, " {max}")?;
1301        }
1302        self.result.reset_color()?;
1303        Ok(())
1304    }
1305
1306    fn print_global_type(&mut self, state: &State, ty: &GlobalType, index: bool) -> Result<()> {
1307        self.start_group("global ")?;
1308        if index {
1309            self.print_name(&state.core.global_names, state.core.globals)?;
1310            self.result.write_str(" ")?;
1311        }
1312        if ty.shared || ty.mutable {
1313            self.result.write_str("(")?;
1314            if ty.shared {
1315                self.print_type_keyword("shared ")?;
1316            }
1317            if ty.mutable {
1318                self.print_type_keyword("mut ")?;
1319            }
1320            self.print_valtype(state, ty.content_type)?;
1321            self.result.write_str(")")?;
1322        } else {
1323            self.print_valtype(state, ty.content_type)?;
1324        }
1325        Ok(())
1326    }
1327
1328    fn print_tables(&mut self, state: &mut State, parser: TableSectionReader<'_>) -> Result<()> {
1329        for table in parser.into_iter_with_offsets() {
1330            let (offset, table) = table?;
1331            self.newline(offset)?;
1332            self.print_table_type(state, &table.ty, true)?;
1333            match &table.init {
1334                TableInit::RefNull => {}
1335                TableInit::Expr(expr) => {
1336                    self.result.write_str(" ")?;
1337                    self.print_const_expr(state, expr, self.config.fold_instructions)?;
1338                }
1339            }
1340            self.end_group()?;
1341            state.core.tables += 1;
1342        }
1343        Ok(())
1344    }
1345
1346    fn print_memories(&mut self, state: &mut State, parser: MemorySectionReader<'_>) -> Result<()> {
1347        for memory in parser.into_iter_with_offsets() {
1348            let (offset, memory) = memory?;
1349            self.newline(offset)?;
1350            self.print_memory_type(state, &memory, true)?;
1351            self.end_group()?;
1352            state.core.memories += 1;
1353        }
1354        Ok(())
1355    }
1356
1357    fn print_tags(&mut self, state: &mut State, parser: TagSectionReader<'_>) -> Result<()> {
1358        for tag in parser.into_iter_with_offsets() {
1359            let (offset, tag) = tag?;
1360            self.newline(offset)?;
1361            self.print_tag_type(state, &tag, true)?;
1362            self.end_group()?;
1363            debug_assert!(state.core.tag_to_type.len() == state.core.tags as usize);
1364            state.core.tags += 1;
1365            state.core.tag_to_type.push(Some(tag.func_type_idx));
1366        }
1367        Ok(())
1368    }
1369
1370    fn print_globals(&mut self, state: &mut State, parser: GlobalSectionReader<'_>) -> Result<()> {
1371        for global in parser.into_iter_with_offsets() {
1372            let (offset, global) = global?;
1373            self.newline(offset)?;
1374            self.print_global_type(state, &global.ty, true)?;
1375            self.result.write_str(" ")?;
1376            self.print_const_expr(state, &global.init_expr, self.config.fold_instructions)?;
1377            self.end_group()?;
1378            state.core.globals += 1;
1379        }
1380        Ok(())
1381    }
1382
1383    fn print_code_section_entry(
1384        &mut self,
1385        state: &mut State,
1386        body: &FunctionBody<'_>,
1387        validator: Option<operand_stack::FuncValidator>,
1388    ) -> Result<()> {
1389        self.newline(body.get_binary_reader().original_position())?;
1390        self.start_group("func ")?;
1391        let func_idx = state.core.funcs;
1392        self.print_name(&state.core.func_names, func_idx)?;
1393        self.result.write_str(" ")?;
1394        let ty = match state.core.func_to_type.get(func_idx as usize) {
1395            Some(Some(x)) => *x,
1396            _ => panic!("invalid function type"),
1397        };
1398        let params = self
1399            .print_core_functype_idx(state, ty, Some(func_idx))?
1400            .unwrap_or(0);
1401
1402        // Hints are stored on `self` in reverse order of function index so
1403        // check the last one and see if it matches this function.
1404        let hints = match self.code_section_hints.last() {
1405            Some((f, _)) if *f == func_idx => {
1406                let (_, hints) = self.code_section_hints.pop().unwrap();
1407                hints
1408            }
1409            _ => Vec::new(),
1410        };
1411
1412        if self.config.print_skeleton {
1413            self.result.write_str(" ...")?;
1414        } else {
1415            self.print_func_body(state, func_idx, params, &body, &hints, validator)?;
1416        }
1417
1418        self.end_group()?;
1419        state.core.funcs += 1;
1420        Ok(())
1421    }
1422
1423    fn print_func_body(
1424        &mut self,
1425        state: &mut State,
1426        func_idx: u32,
1427        params: u32,
1428        body: &FunctionBody<'_>,
1429        branch_hints: &[(usize, BranchHint)],
1430        mut validator: Option<operand_stack::FuncValidator>,
1431    ) -> Result<()> {
1432        let mut first = true;
1433        let mut local_idx = 0;
1434        let mut locals = NamedLocalPrinter::new("local");
1435        let mut reader = body.get_binary_reader();
1436        let func_start = reader.original_position();
1437        for _ in 0..reader.read_var_u32()? {
1438            let offset = reader.original_position();
1439            let cnt = reader.read_var_u32()?;
1440            let ty = reader.read()?;
1441            if MAX_LOCALS
1442                .checked_sub(local_idx)
1443                .and_then(|s| s.checked_sub(cnt))
1444                .is_none()
1445            {
1446                bail!("function exceeds the maximum number of locals that can be printed");
1447            }
1448            for _ in 0..cnt {
1449                if first {
1450                    self.newline(offset)?;
1451                    first = false;
1452                }
1453                locals.start_local(Some(func_idx), params + local_idx, self, state)?;
1454                self.print_valtype(state, ty)?;
1455                locals.end_local(self)?;
1456                local_idx += 1;
1457            }
1458        }
1459        locals.finish(self)?;
1460
1461        if let Some(f) = &mut validator {
1462            if let Err(e) = f.read_locals(body.get_binary_reader()) {
1463                validator = None;
1464                self.newline_unknown_pos()?;
1465                write!(self.result, ";; locals are invalid: {e}")?;
1466            }
1467        }
1468
1469        let nesting_start = self.nesting;
1470        let fold_instructions = self.config.fold_instructions;
1471        let mut operator_state = OperatorState::new(self, OperatorSeparator::Newline);
1472
1473        if fold_instructions {
1474            let mut folded_printer = PrintOperatorFolded::new(self, state, &mut operator_state);
1475            folded_printer.set_offset(func_start);
1476            folded_printer.begin_function(func_idx)?;
1477            Self::print_operators(
1478                &mut reader,
1479                branch_hints,
1480                func_start,
1481                &mut folded_printer,
1482                validator,
1483            )?;
1484        } else {
1485            let mut flat_printer = PrintOperator::new(self, state, &mut operator_state);
1486            Self::print_operators(
1487                &mut reader,
1488                branch_hints,
1489                func_start,
1490                &mut flat_printer,
1491                validator,
1492            )?;
1493        }
1494
1495        // If this was an invalid function body then the nesting may not
1496        // have reset back to normal. Fix that up here and forcibly insert
1497        // a newline as well in case the last instruction was something
1498        // like an `if` which has a comment after it which could interfere
1499        // with the closing paren printed for the func.
1500        if self.nesting != nesting_start {
1501            self.nesting = nesting_start;
1502            self.newline(reader.original_position())?;
1503        }
1504
1505        Ok(())
1506    }
1507
1508    fn print_operators<'a, O: OpPrinter>(
1509        body: &mut BinaryReader<'a>,
1510        mut branch_hints: &[(usize, BranchHint)],
1511        func_start: usize,
1512        op_printer: &mut O,
1513        mut validator: Option<operand_stack::FuncValidator>,
1514    ) -> Result<()> {
1515        let mut ops = OperatorsReader::new(body.clone());
1516        while !ops.eof() {
1517            if ops.is_end_then_eof() {
1518                let mut annotation = None;
1519                if let Some(f) = &mut validator {
1520                    match f.visit_operator(&ops, true) {
1521                        Ok(()) => {}
1522                        Err(_) => {
1523                            annotation = Some(String::from("type mismatch at end of expression"))
1524                        }
1525                    }
1526                }
1527
1528                ops.read()?; // final "end" opcode terminates instruction sequence
1529                ops.finish()?;
1530                op_printer.finalize(annotation.as_deref())?;
1531                return Ok(());
1532            }
1533
1534            // Branch hints are stored in increasing order of their body offset
1535            // so print them whenever their instruction comes up.
1536            if let Some(((hint_offset, hint), rest)) = branch_hints.split_first() {
1537                if hint.func_offset == (ops.original_position() - func_start) as u32 {
1538                    branch_hints = rest;
1539                    op_printer.branch_hint(*hint_offset, hint.taken)?;
1540                }
1541            }
1542            let mut annotation = None;
1543            if let Some(f) = &mut validator {
1544                let result = f
1545                    .visit_operator(&ops, false)
1546                    .map_err(anyhow::Error::from)
1547                    .and_then(|()| f.visualize_operand_stack(op_printer.use_color()));
1548                match result {
1549                    Ok(s) => annotation = Some(s),
1550                    Err(_) => {
1551                        validator = None;
1552                        annotation = Some(String::from("(invalid)"));
1553                    }
1554                }
1555            }
1556            op_printer.set_offset(ops.original_position());
1557            op_printer.visit_operator(&mut ops, annotation.as_deref())?;
1558        }
1559        ops.finish()?; // for the error message
1560        bail!("unexpected end of operators");
1561    }
1562
1563    fn newline(&mut self, offset: usize) -> Result<()> {
1564        self.print_newline(Some(offset))
1565    }
1566
1567    fn newline_unknown_pos(&mut self) -> Result<()> {
1568        self.print_newline(None)
1569    }
1570
1571    fn print_newline(&mut self, offset: Option<usize>) -> Result<()> {
1572        self.result.newline()?;
1573        self.result.start_line(offset);
1574
1575        if self.config.print_offsets {
1576            match offset {
1577                Some(offset) => {
1578                    self.result.start_comment()?;
1579                    write!(self.result, "(;@{offset:<6x};)")?;
1580                    self.result.reset_color()?;
1581                }
1582                None => self.result.write_str("           ")?,
1583            }
1584        }
1585        self.line += 1;
1586
1587        // Clamp the maximum nesting size that we print at something somewhat
1588        // reasonable to avoid generating hundreds of megabytes of whitespace
1589        // for small-ish modules that have deep-ish nesting.
1590        for _ in 0..self.nesting.min(MAX_NESTING_TO_PRINT) {
1591            self.result.write_str(&self.config.indent_text)?;
1592        }
1593        Ok(())
1594    }
1595
1596    fn print_exports(&mut self, state: &State, data: ExportSectionReader) -> Result<()> {
1597        for export in data.into_iter_with_offsets() {
1598            let (offset, export) = export?;
1599            self.newline(offset)?;
1600            self.print_export(state, &export)?;
1601        }
1602        Ok(())
1603    }
1604
1605    fn print_export(&mut self, state: &State, export: &Export) -> Result<()> {
1606        self.start_group("export ")?;
1607        self.print_str(export.name)?;
1608        self.result.write_str(" ")?;
1609        self.print_external_kind(state, export.kind, export.index)?;
1610        self.end_group()?; // export
1611        Ok(())
1612    }
1613
1614    fn print_external_kind(&mut self, state: &State, kind: ExternalKind, index: u32) -> Result<()> {
1615        match kind {
1616            ExternalKind::Func => {
1617                self.start_group("func ")?;
1618                self.print_idx(&state.core.func_names, index)?;
1619            }
1620            ExternalKind::Table => {
1621                self.start_group("table ")?;
1622                self.print_idx(&state.core.table_names, index)?;
1623            }
1624            ExternalKind::Global => {
1625                self.start_group("global ")?;
1626                self.print_idx(&state.core.global_names, index)?;
1627            }
1628            ExternalKind::Memory => {
1629                self.start_group("memory ")?;
1630                self.print_idx(&state.core.memory_names, index)?;
1631            }
1632            ExternalKind::Tag => {
1633                self.start_group("tag ")?;
1634                write!(self.result, "{index}")?;
1635            }
1636        }
1637        self.end_group()?;
1638        Ok(())
1639    }
1640
1641    fn print_core_type_ref(&mut self, state: &State, idx: u32) -> Result<()> {
1642        self.start_group("type ")?;
1643        self.print_idx(&state.core.type_names, idx)?;
1644        self.end_group()?;
1645        Ok(())
1646    }
1647
1648    // Note: in the text format, modules can use identifiers that are defined anywhere, but
1649    // components can only use previously-defined identifiers. In the binary format,
1650    // invalid components can make forward references to an index that appears in the name section;
1651    // these can be printed but the output won't parse.
1652    fn print_idx<K>(&mut self, names: &NamingMap<u32, K>, idx: u32) -> Result<()>
1653    where
1654        K: NamingNamespace,
1655    {
1656        self._print_idx(&names.index_to_name, idx, K::desc())
1657    }
1658
1659    fn _print_idx(&mut self, names: &HashMap<u32, Naming>, idx: u32, desc: &str) -> Result<()> {
1660        self.result.start_name()?;
1661        match names.get(&idx) {
1662            Some(name) => name.write_identifier(self)?,
1663            None if self.config.name_unnamed => write!(self.result, "$#{desc}{idx}")?,
1664            None => write!(self.result, "{idx}")?,
1665        }
1666        self.result.reset_color()?;
1667        Ok(())
1668    }
1669
1670    fn print_local_idx(&mut self, state: &State, func: u32, idx: u32) -> Result<()> {
1671        self.result.start_name()?;
1672        match state.core.local_names.index_to_name.get(&(func, idx)) {
1673            Some(name) => name.write_identifier(self)?,
1674            None if self.config.name_unnamed => write!(self.result, "$#local{idx}")?,
1675            None => write!(self.result, "{idx}")?,
1676        }
1677        self.result.reset_color()?;
1678        Ok(())
1679    }
1680
1681    fn print_field_idx(&mut self, state: &State, ty: u32, idx: u32) -> Result<()> {
1682        self.result.start_name()?;
1683        match state.core.field_names.index_to_name.get(&(ty, idx)) {
1684            Some(name) => name.write_identifier(self)?,
1685            None if self.config.name_unnamed => write!(self.result, "$#field{idx}")?,
1686            None => write!(self.result, "{idx}")?,
1687        }
1688        self.result.reset_color()?;
1689        Ok(())
1690    }
1691
1692    fn print_name<K>(&mut self, names: &NamingMap<u32, K>, cur_idx: u32) -> Result<()>
1693    where
1694        K: NamingNamespace,
1695    {
1696        self._print_name(&names.index_to_name, cur_idx, K::desc())
1697    }
1698
1699    fn _print_name(
1700        &mut self,
1701        names: &HashMap<u32, Naming>,
1702        cur_idx: u32,
1703        desc: &str,
1704    ) -> Result<()> {
1705        self.result.start_name()?;
1706        match names.get(&cur_idx) {
1707            Some(name) => {
1708                name.write(self)?;
1709                self.result.write_str(" ")?;
1710            }
1711            None if self.config.name_unnamed => {
1712                write!(self.result, "$#{desc}{cur_idx} ")?;
1713            }
1714            None => {}
1715        }
1716        write!(self.result, "(;{cur_idx};)")?;
1717        self.result.reset_color()?;
1718        Ok(())
1719    }
1720
1721    fn print_elems(&mut self, state: &mut State, data: ElementSectionReader) -> Result<()> {
1722        for (i, elem) in data.into_iter_with_offsets().enumerate() {
1723            let (offset, mut elem) = elem?;
1724            self.newline(offset)?;
1725            self.start_group("elem ")?;
1726            self.print_name(&state.core.element_names, i as u32)?;
1727            match &mut elem.kind {
1728                ElementKind::Passive => {}
1729                ElementKind::Declared => self.result.write_str(" declare")?,
1730                ElementKind::Active {
1731                    table_index,
1732                    offset_expr,
1733                } => {
1734                    if let Some(table_index) = *table_index {
1735                        self.result.write_str(" ")?;
1736                        self.start_group("table ")?;
1737                        self.print_idx(&state.core.table_names, table_index)?;
1738                        self.end_group()?;
1739                    }
1740                    self.result.write_str(" ")?;
1741                    self.print_const_expr_sugar(state, offset_expr, "offset")?;
1742                }
1743            }
1744            self.result.write_str(" ")?;
1745
1746            if self.config.print_skeleton {
1747                self.result.write_str("...")?;
1748            } else {
1749                match elem.items {
1750                    ElementItems::Functions(reader) => {
1751                        self.result.write_str("func")?;
1752                        for idx in reader {
1753                            self.result.write_str(" ")?;
1754                            self.print_idx(&state.core.func_names, idx?)?
1755                        }
1756                    }
1757                    ElementItems::Expressions(ty, reader) => {
1758                        self.print_reftype(state, ty)?;
1759                        for expr in reader {
1760                            self.result.write_str(" ")?;
1761                            self.print_const_expr_sugar(state, &expr?, "item")?
1762                        }
1763                    }
1764                }
1765            }
1766            self.end_group()?;
1767        }
1768        Ok(())
1769    }
1770
1771    fn print_data(&mut self, state: &mut State, data: DataSectionReader) -> Result<()> {
1772        for (i, data) in data.into_iter_with_offsets().enumerate() {
1773            let (offset, data) = data?;
1774            self.newline(offset)?;
1775            self.start_group("data ")?;
1776            self.print_name(&state.core.data_names, i as u32)?;
1777            self.result.write_str(" ")?;
1778            match &data.kind {
1779                DataKind::Passive => {}
1780                DataKind::Active {
1781                    memory_index,
1782                    offset_expr,
1783                } => {
1784                    if *memory_index != 0 {
1785                        self.start_group("memory ")?;
1786                        self.print_idx(&state.core.memory_names, *memory_index)?;
1787                        self.end_group()?;
1788                        self.result.write_str(" ")?;
1789                    }
1790                    self.print_const_expr_sugar(state, offset_expr, "offset")?;
1791                    self.result.write_str(" ")?;
1792                }
1793            }
1794            if self.config.print_skeleton {
1795                self.result.write_str("...")?;
1796            } else {
1797                self.print_bytes(data.data)?;
1798            }
1799            self.end_group()?;
1800        }
1801        Ok(())
1802    }
1803
1804    /// Prints the operators of `expr` space-separated, taking into account that
1805    /// if there's only one operator in `expr` then instead of `(explicit ...)`
1806    /// the printing can be `(...)`.
1807    fn print_const_expr_sugar(
1808        &mut self,
1809        state: &mut State,
1810        expr: &ConstExpr,
1811        explicit: &str,
1812    ) -> Result<()> {
1813        self.start_group("")?;
1814        let mut reader = expr.get_operators_reader();
1815
1816        if reader.read().is_ok() && !reader.is_end_then_eof() {
1817            write!(self.result, "{explicit} ")?;
1818            self.print_const_expr(state, expr, self.config.fold_instructions)?;
1819        } else {
1820            self.print_const_expr(state, expr, false)?;
1821        }
1822
1823        self.end_group()?;
1824        Ok(())
1825    }
1826
1827    /// Prints the operators of `expr` space-separated.
1828    fn print_const_expr(&mut self, state: &mut State, expr: &ConstExpr, fold: bool) -> Result<()> {
1829        let mut reader = expr.get_binary_reader();
1830        let mut operator_state = OperatorState::new(self, OperatorSeparator::NoneThenSpace);
1831
1832        if fold {
1833            let mut folded_printer = PrintOperatorFolded::new(self, state, &mut operator_state);
1834            folded_printer.begin_const_expr();
1835            Self::print_operators(&mut reader, &[], 0, &mut folded_printer, None)?;
1836        } else {
1837            let mut op_printer = PrintOperator::new(self, state, &mut operator_state);
1838            Self::print_operators(&mut reader, &[], 0, &mut op_printer, None)?;
1839        }
1840
1841        Ok(())
1842    }
1843
1844    fn print_str(&mut self, name: &str) -> Result<()> {
1845        self.result.start_literal()?;
1846        self.result.write_str("\"")?;
1847        self.print_str_contents(name)?;
1848        self.result.write_str("\"")?;
1849        self.result.reset_color()?;
1850        Ok(())
1851    }
1852
1853    fn print_str_contents(&mut self, name: &str) -> Result<()> {
1854        for c in name.chars() {
1855            let v = c as u32;
1856            if (0x20..0x7f).contains(&v) && c != '"' && c != '\\' && v < 0xff {
1857                write!(self.result, "{c}")?;
1858            } else {
1859                write!(self.result, "\\u{{{v:x}}}",)?;
1860            }
1861        }
1862        Ok(())
1863    }
1864
1865    fn print_bytes(&mut self, bytes: &[u8]) -> Result<()> {
1866        self.result.start_literal()?;
1867        self.result.write_str("\"")?;
1868        for byte in bytes {
1869            if *byte >= 0x20 && *byte < 0x7f && *byte != b'"' && *byte != b'\\' {
1870                write!(self.result, "{}", *byte as char)?;
1871            } else {
1872                self.hex_byte(*byte)?;
1873            }
1874        }
1875        self.result.write_str("\"")?;
1876        self.result.reset_color()?;
1877        Ok(())
1878    }
1879
1880    fn hex_byte(&mut self, byte: u8) -> Result<()> {
1881        write!(self.result, "\\{byte:02x}")?;
1882        Ok(())
1883    }
1884
1885    fn print_known_custom_section(&mut self, section: CustomSectionReader<'_>) -> Result<bool> {
1886        match section.as_known() {
1887            // For now `wasmprinter` has invented syntax for `producers` and
1888            // `dylink.0` below to use in tests. Note that this syntax is not
1889            // official at this time.
1890            KnownCustom::Producers(s) => {
1891                self.newline(section.range().start)?;
1892                self.print_producers_section(s)?;
1893                Ok(true)
1894            }
1895            KnownCustom::Dylink0(s) => {
1896                self.newline(section.range().start)?;
1897                self.print_dylink0_section(s)?;
1898                Ok(true)
1899            }
1900
1901            // These are parsed during `read_names` and are part of
1902            // printing elsewhere, so don't print them.
1903            KnownCustom::Name(_) | KnownCustom::BranchHints(_) => Ok(true),
1904            #[cfg(feature = "component-model")]
1905            KnownCustom::ComponentName(_) => Ok(true),
1906
1907            _ => Ok(false),
1908        }
1909    }
1910
1911    fn print_raw_custom_section(
1912        &mut self,
1913        state: &State,
1914        section: CustomSectionReader<'_>,
1915    ) -> Result<()> {
1916        self.newline(section.range().start)?;
1917        self.start_group("@custom ")?;
1918        self.print_str(section.name())?;
1919        if let Some((place, _)) = state.custom_section_place {
1920            write!(self.result, " ({place})")?;
1921        }
1922        self.result.write_str(" ")?;
1923        if self.config.print_skeleton {
1924            self.result.write_str("...")?;
1925        } else {
1926            self.print_bytes(section.data())?;
1927        }
1928        self.end_group()?;
1929        Ok(())
1930    }
1931
1932    fn print_producers_section(&mut self, section: ProducersSectionReader<'_>) -> Result<()> {
1933        self.start_group("@producers")?;
1934        for field in section {
1935            let field = field?;
1936            for value in field.values.into_iter_with_offsets() {
1937                let (offset, value) = value?;
1938                self.newline(offset)?;
1939                self.start_group(field.name)?;
1940                self.result.write_str(" ")?;
1941                self.print_str(value.name)?;
1942                self.result.write_str(" ")?;
1943                self.print_str(value.version)?;
1944                self.end_group()?;
1945            }
1946        }
1947        self.end_group()?;
1948        Ok(())
1949    }
1950
1951    fn print_dylink0_section(&mut self, mut section: Dylink0SectionReader<'_>) -> Result<()> {
1952        self.start_group("@dylink.0")?;
1953        loop {
1954            let start = section.original_position();
1955            let next = match section.next() {
1956                Some(Ok(next)) => next,
1957                Some(Err(e)) => return Err(e.into()),
1958                None => break,
1959            };
1960            match next {
1961                Dylink0Subsection::MemInfo(info) => {
1962                    self.newline(start)?;
1963                    self.start_group("mem-info")?;
1964                    if info.memory_size > 0 || info.memory_alignment > 0 {
1965                        write!(
1966                            self.result,
1967                            " (memory {} {})",
1968                            info.memory_size, info.memory_alignment
1969                        )?;
1970                    }
1971                    if info.table_size > 0 || info.table_alignment > 0 {
1972                        write!(
1973                            self.result,
1974                            " (table {} {})",
1975                            info.table_size, info.table_alignment
1976                        )?;
1977                    }
1978                    self.end_group()?;
1979                }
1980                Dylink0Subsection::Needed(needed) => {
1981                    self.newline(start)?;
1982                    self.start_group("needed")?;
1983                    for s in needed {
1984                        self.result.write_str(" ")?;
1985                        self.print_str(s)?;
1986                    }
1987                    self.end_group()?;
1988                }
1989                Dylink0Subsection::ExportInfo(info) => {
1990                    for info in info {
1991                        self.newline(start)?;
1992                        self.start_group("export-info ")?;
1993                        self.print_str(info.name)?;
1994                        self.print_dylink0_flags(info.flags)?;
1995                        self.end_group()?;
1996                    }
1997                }
1998                Dylink0Subsection::ImportInfo(info) => {
1999                    for info in info {
2000                        self.newline(start)?;
2001                        self.start_group("import-info ")?;
2002                        self.print_str(info.module)?;
2003                        self.result.write_str(" ")?;
2004                        self.print_str(info.field)?;
2005                        self.print_dylink0_flags(info.flags)?;
2006                        self.end_group()?;
2007                    }
2008                }
2009                Dylink0Subsection::RuntimePath(runtime_path) => {
2010                    self.newline(start)?;
2011                    self.start_group("runtime-path")?;
2012                    for s in runtime_path {
2013                        self.result.write_str(" ")?;
2014                        self.print_str(s)?;
2015                    }
2016                    self.end_group()?;
2017                }
2018                Dylink0Subsection::Unknown { ty, .. } => {
2019                    bail!("don't know how to print dylink.0 subsection id {ty}");
2020                }
2021            }
2022        }
2023        self.end_group()?;
2024        Ok(())
2025    }
2026
2027    fn print_dylink0_flags(&mut self, mut flags: SymbolFlags) -> Result<()> {
2028        macro_rules! print_flag {
2029            ($($name:ident = $text:tt)*) => ({$(
2030                if flags.contains(SymbolFlags::$name) {
2031                    flags.remove(SymbolFlags::$name);
2032                    write!(self.result, concat!(" ", $text))?;
2033                }
2034            )*})
2035        }
2036        // N.B.: Keep in sync with `parse_sym_flags` in `crates/wast/src/core/custom.rs`.
2037        print_flag! {
2038            BINDING_WEAK = "binding-weak"
2039            BINDING_LOCAL = "binding-local"
2040            VISIBILITY_HIDDEN = "visibility-hidden"
2041            UNDEFINED = "undefined"
2042            EXPORTED = "exported"
2043            EXPLICIT_NAME = "explicit-name"
2044            NO_STRIP = "no-strip"
2045            TLS = "tls"
2046            ABSOLUTE = "absolute"
2047        }
2048        if !flags.is_empty() {
2049            write!(self.result, " {flags:#x}")?;
2050        }
2051        Ok(())
2052    }
2053
2054    fn register_branch_hint_section(&mut self, section: BranchHintSectionReader<'_>) -> Result<()> {
2055        self.code_section_hints.clear();
2056        for func in section {
2057            let func = func?;
2058            if self.code_section_hints.len() >= MAX_WASM_FUNCTIONS as usize {
2059                bail!("found too many hints");
2060            }
2061            if func.hints.count() >= MAX_WASM_FUNCTION_SIZE {
2062                bail!("found too many hints");
2063            }
2064            let hints = func
2065                .hints
2066                .into_iter_with_offsets()
2067                .collect::<wasmparser::Result<Vec<_>>>()?;
2068            self.code_section_hints.push((func.func, hints));
2069        }
2070        self.code_section_hints.reverse();
2071        Ok(())
2072    }
2073}
2074
2075struct NamedLocalPrinter {
2076    group_name: &'static str,
2077    in_group: bool,
2078    end_group_after_local: bool,
2079    first: bool,
2080}
2081
2082impl NamedLocalPrinter {
2083    fn new(group_name: &'static str) -> NamedLocalPrinter {
2084        NamedLocalPrinter {
2085            group_name,
2086            in_group: false,
2087            end_group_after_local: false,
2088            first: true,
2089        }
2090    }
2091
2092    fn start_local(
2093        &mut self,
2094        func: Option<u32>,
2095        local: u32,
2096        dst: &mut Printer,
2097        state: &State,
2098    ) -> Result<()> {
2099        let name = state
2100            .core
2101            .local_names
2102            .index_to_name
2103            .get(&(func.unwrap_or(u32::MAX), local));
2104
2105        // Named locals must be in their own group, so if we have a name we need
2106        // to terminate the previous group.
2107        if name.is_some() && self.in_group {
2108            dst.end_group()?;
2109            self.in_group = false;
2110        }
2111
2112        if self.first {
2113            self.first = false;
2114        } else {
2115            dst.result.write_str(" ")?;
2116        }
2117
2118        // Next we either need a separator if we're already in a group or we
2119        // need to open a group for our new local.
2120        if !self.in_group {
2121            dst.start_group(self.group_name)?;
2122            dst.result.write_str(" ")?;
2123            self.in_group = true;
2124        }
2125
2126        // Print the optional name if given...
2127        match name {
2128            Some(name) => {
2129                name.write(dst)?;
2130                dst.result.write_str(" ")?;
2131                self.end_group_after_local = true;
2132            }
2133            None if dst.config.name_unnamed && func.is_some() => {
2134                write!(dst.result, "$#local{local} ")?;
2135                self.end_group_after_local = true;
2136            }
2137            None => {
2138                self.end_group_after_local = false;
2139            }
2140        }
2141        Ok(())
2142    }
2143
2144    fn end_local(&mut self, dst: &mut Printer) -> Result<()> {
2145        if self.end_group_after_local {
2146            dst.end_group()?;
2147            self.end_group_after_local = false;
2148            self.in_group = false;
2149        }
2150        Ok(())
2151    }
2152    fn finish(self, dst: &mut Printer) -> Result<()> {
2153        if self.in_group {
2154            dst.end_group()?;
2155        }
2156        Ok(())
2157    }
2158}
2159
2160macro_rules! print_float {
2161    ($name:ident $float:ident $uint:ident $sint:ident $exp_bits:tt) => {
2162        fn $name(&mut self, mut bits: $uint) -> Result<()> {
2163            // Calculate a few constants
2164            let int_width = mem::size_of::<$uint>() * 8;
2165            let exp_width = $exp_bits;
2166            let mantissa_width = int_width - 1 - exp_width;
2167            let bias = (1 << (exp_width - 1)) - 1;
2168            let max_exp = (1 as $sint) << (exp_width - 1);
2169            let min_exp = -max_exp + 1;
2170
2171            // Handle `NaN` and infinity specially
2172            let f = $float::from_bits(bits);
2173            if bits >> (int_width - 1) != 0 {
2174                bits ^= 1 << (int_width - 1);
2175                self.result.write_str("-")?;
2176            }
2177            if f.is_infinite() {
2178                self.result.start_literal()?;
2179                self.result.write_str("inf ")?;
2180                self.result.start_comment()?;
2181                write!(self.result, "(;={f};)")?;
2182                self.result.reset_color()?;
2183                return Ok(());
2184            }
2185            if f.is_nan() {
2186                let payload = bits & ((1 << mantissa_width) - 1);
2187                self.result.start_literal()?;
2188                if payload == 1 << (mantissa_width - 1) {
2189                    self.result.write_str("nan ")?;
2190                    self.result.start_comment()?;
2191                    write!(self.result, "(;={f};)")?;
2192                } else {
2193                    write!(self.result, "nan:{:#x} ", payload)?;
2194                    self.result.start_comment()?;
2195                    write!(self.result, "(;={f};)")?;
2196                }
2197                self.result.reset_color()?;
2198                return Ok(());
2199            }
2200
2201            // Figure out our exponent, but keep in mine that it's in an
2202            // integer width that may not be supported. As a result we do a few
2203            // tricks here:
2204            //
2205            // * Make the MSB the top bit of the exponent, then shift the
2206            //   exponent to the bottom. This means we now have a signed
2207            //   integer in `$sint` width representing the whole exponent.
2208            // * Do the arithmetic for the exponent (subtract)
2209            // * Next we only care about the lowest `$exp_bits` bits of the
2210            //   result, but we do care about the sign. Use sign-carrying of
2211            //   the signed integer shifts to shift it left then shift it back.
2212            //
2213            // Overall this should do basic arithmetic for `$exp_bits` bit
2214            // numbers and get the result back as a signed integer with `$sint`
2215            // bits in `exponent` representing the same decimal value.
2216            let mut exponent = (((bits << 1) as $sint) >> (mantissa_width + 1)).wrapping_sub(bias);
2217            exponent = (exponent << (int_width - exp_width)) >> (int_width - exp_width);
2218            let mut fraction = bits & ((1 << mantissa_width) - 1);
2219            self.result.start_literal()?;
2220            self.result.write_str("0x")?;
2221            if bits == 0 {
2222                self.result.write_str("0p+0")?;
2223            } else {
2224                self.result.write_str("1")?;
2225                if fraction > 0 {
2226                    fraction <<= (int_width - mantissa_width);
2227
2228                    // Apparently the subnormal is handled here. I don't know
2229                    // what a subnormal is. If someone else does, please let me
2230                    // know!
2231                    if exponent == min_exp {
2232                        let leading = fraction.leading_zeros();
2233                        if (leading as usize) < int_width - 1 {
2234                            fraction <<= leading + 1;
2235                        } else {
2236                            fraction = 0;
2237                        }
2238                        exponent -= leading as $sint;
2239                    }
2240
2241                    self.result.write_str(".")?;
2242                    while fraction > 0 {
2243                        write!(self.result, "{:x}", fraction >> (int_width - 4))?;
2244                        fraction <<= 4;
2245                    }
2246                }
2247                write!(self.result, "p{:+}", exponent)?;
2248            }
2249            self.result.start_comment()?;
2250            write!(self.result, " (;={};)", f)?;
2251            self.result.reset_color()?;
2252            Ok(())
2253        }
2254    };
2255}
2256
2257impl Printer<'_, '_> {
2258    print_float!(print_f32 f32 u32 i32 8);
2259    print_float!(print_f64 f64 u64 i64 11);
2260}
2261
2262impl Naming {
2263    fn new<'a>(
2264        name: &'a str,
2265        index: u32,
2266        group: &str,
2267        used: Option<&mut HashSet<&'a str>>,
2268    ) -> Naming {
2269        let mut kind = NamingKind::DollarName;
2270        if name.chars().any(|c| !is_idchar(c)) {
2271            kind = NamingKind::DollarQuotedName;
2272        }
2273
2274        // If the `name` provided can't be used as the raw identifier for the
2275        // item that it's describing then a synthetic name must be made. The
2276        // rules here which generate a name are:
2277        //
2278        // * Empty identifiers are not allowed
2279        // * Identifiers have a fixed set of valid characters
2280        // * For wasmprinter's purposes we "reserve" identifiers with the `#`
2281        //   prefix, which is in theory rare to encounter in practice.
2282        // * If the name has already been used for some other item and cannot
2283        //   be reused (e.g. because shadowing in this context is not possible).
2284        //
2285        // If any of these conditions match then we generate a unique identifier
2286        // based on `name` but not it exactly. By factoring in the `group`,
2287        // `index`, and `name` we get a guaranteed unique identifier (due to the
2288        // leading `#` prefix that we reserve and factoring in of the item
2289        // index) while preserving human readability at least somewhat (the
2290        // valid identifier characters of `name` still appear in the returned
2291        // name).
2292        if name.is_empty()
2293            || name.starts_with('#')
2294            || used.map(|set| !set.insert(name)).unwrap_or(false)
2295        {
2296            kind = NamingKind::SyntheticPrefix(format!("#{group}{index}"));
2297        }
2298        return Naming {
2299            kind,
2300            name: name.to_string(),
2301        };
2302
2303        // See https://webassembly.github.io/spec/core/text/values.html#text-id
2304        fn is_idchar(c: char) -> bool {
2305            matches!(
2306                c,
2307                '0'..='9'
2308                | 'a'..='z'
2309                | 'A'..='Z'
2310                | '!'
2311                | '#'
2312                | '$'
2313                | '%'
2314                | '&'
2315                | '\''
2316                | '*'
2317                | '+'
2318                | '-'
2319                | '.'
2320                | '/'
2321                | ':'
2322                | '<'
2323                | '='
2324                | '>'
2325                | '?'
2326                | '@'
2327                | '\\'
2328                | '^'
2329                | '_'
2330                | '`'
2331                | '|'
2332                | '~'
2333            )
2334        }
2335    }
2336
2337    fn write_identifier(&self, printer: &mut Printer<'_, '_>) -> Result<()> {
2338        match &self.kind {
2339            NamingKind::DollarName => {
2340                printer.result.write_str("$")?;
2341                printer.result.write_str(&self.name)?;
2342            }
2343            NamingKind::DollarQuotedName => {
2344                printer.result.write_str("$\"")?;
2345                printer.print_str_contents(&self.name)?;
2346                printer.result.write_str("\"")?;
2347            }
2348            NamingKind::SyntheticPrefix(prefix) => {
2349                printer.result.write_str("$\"")?;
2350                printer.result.write_str(&prefix)?;
2351                printer.result.write_str(" ")?;
2352                printer.print_str_contents(&self.name)?;
2353                printer.result.write_str("\"")?;
2354            }
2355        }
2356        Ok(())
2357    }
2358
2359    fn write(&self, dst: &mut Printer<'_, '_>) -> Result<()> {
2360        self.write_identifier(dst)?;
2361        match &self.kind {
2362            NamingKind::DollarName | NamingKind::DollarQuotedName => {}
2363
2364            NamingKind::SyntheticPrefix(_) => {
2365                dst.result.write_str(" ")?;
2366                dst.start_group("@name \"")?;
2367                dst.print_str_contents(&self.name)?;
2368                dst.result.write_str("\"")?;
2369                dst.end_group()?;
2370            }
2371        }
2372        Ok(())
2373    }
2374}
2375
2376/// Helper trait for the `NamingMap` type's `K` type parameter.
2377trait NamingNamespace {
2378    fn desc() -> &'static str;
2379}
2380
2381macro_rules! naming_namespaces {
2382    ($(struct $name:ident => $desc:tt)*) => ($(
2383        struct $name;
2384
2385        impl NamingNamespace for $name {
2386            fn desc() -> &'static str { $desc }
2387        }
2388    )*)
2389}
2390
2391naming_namespaces! {
2392    struct NameFunc => "func"
2393    struct NameGlobal => "global"
2394    struct NameMemory => "memory"
2395    struct NameLocal => "local"
2396    struct NameLabel => "label"
2397    struct NameTable => "table"
2398    struct NameType => "type"
2399    struct NameField => "field"
2400    struct NameData => "data"
2401    struct NameElem => "elem"
2402    struct NameTag => "tag"
2403}
2404
2405#[cfg(feature = "component-model")]
2406naming_namespaces! {
2407    struct NameModule => "module"
2408    struct NameInstance => "instance"
2409    struct NameValue => "value"
2410    struct NameComponent => "component"
2411}
2412
2413fn name_map<K>(into: &mut NamingMap<u32, K>, names: NameMap<'_>, name: &str) -> Result<()> {
2414    let mut used = HashSet::new();
2415    for naming in names {
2416        let naming = naming?;
2417        into.index_to_name.insert(
2418            naming.index,
2419            Naming::new(naming.name, naming.index, name, Some(&mut used)),
2420        );
2421    }
2422    Ok(())
2423}