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