• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

henrythasler / rust-tiny-wasm / 23354992303

20 Mar 2026 05:35PM UTC coverage: 86.175% (-7.6%) from 93.74%
23354992303

push

github

web-flow
Merge pull request #3 from henrythasler/feature/wasmparser

Feature/wasmparser

90 of 136 new or added lines in 6 files covered. (66.18%)

2 existing lines in 1 file now uncovered.

374 of 434 relevant lines covered (86.18%)

6.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.85
/src/compiler.rs
1
#![allow(dead_code)]
2
//! Processes a Webassembly module and returns a LinkedModule for subsequent execution
3
use std::mem;
4

5
use wasmparser::{Operator, Parser, Payload::*, ValType};
6

7
use super::*;
8
use crate::assembler::aarch64::*;
9
use crate::assembler::{emit_epilogue, emit_prologue};
10

11
use control_instructions::*;
12
use procedure_call::*;
13

14
mod control_instructions;
15
mod numeric_instructions;
16
mod procedure_call;
17

18
#[derive(Clone)]
19
pub struct WasmFunction {
20
    pub name: String,
21
    /// offset in INSTRUCTION_SIZE units
22
    pub offset: usize,
23
    /// length in INSTRUCTION_SIZE units
24
    pub length: usize,
25
}
26

27
pub struct LinkedModule {
28
    pub machinecode: Vec<u32>,
29
    pub functions: Vec<WasmFunction>,
30
}
31

32
impl LinkedModule {
33
    pub fn new(machinecode: Vec<u32>, functions: Vec<WasmFunction>) -> Self {
3✔
34
        Self {
3✔
35
            machinecode,
3✔
36
            functions,
3✔
37
        }
3✔
38
    }
3✔
39
}
40

41
#[derive(Debug)]
42
pub enum Opcode {
43
    Func,
44
    Block,
45
    Loop,
46
    If,
47
    Else,
48
}
49

50
#[derive(Debug)]
51
pub enum Instruction {
52
    Br,
53
}
54

55
#[derive(Debug)]
56
pub struct Patch {
57
    pub location: usize,
58
    pub instruction: Instruction,
59
}
60

61
#[derive(Debug)]
62
pub struct ControlFrame {
63
    pub opcode: Opcode,
64
    pub start_types: Vec<ValType>,
65
    pub end_types: Vec<ValType>,
66
    pub stack_height: usize,
67
    pub patches: Vec<Patch>,
68
}
69

70
#[derive(Debug)]
71
pub struct StackElement {
72
    reg: Reg,
73
    valtype: wasmparser::ValType,
74
}
75

76
#[derive(Debug)]
77
pub struct Export {
78
    pub name: String,
79
    pub r#type: wasmparser::ExternalKind,
80
    pub index: u32,
81
}
82

83
pub fn compile(module: &[u8]) -> Result<LinkedModule> {
4✔
84
    let mut machinecode: Vec<u32> = Vec::new();
4✔
85
    let mut wasm_functions: Vec<WasmFunction> = Vec::new();
4✔
86

87
    let parser = Parser::new(0);
4✔
88

89
    // temporary module sections for lookup
90
    let mut types: Vec<wasmparser::FuncType> = Vec::new();
4✔
91
    let mut exports: Vec<Export> = Vec::new();
4✔
92
    let mut functions: Vec<u32> = Vec::new();
4✔
93

94
    let mut function_index = 0;
4✔
95

96
    for payload in parser.parse_all(module) {
35✔
97
        match payload? {
35✔
98
            // Sections for WebAssembly modules
99
            Version { .. } => { /* ... */ }
4✔
100
            TypeSection(reader) => {
4✔
101
                for ty in reader.into_iter() {
6✔
102
                    for (_, item) in ty?.into_types_and_offsets() {
6✔
103
                        if let wasmparser::CompositeInnerType::Func(func) =
6✔
104
                            item.composite_type.inner
6✔
105
                        {
6✔
106
                            println!("{}", func);
6✔
107
                            types.push(func);
6✔
108
                        }
6✔
109
                    }
110
                }
111
            }
NEW
112
            ImportSection(_) => { /* ... */ }
×
113
            FunctionSection(reader) => {
4✔
114
                for func in reader {
11✔
115
                    functions.push(func?);
11✔
116
                }
117
            }
NEW
118
            TableSection(_) => { /* ... */ }
×
NEW
119
            MemorySection(_) => { /* ... */ }
×
NEW
120
            TagSection(_) => { /* ... */ }
×
NEW
121
            GlobalSection(_) => { /* ... */ }
×
122
            ExportSection(reader) => {
4✔
123
                for export in reader {
11✔
124
                    let export = export?;
11✔
125
                    exports.push(Export {
11✔
126
                        name: export.name.to_string(),
11✔
127
                        r#type: export.kind,
11✔
128
                        index: export.index,
11✔
129
                    });
11✔
130
                }
131
            }
NEW
132
            StartSection { .. } => { /* ... */ }
×
NEW
133
            ElementSection(_) => { /* ... */ }
×
NEW
134
            DataCountSection { .. } => { /* ... */ }
×
NEW
135
            DataSection(_) => { /* ... */ }
×
136

137
            // Here we know how many functions we'll be receiving as
138
            // `CodeSectionEntry`, so we can prepare for that, and
139
            // afterwards we can parse and handle each function
140
            // individually.
141
            CodeSectionStart { .. } => {}
4✔
142
            CodeSectionEntry(body) => {
11✔
143
                // here we can iterate over `body` to parse the function
144
                // and its locals
145
                let offset = machinecode.len();
11✔
146
                let mut reader = body.get_operators_reader()?;
11✔
147
                let fn_idx = *functions.get(function_index).unwrap() as usize;
11✔
148
                compile_function(&mut reader, types.get(fn_idx).unwrap(), &mut machinecode)?;
11✔
149

150
                wasm_functions.push(WasmFunction {
11✔
151
                    name: exports.get(function_index).unwrap().name.clone(),
11✔
152
                    offset,
11✔
153
                    length: machinecode.len() - offset,
11✔
154
                });
11✔
155
                function_index += 1;
11✔
156
            }
157

158
            // Sections for WebAssembly components
NEW
159
            ModuleSection { .. } => { /* ... */ }
×
NEW
160
            InstanceSection(_) => { /* ... */ }
×
NEW
161
            CoreTypeSection(_) => { /* ... */ }
×
NEW
162
            ComponentSection { .. } => { /* ... */ }
×
NEW
163
            ComponentInstanceSection(_) => { /* ... */ }
×
NEW
164
            ComponentAliasSection(_) => { /* ... */ }
×
NEW
165
            ComponentTypeSection(_) => { /* ... */ }
×
NEW
166
            ComponentCanonicalSection(_) => { /* ... */ }
×
NEW
167
            ComponentStartSection { .. } => { /* ... */ }
×
NEW
168
            ComponentImportSection(_) => { /* ... */ }
×
NEW
169
            ComponentExportSection(_) => { /* ... */ }
×
170

NEW
171
            CustomSection(_) => { /* ... */ }
×
172

173
            // Once we've reached the end of a parser we either resume
174
            // at the parent parser or the payload iterator is at its
175
            // end and we're done.
176
            End(_) => {}
4✔
177

178
            // most likely you'd return an error here, but if you want
179
            // you can also inspect the raw contents of unknown sections
NEW
180
            _ => {}
×
181
        }
182
    }
183

184
    Ok(LinkedModule {
4✔
185
        machinecode,
4✔
186
        functions: wasm_functions,
4✔
187
    })
4✔
188
}
4✔
189

