1use wasmparser::{BlockType, Operator, Parser, Payload, ValType};
8
9pub struct WasmFunc {
11 pub index: u32,
12 pub name: String,
13 pub params: Vec<ValType>,
14 pub results: Vec<ValType>,
15 pub locals: Vec<ValType>,
16 pub body_offset: usize,
17 pub body_size: usize,
18}
19
20pub fn parse_wasm(data: &[u8]) -> Vec<WasmFunc> {
22 let mut functions = Vec::new();
23 let mut func_types: Vec<(Vec<ValType>, Vec<ValType>)> = Vec::new();
24 let mut func_type_indices: Vec<u32> = Vec::new();
25 let mut import_count: u32 = 0;
26 let mut export_names: std::collections::HashMap<u32, String> = std::collections::HashMap::new();
27 let mut code_idx: u32 = 0;
28
29 let parser = Parser::new(0);
30 for payload in parser.parse_all(data) {
31 let Ok(payload) = payload else { continue };
32 match payload {
33 Payload::TypeSection(reader) => {
34 for ty in reader.into_iter_err_on_gc_types() {
35 if let Ok(ft) = ty {
36 let params: Vec<ValType> = ft.params().to_vec();
37 let results: Vec<ValType> = ft.results().to_vec();
38 func_types.push((params, results));
39 }
40 }
41 }
42 Payload::ImportSection(reader) => {
43 for imp in reader {
44 if let Ok(imp) = imp {
45 if matches!(imp.ty, wasmparser::TypeRef::Func(_)) {
46 import_count += 1;
47 }
48 }
49 }
50 }
51 Payload::FunctionSection(reader) => {
52 for idx in reader {
53 if let Ok(idx) = idx {
54 func_type_indices.push(idx);
55 }
56 }
57 }
58 Payload::ExportSection(reader) => {
59 for exp in reader {
60 if let Ok(exp) = exp {
61 if exp.kind == wasmparser::ExternalKind::Func {
62 export_names.insert(exp.index, exp.name.to_string());
63 }
64 }
65 }
66 }
67 Payload::CodeSectionEntry(body) => {
68 let func_idx = import_count + code_idx;
69 let type_idx = func_type_indices
70 .get(code_idx as usize)
71 .copied()
72 .unwrap_or(0) as usize;
73 let (params, results) = func_types.get(type_idx).cloned().unwrap_or_default();
74
75 let mut locals = Vec::new();
76 if let Ok(local_reader) = body.get_locals_reader() {
77 for local in local_reader {
78 if let Ok((count, ty)) = local {
79 for _ in 0..count {
80 locals.push(ty);
81 }
82 }
83 }
84 }
85
86 let name = export_names
87 .get(&func_idx)
88 .cloned()
89 .unwrap_or_else(|| format!("func_{}", func_idx));
90
91 let range = body.range();
92 functions.push(WasmFunc {
93 index: func_idx,
94 name,
95 params,
96 results,
97 locals,
98 body_offset: range.start,
99 body_size: range.end - range.start,
100 });
101 code_idx += 1;
102 }
103 _ => {}
104 }
105 }
106 functions
107}
108
109fn valtype_to_c(ty: ValType) -> &'static str {
110 match ty {
111 ValType::I32 => "int",
112 ValType::I64 => "long",
113 ValType::F32 => "float",
114 ValType::F64 => "double",
115 _ => "void",
116 }
117}
118
119pub fn decompile_wasm_func(data: &[u8], func: &WasmFunc, all_funcs: &[WasmFunc]) -> String {
121 let mut out = String::new();
122
123 let ret_type = func
125 .results
126 .first()
127 .map(|t| valtype_to_c(*t))
128 .unwrap_or("void");
129 let params_str: Vec<String> = func
130 .params
131 .iter()
132 .enumerate()
133 .map(|(i, t)| format!("{} param_{}", valtype_to_c(*t), i))
134 .collect();
135 out.push_str(&format!("// {}\n", func.name));
136 out.push_str(&format!(
137 "{} {}({}) {{\n",
138 ret_type,
139 func.name,
140 params_str.join(", ")
141 ));
142
143 let param_count = func.params.len();
145 for (i, ty) in func.locals.iter().enumerate() {
146 out.push_str(&format!(" {} local_{};\n", valtype_to_c(*ty), i));
147 }
148 if !func.locals.is_empty() {
149 out.push_str("\n");
150 }
151
152 let body_start = func.body_offset;
154 let body_end = func.body_offset + func.body_size;
155 if body_end > data.len() {
156 out.push_str(" // body out of bounds\n}\n");
157 return out;
158 }
159
160 let parser = Parser::new(body_start as u64);
161 let mut stack: Vec<String> = Vec::new();
162 let mut indent = 1usize;
163
164 for payload in parser.parse_all(&data[..body_end]) {
165 let Ok(payload) = payload else { continue };
166 if let Payload::CodeSectionEntry(body) = payload {
167 let Ok(ops_reader) = body.get_operators_reader() else {
168 continue;
169 };
170 for op in ops_reader {
171 let Ok(op) = op else { continue };
172 let pad = " ".repeat(indent);
173 match op {
174 Operator::LocalGet { local_index } => {
175 let name = if (local_index as usize) < param_count {
176 format!("param_{}", local_index)
177 } else {
178 format!("local_{}", local_index as usize - param_count)
179 };
180 stack.push(name);
181 }
182 Operator::LocalSet { local_index } => {
183 let val = stack.pop().unwrap_or("?".into());
184 let name = if (local_index as usize) < param_count {
185 format!("param_{}", local_index)
186 } else {
187 format!("local_{}", local_index as usize - param_count)
188 };
189 out.push_str(&format!("{}{} = {};\n", pad, name, val));
190 }
191 Operator::LocalTee { local_index } => {
192 let val = stack.last().cloned().unwrap_or("?".into());
193 let name = if (local_index as usize) < param_count {
194 format!("param_{}", local_index)
195 } else {
196 format!("local_{}", local_index as usize - param_count)
197 };
198 out.push_str(&format!("{}{} = {};\n", pad, name, val));
199 }
200 Operator::GlobalGet { global_index } => {
201 stack.push(format!("global_{}", global_index))
202 }
203 Operator::GlobalSet { global_index } => {
204 let val = stack.pop().unwrap_or("?".into());
205 out.push_str(&format!("{}global_{} = {};\n", pad, global_index, val));
206 }
207 Operator::I32Const { value } => stack.push(format!("{}", value)),
208 Operator::I64Const { value } => stack.push(format!("{}L", value)),
209 Operator::F32Const { value } => {
210 stack.push(format!("{}f", f32::from_bits(value.bits())))
211 }
212 Operator::F64Const { value } => {
213 stack.push(format!("{}", f64::from_bits(value.bits())))
214 }
215 Operator::I32Add | Operator::I64Add => binop(&mut stack, "+"),
217 Operator::I32Sub | Operator::I64Sub => binop(&mut stack, "-"),
218 Operator::I32Mul | Operator::I64Mul => binop(&mut stack, "*"),
219 Operator::I32DivS | Operator::I64DivS => binop(&mut stack, "/"),
220 Operator::I32DivU | Operator::I64DivU => binop(&mut stack, "/ (unsigned)"),
221 Operator::I32RemS | Operator::I64RemS => binop(&mut stack, "%"),
222 Operator::I32RemU | Operator::I64RemU => binop(&mut stack, "% (unsigned)"),
223 Operator::I32And | Operator::I64And => binop(&mut stack, "&"),
225 Operator::I32Or | Operator::I64Or => binop(&mut stack, "|"),
226 Operator::I32Xor | Operator::I64Xor => binop(&mut stack, "^"),
227 Operator::I32Shl | Operator::I64Shl => binop(&mut stack, "<<"),
228 Operator::I32ShrS | Operator::I64ShrS => binop(&mut stack, ">>"),
229 Operator::I32ShrU | Operator::I64ShrU => binop(&mut stack, ">>> "),
230 Operator::I32Eqz | Operator::I64Eqz => {
232 let a = stack.pop().unwrap_or("?".into());
233 stack.push(format!("{} == 0", a));
234 }
235 Operator::I32Eq | Operator::I64Eq => binop(&mut stack, "=="),
236 Operator::I32Ne | Operator::I64Ne => binop(&mut stack, "!="),
237 Operator::I32LtS | Operator::I64LtS => binop(&mut stack, "<"),
238 Operator::I32GtS | Operator::I64GtS => binop(&mut stack, ">"),
239 Operator::I32LeS | Operator::I64LeS => binop(&mut stack, "<="),
240 Operator::I32GeS | Operator::I64GeS => binop(&mut stack, ">="),
241 Operator::I32LtU | Operator::I64LtU => binop(&mut stack, "< (unsigned)"),
242 Operator::I32GtU | Operator::I64GtU => binop(&mut stack, "> (unsigned)"),
243 Operator::I32LeU | Operator::I64LeU => binop(&mut stack, "<= (unsigned)"),
244 Operator::I32GeU | Operator::I64GeU => binop(&mut stack, ">= (unsigned)"),
245 Operator::F32Add | Operator::F64Add => binop(&mut stack, "+"),
247 Operator::F32Sub | Operator::F64Sub => binop(&mut stack, "-"),
248 Operator::F32Mul | Operator::F64Mul => binop(&mut stack, "*"),
249 Operator::F32Div | Operator::F64Div => binop(&mut stack, "/"),
250 Operator::I32Load { memarg } => {
252 let addr = stack.pop().unwrap_or("?".into());
253 if memarg.offset > 0 {
254 stack.push(format!("*(int*)({} + {})", addr, memarg.offset));
255 } else {
256 stack.push(format!("*(int*)({})", addr));
257 }
258 }
259 Operator::I64Load { memarg } => {
260 let addr = stack.pop().unwrap_or("?".into());
261 if memarg.offset > 0 {
262 stack.push(format!("*(long*)({} + {})", addr, memarg.offset));
263 } else {
264 stack.push(format!("*(long*)({})", addr));
265 }
266 }
267 Operator::I32Store { memarg } => {
268 let val = stack.pop().unwrap_or("?".into());
269 let addr = stack.pop().unwrap_or("?".into());
270 if memarg.offset > 0 {
271 out.push_str(&format!(
272 "{}*(int*)({} + {}) = {};\n",
273 pad, addr, memarg.offset, val
274 ));
275 } else {
276 out.push_str(&format!("{}*(int*)({}) = {};\n", pad, addr, val));
277 }
278 }
279 Operator::I64Store { memarg } => {
280 let val = stack.pop().unwrap_or("?".into());
281 let addr = stack.pop().unwrap_or("?".into());
282 if memarg.offset > 0 {
283 out.push_str(&format!(
284 "{}*(long*)({} + {}) = {};\n",
285 pad, addr, memarg.offset, val
286 ));
287 } else {
288 out.push_str(&format!("{}*(long*)({}) = {};\n", pad, addr, val));
289 }
290 }
291 Operator::I32Load8S { memarg } | Operator::I32Load8U { memarg } => {
292 let addr = stack.pop().unwrap_or("?".into());
293 let expr = if memarg.offset > 0 {
294 format!("*(byte*)({} + {})", addr, memarg.offset)
295 } else {
296 format!("*(byte*)({})", addr)
297 };
298 stack.push(expr);
299 }
300 Operator::I32Load16S { memarg } | Operator::I32Load16U { memarg } => {
301 let addr = stack.pop().unwrap_or("?".into());
302 let expr = if memarg.offset > 0 {
303 format!("*(short*)({} + {})", addr, memarg.offset)
304 } else {
305 format!("*(short*)({})", addr)
306 };
307 stack.push(expr);
308 }
309 Operator::I32Store8 { memarg } => {
310 let val = stack.pop().unwrap_or("?".into());
311 let addr = stack.pop().unwrap_or("?".into());
312 out.push_str(&format!(
313 "{}*(byte*)({}{}) = {};\n",
314 pad,
315 addr,
316 if memarg.offset > 0 {
317 format!(" + {}", memarg.offset)
318 } else {
319 String::new()
320 },
321 val
322 ));
323 }
324 Operator::I32Store16 { memarg } => {
325 let val = stack.pop().unwrap_or("?".into());
326 let addr = stack.pop().unwrap_or("?".into());
327 out.push_str(&format!(
328 "{}*(short*)({}{}) = {};\n",
329 pad,
330 addr,
331 if memarg.offset > 0 {
332 format!(" + {}", memarg.offset)
333 } else {
334 String::new()
335 },
336 val
337 ));
338 }
339 Operator::Block { blockty } => {
341 out.push_str(&format!("{}{{ // block\n", pad));
342 indent += 1;
343 }
344 Operator::Loop { blockty } => {
345 out.push_str(&format!("{}while (1) {{ // loop\n", pad));
346 indent += 1;
347 }
348 Operator::If { blockty } => {
349 let cond = stack.pop().unwrap_or("?".into());
350 out.push_str(&format!("{}if ({}) {{\n", pad, cond));
351 indent += 1;
352 }
353 Operator::Else => {
354 if indent > 1 {
355 indent -= 1;
356 }
357 let pad = " ".repeat(indent);
358 out.push_str(&format!("{}}} else {{\n", pad));
359 indent += 1;
360 }
361 Operator::End => {
362 if indent > 1 {
363 indent -= 1;
364 }
365 let pad = " ".repeat(indent);
366 if indent >= 1 {
368 out.push_str(&format!("{}}}\n", pad));
369 }
370 }
371 Operator::Br { relative_depth } => {
372 out.push_str(&format!("{}break; // br {}\n", pad, relative_depth));
373 }
374 Operator::BrIf { relative_depth } => {
375 let cond = stack.pop().unwrap_or("?".into());
376 out.push_str(&format!(
377 "{}if ({}) break; // br_if {}\n",
378 pad, cond, relative_depth
379 ));
380 }
381 Operator::Return => {
382 let val = stack.pop();
383 if let Some(v) = val {
384 out.push_str(&format!("{}return {};\n", pad, v));
385 } else {
386 out.push_str(&format!("{}return;\n", pad));
387 }
388 }
389 Operator::Call { function_index } => {
390 let callee = all_funcs
391 .iter()
392 .find(|f| f.index == function_index)
393 .map(|f| f.name.as_str())
394 .unwrap_or("?");
395 let callee_params = all_funcs
397 .iter()
398 .find(|f| f.index == function_index)
399 .map(|f| f.params.len())
400 .unwrap_or(0);
401 let mut args = Vec::new();
402 for _ in 0..callee_params {
403 args.push(stack.pop().unwrap_or("?".into()));
404 }
405 args.reverse();
406 let callee_has_result = all_funcs
407 .iter()
408 .find(|f| f.index == function_index)
409 .map(|f| !f.results.is_empty())
410 .unwrap_or(false);
411 let call_expr = format!("{}({})", callee, args.join(", "));
412 if callee_has_result {
413 stack.push(call_expr);
414 } else {
415 out.push_str(&format!("{}{};\n", pad, call_expr));
416 }
417 }
418 Operator::Drop => {
419 stack.pop();
420 }
421 Operator::Select => {
422 let cond = stack.pop().unwrap_or("?".into());
423 let b = stack.pop().unwrap_or("?".into());
424 let a = stack.pop().unwrap_or("?".into());
425 stack.push(format!("{} ? {} : {}", cond, a, b));
426 }
427 Operator::MemoryGrow { .. } => {
428 let pages = stack.pop().unwrap_or("?".into());
429 stack.push(format!("memory_grow({})", pages));
430 }
431 Operator::MemorySize { .. } => {
432 stack.push("memory_size()".into());
433 }
434 Operator::I32WrapI64 => {
436 let v = stack.pop().unwrap_or("?".into());
437 stack.push(format!("(int){}", v));
438 }
439 Operator::I64ExtendI32S => {
440 let v = stack.pop().unwrap_or("?".into());
441 stack.push(format!("(long){}", v));
442 }
443 Operator::I64ExtendI32U => {
444 let v = stack.pop().unwrap_or("?".into());
445 stack.push(format!("(unsigned long){}", v));
446 }
447 Operator::Unreachable => {
448 out.push_str(&format!("{}__builtin_unreachable();\n", pad));
449 }
450 Operator::Nop => {}
451 _ => {
452 }
455 }
456 }
457 }
458 }
459
460 if !func.results.is_empty() {
462 if let Some(val) = stack.pop() {
463 let pad = " ".repeat(1);
464 out.push_str(&format!("{}return {};\n", pad, val));
465 }
466 }
467
468 out.push_str("}\n");
469 out
470}
471
472fn binop(stack: &mut Vec<String>, op: &str) {
473 let b = stack.pop().unwrap_or("?".into());
474 let a = stack.pop().unwrap_or("?".into());
475 stack.push(format!("{} {} {}", a, op, b));
476}