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