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
50impl WasmMemory {
51 pub fn initial_bytes(&self) -> u32 {
53 self.initial_pages * 65536
54 }
55
56 pub fn max_bytes(&self) -> u32 {
58 self.max_pages.unwrap_or(self.initial_pages) * 65536
59 }
60}
61
62#[derive(Debug, Clone)]
64pub struct DecodedModule {
65 pub functions: Vec<FunctionOps>,
67 pub memories: Vec<WasmMemory>,
69 pub data_segments: Vec<(u32, Vec<u8>)>,
71 pub imports: Vec<ImportEntry>,
73 pub num_imported_funcs: u32,
75 pub func_arg_counts: Vec<u32>,
81 pub type_arg_counts: Vec<u32>,
85}
86
87pub fn decode_wasm_module(wasm_bytes: &[u8]) -> Result<DecodedModule> {
89 let mut functions = Vec::new();
90 let mut memories = Vec::new();
91 let mut data_segments = Vec::new();
92 let mut imports = Vec::new();
93 let mut func_index = 0u32;
94 let mut num_imported_funcs = 0u32;
95 let mut export_names: HashMap<u32, String> = HashMap::new();
96 let mut type_arg_counts: Vec<u32> = Vec::new();
99 let mut func_arg_counts: Vec<u32> = Vec::new();
100
101 for payload in Parser::new(0).parse_all(wasm_bytes) {
102 let payload = payload.context("Failed to parse WASM payload")?;
103
104 match payload {
105 Payload::TypeSection(reader) => {
106 for rec_group in reader {
109 let rec_group = rec_group.context("Failed to parse type")?;
110 for sub_ty in rec_group.types() {
111 let count = match &sub_ty.composite_type.inner {
112 wasmparser::CompositeInnerType::Func(func_ty) => {
113 func_ty.params().len() as u32
114 }
115 _ => 0,
116 };
117 type_arg_counts.push(count);
118 }
119 }
120 }
121 Payload::ImportSection(reader) => {
122 for import in reader {
123 let import = import.context("Failed to parse import")?;
124 let (kind, idx) = match import.ty {
125 wasmparser::TypeRef::Func(type_idx) => {
126 let idx = num_imported_funcs;
127 num_imported_funcs += 1;
128 func_arg_counts
131 .push(type_arg_counts.get(type_idx as usize).copied().unwrap_or(0));
132 (ImportKind::Function(type_idx), idx)
133 }
134 wasmparser::TypeRef::Memory(_) => (ImportKind::Memory, 0),
135 wasmparser::TypeRef::Table(_) => (ImportKind::Table, 0),
136 wasmparser::TypeRef::Global(_) => (ImportKind::Global, 0),
137 _ => continue,
138 };
139 imports.push(ImportEntry {
140 module: import.module.to_string(),
141 name: import.name.to_string(),
142 kind,
143 index: idx,
144 });
145 }
146 }
147 Payload::FunctionSection(reader) => {
148 for ty in reader {
153 let type_idx = ty.context("Failed to parse function type index")?;
154 func_arg_counts
155 .push(type_arg_counts.get(type_idx as usize).copied().unwrap_or(0));
156 }
157 }
158 Payload::MemorySection(reader) => {
159 for (idx, memory) in reader.into_iter().enumerate() {
160 let mem = memory.context("Failed to parse memory")?;
161 memories.push(WasmMemory {
162 index: idx as u32,
163 initial_pages: mem.initial as u32,
164 max_pages: mem.maximum.map(|m| m as u32),
165 shared: mem.shared,
166 });
167 }
168 }
169 Payload::DataSection(reader) => {
170 for data in reader {
171 let data = data.context("Failed to parse data segment")?;
172 if let wasmparser::DataKind::Active {
173 memory_index: 0,
174 offset_expr,
175 } = data.kind
176 {
177 let mut ops = offset_expr.get_operators_reader();
178 if let Ok(wasmparser::Operator::I32Const { value }) = ops.read() {
179 data_segments.push((value as u32, data.data.to_vec()));
180 }
181 }
182 }
183 }
184 Payload::ExportSection(exports) => {
185 for export in exports {
186 let export = export.context("Failed to parse export")?;
187 if export.kind == ExternalKind::Func {
188 export_names.insert(export.index, export.name.to_string());
189 }
190 }
191 }
192 Payload::CodeSectionEntry(body) => {
193 let ops = decode_function_body(&body)?;
194 let actual_index = num_imported_funcs + func_index;
195 let export_name = export_names.get(&actual_index).cloned();
196
197 functions.push(FunctionOps {
198 index: actual_index,
199 export_name,
200 ops,
201 });
202 func_index += 1;
203 }
204 _ => {}
205 }
206 }
207
208 Ok(DecodedModule {
209 functions,
210 memories,
211 data_segments,
212 imports,
213 num_imported_funcs,
214 func_arg_counts,
215 type_arg_counts,
216 })
217}
218
219pub fn decode_wasm_functions(wasm_bytes: &[u8]) -> Result<Vec<FunctionOps>> {
221 let mut functions = Vec::new();
222 let mut func_index = 0u32;
223 let mut num_imported_funcs = 0u32;
224 let mut export_names: HashMap<u32, String> = HashMap::new();
225
226 for payload in Parser::new(0).parse_all(wasm_bytes) {
227 let payload = payload.context("Failed to parse WASM payload")?;
228
229 match payload {
230 Payload::ImportSection(imports) => {
231 for import in imports {
232 let import = import.context("Failed to parse import")?;
233 if matches!(import.ty, wasmparser::TypeRef::Func(_)) {
234 num_imported_funcs += 1;
235 }
236 }
237 }
238 Payload::ExportSection(exports) => {
239 for export in exports {
240 let export = export.context("Failed to parse export")?;
241 if export.kind == ExternalKind::Func {
242 export_names.insert(export.index, export.name.to_string());
243 }
244 }
245 }
246 Payload::CodeSectionEntry(body) => {
247 let ops = decode_function_body(&body)?;
248 let actual_index = num_imported_funcs + func_index;
249 let export_name = export_names.get(&actual_index).cloned();
250
251 functions.push(FunctionOps {
252 index: actual_index,
253 export_name,
254 ops,
255 });
256 func_index += 1;
257 }
258 _ => {}
259 }
260 }
261
262 Ok(functions)
263}
264
265#[derive(Debug, Clone)]
267pub struct FunctionOps {
268 pub index: u32,
270 pub export_name: Option<String>,
272 pub ops: Vec<WasmOp>,
274}
275
276fn decode_function_body(body: &wasmparser::FunctionBody) -> Result<Vec<WasmOp>> {
278 let mut ops = Vec::new();
279
280 let ops_reader = body.get_operators_reader()?;
281 for op_result in ops_reader {
282 let op = op_result.context("Failed to read operator")?;
283
284 if let Some(wasm_op) = convert_operator(&op) {
285 ops.push(wasm_op);
286 }
287 }
288
289 Ok(ops)
290}
291
292fn convert_operator(op: &wasmparser::Operator) -> Option<WasmOp> {
294 use wasmparser::Operator::*;
295
296 match op {
297 I32Const { value } => Some(WasmOp::I32Const(*value)),
299
300 I32Add => Some(WasmOp::I32Add),
302 I32Sub => Some(WasmOp::I32Sub),
303 I32Mul => Some(WasmOp::I32Mul),
304 I32DivS => Some(WasmOp::I32DivS),
305 I32DivU => Some(WasmOp::I32DivU),
306 I32RemS => Some(WasmOp::I32RemS),
307 I32RemU => Some(WasmOp::I32RemU),
308
309 I64Const { value } => Some(WasmOp::I64Const(*value)),
311
312 I64Add => Some(WasmOp::I64Add),
314 I64Sub => Some(WasmOp::I64Sub),
315 I64Mul => Some(WasmOp::I64Mul),
316 I64DivS => Some(WasmOp::I64DivS),
317 I64DivU => Some(WasmOp::I64DivU),
318 I64RemS => Some(WasmOp::I64RemS),
319 I64RemU => Some(WasmOp::I64RemU),
320
321 I64And => Some(WasmOp::I64And),
323 I64Or => Some(WasmOp::I64Or),
324 I64Xor => Some(WasmOp::I64Xor),
325 I64Shl => Some(WasmOp::I64Shl),
326 I64ShrS => Some(WasmOp::I64ShrS),
327 I64ShrU => Some(WasmOp::I64ShrU),
328 I64Rotl => Some(WasmOp::I64Rotl),
329 I64Rotr => Some(WasmOp::I64Rotr),
330 I64Clz => Some(WasmOp::I64Clz),
331 I64Ctz => Some(WasmOp::I64Ctz),
332 I64Popcnt => Some(WasmOp::I64Popcnt),
333 I64Extend8S => Some(WasmOp::I64Extend8S),
334 I64Extend16S => Some(WasmOp::I64Extend16S),
335 I64Extend32S => Some(WasmOp::I64Extend32S),
336
337 I64Eqz => Some(WasmOp::I64Eqz),
339 I64Eq => Some(WasmOp::I64Eq),
340 I64Ne => Some(WasmOp::I64Ne),
341 I64LtS => Some(WasmOp::I64LtS),
342 I64LtU => Some(WasmOp::I64LtU),
343 I64LeS => Some(WasmOp::I64LeS),
344 I64LeU => Some(WasmOp::I64LeU),
345 I64GtS => Some(WasmOp::I64GtS),
346 I64GtU => Some(WasmOp::I64GtU),
347 I64GeS => Some(WasmOp::I64GeS),
348 I64GeU => Some(WasmOp::I64GeU),
349
350 I32And => Some(WasmOp::I32And),
352 I32Or => Some(WasmOp::I32Or),
353 I32Xor => Some(WasmOp::I32Xor),
354 I32Shl => Some(WasmOp::I32Shl),
355 I32ShrS => Some(WasmOp::I32ShrS),
356 I32ShrU => Some(WasmOp::I32ShrU),
357 I32Rotl => Some(WasmOp::I32Rotl),
358 I32Rotr => Some(WasmOp::I32Rotr),
359 I32Clz => Some(WasmOp::I32Clz),
360 I32Ctz => Some(WasmOp::I32Ctz),
361 I32Popcnt => Some(WasmOp::I32Popcnt),
362 I32Extend8S => Some(WasmOp::I32Extend8S),
363 I32Extend16S => Some(WasmOp::I32Extend16S),
364
365 I32Eqz => Some(WasmOp::I32Eqz),
367 I32Eq => Some(WasmOp::I32Eq),
368 I32Ne => Some(WasmOp::I32Ne),
369 I32LtS => Some(WasmOp::I32LtS),
370 I32LtU => Some(WasmOp::I32LtU),
371 I32LeS => Some(WasmOp::I32LeS),
372 I32LeU => Some(WasmOp::I32LeU),
373 I32GtS => Some(WasmOp::I32GtS),
374 I32GtU => Some(WasmOp::I32GtU),
375 I32GeS => Some(WasmOp::I32GeS),
376 I32GeU => Some(WasmOp::I32GeU),
377
378 I32Load { memarg } => Some(WasmOp::I32Load {
380 offset: memarg.offset as u32,
381 align: memarg.align as u32,
382 }),
383 I32Store { memarg } => Some(WasmOp::I32Store {
384 offset: memarg.offset as u32,
385 align: memarg.align as u32,
386 }),
387
388 I32Load8S { memarg } => Some(WasmOp::I32Load8S {
390 offset: memarg.offset as u32,
391 align: memarg.align as u32,
392 }),
393 I32Load8U { memarg } => Some(WasmOp::I32Load8U {
394 offset: memarg.offset as u32,
395 align: memarg.align as u32,
396 }),
397 I32Load16S { memarg } => Some(WasmOp::I32Load16S {
398 offset: memarg.offset as u32,
399 align: memarg.align as u32,
400 }),
401 I32Load16U { memarg } => Some(WasmOp::I32Load16U {
402 offset: memarg.offset as u32,
403 align: memarg.align as u32,
404 }),
405
406 I32Store8 { memarg } => Some(WasmOp::I32Store8 {
408 offset: memarg.offset as u32,
409 align: memarg.align as u32,
410 }),
411 I32Store16 { memarg } => Some(WasmOp::I32Store16 {
412 offset: memarg.offset as u32,
413 align: memarg.align as u32,
414 }),
415
416 LocalGet { local_index } => Some(WasmOp::LocalGet(*local_index)),
418 LocalSet { local_index } => Some(WasmOp::LocalSet(*local_index)),
419 LocalTee { local_index } => Some(WasmOp::LocalTee(*local_index)),
420 GlobalGet { global_index } => Some(WasmOp::GlobalGet(*global_index)),
421 GlobalSet { global_index } => Some(WasmOp::GlobalSet(*global_index)),
422
423 Block { .. } => Some(WasmOp::Block),
425 Loop { .. } => Some(WasmOp::Loop),
426 Br { relative_depth } => Some(WasmOp::Br(*relative_depth)),
427 BrIf { relative_depth } => Some(WasmOp::BrIf(*relative_depth)),
428 Return => Some(WasmOp::Return),
429 Call { function_index } => Some(WasmOp::Call(*function_index)),
430 CallIndirect {
431 type_index,
432 table_index,
433 ..
434 } => Some(WasmOp::CallIndirect {
435 type_index: *type_index,
436 table_index: *table_index,
437 }),
438
439 End => Some(WasmOp::End),
441
442 Nop | Unreachable => None,
444
445 Drop => Some(WasmOp::Drop),
447
448 Select => Some(WasmOp::Select),
450
451 If { .. } => Some(WasmOp::If),
453 Else => Some(WasmOp::Else),
454
455 I64Load8S { memarg } => Some(WasmOp::I64Load8S {
457 offset: memarg.offset as u32,
458 align: memarg.align as u32,
459 }),
460 I64Load8U { memarg } => Some(WasmOp::I64Load8U {
461 offset: memarg.offset as u32,
462 align: memarg.align as u32,
463 }),
464 I64Load16S { memarg } => Some(WasmOp::I64Load16S {
465 offset: memarg.offset as u32,
466 align: memarg.align as u32,
467 }),
468 I64Load16U { memarg } => Some(WasmOp::I64Load16U {
469 offset: memarg.offset as u32,
470 align: memarg.align as u32,
471 }),
472 I64Load32S { memarg } => Some(WasmOp::I64Load32S {
473 offset: memarg.offset as u32,
474 align: memarg.align as u32,
475 }),
476 I64Load32U { memarg } => Some(WasmOp::I64Load32U {
477 offset: memarg.offset as u32,
478 align: memarg.align as u32,
479 }),
480
481 I64Store8 { memarg } => Some(WasmOp::I64Store8 {
483 offset: memarg.offset as u32,
484 align: memarg.align as u32,
485 }),
486 I64Store16 { memarg } => Some(WasmOp::I64Store16 {
487 offset: memarg.offset as u32,
488 align: memarg.align as u32,
489 }),
490 I64Store32 { memarg } => Some(WasmOp::I64Store32 {
491 offset: memarg.offset as u32,
492 align: memarg.align as u32,
493 }),
494
495 MemorySize { mem, .. } => Some(WasmOp::MemorySize(*mem)),
497 MemoryGrow { mem, .. } => Some(WasmOp::MemoryGrow(*mem)),
498
499 V128Const { value } => {
503 let mut bytes = [0u8; 16];
504 bytes.copy_from_slice(value.bytes());
505 Some(WasmOp::V128Const(bytes))
506 }
507 V128Load { memarg } => Some(WasmOp::V128Load {
508 offset: memarg.offset as u32,
509 align: memarg.align as u32,
510 }),
511 V128Store { memarg } => Some(WasmOp::V128Store {
512 offset: memarg.offset as u32,
513 align: memarg.align as u32,
514 }),
515
516 V128And => Some(WasmOp::V128And),
518 V128Or => Some(WasmOp::V128Or),
519 V128Xor => Some(WasmOp::V128Xor),
520 V128Not => Some(WasmOp::V128Not),
521 V128AndNot => Some(WasmOp::V128AndNot),
522
523 I8x16Add => Some(WasmOp::I8x16Add),
525 I8x16Sub => Some(WasmOp::I8x16Sub),
526 I8x16Neg => Some(WasmOp::I8x16Neg),
527 I8x16Eq => Some(WasmOp::I8x16Eq),
528 I8x16Ne => Some(WasmOp::I8x16Ne),
529 I8x16LtS => Some(WasmOp::I8x16LtS),
530 I8x16LtU => Some(WasmOp::I8x16LtU),
531 I8x16GtS => Some(WasmOp::I8x16GtS),
532 I8x16GtU => Some(WasmOp::I8x16GtU),
533 I8x16LeS => Some(WasmOp::I8x16LeS),
534 I8x16LeU => Some(WasmOp::I8x16LeU),
535 I8x16GeS => Some(WasmOp::I8x16GeS),
536 I8x16GeU => Some(WasmOp::I8x16GeU),
537 I8x16Splat => Some(WasmOp::I8x16Splat),
538 I8x16ExtractLaneS { lane } => Some(WasmOp::I8x16ExtractLaneS(*lane)),
539 I8x16ExtractLaneU { lane } => Some(WasmOp::I8x16ExtractLaneU(*lane)),
540 I8x16ReplaceLane { lane } => Some(WasmOp::I8x16ReplaceLane(*lane)),
541 I8x16Shuffle { lanes } => Some(WasmOp::I8x16Shuffle(*lanes)),
542 I8x16Swizzle => Some(WasmOp::I8x16Swizzle),
543
544 I16x8Add => Some(WasmOp::I16x8Add),
546 I16x8Sub => Some(WasmOp::I16x8Sub),
547 I16x8Mul => Some(WasmOp::I16x8Mul),
548 I16x8Neg => Some(WasmOp::I16x8Neg),
549 I16x8Eq => Some(WasmOp::I16x8Eq),
550 I16x8Ne => Some(WasmOp::I16x8Ne),
551 I16x8LtS => Some(WasmOp::I16x8LtS),
552 I16x8LtU => Some(WasmOp::I16x8LtU),
553 I16x8GtS => Some(WasmOp::I16x8GtS),
554 I16x8GtU => Some(WasmOp::I16x8GtU),
555 I16x8LeS => Some(WasmOp::I16x8LeS),
556 I16x8LeU => Some(WasmOp::I16x8LeU),
557 I16x8GeS => Some(WasmOp::I16x8GeS),
558 I16x8GeU => Some(WasmOp::I16x8GeU),
559 I16x8Splat => Some(WasmOp::I16x8Splat),
560 I16x8ExtractLaneS { lane } => Some(WasmOp::I16x8ExtractLaneS(*lane)),
561 I16x8ExtractLaneU { lane } => Some(WasmOp::I16x8ExtractLaneU(*lane)),
562 I16x8ReplaceLane { lane } => Some(WasmOp::I16x8ReplaceLane(*lane)),
563
564 I32x4Add => Some(WasmOp::I32x4Add),
566 I32x4Sub => Some(WasmOp::I32x4Sub),
567 I32x4Mul => Some(WasmOp::I32x4Mul),
568 I32x4Neg => Some(WasmOp::I32x4Neg),
569 I32x4Eq => Some(WasmOp::I32x4Eq),
570 I32x4Ne => Some(WasmOp::I32x4Ne),
571 I32x4LtS => Some(WasmOp::I32x4LtS),
572 I32x4LtU => Some(WasmOp::I32x4LtU),
573 I32x4GtS => Some(WasmOp::I32x4GtS),
574 I32x4GtU => Some(WasmOp::I32x4GtU),
575 I32x4LeS => Some(WasmOp::I32x4LeS),
576 I32x4LeU => Some(WasmOp::I32x4LeU),
577 I32x4GeS => Some(WasmOp::I32x4GeS),
578 I32x4GeU => Some(WasmOp::I32x4GeU),
579 I32x4Splat => Some(WasmOp::I32x4Splat),
580 I32x4ExtractLane { lane } => Some(WasmOp::I32x4ExtractLane(*lane)),
581 I32x4ReplaceLane { lane } => Some(WasmOp::I32x4ReplaceLane(*lane)),
582
583 I64x2Add => Some(WasmOp::I64x2Add),
585 I64x2Sub => Some(WasmOp::I64x2Sub),
586 I64x2Mul => Some(WasmOp::I64x2Mul),
587 I64x2Neg => Some(WasmOp::I64x2Neg),
588 I64x2Eq => Some(WasmOp::I64x2Eq),
589 I64x2Ne => Some(WasmOp::I64x2Ne),
590 I64x2LtS => Some(WasmOp::I64x2LtS),
591 I64x2GtS => Some(WasmOp::I64x2GtS),
592 I64x2LeS => Some(WasmOp::I64x2LeS),
593 I64x2GeS => Some(WasmOp::I64x2GeS),
594 I64x2Splat => Some(WasmOp::I64x2Splat),
595 I64x2ExtractLane { lane } => Some(WasmOp::I64x2ExtractLane(*lane)),
596 I64x2ReplaceLane { lane } => Some(WasmOp::I64x2ReplaceLane(*lane)),
597
598 F32x4Add => Some(WasmOp::F32x4Add),
600 F32x4Sub => Some(WasmOp::F32x4Sub),
601 F32x4Mul => Some(WasmOp::F32x4Mul),
602 F32x4Div => Some(WasmOp::F32x4Div),
603 F32x4Abs => Some(WasmOp::F32x4Abs),
604 F32x4Neg => Some(WasmOp::F32x4Neg),
605 F32x4Sqrt => Some(WasmOp::F32x4Sqrt),
606 F32x4Eq => Some(WasmOp::F32x4Eq),
607 F32x4Ne => Some(WasmOp::F32x4Ne),
608 F32x4Lt => Some(WasmOp::F32x4Lt),
609 F32x4Le => Some(WasmOp::F32x4Le),
610 F32x4Gt => Some(WasmOp::F32x4Gt),
611 F32x4Ge => Some(WasmOp::F32x4Ge),
612 F32x4Splat => Some(WasmOp::F32x4Splat),
613 F32x4ExtractLane { lane } => Some(WasmOp::F32x4ExtractLane(*lane)),
614 F32x4ReplaceLane { lane } => Some(WasmOp::F32x4ReplaceLane(*lane)),
615
616 _ => None,
618 }
619}
620
621#[cfg(test)]
622mod tests {
623 use super::*;
624
625 #[test]
626 fn test_decode_simple_add() {
627 let wat = r#"
628 (module
629 (func (export "add") (param i32 i32) (result i32)
630 local.get 0
631 local.get 1
632 i32.add
633 )
634 )
635 "#;
636
637 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
638 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
639
640 assert_eq!(functions.len(), 1);
641 assert_eq!(functions[0].index, 0);
642 assert_eq!(functions[0].export_name, Some("add".to_string()));
643 assert_eq!(
644 functions[0].ops,
645 vec![
646 WasmOp::LocalGet(0),
647 WasmOp::LocalGet(1),
648 WasmOp::I32Add,
649 WasmOp::End
650 ]
651 );
652 }
653
654 #[test]
655 fn test_decode_arithmetic() {
656 let wat = r#"
657 (module
658 (func (export "calc") (result i32)
659 i32.const 5
660 i32.const 3
661 i32.mul
662 i32.const 2
663 i32.add
664 )
665 )
666 "#;
667
668 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
669 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
670
671 assert_eq!(functions.len(), 1);
672 assert_eq!(functions[0].export_name, Some("calc".to_string()));
673 assert_eq!(
674 functions[0].ops,
675 vec![
676 WasmOp::I32Const(5),
677 WasmOp::I32Const(3),
678 WasmOp::I32Mul,
679 WasmOp::I32Const(2),
680 WasmOp::I32Add,
681 WasmOp::End,
682 ]
683 );
684 }
685
686 #[test]
687 fn test_decode_multi_function_module() {
688 let wat = r#"
689 (module
690 (func $helper)
691 (func (export "add") (param i32 i32) (result i32)
692 local.get 0
693 local.get 1
694 i32.add
695 )
696 (func (export "sub") (param i32 i32) (result i32)
697 local.get 0
698 local.get 1
699 i32.sub
700 )
701 )
702 "#;
703
704 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
705 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
706
707 assert_eq!(functions.len(), 3);
708 assert_eq!(functions[0].index, 0);
709 assert_eq!(functions[0].export_name, None);
710 assert_eq!(functions[1].index, 1);
711 assert_eq!(functions[1].export_name, Some("add".to_string()));
712 assert_eq!(functions[2].index, 2);
713 assert_eq!(functions[2].export_name, Some("sub".to_string()));
714 }
715
716 #[test]
717 fn test_decode_module_with_imports() {
718 let wat = r#"
719 (module
720 (import "env" "log" (func $log (param i32)))
721 (import "env" "memory" (memory 1))
722 (func (export "run") (param i32)
723 local.get 0
724 call 0
725 )
726 )
727 "#;
728
729 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
730 let module = decode_wasm_module(&wasm).expect("Failed to decode");
731
732 assert_eq!(module.imports.len(), 2);
734 assert_eq!(module.num_imported_funcs, 1);
735
736 assert_eq!(module.imports[0].module, "env");
738 assert_eq!(module.imports[0].name, "log");
739 assert!(matches!(module.imports[0].kind, ImportKind::Function(_)));
740
741 assert_eq!(module.imports[1].module, "env");
743 assert_eq!(module.imports[1].name, "memory");
744 assert_eq!(module.imports[1].kind, ImportKind::Memory);
745
746 assert_eq!(module.functions.len(), 1);
748 assert_eq!(module.functions[0].index, 1);
749 assert_eq!(module.functions[0].export_name, Some("run".to_string()));
750 }
751
752 #[test]
753 fn test_find_function_by_export_name() {
754 let wat = r#"
755 (module
756 (func $helper)
757 (func (export "add") (param i32 i32) (result i32)
758 local.get 0
759 local.get 1
760 i32.add
761 )
762 )
763 "#;
764
765 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
766 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
767
768 let add_func = functions
769 .iter()
770 .find(|f| f.export_name.as_deref() == Some("add"))
771 .expect("Should find 'add' function");
772
773 assert_eq!(add_func.index, 1);
774 assert!(add_func.ops.contains(&WasmOp::I32Add));
775 }
776
777 #[test]
778 fn test_decode_subword_loads() {
779 let wat = r#"
780 (module
781 (memory 1)
782 (func (export "test") (param i32) (result i32)
783 local.get 0
784 i32.load8_u
785 )
786 )
787 "#;
788
789 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
790 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
791
792 assert_eq!(functions.len(), 1);
793 assert!(functions[0].ops.contains(&WasmOp::I32Load8U {
794 offset: 0,
795 align: 0,
796 }));
797 }
798
799 #[test]
800 fn test_decode_subword_stores() {
801 let wat = r#"
802 (module
803 (memory 1)
804 (func (export "test") (param i32 i32)
805 local.get 0
806 local.get 1
807 i32.store8
808 )
809 )
810 "#;
811
812 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
813 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
814
815 assert_eq!(functions.len(), 1);
816 assert!(functions[0].ops.contains(&WasmOp::I32Store8 {
817 offset: 0,
818 align: 0,
819 }));
820 }
821
822 #[test]
823 fn test_decode_memory_size_grow() {
824 let wat = r#"
825 (module
826 (memory 1)
827 (func (export "test") (result i32)
828 memory.size
829 )
830 )
831 "#;
832
833 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
834 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
835
836 assert_eq!(functions.len(), 1);
837 assert!(functions[0].ops.contains(&WasmOp::MemorySize(0)));
838 }
839
840 #[test]
841 fn test_decode_memory_grow() {
842 let wat = r#"
843 (module
844 (memory 1)
845 (func (export "test") (param i32) (result i32)
846 local.get 0
847 memory.grow
848 )
849 )
850 "#;
851
852 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
853 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
854
855 assert_eq!(functions.len(), 1);
856 assert!(functions[0].ops.contains(&WasmOp::MemoryGrow(0)));
857 }
858
859 #[test]
860 fn test_decode_i64_subword_loads() {
861 let wat = r#"
862 (module
863 (memory 1)
864 (func (export "test") (param i32) (result i64)
865 local.get 0
866 i64.load8_s
867 )
868 )
869 "#;
870
871 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
872 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
873
874 assert_eq!(functions.len(), 1);
875 assert!(functions[0].ops.contains(&WasmOp::I64Load8S {
876 offset: 0,
877 align: 0,
878 }));
879 }
880
881 #[test]
882 fn test_decode_all_subword_memory_ops() {
883 let wat = r#"
885 (module
886 (memory 1)
887 (func (export "test") (param i32)
888 ;; i32 sub-word loads
889 local.get 0
890 i32.load8_s
891 drop
892 local.get 0
893 i32.load8_u
894 drop
895 local.get 0
896 i32.load16_s
897 drop
898 local.get 0
899 i32.load16_u
900 drop
901
902 ;; i32 sub-word stores
903 local.get 0
904 i32.const 42
905 i32.store8
906 local.get 0
907 i32.const 42
908 i32.store16
909
910 ;; i64 sub-word loads
911 local.get 0
912 i64.load8_s
913 drop
914 local.get 0
915 i64.load8_u
916 drop
917 local.get 0
918 i64.load16_s
919 drop
920 local.get 0
921 i64.load16_u
922 drop
923 local.get 0
924 i64.load32_s
925 drop
926 local.get 0
927 i64.load32_u
928 drop
929
930 ;; i64 sub-word stores
931 local.get 0
932 i64.const 42
933 i64.store8
934 local.get 0
935 i64.const 42
936 i64.store16
937 local.get 0
938 i64.const 42
939 i64.store32
940 )
941 )
942 "#;
943
944 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
945 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
946
947 assert_eq!(functions.len(), 1);
948 let ops = &functions[0].ops;
949
950 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load8S { .. })));
952 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load8U { .. })));
953 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load16S { .. })));
954 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load16U { .. })));
955 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Store8 { .. })));
956 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Store16 { .. })));
957
958 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load8S { .. })));
960 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load8U { .. })));
961 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load16S { .. })));
962 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load16U { .. })));
963 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load32S { .. })));
964 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load32U { .. })));
965 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store8 { .. })));
966 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store16 { .. })));
967 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store32 { .. })));
968 }
969
970 #[test]
971 fn test_decode_simd_i32x4_add() {
972 let wat = r#"
973 (module
974 (func (export "add_v128") (param v128 v128) (result v128)
975 local.get 0
976 local.get 1
977 i32x4.add
978 )
979 )
980 "#;
981
982 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
983 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
984
985 assert_eq!(functions.len(), 1);
986 assert!(
987 functions[0].ops.contains(&WasmOp::I32x4Add),
988 "Should decode i32x4.add: {:?}",
989 functions[0].ops
990 );
991 }
992
993 #[test]
994 fn test_decode_simd_v128_const() {
995 let wat = r#"
996 (module
997 (func (export "const_v128") (result v128)
998 v128.const i32x4 1 2 3 4
999 )
1000 )
1001 "#;
1002
1003 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1004 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1005
1006 assert_eq!(functions.len(), 1);
1007 assert!(
1008 functions[0]
1009 .ops
1010 .iter()
1011 .any(|o| matches!(o, WasmOp::V128Const(_))),
1012 "Should decode v128.const: {:?}",
1013 functions[0].ops
1014 );
1015 }
1016
1017 #[test]
1018 fn test_decode_simd_v128_load_store() {
1019 let wat = r#"
1020 (module
1021 (memory 1)
1022 (func (export "load_store") (param i32)
1023 local.get 0
1024 v128.load
1025 local.get 0
1026 v128.store
1027 )
1028 )
1029 "#;
1030
1031 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1032 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1033
1034 assert_eq!(functions.len(), 1);
1035 let ops = &functions[0].ops;
1036 assert!(
1037 ops.iter().any(|o| matches!(o, WasmOp::V128Load { .. })),
1038 "Should decode v128.load"
1039 );
1040 assert!(
1041 ops.iter().any(|o| matches!(o, WasmOp::V128Store { .. })),
1042 "Should decode v128.store"
1043 );
1044 }
1045
1046 #[test]
1047 fn test_decode_simd_bitwise_ops() {
1048 let wat = r#"
1049 (module
1050 (func (export "bitwise") (param v128 v128) (result v128)
1051 local.get 0
1052 local.get 1
1053 v128.and
1054 )
1055 )
1056 "#;
1057
1058 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1059 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1060
1061 assert_eq!(functions.len(), 1);
1062 assert!(functions[0].ops.contains(&WasmOp::V128And));
1063 }
1064
1065 #[test]
1066 fn test_decode_simd_splat() {
1067 let wat = r#"
1068 (module
1069 (func (export "splat") (param i32) (result v128)
1070 local.get 0
1071 i32x4.splat
1072 )
1073 )
1074 "#;
1075
1076 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1077 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1078
1079 assert_eq!(functions.len(), 1);
1080 assert!(functions[0].ops.contains(&WasmOp::I32x4Splat));
1081 }
1082
1083 #[test]
1084 fn test_decode_simd_extract_lane() {
1085 let wat = r#"
1086 (module
1087 (func (export "extract") (param v128) (result i32)
1088 local.get 0
1089 i32x4.extract_lane 2
1090 )
1091 )
1092 "#;
1093
1094 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1095 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1096
1097 assert_eq!(functions.len(), 1);
1098 assert!(
1099 functions[0].ops.contains(&WasmOp::I32x4ExtractLane(2)),
1100 "Should decode i32x4.extract_lane 2"
1101 );
1102 }
1103
1104 #[test]
1105 fn test_decode_simd_f32x4_arithmetic() {
1106 let wat = r#"
1107 (module
1108 (func (export "f32x4_add") (param v128 v128) (result v128)
1109 local.get 0
1110 local.get 1
1111 f32x4.add
1112 )
1113 )
1114 "#;
1115
1116 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1117 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1118
1119 assert_eq!(functions.len(), 1);
1120 assert!(functions[0].ops.contains(&WasmOp::F32x4Add));
1121 }
1122
1123 #[test]
1124 fn test_decode_simd_multiple_ops() {
1125 let wat = r#"
1126 (module
1127 (func (export "simd_ops") (param v128 v128 v128) (result v128)
1128 ;; (a + b) * c
1129 local.get 0
1130 local.get 1
1131 i32x4.add
1132 local.get 2
1133 i32x4.mul
1134 )
1135 )
1136 "#;
1137
1138 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1139 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1140
1141 assert_eq!(functions.len(), 1);
1142 let ops = &functions[0].ops;
1143 assert!(ops.contains(&WasmOp::I32x4Add));
1144 assert!(ops.contains(&WasmOp::I32x4Mul));
1145 }
1146}