Skip to main content

wasmprinter/
lib.rs

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