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