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