wasmparser_dump/
lib.rs

1use anyhow::Result;
2use std::fmt::Write as _;
3use std::io::Write;
4use wasmparser::*;
5
6pub fn dump_wasm(bytes: &[u8]) -> Result<String> {
7    let mut dst = vec![];
8    {
9        let mut d = Dump::new(bytes, &mut dst);
10        d.run()?;
11    }
12    Ok(String::from_utf8(dst).unwrap())
13}
14
15pub fn dump_wasm_into(bytes: &[u8], into: impl Write) -> Result<()> {
16    let mut d = Dump::new(bytes, into);
17    d.run()?;
18    Ok(())
19}
20
21struct Dump<'a> {
22    bytes: &'a [u8],
23    cur: usize,
24    state: String,
25    dst: Box<dyn Write + 'a>,
26    nesting: u32,
27    offset_width: usize,
28}
29
30#[derive(Default)]
31struct Indices {
32    // Core module indexes
33    core_types: u32,
34    core_funcs: u32,
35    core_globals: u32,
36    core_tables: u32,
37    core_memories: u32,
38    core_tags: u32,
39    core_modules: u32,
40    core_instances: u32,
41
42    // Component indexes
43    types: u32,
44    funcs: u32,
45    components: u32,
46    instances: u32,
47    values: u32,
48}
49
50enum ComponentTypeKind {
51    Func,
52    Component,
53    Instance,
54    DefinedType,
55}
56
57const NBYTES: usize = 4;
58
59impl<'a> Dump<'a> {
60    fn new(bytes: &'a [u8], dst: impl Write + 'a) -> Dump<'a> {
61        Dump {
62            bytes,
63            cur: 0,
64            nesting: 0,
65            state: String::new(),
66            dst: Box::new(dst) as _,
67            offset_width: format!("{:x}", bytes.len()).len() + 1,
68        }
69    }
70
71    fn run(&mut self) -> Result<()> {
72        self.print_module()?;
73        assert_eq!(self.cur, self.bytes.len());
74        Ok(())
75    }
76
77    fn print_module(&mut self) -> Result<()> {
78        let mut stack = Vec::new();
79        let mut i = Indices::default();
80        let mut component_types = Vec::new();
81        self.nesting += 1;
82
83        for item in Parser::new(0).parse_all(self.bytes) {
84            match item? {
85                Payload::Version {
86                    num,
87                    encoding,
88                    range,
89                } => {
90                    write!(self.state, "version {} ({:?})", num, encoding)?;
91                    self.print(range.end)?;
92                }
93                Payload::TypeSection(s) => self.section(s, "type", |me, end, t| {
94                    write!(me.state, "[type {}] {:?}", inc(&mut i.core_types), t)?;
95                    me.print(end)
96                })?,
97                Payload::ImportSection(s) => self.section(s, "import", |me, end, imp| {
98                    write!(me.state, "import ")?;
99                    match imp.ty {
100                        TypeRef::Func(_) => write!(me.state, "[func {}]", inc(&mut i.core_funcs))?,
101                        TypeRef::Memory(_) => {
102                            write!(me.state, "[memory {}]", inc(&mut i.core_memories))?
103                        }
104                        TypeRef::Tag(_) => write!(me.state, "[tag {}]", inc(&mut i.core_tags))?,
105                        TypeRef::Table(_) => {
106                            write!(me.state, "[table {}]", inc(&mut i.core_tables))?
107                        }
108                        TypeRef::Global(_) => {
109                            write!(me.state, "[global {}]", inc(&mut i.core_globals))?
110                        }
111                    }
112                    write!(me.state, " {:?}", imp)?;
113                    me.print(end)
114                })?,
115                Payload::FunctionSection(s) => {
116                    let mut cnt = i.core_funcs;
117                    self.section(s, "func", |me, end, f| {
118                        write!(me.state, "[func {}] type {:?}", inc(&mut cnt), f)?;
119                        me.print(end)
120                    })?
121                }
122                Payload::TableSection(s) => self.section(s, "table", |me, end, t| {
123                    write!(me.state, "[table {}] {:?}", inc(&mut i.core_tables), t)?;
124                    me.print(end)
125                })?,
126                Payload::MemorySection(s) => self.section(s, "memory", |me, end, m| {
127                    write!(me.state, "[memory {}] {:?}", inc(&mut i.core_memories), m)?;
128                    me.print(end)
129                })?,
130                Payload::TagSection(s) => self.section(s, "tag", |me, end, m| {
131                    write!(me.state, "[tag {}] {:?}", inc(&mut i.core_tags), m)?;
132                    me.print(end)
133                })?,
134                Payload::ExportSection(s) => self.section(s, "export", |me, end, e| {
135                    write!(me.state, "export {:?}", e)?;
136                    me.print(end)
137                })?,
138                Payload::GlobalSection(s) => self.section(s, "global", |me, _end, g| {
139                    write!(me.state, "[global {}] {:?}", inc(&mut i.core_globals), g.ty)?;
140                    me.print(g.init_expr.get_binary_reader().original_position())?;
141                    me.print_ops(g.init_expr.get_operators_reader())
142                })?,
143                Payload::StartSection { func, range } => {
144                    write!(self.state, "start section")?;
145                    self.print(range.start)?;
146                    write!(self.state, "start function {}", func)?;
147                    self.print(range.end)?;
148                }
149                Payload::DataCountSection { count, range } => {
150                    write!(self.state, "data count section")?;
151                    self.print(range.start)?;
152                    write!(self.state, "data count {}", count)?;
153                    self.print(range.end)?;
154                }
155                Payload::ElementSection(s) => self.section(s, "element", |me, _end, i| {
156                    write!(me.state, "element {:?}", i.ty)?;
157                    let item_count = match &i.items {
158                        ElementItems::Functions(reader) => reader.count(),
159                        ElementItems::Expressions(reader) => reader.count(),
160                    };
161                    match i.kind {
162                        ElementKind::Passive => {
163                            write!(me.state, " passive, {} items", item_count)?;
164                        }
165                        ElementKind::Active {
166                            table_index,
167                            offset_expr,
168                        } => {
169                            write!(me.state, " table[{}]", table_index)?;
170                            me.print(offset_expr.get_binary_reader().original_position())?;
171                            me.print_ops(offset_expr.get_operators_reader())?;
172                            write!(me.state, "{} items", item_count)?;
173                        }
174                        ElementKind::Declared => {
175                            write!(me.state, " declared {} items", item_count)?;
176                        }
177                    }
178                    match i.items {
179                        ElementItems::Functions(reader) => {
180                            let mut iter = reader.into_iter();
181                            me.print(iter.original_position())?;
182                            while let Some(item) = iter.next() {
183                                write!(me.state, "item {:?}", item?)?;
184                                me.print(iter.original_position())?;
185                            }
186                        }
187                        ElementItems::Expressions(reader) => {
188                            let mut iter = reader.into_iter();
189                            me.print(iter.original_position())?;
190                            while let Some(item) = iter.next() {
191                                write!(me.state, "item {:?}", item?)?;
192                                me.print(iter.original_position())?;
193                            }
194                        }
195                    }
196                    Ok(())
197                })?,
198
199                Payload::DataSection(s) => self.section(s, "data", |me, end, i| {
200                    match i.kind {
201                        DataKind::Passive => {
202                            write!(me.state, "data passive")?;
203                            me.print(end - i.data.len())?;
204                        }
205                        DataKind::Active {
206                            memory_index,
207                            offset_expr,
208                        } => {
209                            write!(me.state, "data memory[{}]", memory_index)?;
210                            me.print(offset_expr.get_binary_reader().original_position())?;
211                            me.print_ops(offset_expr.get_operators_reader())?;
212                        }
213                    }
214                    me.print_byte_header()?;
215                    for _ in 0..NBYTES {
216                        write!(me.dst, "---")?;
217                    }
218                    writeln!(me.dst, "-| ... {} bytes of data", i.data.len())?;
219                    me.cur = end;
220                    Ok(())
221                })?,
222
223                Payload::CodeSectionStart { count, range, size } => {
224                    write!(self.state, "code section")?;
225                    self.print(range.start)?;
226                    write!(self.state, "{} count", count)?;
227                    self.print(range.end - size as usize)?;
228                }
229
230                Payload::CodeSectionEntry(body) => {
231                    writeln!(
232                        self.dst,
233                        "============== func {} ====================",
234                        inc(&mut i.core_funcs),
235                    )?;
236                    write!(self.state, "size of function")?;
237                    self.print(body.get_binary_reader().original_position())?;
238                    let mut locals = body.get_locals_reader()?;
239                    write!(self.state, "{} local blocks", locals.get_count())?;
240                    self.print(locals.original_position())?;
241                    for _ in 0..locals.get_count() {
242                        let (amt, ty) = locals.read()?;
243                        write!(self.state, "{} locals of type {:?}", amt, ty)?;
244                        self.print(locals.original_position())?;
245                    }
246                    self.print_ops(body.get_operators_reader()?)?;
247                }
248
249                // Component sections
250                Payload::ModuleSection { range, .. } => {
251                    write!(
252                        self.state,
253                        "[core module {}] inline size",
254                        inc(&mut i.core_modules)
255                    )?;
256                    self.print(range.start)?;
257                    self.nesting += 1;
258                    stack.push(i);
259                    i = Indices::default();
260                }
261
262                Payload::InstanceSection(s) => self.section(s, "core instance", |me, end, e| {
263                    write!(
264                        me.state,
265                        "[core instance {}] {:?}",
266                        inc(&mut i.core_instances),
267                        e
268                    )?;
269                    me.print(end)
270                })?,
271
272                Payload::CoreTypeSection(s) => self.section(s, "core type", |me, end, t| {
273                    write!(me.state, "[core type {}] {:?}", inc(&mut i.core_types), t)?;
274                    me.print(end)
275                })?,
276
277                Payload::ComponentSection { range, .. } => {
278                    write!(
279                        self.state,
280                        "[component {}] inline size",
281                        inc(&mut i.components)
282                    )?;
283                    self.print(range.start)?;
284                    self.nesting += 1;
285                    stack.push(i);
286                    i = Indices::default();
287                }
288
289                Payload::ComponentInstanceSection(s) => {
290                    self.section(s, "component instance", |me, end, e| {
291                        write!(me.state, "[instance {}] {:?}", inc(&mut i.instances), e)?;
292                        me.print(end)
293                    })?
294                }
295
296                Payload::ComponentAliasSection(s) => {
297                    self.section(s, "component alias", |me, end, a| {
298                        let (kind, num) = match a {
299                            ComponentAlias::InstanceExport {
300                                kind: ComponentExternalKind::Module,
301                                ..
302                            }
303                            | ComponentAlias::Outer {
304                                kind: ComponentOuterAliasKind::CoreModule,
305                                ..
306                            } => ("module", inc(&mut i.core_modules)),
307                            ComponentAlias::Outer {
308                                kind: ComponentOuterAliasKind::CoreType,
309                                ..
310                            } => ("core type", inc(&mut i.core_types)),
311                            ComponentAlias::InstanceExport {
312                                kind: ComponentExternalKind::Func,
313                                ..
314                            } => ("func", inc(&mut i.funcs)),
315                            ComponentAlias::InstanceExport {
316                                kind: ComponentExternalKind::Value,
317                                ..
318                            } => ("value", inc(&mut i.values)),
319                            ComponentAlias::InstanceExport {
320                                kind: ComponentExternalKind::Type,
321                                ..
322                            }
323                            | ComponentAlias::Outer {
324                                kind: ComponentOuterAliasKind::Type,
325                                ..
326                            } => ("type", inc(&mut i.types)),
327                            ComponentAlias::InstanceExport {
328                                kind: ComponentExternalKind::Instance,
329                                ..
330                            } => ("instance", inc(&mut i.instances)),
331                            ComponentAlias::InstanceExport {
332                                kind: ComponentExternalKind::Component,
333                                ..
334                            }
335                            | ComponentAlias::Outer {
336                                kind: ComponentOuterAliasKind::Component,
337                                ..
338                            } => ("component", inc(&mut i.components)),
339                            ComponentAlias::CoreInstanceExport { kind, .. } => match kind {
340                                ExternalKind::Func => ("core func", inc(&mut i.core_funcs)),
341                                ExternalKind::Table => ("core table", inc(&mut i.core_tables)),
342                                ExternalKind::Memory => ("core memory", inc(&mut i.core_memories)),
343                                ExternalKind::Global => ("core global", inc(&mut i.core_globals)),
344                                ExternalKind::Tag => ("core tag", inc(&mut i.core_tags)),
345                            },
346                        };
347
348                        write!(me.state, "alias [{} {}] {:?}", kind, num, a)?;
349                        me.print(end)
350                    })?
351                }
352
353                Payload::ComponentTypeSection(s) => {
354                    self.section(s, "component type", |me, end, t| {
355                        write!(me.state, "[type {}] {:?}", inc(&mut i.types), t)?;
356                        component_types.push(match t {
357                            ComponentType::Defined(_) => ComponentTypeKind::DefinedType,
358                            ComponentType::Func(_) => ComponentTypeKind::Func,
359                            ComponentType::Component(_) => ComponentTypeKind::Component,
360                            ComponentType::Instance(_) => ComponentTypeKind::Instance,
361                        });
362                        me.print(end)
363                    })?
364                }
365
366                Payload::ComponentImportSection(s) => {
367                    self.section(s, "component import", |me, end, item| {
368                        let (desc, idx) = match item.ty {
369                            ComponentTypeRef::Module(..) => ("module", inc(&mut i.core_modules)),
370                            ComponentTypeRef::Func(..) => ("func", inc(&mut i.funcs)),
371                            ComponentTypeRef::Value(..) => ("value", inc(&mut i.values)),
372                            ComponentTypeRef::Type(..) => ("type", inc(&mut i.types)),
373                            ComponentTypeRef::Instance(..) => ("instance", inc(&mut i.instances)),
374                            ComponentTypeRef::Component(..) => {
375                                ("component", inc(&mut i.components))
376                            }
377                        };
378                        write!(me.state, "[{desc} {idx}] {item:?}")?;
379                        me.print(end)
380                    })?
381                }
382
383                Payload::ComponentCanonicalSection(s) => {
384                    self.section(s, "canonical function", |me, end, f| {
385                        let (name, col) = match &f {
386                            CanonicalFunction::Lift { .. } => ("func", &mut i.funcs),
387                            CanonicalFunction::Lower { .. } => ("core func", &mut i.core_funcs),
388                        };
389
390                        write!(me.state, "[{} {}] {:?}", name, inc(col), f)?;
391                        me.print(end)
392                    })?
393                }
394
395                Payload::ComponentExportSection(s) => {
396                    self.section(s, "component export", |me, end, e| {
397                        write!(me.state, "export {:?}", e)?;
398                        me.print(end)
399                    })?
400                }
401
402                Payload::ComponentStartSection { start, range } => {
403                    write!(self.state, "start section")?;
404                    self.print(range.start)?;
405                    write!(self.state, "{:?}", start)?;
406                    self.print(range.end)?;
407                }
408
409                Payload::CustomSection(c) => {
410                    write!(self.state, "custom section")?;
411                    self.print(c.range().start)?;
412                    write!(self.state, "name: {:?}", c.name())?;
413                    self.print(c.data_offset())?;
414                    if c.name() == "name" {
415                        let mut iter = NameSectionReader::new(c.data(), c.data_offset());
416                        while let Some(section) = iter.next() {
417                            self.print_custom_name_section(section?, iter.original_position())?;
418                        }
419                    } else if c.name() == "component-name" {
420                        let mut iter = ComponentNameSectionReader::new(c.data(), c.data_offset());
421                        while let Some(section) = iter.next() {
422                            self.print_custom_component_name_section(
423                                section?,
424                                iter.original_position(),
425                            )?;
426                        }
427                    } else {
428                        self.print_byte_header()?;
429                        for _ in 0..NBYTES {
430                            write!(self.dst, "---")?;
431                        }
432                        writeln!(self.dst, "-| ... {} bytes of data", c.data().len())?;
433                        self.cur += c.data().len();
434                    }
435                }
436                Payload::UnknownSection {
437                    id,
438                    range,
439                    contents,
440                } => {
441                    write!(self.state, "unknown section: {}", id)?;
442                    self.print(range.start)?;
443                    self.print_byte_header()?;
444                    for _ in 0..NBYTES {
445                        write!(self.dst, "---")?;
446                    }
447                    writeln!(self.dst, "-| ... {} bytes of data", contents.len())?;
448                    self.cur += contents.len();
449                }
450                Payload::End(_) => {
451                    self.nesting -= 1;
452                    if self.nesting > 0 {
453                        i = stack.pop().unwrap();
454                    }
455                }
456            }
457        }
458
459        Ok(())
460    }
461
462    fn print_name_map(&mut self, thing: &str, n: NameMap<'_>) -> Result<()> {
463        self.section(n, thing, |me, end, naming| {
464            write!(me.state, "{:?}", naming)?;
465            me.print(end)
466        })
467    }
468
469    fn print_indirect_name_map(
470        &mut self,
471        thing_a: &str,
472        thing_b: &str,
473        n: IndirectNameMap<'_>,
474    ) -> Result<()> {
475        self.section(n, thing_b, |me, _end, naming| {
476            write!(me.state, "{} {} ", thing_a, naming.index)?;
477            me.print_name_map(thing_b, naming.names)
478        })
479    }
480
481    fn print_custom_name_section(&mut self, name: Name<'_>, end: usize) -> Result<()> {
482        match name {
483            Name::Module { name, name_range } => {
484                write!(self.state, "module name")?;
485                self.print(name_range.start)?;
486                write!(self.state, "{:?}", name)?;
487                self.print(name_range.end)?;
488            }
489            Name::Function(n) => self.print_name_map("function", n)?,
490            Name::Local(n) => self.print_indirect_name_map("function", "local", n)?,
491            Name::Label(n) => self.print_indirect_name_map("function", "label", n)?,
492            Name::Type(n) => self.print_name_map("type", n)?,
493            Name::Table(n) => self.print_name_map("table", n)?,
494            Name::Memory(n) => self.print_name_map("memory", n)?,
495            Name::Global(n) => self.print_name_map("global", n)?,
496            Name::Element(n) => self.print_name_map("element", n)?,
497            Name::Data(n) => self.print_name_map("data", n)?,
498            Name::Unknown { ty, range, .. } => {
499                write!(self.state, "unknown names: {}", ty)?;
500                self.print(range.start)?;
501                self.print(end)?;
502            }
503        }
504        Ok(())
505    }
506
507    fn print_custom_component_name_section(
508        &mut self,
509        name: ComponentName<'_>,
510        end: usize,
511    ) -> Result<()> {
512        match name {
513            ComponentName::Component { name, name_range } => {
514                write!(self.state, "component name")?;
515                self.print(name_range.start)?;
516                write!(self.state, "{:?}", name)?;
517                self.print(name_range.end)?;
518            }
519            ComponentName::CoreFuncs(n) => self.print_name_map("core func", n)?,
520            ComponentName::CoreTables(n) => self.print_name_map("core table", n)?,
521            ComponentName::CoreGlobals(n) => self.print_name_map("core global", n)?,
522            ComponentName::CoreMemories(n) => self.print_name_map("core memory", n)?,
523            ComponentName::CoreInstances(n) => self.print_name_map("core instance", n)?,
524            ComponentName::CoreModules(n) => self.print_name_map("core module", n)?,
525            ComponentName::CoreTypes(n) => self.print_name_map("core type", n)?,
526            ComponentName::Types(n) => self.print_name_map("type", n)?,
527            ComponentName::Instances(n) => self.print_name_map("instance", n)?,
528            ComponentName::Components(n) => self.print_name_map("component", n)?,
529            ComponentName::Funcs(n) => self.print_name_map("func", n)?,
530            ComponentName::Values(n) => self.print_name_map("value", n)?,
531            ComponentName::Unknown { ty, range, .. } => {
532                write!(self.state, "unknown names: {}", ty)?;
533                self.print(range.start)?;
534                self.print(end)?;
535            }
536        }
537        Ok(())
538    }
539
540    fn section<'b, T>(
541        &mut self,
542        iter: SectionLimited<'b, T>,
543        name: &str,
544        print: impl FnMut(&mut Self, usize, T) -> Result<()>,
545    ) -> Result<()>
546    where
547        T: FromReader<'b>,
548    {
549        write!(self.state, "{} section", name)?;
550        self.print(iter.range().start)?;
551        self.print_iter(iter, print)
552    }
553
554    fn print_iter<'b, T>(
555        &mut self,
556        iter: SectionLimited<'b, T>,
557        mut print: impl FnMut(&mut Self, usize, T) -> Result<()>,
558    ) -> Result<()>
559    where
560        T: FromReader<'b>,
561    {
562        write!(self.state, "{} count", iter.count())?;
563        let mut iter = iter.into_iter();
564        self.print(iter.original_position())?;
565        while let Some(item) = iter.next() {
566            print(self, iter.original_position(), item?)?;
567        }
568        Ok(())
569    }
570
571    fn print_ops(&mut self, mut i: OperatorsReader) -> Result<()> {
572        while !i.eof() {
573            match i.visit_operator(self) {
574                Ok(()) => {}
575                Err(_) => write!(self.state, "??")?,
576            }
577            self.print(i.original_position())?;
578        }
579        Ok(())
580    }
581
582    fn print(&mut self, end: usize) -> Result<()> {
583        assert!(
584            self.cur < end,
585            "{:#x} >= {:#x}\ntrying to print: {}",
586            self.cur,
587            end,
588            self.state,
589        );
590        let bytes = &self.bytes[self.cur..end];
591        self.print_byte_header()?;
592        for (i, chunk) in bytes.chunks(NBYTES).enumerate() {
593            if i > 0 {
594                for _ in 0..self.nesting - 1 {
595                    write!(self.dst, "  ")?;
596                }
597                for _ in 0..self.offset_width {
598                    write!(self.dst, " ")?;
599                }
600                write!(self.dst, "   |")?;
601            }
602            for j in 0..NBYTES {
603                match chunk.get(j) {
604                    Some(b) => write!(self.dst, " {:02x}", b)?,
605                    None => write!(self.dst, "   ")?,
606                }
607            }
608            if i == 0 {
609                write!(self.dst, " | ")?;
610                write!(self.dst, "{}", &self.state)?;
611                self.state.truncate(0);
612            }
613            writeln!(self.dst)?;
614        }
615        self.cur = end;
616        Ok(())
617    }
618
619    fn print_byte_header(&mut self) -> Result<()> {
620        for _ in 0..self.nesting - 1 {
621            write!(self.dst, "  ")?;
622        }
623        write!(
624            self.dst,
625            "{:#width$x} |",
626            self.cur,
627            width = self.offset_width + 2
628        )?;
629        Ok(())
630    }
631}
632
633fn inc(spot: &mut u32) -> u32 {
634    let ret = *spot;
635    *spot += 1;
636    ret
637}
638
639macro_rules! define_visit_operator {
640    ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
641        $(
642            fn $visit(&mut self $($(,$arg: $argty)*)?) {
643                write!(
644                    self.state,
645                    concat!(
646                        "{}"
647                        $( $(, " ", stringify!($arg), ":{:?}")* )?
648                    ),
649                    stringify!($visit).strip_prefix("visit_").unwrap(),
650                    $( $($arg,)* )?
651                ).unwrap();
652            }
653        )*
654    }
655}
656
657impl<'a> VisitOperator<'a> for Dump<'_> {
658    type Output = ();
659
660    wasmparser::for_each_operator!(define_visit_operator);
661}