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, op_offsets, 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 op_offsets,
347 unsupported,
348 });
349 func_index += 1;
350 }
351 _ => {}
352 }
353 }
354
355 Ok(DecodedModule {
356 functions,
357 memories,
358 data_segments,
359 imports,
360 num_imported_funcs,
361 func_arg_counts,
362 type_arg_counts,
363 func_ret_i64,
364 type_ret_i64,
365 func_params_i64,
366 globals,
367 elem_func_indices,
368 })
369}
370
371pub fn decode_wasm_functions(wasm_bytes: &[u8]) -> Result<Vec<FunctionOps>> {
373 let mut functions = Vec::new();
374 let mut func_index = 0u32;
375 let mut num_imported_funcs = 0u32;
376 let mut export_names: HashMap<u32, String> = HashMap::new();
377
378 for payload in Parser::new(0).parse_all(wasm_bytes) {
379 let payload = payload.context("Failed to parse WASM payload")?;
380
381 match payload {
382 Payload::ImportSection(imports) => {
383 for import in imports.into_imports() {
386 let import = import.context("Failed to parse import")?;
387 if matches!(import.ty, wasmparser::TypeRef::Func(_)) {
388 num_imported_funcs += 1;
389 }
390 }
391 }
392 Payload::ExportSection(exports) => {
393 for export in exports {
394 let export = export.context("Failed to parse export")?;
395 if export.kind == ExternalKind::Func {
396 export_names.insert(export.index, export.name.to_string());
397 }
398 }
399 }
400 Payload::CodeSectionEntry(body) => {
401 let (ops, op_offsets, unsupported) = decode_function_body(&body)?;
402 let actual_index = num_imported_funcs + func_index;
403 let export_name = export_names.get(&actual_index).cloned();
404
405 functions.push(FunctionOps {
406 index: actual_index,
407 export_name,
408 ops,
409 op_offsets,
410 unsupported,
411 });
412 func_index += 1;
413 }
414 _ => {}
415 }
416 }
417
418 Ok(functions)
419}
420
421#[derive(Debug, Clone)]
423pub struct FunctionOps {
424 pub index: u32,
426 pub export_name: Option<String>,
428 pub ops: Vec<WasmOp>,
430 pub op_offsets: Vec<u32>,
438 pub unsupported: Option<String>,
448}
449
450fn decode_function_body(
456 body: &wasmparser::FunctionBody,
457) -> Result<(Vec<WasmOp>, Vec<u32>, Option<String>)> {
458 let mut ops = Vec::new();
459 let mut op_offsets = Vec::new();
464 let mut unsupported: Option<String> = None;
465
466 let ops_reader = body.get_operators_reader()?;
467 for item in ops_reader.into_iter_with_offsets() {
468 let (op, offset) = item.context("Failed to read operator")?;
469
470 if let Some(wasm_op) = convert_operator(&op) {
471 ops.push(wasm_op);
472 op_offsets.push(offset as u32);
473 } else if unsupported.is_none() && !is_intentionally_ignored(&op) {
474 unsupported = Some(format!("{op:?}"));
478 }
479 }
480
481 Ok((ops, op_offsets, unsupported))
482}
483
484fn is_intentionally_ignored(op: &wasmparser::Operator) -> bool {
489 use wasmparser::Operator::*;
490 matches!(op, Nop | Unreachable)
491}
492
493fn convert_operator(op: &wasmparser::Operator) -> Option<WasmOp> {
495 use wasmparser::Operator::*;
496
497 match op {
498 I32Const { value } => Some(WasmOp::I32Const(*value)),
500
501 I32Add => Some(WasmOp::I32Add),
503 I32Sub => Some(WasmOp::I32Sub),
504 I32Mul => Some(WasmOp::I32Mul),
505 I32DivS => Some(WasmOp::I32DivS),
506 I32DivU => Some(WasmOp::I32DivU),
507 I32RemS => Some(WasmOp::I32RemS),
508 I32RemU => Some(WasmOp::I32RemU),
509
510 I64Const { value } => Some(WasmOp::I64Const(*value)),
512
513 I64Add => Some(WasmOp::I64Add),
515 I64Sub => Some(WasmOp::I64Sub),
516 I64Mul => Some(WasmOp::I64Mul),
517 I64DivS => Some(WasmOp::I64DivS),
518 I64DivU => Some(WasmOp::I64DivU),
519 I64RemS => Some(WasmOp::I64RemS),
520 I64RemU => Some(WasmOp::I64RemU),
521
522 I64And => Some(WasmOp::I64And),
524 I64Or => Some(WasmOp::I64Or),
525 I64Xor => Some(WasmOp::I64Xor),
526 I64Shl => Some(WasmOp::I64Shl),
527 I64ShrS => Some(WasmOp::I64ShrS),
528 I64ShrU => Some(WasmOp::I64ShrU),
529 I64Rotl => Some(WasmOp::I64Rotl),
530 I64Rotr => Some(WasmOp::I64Rotr),
531 I64Clz => Some(WasmOp::I64Clz),
532 I64Ctz => Some(WasmOp::I64Ctz),
533 I64Popcnt => Some(WasmOp::I64Popcnt),
534 I64Extend8S => Some(WasmOp::I64Extend8S),
535 I64Extend16S => Some(WasmOp::I64Extend16S),
536 I64Extend32S => Some(WasmOp::I64Extend32S),
537 I64ExtendI32U => Some(WasmOp::I64ExtendI32U),
543 I64ExtendI32S => Some(WasmOp::I64ExtendI32S),
544 I32WrapI64 => Some(WasmOp::I32WrapI64),
545
546 I64Eqz => Some(WasmOp::I64Eqz),
548 I64Eq => Some(WasmOp::I64Eq),
549 I64Ne => Some(WasmOp::I64Ne),
550 I64LtS => Some(WasmOp::I64LtS),
551 I64LtU => Some(WasmOp::I64LtU),
552 I64LeS => Some(WasmOp::I64LeS),
553 I64LeU => Some(WasmOp::I64LeU),
554 I64GtS => Some(WasmOp::I64GtS),
555 I64GtU => Some(WasmOp::I64GtU),
556 I64GeS => Some(WasmOp::I64GeS),
557 I64GeU => Some(WasmOp::I64GeU),
558
559 I32And => Some(WasmOp::I32And),
561 I32Or => Some(WasmOp::I32Or),
562 I32Xor => Some(WasmOp::I32Xor),
563 I32Shl => Some(WasmOp::I32Shl),
564 I32ShrS => Some(WasmOp::I32ShrS),
565 I32ShrU => Some(WasmOp::I32ShrU),
566 I32Rotl => Some(WasmOp::I32Rotl),
567 I32Rotr => Some(WasmOp::I32Rotr),
568 I32Clz => Some(WasmOp::I32Clz),
569 I32Ctz => Some(WasmOp::I32Ctz),
570 I32Popcnt => Some(WasmOp::I32Popcnt),
571 I32Extend8S => Some(WasmOp::I32Extend8S),
572 I32Extend16S => Some(WasmOp::I32Extend16S),
573
574 I32Eqz => Some(WasmOp::I32Eqz),
576 I32Eq => Some(WasmOp::I32Eq),
577 I32Ne => Some(WasmOp::I32Ne),
578 I32LtS => Some(WasmOp::I32LtS),
579 I32LtU => Some(WasmOp::I32LtU),
580 I32LeS => Some(WasmOp::I32LeS),
581 I32LeU => Some(WasmOp::I32LeU),
582 I32GtS => Some(WasmOp::I32GtS),
583 I32GtU => Some(WasmOp::I32GtU),
584 I32GeS => Some(WasmOp::I32GeS),
585 I32GeU => Some(WasmOp::I32GeU),
586
587 I32Load { memarg } => Some(WasmOp::I32Load {
589 offset: memarg.offset as u32,
590 align: memarg.align as u32,
591 }),
592 I32Store { memarg } => Some(WasmOp::I32Store {
593 offset: memarg.offset as u32,
594 align: memarg.align as u32,
595 }),
596 I64Load { memarg } => Some(WasmOp::I64Load {
603 offset: memarg.offset as u32,
604 align: memarg.align as u32,
605 }),
606 I64Store { memarg } => Some(WasmOp::I64Store {
607 offset: memarg.offset as u32,
608 align: memarg.align as u32,
609 }),
610
611 I32Load8S { memarg } => Some(WasmOp::I32Load8S {
613 offset: memarg.offset as u32,
614 align: memarg.align as u32,
615 }),
616 I32Load8U { memarg } => Some(WasmOp::I32Load8U {
617 offset: memarg.offset as u32,
618 align: memarg.align as u32,
619 }),
620 I32Load16S { memarg } => Some(WasmOp::I32Load16S {
621 offset: memarg.offset as u32,
622 align: memarg.align as u32,
623 }),
624 I32Load16U { memarg } => Some(WasmOp::I32Load16U {
625 offset: memarg.offset as u32,
626 align: memarg.align as u32,
627 }),
628
629 I32Store8 { memarg } => Some(WasmOp::I32Store8 {
631 offset: memarg.offset as u32,
632 align: memarg.align as u32,
633 }),
634 I32Store16 { memarg } => Some(WasmOp::I32Store16 {
635 offset: memarg.offset as u32,
636 align: memarg.align as u32,
637 }),
638
639 LocalGet { local_index } => Some(WasmOp::LocalGet(*local_index)),
641 LocalSet { local_index } => Some(WasmOp::LocalSet(*local_index)),
642 LocalTee { local_index } => Some(WasmOp::LocalTee(*local_index)),
643 GlobalGet { global_index } => Some(WasmOp::GlobalGet(*global_index)),
644 GlobalSet { global_index } => Some(WasmOp::GlobalSet(*global_index)),
645
646 Block { .. } => Some(WasmOp::Block),
648 Loop { .. } => Some(WasmOp::Loop),
649 Br { relative_depth } => Some(WasmOp::Br(*relative_depth)),
650 BrIf { relative_depth } => Some(WasmOp::BrIf(*relative_depth)),
651 BrTable { targets } => {
657 let default = targets.default();
658 let tgts: Vec<u32> = targets.targets().filter_map(Result::ok).collect();
659 Some(WasmOp::BrTable {
660 targets: tgts,
661 default,
662 })
663 }
664 Return => Some(WasmOp::Return),
665 Call { function_index } => Some(WasmOp::Call(*function_index)),
666 CallIndirect {
667 type_index,
668 table_index,
669 ..
670 } => Some(WasmOp::CallIndirect {
671 type_index: *type_index,
672 table_index: *table_index,
673 }),
674
675 End => Some(WasmOp::End),
677
678 Nop | Unreachable => None,
680
681 Drop => Some(WasmOp::Drop),
683
684 Select => Some(WasmOp::Select),
686
687 If { .. } => Some(WasmOp::If),
689 Else => Some(WasmOp::Else),
690
691 I64Load8S { memarg } => Some(WasmOp::I64Load8S {
693 offset: memarg.offset as u32,
694 align: memarg.align as u32,
695 }),
696 I64Load8U { memarg } => Some(WasmOp::I64Load8U {
697 offset: memarg.offset as u32,
698 align: memarg.align as u32,
699 }),
700 I64Load16S { memarg } => Some(WasmOp::I64Load16S {
701 offset: memarg.offset as u32,
702 align: memarg.align as u32,
703 }),
704 I64Load16U { memarg } => Some(WasmOp::I64Load16U {
705 offset: memarg.offset as u32,
706 align: memarg.align as u32,
707 }),
708 I64Load32S { memarg } => Some(WasmOp::I64Load32S {
709 offset: memarg.offset as u32,
710 align: memarg.align as u32,
711 }),
712 I64Load32U { memarg } => Some(WasmOp::I64Load32U {
713 offset: memarg.offset as u32,
714 align: memarg.align as u32,
715 }),
716
717 I64Store8 { memarg } => Some(WasmOp::I64Store8 {
719 offset: memarg.offset as u32,
720 align: memarg.align as u32,
721 }),
722 I64Store16 { memarg } => Some(WasmOp::I64Store16 {
723 offset: memarg.offset as u32,
724 align: memarg.align as u32,
725 }),
726 I64Store32 { memarg } => Some(WasmOp::I64Store32 {
727 offset: memarg.offset as u32,
728 align: memarg.align as u32,
729 }),
730
731 MemorySize { mem, .. } => Some(WasmOp::MemorySize(*mem)),
733 MemoryGrow { mem, .. } => Some(WasmOp::MemoryGrow(*mem)),
734
735 MemoryCopy {
742 dst_mem: 0,
743 src_mem: 0,
744 } => Some(WasmOp::MemoryCopy),
745 MemoryFill { mem: 0 } => Some(WasmOp::MemoryFill),
746
747 V128Const { value } => {
751 let mut bytes = [0u8; 16];
752 bytes.copy_from_slice(value.bytes());
753 Some(WasmOp::V128Const(bytes))
754 }
755 V128Load { memarg } => Some(WasmOp::V128Load {
756 offset: memarg.offset as u32,
757 align: memarg.align as u32,
758 }),
759 V128Store { memarg } => Some(WasmOp::V128Store {
760 offset: memarg.offset as u32,
761 align: memarg.align as u32,
762 }),
763
764 V128And => Some(WasmOp::V128And),
766 V128Or => Some(WasmOp::V128Or),
767 V128Xor => Some(WasmOp::V128Xor),
768 V128Not => Some(WasmOp::V128Not),
769 V128AndNot => Some(WasmOp::V128AndNot),
770
771 I8x16Add => Some(WasmOp::I8x16Add),
773 I8x16Sub => Some(WasmOp::I8x16Sub),
774 I8x16Neg => Some(WasmOp::I8x16Neg),
775 I8x16Eq => Some(WasmOp::I8x16Eq),
776 I8x16Ne => Some(WasmOp::I8x16Ne),
777 I8x16LtS => Some(WasmOp::I8x16LtS),
778 I8x16LtU => Some(WasmOp::I8x16LtU),
779 I8x16GtS => Some(WasmOp::I8x16GtS),
780 I8x16GtU => Some(WasmOp::I8x16GtU),
781 I8x16LeS => Some(WasmOp::I8x16LeS),
782 I8x16LeU => Some(WasmOp::I8x16LeU),
783 I8x16GeS => Some(WasmOp::I8x16GeS),
784 I8x16GeU => Some(WasmOp::I8x16GeU),
785 I8x16Splat => Some(WasmOp::I8x16Splat),
786 I8x16ExtractLaneS { lane } => Some(WasmOp::I8x16ExtractLaneS(*lane)),
787 I8x16ExtractLaneU { lane } => Some(WasmOp::I8x16ExtractLaneU(*lane)),
788 I8x16ReplaceLane { lane } => Some(WasmOp::I8x16ReplaceLane(*lane)),
789 I8x16Shuffle { lanes } => Some(WasmOp::I8x16Shuffle(*lanes)),
790 I8x16Swizzle => Some(WasmOp::I8x16Swizzle),
791
792 I16x8Add => Some(WasmOp::I16x8Add),
794 I16x8Sub => Some(WasmOp::I16x8Sub),
795 I16x8Mul => Some(WasmOp::I16x8Mul),
796 I16x8Neg => Some(WasmOp::I16x8Neg),
797 I16x8Eq => Some(WasmOp::I16x8Eq),
798 I16x8Ne => Some(WasmOp::I16x8Ne),
799 I16x8LtS => Some(WasmOp::I16x8LtS),
800 I16x8LtU => Some(WasmOp::I16x8LtU),
801 I16x8GtS => Some(WasmOp::I16x8GtS),
802 I16x8GtU => Some(WasmOp::I16x8GtU),
803 I16x8LeS => Some(WasmOp::I16x8LeS),
804 I16x8LeU => Some(WasmOp::I16x8LeU),
805 I16x8GeS => Some(WasmOp::I16x8GeS),
806 I16x8GeU => Some(WasmOp::I16x8GeU),
807 I16x8Splat => Some(WasmOp::I16x8Splat),
808 I16x8ExtractLaneS { lane } => Some(WasmOp::I16x8ExtractLaneS(*lane)),
809 I16x8ExtractLaneU { lane } => Some(WasmOp::I16x8ExtractLaneU(*lane)),
810 I16x8ReplaceLane { lane } => Some(WasmOp::I16x8ReplaceLane(*lane)),
811
812 I32x4Add => Some(WasmOp::I32x4Add),
814 I32x4Sub => Some(WasmOp::I32x4Sub),
815 I32x4Mul => Some(WasmOp::I32x4Mul),
816 I32x4Neg => Some(WasmOp::I32x4Neg),
817 I32x4Eq => Some(WasmOp::I32x4Eq),
818 I32x4Ne => Some(WasmOp::I32x4Ne),
819 I32x4LtS => Some(WasmOp::I32x4LtS),
820 I32x4LtU => Some(WasmOp::I32x4LtU),
821 I32x4GtS => Some(WasmOp::I32x4GtS),
822 I32x4GtU => Some(WasmOp::I32x4GtU),
823 I32x4LeS => Some(WasmOp::I32x4LeS),
824 I32x4LeU => Some(WasmOp::I32x4LeU),
825 I32x4GeS => Some(WasmOp::I32x4GeS),
826 I32x4GeU => Some(WasmOp::I32x4GeU),
827 I32x4Splat => Some(WasmOp::I32x4Splat),
828 I32x4ExtractLane { lane } => Some(WasmOp::I32x4ExtractLane(*lane)),
829 I32x4ReplaceLane { lane } => Some(WasmOp::I32x4ReplaceLane(*lane)),
830
831 I64x2Add => Some(WasmOp::I64x2Add),
833 I64x2Sub => Some(WasmOp::I64x2Sub),
834 I64x2Mul => Some(WasmOp::I64x2Mul),
835 I64x2Neg => Some(WasmOp::I64x2Neg),
836 I64x2Eq => Some(WasmOp::I64x2Eq),
837 I64x2Ne => Some(WasmOp::I64x2Ne),
838 I64x2LtS => Some(WasmOp::I64x2LtS),
839 I64x2GtS => Some(WasmOp::I64x2GtS),
840 I64x2LeS => Some(WasmOp::I64x2LeS),
841 I64x2GeS => Some(WasmOp::I64x2GeS),
842 I64x2Splat => Some(WasmOp::I64x2Splat),
843 I64x2ExtractLane { lane } => Some(WasmOp::I64x2ExtractLane(*lane)),
844 I64x2ReplaceLane { lane } => Some(WasmOp::I64x2ReplaceLane(*lane)),
845
846 F32x4Add => Some(WasmOp::F32x4Add),
848 F32x4Sub => Some(WasmOp::F32x4Sub),
849 F32x4Mul => Some(WasmOp::F32x4Mul),
850 F32x4Div => Some(WasmOp::F32x4Div),
851 F32x4Abs => Some(WasmOp::F32x4Abs),
852 F32x4Neg => Some(WasmOp::F32x4Neg),
853 F32x4Sqrt => Some(WasmOp::F32x4Sqrt),
854 F32x4Eq => Some(WasmOp::F32x4Eq),
855 F32x4Ne => Some(WasmOp::F32x4Ne),
856 F32x4Lt => Some(WasmOp::F32x4Lt),
857 F32x4Le => Some(WasmOp::F32x4Le),
858 F32x4Gt => Some(WasmOp::F32x4Gt),
859 F32x4Ge => Some(WasmOp::F32x4Ge),
860 F32x4Splat => Some(WasmOp::F32x4Splat),
861 F32x4ExtractLane { lane } => Some(WasmOp::F32x4ExtractLane(*lane)),
862 F32x4ReplaceLane { lane } => Some(WasmOp::F32x4ReplaceLane(*lane)),
863
864 _ => None,
866 }
867}
868
869#[cfg(test)]
870mod tests {
871 use super::*;
872
873 #[test]
874 fn test_decode_simple_add() {
875 let wat = r#"
876 (module
877 (func (export "add") (param i32 i32) (result i32)
878 local.get 0
879 local.get 1
880 i32.add
881 )
882 )
883 "#;
884
885 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
886 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
887
888 assert_eq!(functions.len(), 1);
889 assert_eq!(functions[0].index, 0);
890 assert_eq!(functions[0].export_name, Some("add".to_string()));
891 assert_eq!(
892 functions[0].ops,
893 vec![
894 WasmOp::LocalGet(0),
895 WasmOp::LocalGet(1),
896 WasmOp::I32Add,
897 WasmOp::End
898 ]
899 );
900 }
901
902 #[test]
908 fn test_decode_i64_i32_width_conversions() {
909 let wat = r#"
910 (module
911 (func (export "conv") (param i32 i64) (result i32)
912 local.get 0
913 i64.extend_i32_u
914 local.get 0
915 i64.extend_i32_s
916 i64.add
917 local.get 1
918 i64.add
919 i32.wrap_i64
920 )
921 )
922 "#;
923 let wasm = wat::parse_str(wat).expect("parse");
924 let functions = decode_wasm_functions(&wasm).expect("decode");
925 let ops = &functions[0].ops;
926 assert!(
927 ops.contains(&WasmOp::I64ExtendI32U),
928 "i64.extend_i32_u must decode (not be dropped): {ops:?}"
929 );
930 assert!(
931 ops.contains(&WasmOp::I64ExtendI32S),
932 "i64.extend_i32_s must decode (not be dropped): {ops:?}"
933 );
934 assert!(
935 ops.contains(&WasmOp::I32WrapI64),
936 "i32.wrap_i64 must decode (not be dropped): {ops:?}"
937 );
938 }
939
940 #[test]
945 fn test_decode_br_table() {
946 let wat = r#"
947 (module
948 (func (export "bt") (param i32) (result i32)
949 (block (block (block
950 local.get 0
951 br_table 2 0 1 2)
952 i32.const 30 return)
953 i32.const 20 return)
954 i32.const 10))
955 "#;
956 let wasm = wat::parse_str(wat).expect("parse");
957 let functions = decode_wasm_functions(&wasm).expect("decode");
958 let bt = functions[0]
959 .ops
960 .iter()
961 .find_map(|o| match o {
962 WasmOp::BrTable { targets, default } => Some((targets.clone(), *default)),
963 _ => None,
964 })
965 .expect("br_table must decode (not be dropped)");
966 assert_eq!(bt.0, vec![2, 0, 1], "br_table targets preserved in order");
967 assert_eq!(bt.1, 2, "br_table default preserved");
968 }
969
970 #[test]
971 fn test_decode_arithmetic() {
972 let wat = r#"
973 (module
974 (func (export "calc") (result i32)
975 i32.const 5
976 i32.const 3
977 i32.mul
978 i32.const 2
979 i32.add
980 )
981 )
982 "#;
983
984 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
985 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
986
987 assert_eq!(functions.len(), 1);
988 assert_eq!(functions[0].export_name, Some("calc".to_string()));
989 assert_eq!(
990 functions[0].ops,
991 vec![
992 WasmOp::I32Const(5),
993 WasmOp::I32Const(3),
994 WasmOp::I32Mul,
995 WasmOp::I32Const(2),
996 WasmOp::I32Add,
997 WasmOp::End,
998 ]
999 );
1000 }
1001
1002 #[test]
1003 fn test_decode_multi_function_module() {
1004 let wat = r#"
1005 (module
1006 (func $helper)
1007 (func (export "add") (param i32 i32) (result i32)
1008 local.get 0
1009 local.get 1
1010 i32.add
1011 )
1012 (func (export "sub") (param i32 i32) (result i32)
1013 local.get 0
1014 local.get 1
1015 i32.sub
1016 )
1017 )
1018 "#;
1019
1020 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1021 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1022
1023 assert_eq!(functions.len(), 3);
1024 assert_eq!(functions[0].index, 0);
1025 assert_eq!(functions[0].export_name, None);
1026 assert_eq!(functions[1].index, 1);
1027 assert_eq!(functions[1].export_name, Some("add".to_string()));
1028 assert_eq!(functions[2].index, 2);
1029 assert_eq!(functions[2].export_name, Some("sub".to_string()));
1030 }
1031
1032 #[test]
1033 fn test_decode_module_with_imports() {
1034 let wat = r#"
1035 (module
1036 (import "env" "log" (func $log (param i32)))
1037 (import "env" "memory" (memory 1))
1038 (func (export "run") (param i32)
1039 local.get 0
1040 call 0
1041 )
1042 )
1043 "#;
1044
1045 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1046 let module = decode_wasm_module(&wasm).expect("Failed to decode");
1047
1048 assert_eq!(module.imports.len(), 2);
1050 assert_eq!(module.num_imported_funcs, 1);
1051
1052 assert_eq!(module.imports[0].module, "env");
1054 assert_eq!(module.imports[0].name, "log");
1055 assert!(matches!(module.imports[0].kind, ImportKind::Function(_)));
1056
1057 assert_eq!(module.imports[1].module, "env");
1059 assert_eq!(module.imports[1].name, "memory");
1060 assert_eq!(module.imports[1].kind, ImportKind::Memory);
1061
1062 assert_eq!(module.functions.len(), 1);
1064 assert_eq!(module.functions[0].index, 1);
1065 assert_eq!(module.functions[0].export_name, Some("run".to_string()));
1066 }
1067
1068 #[test]
1069 fn test_find_function_by_export_name() {
1070 let wat = r#"
1071 (module
1072 (func $helper)
1073 (func (export "add") (param i32 i32) (result i32)
1074 local.get 0
1075 local.get 1
1076 i32.add
1077 )
1078 )
1079 "#;
1080
1081 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1082 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1083
1084 let add_func = functions
1085 .iter()
1086 .find(|f| f.export_name.as_deref() == Some("add"))
1087 .expect("Should find 'add' function");
1088
1089 assert_eq!(add_func.index, 1);
1090 assert!(add_func.ops.contains(&WasmOp::I32Add));
1091 }
1092
1093 #[test]
1094 fn test_decode_subword_loads() {
1095 let wat = r#"
1096 (module
1097 (memory 1)
1098 (func (export "test") (param i32) (result i32)
1099 local.get 0
1100 i32.load8_u
1101 )
1102 )
1103 "#;
1104
1105 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1106 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1107
1108 assert_eq!(functions.len(), 1);
1109 assert!(functions[0].ops.contains(&WasmOp::I32Load8U {
1110 offset: 0,
1111 align: 0,
1112 }));
1113 }
1114
1115 #[test]
1116 fn test_decode_subword_stores() {
1117 let wat = r#"
1118 (module
1119 (memory 1)
1120 (func (export "test") (param i32 i32)
1121 local.get 0
1122 local.get 1
1123 i32.store8
1124 )
1125 )
1126 "#;
1127
1128 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1129 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1130
1131 assert_eq!(functions.len(), 1);
1132 assert!(functions[0].ops.contains(&WasmOp::I32Store8 {
1133 offset: 0,
1134 align: 0,
1135 }));
1136 }
1137
1138 #[test]
1139 fn test_decode_memory_size_grow() {
1140 let wat = r#"
1141 (module
1142 (memory 1)
1143 (func (export "test") (result i32)
1144 memory.size
1145 )
1146 )
1147 "#;
1148
1149 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1150 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1151
1152 assert_eq!(functions.len(), 1);
1153 assert!(functions[0].ops.contains(&WasmOp::MemorySize(0)));
1154 }
1155
1156 #[test]
1157 fn test_decode_memory_grow() {
1158 let wat = r#"
1159 (module
1160 (memory 1)
1161 (func (export "test") (param i32) (result i32)
1162 local.get 0
1163 memory.grow
1164 )
1165 )
1166 "#;
1167
1168 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1169 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1170
1171 assert_eq!(functions.len(), 1);
1172 assert!(functions[0].ops.contains(&WasmOp::MemoryGrow(0)));
1173 }
1174
1175 #[test]
1176 fn test_decode_bulk_memory_374() {
1177 let wat = r#"
1180 (module
1181 (memory 1)
1182 (func (export "cpy") (param i32 i32 i32)
1183 local.get 0 local.get 1 local.get 2 memory.copy)
1184 (func (export "fil") (param i32 i32 i32)
1185 local.get 0 local.get 1 local.get 2 memory.fill)
1186 )
1187 "#;
1188 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1189 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1190 assert_eq!(functions.len(), 2);
1191 assert!(functions[0].ops.contains(&WasmOp::MemoryCopy));
1192 assert!(functions[1].ops.contains(&WasmOp::MemoryFill));
1193 assert!(functions[0].unsupported.is_none());
1195 assert!(functions[1].unsupported.is_none());
1196 }
1197
1198 #[test]
1199 fn test_decode_i64_subword_loads() {
1200 let wat = r#"
1201 (module
1202 (memory 1)
1203 (func (export "test") (param i32) (result i64)
1204 local.get 0
1205 i64.load8_s
1206 )
1207 )
1208 "#;
1209
1210 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1211 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1212
1213 assert_eq!(functions.len(), 1);
1214 assert!(functions[0].ops.contains(&WasmOp::I64Load8S {
1215 offset: 0,
1216 align: 0,
1217 }));
1218 }
1219
1220 #[test]
1221 fn test_decode_all_subword_memory_ops() {
1222 let wat = r#"
1224 (module
1225 (memory 1)
1226 (func (export "test") (param i32)
1227 ;; i32 sub-word loads
1228 local.get 0
1229 i32.load8_s
1230 drop
1231 local.get 0
1232 i32.load8_u
1233 drop
1234 local.get 0
1235 i32.load16_s
1236 drop
1237 local.get 0
1238 i32.load16_u
1239 drop
1240
1241 ;; i32 sub-word stores
1242 local.get 0
1243 i32.const 42
1244 i32.store8
1245 local.get 0
1246 i32.const 42
1247 i32.store16
1248
1249 ;; i64 sub-word loads
1250 local.get 0
1251 i64.load8_s
1252 drop
1253 local.get 0
1254 i64.load8_u
1255 drop
1256 local.get 0
1257 i64.load16_s
1258 drop
1259 local.get 0
1260 i64.load16_u
1261 drop
1262 local.get 0
1263 i64.load32_s
1264 drop
1265 local.get 0
1266 i64.load32_u
1267 drop
1268
1269 ;; i64 sub-word stores
1270 local.get 0
1271 i64.const 42
1272 i64.store8
1273 local.get 0
1274 i64.const 42
1275 i64.store16
1276 local.get 0
1277 i64.const 42
1278 i64.store32
1279 )
1280 )
1281 "#;
1282
1283 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1284 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1285
1286 assert_eq!(functions.len(), 1);
1287 let ops = &functions[0].ops;
1288
1289 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load8S { .. })));
1291 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load8U { .. })));
1292 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load16S { .. })));
1293 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load16U { .. })));
1294 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Store8 { .. })));
1295 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Store16 { .. })));
1296
1297 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load8S { .. })));
1299 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load8U { .. })));
1300 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load16S { .. })));
1301 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load16U { .. })));
1302 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load32S { .. })));
1303 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load32U { .. })));
1304 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store8 { .. })));
1305 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store16 { .. })));
1306 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store32 { .. })));
1307 }
1308
1309 #[test]
1310 fn test_decode_simd_i32x4_add() {
1311 let wat = r#"
1312 (module
1313 (func (export "add_v128") (param v128 v128) (result v128)
1314 local.get 0
1315 local.get 1
1316 i32x4.add
1317 )
1318 )
1319 "#;
1320
1321 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1322 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1323
1324 assert_eq!(functions.len(), 1);
1325 assert!(
1326 functions[0].ops.contains(&WasmOp::I32x4Add),
1327 "Should decode i32x4.add: {:?}",
1328 functions[0].ops
1329 );
1330 }
1331
1332 #[test]
1333 fn test_decode_simd_v128_const() {
1334 let wat = r#"
1335 (module
1336 (func (export "const_v128") (result v128)
1337 v128.const i32x4 1 2 3 4
1338 )
1339 )
1340 "#;
1341
1342 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1343 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1344
1345 assert_eq!(functions.len(), 1);
1346 assert!(
1347 functions[0]
1348 .ops
1349 .iter()
1350 .any(|o| matches!(o, WasmOp::V128Const(_))),
1351 "Should decode v128.const: {:?}",
1352 functions[0].ops
1353 );
1354 }
1355
1356 #[test]
1357 fn test_decode_simd_v128_load_store() {
1358 let wat = r#"
1359 (module
1360 (memory 1)
1361 (func (export "load_store") (param i32)
1362 local.get 0
1363 v128.load
1364 local.get 0
1365 v128.store
1366 )
1367 )
1368 "#;
1369
1370 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1371 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1372
1373 assert_eq!(functions.len(), 1);
1374 let ops = &functions[0].ops;
1375 assert!(
1376 ops.iter().any(|o| matches!(o, WasmOp::V128Load { .. })),
1377 "Should decode v128.load"
1378 );
1379 assert!(
1380 ops.iter().any(|o| matches!(o, WasmOp::V128Store { .. })),
1381 "Should decode v128.store"
1382 );
1383 }
1384
1385 #[test]
1386 fn test_decode_simd_bitwise_ops() {
1387 let wat = r#"
1388 (module
1389 (func (export "bitwise") (param v128 v128) (result v128)
1390 local.get 0
1391 local.get 1
1392 v128.and
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::V128And));
1402 }
1403
1404 #[test]
1405 fn test_decode_simd_splat() {
1406 let wat = r#"
1407 (module
1408 (func (export "splat") (param i32) (result v128)
1409 local.get 0
1410 i32x4.splat
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!(functions[0].ops.contains(&WasmOp::I32x4Splat));
1420 }
1421
1422 #[test]
1423 fn test_decode_simd_extract_lane() {
1424 let wat = r#"
1425 (module
1426 (func (export "extract") (param v128) (result i32)
1427 local.get 0
1428 i32x4.extract_lane 2
1429 )
1430 )
1431 "#;
1432
1433 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1434 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1435
1436 assert_eq!(functions.len(), 1);
1437 assert!(
1438 functions[0].ops.contains(&WasmOp::I32x4ExtractLane(2)),
1439 "Should decode i32x4.extract_lane 2"
1440 );
1441 }
1442
1443 #[test]
1444 fn test_decode_simd_f32x4_arithmetic() {
1445 let wat = r#"
1446 (module
1447 (func (export "f32x4_add") (param v128 v128) (result v128)
1448 local.get 0
1449 local.get 1
1450 f32x4.add
1451 )
1452 )
1453 "#;
1454
1455 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1456 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1457
1458 assert_eq!(functions.len(), 1);
1459 assert!(functions[0].ops.contains(&WasmOp::F32x4Add));
1460 }
1461
1462 #[test]
1463 fn test_369_scalar_float_op_flags_function_unsupported_not_dropped() {
1464 let wat = r#"
1468 (module
1469 (func (export "fadd") (param f32 f32) (result f32)
1470 local.get 0 local.get 1 f32.add)
1471 (func (export "iadd") (param i32 i32) (result i32)
1472 local.get 0 local.get 1 i32.add))
1473 "#;
1474 let wasm = wat::parse_str(wat).expect("parse");
1475 let functions = decode_wasm_functions(&wasm).expect("decode");
1476 let fadd = functions
1477 .iter()
1478 .find(|f| f.export_name.as_deref() == Some("fadd"))
1479 .unwrap();
1480 let iadd = functions
1481 .iter()
1482 .find(|f| f.export_name.as_deref() == Some("iadd"))
1483 .unwrap();
1484 assert!(
1485 fadd.unsupported.is_some(),
1486 "f32.add must flag the function unsupported (loud-skip), got {:?}",
1487 fadd.unsupported
1488 );
1489 assert!(
1490 fadd.unsupported.as_deref().unwrap().contains("F32Add"),
1491 "diagnostic should name the op: {:?}",
1492 fadd.unsupported
1493 );
1494 assert!(
1495 iadd.unsupported.is_none(),
1496 "a pure-integer function must NOT be flagged: {:?}",
1497 iadd.unsupported
1498 );
1499 }
1500
1501 #[test]
1502 fn test_decode_simd_multiple_ops() {
1503 let wat = r#"
1504 (module
1505 (func (export "simd_ops") (param v128 v128 v128) (result v128)
1506 ;; (a + b) * c
1507 local.get 0
1508 local.get 1
1509 i32x4.add
1510 local.get 2
1511 i32x4.mul
1512 )
1513 )
1514 "#;
1515
1516 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1517 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1518
1519 assert_eq!(functions.len(), 1);
1520 let ops = &functions[0].ops;
1521 assert!(ops.contains(&WasmOp::I32x4Add));
1522 assert!(ops.contains(&WasmOp::I32x4Mul));
1523 }
1524
1525 #[test]
1531 fn test_decode_records_aligned_increasing_op_offsets_dbg001() {
1532 let wat = r#"
1533 (module
1534 (func (export "f") (param i32 i32) (result i32)
1535 local.get 0
1536 local.get 1
1537 i32.add
1538 i32.const 7
1539 i32.mul))
1540 "#;
1541 let wasm = wat::parse_str(wat).expect("parse WAT");
1542 let functions = decode_wasm_functions(&wasm).expect("decode");
1543 let f = &functions[0];
1544
1545 assert_eq!(
1547 f.op_offsets.len(),
1548 f.ops.len(),
1549 "op_offsets must be parallel to ops"
1550 );
1551 assert!(!f.op_offsets.is_empty());
1552
1553 assert!(
1556 f.op_offsets.windows(2).all(|w| w[1] > w[0]),
1557 "wasm byte offsets must strictly increase: {:?}",
1558 f.op_offsets
1559 );
1560 assert!(
1561 f.op_offsets[0] >= 8,
1562 "module-relative offset is past the 8-byte wasm header"
1563 );
1564 }
1565
1566 #[test]
1569 fn test_decode_captures_global_initializer() {
1570 let wat = r#"
1571 (module
1572 (memory 2)
1573 (global $__stack_pointer (mut i32) (i32.const 65536))
1574 (global $immutable_const i32 (i32.const 7))
1575 (func (export "f") (result i32) global.get 0)
1576 )
1577 "#;
1578 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1579 let module = decode_wasm_module(&wasm).expect("Failed to decode");
1580
1581 assert_eq!(module.globals.len(), 2, "both globals captured");
1582 let sp = &module.globals[0];
1583 assert_eq!(sp.index, 0);
1584 assert_eq!(sp.init_i32, Some(65536), "stack-pointer init captured");
1585 assert!(sp.mutable, "stack pointer is mutable");
1586 let c = &module.globals[1];
1587 assert_eq!(c.init_i32, Some(7));
1588 assert!(!c.mutable, "second global is immutable");
1589 }
1590}