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 I64ExtendI32U => Some(WasmOp::I64ExtendI32U),
342 I64ExtendI32S => Some(WasmOp::I64ExtendI32S),
343 I32WrapI64 => Some(WasmOp::I32WrapI64),
344
345 I64Eqz => Some(WasmOp::I64Eqz),
347 I64Eq => Some(WasmOp::I64Eq),
348 I64Ne => Some(WasmOp::I64Ne),
349 I64LtS => Some(WasmOp::I64LtS),
350 I64LtU => Some(WasmOp::I64LtU),
351 I64LeS => Some(WasmOp::I64LeS),
352 I64LeU => Some(WasmOp::I64LeU),
353 I64GtS => Some(WasmOp::I64GtS),
354 I64GtU => Some(WasmOp::I64GtU),
355 I64GeS => Some(WasmOp::I64GeS),
356 I64GeU => Some(WasmOp::I64GeU),
357
358 I32And => Some(WasmOp::I32And),
360 I32Or => Some(WasmOp::I32Or),
361 I32Xor => Some(WasmOp::I32Xor),
362 I32Shl => Some(WasmOp::I32Shl),
363 I32ShrS => Some(WasmOp::I32ShrS),
364 I32ShrU => Some(WasmOp::I32ShrU),
365 I32Rotl => Some(WasmOp::I32Rotl),
366 I32Rotr => Some(WasmOp::I32Rotr),
367 I32Clz => Some(WasmOp::I32Clz),
368 I32Ctz => Some(WasmOp::I32Ctz),
369 I32Popcnt => Some(WasmOp::I32Popcnt),
370 I32Extend8S => Some(WasmOp::I32Extend8S),
371 I32Extend16S => Some(WasmOp::I32Extend16S),
372
373 I32Eqz => Some(WasmOp::I32Eqz),
375 I32Eq => Some(WasmOp::I32Eq),
376 I32Ne => Some(WasmOp::I32Ne),
377 I32LtS => Some(WasmOp::I32LtS),
378 I32LtU => Some(WasmOp::I32LtU),
379 I32LeS => Some(WasmOp::I32LeS),
380 I32LeU => Some(WasmOp::I32LeU),
381 I32GtS => Some(WasmOp::I32GtS),
382 I32GtU => Some(WasmOp::I32GtU),
383 I32GeS => Some(WasmOp::I32GeS),
384 I32GeU => Some(WasmOp::I32GeU),
385
386 I32Load { memarg } => Some(WasmOp::I32Load {
388 offset: memarg.offset as u32,
389 align: memarg.align as u32,
390 }),
391 I32Store { memarg } => Some(WasmOp::I32Store {
392 offset: memarg.offset as u32,
393 align: memarg.align as u32,
394 }),
395
396 I32Load8S { memarg } => Some(WasmOp::I32Load8S {
398 offset: memarg.offset as u32,
399 align: memarg.align as u32,
400 }),
401 I32Load8U { memarg } => Some(WasmOp::I32Load8U {
402 offset: memarg.offset as u32,
403 align: memarg.align as u32,
404 }),
405 I32Load16S { memarg } => Some(WasmOp::I32Load16S {
406 offset: memarg.offset as u32,
407 align: memarg.align as u32,
408 }),
409 I32Load16U { memarg } => Some(WasmOp::I32Load16U {
410 offset: memarg.offset as u32,
411 align: memarg.align as u32,
412 }),
413
414 I32Store8 { memarg } => Some(WasmOp::I32Store8 {
416 offset: memarg.offset as u32,
417 align: memarg.align as u32,
418 }),
419 I32Store16 { memarg } => Some(WasmOp::I32Store16 {
420 offset: memarg.offset as u32,
421 align: memarg.align as u32,
422 }),
423
424 LocalGet { local_index } => Some(WasmOp::LocalGet(*local_index)),
426 LocalSet { local_index } => Some(WasmOp::LocalSet(*local_index)),
427 LocalTee { local_index } => Some(WasmOp::LocalTee(*local_index)),
428 GlobalGet { global_index } => Some(WasmOp::GlobalGet(*global_index)),
429 GlobalSet { global_index } => Some(WasmOp::GlobalSet(*global_index)),
430
431 Block { .. } => Some(WasmOp::Block),
433 Loop { .. } => Some(WasmOp::Loop),
434 Br { relative_depth } => Some(WasmOp::Br(*relative_depth)),
435 BrIf { relative_depth } => Some(WasmOp::BrIf(*relative_depth)),
436 BrTable { targets } => {
442 let default = targets.default();
443 let tgts: Vec<u32> = targets.targets().filter_map(Result::ok).collect();
444 Some(WasmOp::BrTable {
445 targets: tgts,
446 default,
447 })
448 }
449 Return => Some(WasmOp::Return),
450 Call { function_index } => Some(WasmOp::Call(*function_index)),
451 CallIndirect {
452 type_index,
453 table_index,
454 ..
455 } => Some(WasmOp::CallIndirect {
456 type_index: *type_index,
457 table_index: *table_index,
458 }),
459
460 End => Some(WasmOp::End),
462
463 Nop | Unreachable => None,
465
466 Drop => Some(WasmOp::Drop),
468
469 Select => Some(WasmOp::Select),
471
472 If { .. } => Some(WasmOp::If),
474 Else => Some(WasmOp::Else),
475
476 I64Load8S { memarg } => Some(WasmOp::I64Load8S {
478 offset: memarg.offset as u32,
479 align: memarg.align as u32,
480 }),
481 I64Load8U { memarg } => Some(WasmOp::I64Load8U {
482 offset: memarg.offset as u32,
483 align: memarg.align as u32,
484 }),
485 I64Load16S { memarg } => Some(WasmOp::I64Load16S {
486 offset: memarg.offset as u32,
487 align: memarg.align as u32,
488 }),
489 I64Load16U { memarg } => Some(WasmOp::I64Load16U {
490 offset: memarg.offset as u32,
491 align: memarg.align as u32,
492 }),
493 I64Load32S { memarg } => Some(WasmOp::I64Load32S {
494 offset: memarg.offset as u32,
495 align: memarg.align as u32,
496 }),
497 I64Load32U { memarg } => Some(WasmOp::I64Load32U {
498 offset: memarg.offset as u32,
499 align: memarg.align as u32,
500 }),
501
502 I64Store8 { memarg } => Some(WasmOp::I64Store8 {
504 offset: memarg.offset as u32,
505 align: memarg.align as u32,
506 }),
507 I64Store16 { memarg } => Some(WasmOp::I64Store16 {
508 offset: memarg.offset as u32,
509 align: memarg.align as u32,
510 }),
511 I64Store32 { memarg } => Some(WasmOp::I64Store32 {
512 offset: memarg.offset as u32,
513 align: memarg.align as u32,
514 }),
515
516 MemorySize { mem, .. } => Some(WasmOp::MemorySize(*mem)),
518 MemoryGrow { mem, .. } => Some(WasmOp::MemoryGrow(*mem)),
519
520 V128Const { value } => {
524 let mut bytes = [0u8; 16];
525 bytes.copy_from_slice(value.bytes());
526 Some(WasmOp::V128Const(bytes))
527 }
528 V128Load { memarg } => Some(WasmOp::V128Load {
529 offset: memarg.offset as u32,
530 align: memarg.align as u32,
531 }),
532 V128Store { memarg } => Some(WasmOp::V128Store {
533 offset: memarg.offset as u32,
534 align: memarg.align as u32,
535 }),
536
537 V128And => Some(WasmOp::V128And),
539 V128Or => Some(WasmOp::V128Or),
540 V128Xor => Some(WasmOp::V128Xor),
541 V128Not => Some(WasmOp::V128Not),
542 V128AndNot => Some(WasmOp::V128AndNot),
543
544 I8x16Add => Some(WasmOp::I8x16Add),
546 I8x16Sub => Some(WasmOp::I8x16Sub),
547 I8x16Neg => Some(WasmOp::I8x16Neg),
548 I8x16Eq => Some(WasmOp::I8x16Eq),
549 I8x16Ne => Some(WasmOp::I8x16Ne),
550 I8x16LtS => Some(WasmOp::I8x16LtS),
551 I8x16LtU => Some(WasmOp::I8x16LtU),
552 I8x16GtS => Some(WasmOp::I8x16GtS),
553 I8x16GtU => Some(WasmOp::I8x16GtU),
554 I8x16LeS => Some(WasmOp::I8x16LeS),
555 I8x16LeU => Some(WasmOp::I8x16LeU),
556 I8x16GeS => Some(WasmOp::I8x16GeS),
557 I8x16GeU => Some(WasmOp::I8x16GeU),
558 I8x16Splat => Some(WasmOp::I8x16Splat),
559 I8x16ExtractLaneS { lane } => Some(WasmOp::I8x16ExtractLaneS(*lane)),
560 I8x16ExtractLaneU { lane } => Some(WasmOp::I8x16ExtractLaneU(*lane)),
561 I8x16ReplaceLane { lane } => Some(WasmOp::I8x16ReplaceLane(*lane)),
562 I8x16Shuffle { lanes } => Some(WasmOp::I8x16Shuffle(*lanes)),
563 I8x16Swizzle => Some(WasmOp::I8x16Swizzle),
564
565 I16x8Add => Some(WasmOp::I16x8Add),
567 I16x8Sub => Some(WasmOp::I16x8Sub),
568 I16x8Mul => Some(WasmOp::I16x8Mul),
569 I16x8Neg => Some(WasmOp::I16x8Neg),
570 I16x8Eq => Some(WasmOp::I16x8Eq),
571 I16x8Ne => Some(WasmOp::I16x8Ne),
572 I16x8LtS => Some(WasmOp::I16x8LtS),
573 I16x8LtU => Some(WasmOp::I16x8LtU),
574 I16x8GtS => Some(WasmOp::I16x8GtS),
575 I16x8GtU => Some(WasmOp::I16x8GtU),
576 I16x8LeS => Some(WasmOp::I16x8LeS),
577 I16x8LeU => Some(WasmOp::I16x8LeU),
578 I16x8GeS => Some(WasmOp::I16x8GeS),
579 I16x8GeU => Some(WasmOp::I16x8GeU),
580 I16x8Splat => Some(WasmOp::I16x8Splat),
581 I16x8ExtractLaneS { lane } => Some(WasmOp::I16x8ExtractLaneS(*lane)),
582 I16x8ExtractLaneU { lane } => Some(WasmOp::I16x8ExtractLaneU(*lane)),
583 I16x8ReplaceLane { lane } => Some(WasmOp::I16x8ReplaceLane(*lane)),
584
585 I32x4Add => Some(WasmOp::I32x4Add),
587 I32x4Sub => Some(WasmOp::I32x4Sub),
588 I32x4Mul => Some(WasmOp::I32x4Mul),
589 I32x4Neg => Some(WasmOp::I32x4Neg),
590 I32x4Eq => Some(WasmOp::I32x4Eq),
591 I32x4Ne => Some(WasmOp::I32x4Ne),
592 I32x4LtS => Some(WasmOp::I32x4LtS),
593 I32x4LtU => Some(WasmOp::I32x4LtU),
594 I32x4GtS => Some(WasmOp::I32x4GtS),
595 I32x4GtU => Some(WasmOp::I32x4GtU),
596 I32x4LeS => Some(WasmOp::I32x4LeS),
597 I32x4LeU => Some(WasmOp::I32x4LeU),
598 I32x4GeS => Some(WasmOp::I32x4GeS),
599 I32x4GeU => Some(WasmOp::I32x4GeU),
600 I32x4Splat => Some(WasmOp::I32x4Splat),
601 I32x4ExtractLane { lane } => Some(WasmOp::I32x4ExtractLane(*lane)),
602 I32x4ReplaceLane { lane } => Some(WasmOp::I32x4ReplaceLane(*lane)),
603
604 I64x2Add => Some(WasmOp::I64x2Add),
606 I64x2Sub => Some(WasmOp::I64x2Sub),
607 I64x2Mul => Some(WasmOp::I64x2Mul),
608 I64x2Neg => Some(WasmOp::I64x2Neg),
609 I64x2Eq => Some(WasmOp::I64x2Eq),
610 I64x2Ne => Some(WasmOp::I64x2Ne),
611 I64x2LtS => Some(WasmOp::I64x2LtS),
612 I64x2GtS => Some(WasmOp::I64x2GtS),
613 I64x2LeS => Some(WasmOp::I64x2LeS),
614 I64x2GeS => Some(WasmOp::I64x2GeS),
615 I64x2Splat => Some(WasmOp::I64x2Splat),
616 I64x2ExtractLane { lane } => Some(WasmOp::I64x2ExtractLane(*lane)),
617 I64x2ReplaceLane { lane } => Some(WasmOp::I64x2ReplaceLane(*lane)),
618
619 F32x4Add => Some(WasmOp::F32x4Add),
621 F32x4Sub => Some(WasmOp::F32x4Sub),
622 F32x4Mul => Some(WasmOp::F32x4Mul),
623 F32x4Div => Some(WasmOp::F32x4Div),
624 F32x4Abs => Some(WasmOp::F32x4Abs),
625 F32x4Neg => Some(WasmOp::F32x4Neg),
626 F32x4Sqrt => Some(WasmOp::F32x4Sqrt),
627 F32x4Eq => Some(WasmOp::F32x4Eq),
628 F32x4Ne => Some(WasmOp::F32x4Ne),
629 F32x4Lt => Some(WasmOp::F32x4Lt),
630 F32x4Le => Some(WasmOp::F32x4Le),
631 F32x4Gt => Some(WasmOp::F32x4Gt),
632 F32x4Ge => Some(WasmOp::F32x4Ge),
633 F32x4Splat => Some(WasmOp::F32x4Splat),
634 F32x4ExtractLane { lane } => Some(WasmOp::F32x4ExtractLane(*lane)),
635 F32x4ReplaceLane { lane } => Some(WasmOp::F32x4ReplaceLane(*lane)),
636
637 _ => None,
639 }
640}
641
642#[cfg(test)]
643mod tests {
644 use super::*;
645
646 #[test]
647 fn test_decode_simple_add() {
648 let wat = r#"
649 (module
650 (func (export "add") (param i32 i32) (result i32)
651 local.get 0
652 local.get 1
653 i32.add
654 )
655 )
656 "#;
657
658 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
659 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
660
661 assert_eq!(functions.len(), 1);
662 assert_eq!(functions[0].index, 0);
663 assert_eq!(functions[0].export_name, Some("add".to_string()));
664 assert_eq!(
665 functions[0].ops,
666 vec![
667 WasmOp::LocalGet(0),
668 WasmOp::LocalGet(1),
669 WasmOp::I32Add,
670 WasmOp::End
671 ]
672 );
673 }
674
675 #[test]
681 fn test_decode_i64_i32_width_conversions() {
682 let wat = r#"
683 (module
684 (func (export "conv") (param i32 i64) (result i32)
685 local.get 0
686 i64.extend_i32_u
687 local.get 0
688 i64.extend_i32_s
689 i64.add
690 local.get 1
691 i64.add
692 i32.wrap_i64
693 )
694 )
695 "#;
696 let wasm = wat::parse_str(wat).expect("parse");
697 let functions = decode_wasm_functions(&wasm).expect("decode");
698 let ops = &functions[0].ops;
699 assert!(
700 ops.contains(&WasmOp::I64ExtendI32U),
701 "i64.extend_i32_u must decode (not be dropped): {ops:?}"
702 );
703 assert!(
704 ops.contains(&WasmOp::I64ExtendI32S),
705 "i64.extend_i32_s must decode (not be dropped): {ops:?}"
706 );
707 assert!(
708 ops.contains(&WasmOp::I32WrapI64),
709 "i32.wrap_i64 must decode (not be dropped): {ops:?}"
710 );
711 }
712
713 #[test]
718 fn test_decode_br_table() {
719 let wat = r#"
720 (module
721 (func (export "bt") (param i32) (result i32)
722 (block (block (block
723 local.get 0
724 br_table 2 0 1 2)
725 i32.const 30 return)
726 i32.const 20 return)
727 i32.const 10))
728 "#;
729 let wasm = wat::parse_str(wat).expect("parse");
730 let functions = decode_wasm_functions(&wasm).expect("decode");
731 let bt = functions[0]
732 .ops
733 .iter()
734 .find_map(|o| match o {
735 WasmOp::BrTable { targets, default } => Some((targets.clone(), *default)),
736 _ => None,
737 })
738 .expect("br_table must decode (not be dropped)");
739 assert_eq!(bt.0, vec![2, 0, 1], "br_table targets preserved in order");
740 assert_eq!(bt.1, 2, "br_table default preserved");
741 }
742
743 #[test]
744 fn test_decode_arithmetic() {
745 let wat = r#"
746 (module
747 (func (export "calc") (result i32)
748 i32.const 5
749 i32.const 3
750 i32.mul
751 i32.const 2
752 i32.add
753 )
754 )
755 "#;
756
757 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
758 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
759
760 assert_eq!(functions.len(), 1);
761 assert_eq!(functions[0].export_name, Some("calc".to_string()));
762 assert_eq!(
763 functions[0].ops,
764 vec![
765 WasmOp::I32Const(5),
766 WasmOp::I32Const(3),
767 WasmOp::I32Mul,
768 WasmOp::I32Const(2),
769 WasmOp::I32Add,
770 WasmOp::End,
771 ]
772 );
773 }
774
775 #[test]
776 fn test_decode_multi_function_module() {
777 let wat = r#"
778 (module
779 (func $helper)
780 (func (export "add") (param i32 i32) (result i32)
781 local.get 0
782 local.get 1
783 i32.add
784 )
785 (func (export "sub") (param i32 i32) (result i32)
786 local.get 0
787 local.get 1
788 i32.sub
789 )
790 )
791 "#;
792
793 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
794 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
795
796 assert_eq!(functions.len(), 3);
797 assert_eq!(functions[0].index, 0);
798 assert_eq!(functions[0].export_name, None);
799 assert_eq!(functions[1].index, 1);
800 assert_eq!(functions[1].export_name, Some("add".to_string()));
801 assert_eq!(functions[2].index, 2);
802 assert_eq!(functions[2].export_name, Some("sub".to_string()));
803 }
804
805 #[test]
806 fn test_decode_module_with_imports() {
807 let wat = r#"
808 (module
809 (import "env" "log" (func $log (param i32)))
810 (import "env" "memory" (memory 1))
811 (func (export "run") (param i32)
812 local.get 0
813 call 0
814 )
815 )
816 "#;
817
818 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
819 let module = decode_wasm_module(&wasm).expect("Failed to decode");
820
821 assert_eq!(module.imports.len(), 2);
823 assert_eq!(module.num_imported_funcs, 1);
824
825 assert_eq!(module.imports[0].module, "env");
827 assert_eq!(module.imports[0].name, "log");
828 assert!(matches!(module.imports[0].kind, ImportKind::Function(_)));
829
830 assert_eq!(module.imports[1].module, "env");
832 assert_eq!(module.imports[1].name, "memory");
833 assert_eq!(module.imports[1].kind, ImportKind::Memory);
834
835 assert_eq!(module.functions.len(), 1);
837 assert_eq!(module.functions[0].index, 1);
838 assert_eq!(module.functions[0].export_name, Some("run".to_string()));
839 }
840
841 #[test]
842 fn test_find_function_by_export_name() {
843 let wat = r#"
844 (module
845 (func $helper)
846 (func (export "add") (param i32 i32) (result i32)
847 local.get 0
848 local.get 1
849 i32.add
850 )
851 )
852 "#;
853
854 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
855 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
856
857 let add_func = functions
858 .iter()
859 .find(|f| f.export_name.as_deref() == Some("add"))
860 .expect("Should find 'add' function");
861
862 assert_eq!(add_func.index, 1);
863 assert!(add_func.ops.contains(&WasmOp::I32Add));
864 }
865
866 #[test]
867 fn test_decode_subword_loads() {
868 let wat = r#"
869 (module
870 (memory 1)
871 (func (export "test") (param i32) (result i32)
872 local.get 0
873 i32.load8_u
874 )
875 )
876 "#;
877
878 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
879 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
880
881 assert_eq!(functions.len(), 1);
882 assert!(functions[0].ops.contains(&WasmOp::I32Load8U {
883 offset: 0,
884 align: 0,
885 }));
886 }
887
888 #[test]
889 fn test_decode_subword_stores() {
890 let wat = r#"
891 (module
892 (memory 1)
893 (func (export "test") (param i32 i32)
894 local.get 0
895 local.get 1
896 i32.store8
897 )
898 )
899 "#;
900
901 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
902 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
903
904 assert_eq!(functions.len(), 1);
905 assert!(functions[0].ops.contains(&WasmOp::I32Store8 {
906 offset: 0,
907 align: 0,
908 }));
909 }
910
911 #[test]
912 fn test_decode_memory_size_grow() {
913 let wat = r#"
914 (module
915 (memory 1)
916 (func (export "test") (result i32)
917 memory.size
918 )
919 )
920 "#;
921
922 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
923 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
924
925 assert_eq!(functions.len(), 1);
926 assert!(functions[0].ops.contains(&WasmOp::MemorySize(0)));
927 }
928
929 #[test]
930 fn test_decode_memory_grow() {
931 let wat = r#"
932 (module
933 (memory 1)
934 (func (export "test") (param i32) (result i32)
935 local.get 0
936 memory.grow
937 )
938 )
939 "#;
940
941 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
942 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
943
944 assert_eq!(functions.len(), 1);
945 assert!(functions[0].ops.contains(&WasmOp::MemoryGrow(0)));
946 }
947
948 #[test]
949 fn test_decode_i64_subword_loads() {
950 let wat = r#"
951 (module
952 (memory 1)
953 (func (export "test") (param i32) (result i64)
954 local.get 0
955 i64.load8_s
956 )
957 )
958 "#;
959
960 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
961 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
962
963 assert_eq!(functions.len(), 1);
964 assert!(functions[0].ops.contains(&WasmOp::I64Load8S {
965 offset: 0,
966 align: 0,
967 }));
968 }
969
970 #[test]
971 fn test_decode_all_subword_memory_ops() {
972 let wat = r#"
974 (module
975 (memory 1)
976 (func (export "test") (param i32)
977 ;; i32 sub-word loads
978 local.get 0
979 i32.load8_s
980 drop
981 local.get 0
982 i32.load8_u
983 drop
984 local.get 0
985 i32.load16_s
986 drop
987 local.get 0
988 i32.load16_u
989 drop
990
991 ;; i32 sub-word stores
992 local.get 0
993 i32.const 42
994 i32.store8
995 local.get 0
996 i32.const 42
997 i32.store16
998
999 ;; i64 sub-word loads
1000 local.get 0
1001 i64.load8_s
1002 drop
1003 local.get 0
1004 i64.load8_u
1005 drop
1006 local.get 0
1007 i64.load16_s
1008 drop
1009 local.get 0
1010 i64.load16_u
1011 drop
1012 local.get 0
1013 i64.load32_s
1014 drop
1015 local.get 0
1016 i64.load32_u
1017 drop
1018
1019 ;; i64 sub-word stores
1020 local.get 0
1021 i64.const 42
1022 i64.store8
1023 local.get 0
1024 i64.const 42
1025 i64.store16
1026 local.get 0
1027 i64.const 42
1028 i64.store32
1029 )
1030 )
1031 "#;
1032
1033 let wasm = wat::parse_str(wat).expect("Failed to parse WAT");
1034 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1035
1036 assert_eq!(functions.len(), 1);
1037 let ops = &functions[0].ops;
1038
1039 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load8S { .. })));
1041 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load8U { .. })));
1042 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load16S { .. })));
1043 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Load16U { .. })));
1044 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Store8 { .. })));
1045 assert!(ops.iter().any(|o| matches!(o, WasmOp::I32Store16 { .. })));
1046
1047 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load8S { .. })));
1049 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load8U { .. })));
1050 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load16S { .. })));
1051 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load16U { .. })));
1052 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load32S { .. })));
1053 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Load32U { .. })));
1054 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store8 { .. })));
1055 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store16 { .. })));
1056 assert!(ops.iter().any(|o| matches!(o, WasmOp::I64Store32 { .. })));
1057 }
1058
1059 #[test]
1060 fn test_decode_simd_i32x4_add() {
1061 let wat = r#"
1062 (module
1063 (func (export "add_v128") (param v128 v128) (result v128)
1064 local.get 0
1065 local.get 1
1066 i32x4.add
1067 )
1068 )
1069 "#;
1070
1071 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1072 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1073
1074 assert_eq!(functions.len(), 1);
1075 assert!(
1076 functions[0].ops.contains(&WasmOp::I32x4Add),
1077 "Should decode i32x4.add: {:?}",
1078 functions[0].ops
1079 );
1080 }
1081
1082 #[test]
1083 fn test_decode_simd_v128_const() {
1084 let wat = r#"
1085 (module
1086 (func (export "const_v128") (result v128)
1087 v128.const i32x4 1 2 3 4
1088 )
1089 )
1090 "#;
1091
1092 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1093 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1094
1095 assert_eq!(functions.len(), 1);
1096 assert!(
1097 functions[0]
1098 .ops
1099 .iter()
1100 .any(|o| matches!(o, WasmOp::V128Const(_))),
1101 "Should decode v128.const: {:?}",
1102 functions[0].ops
1103 );
1104 }
1105
1106 #[test]
1107 fn test_decode_simd_v128_load_store() {
1108 let wat = r#"
1109 (module
1110 (memory 1)
1111 (func (export "load_store") (param i32)
1112 local.get 0
1113 v128.load
1114 local.get 0
1115 v128.store
1116 )
1117 )
1118 "#;
1119
1120 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1121 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1122
1123 assert_eq!(functions.len(), 1);
1124 let ops = &functions[0].ops;
1125 assert!(
1126 ops.iter().any(|o| matches!(o, WasmOp::V128Load { .. })),
1127 "Should decode v128.load"
1128 );
1129 assert!(
1130 ops.iter().any(|o| matches!(o, WasmOp::V128Store { .. })),
1131 "Should decode v128.store"
1132 );
1133 }
1134
1135 #[test]
1136 fn test_decode_simd_bitwise_ops() {
1137 let wat = r#"
1138 (module
1139 (func (export "bitwise") (param v128 v128) (result v128)
1140 local.get 0
1141 local.get 1
1142 v128.and
1143 )
1144 )
1145 "#;
1146
1147 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1148 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1149
1150 assert_eq!(functions.len(), 1);
1151 assert!(functions[0].ops.contains(&WasmOp::V128And));
1152 }
1153
1154 #[test]
1155 fn test_decode_simd_splat() {
1156 let wat = r#"
1157 (module
1158 (func (export "splat") (param i32) (result v128)
1159 local.get 0
1160 i32x4.splat
1161 )
1162 )
1163 "#;
1164
1165 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1166 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1167
1168 assert_eq!(functions.len(), 1);
1169 assert!(functions[0].ops.contains(&WasmOp::I32x4Splat));
1170 }
1171
1172 #[test]
1173 fn test_decode_simd_extract_lane() {
1174 let wat = r#"
1175 (module
1176 (func (export "extract") (param v128) (result i32)
1177 local.get 0
1178 i32x4.extract_lane 2
1179 )
1180 )
1181 "#;
1182
1183 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1184 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1185
1186 assert_eq!(functions.len(), 1);
1187 assert!(
1188 functions[0].ops.contains(&WasmOp::I32x4ExtractLane(2)),
1189 "Should decode i32x4.extract_lane 2"
1190 );
1191 }
1192
1193 #[test]
1194 fn test_decode_simd_f32x4_arithmetic() {
1195 let wat = r#"
1196 (module
1197 (func (export "f32x4_add") (param v128 v128) (result v128)
1198 local.get 0
1199 local.get 1
1200 f32x4.add
1201 )
1202 )
1203 "#;
1204
1205 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1206 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1207
1208 assert_eq!(functions.len(), 1);
1209 assert!(functions[0].ops.contains(&WasmOp::F32x4Add));
1210 }
1211
1212 #[test]
1213 fn test_decode_simd_multiple_ops() {
1214 let wat = r#"
1215 (module
1216 (func (export "simd_ops") (param v128 v128 v128) (result v128)
1217 ;; (a + b) * c
1218 local.get 0
1219 local.get 1
1220 i32x4.add
1221 local.get 2
1222 i32x4.mul
1223 )
1224 )
1225 "#;
1226
1227 let wasm = wat::parse_str(wat).expect("Failed to parse WAT with SIMD");
1228 let functions = decode_wasm_functions(&wasm).expect("Failed to decode");
1229
1230 assert_eq!(functions.len(), 1);
1231 let ops = &functions[0].ops;
1232 assert!(ops.contains(&WasmOp::I32x4Add));
1233 assert!(ops.contains(&WasmOp::I32x4Mul));
1234 }
1235}