190
fn compile_function(
11✔
191
    reader: &mut wasmparser::OperatorsReader<'_>,
11✔
192
    func_type: &wasmparser::FuncType,
11✔
193
    machinecode: &mut Vec<u32>,
11✔
194
) -> Result<usize> {
11✔
195
    // Value stack starts empty
196
    let mut value_stack: Vec<StackElement> = vec![];
11✔
197

198
    // Control stack is initialized with the (implicit) outer func-block
199
    let mut control_stack: Vec<ControlFrame> = vec![ControlFrame {
11✔
200
        opcode: Opcode::Func,
11✔
201
        start_types: func_type.params().to_vec(),
11✔
202
        end_types: func_type.results().to_vec(),
11✔
203
        stack_height: value_stack.len(),
11✔
204
        patches: vec![],
11✔
205
    }];
11✔
206

207
    let initial_size = machinecode.len();
11✔
208

209
    let register_pool = RegisterPool::new();
11✔
210

211
    // every functions starts with an epilogue to save the initial state and create a new stack frame
212
    emit_prologue(machinecode);
11✔
213

214
    'expression: while !reader.eof() {
22✔
215
        let index = reader.original_position();
22✔
216
        let op = reader.read().unwrap();
22✔
217
        match op {
22✔
218
            Operator::End => {
219
                if compile_end(&mut control_stack, &mut value_stack, machinecode) {
11✔
220
                    break 'expression;
11✔
NEW
221
                }
×
222
            }
223
            Operator::Return => {
3✔
224
                compile_return(&mut control_stack, machinecode);
3✔
225
            }
3✔
226
            Operator::I32Const { value } => {
4✔
227
                let reg = register_pool.allocate_register();
4✔
228
                value_stack.push(StackElement {
4✔
229
                    reg,
4✔
230
                    valtype: ValType::I32,
4✔
231
                });
4✔
232
                compound::mov_large_immediate(reg, value as i64, RegSize::Reg32bit, machinecode);
4✔
233
            }
4✔
234
            Operator::I64Const { value } => {
4✔
235
                let reg = register_pool.allocate_register();
4✔
236
                value_stack.push(StackElement {
4✔
237
                    reg,
4✔
238
                    valtype: ValType::I64,
4✔
239
                });
4✔
240
                compound::mov_large_immediate(reg, value, RegSize::Reg64bit, machinecode);
4✔
241
            }
4✔
242
            _ => {
NEW
243
                return Err(TinyWasmError::Compiler(format!(
×
NEW
244
                    "unsupported instruction: {:?} at position {}",
×
NEW
245
                    op, index
×
NEW
246
                )));
×
247
            }
248
        }
249
    }
250

251
    // move result values to result registers according to Aarch64 Procedure Call Standard (X0..X7)
252
    if !func_type.results().is_empty() {
11✔
253
        load_results(&mut value_stack, func_type.results().len(), machinecode)?;
8✔
254
    }
3✔
255

256
    // restore initial state before returning to the caller
257
    emit_epilogue(machinecode);
11✔
258

259
    // add padding to INSTRUCTION_SIZE to align subsequent functions to the correct size
260
    let padding_instructions =
11✔
261
        ((machinecode.len() * INSTRUCTION_SIZE) % mem::align_of::<fn()>()) / INSTRUCTION_SIZE;
11✔
262
    for _ in 0..padding_instructions {
11✔
263
        machinecode.push(hint::nop());
9✔
264
    }
9✔
265

266
    Ok(machinecode.len() - initial_size)
11✔
267
}
11✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc