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
implicit val cs = IO.contextShift(scala.concurrent.ExecutionContext.global)
def compdec(p: String): (Doc, Doc) =
Blocker[IO].use { blocker =>
for {
tcompiler <- Compiler[IO](blocker)
rdecompiler <-RawDecompiler[IO]
tdecompiler <- TextDecompiler[IO](blocker)
rd <- rdecompiler.decompile(tcompiler.stream(Paths.get(p), true, blocker))
td <- tdecompiler.decompile(tcompiler.stream(Paths.get(p), true, blocker))
} 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]
// ; type 2
// [i64, i64, i64] → [i64]
// ; section "Imports"
//
// ; section "Functions"
// ; function type 0
// 0
// ; function type 1
// 2
// ; 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 @1
// 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 @1
// 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
// (type
// 1
// (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
// (type
// 1
// (i64.gt_s
// (local.get $n)
// (i64.const 0))
// (then
// (call $inner
// (local.get $n1)
// (i64.add
// (local.get $n1)
// (local.get $n2))
// (i64.sub
// (local.get $n)
// (i64.const 1))))
// (else
// local.get $n2)))
// (func $clever
// (param $i i64)
// (result i64)
// (call $inner
// (i64.const 1)
// (i64.const 1)
// (local.get $i))))