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 func_params_i64: Vec<Vec<bool>>,
111 pub globals: Vec<WasmGlobal>,
116 pub elem_func_indices: Vec<u32>,
123}
124
125pub fn decode_wasm_module(wasm_bytes: &[u8]) -> Result<DecodedModule> {
127 let mut functions = Vec::new();
128 let mut memories = Vec::new();
129 let mut data_segments = Vec::new();
130 let mut globals: Vec<WasmGlobal> = Vec::new();
131 let mut imports = Vec::new();
132 let mut func_index = 0u32;
133 let mut num_imported_funcs = 0u32;
134 let mut export_names: HashMap<u32, String> = HashMap::new();
135 let mut type_arg_counts: Vec<u32> = Vec::new();
138 let mut func_arg_counts: Vec<u32> = Vec::new();
139 let mut type_ret_i64: Vec<bool> = Vec::new();
140 let mut func_ret_i64: Vec<bool> = Vec::new();
141 let mut type_params_i64: Vec<Vec<bool>> = Vec::new();
143 let mut func_params_i64: Vec<Vec<bool>> = Vec::new();
144 let mut elem_func_indices: Vec<u32> = Vec::new();
145
146 for payload in Parser::new(0).parse_all(wasm_bytes) {
147 let payload = payload.context("Failed to parse WASM payload")?;
148
149 match payload {
150 Payload::TypeSection(reader) => {
151 for rec_group in reader {
154 let rec_group = rec_group.context("Failed to parse type")?;
155 for sub_ty in rec_group.types() {
156 let (count, ret_i64, params_i64) = match &sub_ty.composite_type.inner {
157 wasmparser::CompositeInnerType::Func(func_ty) => (
158 func_ty.params().len() as u32,
159 func_ty
160 .results()
161 .first()
162 .is_some_and(|t| *t == wasmparser::ValType::I64),
163 func_ty
168 .params()
169 .iter()
170 .map(|t| {
171 matches!(
172 t,
173 wasmparser::ValType::I64 | wasmparser::ValType::F64
174 )
175 })
176 .collect::<Vec<bool>>(),
177 ),
178 _ => (0, false, Vec::new()),
179 };
180 type_arg_counts.push(count);
181 type_ret_i64.push(ret_i64);
182 type_params_i64.push(params_i64);
183 }
184 }
185 }
186 Payload::ImportSection(reader) => {
187 for import in reader.into_imports() {
193 let import = import.context("Failed to parse import")?;
194 let (kind, idx) = match import.ty {
195 wasmparser::TypeRef::Func(type_idx) => {
196 let idx = num_imported_funcs;
197 num_imported_funcs += 1;
198 func_arg_counts
201 .push(type_arg_counts.get(type_idx as usize).copied().unwrap_or(0));
202 func_ret_i64.push(
203 type_ret_i64
204 .get(type_idx as usize)
205 .copied()
206 .unwrap_or(false),
207 );
208 func_params_i64.push(
209 type_params_i64
210 .get(type_idx as usize)
211 .cloned()
212 .unwrap_or_default(),
213 );
214 (ImportKind::Function(type_idx), idx)
215 }
216 wasmparser::TypeRef::Memory(_) => (ImportKind::Memory, 0),
217 wasmparser::TypeRef::Table(_) => (ImportKind::Table, 0),
218 wasmparser::TypeRef::Global(_) => (ImportKind::Global, 0),
219 _ => continue,
220 };
221 imports.push(ImportEntry {
222 module: import.module.to_string(),
223 name: import.name.to_string(),
224 kind,
225 index: idx,
226 });
227 }
228 }
229 Payload::FunctionSection(reader) => {
230 for ty in reader {
235 let type_idx = ty.context("Failed to parse function type index")?;
236 func_arg_counts
237 .push(type_arg_counts.get(type_idx as usize).copied().unwrap_or(0));
238 func_ret_i64.push(
239 type_ret_i64
240 .get(type_idx as usize)
241 .copied()
242 .unwrap_or(false),
243 );
244 func_params_i64.push(
245 type_params_i64
246 .get(type_idx as usize)
247 .cloned()
248 .unwrap_or_default(),
249 );
250 }
251 }
252 Payload::MemorySection(reader) => {
253 for (idx, memory) in reader.into_iter().enumerate() {
254 let mem = memory.context("Failed to parse memory")?;
255 memories.push(WasmMemory {
256 index: idx as u32,
257 initial_pages: mem.initial as u32,
258 max_pages: mem.maximum.map(|m| m as u32),
259 shared: mem.shared,
260 });
261 }
262 }
263 Payload::GlobalSection(reader) => {
264 for (idx, global) in reader.into_iter().enumerate() {
270 let global = global.context("Failed to parse global")?;
271 let mut init_i32 = None;
272 let mut ops = global.init_expr.get_operators_reader();
273 if let Ok(wasmparser::Operator::I32Const { value }) = ops.read() {
274 init_i32 = Some(value);
275 }
276 globals.push(WasmGlobal {
277 index: idx as u32,
278 init_i32,
279 mutable: global.ty.mutable,
280 });
281 }
282 }
283 Payload::DataSection(reader) => {
284 for data in reader {
285 let data = data.context("Failed to parse data segment")?;
286 if let wasmparser::DataKind::Active {
287 memory_index: 0,
288 offset_expr,
289 } = data.kind
290 {
291 let mut ops = offset_expr.get_operators_reader();
292 if let Ok(wasmparser::Operator::I32Const { value }) = ops.read() {
293 data_segments.push((value as u32, data.data.to_vec()));
294 }
295 }
296 }
297 }
298 Payload::ElementSection(reader) => {
299 for elem in reader {
306 let elem = elem.context("Failed to parse element segment")?;
307 match elem.items {
308 wasmparser::ElementItems::Functions(funcs) => {
309 for f in funcs {
310 elem_func_indices
311 .push(f.context("Failed to parse element func index")?);
312 }
313 }
314 wasmparser::ElementItems::Expressions(_, exprs) => {
315 for expr in exprs {
316 let expr = expr.context("Failed to parse element expr")?;
317 for op in expr.get_operators_reader() {
318 if let wasmparser::Operator::RefFunc { function_index } =
319 op.context("Failed to parse element op")?
320 {
321 elem_func_indices.push(function_index);
322 }
323 }
324 }
325 }
326 }
327 }
328 }
329 Payload::ExportSection(exports) => {
330 for export in exports {
331 let export = export.context("Failed to parse export")?;
332 if export.kind == ExternalKind::Func {
333 export_names.insert(export.index, export.name.to_string());
334 }
335 }
336 }
337 Payload::CodeSectionEntry(body) => {
338 let (ops, unsupported) = decode_function_body(&body)?;
339 let actual_index = num_imported_funcs + func_index;
340 let export_name = export_names.get(&actual_index).cloned();
341
342 functions.push(FunctionOps {
343 index: actual_index,
344 export_name,
345 ops,
346 unsupported,
347 });
348 func_index += 1;
349 }
350 _ => {}
351 }
352 }
353
354 Ok(DecodedModule {
355 functions,
356 memories,
357 data_segments,
358 imports,
359 num_imported_funcs,
360 func_arg_counts,
361 type_arg_counts,
362 func_ret_i64,
363 type_ret_i64,
364 func_params_i64,
365 globals,
366 elem_func_indices,
367 })
368}
369
370pub fn decode_wasm_functions(wasm_bytes: &[u8]) -> Result<Vec<FunctionOps>> {
372 let mut functions = Vec::new();
373 let mut func_index = 0u32;
374 let mut num_imported_funcs = 0u32;
375 let mut export_names: HashMap<u32, String> = HashMap::new();
376
377 for payload in Parser::new(0).parse_all(wasm_bytes) {
378 let payload = payload.context("Failed to parse WASM payload")?;
379
380 match payload {
381 Payload::ImportSection(imports) => {
382 for import in imports.into_imports() {
385 let import = import.context("Failed to parse import")?;
386 if matches!(import.ty, wasmparser::TypeRef::Func(_)) {
387 num_imported_funcs += 1;
388 }
389 }
390 }
391 Payload::ExportSection(exports) => {
392 for export in exports {
393 let export = export.context("Failed to parse export")?;
394 if export.kind == ExternalKind::Func {
395 export_names.insert(export.index, export.name.to_string());
396 }
397 }
398 }
399 Payload::CodeSectionEntry(body) => {
400 let (ops, unsupported) = decode_function_body(&body)?;
401 let actual_index = num_imported_funcs + func_index;
402 let export_name = export_names.get(&actual_index).cloned();
403
404 functions.push(FunctionOps {
405 index: actual_index,
406 export_name,
407 ops,
408 unsupported,
409 });
410 func_index += 1;
411 }
412 _ => {}
413 }
414 }
415
416 Ok(functions)
417}
418
419#[derive(Debug, Clone)]
421pub struct FunctionOps {
422 pub index: u32,
424 pub export_name: Option<String>,
426 pub ops: Vec<WasmOp>,
428 pub unsupported: Option<String>,
438}
439
440fn decode_function_body(body: &wasmparser::FunctionBody) -> Result<(Vec<WasmOp>, Option<String>)> {
446 let mut ops = Vec::new();
447 let mut unsupported: Option<String> = None;
448
449 let ops_reader = body.get_operators_reader()?;
450 for op_result in ops_reader {
451 let op = op_result.context("Failed to read operator")?;
452
453 if let Some(wasm_op) = convert_operator(&op) {
454 ops.push(wasm_op);
455 } else if unsupported.is_none() && !is_intentionally_ignored(&op) {
456 unsupported = Some(format!("{op:?}"));
460 }
461 }
462
463 Ok((ops, unsupported))
464}
465
466fn is_intentionally_ignored(op: &wasmparser::Operator) -> bool {
471 use wasmparser::Operator::*;
472 matches!(op, Nop | Unreachable)
473}
474
475fn convert_operator(op: &wasmparser::Operator) -> Option<WasmOp> {
477 use wasmparser::Operator::*;
478
479 match op {
480 I32Const { value } => Some(WasmOp::I32Const(*value)),
482
483 I32Add => Some(WasmOp::I32Add),
485 I32Sub => Some(WasmOp::I32Sub),
486 I32Mul => Some(WasmOp::I32Mul),
487 I32DivS => Some(WasmOp::I32DivS),
488 I32DivU => Some(WasmOp::I32DivU),
489 I32RemS => Some(WasmOp::I32RemS),
490 I32RemU => Some(WasmOp::I32RemU),
491
492 I64Const { value } => Some(WasmOp::I64Const(*value)),
494
495 I64Add => Some(WasmOp::I64Add),
497 I64Sub => Some(WasmOp::I64Sub),
498 I64Mul => Some(WasmOp::I64Mul),
499 I64DivS => Some(WasmOp::I64DivS),
500 I64DivU => Some(WasmOp::I64DivU),
501 I64RemS => Some(WasmOp::I64RemS),
502 I64RemU => Some(WasmOp::I64RemU),
503
504 I64And => Some(WasmOp::I64And),
506 I64Or => Some(WasmOp::I64Or),
507 I64Xor => Some(WasmOp::I64Xor),
508 I64Shl => Some(WasmOp::I64Shl),
509 I64ShrS => Some(WasmOp::I64ShrS),
510 I64ShrU => Some(WasmOp::I64ShrU),
511 I64Rotl => Some(WasmOp::I64Rotl),
512 I64Rotr => Some(WasmOp::I64Rotr),
513 I64Clz => Some(WasmOp::I64Clz),
514 I64Ctz => Some(WasmOp::I64Ctz),
515 I64Popcnt => Some(WasmOp::I64Popcnt),
516 I64Extend8S => Some(WasmOp::I64Extend8S),
517 I64Extend16S => Some(WasmOp::I64Extend16S),
518 I64Extend32S => Some(WasmOp::I64Extend32S),
519 I64ExtendI32U => Some(WasmOp::I64ExtendI32U),
525 I64ExtendI32S => Some(WasmOp::I64ExtendI32S),
526 I32WrapI64 => Some(WasmOp::I32WrapI64),
527
528 I64Eqz => Some(WasmOp::I64Eqz),
530 I64Eq => Some(WasmOp::I64Eq),
531 I64Ne => Some(WasmOp::I64Ne),
532 I64LtS => Some(WasmOp::I64LtS),
533 I64LtU => Some(WasmOp::I64LtU),
534 I64LeS => Some(WasmOp::I64LeS),
535 I64LeU => Some(WasmOp::I64LeU),
536 I64GtS => Some(WasmOp::I64GtS),
537 I64GtU => Some(WasmOp::I64GtU),
538 I64GeS => Some(WasmOp::I64GeS),
539 I64GeU => Some(WasmOp::I64GeU),
540
541 I32And => Some(WasmOp::I32And),
543 I32Or => Some(WasmOp::I32Or),
544 I32Xor => Some(WasmOp::I32Xor),
545 I32Shl => Some(WasmOp::I32Shl),
546 I32ShrS => Some(WasmOp::I32ShrS),
547 I32ShrU => Some(WasmOp::I32ShrU),
548 I32Rotl => Some(WasmOp::I32Rotl),
549 I32Rotr => Some(WasmOp::I32Rotr),
550 I32Clz => Some(WasmOp::I32Clz),
551 I32Ctz => Some(WasmOp::I32Ctz),
552 I32Popcnt => Some(WasmOp::I32Popcnt),
553 I32Extend8S => Some(WasmOp::I32Extend8S),
554 I32Extend16S => Some(WasmOp::I32Extend16S),
555
556 I32Eqz => Some(WasmOp::I32Eqz),
558 I32Eq => Some(WasmOp::I32Eq),
559 I32Ne => Some(WasmOp::I32Ne),
560 I32LtS => Some(WasmOp::I32LtS),
561 I32LtU => Some(WasmOp::I32LtU),
562 I32LeS => Some(WasmOp::I32LeS),
563 I32LeU => Some(WasmOp::I32LeU),
564 I32GtS => Some(WasmOp::I32GtS),
565 I32GtU => Some(WasmOp::I32GtU),
566 I32GeS => Some(WasmOp::I32GeS),
567 I32GeU => Some(WasmOp::I32GeU),
568
569 I32Load { memarg } => Some(WasmOp::I32Load {
571 offset: memarg.offset as u32,
572 align: memarg.align as u32,
573 }),
574 I32Store { memarg } => Some(WasmOp::I32Store {
575 offset: memarg.offset as u32,
576 align: memarg.align as u32,
577 }),
578 I64Load { memarg } => Some(WasmOp::I64Load {
585 offset: memarg.offset as u32,
586 align: memarg.align as u32,
587 }),
588 I64Store { memarg } => Some(WasmOp::I64Store {
589 offset: memarg.offset as u32,
590 align: memarg.align as u32,
591 }),
592
593 I32Load8S { memarg } => Some(WasmOp::I32Load8S {
595 offset: memarg.offset as u32,
596 align: memarg.align as u32,
597 }),
598 I32Load8U { memarg } => Some(WasmOp::I32Load8U {
599 offset: memarg.offset as u32,
600 align: memarg.align as u32,
601 }),
602 I32Load16S { memarg } => Some(WasmOp::I32Load16S {
603 offset: memarg.offset as u32,
604 align: memarg.align as u32,
605 }),
606 I32Load16U { memarg } => Some(WasmOp::I32Load16U {
607 offset: memarg.offset as u32,
608 align: memarg.align as u32,
609 }),
610
611 I32Store8 { memarg } => Some(WasmOp::I32Store8 {
613 offset: memarg.offset as u32,
614 align: memarg.align as u32,
615 }),
616 I32Store16 { memarg } => Some(WasmOp::I32Store16 {
617 offset: memarg.offset as u32,
618 align: memarg.align as u32,
619 }),
620
621 LocalGet { local_index } => Some(WasmOp::LocalGet(*local_index)),
623 LocalSet { local_index } => Some(WasmOp::LocalSet(*local_index)),
624 LocalTee { local_index } => Some(WasmOp::LocalTee(*local_index)),
625 GlobalGet { global_index } => Some(WasmOp::GlobalGet(*global_index)),
626 GlobalSet { global_index } => Some(WasmOp::GlobalSet(*global_index)),
627
628 Block { .. } => Some(WasmOp::Block),
630 Loop { .. } => Some(WasmOp::Loop),
631 Br { relative_depth } => Some(WasmOp::Br(*relative_depth)),
632 BrIf { relative_depth } => Some(WasmOp::BrIf(*relative_depth)),
633 BrTable { targets } => {
639 let default = targets.default();
640 let tgts: Vec<u32> = targets.targets().filter_map(Result::ok).collect();
641 Some(WasmOp::BrTable {
642 targets: tgts,
643 default,
644 })
645 }
646 Return => Some(WasmOp::Return),
647 Call { function_index } => Some(WasmOp::Call(*function_index)),
648 CallIndirect {
649 type_index,
650 table_index,
651 ..
652 } => Some(WasmOp::CallIndirect {
653 type_index: *type_index,
654 table_index: *table_index,
655 }),
656
657 End => Some(WasmOp::End),
659
660 Nop | Unreachable => None,
662
663 Drop => Some(WasmOp::Drop),
665
666 Select => Some(WasmOp::Select),
668
669 If { .. } => Some(WasmOp::If),
671 Else => Some(WasmOp::Else),
672
673 I64Load8S { memarg } => Some(WasmOp::I64Load8S {
675 offset: memarg.offset as u32,
676 align: memarg.align as u32,
677 }),
678 I64Load8U { memarg } => Some(WasmOp::I64Load8U {
679 offset: memarg.offset as u32,
680 align: memarg.align as u32,
681 }),
682 I64Load16S { memarg } => Some(WasmOp::I64Load16S {
683 offset: memarg.offset as u32,
684 align: memarg.align as u32,
685 }),
686 I64Load16U { memarg } => Some(WasmOp::I64Load16U {
687 offset: memarg.offset as u32,
688 align: memarg.align as u32,
689 }),
690 I64Load32S { memarg } => Some(WasmOp::I64Load32S {
691 offset: memarg.offset as u32,
692 align: memarg.align as u32,
693 }),
694 I64Load32U { memarg } => Some(WasmOp::I64Load32U {
695 offset: memarg.offset as u32,
696 align: memarg.align as u32,
697 }),
698
699 I64Store8 { memarg } => Some(WasmOp::I64Store8 {
701 offset: memarg.offset as u32,
702 align: memarg.align as u32,
703 }),
704 I64Store16 { memarg } => Some(WasmOp::I64Store16 {
705 offset: memarg.offset as u32,
706 align: memarg.align as u32,
707 }),
708 I64Store32 { memarg } => Some(WasmOp::I64Store32 {
709 offset: memarg.offset as u32,
710 align: memarg.align as u32,
711 }),
712
713 MemorySize { mem, .. } => Some(WasmOp::MemorySize(*mem)),
715 MemoryGrow { mem, .. } => Some(WasmOp::MemoryGrow(*mem)),
716
717 MemoryCopy {
724 dst_mem: 0,
725 src_mem: 0,
726 } => Some(WasmOp::MemoryCopy),
727 MemoryFill { mem: 0 } => Some(WasmOp::MemoryFill),
728
729 V128Const { value } => {
733 let mut bytes = [0u8; 16];
734 bytes.copy_from_slice(value.bytes());
735 Some(WasmOp::V128Const(bytes))
736 }
737 V128Load { memarg } => Some(WasmOp::V128Load {
738 offset: memarg.offset as u32,
739 align: memarg.align as u32,
740 }),
741 V128Store { memarg } => Some(WasmOp::V128Store {
742 offset: memarg.offset as u32,
743 align: memarg.align as u32,
744 }),
745
746 V128And => Some(WasmOp::V128And),
748 V128Or => Some(WasmOp::V128Or),
749 V128Xor => Some(WasmOp::V128Xor),
750 V128Not => Some(WasmOp::V128Not),
751 V128AndNot => Some(WasmOp::V128AndNot),
752
753 I8x16Add => Some(WasmOp::I8x16Add),
755 I8x16Sub => Some(WasmOp::I8x16Sub),
756 I8x16Neg => Some(WasmOp::I8x16Neg),
757 I8x16Eq => Some(WasmOp::I8x16Eq),
758 I8x16Ne => Some(WasmOp::I8x16Ne),
759 I8x16LtS => Some(WasmOp::I8x16LtS),
760 I8x16LtU => Some(WasmOp::I8x16LtU),
761 I8x16GtS => Some(WasmOp::I8x16GtS),
762 I8x16GtU => Some(WasmOp::I8x16GtU),
763 I8x16LeS => Some(WasmOp::I8x16LeS),
764 I8x16LeU => Some(WasmOp::I8x16LeU),
765 I8x16GeS => Some(WasmOp::I8x16GeS),
766 I8x16GeU => Some(WasmOp::I8x16GeU),
767 I8x16Splat => Some(WasmOp::I8x16Splat),
768 I8x16ExtractLaneS { lane } => Some(WasmOp::I8x16ExtractLaneS(*lane)),
769 I8x16ExtractLaneU { lane } => Some(WasmOp::I8x16ExtractLaneU(*lane)),
770 I8x16ReplaceLane { lane } => Some(WasmOp::I8x16ReplaceLane(*lane)),
771 I8x16Shuffle { lanes } => Some(WasmOp::I8x16Shuffle(*lanes)),
772 I8x16Swizzle => Some(WasmOp::I8x16Swizzle),
773
774 I16x8Add => Some(WasmOp::I16x8Add),
776 I16x8Sub => Some(WasmOp::I16x8Sub),
777 I16x8Mul => Some(WasmOp::I16x8Mul),
778 I16x8Neg => Some(WasmOp::I16x8Neg),
779 I16x8Eq => Some(WasmOp::I16x8Eq),
780 I16x8Ne => Some(WasmOp::I16x8Ne),
781 I16x8LtS => Some(WasmOp::I16x8LtS),
782 I16x8LtU => Some(WasmOp::I16x8LtU),
783 I16x8GtS => Some(WasmOp::I16x8GtS),
784 I16x8GtU => Some(WasmOp::I16x8GtU),
785 I16x8LeS => Some(WasmOp::I16x8LeS),
786 I16x8LeU => Some(WasmOp::I16x8LeU),
787 I16x8GeS => Some(WasmOp::I16x8GeS),
788 I16x8GeU => Some(WasmOp::I16x8GeU),
789 I16x8Splat => Some(WasmOp::I16x8Splat),
790 I16x8ExtractLaneS { lane } => Some(WasmOp::I16x8ExtractLaneS(*lane)),
791 I16x8ExtractLaneU { lane } => Some(WasmOp::I16x8ExtractLaneU(*lane)),
792 I16x8ReplaceLane { lane } => Some(WasmOp::I16x8ReplaceLane(*lane)),
793
794 I32x4Add => Some(WasmOp::I32x4Add),
796 I32x4Sub => Some(WasmOp::I32x4Sub),
797 I32x4Mul => Some(WasmOp::I32x4Mul),
798 I32x4Neg => Some(WasmOp::I32x4Neg),
799 I32x4Eq => Some(WasmOp::I32x4Eq),
800 I32x4Ne => Some(WasmOp::I32x4Ne),
801 I32x4LtS => Some(WasmOp::I32x4LtS),
802 I32x4LtU => Some(WasmOp::I32x4LtU),
803 I32x4GtS => Some(WasmOp::I32x4GtS),
804 I32x4GtU => Some(WasmOp::I32x4GtU),
805 I32x4LeS => Some(WasmOp::I32x4LeS),
806 I32x4LeU => Some(WasmOp::I32x4LeU),
807 I32x4GeS => Some(WasmOp::I32x4GeS),
808 I32x4GeU => Some(WasmOp::I32x4GeU),
809 I32x4Splat => Some(WasmOp::I32x4Splat),
810 I32x4ExtractLane { lane } => Some(WasmOp::I32x4ExtractLane(*lane)),
811 I32x4ReplaceLane { lane } => Some(WasmOp::I32x4ReplaceLane(*lane)),
812
813 I64x2Add => Some(WasmOp::I64x2Add),
815 I64x2Sub => Some(WasmOp::I64x2Sub),
816 I64x2Mul => Some(WasmOp::I64x2Mul),
817 I64x2Neg => Some(WasmOp::I64x2Neg),
818 I64x2Eq => Some(WasmOp::I64x2Eq),
819 I64x2Ne => Some(WasmOp::I64x2Ne),
820 I64x2LtS => Some(WasmOp::I64x2LtS),
821 I64x2GtS => Some(WasmOp::I64x2GtS),
822 I64x2LeS => Some(WasmOp::I64x2LeS),
823 I64x2GeS => Some(WasmOp::I64x2GeS),
824 I64x2Splat => Some(WasmOp::I64x2Splat),
825 I64x2ExtractLane { lane } => Some(WasmOp::I64x2ExtractLane(*lane)),
826 I64x2ReplaceLane { lane } => Some(WasmOp::I64x2ReplaceLane(*lane)),
827
828 F32x4Add => Some(WasmOp::F32x4Add),
830 F32x4Sub => Some(WasmOp::F32x4Sub),
831 F32x4Mul => Some(WasmOp::F32x4Mul),
832 F32x4Div => Some(WasmOp::F32x4Div),
833 F32x4Abs => Some(WasmOp::F32x4Abs),
834 F32x4Neg => Some(WasmOp::F32x4Neg),
835 F32x4Sqrt => Some(WasmOp::F32x4Sqrt),
836 F32x4Eq => Some(WasmOp::F32x4Eq),
837 F32x4Ne => Some(WasmOp::F32x4Ne),
838 F32x4Lt => Some(WasmOp::F32x4Lt),
839 F32x4Le => Some(WasmOp::F32x4Le),
840 F32x4Gt => Some(WasmOp::F32x4Gt),
841 F32x4Ge => Some(WasmOp::F32x4Ge),
842 F32x4Splat => Some(WasmOp::F32x4Splat),
843 F32x4ExtractLane { lane } => Some(WasmOp::F32x4ExtractLane(*lane)),
844 F32x4ReplaceLane { lane } => Some(WasmOp::F32x4ReplaceLane(*lane)),
845
846 _ => None,
848 }
849}
850
851#[cfg(test)]
852mod tests {
853 use super::*;
854
855 #[test]
856 fn test_decode_simple_add() {
857 let wat = r#"
858 (module
859 (func (export "add") (param i32 i32) (result i32)
860 local.get 0
861 local.get 1
862 i32.add
863 )
864 )
865 "#;
866
867 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
868 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
869
870 assert_eq!(functions.len(), 1);
871 assert_eq!(functions[0].index, 0);
872 assert_eq!(functions[0].export_name, Some("add".to_string()));
873 assert_eq!(
874 functions[0].ops,
875 vec![
876 WasmOp::LocalGet(0),
877 WasmOp::LocalGet(1),
878 WasmOp::I32Add,
879 WasmOp::End
880 ]
881 );
882 }
883
884 #[test]
890 fn test_decode_i64_i32_width_conversions() {
891 let wat = r#"
892 (module
893 (func (export "conv") (param i32 i64) (result i32)
894 local.get 0
895 i64.extend_i32_u
896 local.get 0
897 i64.extend_i32_s
898 i64.add
899 local.get 1
900 i64.add
901 i32.wrap_i64
902 )
903 )
904 "#;
905 let wasm = wat::parse_str(wat).expect("parse");
906 let functions = decode_wasm_functions(&wasm).expect("decode");
907 let ops = &functions[0].ops;
908 assert!(
909 ops.contains(&WasmOp::I64ExtendI32U),
910 "i64.extend_i32_u must decode (not be dropped): {ops:?}"
911 );
912 assert!(
913 ops.contains(&WasmOp::I64ExtendI32S),
914 "i64.extend_i32_s must decode (not be dropped): {ops:?}"
915 );
916 assert!(
917 ops.contains(&WasmOp::I32WrapI64),
918 "i32.wrap_i64 must decode (not be dropped): {ops:?}"
919 );
920 }
921
922 #[test]
927 fn test_decode_br_table() {
928 let wat = r#"
929 (module
930 (func (export "bt") (param i32) (result i32)
931 (block (block (block
932 local.get 0
933 br_table 2 0 1 2)
934 i32.const 30 return)
935 i32.const 20 return)
936 i32.const 10))
937 "#;
938 let wasm = wat::parse_str(wat).expect("parse");
939 let functions = decode_wasm_functions(&wasm).expect("decode");
940 let bt = functions[0]
941 .ops
942 .iter()
943 .find_map(|o| match o {
944 WasmOp::BrTable { targets, default } => Some((targets.clone(), *default)),
945 _ => None,
946 })
947 .expect("br_table must decode (not be dropped)");
948 assert_eq!(bt.0, vec![2, 0, 1], "br_table targets preserved in order");
949 assert_eq!(bt.1, 2, "br_table default preserved");
950 }
951
952 #[test]
953 fn test_decode_arithmetic() {
954 let wat = r#"
955 (module
956 (func (export "calc") (result i32)
957 i32.const 5
958 i32.const 3
959 i32.mul
960 i32.const 2
961 i32.add
962 )
963 )
964 "#;
965
966 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
967 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
968
969 assert_eq!(functions.len(), 1);
970 assert_eq!(functions[0].export_name, Some("calc".to_string()));
971 assert_eq!(
972 functions[0].ops,
973 vec![
974 WasmOp::I32Const(5),
975 WasmOp::I32Const(3),
976 WasmOp::I32Mul,
977 WasmOp::I32Const(2),
978 WasmOp::I32Add,
979 WasmOp::End,
980 ]
981 );
982 }
983
984 #[test]
985 fn test_decode_multi_function_module() {
986 let wat = r#"
987 (module
988 (func $helper)
989 (func (export "add") (param i32 i32) (result i32)
990 local.get 0
991 local.get 1
992 i32.add
993 )
994 (func (export "sub") (param i32 i32) (result i32)
995 local.get 0
996 local.get 1
997 i32.sub
998 )
999 )
1000 "#;
1001
1002 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1003 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1004
1005 assert_eq!(functions.len(), 3);
1006 assert_eq!(functions[0].index, 0);
1007 assert_eq!(functions[0].export_name, None);
1008 assert_eq!(functions[1].index, 1);
1009 assert_eq!(functions[1].export_name, Some("add".to_string()));
1010 assert_eq!(functions[2].index, 2);
1011 assert_eq!(functions[2].export_name, Some("sub".to_string()));
1012 }
1013
1014 #[test]
1015 fn test_decode_module_with_imports() {
1016 let wat = r#"
1017 (module
1018 (import "env" "log" (func $log (param i32)))
1019 (import "env" "memory" (memory 1))
1020 (func (export "run") (param i32)
1021 local.get 0
1022 call 0
1023 )
1024 )
1025 "#;
1026
1027 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1028 let module = decode_wasm_module(&wasm).expect("Failed to decode");
1029
1030 assert_eq!(module.imports.len(), 2);
1032 assert_eq!(module.num_imported_funcs, 1);
1033
1034 assert_eq!(module.imports[0].module, "env");
1036 assert_eq!(module.imports[0].name, "log");
1037 assert!(matches!(module.imports[0].kind, ImportKind::Function(_)));
1038
1039 assert_eq!(module.imports[1].module, "env");
1041 assert_eq!(module.imports[1].name, "memory");
1042 assert_eq!(module.imports[1].kind, ImportKind::Memory);
1043
1044 assert_eq!(module.functions.len(), 1);
1046 assert_eq!(module.functions[0].index, 1);
1047 assert_eq!(module.functions[0].export_name, Some("run".to_string()));
1048 }
1049
1050 #[test]
1051 fn test_find_function_by_export_name() {
1052 let wat = r#"
1053 (module
1054 (func $helper)
1055 (func (export "add") (param i32 i32) (result i32)
1056 local.get 0
1057 local.get 1
1058 i32.add
1059 )
1060 )
1061 "#;
1062
1063 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1064 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1065
1066 let add_func = functions
1067 .iter()
1068 .find(|f| f.export_name.as_deref() == Some("add"))
1069 .expect("Should find 'add' function");
1070
1071 assert_eq!(add_func.index, 1);
1072 assert!(add_func.ops.contains(&WasmOp::I32Add));
1073 }
1074
1075 #[test]
1076 fn test_decode_subword_loads() {
1077 let wat = r#"
1078 (module
1079 (memory 1)
1080 (func (export "test") (param i32) (result i32)
1081 local.get 0
1082 i32.load8_u
1083 )
1084 )
1085 "#;
1086
1087 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1088 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1089
1090 assert_eq!(functions.len(), 1);
1091 assert!(functions[0].ops.contains(&WasmOp::I32Load8U {
1092 offset: 0,
1093 align: 0,
1094 }));
1095 }
1096
1097 #[test]
1098 fn test_decode_subword_stores() {
1099 let wat = r#"
1100 (module
1101 (memory 1)
1102 (func (export "test") (param i32 i32)
1103 local.get 0
1104 local.get 1
1105 i32.store8
1106 )
1107 )
1108 "#;
1109
1110 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1111 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1112
1113 assert_eq!(functions.len(), 1);
1114 assert!(functions[0].ops.contains(&WasmOp::I32Store8 {
1115 offset: 0,
1116 align: 0,
1117 }));
1118 }
1119
1120 #[test]
1121 fn test_decode_memory_size_grow() {
1122 let wat = r#"
1123 (module
1124 (memory 1)
1125 (func (export "test") (result i32)
1126 memory.size
1127 )
1128 )
1129 "#;
1130
1131 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1132 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1133
1134 assert_eq!(functions.len(), 1);
1135 assert!(functions[0].ops.contains(&WasmOp::MemorySize(0)));
1136 }
1137
1138 #[test]
1139 fn test_decode_memory_grow() {
1140 let wat = r#"
1141 (module
1142 (memory 1)
1143 (func (export "test") (param i32) (result i32)
1144 local.get 0
1145 memory.grow
1146 )
1147 )
1148 "#;
1149
1150 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1151 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1152
1153 assert_eq!(functions.len(), 1);
1154 assert!(functions[0].ops.contains(&WasmOp::MemoryGrow(0)));
1155 }
1156
1157 #[test]
1158 fn test_decode_bulk_memory_374() {
1159 let wat = r#"
1162 (module
1163 (memory 1)
1164 (func (export "cpy") (param i32 i32 i32)
1165 local.get 0 local.get 1 local.get 2 memory.copy)
1166 (func (export "fil") (param i32 i32 i32)
1167 local.get 0 local.get 1 local.get 2 memory.fill)
1168 )
1169 "#;
1170 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1171 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1172 assert_eq!(functions.len(), 2);
1173 assert!(functions[0].ops.contains(&WasmOp::MemoryCopy));
1174 assert!(functions[1].ops.contains(&WasmOp::MemoryFill));
1175 assert!(functions[0].unsupported.is_none());
1177 assert!(functions[1].unsupported.is_none());
1178 }
1179
1180 #[test]
1181 fn test_decode_i64_subword_loads() {
1182 let wat = r#"
1183 (module
1184 (memory 1)
1185 (func (export "test") (param i32) (result i64)
1186 local.get 0
1187 i64.load8_s
1188 )
1189 )
1190 "#;
1191
1192 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1193 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1194
1195 assert_eq!(functions.len(), 1);
1196 assert!(functions[0].ops.contains(&WasmOp::I64Load8S {
1197 offset: 0,
1198 align: 0,
1199 }));
1200 }
1201
1202 #[test]
1203 fn test_decode_all_subword_memory_ops() {
1204 let wat = r#"
1206 (module
1207 (memory 1)
1208 (func (export "test") (param i32)
1209 ;; i32 sub-word loads
1210 local.get 0
1211 i32.load8_s
1212 drop
1213 local.get 0
1214 i32.load8_u
1215 drop
1216 local.get 0
1217 i32.load16_s
1218 drop
1219 local.get 0
1220 i32.load16_u
1221 drop
1222
1223 ;; i32 sub-word stores
1224 local.get 0
1225 i32.const 42
1226 i32.store8
1227 local.get 0
1228 i32.const 42
1229 i32.store16
1230
1231 ;; i64 sub-word loads
1232 local.get 0
1233 i64.load8_s
1234 drop
1235 local.get 0
1236 i64.load8_u
1237 drop
1238 local.get 0
1239 i64.load16_s
1240 drop
1241 local.get 0
1242 i64.load16_u
1243 drop
1244 local.get 0
1245 i64.load32_s
1246 drop
1247 local.get 0
1248 i64.load32_u
1249 drop
1250
1251 ;; i64 sub-word stores
1252 local.get 0
1253 i64.const 42
1254 i64.store8
1255 local.get 0
1256 i64.const 42
1257 i64.store16
1258 local.get 0
1259 i64.const 42
1260 i64.store32
1261 )
1262 )
1263 "#;
1264
1265 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1266 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1267
1268 assert_eq!(functions.len(), 1);
1269 let ops = &functions[0].ops;
1270
1271 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load8S { .. })));
1273 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load8U { .. })));
1274 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load16S { .. })));
1275 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load16U { .. })));
1276 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Store8 { .. })));
1277 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Store16 { .. })));
1278
1279 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load8S { .. })));
1281 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load8U { .. })));
1282 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load16S { .. })));
1283 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load16U { .. })));
1284 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load32S { .. })));
1285 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load32U { .. })));
1286 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store8 { .. })));
1287 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store16 { .. })));
1288 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store32 { .. })));
1289 }
1290
1291 #[test]
1292 fn test_decode_simd_i32x4_add() {
1293 let wat = r#"
1294 (module
1295 (func (export "add_v128") (param v128 v128) (result v128)
1296 local.get 0
1297 local.get 1
1298 i32x4.add
1299 )
1300 )
1301 "#;
1302
1303 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1304 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1305
1306 assert_eq!(functions.len(), 1);
1307 assert!(
1308 functions[0].ops.contains(&WasmOp::I32x4Add),
1309 "Should decode i32x4.add: {:?}",
1310 functions[0].ops
1311 );
1312 }
1313
1314 #[test]
1315 fn test_decode_simd_v128_const() {
1316 let wat = r#"
1317 (module
1318 (func (export "const_v128") (result v128)
1319 v128.const i32x4 1 2 3 4
1320 )
1321 )
1322 "#;
1323
1324 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1325 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1326
1327 assert_eq!(functions.len(), 1);
1328 assert!(
1329 functions[0]
1330 .ops
1331 .iter()
1332 .any(|o| matches!(o, WasmOp::V128Const(_))),
1333 "Should decode v128.const: {:?}",
1334 functions[0].ops
1335 );
1336 }
1337
1338 #[test]
1339 fn test_decode_simd_v128_load_store() {
1340 let wat = r#"
1341 (module
1342 (memory 1)
1343 (func (export "load_store") (param i32)
1344 local.get 0
1345 v128.load
1346 local.get 0
1347 v128.store
1348 )
1349 )
1350 "#;
1351
1352 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1353 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1354
1355 assert_eq!(functions.len(), 1);
1356 let ops = &functions[0].ops;
1357 assert!(
1358 ops.iter().any(|o| matches!(o, WasmOp::V128Load { .. })),
1359 "Should decode v128.load"
1360 );
1361 assert!(
1362 ops.iter().any(|o| matches!(o, WasmOp::V128Store { .. })),
1363 "Should decode v128.store"
1364 );
1365 }
1366
1367 #[test]
1368 fn test_decode_simd_bitwise_ops() {
1369 let wat = r#"
1370 (module
1371 (func (export "bitwise") (param v128 v128) (result v128)
1372 local.get 0
1373 local.get 1
1374 v128.and
1375 )
1376 )
1377 "#;
1378
1379 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1380 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1381
1382 assert_eq!(functions.len(), 1);
1383 assert!(functions[0].ops.contains(&WasmOp::V128And));
1384 }
1385
1386 #[test]
1387 fn test_decode_simd_splat() {
1388 let wat = r#"
1389 (module
1390 (func (export "splat") (param i32) (result v128)
1391 local.get 0
1392 i32x4.splat
1393 )
1394 )
1395 "#;
1396
1397 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1398 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1399
1400 assert_eq!(functions.len(), 1);
1401 assert!(functions[0].ops.contains(&WasmOp::I32x4Splat));
1402 }
1403
1404 #[test]
1405 fn test_decode_simd_extract_lane() {
1406 let wat = r#"
1407 (module
1408 (func (export "extract") (param v128) (result i32)
1409 local.get 0
1410 i32x4.extract_lane 2
1411 )
1412 )
1413 "#;
1414
1415 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1416 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1417
1418 assert_eq!(functions.len(), 1);
1419 assert!(
1420 functions[0].ops.contains(&WasmOp::I32x4ExtractLane(2)),
1421 "Should decode i32x4.extract_lane 2"
1422 );
1423 }
1424
1425 #[test]
1426 fn test_decode_simd_f32x4_arithmetic() {
1427 let wat = r#"
1428 (module
1429 (func (export "f32x4_add") (param v128 v128) (result v128)
1430 local.get 0
1431 local.get 1
1432 f32x4.add
1433 )
1434 )
1435 "#;
1436
1437 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1438 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1439
1440 assert_eq!(functions.len(), 1);
1441 assert!(functions[0].ops.contains(&WasmOp::F32x4Add));
1442 }
1443
1444 #[test]
1445 fn test_369_scalar_float_op_flags_function_unsupported_not_dropped() {
1446 let wat = r#"
1450 (module
1451 (func (export "fadd") (param f32 f32) (result f32)
1452 local.get 0 local.get 1 f32.add)
1453 (func (export "iadd") (param i32 i32) (result i32)
1454 local.get 0 local.get 1 i32.add))
1455 "#;
1456 let wasm = wat::parse_str(wat).expect("parse");
1457 let functions = decode_wasm_functions(&wasm).expect("decode");
1458 let fadd = functions
1459 .iter()
1460 .find(|f| f.export_name.as_deref() == Some("fadd"))
1461 .unwrap();
1462 let iadd = functions
1463 .iter()
1464 .find(|f| f.export_name.as_deref() == Some("iadd"))
1465 .unwrap();
1466 assert!(
1467 fadd.unsupported.is_some(),
1468 "f32.add must flag the function unsupported (loud-skip), got {:?}",
1469 fadd.unsupported
1470 );
1471 assert!(
1472 fadd.unsupported.as_deref().unwrap().contains("F32Add"),
1473 "diagnostic should name the op: {:?}",
1474 fadd.unsupported
1475 );
1476 assert!(
1477 iadd.unsupported.is_none(),
1478 "a pure-integer function must NOT be flagged: {:?}",
1479 iadd.unsupported
1480 );
1481 }
1482
1483 #[test]
1484 fn test_decode_simd_multiple_ops() {
1485 let wat = r#"
1486 (module
1487 (func (export "simd_ops") (param v128 v128 v128) (result v128)
1488 ;; (a + b) * c
1489 local.get 0
1490 local.get 1
1491 i32x4.add
1492 local.get 2
1493 i32x4.mul
1494 )
1495 )
1496 "#;
1497
1498 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1499 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1500
1501 assert_eq!(functions.len(), 1);
1502 let ops = &functions[0].ops;
1503 assert!(ops.contains(&WasmOp::I32x4Add));
1504 assert!(ops.contains(&WasmOp::I32x4Mul));
1505 }
1506
1507 #[test]
1510 fn test_decode_captures_global_initializer() {
1511 let wat = r#"
1512 (module
1513 (memory 2)
1514 (global $__stack_pointer (mut i32) (i32.const 65536))
1515 (global $immutable_const i32 (i32.const 7))
1516 (func (export "f") (result i32) global.get 0)
1517 )
1518 "#;
1519 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1520 let module = decode_wasm_module(&wasm).expect("Failed to decode");
1521
1522 assert_eq!(module.globals.len(), 2, "both globals captured");
1523 let sp = &module.globals[0];
1524 assert_eq!(sp.index, 0);
1525 assert_eq!(sp.init_i32, Some(65536), "stack-pointer init captured");
1526 assert!(sp.mutable, "stack pointer is mutable");
1527 let c = &module.globals[1];
1528 assert_eq!(c.init_i32, Some(7));
1529 assert!(!c.mutable, "second global is immutable");
1530 }
1531}