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 type_block_arity: Vec<(u8, u8)> = Vec::new();
146 let mut elem_func_indices: Vec<u32> = Vec::new();
147 let mut name_section_names: HashMap<u32, String> = HashMap::new();
153
154 for payload in Parser::new(0).parse_all(wasm_bytes) {
155 let payload = payload.context("Failed to parse WASM payload")?;
156
157 match payload {
158 Payload::TypeSection(reader) => {
159 for rec_group in reader {
162 let rec_group = rec_group.context("Failed to parse type")?;
163 for sub_ty in rec_group.types() {
164 type_block_arity.push(match &sub_ty.composite_type.inner {
169 wasmparser::CompositeInnerType::Func(f) => (
170 u8::try_from(f.params().len()).unwrap_or(u8::MAX),
171 u8::try_from(f.results().len()).unwrap_or(u8::MAX),
172 ),
173 _ => (u8::MAX, u8::MAX),
174 });
175 let (count, ret_i64, params_i64) = match &sub_ty.composite_type.inner {
176 wasmparser::CompositeInnerType::Func(func_ty) => (
177 func_ty.params().len() as u32,
178 func_ty
179 .results()
180 .first()
181 .is_some_and(|t| *t == wasmparser::ValType::I64),
182 func_ty
187 .params()
188 .iter()
189 .map(|t| {
190 matches!(
191 t,
192 wasmparser::ValType::I64 | wasmparser::ValType::F64
193 )
194 })
195 .collect::<Vec<bool>>(),
196 ),
197 _ => (0, false, Vec::new()),
198 };
199 type_arg_counts.push(count);
200 type_ret_i64.push(ret_i64);
201 type_params_i64.push(params_i64);
202 }
203 }
204 }
205 Payload::ImportSection(reader) => {
206 for import in reader.into_imports() {
212 let import = import.context("Failed to parse import")?;
213 let (kind, idx) = match import.ty {
214 wasmparser::TypeRef::Func(type_idx) => {
215 let idx = num_imported_funcs;
216 num_imported_funcs += 1;
217 func_arg_counts
220 .push(type_arg_counts.get(type_idx as usize).copied().unwrap_or(0));
221 func_ret_i64.push(
222 type_ret_i64
223 .get(type_idx as usize)
224 .copied()
225 .unwrap_or(false),
226 );
227 func_params_i64.push(
228 type_params_i64
229 .get(type_idx as usize)
230 .cloned()
231 .unwrap_or_default(),
232 );
233 (ImportKind::Function(type_idx), idx)
234 }
235 wasmparser::TypeRef::Memory(_) => (ImportKind::Memory, 0),
236 wasmparser::TypeRef::Table(_) => (ImportKind::Table, 0),
237 wasmparser::TypeRef::Global(_) => (ImportKind::Global, 0),
238 _ => continue,
239 };
240 imports.push(ImportEntry {
241 module: import.module.to_string(),
242 name: import.name.to_string(),
243 kind,
244 index: idx,
245 });
246 }
247 }
248 Payload::FunctionSection(reader) => {
249 for ty in reader {
254 let type_idx = ty.context("Failed to parse function type index")?;
255 func_arg_counts
256 .push(type_arg_counts.get(type_idx as usize).copied().unwrap_or(0));
257 func_ret_i64.push(
258 type_ret_i64
259 .get(type_idx as usize)
260 .copied()
261 .unwrap_or(false),
262 );
263 func_params_i64.push(
264 type_params_i64
265 .get(type_idx as usize)
266 .cloned()
267 .unwrap_or_default(),
268 );
269 }
270 }
271 Payload::MemorySection(reader) => {
272 for (idx, memory) in reader.into_iter().enumerate() {
273 let mem = memory.context("Failed to parse memory")?;
274 memories.push(WasmMemory {
275 index: idx as u32,
276 initial_pages: mem.initial as u32,
277 max_pages: mem.maximum.map(|m| m as u32),
278 shared: mem.shared,
279 });
280 }
281 }
282 Payload::GlobalSection(reader) => {
283 for (idx, global) in reader.into_iter().enumerate() {
289 let global = global.context("Failed to parse global")?;
290 let mut init_i32 = None;
291 let mut ops = global.init_expr.get_operators_reader();
292 if let Ok(wasmparser::Operator::I32Const { value }) = ops.read() {
293 init_i32 = Some(value);
294 }
295 globals.push(WasmGlobal {
296 index: idx as u32,
297 init_i32,
298 mutable: global.ty.mutable,
299 });
300 }
301 }
302 Payload::DataSection(reader) => {
303 for data in reader {
304 let data = data.context("Failed to parse data segment")?;
305 if let wasmparser::DataKind::Active {
306 memory_index: 0,
307 offset_expr,
308 } = data.kind
309 {
310 let mut ops = offset_expr.get_operators_reader();
311 if let Ok(wasmparser::Operator::I32Const { value }) = ops.read() {
312 data_segments.push((value as u32, data.data.to_vec()));
313 }
314 }
315 }
316 }
317 Payload::ElementSection(reader) => {
318 for elem in reader {
325 let elem = elem.context("Failed to parse element segment")?;
326 match elem.items {
327 wasmparser::ElementItems::Functions(funcs) => {
328 for f in funcs {
329 elem_func_indices
330 .push(f.context("Failed to parse element func index")?);
331 }
332 }
333 wasmparser::ElementItems::Expressions(_, exprs) => {
334 for expr in exprs {
335 let expr = expr.context("Failed to parse element expr")?;
336 for op in expr.get_operators_reader() {
337 if let wasmparser::Operator::RefFunc { function_index } =
338 op.context("Failed to parse element op")?
339 {
340 elem_func_indices.push(function_index);
341 }
342 }
343 }
344 }
345 }
346 }
347 }
348 Payload::ExportSection(exports) => {
349 for export in exports {
350 let export = export.context("Failed to parse export")?;
351 if export.kind == ExternalKind::Func {
352 export_names.insert(export.index, export.name.to_string());
353 }
354 }
355 }
356 Payload::CodeSectionEntry(body) => {
357 let (ops, op_offsets, block_arity, unsupported) =
358 decode_function_body(&body, &type_block_arity)?;
359 let actual_index = num_imported_funcs + func_index;
360 let export_name = export_names.get(&actual_index).cloned();
361
362 functions.push(FunctionOps {
363 index: actual_index,
364 export_name,
365 debug_name: None, ops,
367 op_offsets,
368 unsupported,
369 block_arity,
370 });
371 func_index += 1;
372 }
373 Payload::CustomSection(c) => {
374 if let wasmparser::KnownCustom::Name(reader) = c.as_known() {
376 parse_name_section_func_names(reader, &mut name_section_names);
377 }
378 }
379 _ => {}
380 }
381 }
382
383 apply_name_section(&mut functions, &name_section_names);
384
385 Ok(DecodedModule {
386 functions,
387 memories,
388 data_segments,
389 imports,
390 num_imported_funcs,
391 func_arg_counts,
392 type_arg_counts,
393 func_ret_i64,
394 type_ret_i64,
395 func_params_i64,
396 globals,
397 elem_func_indices,
398 })
399}
400
401fn parse_name_section_func_names(
407 reader: wasmparser::NameSectionReader<'_>,
408 out: &mut HashMap<u32, String>,
409) {
410 for subsection in reader.into_iter().flatten() {
411 if let wasmparser::Name::Function(map) = subsection {
412 for naming in map.into_iter().flatten() {
413 out.insert(naming.index, naming.name.to_string());
414 }
415 }
416 }
417}
418
419fn apply_name_section(functions: &mut [FunctionOps], names: &HashMap<u32, String>) {
423 if names.is_empty() {
424 return;
425 }
426 for f in functions {
427 f.debug_name = names.get(&f.index).cloned();
428 }
429}
430
431pub fn decode_wasm_functions(wasm_bytes: &[u8]) -> Result<Vec<FunctionOps>> {
433 let mut functions = Vec::new();
434 let mut func_index = 0u32;
435 let mut num_imported_funcs = 0u32;
436 let mut export_names: HashMap<u32, String> = HashMap::new();
437 let mut name_section_names: HashMap<u32, String> = HashMap::new();
438 let mut type_block_arity: Vec<(u8, u8)> = Vec::new();
440
441 for payload in Parser::new(0).parse_all(wasm_bytes) {
442 let payload = payload.context("Failed to parse WASM payload")?;
443
444 match payload {
445 Payload::TypeSection(reader) => {
446 for rec_group in reader {
451 let rec_group = rec_group.context("Failed to parse type")?;
452 for sub_ty in rec_group.types() {
453 type_block_arity.push(match &sub_ty.composite_type.inner {
454 wasmparser::CompositeInnerType::Func(f) => (
455 u8::try_from(f.params().len()).unwrap_or(u8::MAX),
456 u8::try_from(f.results().len()).unwrap_or(u8::MAX),
457 ),
458 _ => (u8::MAX, u8::MAX),
459 });
460 }
461 }
462 }
463 Payload::ImportSection(imports) => {
464 for import in imports.into_imports() {
467 let import = import.context("Failed to parse import")?;
468 if matches!(import.ty, wasmparser::TypeRef::Func(_)) {
469 num_imported_funcs += 1;
470 }
471 }
472 }
473 Payload::ExportSection(exports) => {
474 for export in exports {
475 let export = export.context("Failed to parse export")?;
476 if export.kind == ExternalKind::Func {
477 export_names.insert(export.index, export.name.to_string());
478 }
479 }
480 }
481 Payload::CodeSectionEntry(body) => {
482 let (ops, op_offsets, block_arity, unsupported) =
483 decode_function_body(&body, &type_block_arity)?;
484 let actual_index = num_imported_funcs + func_index;
485 let export_name = export_names.get(&actual_index).cloned();
486
487 functions.push(FunctionOps {
488 index: actual_index,
489 export_name,
490 debug_name: None, ops,
492 op_offsets,
493 unsupported,
494 block_arity,
495 });
496 func_index += 1;
497 }
498 Payload::CustomSection(c) => {
499 if let wasmparser::KnownCustom::Name(reader) = c.as_known() {
501 parse_name_section_func_names(reader, &mut name_section_names);
502 }
503 }
504 _ => {}
505 }
506 }
507
508 apply_name_section(&mut functions, &name_section_names);
509
510 Ok(functions)
511}
512
513#[derive(Debug, Clone)]
515pub struct FunctionOps {
516 pub index: u32,
518 pub export_name: Option<String>,
520 pub debug_name: Option<String>,
529 pub ops: Vec<WasmOp>,
531 pub op_offsets: Vec<u32>,
539 pub unsupported: Option<String>,
549 pub block_arity: Vec<(u8, u8)>,
565}
566
567fn blocktype_arity(bt: &wasmparser::BlockType, type_block_arity: &[(u8, u8)]) -> (u8, u8) {
572 match bt {
573 wasmparser::BlockType::Empty => (0, 0),
574 wasmparser::BlockType::Type(_) => (0, 1),
575 wasmparser::BlockType::FuncType(i) => type_block_arity
576 .get(*i as usize)
577 .copied()
578 .unwrap_or((u8::MAX, u8::MAX)),
579 }
580}
581
582type DecodedBody = (Vec<WasmOp>, Vec<u32>, Vec<(u8, u8)>, Option<String>);
586
587fn decode_function_body(
593 body: &wasmparser::FunctionBody,
594 type_block_arity: &[(u8, u8)],
595) -> Result<DecodedBody> {
596 let mut ops = Vec::new();
597 let mut op_offsets = Vec::new();
602 let mut block_arity: Vec<(u8, u8)> = Vec::new();
605 let mut unsupported: Option<String> = None;
606
607 let ops_reader = body.get_operators_reader()?;
608 for item in ops_reader.into_iter_with_offsets() {
609 let (op, offset) = item.context("Failed to read operator")?;
610
611 if let Some(wasm_op) = convert_operator(&op) {
612 if let wasmparser::Operator::Block { blockty }
615 | wasmparser::Operator::Loop { blockty }
616 | wasmparser::Operator::If { blockty } = &op
617 {
618 block_arity.push(blocktype_arity(blockty, type_block_arity));
619 }
620 ops.push(wasm_op);
621 op_offsets.push(offset as u32);
622 } else if unsupported.is_none() && !is_intentionally_ignored(&op) {
623 unsupported = Some(format!("{op:?}"));
627 }
628 }
629
630 Ok((ops, op_offsets, block_arity, unsupported))
631}
632
633fn is_intentionally_ignored(op: &wasmparser::Operator) -> bool {
638 use wasmparser::Operator::*;
639 matches!(op, Nop | Unreachable)
640}
641
642fn convert_operator(op: &wasmparser::Operator) -> Option<WasmOp> {
644 use wasmparser::Operator::*;
645
646 match op {
647 I32Const { value } => Some(WasmOp::I32Const(*value)),
649
650 I32Add => Some(WasmOp::I32Add),
652 I32Sub => Some(WasmOp::I32Sub),
653 I32Mul => Some(WasmOp::I32Mul),
654 I32DivS => Some(WasmOp::I32DivS),
655 I32DivU => Some(WasmOp::I32DivU),
656 I32RemS => Some(WasmOp::I32RemS),
657 I32RemU => Some(WasmOp::I32RemU),
658
659 I64Const { value } => Some(WasmOp::I64Const(*value)),
661
662 I64Add => Some(WasmOp::I64Add),
664 I64Sub => Some(WasmOp::I64Sub),
665 I64Mul => Some(WasmOp::I64Mul),
666 I64DivS => Some(WasmOp::I64DivS),
667 I64DivU => Some(WasmOp::I64DivU),
668 I64RemS => Some(WasmOp::I64RemS),
669 I64RemU => Some(WasmOp::I64RemU),
670
671 I64And => Some(WasmOp::I64And),
673 I64Or => Some(WasmOp::I64Or),
674 I64Xor => Some(WasmOp::I64Xor),
675 I64Shl => Some(WasmOp::I64Shl),
676 I64ShrS => Some(WasmOp::I64ShrS),
677 I64ShrU => Some(WasmOp::I64ShrU),
678 I64Rotl => Some(WasmOp::I64Rotl),
679 I64Rotr => Some(WasmOp::I64Rotr),
680 I64Clz => Some(WasmOp::I64Clz),
681 I64Ctz => Some(WasmOp::I64Ctz),
682 I64Popcnt => Some(WasmOp::I64Popcnt),
683 I64Extend8S => Some(WasmOp::I64Extend8S),
684 I64Extend16S => Some(WasmOp::I64Extend16S),
685 I64Extend32S => Some(WasmOp::I64Extend32S),
686 I64ExtendI32U => Some(WasmOp::I64ExtendI32U),
692 I64ExtendI32S => Some(WasmOp::I64ExtendI32S),
693 I32WrapI64 => Some(WasmOp::I32WrapI64),
694
695 I64Eqz => Some(WasmOp::I64Eqz),
697 I64Eq => Some(WasmOp::I64Eq),
698 I64Ne => Some(WasmOp::I64Ne),
699 I64LtS => Some(WasmOp::I64LtS),
700 I64LtU => Some(WasmOp::I64LtU),
701 I64LeS => Some(WasmOp::I64LeS),
702 I64LeU => Some(WasmOp::I64LeU),
703 I64GtS => Some(WasmOp::I64GtS),
704 I64GtU => Some(WasmOp::I64GtU),
705 I64GeS => Some(WasmOp::I64GeS),
706 I64GeU => Some(WasmOp::I64GeU),
707
708 I32And => Some(WasmOp::I32And),
710 I32Or => Some(WasmOp::I32Or),
711 I32Xor => Some(WasmOp::I32Xor),
712 I32Shl => Some(WasmOp::I32Shl),
713 I32ShrS => Some(WasmOp::I32ShrS),
714 I32ShrU => Some(WasmOp::I32ShrU),
715 I32Rotl => Some(WasmOp::I32Rotl),
716 I32Rotr => Some(WasmOp::I32Rotr),
717 I32Clz => Some(WasmOp::I32Clz),
718 I32Ctz => Some(WasmOp::I32Ctz),
719 I32Popcnt => Some(WasmOp::I32Popcnt),
720 I32Extend8S => Some(WasmOp::I32Extend8S),
721 I32Extend16S => Some(WasmOp::I32Extend16S),
722
723 I32Eqz => Some(WasmOp::I32Eqz),
725 I32Eq => Some(WasmOp::I32Eq),
726 I32Ne => Some(WasmOp::I32Ne),
727 I32LtS => Some(WasmOp::I32LtS),
728 I32LtU => Some(WasmOp::I32LtU),
729 I32LeS => Some(WasmOp::I32LeS),
730 I32LeU => Some(WasmOp::I32LeU),
731 I32GtS => Some(WasmOp::I32GtS),
732 I32GtU => Some(WasmOp::I32GtU),
733 I32GeS => Some(WasmOp::I32GeS),
734 I32GeU => Some(WasmOp::I32GeU),
735
736 I32Load { memarg } => Some(WasmOp::I32Load {
738 offset: memarg.offset as u32,
739 align: memarg.align as u32,
740 }),
741 I32Store { memarg } => Some(WasmOp::I32Store {
742 offset: memarg.offset as u32,
743 align: memarg.align as u32,
744 }),
745 I64Load { memarg } => Some(WasmOp::I64Load {
752 offset: memarg.offset as u32,
753 align: memarg.align as u32,
754 }),
755 I64Store { memarg } => Some(WasmOp::I64Store {
756 offset: memarg.offset as u32,
757 align: memarg.align as u32,
758 }),
759
760 I32Load8S { memarg } => Some(WasmOp::I32Load8S {
762 offset: memarg.offset as u32,
763 align: memarg.align as u32,
764 }),
765 I32Load8U { memarg } => Some(WasmOp::I32Load8U {
766 offset: memarg.offset as u32,
767 align: memarg.align as u32,
768 }),
769 I32Load16S { memarg } => Some(WasmOp::I32Load16S {
770 offset: memarg.offset as u32,
771 align: memarg.align as u32,
772 }),
773 I32Load16U { memarg } => Some(WasmOp::I32Load16U {
774 offset: memarg.offset as u32,
775 align: memarg.align as u32,
776 }),
777
778 I32Store8 { memarg } => Some(WasmOp::I32Store8 {
780 offset: memarg.offset as u32,
781 align: memarg.align as u32,
782 }),
783 I32Store16 { memarg } => Some(WasmOp::I32Store16 {
784 offset: memarg.offset as u32,
785 align: memarg.align as u32,
786 }),
787
788 LocalGet { local_index } => Some(WasmOp::LocalGet(*local_index)),
790 LocalSet { local_index } => Some(WasmOp::LocalSet(*local_index)),
791 LocalTee { local_index } => Some(WasmOp::LocalTee(*local_index)),
792 GlobalGet { global_index } => Some(WasmOp::GlobalGet(*global_index)),
793 GlobalSet { global_index } => Some(WasmOp::GlobalSet(*global_index)),
794
795 Block { .. } => Some(WasmOp::Block),
797 Loop { .. } => Some(WasmOp::Loop),
798 Br { relative_depth } => Some(WasmOp::Br(*relative_depth)),
799 BrIf { relative_depth } => Some(WasmOp::BrIf(*relative_depth)),
800 BrTable { targets } => {
806 let default = targets.default();
807 let tgts: Vec<u32> = targets.targets().filter_map(Result::ok).collect();
808 Some(WasmOp::BrTable {
809 targets: tgts,
810 default,
811 })
812 }
813 Return => Some(WasmOp::Return),
814 Call { function_index } => Some(WasmOp::Call(*function_index)),
815 CallIndirect {
816 type_index,
817 table_index,
818 ..
819 } => Some(WasmOp::CallIndirect {
820 type_index: *type_index,
821 table_index: *table_index,
822 }),
823
824 End => Some(WasmOp::End),
826
827 Nop | Unreachable => None,
829
830 Drop => Some(WasmOp::Drop),
832
833 Select => Some(WasmOp::Select),
835
836 If { .. } => Some(WasmOp::If),
838 Else => Some(WasmOp::Else),
839
840 I64Load8S { memarg } => Some(WasmOp::I64Load8S {
842 offset: memarg.offset as u32,
843 align: memarg.align as u32,
844 }),
845 I64Load8U { memarg } => Some(WasmOp::I64Load8U {
846 offset: memarg.offset as u32,
847 align: memarg.align as u32,
848 }),
849 I64Load16S { memarg } => Some(WasmOp::I64Load16S {
850 offset: memarg.offset as u32,
851 align: memarg.align as u32,
852 }),
853 I64Load16U { memarg } => Some(WasmOp::I64Load16U {
854 offset: memarg.offset as u32,
855 align: memarg.align as u32,
856 }),
857 I64Load32S { memarg } => Some(WasmOp::I64Load32S {
858 offset: memarg.offset as u32,
859 align: memarg.align as u32,
860 }),
861 I64Load32U { memarg } => Some(WasmOp::I64Load32U {
862 offset: memarg.offset as u32,
863 align: memarg.align as u32,
864 }),
865
866 I64Store8 { memarg } => Some(WasmOp::I64Store8 {
868 offset: memarg.offset as u32,
869 align: memarg.align as u32,
870 }),
871 I64Store16 { memarg } => Some(WasmOp::I64Store16 {
872 offset: memarg.offset as u32,
873 align: memarg.align as u32,
874 }),
875 I64Store32 { memarg } => Some(WasmOp::I64Store32 {
876 offset: memarg.offset as u32,
877 align: memarg.align as u32,
878 }),
879
880 MemorySize { mem, .. } => Some(WasmOp::MemorySize(*mem)),
882 MemoryGrow { mem, .. } => Some(WasmOp::MemoryGrow(*mem)),
883
884 MemoryCopy {
891 dst_mem: 0,
892 src_mem: 0,
893 } => Some(WasmOp::MemoryCopy),
894 MemoryFill { mem: 0 } => Some(WasmOp::MemoryFill),
895
896 V128Const { value } => {
900 let mut bytes = [0u8; 16];
901 bytes.copy_from_slice(value.bytes());
902 Some(WasmOp::V128Const(bytes))
903 }
904 V128Load { memarg } => Some(WasmOp::V128Load {
905 offset: memarg.offset as u32,
906 align: memarg.align as u32,
907 }),
908 V128Store { memarg } => Some(WasmOp::V128Store {
909 offset: memarg.offset as u32,
910 align: memarg.align as u32,
911 }),
912
913 V128And => Some(WasmOp::V128And),
915 V128Or => Some(WasmOp::V128Or),
916 V128Xor => Some(WasmOp::V128Xor),
917 V128Not => Some(WasmOp::V128Not),
918 V128AndNot => Some(WasmOp::V128AndNot),
919
920 I8x16Add => Some(WasmOp::I8x16Add),
922 I8x16Sub => Some(WasmOp::I8x16Sub),
923 I8x16Neg => Some(WasmOp::I8x16Neg),
924 I8x16Eq => Some(WasmOp::I8x16Eq),
925 I8x16Ne => Some(WasmOp::I8x16Ne),
926 I8x16LtS => Some(WasmOp::I8x16LtS),
927 I8x16LtU => Some(WasmOp::I8x16LtU),
928 I8x16GtS => Some(WasmOp::I8x16GtS),
929 I8x16GtU => Some(WasmOp::I8x16GtU),
930 I8x16LeS => Some(WasmOp::I8x16LeS),
931 I8x16LeU => Some(WasmOp::I8x16LeU),
932 I8x16GeS => Some(WasmOp::I8x16GeS),
933 I8x16GeU => Some(WasmOp::I8x16GeU),
934 I8x16Splat => Some(WasmOp::I8x16Splat),
935 I8x16ExtractLaneS { lane } => Some(WasmOp::I8x16ExtractLaneS(*lane)),
936 I8x16ExtractLaneU { lane } => Some(WasmOp::I8x16ExtractLaneU(*lane)),
937 I8x16ReplaceLane { lane } => Some(WasmOp::I8x16ReplaceLane(*lane)),
938 I8x16Shuffle { lanes } => Some(WasmOp::I8x16Shuffle(*lanes)),
939 I8x16Swizzle => Some(WasmOp::I8x16Swizzle),
940
941 I16x8Add => Some(WasmOp::I16x8Add),
943 I16x8Sub => Some(WasmOp::I16x8Sub),
944 I16x8Mul => Some(WasmOp::I16x8Mul),
945 I16x8Neg => Some(WasmOp::I16x8Neg),
946 I16x8Eq => Some(WasmOp::I16x8Eq),
947 I16x8Ne => Some(WasmOp::I16x8Ne),
948 I16x8LtS => Some(WasmOp::I16x8LtS),
949 I16x8LtU => Some(WasmOp::I16x8LtU),
950 I16x8GtS => Some(WasmOp::I16x8GtS),
951 I16x8GtU => Some(WasmOp::I16x8GtU),
952 I16x8LeS => Some(WasmOp::I16x8LeS),
953 I16x8LeU => Some(WasmOp::I16x8LeU),
954 I16x8GeS => Some(WasmOp::I16x8GeS),
955 I16x8GeU => Some(WasmOp::I16x8GeU),
956 I16x8Splat => Some(WasmOp::I16x8Splat),
957 I16x8ExtractLaneS { lane } => Some(WasmOp::I16x8ExtractLaneS(*lane)),
958 I16x8ExtractLaneU { lane } => Some(WasmOp::I16x8ExtractLaneU(*lane)),
959 I16x8ReplaceLane { lane } => Some(WasmOp::I16x8ReplaceLane(*lane)),
960
961 I32x4Add => Some(WasmOp::I32x4Add),
963 I32x4Sub => Some(WasmOp::I32x4Sub),
964 I32x4Mul => Some(WasmOp::I32x4Mul),
965 I32x4Neg => Some(WasmOp::I32x4Neg),
966 I32x4Eq => Some(WasmOp::I32x4Eq),
967 I32x4Ne => Some(WasmOp::I32x4Ne),
968 I32x4LtS => Some(WasmOp::I32x4LtS),
969 I32x4LtU => Some(WasmOp::I32x4LtU),
970 I32x4GtS => Some(WasmOp::I32x4GtS),
971 I32x4GtU => Some(WasmOp::I32x4GtU),
972 I32x4LeS => Some(WasmOp::I32x4LeS),
973 I32x4LeU => Some(WasmOp::I32x4LeU),
974 I32x4GeS => Some(WasmOp::I32x4GeS),
975 I32x4GeU => Some(WasmOp::I32x4GeU),
976 I32x4Splat => Some(WasmOp::I32x4Splat),
977 I32x4ExtractLane { lane } => Some(WasmOp::I32x4ExtractLane(*lane)),
978 I32x4ReplaceLane { lane } => Some(WasmOp::I32x4ReplaceLane(*lane)),
979
980 I64x2Add => Some(WasmOp::I64x2Add),
982 I64x2Sub => Some(WasmOp::I64x2Sub),
983 I64x2Mul => Some(WasmOp::I64x2Mul),
984 I64x2Neg => Some(WasmOp::I64x2Neg),
985 I64x2Eq => Some(WasmOp::I64x2Eq),
986 I64x2Ne => Some(WasmOp::I64x2Ne),
987 I64x2LtS => Some(WasmOp::I64x2LtS),
988 I64x2GtS => Some(WasmOp::I64x2GtS),
989 I64x2LeS => Some(WasmOp::I64x2LeS),
990 I64x2GeS => Some(WasmOp::I64x2GeS),
991 I64x2Splat => Some(WasmOp::I64x2Splat),
992 I64x2ExtractLane { lane } => Some(WasmOp::I64x2ExtractLane(*lane)),
993 I64x2ReplaceLane { lane } => Some(WasmOp::I64x2ReplaceLane(*lane)),
994
995 F32x4Add => Some(WasmOp::F32x4Add),
997 F32x4Sub => Some(WasmOp::F32x4Sub),
998 F32x4Mul => Some(WasmOp::F32x4Mul),
999 F32x4Div => Some(WasmOp::F32x4Div),
1000 F32x4Abs => Some(WasmOp::F32x4Abs),
1001 F32x4Neg => Some(WasmOp::F32x4Neg),
1002 F32x4Sqrt => Some(WasmOp::F32x4Sqrt),
1003 F32x4Eq => Some(WasmOp::F32x4Eq),
1004 F32x4Ne => Some(WasmOp::F32x4Ne),
1005 F32x4Lt => Some(WasmOp::F32x4Lt),
1006 F32x4Le => Some(WasmOp::F32x4Le),
1007 F32x4Gt => Some(WasmOp::F32x4Gt),
1008 F32x4Ge => Some(WasmOp::F32x4Ge),
1009 F32x4Splat => Some(WasmOp::F32x4Splat),
1010 F32x4ExtractLane { lane } => Some(WasmOp::F32x4ExtractLane(*lane)),
1011 F32x4ReplaceLane { lane } => Some(WasmOp::F32x4ReplaceLane(*lane)),
1012
1013 _ => None,
1015 }
1016}
1017
1018#[cfg(test)]
1019mod tests {
1020 use super::*;
1021
1022 #[test]
1023 fn test_decode_simple_add() {
1024 let wat = r#"
1025 (module
1026 (func (export "add") (param i32 i32) (result i32)
1027 local.get 0
1028 local.get 1
1029 i32.add
1030 )
1031 )
1032 "#;
1033
1034 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1035 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1036
1037 assert_eq!(functions.len(), 1);
1038 assert_eq!(functions[0].index, 0);
1039 assert_eq!(functions[0].export_name, Some("add".to_string()));
1040 assert_eq!(
1041 functions[0].ops,
1042 vec![
1043 WasmOp::LocalGet(0),
1044 WasmOp::LocalGet(1),
1045 WasmOp::I32Add,
1046 WasmOp::End
1047 ]
1048 );
1049 }
1050
1051 #[test]
1057 fn test_decode_i64_i32_width_conversions() {
1058 let wat = r#"
1059 (module
1060 (func (export "conv") (param i32 i64) (result i32)
1061 local.get 0
1062 i64.extend_i32_u
1063 local.get 0
1064 i64.extend_i32_s
1065 i64.add
1066 local.get 1
1067 i64.add
1068 i32.wrap_i64
1069 )
1070 )
1071 "#;
1072 let wasm = wat::parse_str(wat).expect("parse");
1073 let functions = decode_wasm_functions(&wasm).expect("decode");
1074 let ops = &functions[0].ops;
1075 assert!(
1076 ops.contains(&WasmOp::I64ExtendI32U),
1077 "i64.extend_i32_u must decode (not be dropped): {ops:?}"
1078 );
1079 assert!(
1080 ops.contains(&WasmOp::I64ExtendI32S),
1081 "i64.extend_i32_s must decode (not be dropped): {ops:?}"
1082 );
1083 assert!(
1084 ops.contains(&WasmOp::I32WrapI64),
1085 "i32.wrap_i64 must decode (not be dropped): {ops:?}"
1086 );
1087 }
1088
1089 #[test]
1094 fn test_decode_br_table() {
1095 let wat = r#"
1096 (module
1097 (func (export "bt") (param i32) (result i32)
1098 (block (block (block
1099 local.get 0
1100 br_table 2 0 1 2)
1101 i32.const 30 return)
1102 i32.const 20 return)
1103 i32.const 10))
1104 "#;
1105 let wasm = wat::parse_str(wat).expect("parse");
1106 let functions = decode_wasm_functions(&wasm).expect("decode");
1107 let bt = functions[0]
1108 .ops
1109 .iter()
1110 .find_map(|o| match o {
1111 WasmOp::BrTable { targets, default } => Some((targets.clone(), *default)),
1112 _ => None,
1113 })
1114 .expect("br_table must decode (not be dropped)");
1115 assert_eq!(bt.0, vec![2, 0, 1], "br_table targets preserved in order");
1116 assert_eq!(bt.1, 2, "br_table default preserved");
1117 }
1118
1119 #[test]
1120 fn test_decode_arithmetic() {
1121 let wat = r#"
1122 (module
1123 (func (export "calc") (result i32)
1124 i32.const 5
1125 i32.const 3
1126 i32.mul
1127 i32.const 2
1128 i32.add
1129 )
1130 )
1131 "#;
1132
1133 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1134 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1135
1136 assert_eq!(functions.len(), 1);
1137 assert_eq!(functions[0].export_name, Some("calc".to_string()));
1138 assert_eq!(
1139 functions[0].ops,
1140 vec![
1141 WasmOp::I32Const(5),
1142 WasmOp::I32Const(3),
1143 WasmOp::I32Mul,
1144 WasmOp::I32Const(2),
1145 WasmOp::I32Add,
1146 WasmOp::End,
1147 ]
1148 );
1149 }
1150
1151 #[test]
1152 fn test_decode_multi_function_module() {
1153 let wat = r#"
1154 (module
1155 (func $helper)
1156 (func (export "add") (param i32 i32) (result i32)
1157 local.get 0
1158 local.get 1
1159 i32.add
1160 )
1161 (func (export "sub") (param i32 i32) (result i32)
1162 local.get 0
1163 local.get 1
1164 i32.sub
1165 )
1166 )
1167 "#;
1168
1169 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1170 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1171
1172 assert_eq!(functions.len(), 3);
1173 assert_eq!(functions[0].index, 0);
1174 assert_eq!(functions[0].export_name, None);
1175 assert_eq!(functions[1].index, 1);
1176 assert_eq!(functions[1].export_name, Some("add".to_string()));
1177 assert_eq!(functions[2].index, 2);
1178 assert_eq!(functions[2].export_name, Some("sub".to_string()));
1179 }
1180
1181 #[test]
1182 fn test_decode_module_with_imports() {
1183 let wat = r#"
1184 (module
1185 (import "env" "log" (func $log (param i32)))
1186 (import "env" "memory" (memory 1))
1187 (func (export "run") (param i32)
1188 local.get 0
1189 call 0
1190 )
1191 )
1192 "#;
1193
1194 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1195 let module = decode_wasm_module(&wasm).expect("Failed to decode");
1196
1197 assert_eq!(module.imports.len(), 2);
1199 assert_eq!(module.num_imported_funcs, 1);
1200
1201 assert_eq!(module.imports[0].module, "env");
1203 assert_eq!(module.imports[0].name, "log");
1204 assert!(matches!(module.imports[0].kind, ImportKind::Function(_)));
1205
1206 assert_eq!(module.imports[1].module, "env");
1208 assert_eq!(module.imports[1].name, "memory");
1209 assert_eq!(module.imports[1].kind, ImportKind::Memory);
1210
1211 assert_eq!(module.functions.len(), 1);
1213 assert_eq!(module.functions[0].index, 1);
1214 assert_eq!(module.functions[0].export_name, Some("run".to_string()));
1215 }
1216
1217 #[test]
1218 fn test_find_function_by_export_name() {
1219 let wat = r#"
1220 (module
1221 (func $helper)
1222 (func (export "add") (param i32 i32) (result i32)
1223 local.get 0
1224 local.get 1
1225 i32.add
1226 )
1227 )
1228 "#;
1229
1230 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1231 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1232
1233 let add_func = functions
1234 .iter()
1235 .find(|f| f.export_name.as_deref() == Some("add"))
1236 .expect("Should find 'add' function");
1237
1238 assert_eq!(add_func.index, 1);
1239 assert!(add_func.ops.contains(&WasmOp::I32Add));
1240 }
1241
1242 #[test]
1243 fn test_decode_subword_loads() {
1244 let wat = r#"
1245 (module
1246 (memory 1)
1247 (func (export "test") (param i32) (result i32)
1248 local.get 0
1249 i32.load8_u
1250 )
1251 )
1252 "#;
1253
1254 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1255 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1256
1257 assert_eq!(functions.len(), 1);
1258 assert!(functions[0].ops.contains(&WasmOp::I32Load8U {
1259 offset: 0,
1260 align: 0,
1261 }));
1262 }
1263
1264 #[test]
1265 fn test_decode_subword_stores() {
1266 let wat = r#"
1267 (module
1268 (memory 1)
1269 (func (export "test") (param i32 i32)
1270 local.get 0
1271 local.get 1
1272 i32.store8
1273 )
1274 )
1275 "#;
1276
1277 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1278 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1279
1280 assert_eq!(functions.len(), 1);
1281 assert!(functions[0].ops.contains(&WasmOp::I32Store8 {
1282 offset: 0,
1283 align: 0,
1284 }));
1285 }
1286
1287 #[test]
1288 fn test_decode_memory_size_grow() {
1289 let wat = r#"
1290 (module
1291 (memory 1)
1292 (func (export "test") (result i32)
1293 memory.size
1294 )
1295 )
1296 "#;
1297
1298 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1299 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1300
1301 assert_eq!(functions.len(), 1);
1302 assert!(functions[0].ops.contains(&WasmOp::MemorySize(0)));
1303 }
1304
1305 #[test]
1306 fn test_decode_memory_grow() {
1307 let wat = r#"
1308 (module
1309 (memory 1)
1310 (func (export "test") (param i32) (result i32)
1311 local.get 0
1312 memory.grow
1313 )
1314 )
1315 "#;
1316
1317 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1318 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1319
1320 assert_eq!(functions.len(), 1);
1321 assert!(functions[0].ops.contains(&WasmOp::MemoryGrow(0)));
1322 }
1323
1324 #[test]
1325 fn test_decode_bulk_memory_374() {
1326 let wat = r#"
1329 (module
1330 (memory 1)
1331 (func (export "cpy") (param i32 i32 i32)
1332 local.get 0 local.get 1 local.get 2 memory.copy)
1333 (func (export "fil") (param i32 i32 i32)
1334 local.get 0 local.get 1 local.get 2 memory.fill)
1335 )
1336 "#;
1337 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1338 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1339 assert_eq!(functions.len(), 2);
1340 assert!(functions[0].ops.contains(&WasmOp::MemoryCopy));
1341 assert!(functions[1].ops.contains(&WasmOp::MemoryFill));
1342 assert!(functions[0].unsupported.is_none());
1344 assert!(functions[1].unsupported.is_none());
1345 }
1346
1347 #[test]
1348 fn test_decode_i64_subword_loads() {
1349 let wat = r#"
1350 (module
1351 (memory 1)
1352 (func (export "test") (param i32) (result i64)
1353 local.get 0
1354 i64.load8_s
1355 )
1356 )
1357 "#;
1358
1359 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1360 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1361
1362 assert_eq!(functions.len(), 1);
1363 assert!(functions[0].ops.contains(&WasmOp::I64Load8S {
1364 offset: 0,
1365 align: 0,
1366 }));
1367 }
1368
1369 #[test]
1370 fn test_decode_all_subword_memory_ops() {
1371 let wat = r#"
1373 (module
1374 (memory 1)
1375 (func (export "test") (param i32)
1376 ;; i32 sub-word loads
1377 local.get 0
1378 i32.load8_s
1379 drop
1380 local.get 0
1381 i32.load8_u
1382 drop
1383 local.get 0
1384 i32.load16_s
1385 drop
1386 local.get 0
1387 i32.load16_u
1388 drop
1389
1390 ;; i32 sub-word stores
1391 local.get 0
1392 i32.const 42
1393 i32.store8
1394 local.get 0
1395 i32.const 42
1396 i32.store16
1397
1398 ;; i64 sub-word loads
1399 local.get 0
1400 i64.load8_s
1401 drop
1402 local.get 0
1403 i64.load8_u
1404 drop
1405 local.get 0
1406 i64.load16_s
1407 drop
1408 local.get 0
1409 i64.load16_u
1410 drop
1411 local.get 0
1412 i64.load32_s
1413 drop
1414 local.get 0
1415 i64.load32_u
1416 drop
1417
1418 ;; i64 sub-word stores
1419 local.get 0
1420 i64.const 42
1421 i64.store8
1422 local.get 0
1423 i64.const 42
1424 i64.store16
1425 local.get 0
1426 i64.const 42
1427 i64.store32
1428 )
1429 )
1430 "#;
1431
1432 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1433 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1434
1435 assert_eq!(functions.len(), 1);
1436 let ops = &functions[0].ops;
1437
1438 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load8S { .. })));
1440 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load8U { .. })));
1441 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load16S { .. })));
1442 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load16U { .. })));
1443 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Store8 { .. })));
1444 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Store16 { .. })));
1445
1446 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load8S { .. })));
1448 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load8U { .. })));
1449 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load16S { .. })));
1450 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load16U { .. })));
1451 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load32S { .. })));
1452 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load32U { .. })));
1453 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store8 { .. })));
1454 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store16 { .. })));
1455 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store32 { .. })));
1456 }
1457
1458 #[test]
1459 fn test_decode_simd_i32x4_add() {
1460 let wat = r#"
1461 (module
1462 (func (export "add_v128") (param v128 v128) (result v128)
1463 local.get 0
1464 local.get 1
1465 i32x4.add
1466 )
1467 )
1468 "#;
1469
1470 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1471 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1472
1473 assert_eq!(functions.len(), 1);
1474 assert!(
1475 functions[0].ops.contains(&WasmOp::I32x4Add),
1476 "Should decode i32x4.add: {:?}",
1477 functions[0].ops
1478 );
1479 }
1480
1481 #[test]
1482 fn test_decode_simd_v128_const() {
1483 let wat = r#"
1484 (module
1485 (func (export "const_v128") (result v128)
1486 v128.const i32x4 1 2 3 4
1487 )
1488 )
1489 "#;
1490
1491 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1492 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1493
1494 assert_eq!(functions.len(), 1);
1495 assert!(
1496 functions[0]
1497 .ops
1498 .iter()
1499 .any(|o| matches!(o, WasmOp::V128Const(_))),
1500 "Should decode v128.const: {:?}",
1501 functions[0].ops
1502 );
1503 }
1504
1505 #[test]
1506 fn test_decode_simd_v128_load_store() {
1507 let wat = r#"
1508 (module
1509 (memory 1)
1510 (func (export "load_store") (param i32)
1511 local.get 0
1512 v128.load
1513 local.get 0
1514 v128.store
1515 )
1516 )
1517 "#;
1518
1519 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1520 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1521
1522 assert_eq!(functions.len(), 1);
1523 let ops = &functions[0].ops;
1524 assert!(
1525 ops.iter().any(|o| matches!(o, WasmOp::V128Load { .. })),
1526 "Should decode v128.load"
1527 );
1528 assert!(
1529 ops.iter().any(|o| matches!(o, WasmOp::V128Store { .. })),
1530 "Should decode v128.store"
1531 );
1532 }
1533
1534 #[test]
1535 fn test_decode_simd_bitwise_ops() {
1536 let wat = r#"
1537 (module
1538 (func (export "bitwise") (param v128 v128) (result v128)
1539 local.get 0
1540 local.get 1
1541 v128.and
1542 )
1543 )
1544 "#;
1545
1546 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1547 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1548
1549 assert_eq!(functions.len(), 1);
1550 assert!(functions[0].ops.contains(&WasmOp::V128And));
1551 }
1552
1553 #[test]
1554 fn test_decode_simd_splat() {
1555 let wat = r#"
1556 (module
1557 (func (export "splat") (param i32) (result v128)
1558 local.get 0
1559 i32x4.splat
1560 )
1561 )
1562 "#;
1563
1564 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1565 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1566
1567 assert_eq!(functions.len(), 1);
1568 assert!(functions[0].ops.contains(&WasmOp::I32x4Splat));
1569 }
1570
1571 #[test]
1572 fn test_decode_simd_extract_lane() {
1573 let wat = r#"
1574 (module
1575 (func (export "extract") (param v128) (result i32)
1576 local.get 0
1577 i32x4.extract_lane 2
1578 )
1579 )
1580 "#;
1581
1582 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1583 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1584
1585 assert_eq!(functions.len(), 1);
1586 assert!(
1587 functions[0].ops.contains(&WasmOp::I32x4ExtractLane(2)),
1588 "Should decode i32x4.extract_lane 2"
1589 );
1590 }
1591
1592 #[test]
1593 fn test_decode_simd_f32x4_arithmetic() {
1594 let wat = r#"
1595 (module
1596 (func (export "f32x4_add") (param v128 v128) (result v128)
1597 local.get 0
1598 local.get 1
1599 f32x4.add
1600 )
1601 )
1602 "#;
1603
1604 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1605 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1606
1607 assert_eq!(functions.len(), 1);
1608 assert!(functions[0].ops.contains(&WasmOp::F32x4Add));
1609 }
1610
1611 #[test]
1612 fn test_369_scalar_float_op_flags_function_unsupported_not_dropped() {
1613 let wat = r#"
1617 (module
1618 (func (export "fadd") (param f32 f32) (result f32)
1619 local.get 0 local.get 1 f32.add)
1620 (func (export "iadd") (param i32 i32) (result i32)
1621 local.get 0 local.get 1 i32.add))
1622 "#;
1623 let wasm = wat::parse_str(wat).expect("parse");
1624 let functions = decode_wasm_functions(&wasm).expect("decode");
1625 let fadd = functions
1626 .iter()
1627 .find(|f| f.export_name.as_deref() == Some("fadd"))
1628 .unwrap();
1629 let iadd = functions
1630 .iter()
1631 .find(|f| f.export_name.as_deref() == Some("iadd"))
1632 .unwrap();
1633 assert!(
1634 fadd.unsupported.is_some(),
1635 "f32.add must flag the function unsupported (loud-skip), got {:?}",
1636 fadd.unsupported
1637 );
1638 assert!(
1639 fadd.unsupported.as_deref().unwrap().contains("F32Add"),
1640 "diagnostic should name the op: {:?}",
1641 fadd.unsupported
1642 );
1643 assert!(
1644 iadd.unsupported.is_none(),
1645 "a pure-integer function must NOT be flagged: {:?}",
1646 iadd.unsupported
1647 );
1648 }
1649
1650 #[test]
1651 fn test_decode_simd_multiple_ops() {
1652 let wat = r#"
1653 (module
1654 (func (export "simd_ops") (param v128 v128 v128) (result v128)
1655 ;; (a + b) * c
1656 local.get 0
1657 local.get 1
1658 i32x4.add
1659 local.get 2
1660 i32x4.mul
1661 )
1662 )
1663 "#;
1664
1665 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1666 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1667
1668 assert_eq!(functions.len(), 1);
1669 let ops = &functions[0].ops;
1670 assert!(ops.contains(&WasmOp::I32x4Add));
1671 assert!(ops.contains(&WasmOp::I32x4Mul));
1672 }
1673
1674 #[test]
1680 fn test_decode_records_aligned_increasing_op_offsets_dbg001() {
1681 let wat = r#"
1682 (module
1683 (func (export "f") (param i32 i32) (result i32)
1684 local.get 0
1685 local.get 1
1686 i32.add
1687 i32.const 7
1688 i32.mul))
1689 "#;
1690 let wasm = wat::parse_str(wat).expect("parse WAT");
1691 let functions = decode_wasm_functions(&wasm).expect("decode");
1692 let f = &functions[0];
1693
1694 assert_eq!(
1696 f.op_offsets.len(),
1697 f.ops.len(),
1698 "op_offsets must be parallel to ops"
1699 );
1700 assert!(!f.op_offsets.is_empty());
1701
1702 assert!(
1705 f.op_offsets.windows(2).all(|w| w[1] > w[0]),
1706 "wasm byte offsets must strictly increase: {:?}",
1707 f.op_offsets
1708 );
1709 assert!(
1710 f.op_offsets[0] >= 8,
1711 "module-relative offset is past the 8-byte wasm header"
1712 );
1713 }
1714
1715 #[test]
1718 fn test_decode_captures_global_initializer() {
1719 let wat = r#"
1720 (module
1721 (memory 2)
1722 (global $__stack_pointer (mut i32) (i32.const 65536))
1723 (global $immutable_const i32 (i32.const 7))
1724 (func (export "f") (result i32) global.get 0)
1725 )
1726 "#;
1727 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1728 let module = decode_wasm_module(&wasm).expect("Failed to decode");
1729
1730 assert_eq!(module.globals.len(), 2, "both globals captured");
1731 let sp = &module.globals[0];
1732 assert_eq!(sp.index, 0);
1733 assert_eq!(sp.init_i32, Some(65536), "stack-pointer init captured");
1734 assert!(sp.mutable, "stack pointer is mutable");
1735 let c = &module.globals[1];
1736 assert_eq!(c.init_i32, Some(7));
1737 assert!(!c.mutable, "second global is immutable");
1738 }
1739
1740 #[test]
1746 fn test_decode_records_block_arity_side_table_509() {
1747 let wat = r#"
1748 (module
1749 (func (export "f") (param i32) (result i32)
1750 (block (result i32)
1751 (block (nop))
1752 (local.get 0)
1753 (if (result i32)
1754 (then (i32.const 1))
1755 (else (i32.const 2)))))
1756 (func (export "g") (result i32)
1757 (block (result i32 i32)
1758 (i32.const 1) (i32.const 2))
1759 i32.add)
1760 (func (export "h") (param i32) (result i32)
1761 (local.get 0)
1762 (loop (param i32) (result i32))))
1763 "#;
1764 let wasm = wat::parse_str(wat).expect("parse WAT");
1765
1766 for functions in [
1768 decode_wasm_functions(&wasm).expect("decode"),
1769 decode_wasm_module(&wasm).expect("decode").functions,
1770 ] {
1771 assert_eq!(
1773 functions[0].block_arity,
1774 vec![(0, 1), (0, 0), (0, 1)],
1775 "f: ValType/Empty/ValType blocktypes"
1776 );
1777 assert_eq!(
1779 functions[1].block_arity,
1780 vec![(0, 2)],
1781 "g: functype blocktype result count from the type section"
1782 );
1783 assert_eq!(
1786 functions[2].block_arity,
1787 vec![(1, 1)],
1788 "h: loop params captured"
1789 );
1790 }
1791 }
1792}