Skip to main content

waside/
decode.rs

1use crate::ast::const_expr::ConstExpr;
2use crate::ast::custom::CustomSection;
3use crate::ast::data::{Data, DataKind};
4use crate::ast::elements::{Element, ElementItems, ElementKind};
5use crate::ast::exports::{Export, ExternalKind};
6use crate::ast::functions::{Func, FuncBody, FuncBodyDef};
7use crate::ast::globals::Global;
8use crate::ast::imports::{Import, ImportType, TagKind, TagType};
9use crate::ast::instructions::Instruction;
10use crate::ast::memories::Memory;
11use crate::ast::module::Module;
12use crate::ast::tables::Table;
13use crate::ast::tags::Tag;
14use crate::ast::types::{
15    ArrayType, CompositeInnerType, CompositeType, ContType, FieldType, FuncType, RecGroup,
16    StorageType, StructType, SubType,
17};
18use crate::error::{Error, Result};
19use crate::Span;
20
21/// Options for decoding a wasm module.
22#[derive(Debug, Clone, Default)]
23pub struct DecodeOptions {
24    /// If true, function bodies are stored as lazy references instead of being parsed.
25    pub skeleton: bool,
26}
27
28impl Module {
29    /// Decode a wasm binary into a Module.
30    pub fn decode(bytes: &[u8]) -> Result<Module> {
31        Self::decode_with_options(bytes, &DecodeOptions::default())
32    }
33
34    /// Decode a wasm binary with the given options.
35    pub fn decode_with_options(bytes: &[u8], options: &DecodeOptions) -> Result<Module> {
36        // Validate the module first
37        wasmparser::Validator::new().validate_all(bytes)?;
38
39        let mut module = Module::new();
40
41        let parser = wasmparser::Parser::new(0);
42        let mut _code_section_range: Option<std::ops::Range<usize>> = None;
43        let mut custom_section_place: Option<&'static str> = Some("before first");
44
45        for payload in parser.parse_all(bytes) {
46            let payload = payload?;
47            match payload {
48                wasmparser::Payload::Version { .. } => {}
49                wasmparser::Payload::TypeSection(reader) => {
50                    let has_content = reader.count() > 0;
51                    decode_type_section(&mut module, reader)?;
52                    if has_content {
53                        custom_section_place = Some("after type");
54                    }
55                }
56                wasmparser::Payload::ImportSection(reader) => {
57                    let has_content = reader.count() > 0;
58                    decode_import_section(&mut module, reader)?;
59                    if has_content {
60                        custom_section_place = Some("after import");
61                    }
62                }
63                wasmparser::Payload::FunctionSection(reader) => {
64                    decode_function_section(&mut module, reader)?;
65                    // Function section never produces printed output
66                }
67                wasmparser::Payload::TableSection(reader) => {
68                    let has_content = reader.count() > 0;
69                    decode_table_section(&mut module, reader)?;
70                    if has_content {
71                        custom_section_place = Some("after table");
72                    }
73                }
74                wasmparser::Payload::MemorySection(reader) => {
75                    let has_content = reader.count() > 0;
76                    decode_memory_section(&mut module, reader)?;
77                    if has_content {
78                        custom_section_place = Some("after memory");
79                    }
80                }
81                wasmparser::Payload::TagSection(reader) => {
82                    let has_content = reader.count() > 0;
83                    decode_tag_section(&mut module, reader)?;
84                    if has_content {
85                        custom_section_place = Some("after tag");
86                    }
87                }
88                wasmparser::Payload::GlobalSection(reader) => {
89                    let has_content = reader.count() > 0;
90                    decode_global_section(&mut module, reader)?;
91                    if has_content {
92                        custom_section_place = Some("after global");
93                    }
94                }
95                wasmparser::Payload::ExportSection(reader) => {
96                    let has_content = reader.count() > 0;
97                    decode_export_section(&mut module, reader)?;
98                    if has_content {
99                        custom_section_place = Some("after export");
100                    }
101                }
102                wasmparser::Payload::StartSection { func, range: _ } => {
103                    module.start = Some(func);
104                    custom_section_place = Some("after start");
105                }
106                wasmparser::Payload::ElementSection(reader) => {
107                    let has_content = reader.count() > 0;
108                    decode_element_section(&mut module, reader)?;
109                    if has_content {
110                        custom_section_place = Some("after elem");
111                    }
112                }
113                wasmparser::Payload::DataCountSection { count, range: _ } => {
114                    module.data_count = Some(count);
115                    // DataCount doesn't produce visible output
116                }
117                wasmparser::Payload::DataSection(reader) => {
118                    let has_content = reader.count() > 0;
119                    decode_data_section(&mut module, reader)?;
120                    if has_content {
121                        custom_section_place = Some("after data");
122                    }
123                }
124                wasmparser::Payload::CodeSectionStart { count, range, .. } => {
125                    _code_section_range = Some(range);
126                    module.bodies.reserve(count as usize);
127                    if count > 0 {
128                        custom_section_place = Some("after code");
129                    }
130                }
131                wasmparser::Payload::CodeSectionEntry(body) => {
132                    decode_code_entry(&mut module, body, options)?;
133                }
134                wasmparser::Payload::CustomSection(reader) => {
135                    decode_custom_section(&mut module, reader, custom_section_place)?;
136                }
137                wasmparser::Payload::End(_) => {}
138                // Skip component model sections
139                _ => {}
140            }
141        }
142
143        Ok(module)
144    }
145
146    /// Decode a single function body, storing the result back into the module. For lazy
147    /// bodies, `bytes` must be the original module binary that was passed to `decode`.
148    /// Returns a reference to the stored definition.
149    pub fn decode_function(&mut self, idx: usize, bytes: &[u8]) -> Result<&FuncBodyDef> {
150        if let FuncBody::Lazy { offset, len } = self.bodies[idx] {
151            let body_bytes = &bytes[offset..offset + len];
152            let reader =
153                wasmparser::FunctionBody::new(wasmparser::BinaryReader::new(body_bytes, offset));
154            let def = decode_function_body(reader)?;
155            self.bodies[idx] = FuncBody::Decoded(def);
156        }
157        match &self.bodies[idx] {
158            FuncBody::Decoded(def) => Ok(def),
159            FuncBody::Lazy { .. } => unreachable!(),
160        }
161    }
162}
163
164fn decode_type_section(module: &mut Module, reader: wasmparser::TypeSectionReader) -> Result<()> {
165    for rec_group in reader {
166        let rec_group = rec_group?;
167        let is_explicit = rec_group.is_explicit_rec_group();
168        let mut types = Vec::new();
169        let mut first_offset = 0;
170        let mut last_end = 0;
171        for (offset, sub_type) in rec_group.into_types_and_offsets() {
172            if types.is_empty() {
173                first_offset = offset;
174            }
175            last_end = offset;
176            types.push(convert_sub_type(sub_type));
177        }
178        let span = Span::new(first_offset, last_end.saturating_sub(first_offset));
179        module.types.push(RecGroup {
180            span,
181            types,
182            is_explicit,
183        });
184    }
185    Ok(())
186}
187
188fn convert_sub_type(sub: wasmparser::SubType) -> SubType {
189    SubType {
190        is_final: sub.is_final,
191        supertype_idx: sub.supertype_idx.map(|idx| idx.as_module_index().unwrap()),
192        composite_type: convert_composite_type(sub.composite_type),
193    }
194}
195
196fn convert_composite_type(ct: wasmparser::CompositeType) -> CompositeType {
197    CompositeType {
198        shared: ct.shared,
199        inner: match ct.inner {
200            wasmparser::CompositeInnerType::Func(f) => CompositeInnerType::Func(FuncType {
201                params: f.params().to_vec(),
202                results: f.results().to_vec(),
203            }),
204            wasmparser::CompositeInnerType::Array(a) => CompositeInnerType::Array(ArrayType {
205                field_type: convert_field_type(a.0),
206            }),
207            wasmparser::CompositeInnerType::Struct(s) => CompositeInnerType::Struct(StructType {
208                fields: s.fields.iter().map(|f| convert_field_type(*f)).collect(),
209            }),
210            wasmparser::CompositeInnerType::Cont(c) => CompositeInnerType::Cont(ContType {
211                type_index: c.0.as_module_index().unwrap(),
212            }),
213        },
214    }
215}
216
217fn convert_field_type(ft: wasmparser::FieldType) -> FieldType {
218    FieldType {
219        mutable: ft.mutable,
220        element_type: match ft.element_type {
221            wasmparser::StorageType::I8 => StorageType::I8,
222            wasmparser::StorageType::I16 => StorageType::I16,
223            wasmparser::StorageType::Val(v) => StorageType::Val(v),
224        },
225    }
226}
227
228fn decode_import_section(
229    module: &mut Module,
230    reader: wasmparser::ImportSectionReader,
231) -> Result<()> {
232    let section_end = reader.range().end;
233    let mut iter = reader.into_imports_with_offsets().peekable();
234    while let Some(result) = iter.next() {
235        let (start, import) = result?;
236        let end = iter
237            .peek()
238            .and_then(|r| r.as_ref().ok().map(|(off, _)| *off))
239            .unwrap_or(section_end);
240        let ty = match import.ty {
241            wasmparser::TypeRef::Func(idx) | wasmparser::TypeRef::FuncExact(idx) => {
242                ImportType::Func(idx)
243            }
244            wasmparser::TypeRef::Table(t) => ImportType::Table(t),
245            wasmparser::TypeRef::Memory(m) => ImportType::Memory(m),
246            wasmparser::TypeRef::Global(g) => ImportType::Global(g),
247            wasmparser::TypeRef::Tag(t) => ImportType::Tag(TagType {
248                kind: TagKind::Exception,
249                func_type_idx: t.func_type_idx,
250            }),
251        };
252        module.imports.push(Import {
253            span: Span::new(start, end - start),
254            module: import.module.to_string(),
255            name: import.name.to_string(),
256            ty,
257        });
258    }
259    Ok(())
260}
261
262fn decode_function_section(
263    module: &mut Module,
264    reader: wasmparser::FunctionSectionReader,
265) -> Result<()> {
266    let section_end = reader.range().end;
267    let mut iter = reader.into_iter_with_offsets().peekable();
268    while let Some(result) = iter.next() {
269        let (start, type_index) = result?;
270        let end = iter
271            .peek()
272            .and_then(|r| r.as_ref().ok().map(|(off, _)| *off))
273            .unwrap_or(section_end);
274        module.functions.push(Func {
275            span: Span::new(start, end - start),
276            type_index,
277        });
278    }
279    Ok(())
280}
281
282fn decode_table_section(module: &mut Module, reader: wasmparser::TableSectionReader) -> Result<()> {
283    let section_end = reader.range().end;
284    let mut iter = reader.into_iter_with_offsets().peekable();
285    while let Some(result) = iter.next() {
286        let (start, table) = result?;
287        let end = iter
288            .peek()
289            .and_then(|r| r.as_ref().ok().map(|(off, _)| *off))
290            .unwrap_or(section_end);
291        let init = match &table.init {
292            wasmparser::TableInit::Expr(expr) => Some(decode_const_expr(expr.clone())?),
293            wasmparser::TableInit::RefNull => None,
294        };
295        module.tables.push(Table {
296            span: Span::new(start, end - start),
297            ty: table.ty,
298            init,
299        });
300    }
301    Ok(())
302}
303
304fn decode_memory_section(
305    module: &mut Module,
306    reader: wasmparser::MemorySectionReader,
307) -> Result<()> {
308    let section_end = reader.range().end;
309    let mut iter = reader.into_iter_with_offsets().peekable();
310    while let Some(result) = iter.next() {
311        let (start, memory) = result?;
312        let end = iter
313            .peek()
314            .and_then(|r| r.as_ref().ok().map(|(off, _)| *off))
315            .unwrap_or(section_end);
316        module.memories.push(Memory {
317            span: Span::new(start, end - start),
318            ty: memory,
319        });
320    }
321    Ok(())
322}
323
324fn decode_tag_section(module: &mut Module, reader: wasmparser::TagSectionReader) -> Result<()> {
325    let section_end = reader.range().end;
326    let mut iter = reader.into_iter_with_offsets().peekable();
327    while let Some(result) = iter.next() {
328        let (start, tag) = result?;
329        let end = iter
330            .peek()
331            .and_then(|r| r.as_ref().ok().map(|(off, _)| *off))
332            .unwrap_or(section_end);
333        module.tags.push(Tag {
334            span: Span::new(start, end - start),
335            ty: TagType {
336                kind: TagKind::Exception,
337                func_type_idx: tag.func_type_idx,
338            },
339        });
340    }
341    Ok(())
342}
343
344fn decode_global_section(
345    module: &mut Module,
346    reader: wasmparser::GlobalSectionReader,
347) -> Result<()> {
348    let section_end = reader.range().end;
349    let mut iter = reader.into_iter_with_offsets().peekable();
350    while let Some(result) = iter.next() {
351        let (start, global) = result?;
352        let end = iter
353            .peek()
354            .and_then(|r| r.as_ref().ok().map(|(off, _)| *off))
355            .unwrap_or(section_end);
356        let init_expr = decode_const_expr(global.init_expr)?;
357        module.globals.push(Global {
358            span: Span::new(start, end - start),
359            ty: global.ty,
360            init_expr,
361        });
362    }
363    Ok(())
364}
365
366fn decode_export_section(
367    module: &mut Module,
368    reader: wasmparser::ExportSectionReader,
369) -> Result<()> {
370    let section_end = reader.range().end;
371    let mut iter = reader.into_iter_with_offsets().peekable();
372    while let Some(result) = iter.next() {
373        let (start, export) = result?;
374        let end = iter
375            .peek()
376            .and_then(|r| r.as_ref().ok().map(|(off, _)| *off))
377            .unwrap_or(section_end);
378        module.exports.push(Export {
379            span: Span::new(start, end - start),
380            name: export.name.to_string(),
381            kind: ExternalKind::from(export.kind),
382            index: export.index,
383        });
384    }
385    Ok(())
386}
387
388fn decode_element_section(
389    module: &mut Module,
390    reader: wasmparser::ElementSectionReader,
391) -> Result<()> {
392    let section_end = reader.range().end;
393    let mut iter = reader.into_iter_with_offsets().peekable();
394    while let Some(result) = iter.next() {
395        let (start, element) = result?;
396        let end = iter
397            .peek()
398            .and_then(|r| r.as_ref().ok().map(|(off, _)| *off))
399            .unwrap_or(section_end);
400        let kind = match element.kind {
401            wasmparser::ElementKind::Passive => ElementKind::Passive,
402            wasmparser::ElementKind::Active {
403                table_index,
404                offset_expr,
405            } => ElementKind::Active {
406                table_index,
407                offset_expr: decode_const_expr(offset_expr)?,
408            },
409            wasmparser::ElementKind::Declared => ElementKind::Declared,
410        };
411        let items = match element.items {
412            wasmparser::ElementItems::Functions(reader) => {
413                let funcs: std::result::Result<Vec<u32>, _> = reader.into_iter().collect();
414                ElementItems::Functions(funcs?)
415            }
416            wasmparser::ElementItems::Expressions(ref_type, reader) => {
417                let exprs: std::result::Result<Vec<_>, _> = reader
418                    .into_iter()
419                    .map(|e| {
420                        e.and_then(|e| {
421                            decode_const_expr(e).map_err(|e| match e {
422                                Error::BinaryReader(e) => e,
423                                _ => unreachable!(),
424                            })
425                        })
426                    })
427                    .collect();
428                ElementItems::Expressions(ref_type, exprs?)
429            }
430        };
431        module.elements.push(Element {
432            span: Span::new(start, end - start),
433            kind,
434            items,
435        });
436    }
437    Ok(())
438}
439
440fn decode_data_section(module: &mut Module, reader: wasmparser::DataSectionReader) -> Result<()> {
441    let section_end = reader.range().end;
442    let mut iter = reader.into_iter_with_offsets().peekable();
443    while let Some(result) = iter.next() {
444        let (start, data) = result?;
445        let end = iter
446            .peek()
447            .and_then(|r| r.as_ref().ok().map(|(off, _)| *off))
448            .unwrap_or(section_end);
449        let kind = match data.kind {
450            wasmparser::DataKind::Passive => DataKind::Passive,
451            wasmparser::DataKind::Active {
452                memory_index,
453                offset_expr,
454            } => DataKind::Active {
455                memory_index,
456                offset_expr: decode_const_expr(offset_expr)?,
457            },
458        };
459        module.data.push(Data {
460            span: Span::new(start, end - start),
461            kind,
462            data: data.data.to_vec(),
463        });
464    }
465    Ok(())
466}
467
468fn decode_code_entry(
469    module: &mut Module,
470    body: wasmparser::FunctionBody,
471    options: &DecodeOptions,
472) -> Result<()> {
473    if options.skeleton {
474        let range = body.range();
475        module.bodies.push(FuncBody::Lazy {
476            offset: range.start,
477            len: range.end - range.start,
478        });
479    } else {
480        let def = decode_function_body(body)?;
481        module.bodies.push(FuncBody::Decoded(def));
482    }
483    Ok(())
484}
485
486fn decode_function_body(body: wasmparser::FunctionBody) -> Result<FuncBodyDef> {
487    let range = body.range();
488    let span = Span::new(range.start, range.end - range.start);
489
490    let mut locals = Vec::new();
491    let local_reader = body.get_locals_reader()?;
492    for local in local_reader {
493        let (count, val_type) = local?;
494        locals.push((count, val_type));
495    }
496
497    let mut instructions = Vec::new();
498    let mut ops_reader = body.get_operators_reader()?;
499    while !ops_reader.eof() {
500        let pos = ops_reader.original_position();
501        let op = ops_reader.read()?;
502        let end = ops_reader.original_position();
503        let instr = Instruction::from_operator(&op);
504        instructions.push((Span::new(pos, end - pos), instr));
505    }
506
507    Ok(FuncBodyDef {
508        span,
509        locals,
510        instructions,
511    })
512}
513
514fn decode_const_expr(expr: wasmparser::ConstExpr) -> Result<ConstExpr> {
515    let range = expr.get_binary_reader().range();
516    let span = Span::new(range.start, range.end - range.start);
517    let mut ops = Vec::new();
518    let mut reader = expr.get_operators_reader();
519    while !reader.eof() {
520        let op = reader.read()?;
521        match op {
522            wasmparser::Operator::End => break,
523            _ => ops.push(Instruction::from_operator(&op)),
524        }
525    }
526    Ok(ConstExpr { span, ops })
527}
528
529fn decode_custom_section(
530    module: &mut Module,
531    reader: wasmparser::CustomSectionReader,
532    placement: Option<&str>,
533) -> Result<()> {
534    match reader.as_known() {
535        wasmparser::KnownCustom::Name(name_reader) => {
536            decode_name_section(module, name_reader)?;
537        }
538        _ => {
539            let range = reader.range();
540            module.custom_sections.push(CustomSection {
541                span: Span::new(range.start, range.end - range.start),
542                name: reader.name().to_string(),
543                data: reader.data().to_vec(),
544                placement: placement.map(|s| s.to_string()),
545            });
546        }
547    }
548    Ok(())
549}
550
551fn decode_name_section(module: &mut Module, reader: wasmparser::NameSectionReader) -> Result<()> {
552    for name in reader {
553        match name? {
554            wasmparser::Name::Module { name, .. } => {
555                module.names.module_name = Some(name.to_string());
556            }
557            wasmparser::Name::Function(map) => {
558                for naming in map {
559                    let naming = naming?;
560                    module
561                        .names
562                        .function_names
563                        .insert(naming.index, naming.name.to_string());
564                }
565            }
566            wasmparser::Name::Local(indirect) => {
567                for func_names in indirect {
568                    let func_names = func_names?;
569                    let func_idx = func_names.index;
570                    let mut local_map = std::collections::HashMap::new();
571                    for naming in func_names.names {
572                        let naming = naming?;
573                        local_map.insert(naming.index, naming.name.to_string());
574                    }
575                    module.names.local_names.insert(func_idx, local_map);
576                }
577            }
578            wasmparser::Name::Label(indirect) => {
579                for func_labels in indirect {
580                    let func_labels = func_labels?;
581                    let func_idx = func_labels.index;
582                    let mut label_map = std::collections::HashMap::new();
583                    for naming in func_labels.names {
584                        let naming = naming?;
585                        label_map.insert(naming.index, naming.name.to_string());
586                    }
587                    module.names.label_names.insert(func_idx, label_map);
588                }
589            }
590            wasmparser::Name::Type(map) => {
591                for naming in map {
592                    let naming = naming?;
593                    module
594                        .names
595                        .type_names
596                        .insert(naming.index, naming.name.to_string());
597                }
598            }
599            wasmparser::Name::Table(map) => {
600                for naming in map {
601                    let naming = naming?;
602                    module
603                        .names
604                        .table_names
605                        .insert(naming.index, naming.name.to_string());
606                }
607            }
608            wasmparser::Name::Memory(map) => {
609                for naming in map {
610                    let naming = naming?;
611                    module
612                        .names
613                        .memory_names
614                        .insert(naming.index, naming.name.to_string());
615                }
616            }
617            wasmparser::Name::Global(map) => {
618                for naming in map {
619                    let naming = naming?;
620                    module
621                        .names
622                        .global_names
623                        .insert(naming.index, naming.name.to_string());
624                }
625            }
626            wasmparser::Name::Element(map) => {
627                for naming in map {
628                    let naming = naming?;
629                    module
630                        .names
631                        .element_names
632                        .insert(naming.index, naming.name.to_string());
633                }
634            }
635            wasmparser::Name::Data(map) => {
636                for naming in map {
637                    let naming = naming?;
638                    module
639                        .names
640                        .data_names
641                        .insert(naming.index, naming.name.to_string());
642                }
643            }
644            wasmparser::Name::Tag(map) => {
645                for naming in map {
646                    let naming = naming?;
647                    module
648                        .names
649                        .tag_names
650                        .insert(naming.index, naming.name.to_string());
651                }
652            }
653            wasmparser::Name::Field(indirect) => {
654                for type_fields in indirect {
655                    let type_fields = type_fields?;
656                    let type_idx = type_fields.index;
657                    let mut field_map = std::collections::HashMap::new();
658                    for naming in type_fields.names {
659                        let naming = naming?;
660                        field_map.insert(naming.index, naming.name.to_string());
661                    }
662                    module.names.field_names.insert(type_idx, field_map);
663                }
664            }
665            wasmparser::Name::Unknown { .. } => {}
666        }
667    }
668    Ok(())
669}