1use crate::wasm_op::WasmOp;
7use anyhow::{Context, Result};
8use std::collections::HashMap;
9use wasmparser::{ExternalKind, Parser, Payload};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum ImportKind {
14 Function(u32),
16 Memory,
18 Table,
20 Global,
22}
23
24#[derive(Debug, Clone)]
26pub struct ImportEntry {
27 pub module: String,
29 pub name: String,
31 pub kind: ImportKind,
33 pub index: u32,
35}
36
37#[derive(Debug, Clone)]
39pub struct WasmMemory {
40 pub index: u32,
42 pub initial_pages: u32,
44 pub max_pages: Option<u32>,
46 pub shared: bool,
48}
49
50#[derive(Debug, Clone)]
56pub struct WasmGlobal {
57 pub index: u32,
59 pub init_i32: Option<i32>,
61 pub mutable: bool,
63}
64
65impl WasmMemory {
66 pub fn initial_bytes(&self) -> u32 {
68 self.initial_pages * 65536
69 }
70
71 pub fn max_bytes(&self) -> u32 {
73 self.max_pages.unwrap_or(self.initial_pages) * 65536
74 }
75}
76
77#[derive(Debug, Clone)]
79pub struct DecodedModule {
80 pub functions: Vec<FunctionOps>,
82 pub memories: Vec<WasmMemory>,
84 pub data_segments: Vec<(u32, Vec<u8>)>,
86 pub imports: Vec<ImportEntry>,
88 pub num_imported_funcs: u32,
90 pub func_arg_counts: Vec<u32>,
96 pub type_arg_counts: Vec<u32>,
100 pub globals: Vec<WasmGlobal>,
105 pub elem_func_indices: Vec<u32>,
112}
113
114pub fn decode_wasm_module(wasm_bytes: &[u8]) -> Result<DecodedModule> {
116 let mut functions = Vec::new();
117 let mut memories = Vec::new();
118 let mut data_segments = Vec::new();
119 let mut globals: Vec<WasmGlobal> = Vec::new();
120 let mut imports = Vec::new();
121 let mut func_index = 0u32;
122 let mut num_imported_funcs = 0u32;
123 let mut export_names: HashMap<u32, String> = HashMap::new();
124 let mut type_arg_counts: Vec<u32> = Vec::new();
127 let mut func_arg_counts: Vec<u32> = Vec::new();
128 let mut elem_func_indices: Vec<u32> = Vec::new();
129
130 for payload in Parser::new(0).parse_all(wasm_bytes) {
131 let payload = payload.context("Failed to parse WASM payload")?;
132
133 match payload {
134 Payload::TypeSection(reader) => {
135 for rec_group in reader {
138 let rec_group = rec_group.context("Failed to parse type")?;
139 for sub_ty in rec_group.types() {
140 let count = match &sub_ty.composite_type.inner {
141 wasmparser::CompositeInnerType::Func(func_ty) => {
142 func_ty.params().len() as u32
143 }
144 _ => 0,
145 };
146 type_arg_counts.push(count);
147 }
148 }
149 }
150 Payload::ImportSection(reader) => {
151 for import in reader {
152 let import = import.context("Failed to parse import")?;
153 let (kind, idx) = match import.ty {
154 wasmparser::TypeRef::Func(type_idx) => {
155 let idx = num_imported_funcs;
156 num_imported_funcs += 1;
157 func_arg_counts
160 .push(type_arg_counts.get(type_idx as usize).copied().unwrap_or(0));
161 (ImportKind::Function(type_idx), idx)
162 }
163 wasmparser::TypeRef::Memory(_) => (ImportKind::Memory, 0),
164 wasmparser::TypeRef::Table(_) => (ImportKind::Table, 0),
165 wasmparser::TypeRef::Global(_) => (ImportKind::Global, 0),
166 _ => continue,
167 };
168 imports.push(ImportEntry {
169 module: import.module.to_string(),
170 name: import.name.to_string(),
171 kind,
172 index: idx,
173 });
174 }
175 }
176 Payload::FunctionSection(reader) => {
177 for ty in reader {
182 let type_idx = ty.context("Failed to parse function type index")?;
183 func_arg_counts
184 .push(type_arg_counts.get(type_idx as usize).copied().unwrap_or(0));
185 }
186 }
187 Payload::MemorySection(reader) => {
188 for (idx, memory) in reader.into_iter().enumerate() {
189 let mem = memory.context("Failed to parse memory")?;
190 memories.push(WasmMemory {
191 index: idx as u32,
192 initial_pages: mem.initial as u32,
193 max_pages: mem.maximum.map(|m| m as u32),
194 shared: mem.shared,
195 });
196 }
197 }
198 Payload::GlobalSection(reader) => {
199 for (idx, global) in reader.into_iter().enumerate() {
205 let global = global.context("Failed to parse global")?;
206 let mut init_i32 = None;
207 let mut ops = global.init_expr.get_operators_reader();
208 if let Ok(wasmparser::Operator::I32Const { value }) = ops.read() {
209 init_i32 = Some(value);
210 }
211 globals.push(WasmGlobal {
212 index: idx as u32,
213 init_i32,
214 mutable: global.ty.mutable,
215 });
216 }
217 }
218 Payload::DataSection(reader) => {
219 for data in reader {
220 let data = data.context("Failed to parse data segment")?;
221 if let wasmparser::DataKind::Active {
222 memory_index: 0,
223 offset_expr,
224 } = data.kind
225 {
226 let mut ops = offset_expr.get_operators_reader();
227 if let Ok(wasmparser::Operator::I32Const { value }) = ops.read() {
228 data_segments.push((value as u32, data.data.to_vec()));
229 }
230 }
231 }
232 }
233 Payload::ElementSection(reader) => {
234 for elem in reader {
241 let elem = elem.context("Failed to parse element segment")?;
242 match elem.items {
243 wasmparser::ElementItems::Functions(funcs) => {
244 for f in funcs {
245 elem_func_indices
246 .push(f.context("Failed to parse element func index")?);
247 }
248 }
249 wasmparser::ElementItems::Expressions(_, exprs) => {
250 for expr in exprs {
251 let expr = expr.context("Failed to parse element expr")?;
252 for op in expr.get_operators_reader() {
253 if let wasmparser::Operator::RefFunc { function_index } =
254 op.context("Failed to parse element op")?
255 {
256 elem_func_indices.push(function_index);
257 }
258 }
259 }
260 }
261 }
262 }
263 }
264 Payload::ExportSection(exports) => {
265 for export in exports {
266 let export = export.context("Failed to parse export")?;
267 if export.kind == ExternalKind::Func {
268 export_names.insert(export.index, export.name.to_string());
269 }
270 }
271 }
272 Payload::CodeSectionEntry(body) => {
273 let ops = decode_function_body(&body)?;
274 let actual_index = num_imported_funcs + func_index;
275 let export_name = export_names.get(&actual_index).cloned();
276
277 functions.push(FunctionOps {
278 index: actual_index,
279 export_name,
280 ops,
281 });
282 func_index += 1;
283 }
284 _ => {}
285 }
286 }
287
288 Ok(DecodedModule {
289 functions,
290 memories,
291 data_segments,
292 imports,
293 num_imported_funcs,
294 func_arg_counts,
295 type_arg_counts,
296 globals,
297 elem_func_indices,
298 })
299}
300
301pub fn decode_wasm_functions(wasm_bytes: &[u8]) -> Result<Vec<FunctionOps>> {
303 let mut functions = Vec::new();
304 let mut func_index = 0u32;
305 let mut num_imported_funcs = 0u32;
306 let mut export_names: HashMap<u32, String> = HashMap::new();
307
308 for payload in Parser::new(0).parse_all(wasm_bytes) {
309 let payload = payload.context("Failed to parse WASM payload")?;
310
311 match payload {
312 Payload::ImportSection(imports) => {
313 for import in imports {
314 let import = import.context("Failed to parse import")?;
315 if matches!(import.ty, wasmparser::TypeRef::Func(_)) {
316 num_imported_funcs += 1;
317 }
318 }
319 }
320 Payload::ExportSection(exports) => {
321 for export in exports {
322 let export = export.context("Failed to parse export")?;
323 if export.kind == ExternalKind::Func {
324 export_names.insert(export.index, export.name.to_string());
325 }
326 }
327 }
328 Payload::CodeSectionEntry(body) => {
329 let ops = decode_function_body(&body)?;
330 let actual_index = num_imported_funcs + func_index;
331 let export_name = export_names.get(&actual_index).cloned();
332
333 functions.push(FunctionOps {
334 index: actual_index,
335 export_name,
336 ops,
337 });
338 func_index += 1;
339 }
340 _ => {}
341 }
342 }
343
344 Ok(functions)
345}
346
347#[derive(Debug, Clone)]
349pub struct FunctionOps {
350 pub index: u32,
352 pub export_name: Option<String>,
354 pub ops: Vec<WasmOp>,
356}
357
358fn decode_function_body(body: &wasmparser::FunctionBody) -> Result<Vec<WasmOp>> {
360 let mut ops = Vec::new();
361
362 let ops_reader = body.get_operators_reader()?;
363 for op_result in ops_reader {
364 let op = op_result.context("Failed to read operator")?;
365
366 if let Some(wasm_op) = convert_operator(&op) {
367 ops.push(wasm_op);
368 }
369 }
370
371 Ok(ops)
372}
373
374fn convert_operator(op: &wasmparser::Operator) -> Option<WasmOp> {
376 use wasmparser::Operator::*;
377
378 match op {
379 I32Const { value } => Some(WasmOp::I32Const(*value)),
381
382 I32Add => Some(WasmOp::I32Add),
384 I32Sub => Some(WasmOp::I32Sub),
385 I32Mul => Some(WasmOp::I32Mul),
386 I32DivS => Some(WasmOp::I32DivS),
387 I32DivU => Some(WasmOp::I32DivU),
388 I32RemS => Some(WasmOp::I32RemS),
389 I32RemU => Some(WasmOp::I32RemU),
390
391 I64Const { value } => Some(WasmOp::I64Const(*value)),
393
394 I64Add => Some(WasmOp::I64Add),
396 I64Sub => Some(WasmOp::I64Sub),
397 I64Mul => Some(WasmOp::I64Mul),
398 I64DivS => Some(WasmOp::I64DivS),
399 I64DivU => Some(WasmOp::I64DivU),
400 I64RemS => Some(WasmOp::I64RemS),
401 I64RemU => Some(WasmOp::I64RemU),
402
403 I64And => Some(WasmOp::I64And),
405 I64Or => Some(WasmOp::I64Or),
406 I64Xor => Some(WasmOp::I64Xor),
407 I64Shl => Some(WasmOp::I64Shl),
408 I64ShrS => Some(WasmOp::I64ShrS),
409 I64ShrU => Some(WasmOp::I64ShrU),
410 I64Rotl => Some(WasmOp::I64Rotl),
411 I64Rotr => Some(WasmOp::I64Rotr),
412 I64Clz => Some(WasmOp::I64Clz),
413 I64Ctz => Some(WasmOp::I64Ctz),
414 I64Popcnt => Some(WasmOp::I64Popcnt),
415 I64Extend8S => Some(WasmOp::I64Extend8S),
416 I64Extend16S => Some(WasmOp::I64Extend16S),
417 I64Extend32S => Some(WasmOp::I64Extend32S),
418 I64ExtendI32U => Some(WasmOp::I64ExtendI32U),
424 I64ExtendI32S => Some(WasmOp::I64ExtendI32S),
425 I32WrapI64 => Some(WasmOp::I32WrapI64),
426
427 I64Eqz => Some(WasmOp::I64Eqz),
429 I64Eq => Some(WasmOp::I64Eq),
430 I64Ne => Some(WasmOp::I64Ne),
431 I64LtS => Some(WasmOp::I64LtS),
432 I64LtU => Some(WasmOp::I64LtU),
433 I64LeS => Some(WasmOp::I64LeS),
434 I64LeU => Some(WasmOp::I64LeU),
435 I64GtS => Some(WasmOp::I64GtS),
436 I64GtU => Some(WasmOp::I64GtU),
437 I64GeS => Some(WasmOp::I64GeS),
438 I64GeU => Some(WasmOp::I64GeU),
439
440 I32And => Some(WasmOp::I32And),
442 I32Or => Some(WasmOp::I32Or),
443 I32Xor => Some(WasmOp::I32Xor),
444 I32Shl => Some(WasmOp::I32Shl),
445 I32ShrS => Some(WasmOp::I32ShrS),
446 I32ShrU => Some(WasmOp::I32ShrU),
447 I32Rotl => Some(WasmOp::I32Rotl),
448 I32Rotr => Some(WasmOp::I32Rotr),
449 I32Clz => Some(WasmOp::I32Clz),
450 I32Ctz => Some(WasmOp::I32Ctz),
451 I32Popcnt => Some(WasmOp::I32Popcnt),
452 I32Extend8S => Some(WasmOp::I32Extend8S),
453 I32Extend16S => Some(WasmOp::I32Extend16S),
454
455 I32Eqz => Some(WasmOp::I32Eqz),
457 I32Eq => Some(WasmOp::I32Eq),
458 I32Ne => Some(WasmOp::I32Ne),
459 I32LtS => Some(WasmOp::I32LtS),
460 I32LtU => Some(WasmOp::I32LtU),
461 I32LeS => Some(WasmOp::I32LeS),
462 I32LeU => Some(WasmOp::I32LeU),
463 I32GtS => Some(WasmOp::I32GtS),
464 I32GtU => Some(WasmOp::I32GtU),
465 I32GeS => Some(WasmOp::I32GeS),
466 I32GeU => Some(WasmOp::I32GeU),
467
468 I32Load { memarg } => Some(WasmOp::I32Load {
470 offset: memarg.offset as u32,
471 align: memarg.align as u32,
472 }),
473 I32Store { memarg } => Some(WasmOp::I32Store {
474 offset: memarg.offset as u32,
475 align: memarg.align as u32,
476 }),
477
478 I32Load8S { memarg } => Some(WasmOp::I32Load8S {
480 offset: memarg.offset as u32,
481 align: memarg.align as u32,
482 }),
483 I32Load8U { memarg } => Some(WasmOp::I32Load8U {
484 offset: memarg.offset as u32,
485 align: memarg.align as u32,
486 }),
487 I32Load16S { memarg } => Some(WasmOp::I32Load16S {
488 offset: memarg.offset as u32,
489 align: memarg.align as u32,
490 }),
491 I32Load16U { memarg } => Some(WasmOp::I32Load16U {
492 offset: memarg.offset as u32,
493 align: memarg.align as u32,
494 }),
495
496 I32Store8 { memarg } => Some(WasmOp::I32Store8 {
498 offset: memarg.offset as u32,
499 align: memarg.align as u32,
500 }),
501 I32Store16 { memarg } => Some(WasmOp::I32Store16 {
502 offset: memarg.offset as u32,
503 align: memarg.align as u32,
504 }),
505
506 LocalGet { local_index } => Some(WasmOp::LocalGet(*local_index)),
508 LocalSet { local_index } => Some(WasmOp::LocalSet(*local_index)),
509 LocalTee { local_index } => Some(WasmOp::LocalTee(*local_index)),
510 GlobalGet { global_index } => Some(WasmOp::GlobalGet(*global_index)),
511 GlobalSet { global_index } => Some(WasmOp::GlobalSet(*global_index)),
512
513 Block { .. } => Some(WasmOp::Block),
515 Loop { .. } => Some(WasmOp::Loop),
516 Br { relative_depth } => Some(WasmOp::Br(*relative_depth)),
517 BrIf { relative_depth } => Some(WasmOp::BrIf(*relative_depth)),
518 BrTable { targets } => {
524 let default = targets.default();
525 let tgts: Vec<u32> = targets.targets().filter_map(Result::ok).collect();
526 Some(WasmOp::BrTable {
527 targets: tgts,
528 default,
529 })
530 }
531 Return => Some(WasmOp::Return),
532 Call { function_index } => Some(WasmOp::Call(*function_index)),
533 CallIndirect {
534 type_index,
535 table_index,
536 ..
537 } => Some(WasmOp::CallIndirect {
538 type_index: *type_index,
539 table_index: *table_index,
540 }),
541
542 End => Some(WasmOp::End),
544
545 Nop | Unreachable => None,
547
548 Drop => Some(WasmOp::Drop),
550
551 Select => Some(WasmOp::Select),
553
554 If { .. } => Some(WasmOp::If),
556 Else => Some(WasmOp::Else),
557
558 I64Load8S { memarg } => Some(WasmOp::I64Load8S {
560 offset: memarg.offset as u32,
561 align: memarg.align as u32,
562 }),
563 I64Load8U { memarg } => Some(WasmOp::I64Load8U {
564 offset: memarg.offset as u32,
565 align: memarg.align as u32,
566 }),
567 I64Load16S { memarg } => Some(WasmOp::I64Load16S {
568 offset: memarg.offset as u32,
569 align: memarg.align as u32,
570 }),
571 I64Load16U { memarg } => Some(WasmOp::I64Load16U {
572 offset: memarg.offset as u32,
573 align: memarg.align as u32,
574 }),
575 I64Load32S { memarg } => Some(WasmOp::I64Load32S {
576 offset: memarg.offset as u32,
577 align: memarg.align as u32,
578 }),
579 I64Load32U { memarg } => Some(WasmOp::I64Load32U {
580 offset: memarg.offset as u32,
581 align: memarg.align as u32,
582 }),
583
584 I64Store8 { memarg } => Some(WasmOp::I64Store8 {
586 offset: memarg.offset as u32,
587 align: memarg.align as u32,
588 }),
589 I64Store16 { memarg } => Some(WasmOp::I64Store16 {
590 offset: memarg.offset as u32,
591 align: memarg.align as u32,
592 }),
593 I64Store32 { memarg } => Some(WasmOp::I64Store32 {
594 offset: memarg.offset as u32,
595 align: memarg.align as u32,
596 }),
597
598 MemorySize { mem, .. } => Some(WasmOp::MemorySize(*mem)),
600 MemoryGrow { mem, .. } => Some(WasmOp::MemoryGrow(*mem)),
601
602 V128Const { value } => {
606 let mut bytes = [0u8; 16];
607 bytes.copy_from_slice(value.bytes());
608 Some(WasmOp::V128Const(bytes))
609 }
610 V128Load { memarg } => Some(WasmOp::V128Load {
611 offset: memarg.offset as u32,
612 align: memarg.align as u32,
613 }),
614 V128Store { memarg } => Some(WasmOp::V128Store {
615 offset: memarg.offset as u32,
616 align: memarg.align as u32,
617 }),
618
619 V128And => Some(WasmOp::V128And),
621 V128Or => Some(WasmOp::V128Or),
622 V128Xor => Some(WasmOp::V128Xor),
623 V128Not => Some(WasmOp::V128Not),
624 V128AndNot => Some(WasmOp::V128AndNot),
625
626 I8x16Add => Some(WasmOp::I8x16Add),
628 I8x16Sub => Some(WasmOp::I8x16Sub),
629 I8x16Neg => Some(WasmOp::I8x16Neg),
630 I8x16Eq => Some(WasmOp::I8x16Eq),
631 I8x16Ne => Some(WasmOp::I8x16Ne),
632 I8x16LtS => Some(WasmOp::I8x16LtS),
633 I8x16LtU => Some(WasmOp::I8x16LtU),
634 I8x16GtS => Some(WasmOp::I8x16GtS),
635 I8x16GtU => Some(WasmOp::I8x16GtU),
636 I8x16LeS => Some(WasmOp::I8x16LeS),
637 I8x16LeU => Some(WasmOp::I8x16LeU),
638 I8x16GeS => Some(WasmOp::I8x16GeS),
639 I8x16GeU => Some(WasmOp::I8x16GeU),
640 I8x16Splat => Some(WasmOp::I8x16Splat),
641 I8x16ExtractLaneS { lane } => Some(WasmOp::I8x16ExtractLaneS(*lane)),
642 I8x16ExtractLaneU { lane } => Some(WasmOp::I8x16ExtractLaneU(*lane)),
643 I8x16ReplaceLane { lane } => Some(WasmOp::I8x16ReplaceLane(*lane)),
644 I8x16Shuffle { lanes } => Some(WasmOp::I8x16Shuffle(*lanes)),
645 I8x16Swizzle => Some(WasmOp::I8x16Swizzle),
646
647 I16x8Add => Some(WasmOp::I16x8Add),
649 I16x8Sub => Some(WasmOp::I16x8Sub),
650 I16x8Mul => Some(WasmOp::I16x8Mul),
651 I16x8Neg => Some(WasmOp::I16x8Neg),
652 I16x8Eq => Some(WasmOp::I16x8Eq),
653 I16x8Ne => Some(WasmOp::I16x8Ne),
654 I16x8LtS => Some(WasmOp::I16x8LtS),
655 I16x8LtU => Some(WasmOp::I16x8LtU),
656 I16x8GtS => Some(WasmOp::I16x8GtS),
657 I16x8GtU => Some(WasmOp::I16x8GtU),
658 I16x8LeS => Some(WasmOp::I16x8LeS),
659 I16x8LeU => Some(WasmOp::I16x8LeU),
660 I16x8GeS => Some(WasmOp::I16x8GeS),
661 I16x8GeU => Some(WasmOp::I16x8GeU),
662 I16x8Splat => Some(WasmOp::I16x8Splat),
663 I16x8ExtractLaneS { lane } => Some(WasmOp::I16x8ExtractLaneS(*lane)),
664 I16x8ExtractLaneU { lane } => Some(WasmOp::I16x8ExtractLaneU(*lane)),
665 I16x8ReplaceLane { lane } => Some(WasmOp::I16x8ReplaceLane(*lane)),
666
667 I32x4Add => Some(WasmOp::I32x4Add),
669 I32x4Sub => Some(WasmOp::I32x4Sub),
670 I32x4Mul => Some(WasmOp::I32x4Mul),
671 I32x4Neg => Some(WasmOp::I32x4Neg),
672 I32x4Eq => Some(WasmOp::I32x4Eq),
673 I32x4Ne => Some(WasmOp::I32x4Ne),
674 I32x4LtS => Some(WasmOp::I32x4LtS),
675 I32x4LtU => Some(WasmOp::I32x4LtU),
676 I32x4GtS => Some(WasmOp::I32x4GtS),
677 I32x4GtU => Some(WasmOp::I32x4GtU),
678 I32x4LeS => Some(WasmOp::I32x4LeS),
679 I32x4LeU => Some(WasmOp::I32x4LeU),
680 I32x4GeS => Some(WasmOp::I32x4GeS),
681 I32x4GeU => Some(WasmOp::I32x4GeU),
682 I32x4Splat => Some(WasmOp::I32x4Splat),
683 I32x4ExtractLane { lane } => Some(WasmOp::I32x4ExtractLane(*lane)),
684 I32x4ReplaceLane { lane } => Some(WasmOp::I32x4ReplaceLane(*lane)),
685
686 I64x2Add => Some(WasmOp::I64x2Add),
688 I64x2Sub => Some(WasmOp::I64x2Sub),
689 I64x2Mul => Some(WasmOp::I64x2Mul),
690 I64x2Neg => Some(WasmOp::I64x2Neg),
691 I64x2Eq => Some(WasmOp::I64x2Eq),
692 I64x2Ne => Some(WasmOp::I64x2Ne),
693 I64x2LtS => Some(WasmOp::I64x2LtS),
694 I64x2GtS => Some(WasmOp::I64x2GtS),
695 I64x2LeS => Some(WasmOp::I64x2LeS),
696 I64x2GeS => Some(WasmOp::I64x2GeS),
697 I64x2Splat => Some(WasmOp::I64x2Splat),
698 I64x2ExtractLane { lane } => Some(WasmOp::I64x2ExtractLane(*lane)),
699 I64x2ReplaceLane { lane } => Some(WasmOp::I64x2ReplaceLane(*lane)),
700
701 F32x4Add => Some(WasmOp::F32x4Add),
703 F32x4Sub => Some(WasmOp::F32x4Sub),
704 F32x4Mul => Some(WasmOp::F32x4Mul),
705 F32x4Div => Some(WasmOp::F32x4Div),
706 F32x4Abs => Some(WasmOp::F32x4Abs),
707 F32x4Neg => Some(WasmOp::F32x4Neg),
708 F32x4Sqrt => Some(WasmOp::F32x4Sqrt),
709 F32x4Eq => Some(WasmOp::F32x4Eq),
710 F32x4Ne => Some(WasmOp::F32x4Ne),
711 F32x4Lt => Some(WasmOp::F32x4Lt),
712 F32x4Le => Some(WasmOp::F32x4Le),
713 F32x4Gt => Some(WasmOp::F32x4Gt),
714 F32x4Ge => Some(WasmOp::F32x4Ge),
715 F32x4Splat => Some(WasmOp::F32x4Splat),
716 F32x4ExtractLane { lane } => Some(WasmOp::F32x4ExtractLane(*lane)),
717 F32x4ReplaceLane { lane } => Some(WasmOp::F32x4ReplaceLane(*lane)),
718
719 _ => None,
721 }
722}
723
724#[cfg(test)]
725mod tests {
726 use super::*;
727
728 #[test]
729 fn test_decode_simple_add() {
730 let wat = r#"
731 (module
732 (func (export "add") (param i32 i32) (result i32)
733 local.get 0
734 local.get 1
735 i32.add
736 )
737 )
738 "#;
739
740 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
741 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
742
743 assert_eq!(functions.len(), 1);
744 assert_eq!(functions[0].index, 0);
745 assert_eq!(functions[0].export_name, Some("add".to_string()));
746 assert_eq!(
747 functions[0].ops,
748 vec![
749 WasmOp::LocalGet(0),
750 WasmOp::LocalGet(1),
751 WasmOp::I32Add,
752 WasmOp::End
753 ]
754 );
755 }
756
757 #[test]
763 fn test_decode_i64_i32_width_conversions() {
764 let wat = r#"
765 (module
766 (func (export "conv") (param i32 i64) (result i32)
767 local.get 0
768 i64.extend_i32_u
769 local.get 0
770 i64.extend_i32_s
771 i64.add
772 local.get 1
773 i64.add
774 i32.wrap_i64
775 )
776 )
777 "#;
778 let wasm = wat::parse_str(wat).expect("parse");
779 let functions = decode_wasm_functions(&wasm).expect("decode");
780 let ops = &functions[0].ops;
781 assert!(
782 ops.contains(&WasmOp::I64ExtendI32U),
783 "i64.extend_i32_u must decode (not be dropped): {ops:?}"
784 );
785 assert!(
786 ops.contains(&WasmOp::I64ExtendI32S),
787 "i64.extend_i32_s must decode (not be dropped): {ops:?}"
788 );
789 assert!(
790 ops.contains(&WasmOp::I32WrapI64),
791 "i32.wrap_i64 must decode (not be dropped): {ops:?}"
792 );
793 }
794
795 #[test]
800 fn test_decode_br_table() {
801 let wat = r#"
802 (module
803 (func (export "bt") (param i32) (result i32)
804 (block (block (block
805 local.get 0
806 br_table 2 0 1 2)
807 i32.const 30 return)
808 i32.const 20 return)
809 i32.const 10))
810 "#;
811 let wasm = wat::parse_str(wat).expect("parse");
812 let functions = decode_wasm_functions(&wasm).expect("decode");
813 let bt = functions[0]
814 .ops
815 .iter()
816 .find_map(|o| match o {
817 WasmOp::BrTable { targets, default } => Some((targets.clone(), *default)),
818 _ => None,
819 })
820 .expect("br_table must decode (not be dropped)");
821 assert_eq!(bt.0, vec![2, 0, 1], "br_table targets preserved in order");
822 assert_eq!(bt.1, 2, "br_table default preserved");
823 }
824
825 #[test]
826 fn test_decode_arithmetic() {
827 let wat = r#"
828 (module
829 (func (export "calc") (result i32)
830 i32.const 5
831 i32.const 3
832 i32.mul
833 i32.const 2
834 i32.add
835 )
836 )
837 "#;
838
839 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
840 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
841
842 assert_eq!(functions.len(), 1);
843 assert_eq!(functions[0].export_name, Some("calc".to_string()));
844 assert_eq!(
845 functions[0].ops,
846 vec![
847 WasmOp::I32Const(5),
848 WasmOp::I32Const(3),
849 WasmOp::I32Mul,
850 WasmOp::I32Const(2),
851 WasmOp::I32Add,
852 WasmOp::End,
853 ]
854 );
855 }
856
857 #[test]
858 fn test_decode_multi_function_module() {
859 let wat = r#"
860 (module
861 (func $helper)
862 (func (export "add") (param i32 i32) (result i32)
863 local.get 0
864 local.get 1
865 i32.add
866 )
867 (func (export "sub") (param i32 i32) (result i32)
868 local.get 0
869 local.get 1
870 i32.sub
871 )
872 )
873 "#;
874
875 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
876 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
877
878 assert_eq!(functions.len(), 3);
879 assert_eq!(functions[0].index, 0);
880 assert_eq!(functions[0].export_name, None);
881 assert_eq!(functions[1].index, 1);
882 assert_eq!(functions[1].export_name, Some("add".to_string()));
883 assert_eq!(functions[2].index, 2);
884 assert_eq!(functions[2].export_name, Some("sub".to_string()));
885 }
886
887 #[test]
888 fn test_decode_module_with_imports() {
889 let wat = r#"
890 (module
891 (import "env" "log" (func $log (param i32)))
892 (import "env" "memory" (memory 1))
893 (func (export "run") (param i32)
894 local.get 0
895 call 0
896 )
897 )
898 "#;
899
900 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
901 let module = decode_wasm_module(&wasm).expect("Failed to decode");
902
903 assert_eq!(module.imports.len(), 2);
905 assert_eq!(module.num_imported_funcs, 1);
906
907 assert_eq!(module.imports[0].module, "env");
909 assert_eq!(module.imports[0].name, "log");
910 assert!(matches!(module.imports[0].kind, ImportKind::Function(_)));
911
912 assert_eq!(module.imports[1].module, "env");
914 assert_eq!(module.imports[1].name, "memory");
915 assert_eq!(module.imports[1].kind, ImportKind::Memory);
916
917 assert_eq!(module.functions.len(), 1);
919 assert_eq!(module.functions[0].index, 1);
920 assert_eq!(module.functions[0].export_name, Some("run".to_string()));
921 }
922
923 #[test]
924 fn test_find_function_by_export_name() {
925 let wat = r#"
926 (module
927 (func $helper)
928 (func (export "add") (param i32 i32) (result i32)
929 local.get 0
930 local.get 1
931 i32.add
932 )
933 )
934 "#;
935
936 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
937 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
938
939 let add_func = functions
940 .iter()
941 .find(|f| f.export_name.as_deref() == Some("add"))
942 .expect("Should find 'add' function");
943
944 assert_eq!(add_func.index, 1);
945 assert!(add_func.ops.contains(&WasmOp::I32Add));
946 }
947
948 #[test]
949 fn test_decode_subword_loads() {
950 let wat = r#"
951 (module
952 (memory 1)
953 (func (export "test") (param i32) (result i32)
954 local.get 0
955 i32.load8_u
956 )
957 )
958 "#;
959
960 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
961 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
962
963 assert_eq!(functions.len(), 1);
964 assert!(functions[0].ops.contains(&WasmOp::I32Load8U {
965 offset: 0,
966 align: 0,
967 }));
968 }
969
970 #[test]
971 fn test_decode_subword_stores() {
972 let wat = r#"
973 (module
974 (memory 1)
975 (func (export "test") (param i32 i32)
976 local.get 0
977 local.get 1
978 i32.store8
979 )
980 )
981 "#;
982
983 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
984 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
985
986 assert_eq!(functions.len(), 1);
987 assert!(functions[0].ops.contains(&WasmOp::I32Store8 {
988 offset: 0,
989 align: 0,
990 }));
991 }
992
993 #[test]
994 fn test_decode_memory_size_grow() {
995 let wat = r#"
996 (module
997 (memory 1)
998 (func (export "test") (result i32)
999 memory.size
1000 )
1001 )
1002 "#;
1003
1004 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1005 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1006
1007 assert_eq!(functions.len(), 1);
1008 assert!(functions[0].ops.contains(&WasmOp::MemorySize(0)));
1009 }
1010
1011 #[test]
1012 fn test_decode_memory_grow() {
1013 let wat = r#"
1014 (module
1015 (memory 1)
1016 (func (export "test") (param i32) (result i32)
1017 local.get 0
1018 memory.grow
1019 )
1020 )
1021 "#;
1022
1023 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1024 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1025
1026 assert_eq!(functions.len(), 1);
1027 assert!(functions[0].ops.contains(&WasmOp::MemoryGrow(0)));
1028 }
1029
1030 #[test]
1031 fn test_decode_i64_subword_loads() {
1032 let wat = r#"
1033 (module
1034 (memory 1)
1035 (func (export "test") (param i32) (result i64)
1036 local.get 0
1037 i64.load8_s
1038 )
1039 )
1040 "#;
1041
1042 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1043 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1044
1045 assert_eq!(functions.len(), 1);
1046 assert!(functions[0].ops.contains(&WasmOp::I64Load8S {
1047 offset: 0,
1048 align: 0,
1049 }));
1050 }
1051
1052 #[test]
1053 fn test_decode_all_subword_memory_ops() {
1054 let wat = r#"
1056 (module
1057 (memory 1)
1058 (func (export "test") (param i32)
1059 ;; i32 sub-word loads
1060 local.get 0
1061 i32.load8_s
1062 drop
1063 local.get 0
1064 i32.load8_u
1065 drop
1066 local.get 0
1067 i32.load16_s
1068 drop
1069 local.get 0
1070 i32.load16_u
1071 drop
1072
1073 ;; i32 sub-word stores
1074 local.get 0
1075 i32.const 42
1076 i32.store8
1077 local.get 0
1078 i32.const 42
1079 i32.store16
1080
1081 ;; i64 sub-word loads
1082 local.get 0
1083 i64.load8_s
1084 drop
1085 local.get 0
1086 i64.load8_u
1087 drop
1088 local.get 0
1089 i64.load16_s
1090 drop
1091 local.get 0
1092 i64.load16_u
1093 drop
1094 local.get 0
1095 i64.load32_s
1096 drop
1097 local.get 0
1098 i64.load32_u
1099 drop
1100
1101 ;; i64 sub-word stores
1102 local.get 0
1103 i64.const 42
1104 i64.store8
1105 local.get 0
1106 i64.const 42
1107 i64.store16
1108 local.get 0
1109 i64.const 42
1110 i64.store32
1111 )
1112 )
1113 "#;
1114
1115 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1116 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1117
1118 assert_eq!(functions.len(), 1);
1119 let ops = &functions[0].ops;
1120
1121 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load8S { .. })));
1123 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load8U { .. })));
1124 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load16S { .. })));
1125 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load16U { .. })));
1126 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Store8 { .. })));
1127 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Store16 { .. })));
1128
1129 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load8S { .. })));
1131 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load8U { .. })));
1132 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load16S { .. })));
1133 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load16U { .. })));
1134 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load32S { .. })));
1135 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load32U { .. })));
1136 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store8 { .. })));
1137 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store16 { .. })));
1138 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store32 { .. })));
1139 }
1140
1141 #[test]
1142 fn test_decode_simd_i32x4_add() {
1143 let wat = r#"
1144 (module
1145 (func (export "add_v128") (param v128 v128) (result v128)
1146 local.get 0
1147 local.get 1
1148 i32x4.add
1149 )
1150 )
1151 "#;
1152
1153 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1154 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1155
1156 assert_eq!(functions.len(), 1);
1157 assert!(
1158 functions[0].ops.contains(&WasmOp::I32x4Add),
1159 "Should decode i32x4.add: {:?}",
1160 functions[0].ops
1161 );
1162 }
1163
1164 #[test]
1165 fn test_decode_simd_v128_const() {
1166 let wat = r#"
1167 (module
1168 (func (export "const_v128") (result v128)
1169 v128.const i32x4 1 2 3 4
1170 )
1171 )
1172 "#;
1173
1174 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1175 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1176
1177 assert_eq!(functions.len(), 1);
1178 assert!(
1179 functions[0]
1180 .ops
1181 .iter()
1182 .any(|o| matches!(o, WasmOp::V128Const(_))),
1183 "Should decode v128.const: {:?}",
1184 functions[0].ops
1185 );
1186 }
1187
1188 #[test]
1189 fn test_decode_simd_v128_load_store() {
1190 let wat = r#"
1191 (module
1192 (memory 1)
1193 (func (export "load_store") (param i32)
1194 local.get 0
1195 v128.load
1196 local.get 0
1197 v128.store
1198 )
1199 )
1200 "#;
1201
1202 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1203 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1204
1205 assert_eq!(functions.len(), 1);
1206 let ops = &functions[0].ops;
1207 assert!(
1208 ops.iter().any(|o| matches!(o, WasmOp::V128Load { .. })),
1209 "Should decode v128.load"
1210 );
1211 assert!(
1212 ops.iter().any(|o| matches!(o, WasmOp::V128Store { .. })),
1213 "Should decode v128.store"
1214 );
1215 }
1216
1217 #[test]
1218 fn test_decode_simd_bitwise_ops() {
1219 let wat = r#"
1220 (module
1221 (func (export "bitwise") (param v128 v128) (result v128)
1222 local.get 0
1223 local.get 1
1224 v128.and
1225 )
1226 )
1227 "#;
1228
1229 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1230 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1231
1232 assert_eq!(functions.len(), 1);
1233 assert!(functions[0].ops.contains(&WasmOp::V128And));
1234 }
1235
1236 #[test]
1237 fn test_decode_simd_splat() {
1238 let wat = r#"
1239 (module
1240 (func (export "splat") (param i32) (result v128)
1241 local.get 0
1242 i32x4.splat
1243 )
1244 )
1245 "#;
1246
1247 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1248 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1249
1250 assert_eq!(functions.len(), 1);
1251 assert!(functions[0].ops.contains(&WasmOp::I32x4Splat));
1252 }
1253
1254 #[test]
1255 fn test_decode_simd_extract_lane() {
1256 let wat = r#"
1257 (module
1258 (func (export "extract") (param v128) (result i32)
1259 local.get 0
1260 i32x4.extract_lane 2
1261 )
1262 )
1263 "#;
1264
1265 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1266 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1267
1268 assert_eq!(functions.len(), 1);
1269 assert!(
1270 functions[0].ops.contains(&WasmOp::I32x4ExtractLane(2)),
1271 "Should decode i32x4.extract_lane 2"
1272 );
1273 }
1274
1275 #[test]
1276 fn test_decode_simd_f32x4_arithmetic() {
1277 let wat = r#"
1278 (module
1279 (func (export "f32x4_add") (param v128 v128) (result v128)
1280 local.get 0
1281 local.get 1
1282 f32x4.add
1283 )
1284 )
1285 "#;
1286
1287 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1288 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1289
1290 assert_eq!(functions.len(), 1);
1291 assert!(functions[0].ops.contains(&WasmOp::F32x4Add));
1292 }
1293
1294 #[test]
1295 fn test_decode_simd_multiple_ops() {
1296 let wat = r#"
1297 (module
1298 (func (export "simd_ops") (param v128 v128 v128) (result v128)
1299 ;; (a + b) * c
1300 local.get 0
1301 local.get 1
1302 i32x4.add
1303 local.get 2
1304 i32x4.mul
1305 )
1306 )
1307 "#;
1308
1309 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1310 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1311
1312 assert_eq!(functions.len(), 1);
1313 let ops = &functions[0].ops;
1314 assert!(ops.contains(&WasmOp::I32x4Add));
1315 assert!(ops.contains(&WasmOp::I32x4Mul));
1316 }
1317
1318 #[test]
1321 fn test_decode_captures_global_initializer() {
1322 let wat = r#"
1323 (module
1324 (memory 2)
1325 (global $__stack_pointer (mut i32) (i32.const 65536))
1326 (global $immutable_const i32 (i32.const 7))
1327 (func (export "f") (result i32) global.get 0)
1328 )
1329 "#;
1330 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1331 let module = decode_wasm_module(&wasm).expect("Failed to decode");
1332
1333 assert_eq!(module.globals.len(), 2, "both globals captured");
1334 let sp = &module.globals[0];
1335 assert_eq!(sp.index, 0);
1336 assert_eq!(sp.init_i32, Some(65536), "stack-pointer init captured");
1337 assert!(sp.mutable, "stack pointer is mutable");
1338 let c = &module.globals[1];
1339 assert_eq!(c.init_i32, Some(7));
1340 assert!(!c.mutable, "second global is immutable");
1341 }
1342}