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