Decompilers make it possible to have human readyble representations of a compiled WebAssembly module. For instance, having defined functions to compute fibonacci numbers.

import swam._
import text._
import decompilation._
import util.pretty._
import cats.effect._
import java.nio.file.Paths

val tcompiler = Compiler[IO]

val rdecompiler = RawDecompiler[IO]

val tdecompiler = TextDecompiler[IO]

def compdec(p: String): (Doc, Doc) =
  (for {
    tcompiler <- tcompiler
    rdecompiler <- rdecompiler
    tdecompiler <- tdecompiler
    rd <- rdecompiler.decompile(tcompiler.stream(Paths.get(p), true))
    td <- tdecompiler.decompile(tcompiler.stream(Paths.get(p), true))
  } yield (rd, td)).unsafeRunSync()

val (rd, td) = compdec("fibo.wat")

The simple raw decompiler simply prints a text version of the module sections. It can render modules that are not valid.

println(rd.render(0))
// ; section "Types"
// ; type 0
// [i64] → [i64]
// ; type 1
// [i64, i64, i64] → [i64]
// ; section "Imports"
// 
// ; section "Functions"
// ; function type 0
// 0
// ; function type 1
// 1
// ; function type 2
// 0
// ; section "Tables"
// 
// ; section "Memories"
// 
// ; section "Globals"
// 
// ; section "Exports"
// {name naive, func 0}
// {name clever, func 2}
// ; section "Elements"
// 
// ; section "Code"
// ; function body 0
// local.get 0
// i64.const 2
// i64.lt_s
// if [i64]
//   i64.const 1
// else
//   local.get 0
//   i64.const 2
//   i64.sub
//   call 0
//   local.get 0
//   i64.const 1
//   i64.sub
//   call 0
//   i64.add
// end
// ; function body 1
// local.get 2
// i64.const 0
// i64.gt_s
// if [i64]
//   local.get 1
//   local.get 1
//   local.get 0
//   i64.add
//   local.get 2
//   i64.const 1
//   i64.sub
//   call 1
// else
//   local.get 0
// end
// ; function body 2
// i64.const 1
// i64.const 1
// local.get 0
// call 1
// ; section "Data"
// 
// ; custom section "name"
// 00046669626f01170300056e616976650105696e6e65720206636c65766572021b03000401000169010c0300026e3201026e3102016e020401000169

The smart text decompiler renders a formatted text version of the module. The module must be valid to be rendered.

println(td.render(0))
// (module $fibo
//   (export
//     "naive"
//     (func $naive))
//   (export
//     "clever"
//     (func $clever))
//   (func $naive
//     (param $i i64)
//     (result i64)
//     (if (result i64)
//       (i64.lt_s
//         (local.get $i)
//         (i64.const 2))
//     (then
//       i64.const 1)
//     (else
//       (i64.add
//         ((call $naive
//           ((i64.sub
//             (local.get $i)
//             (i64.const 2)))))
//         ((call $naive
//           ((i64.sub
//             (local.get $i)
//             (i64.const 1))))))))
//   (func $inner
//     (param $n2 i64)
//     (param $n1 i64)
//     (param $n i64)
//     (result i64)
//     (if (result i64)
//       (i64.gt_s
//         (local.get $n)
//         (i64.const 0))
//     (then
//       (call $inner
//         ((i64.sub
//           (local.get $n)
//           (i64.const 1)))
//         ((i64.add
//           (local.get $n1)
//           (local.get $n2)))
//         (local.get $n1)))
//     (else
//       local.get $n2))
//   (func $clever
//     (param $i i64)
//     (result i64)
//     (call $inner
//       (local.get $i)
//       (i64.const 1)
//       (i64.const 1))))