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 let mut name_section_names: HashMap<u32, String> = HashMap::new();
151
152 for payload in Parser::new(0).parse_all(wasm_bytes) {
153 let payload = payload.context("Failed to parse WASM payload")?;
154
155 match payload {
156 Payload::TypeSection(reader) => {
157 for rec_group in reader {
160 let rec_group = rec_group.context("Failed to parse type")?;
161 for sub_ty in rec_group.types() {
162 let (count, ret_i64, params_i64) = match &sub_ty.composite_type.inner {
163 wasmparser::CompositeInnerType::Func(func_ty) => (
164 func_ty.params().len() as u32,
165 func_ty
166 .results()
167 .first()
168 .is_some_and(|t| *t == wasmparser::ValType::I64),
169 func_ty
174 .params()
175 .iter()
176 .map(|t| {
177 matches!(
178 t,
179 wasmparser::ValType::I64 | wasmparser::ValType::F64
180 )
181 })
182 .collect::<Vec<bool>>(),
183 ),
184 _ => (0, false, Vec::new()),
185 };
186 type_arg_counts.push(count);
187 type_ret_i64.push(ret_i64);
188 type_params_i64.push(params_i64);
189 }
190 }
191 }
192 Payload::ImportSection(reader) => {
193 for import in reader.into_imports() {
199 let import = import.context("Failed to parse import")?;
200 let (kind, idx) = match import.ty {
201 wasmparser::TypeRef::Func(type_idx) => {
202 let idx = num_imported_funcs;
203 num_imported_funcs += 1;
204 func_arg_counts
207 .push(type_arg_counts.get(type_idx as usize).copied().unwrap_or(0));
208 func_ret_i64.push(
209 type_ret_i64
210 .get(type_idx as usize)
211 .copied()
212 .unwrap_or(false),
213 );
214 func_params_i64.push(
215 type_params_i64
216 .get(type_idx as usize)
217 .cloned()
218 .unwrap_or_default(),
219 );
220 (ImportKind::Function(type_idx), idx)
221 }
222 wasmparser::TypeRef::Memory(_) => (ImportKind::Memory, 0),
223 wasmparser::TypeRef::Table(_) => (ImportKind::Table, 0),
224 wasmparser::TypeRef::Global(_) => (ImportKind::Global, 0),
225 _ => continue,
226 };
227 imports.push(ImportEntry {
228 module: import.module.to_string(),
229 name: import.name.to_string(),
230 kind,
231 index: idx,
232 });
233 }
234 }
235 Payload::FunctionSection(reader) => {
236 for ty in reader {
241 let type_idx = ty.context("Failed to parse function type index")?;
242 func_arg_counts
243 .push(type_arg_counts.get(type_idx as usize).copied().unwrap_or(0));
244 func_ret_i64.push(
245 type_ret_i64
246 .get(type_idx as usize)
247 .copied()
248 .unwrap_or(false),
249 );
250 func_params_i64.push(
251 type_params_i64
252 .get(type_idx as usize)
253 .cloned()
254 .unwrap_or_default(),
255 );
256 }
257 }
258 Payload::MemorySection(reader) => {
259 for (idx, memory) in reader.into_iter().enumerate() {
260 let mem = memory.context("Failed to parse memory")?;
261 memories.push(WasmMemory {
262 index: idx as u32,
263 initial_pages: mem.initial as u32,
264 max_pages: mem.maximum.map(|m| m as u32),
265 shared: mem.shared,
266 });
267 }
268 }
269 Payload::GlobalSection(reader) => {
270 for (idx, global) in reader.into_iter().enumerate() {
276 let global = global.context("Failed to parse global")?;
277 let mut init_i32 = None;
278 let mut ops = global.init_expr.get_operators_reader();
279 if let Ok(wasmparser::Operator::I32Const { value }) = ops.read() {
280 init_i32 = Some(value);
281 }
282 globals.push(WasmGlobal {
283 index: idx as u32,
284 init_i32,
285 mutable: global.ty.mutable,
286 });
287 }
288 }
289 Payload::DataSection(reader) => {
290 for data in reader {
291 let data = data.context("Failed to parse data segment")?;
292 if let wasmparser::DataKind::Active {
293 memory_index: 0,
294 offset_expr,
295 } = data.kind
296 {
297 let mut ops = offset_expr.get_operators_reader();
298 if let Ok(wasmparser::Operator::I32Const { value }) = ops.read() {
299 data_segments.push((value as u32, data.data.to_vec()));
300 }
301 }
302 }
303 }
304 Payload::ElementSection(reader) => {
305 for elem in reader {
312 let elem = elem.context("Failed to parse element segment")?;
313 match elem.items {
314 wasmparser::ElementItems::Functions(funcs) => {
315 for f in funcs {
316 elem_func_indices
317 .push(f.context("Failed to parse element func index")?);
318 }
319 }
320 wasmparser::ElementItems::Expressions(_, exprs) => {
321 for expr in exprs {
322 let expr = expr.context("Failed to parse element expr")?;
323 for op in expr.get_operators_reader() {
324 if let wasmparser::Operator::RefFunc { function_index } =
325 op.context("Failed to parse element op")?
326 {
327 elem_func_indices.push(function_index);
328 }
329 }
330 }
331 }
332 }
333 }
334 }
335 Payload::ExportSection(exports) => {
336 for export in exports {
337 let export = export.context("Failed to parse export")?;
338 if export.kind == ExternalKind::Func {
339 export_names.insert(export.index, export.name.to_string());
340 }
341 }
342 }
343 Payload::CodeSectionEntry(body) => {
344 let (ops, op_offsets, unsupported) = decode_function_body(&body)?;
345 let actual_index = num_imported_funcs + func_index;
346 let export_name = export_names.get(&actual_index).cloned();
347
348 functions.push(FunctionOps {
349 index: actual_index,
350 export_name,
351 debug_name: None, ops,
353 op_offsets,
354 unsupported,
355 });
356 func_index += 1;
357 }
358 Payload::CustomSection(c) => {
359 if let wasmparser::KnownCustom::Name(reader) = c.as_known() {
361 parse_name_section_func_names(reader, &mut name_section_names);
362 }
363 }
364 _ => {}
365 }
366 }
367
368 apply_name_section(&mut functions, &name_section_names);
369
370 Ok(DecodedModule {
371 functions,
372 memories,
373 data_segments,
374 imports,
375 num_imported_funcs,
376 func_arg_counts,
377 type_arg_counts,
378 func_ret_i64,
379 type_ret_i64,
380 func_params_i64,
381 globals,
382 elem_func_indices,
383 })
384}
385
386fn parse_name_section_func_names(
392 reader: wasmparser::NameSectionReader<'_>,
393 out: &mut HashMap<u32, String>,
394) {
395 for subsection in reader.into_iter().flatten() {
396 if let wasmparser::Name::Function(map) = subsection {
397 for naming in map.into_iter().flatten() {
398 out.insert(naming.index, naming.name.to_string());
399 }
400 }
401 }
402}
403
404fn apply_name_section(functions: &mut [FunctionOps], names: &HashMap<u32, String>) {
408 if names.is_empty() {
409 return;
410 }
411 for f in functions {
412 f.debug_name = names.get(&f.index).cloned();
413 }
414}
415
416pub fn decode_wasm_functions(wasm_bytes: &[u8]) -> Result<Vec<FunctionOps>> {
418 let mut functions = Vec::new();
419 let mut func_index = 0u32;
420 let mut num_imported_funcs = 0u32;
421 let mut export_names: HashMap<u32, String> = HashMap::new();
422 let mut name_section_names: HashMap<u32, String> = HashMap::new();
423
424 for payload in Parser::new(0).parse_all(wasm_bytes) {
425 let payload = payload.context("Failed to parse WASM payload")?;
426
427 match payload {
428 Payload::ImportSection(imports) => {
429 for import in imports.into_imports() {
432 let import = import.context("Failed to parse import")?;
433 if matches!(import.ty, wasmparser::TypeRef::Func(_)) {
434 num_imported_funcs += 1;
435 }
436 }
437 }
438 Payload::ExportSection(exports) => {
439 for export in exports {
440 let export = export.context("Failed to parse export")?;
441 if export.kind == ExternalKind::Func {
442 export_names.insert(export.index, export.name.to_string());
443 }
444 }
445 }
446 Payload::CodeSectionEntry(body) => {
447 let (ops, op_offsets, unsupported) = decode_function_body(&body)?;
448 let actual_index = num_imported_funcs + func_index;
449 let export_name = export_names.get(&actual_index).cloned();
450
451 functions.push(FunctionOps {
452 index: actual_index,
453 export_name,
454 debug_name: None, ops,
456 op_offsets,
457 unsupported,
458 });
459 func_index += 1;
460 }
461 Payload::CustomSection(c) => {
462 if let wasmparser::KnownCustom::Name(reader) = c.as_known() {
464 parse_name_section_func_names(reader, &mut name_section_names);
465 }
466 }
467 _ => {}
468 }
469 }
470
471 apply_name_section(&mut functions, &name_section_names);
472
473 Ok(functions)
474}
475
476#[derive(Debug, Clone)]
478pub struct FunctionOps {
479 pub index: u32,
481 pub export_name: Option<String>,
483 pub debug_name: Option<String>,
492 pub ops: Vec<WasmOp>,
494 pub op_offsets: Vec<u32>,
502 pub unsupported: Option<String>,
512}
513
514fn decode_function_body(
520 body: &wasmparser::FunctionBody,
521) -> Result<(Vec<WasmOp>, Vec<u32>, Option<String>)> {
522 let mut ops = Vec::new();
523 let mut op_offsets = Vec::new();
528 let mut unsupported: Option<String> = None;
529
530 let ops_reader = body.get_operators_reader()?;
531 for item in ops_reader.into_iter_with_offsets() {
532 let (op, offset) = item.context("Failed to read operator")?;
533
534 if let Some(wasm_op) = convert_operator(&op) {
535 ops.push(wasm_op);
536 op_offsets.push(offset as u32);
537 } else if unsupported.is_none() && !is_intentionally_ignored(&op) {
538 unsupported = Some(format!("{op:?}"));
542 }
543 }
544
545 Ok((ops, op_offsets, unsupported))
546}
547
548fn is_intentionally_ignored(op: &wasmparser::Operator) -> bool {
553 use wasmparser::Operator::*;
554 matches!(op, Nop | Unreachable)
555}
556
557fn convert_operator(op: &wasmparser::Operator) -> Option<WasmOp> {
559 use wasmparser::Operator::*;
560
561 match op {
562 I32Const { value } => Some(WasmOp::I32Const(*value)),
564
565 I32Add => Some(WasmOp::I32Add),
567 I32Sub => Some(WasmOp::I32Sub),
568 I32Mul => Some(WasmOp::I32Mul),
569 I32DivS => Some(WasmOp::I32DivS),
570 I32DivU => Some(WasmOp::I32DivU),
571 I32RemS => Some(WasmOp::I32RemS),
572 I32RemU => Some(WasmOp::I32RemU),
573
574 I64Const { value } => Some(WasmOp::I64Const(*value)),
576
577 I64Add => Some(WasmOp::I64Add),
579 I64Sub => Some(WasmOp::I64Sub),
580 I64Mul => Some(WasmOp::I64Mul),
581 I64DivS => Some(WasmOp::I64DivS),
582 I64DivU => Some(WasmOp::I64DivU),
583 I64RemS => Some(WasmOp::I64RemS),
584 I64RemU => Some(WasmOp::I64RemU),
585
586 I64And => Some(WasmOp::I64And),
588 I64Or => Some(WasmOp::I64Or),
589 I64Xor => Some(WasmOp::I64Xor),
590 I64Shl => Some(WasmOp::I64Shl),
591 I64ShrS => Some(WasmOp::I64ShrS),
592 I64ShrU => Some(WasmOp::I64ShrU),
593 I64Rotl => Some(WasmOp::I64Rotl),
594 I64Rotr => Some(WasmOp::I64Rotr),
595 I64Clz => Some(WasmOp::I64Clz),
596 I64Ctz => Some(WasmOp::I64Ctz),
597 I64Popcnt => Some(WasmOp::I64Popcnt),
598 I64Extend8S => Some(WasmOp::I64Extend8S),
599 I64Extend16S => Some(WasmOp::I64Extend16S),
600 I64Extend32S => Some(WasmOp::I64Extend32S),
601 I64ExtendI32U => Some(WasmOp::I64ExtendI32U),
607 I64ExtendI32S => Some(WasmOp::I64ExtendI32S),
608 I32WrapI64 => Some(WasmOp::I32WrapI64),
609
610 I64Eqz => Some(WasmOp::I64Eqz),
612 I64Eq => Some(WasmOp::I64Eq),
613 I64Ne => Some(WasmOp::I64Ne),
614 I64LtS => Some(WasmOp::I64LtS),
615 I64LtU => Some(WasmOp::I64LtU),
616 I64LeS => Some(WasmOp::I64LeS),
617 I64LeU => Some(WasmOp::I64LeU),
618 I64GtS => Some(WasmOp::I64GtS),
619 I64GtU => Some(WasmOp::I64GtU),
620 I64GeS => Some(WasmOp::I64GeS),
621 I64GeU => Some(WasmOp::I64GeU),
622
623 I32And => Some(WasmOp::I32And),
625 I32Or => Some(WasmOp::I32Or),
626 I32Xor => Some(WasmOp::I32Xor),
627 I32Shl => Some(WasmOp::I32Shl),
628 I32ShrS => Some(WasmOp::I32ShrS),
629 I32ShrU => Some(WasmOp::I32ShrU),
630 I32Rotl => Some(WasmOp::I32Rotl),
631 I32Rotr => Some(WasmOp::I32Rotr),
632 I32Clz => Some(WasmOp::I32Clz),
633 I32Ctz => Some(WasmOp::I32Ctz),
634 I32Popcnt => Some(WasmOp::I32Popcnt),
635 I32Extend8S => Some(WasmOp::I32Extend8S),
636 I32Extend16S => Some(WasmOp::I32Extend16S),
637
638 I32Eqz => Some(WasmOp::I32Eqz),
640 I32Eq => Some(WasmOp::I32Eq),
641 I32Ne => Some(WasmOp::I32Ne),
642 I32LtS => Some(WasmOp::I32LtS),
643 I32LtU => Some(WasmOp::I32LtU),
644 I32LeS => Some(WasmOp::I32LeS),
645 I32LeU => Some(WasmOp::I32LeU),
646 I32GtS => Some(WasmOp::I32GtS),
647 I32GtU => Some(WasmOp::I32GtU),
648 I32GeS => Some(WasmOp::I32GeS),
649 I32GeU => Some(WasmOp::I32GeU),
650
651 I32Load { memarg } => Some(WasmOp::I32Load {
653 offset: memarg.offset as u32,
654 align: memarg.align as u32,
655 }),
656 I32Store { memarg } => Some(WasmOp::I32Store {
657 offset: memarg.offset as u32,
658 align: memarg.align as u32,
659 }),
660 I64Load { memarg } => Some(WasmOp::I64Load {
667 offset: memarg.offset as u32,
668 align: memarg.align as u32,
669 }),
670 I64Store { memarg } => Some(WasmOp::I64Store {
671 offset: memarg.offset as u32,
672 align: memarg.align as u32,
673 }),
674
675 I32Load8S { memarg } => Some(WasmOp::I32Load8S {
677 offset: memarg.offset as u32,
678 align: memarg.align as u32,
679 }),
680 I32Load8U { memarg } => Some(WasmOp::I32Load8U {
681 offset: memarg.offset as u32,
682 align: memarg.align as u32,
683 }),
684 I32Load16S { memarg } => Some(WasmOp::I32Load16S {
685 offset: memarg.offset as u32,
686 align: memarg.align as u32,
687 }),
688 I32Load16U { memarg } => Some(WasmOp::I32Load16U {
689 offset: memarg.offset as u32,
690 align: memarg.align as u32,
691 }),
692
693 I32Store8 { memarg } => Some(WasmOp::I32Store8 {
695 offset: memarg.offset as u32,
696 align: memarg.align as u32,
697 }),
698 I32Store16 { memarg } => Some(WasmOp::I32Store16 {
699 offset: memarg.offset as u32,
700 align: memarg.align as u32,
701 }),
702
703 LocalGet { local_index } => Some(WasmOp::LocalGet(*local_index)),
705 LocalSet { local_index } => Some(WasmOp::LocalSet(*local_index)),
706 LocalTee { local_index } => Some(WasmOp::LocalTee(*local_index)),
707 GlobalGet { global_index } => Some(WasmOp::GlobalGet(*global_index)),
708 GlobalSet { global_index } => Some(WasmOp::GlobalSet(*global_index)),
709
710 Block { .. } => Some(WasmOp::Block),
712 Loop { .. } => Some(WasmOp::Loop),
713 Br { relative_depth } => Some(WasmOp::Br(*relative_depth)),
714 BrIf { relative_depth } => Some(WasmOp::BrIf(*relative_depth)),
715 BrTable { targets } => {
721 let default = targets.default();
722 let tgts: Vec<u32> = targets.targets().filter_map(Result::ok).collect();
723 Some(WasmOp::BrTable {
724 targets: tgts,
725 default,
726 })
727 }
728 Return => Some(WasmOp::Return),
729 Call { function_index } => Some(WasmOp::Call(*function_index)),
730 CallIndirect {
731 type_index,
732 table_index,
733 ..
734 } => Some(WasmOp::CallIndirect {
735 type_index: *type_index,
736 table_index: *table_index,
737 }),
738
739 End => Some(WasmOp::End),
741
742 Nop | Unreachable => None,
744
745 Drop => Some(WasmOp::Drop),
747
748 Select => Some(WasmOp::Select),
750
751 If { .. } => Some(WasmOp::If),
753 Else => Some(WasmOp::Else),
754
755 I64Load8S { memarg } => Some(WasmOp::I64Load8S {
757 offset: memarg.offset as u32,
758 align: memarg.align as u32,
759 }),
760 I64Load8U { memarg } => Some(WasmOp::I64Load8U {
761 offset: memarg.offset as u32,
762 align: memarg.align as u32,
763 }),
764 I64Load16S { memarg } => Some(WasmOp::I64Load16S {
765 offset: memarg.offset as u32,
766 align: memarg.align as u32,
767 }),
768 I64Load16U { memarg } => Some(WasmOp::I64Load16U {
769 offset: memarg.offset as u32,
770 align: memarg.align as u32,
771 }),
772 I64Load32S { memarg } => Some(WasmOp::I64Load32S {
773 offset: memarg.offset as u32,
774 align: memarg.align as u32,
775 }),
776 I64Load32U { memarg } => Some(WasmOp::I64Load32U {
777 offset: memarg.offset as u32,
778 align: memarg.align as u32,
779 }),
780
781 I64Store8 { memarg } => Some(WasmOp::I64Store8 {
783 offset: memarg.offset as u32,
784 align: memarg.align as u32,
785 }),
786 I64Store16 { memarg } => Some(WasmOp::I64Store16 {
787 offset: memarg.offset as u32,
788 align: memarg.align as u32,
789 }),
790 I64Store32 { memarg } => Some(WasmOp::I64Store32 {
791 offset: memarg.offset as u32,
792 align: memarg.align as u32,
793 }),
794
795 MemorySize { mem, .. } => Some(WasmOp::MemorySize(*mem)),
797 MemoryGrow { mem, .. } => Some(WasmOp::MemoryGrow(*mem)),
798
799 MemoryCopy {
806 dst_mem: 0,
807 src_mem: 0,
808 } => Some(WasmOp::MemoryCopy),
809 MemoryFill { mem: 0 } => Some(WasmOp::MemoryFill),
810
811 V128Const { value } => {
815 let mut bytes = [0u8; 16];
816 bytes.copy_from_slice(value.bytes());
817 Some(WasmOp::V128Const(bytes))
818 }
819 V128Load { memarg } => Some(WasmOp::V128Load {
820 offset: memarg.offset as u32,
821 align: memarg.align as u32,
822 }),
823 V128Store { memarg } => Some(WasmOp::V128Store {
824 offset: memarg.offset as u32,
825 align: memarg.align as u32,
826 }),
827
828 V128And => Some(WasmOp::V128And),
830 V128Or => Some(WasmOp::V128Or),
831 V128Xor => Some(WasmOp::V128Xor),
832 V128Not => Some(WasmOp::V128Not),
833 V128AndNot => Some(WasmOp::V128AndNot),
834
835 I8x16Add => Some(WasmOp::I8x16Add),
837 I8x16Sub => Some(WasmOp::I8x16Sub),
838 I8x16Neg => Some(WasmOp::I8x16Neg),
839 I8x16Eq => Some(WasmOp::I8x16Eq),
840 I8x16Ne => Some(WasmOp::I8x16Ne),
841 I8x16LtS => Some(WasmOp::I8x16LtS),
842 I8x16LtU => Some(WasmOp::I8x16LtU),
843 I8x16GtS => Some(WasmOp::I8x16GtS),
844 I8x16GtU => Some(WasmOp::I8x16GtU),
845 I8x16LeS => Some(WasmOp::I8x16LeS),
846 I8x16LeU => Some(WasmOp::I8x16LeU),
847 I8x16GeS => Some(WasmOp::I8x16GeS),
848 I8x16GeU => Some(WasmOp::I8x16GeU),
849 I8x16Splat => Some(WasmOp::I8x16Splat),
850 I8x16ExtractLaneS { lane } => Some(WasmOp::I8x16ExtractLaneS(*lane)),
851 I8x16ExtractLaneU { lane } => Some(WasmOp::I8x16ExtractLaneU(*lane)),
852 I8x16ReplaceLane { lane } => Some(WasmOp::I8x16ReplaceLane(*lane)),
853 I8x16Shuffle { lanes } => Some(WasmOp::I8x16Shuffle(*lanes)),
854 I8x16Swizzle => Some(WasmOp::I8x16Swizzle),
855
856 I16x8Add => Some(WasmOp::I16x8Add),
858 I16x8Sub => Some(WasmOp::I16x8Sub),
859 I16x8Mul => Some(WasmOp::I16x8Mul),
860 I16x8Neg => Some(WasmOp::I16x8Neg),
861 I16x8Eq => Some(WasmOp::I16x8Eq),
862 I16x8Ne => Some(WasmOp::I16x8Ne),
863 I16x8LtS => Some(WasmOp::I16x8LtS),
864 I16x8LtU => Some(WasmOp::I16x8LtU),
865 I16x8GtS => Some(WasmOp::I16x8GtS),
866 I16x8GtU => Some(WasmOp::I16x8GtU),
867 I16x8LeS => Some(WasmOp::I16x8LeS),
868 I16x8LeU => Some(WasmOp::I16x8LeU),
869 I16x8GeS => Some(WasmOp::I16x8GeS),
870 I16x8GeU => Some(WasmOp::I16x8GeU),
871 I16x8Splat => Some(WasmOp::I16x8Splat),
872 I16x8ExtractLaneS { lane } => Some(WasmOp::I16x8ExtractLaneS(*lane)),
873 I16x8ExtractLaneU { lane } => Some(WasmOp::I16x8ExtractLaneU(*lane)),
874 I16x8ReplaceLane { lane } => Some(WasmOp::I16x8ReplaceLane(*lane)),
875
876 I32x4Add => Some(WasmOp::I32x4Add),
878 I32x4Sub => Some(WasmOp::I32x4Sub),
879 I32x4Mul => Some(WasmOp::I32x4Mul),
880 I32x4Neg => Some(WasmOp::I32x4Neg),
881 I32x4Eq => Some(WasmOp::I32x4Eq),
882 I32x4Ne => Some(WasmOp::I32x4Ne),
883 I32x4LtS => Some(WasmOp::I32x4LtS),
884 I32x4LtU => Some(WasmOp::I32x4LtU),
885 I32x4GtS => Some(WasmOp::I32x4GtS),
886 I32x4GtU => Some(WasmOp::I32x4GtU),
887 I32x4LeS => Some(WasmOp::I32x4LeS),
888 I32x4LeU => Some(WasmOp::I32x4LeU),
889 I32x4GeS => Some(WasmOp::I32x4GeS),
890 I32x4GeU => Some(WasmOp::I32x4GeU),
891 I32x4Splat => Some(WasmOp::I32x4Splat),
892 I32x4ExtractLane { lane } => Some(WasmOp::I32x4ExtractLane(*lane)),
893 I32x4ReplaceLane { lane } => Some(WasmOp::I32x4ReplaceLane(*lane)),
894
895 I64x2Add => Some(WasmOp::I64x2Add),
897 I64x2Sub => Some(WasmOp::I64x2Sub),
898 I64x2Mul => Some(WasmOp::I64x2Mul),
899 I64x2Neg => Some(WasmOp::I64x2Neg),
900 I64x2Eq => Some(WasmOp::I64x2Eq),
901 I64x2Ne => Some(WasmOp::I64x2Ne),
902 I64x2LtS => Some(WasmOp::I64x2LtS),
903 I64x2GtS => Some(WasmOp::I64x2GtS),
904 I64x2LeS => Some(WasmOp::I64x2LeS),
905 I64x2GeS => Some(WasmOp::I64x2GeS),
906 I64x2Splat => Some(WasmOp::I64x2Splat),
907 I64x2ExtractLane { lane } => Some(WasmOp::I64x2ExtractLane(*lane)),
908 I64x2ReplaceLane { lane } => Some(WasmOp::I64x2ReplaceLane(*lane)),
909
910 F32x4Add => Some(WasmOp::F32x4Add),
912 F32x4Sub => Some(WasmOp::F32x4Sub),
913 F32x4Mul => Some(WasmOp::F32x4Mul),
914 F32x4Div => Some(WasmOp::F32x4Div),
915 F32x4Abs => Some(WasmOp::F32x4Abs),
916 F32x4Neg => Some(WasmOp::F32x4Neg),
917 F32x4Sqrt => Some(WasmOp::F32x4Sqrt),
918 F32x4Eq => Some(WasmOp::F32x4Eq),
919 F32x4Ne => Some(WasmOp::F32x4Ne),
920 F32x4Lt => Some(WasmOp::F32x4Lt),
921 F32x4Le => Some(WasmOp::F32x4Le),
922 F32x4Gt => Some(WasmOp::F32x4Gt),
923 F32x4Ge => Some(WasmOp::F32x4Ge),
924 F32x4Splat => Some(WasmOp::F32x4Splat),
925 F32x4ExtractLane { lane } => Some(WasmOp::F32x4ExtractLane(*lane)),
926 F32x4ReplaceLane { lane } => Some(WasmOp::F32x4ReplaceLane(*lane)),
927
928 _ => None,
930 }
931}
932
933#[cfg(test)]
934mod tests {
935 use super::*;
936
937 #[test]
938 fn test_decode_simple_add() {
939 let wat = r#"
940 (module
941 (func (export "add") (param i32 i32) (result i32)
942 local.get 0
943 local.get 1
944 i32.add
945 )
946 )
947 "#;
948
949 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
950 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
951
952 assert_eq!(functions.len(), 1);
953 assert_eq!(functions[0].index, 0);
954 assert_eq!(functions[0].export_name, Some("add".to_string()));
955 assert_eq!(
956 functions[0].ops,
957 vec![
958 WasmOp::LocalGet(0),
959 WasmOp::LocalGet(1),
960 WasmOp::I32Add,
961 WasmOp::End
962 ]
963 );
964 }
965
966 #[test]
972 fn test_decode_i64_i32_width_conversions() {
973 let wat = r#"
974 (module
975 (func (export "conv") (param i32 i64) (result i32)
976 local.get 0
977 i64.extend_i32_u
978 local.get 0
979 i64.extend_i32_s
980 i64.add
981 local.get 1
982 i64.add
983 i32.wrap_i64
984 )
985 )
986 "#;
987 let wasm = wat::parse_str(wat).expect("parse");
988 let functions = decode_wasm_functions(&wasm).expect("decode");
989 let ops = &functions[0].ops;
990 assert!(
991 ops.contains(&WasmOp::I64ExtendI32U),
992 "i64.extend_i32_u must decode (not be dropped): {ops:?}"
993 );
994 assert!(
995 ops.contains(&WasmOp::I64ExtendI32S),
996 "i64.extend_i32_s must decode (not be dropped): {ops:?}"
997 );
998 assert!(
999 ops.contains(&WasmOp::I32WrapI64),
1000 "i32.wrap_i64 must decode (not be dropped): {ops:?}"
1001 );
1002 }
1003
1004 #[test]
1009 fn test_decode_br_table() {
1010 let wat = r#"
1011 (module
1012 (func (export "bt") (param i32) (result i32)
1013 (block (block (block
1014 local.get 0
1015 br_table 2 0 1 2)
1016 i32.const 30 return)
1017 i32.const 20 return)
1018 i32.const 10))
1019 "#;
1020 let wasm = wat::parse_str(wat).expect("parse");
1021 let functions = decode_wasm_functions(&wasm).expect("decode");
1022 let bt = functions[0]
1023 .ops
1024 .iter()
1025 .find_map(|o| match o {
1026 WasmOp::BrTable { targets, default } => Some((targets.clone(), *default)),
1027 _ => None,
1028 })
1029 .expect("br_table must decode (not be dropped)");
1030 assert_eq!(bt.0, vec![2, 0, 1], "br_table targets preserved in order");
1031 assert_eq!(bt.1, 2, "br_table default preserved");
1032 }
1033
1034 #[test]
1035 fn test_decode_arithmetic() {
1036 let wat = r#"
1037 (module
1038 (func (export "calc") (result i32)
1039 i32.const 5
1040 i32.const 3
1041 i32.mul
1042 i32.const 2
1043 i32.add
1044 )
1045 )
1046 "#;
1047
1048 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1049 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1050
1051 assert_eq!(functions.len(), 1);
1052 assert_eq!(functions[0].export_name, Some("calc".to_string()));
1053 assert_eq!(
1054 functions[0].ops,
1055 vec![
1056 WasmOp::I32Const(5),
1057 WasmOp::I32Const(3),
1058 WasmOp::I32Mul,
1059 WasmOp::I32Const(2),
1060 WasmOp::I32Add,
1061 WasmOp::End,
1062 ]
1063 );
1064 }
1065
1066 #[test]
1067 fn test_decode_multi_function_module() {
1068 let wat = r#"
1069 (module
1070 (func $helper)
1071 (func (export "add") (param i32 i32) (result i32)
1072 local.get 0
1073 local.get 1
1074 i32.add
1075 )
1076 (func (export "sub") (param i32 i32) (result i32)
1077 local.get 0
1078 local.get 1
1079 i32.sub
1080 )
1081 )
1082 "#;
1083
1084 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1085 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1086
1087 assert_eq!(functions.len(), 3);
1088 assert_eq!(functions[0].index, 0);
1089 assert_eq!(functions[0].export_name, None);
1090 assert_eq!(functions[1].index, 1);
1091 assert_eq!(functions[1].export_name, Some("add".to_string()));
1092 assert_eq!(functions[2].index, 2);
1093 assert_eq!(functions[2].export_name, Some("sub".to_string()));
1094 }
1095
1096 #[test]
1097 fn test_decode_module_with_imports() {
1098 let wat = r#"
1099 (module
1100 (import "env" "log" (func $log (param i32)))
1101 (import "env" "memory" (memory 1))
1102 (func (export "run") (param i32)
1103 local.get 0
1104 call 0
1105 )
1106 )
1107 "#;
1108
1109 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1110 let module = decode_wasm_module(&wasm).expect("Failed to decode");
1111
1112 assert_eq!(module.imports.len(), 2);
1114 assert_eq!(module.num_imported_funcs, 1);
1115
1116 assert_eq!(module.imports[0].module, "env");
1118 assert_eq!(module.imports[0].name, "log");
1119 assert!(matches!(module.imports[0].kind, ImportKind::Function(_)));
1120
1121 assert_eq!(module.imports[1].module, "env");
1123 assert_eq!(module.imports[1].name, "memory");
1124 assert_eq!(module.imports[1].kind, ImportKind::Memory);
1125
1126 assert_eq!(module.functions.len(), 1);
1128 assert_eq!(module.functions[0].index, 1);
1129 assert_eq!(module.functions[0].export_name, Some("run".to_string()));
1130 }
1131
1132 #[test]
1133 fn test_find_function_by_export_name() {
1134 let wat = r#"
1135 (module
1136 (func $helper)
1137 (func (export "add") (param i32 i32) (result i32)
1138 local.get 0
1139 local.get 1
1140 i32.add
1141 )
1142 )
1143 "#;
1144
1145 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1146 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1147
1148 let add_func = functions
1149 .iter()
1150 .find(|f| f.export_name.as_deref() == Some("add"))
1151 .expect("Should find 'add' function");
1152
1153 assert_eq!(add_func.index, 1);
1154 assert!(add_func.ops.contains(&WasmOp::I32Add));
1155 }
1156
1157 #[test]
1158 fn test_decode_subword_loads() {
1159 let wat = r#"
1160 (module
1161 (memory 1)
1162 (func (export "test") (param i32) (result i32)
1163 local.get 0
1164 i32.load8_u
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(), 1);
1173 assert!(functions[0].ops.contains(&WasmOp::I32Load8U {
1174 offset: 0,
1175 align: 0,
1176 }));
1177 }
1178
1179 #[test]
1180 fn test_decode_subword_stores() {
1181 let wat = r#"
1182 (module
1183 (memory 1)
1184 (func (export "test") (param i32 i32)
1185 local.get 0
1186 local.get 1
1187 i32.store8
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::I32Store8 {
1197 offset: 0,
1198 align: 0,
1199 }));
1200 }
1201
1202 #[test]
1203 fn test_decode_memory_size_grow() {
1204 let wat = r#"
1205 (module
1206 (memory 1)
1207 (func (export "test") (result i32)
1208 memory.size
1209 )
1210 )
1211 "#;
1212
1213 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1214 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1215
1216 assert_eq!(functions.len(), 1);
1217 assert!(functions[0].ops.contains(&WasmOp::MemorySize(0)));
1218 }
1219
1220 #[test]
1221 fn test_decode_memory_grow() {
1222 let wat = r#"
1223 (module
1224 (memory 1)
1225 (func (export "test") (param i32) (result i32)
1226 local.get 0
1227 memory.grow
1228 )
1229 )
1230 "#;
1231
1232 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1233 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1234
1235 assert_eq!(functions.len(), 1);
1236 assert!(functions[0].ops.contains(&WasmOp::MemoryGrow(0)));
1237 }
1238
1239 #[test]
1240 fn test_decode_bulk_memory_374() {
1241 let wat = r#"
1244 (module
1245 (memory 1)
1246 (func (export "cpy") (param i32 i32 i32)
1247 local.get 0 local.get 1 local.get 2 memory.copy)
1248 (func (export "fil") (param i32 i32 i32)
1249 local.get 0 local.get 1 local.get 2 memory.fill)
1250 )
1251 "#;
1252 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1253 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1254 assert_eq!(functions.len(), 2);
1255 assert!(functions[0].ops.contains(&WasmOp::MemoryCopy));
1256 assert!(functions[1].ops.contains(&WasmOp::MemoryFill));
1257 assert!(functions[0].unsupported.is_none());
1259 assert!(functions[1].unsupported.is_none());
1260 }
1261
1262 #[test]
1263 fn test_decode_i64_subword_loads() {
1264 let wat = r#"
1265 (module
1266 (memory 1)
1267 (func (export "test") (param i32) (result i64)
1268 local.get 0
1269 i64.load8_s
1270 )
1271 )
1272 "#;
1273
1274 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
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::I64Load8S {
1279 offset: 0,
1280 align: 0,
1281 }));
1282 }
1283
1284 #[test]
1285 fn test_decode_all_subword_memory_ops() {
1286 let wat = r#"
1288 (module
1289 (memory 1)
1290 (func (export "test") (param i32)
1291 ;; i32 sub-word loads
1292 local.get 0
1293 i32.load8_s
1294 drop
1295 local.get 0
1296 i32.load8_u
1297 drop
1298 local.get 0
1299 i32.load16_s
1300 drop
1301 local.get 0
1302 i32.load16_u
1303 drop
1304
1305 ;; i32 sub-word stores
1306 local.get 0
1307 i32.const 42
1308 i32.store8
1309 local.get 0
1310 i32.const 42
1311 i32.store16
1312
1313 ;; i64 sub-word loads
1314 local.get 0
1315 i64.load8_s
1316 drop
1317 local.get 0
1318 i64.load8_u
1319 drop
1320 local.get 0
1321 i64.load16_s
1322 drop
1323 local.get 0
1324 i64.load16_u
1325 drop
1326 local.get 0
1327 i64.load32_s
1328 drop
1329 local.get 0
1330 i64.load32_u
1331 drop
1332
1333 ;; i64 sub-word stores
1334 local.get 0
1335 i64.const 42
1336 i64.store8
1337 local.get 0
1338 i64.const 42
1339 i64.store16
1340 local.get 0
1341 i64.const 42
1342 i64.store32
1343 )
1344 )
1345 "#;
1346
1347 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1348 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1349
1350 assert_eq!(functions.len(), 1);
1351 let ops = &functions[0].ops;
1352
1353 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load8S { .. })));
1355 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load8U { .. })));
1356 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load16S { .. })));
1357 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load16U { .. })));
1358 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Store8 { .. })));
1359 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Store16 { .. })));
1360
1361 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load8S { .. })));
1363 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load8U { .. })));
1364 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load16S { .. })));
1365 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load16U { .. })));
1366 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load32S { .. })));
1367 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load32U { .. })));
1368 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store8 { .. })));
1369 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store16 { .. })));
1370 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store32 { .. })));
1371 }
1372
1373 #[test]
1374 fn test_decode_simd_i32x4_add() {
1375 let wat = r#"
1376 (module
1377 (func (export "add_v128") (param v128 v128) (result v128)
1378 local.get 0
1379 local.get 1
1380 i32x4.add
1381 )
1382 )
1383 "#;
1384
1385 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1386 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1387
1388 assert_eq!(functions.len(), 1);
1389 assert!(
1390 functions[0].ops.contains(&WasmOp::I32x4Add),
1391 "Should decode i32x4.add: {:?}",
1392 functions[0].ops
1393 );
1394 }
1395
1396 #[test]
1397 fn test_decode_simd_v128_const() {
1398 let wat = r#"
1399 (module
1400 (func (export "const_v128") (result v128)
1401 v128.const i32x4 1 2 3 4
1402 )
1403 )
1404 "#;
1405
1406 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1407 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1408
1409 assert_eq!(functions.len(), 1);
1410 assert!(
1411 functions[0]
1412 .ops
1413 .iter()
1414 .any(|o| matches!(o, WasmOp::V128Const(_))),
1415 "Should decode v128.const: {:?}",
1416 functions[0].ops
1417 );
1418 }
1419
1420 #[test]
1421 fn test_decode_simd_v128_load_store() {
1422 let wat = r#"
1423 (module
1424 (memory 1)
1425 (func (export "load_store") (param i32)
1426 local.get 0
1427 v128.load
1428 local.get 0
1429 v128.store
1430 )
1431 )
1432 "#;
1433
1434 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1435 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1436
1437 assert_eq!(functions.len(), 1);
1438 let ops = &functions[0].ops;
1439 assert!(
1440 ops.iter().any(|o| matches!(o, WasmOp::V128Load { .. })),
1441 "Should decode v128.load"
1442 );
1443 assert!(
1444 ops.iter().any(|o| matches!(o, WasmOp::V128Store { .. })),
1445 "Should decode v128.store"
1446 );
1447 }
1448
1449 #[test]
1450 fn test_decode_simd_bitwise_ops() {
1451 let wat = r#"
1452 (module
1453 (func (export "bitwise") (param v128 v128) (result v128)
1454 local.get 0
1455 local.get 1
1456 v128.and
1457 )
1458 )
1459 "#;
1460
1461 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1462 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1463
1464 assert_eq!(functions.len(), 1);
1465 assert!(functions[0].ops.contains(&WasmOp::V128And));
1466 }
1467
1468 #[test]
1469 fn test_decode_simd_splat() {
1470 let wat = r#"
1471 (module
1472 (func (export "splat") (param i32) (result v128)
1473 local.get 0
1474 i32x4.splat
1475 )
1476 )
1477 "#;
1478
1479 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1480 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1481
1482 assert_eq!(functions.len(), 1);
1483 assert!(functions[0].ops.contains(&WasmOp::I32x4Splat));
1484 }
1485
1486 #[test]
1487 fn test_decode_simd_extract_lane() {
1488 let wat = r#"
1489 (module
1490 (func (export "extract") (param v128) (result i32)
1491 local.get 0
1492 i32x4.extract_lane 2
1493 )
1494 )
1495 "#;
1496
1497 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1498 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1499
1500 assert_eq!(functions.len(), 1);
1501 assert!(
1502 functions[0].ops.contains(&WasmOp::I32x4ExtractLane(2)),
1503 "Should decode i32x4.extract_lane 2"
1504 );
1505 }
1506
1507 #[test]
1508 fn test_decode_simd_f32x4_arithmetic() {
1509 let wat = r#"
1510 (module
1511 (func (export "f32x4_add") (param v128 v128) (result v128)
1512 local.get 0
1513 local.get 1
1514 f32x4.add
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 assert!(functions[0].ops.contains(&WasmOp::F32x4Add));
1524 }
1525
1526 #[test]
1527 fn test_369_scalar_float_op_flags_function_unsupported_not_dropped() {
1528 let wat = r#"
1532 (module
1533 (func (export "fadd") (param f32 f32) (result f32)
1534 local.get 0 local.get 1 f32.add)
1535 (func (export "iadd") (param i32 i32) (result i32)
1536 local.get 0 local.get 1 i32.add))
1537 "#;
1538 let wasm = wat::parse_str(wat).expect("parse");
1539 let functions = decode_wasm_functions(&wasm).expect("decode");
1540 let fadd = functions
1541 .iter()
1542 .find(|f| f.export_name.as_deref() == Some("fadd"))
1543 .unwrap();
1544 let iadd = functions
1545 .iter()
1546 .find(|f| f.export_name.as_deref() == Some("iadd"))
1547 .unwrap();
1548 assert!(
1549 fadd.unsupported.is_some(),
1550 "f32.add must flag the function unsupported (loud-skip), got {:?}",
1551 fadd.unsupported
1552 );
1553 assert!(
1554 fadd.unsupported.as_deref().unwrap().contains("F32Add"),
1555 "diagnostic should name the op: {:?}",
1556 fadd.unsupported
1557 );
1558 assert!(
1559 iadd.unsupported.is_none(),
1560 "a pure-integer function must NOT be flagged: {:?}",
1561 iadd.unsupported
1562 );
1563 }
1564
1565 #[test]
1566 fn test_decode_simd_multiple_ops() {
1567 let wat = r#"
1568 (module
1569 (func (export "simd_ops") (param v128 v128 v128) (result v128)
1570 ;; (a + b) * c
1571 local.get 0
1572 local.get 1
1573 i32x4.add
1574 local.get 2
1575 i32x4.mul
1576 )
1577 )
1578 "#;
1579
1580 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1581 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1582
1583 assert_eq!(functions.len(), 1);
1584 let ops = &functions[0].ops;
1585 assert!(ops.contains(&WasmOp::I32x4Add));
1586 assert!(ops.contains(&WasmOp::I32x4Mul));
1587 }
1588
1589 #[test]
1595 fn test_decode_records_aligned_increasing_op_offsets_dbg001() {
1596 let wat = r#"
1597 (module
1598 (func (export "f") (param i32 i32) (result i32)
1599 local.get 0
1600 local.get 1
1601 i32.add
1602 i32.const 7
1603 i32.mul))
1604 "#;
1605 let wasm = wat::parse_str(wat).expect("parse WAT");
1606 let functions = decode_wasm_functions(&wasm).expect("decode");
1607 let f = &functions[0];
1608
1609 assert_eq!(
1611 f.op_offsets.len(),
1612 f.ops.len(),
1613 "op_offsets must be parallel to ops"
1614 );
1615 assert!(!f.op_offsets.is_empty());
1616
1617 assert!(
1620 f.op_offsets.windows(2).all(|w| w[1] > w[0]),
1621 "wasm byte offsets must strictly increase: {:?}",
1622 f.op_offsets
1623 );
1624 assert!(
1625 f.op_offsets[0] >= 8,
1626 "module-relative offset is past the 8-byte wasm header"
1627 );
1628 }
1629
1630 #[test]
1633 fn test_decode_captures_global_initializer() {
1634 let wat = r#"
1635 (module
1636 (memory 2)
1637 (global $__stack_pointer (mut i32) (i32.const 65536))
1638 (global $immutable_const i32 (i32.const 7))
1639 (func (export "f") (result i32) global.get 0)
1640 )
1641 "#;
1642 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1643 let module = decode_wasm_module(&wasm).expect("Failed to decode");
1644
1645 assert_eq!(module.globals.len(), 2, "both globals captured");
1646 let sp = &module.globals[0];
1647 assert_eq!(sp.index, 0);
1648 assert_eq!(sp.init_i32, Some(65536), "stack-pointer init captured");
1649 assert!(sp.mutable, "stack pointer is mutable");
1650 let c = &module.globals[1];
1651 assert_eq!(c.init_i32, Some(7));
1652 assert!(!c.mutable, "second global is immutable");
1653 }
1654}