+
+local COLOR_SCHEMES = {
+ DARK = {
+ keyword = { 0.7, 0.7, 1.0 };
+ value = { 0.8, 1.0, 0.8 };
+ jumppoint = { 1, 0.5, 0.5 };
+
+ dark_background = { 0.1, 0.1, 0.1 };
+ background = { 0.15, 0.15, 0.15 };
+ code = { 0.9, 0.9, 0.9 };
+
+ primary = { (0x0d / 0xff), (0x47 / 0xff), (0xa1 / 0xff) };
+ primary_light = { (0x54 / 0xff), (0x72 / 0xff), (0xd3 / 0xff) };
+ primary_dark = { (0x00 / 0xff), (0x21 / 0xff), (0x71 / 0xff) };
+ secondary = { (0x00 / 0xff), (0x60 / 0xff), (0x64 / 0xff) };
+ secondary_light = { (0x42 / 0xff), (0x8e / 0xff), (0x92 / 0xff) };
+ secondary_dark = { (0x00 / 0xff), (0x36 / 0xff), (0x3a / 0xff) };
+ primary_text = { 1, 1, 1 };
+ secondary_text = { 1, 1, 1 };
+ };
+
+ LIGHT = {
+ keyword = { 0, 0, 1 };
+ value = { 0.0, 0.5, 0.0 };
+ jumppoint = { 1, 0, 0 };
+
+ dark_background = { 0.8, 0.8, 0.8 };
+ background = { 1, 1, 1 };
+ code = { 0.0, 0.0, 0.0 };
+
+ primary = { (0x90 / 0xff), (0xca / 0xff), (0xf9 / 0xff) };
+ primary_light = { (0xc3 / 0xff), (0xfd / 0xff), (0xff / 0xff) };
+ primary_dark = { (0x5d / 0xff), (0x99 / 0xff), (0xc6 / 0xff) };
+ secondary = { (0x80 / 0xff), (0xde / 0xff), (0xea / 0xff) };
+ secondary_light = { (0xb4 / 0xff), (0xff / 0xff), (0xff / 0xff) };
+ secondary_dark = { (0x4b / 0xff), (0xba / 0xff), (0xb8 / 0xff) };
+ primary_text = { 0, 0, 0 };
+ secondary_text = { 0, 0, 0 };
+ }
+}
+
function love.conf(t)
t.window.title = "TBD"
t.window.width = 1200
t.window.height = 900
t.window.resizable = false
end
+
+return {
+ COLOR_SCHEMES = COLOR_SCHEMES;
+
+ COLOR_SCHEME = COLOR_SCHEMES.LIGHT;
+}
+++ /dev/null
--- Recursive decent parser for the WASM v1.0 binary format
--- Brendan Hansen 2020
-
--- Look in clib folder for C-based libraries
-package.cpath = package.cpath .. [[;./clib/?.so]]
-
-import {
- parse_helper = "parse_helper";
- pprint = "lualib.pprint";
-
- build_str = "utils:build_str";
- random_str = "utils:random_str";
- Stack = "utils:Stack";
-}
-
-function parse_valtype(r)
- local val = r:read_byte()
-
- local valtypes_map = {
- [0x7f] = "i32";
- [0x7E] = "i64";
- [0x7D] = "f32";
- [0x7C] = "f64";
- }
-
- return valtypes_map[val]
-end
-
-function parse_vector(tp)
- return function(r)
- local n = r:read_uint(32)
-
- local v = {}
- for i=1, n do
- table.insert(v, tp(r))
- end
-
- return v
- end
-end
-
-function parse_byte(r)
- return r:read_byte()
-end
-
-function parse_name(r)
- local name = parse_vector(parse_byte)(r)
- return name
-end
-
-function parse_blocktype(r)
- if r:peek_byte() == 0x40 then
- r:read_byte()
- return {}
- else
- return { parse_valtype(r) }
- end
-end
-
-function parse_functype(r)
- assert(r:read_byte() == 0x60, "functype expected 0x60 as first byte")
-
- local params = parse_vector(parse_valtype)(r)
- local results = parse_vector(parse_valtype)(r)
-
- return { param_types = params, result_types = results }
-end
-
-function parse_limits(r)
- local t = r:read_byte()
-
- local min = r:read_uint(32)
- local max = nil
- if t == 0x01 then
- max = r:read_uint(32)
- end
-
- return { min = min, max = max }
-end
-
-function parse_memtype(r)
- local lims = parse_limits(r)
-
- return lims
-end
-
-function parse_tabletype(r)
- local elemtype = parse_elemtype(r)
- local limits = parse_limits(r)
-
- return { lim = limits, et = elemtype }
-end
-
-function parse_elemtype(r)
- assert(r:read_byte() == 0x70, "elemtype should be 0x70")
- return "funcref"
-end
-
-function parse_globaltype(r)
- local valtype = parse_valtype(r)
- local ismut = parse_mut(r) == 0x01
-
- return { t = valtype, m = ismut }
-end
-
-function parse_mut(r)
- local v = r:read_byte()
- assert(v == 0x00 or v == 0x01, "mut should be 0x00 or 0x01")
- return v
-end
-
-function parse_instr(r)
- local instr = r:read_byte()
-
- return match(instr) {
- [0x00] = function() return { "unreachable" } end;
- [0x01] = function() return { "nop" } end;
- [0x02] = function()
- local rt = parse_blocktype(r)
- local instrs = {}
- local name = random_str(8)
-
- local block = { "block", label = name, rt = rt }
- r.label_stack:push(block)
-
- while true do
- if r:peek_byte() == 0x0B then
- r:read_byte()
- break
- else
- table.insert(instrs, parse_instr(r))
- end
- end
-
- r.label_stack:pop()
-
- block.instrs = instrs
- return block
- end;
- [0x03] = function()
- local rt = parse_blocktype(r)
- local instrs = {}
- local name = random_str(8)
-
- local block = { "loop", label = name, rt = rt }
- r.label_stack:push(block)
-
- while true do
- if r:peek_byte() == 0x0B then
- r:read_byte()
- break
- else
- table.insert(instrs, parse_instr(r))
- end
- end
-
- r.label_stack:pop()
-
- block.instrs = instrs
- return block
- end;
- [0x04] = function()
- local rt = parse_blocktype(r)
- local instrs = {}
- local else_instrs = {}
- local inelse = false
-
- while true do
- local peek = r:peek_byte()
- if peek == 0x0B then
- r:read_byte()
- break
- elseif peek == 0x05 then
- r:read_byte()
- inelse = true
- else
- if not inelse then
- table.insert(instrs, parse_instr(r))
- else
- table.insert(else_instrs, parse_instr(r))
- end
- end
- end
-
- return { "if", rt = rt, instrs = instrs, else_instrs = else_instrs }
- end;
-
- [0x0C] = function() return { "br", x = parse_labelidx(r) } end;
- [0x0D] = function() return { "br_if", x = parse_labelidx(r) } end;
- [0x0E] = function()
- local labels = parse_vector(parse_labelidx)(r)
- local labeln = parse_labelidx(r)
-
- return { "br_table", labels = labels, labeln = labeln }
- end;
-
- [0x0F] = function() return { "return" } end;
-
- [0x10] = function() return { "call", x = parse_funcidx(r) } end;
- [0x11] = function()
- local x = parse_typeidx(r)
- assert(r:read_byte() ~= 0x00, "call_indirect expects 0x00 at end")
- return { "call_indirect", x = x }
- end;
-
- [0x1A] = function() return { "drop" } end;
- [0x1B] = function() return { "select" } end;
-
- [0x20] = function() return { "local.get", x = parse_localidx(r) } end;
- [0x21] = function() return { "local.set", x = parse_localidx(r) } end;
- [0x22] = function() return { "local.tee", x = parse_localidx(r) } end;
- [0x23] = function() return { "global.get", x = parse_globalidx(r) } end;
- [0x24] = function() return { "global.set", x = parse_globalidx(r) } end;
-
- [0x28] = function() return { "i32.load", x = parse_memarg(r) } end;
- [0x29] = function() return { "i64.load", x = parse_memarg(r) } end;
- [0x2A] = function() return { "f32.load", x = parse_memarg(r) } end;
- [0x2B] = function() return { "f64.load", x = parse_memarg(r) } end;
- [0x2C] = function() return { "i32.load8_s", x = parse_memarg(r) } end;
- [0x2D] = function() return { "i32.load8_u", x = parse_memarg(r) } end;
- [0x2E] = function() return { "i32.load16_s", x = parse_memarg(r) } end;
- [0x2F] = function() return { "i32.load16_u", x = parse_memarg(r) } end;
- [0x30] = function() return { "i64.load8_s", x = parse_memarg(r) } end;
- [0x31] = function() return { "i64.load8_u", x = parse_memarg(r) } end;
- [0x32] = function() return { "i64.load16_s", x = parse_memarg(r) } end;
- [0x33] = function() return { "i64.load16_u", x = parse_memarg(r) } end;
- [0x34] = function() return { "i64.load32_s", x = parse_memarg(r) } end;
- [0x35] = function() return { "i64.load32_u", x = parse_memarg(r) } end;
- [0x36] = function() return { "i32.store", x = parse_memarg(r) } end;
- [0x37] = function() return { "i64.store", x = parse_memarg(r) } end;
- [0x38] = function() return { "f32.store", x = parse_memarg(r) } end;
- [0x39] = function() return { "f64.store", x = parse_memarg(r) } end;
- [0x3A] = function() return { "i32.store8", x = parse_memarg(r) } end;
- [0x3B] = function() return { "i32.store16", x = parse_memarg(r) } end;
- [0x3C] = function() return { "i64.store8", x = parse_memarg(r) } end;
- [0x3D] = function() return { "i64.store16", x = parse_memarg(r) } end;
- [0x3E] = function() return { "i64.store32", x = parse_memarg(r) } end;
- [0x3F] = function()
- assert(r:read_byte() == 0x00, "memory.size expects 0x00")
- return { "memory.size" }
- end;
- [0x40] = function()
- assert(r:read_byte() == 0x00, "memory.grow expects 0x00")
- return { "memory.grow" }
- end;
-
- [0x41] = function() return { "i32.const", x = r:read_sint(32) } end;
- [0x42] = function() return { "i64.const", x = r:read_sint(64) } end;
- [0x43] = function() return { "f32.const", x = r:read_float(32) } end;
- [0x44] = function() return { "f64.const", x = r:read_float(64) } end;
-
- [0x45] = function() return { "i32.eqz" } end;
- [0x46] = function() return { "i32.eq" } end;
- [0x47] = function() return { "i32.ne" } end;
- [0x48] = function() return { "i32.lt_s" } end;
- [0x49] = function() return { "i32.lt_u" } end;
- [0x4A] = function() return { "i32.gt_s" } end;
- [0x4B] = function() return { "i32.gt_u" } end;
- [0x4C] = function() return { "i32.le_s" } end;
- [0x4D] = function() return { "i32.le_u" } end;
- [0x4E] = function() return { "i32.ge_s" } end;
- [0x4F] = function() return { "i32.ge_u" } end;
- [0x50] = function() return { "i64.eqz" } end;
- [0x51] = function() return { "i64.eq" } end;
- [0x52] = function() return { "i64.ne" } end;
- [0x53] = function() return { "i64.lt_s" } end;
- [0x54] = function() return { "i64.lt_u" } end;
- [0x55] = function() return { "i64.gt_s" } end;
- [0x56] = function() return { "i64.gt_u" } end;
- [0x57] = function() return { "i64.le_s" } end;
- [0x58] = function() return { "i64.le_u" } end;
- [0x59] = function() return { "i64.ge_s" } end;
- [0x5A] = function() return { "i64.ge_u" } end;
- [0x5B] = function() return { "f32.eq" } end;
- [0x5C] = function() return { "f32.ne" } end;
- [0x5D] = function() return { "f32.lt" } end;
- [0x5E] = function() return { "f32.gt" } end;
- [0x5F] = function() return { "f32.le" } end;
- [0x60] = function() return { "f32.ge" } end;
- [0x61] = function() return { "f64.eq" } end;
- [0x62] = function() return { "f64.ne" } end;
- [0x63] = function() return { "f64.lt" } end;
- [0x64] = function() return { "f64.gt" } end;
- [0x65] = function() return { "f64.le" } end;
- [0x66] = function() return { "f64.ge" } end;
- [0x67] = function() return { "i32.clz" } end;
- [0x68] = function() return { "i32.ctz" } end;
- [0x69] = function() return { "i32.popcnt" } end;
- [0x6A] = function() return { "i32.add" } end;
- [0x6B] = function() return { "i32.sub" } end;
- [0x6C] = function() return { "i32.mul" } end;
- [0x6D] = function() return { "i32.div_s" } end;
- [0x6E] = function() return { "i32.div_u" } end;
- [0x6F] = function() return { "i32.rem_s" } end;
- [0x70] = function() return { "i32.rem_u" } end;
- [0x71] = function() return { "i32.and" } end;
- [0x72] = function() return { "i32.or" } end;
- [0x73] = function() return { "i32.xor" } end;
- [0x74] = function() return { "i32.shl" } end;
- [0x75] = function() return { "i32.shr_s" } end;
- [0x76] = function() return { "i32.shr_u" } end;
- [0x77] = function() return { "i32.rotl" } end;
- [0x78] = function() return { "i32.rotr" } end;
- [0x79] = function() return { "i64.clz" } end;
- [0x7A] = function() return { "i64.ctz" } end;
- [0x7B] = function() return { "i64.popcnt" } end;
- [0x7C] = function() return { "i64.add" } end;
- [0x7D] = function() return { "i64.sub" } end;
- [0x7E] = function() return { "i64.mul" } end;
- [0x7F] = function() return { "i64.div_s" } end;
- [0x80] = function() return { "i64.div_u" } end;
- [0x81] = function() return { "i64.rem_s" } end;
- [0x82] = function() return { "i64.rem_u" } end;
- [0x83] = function() return { "i64.and" } end;
- [0x84] = function() return { "i64.or" } end;
- [0x85] = function() return { "i64.xor" } end;
- [0x86] = function() return { "i64.shl" } end;
- [0x87] = function() return { "i64.shr_s" } end;
- [0x88] = function() return { "i64.shr_u" } end;
- [0x89] = function() return { "i64.rotl" } end;
- [0x8A] = function() return { "i64.rotr" } end;
- [0x8B] = function() return { "f32.abs" } end;
- [0x8C] = function() return { "f32.neg" } end;
- [0x8D] = function() return { "f32.ceil" } end;
- [0x8E] = function() return { "f32.floor" } end;
- [0x8F] = function() return { "f32.trunc" } end;
- [0x90] = function() return { "f32.nearest" } end;
- [0x91] = function() return { "f32.sqrt" } end;
- [0x92] = function() return { "f32.add" } end;
- [0x93] = function() return { "f32.sub" } end;
- [0x94] = function() return { "f32.mul" } end;
- [0x95] = function() return { "f32.div" } end;
- [0x96] = function() return { "f32.min" } end;
- [0x97] = function() return { "f32.max" } end;
- [0x98] = function() return { "f32.copysign" } end;
- [0x99] = function() return { "f64.abs" } end;
- [0x9A] = function() return { "f64.neg" } end;
- [0x9B] = function() return { "f64.ceil" } end;
- [0x9C] = function() return { "f64.floor" } end;
- [0x9D] = function() return { "f64.trunc" } end;
- [0x9E] = function() return { "f64.nearest" } end;
- [0x9F] = function() return { "f64.sqrt" } end;
- [0xA0] = function() return { "f64.add" } end;
- [0xA1] = function() return { "f64.sub" } end;
- [0xA2] = function() return { "f64.mul" } end;
- [0xA3] = function() return { "f64.div" } end;
- [0xA4] = function() return { "f64.min" } end;
- [0xA5] = function() return { "f64.max" } end;
- [0xA6] = function() return { "f64.copysign" } end;
-
- [0xA7] = function() return { "i32.wrap_i64" } end;
- [0xA8] = function() return { "i32.trunc_f32_s" } end;
- [0xA9] = function() return { "i32.trunc_f32_u" } end;
- [0xAA] = function() return { "i32.trunc_f64_s" } end;
- [0xAB] = function() return { "i32.trunc_f64_u" } end;
- [0xAC] = function() return { "i64.extend_i32_s" } end;
- [0xAD] = function() return { "i64.extend_i32_u" } end;
- [0xAE] = function() return { "i64.trunc_f32_s" } end;
- [0xAF] = function() return { "i64.trunc_f32_u" } end;
- [0xB0] = function() return { "i64.trunc_f64_s" } end;
- [0xB1] = function() return { "i64.trunc_f64_u" } end;
- [0xB2] = function() return { "f32.convert_i32_s" } end;
- [0xB3] = function() return { "f32.convert_i32_u" } end;
- [0xB4] = function() return { "f32.convert_i64_s" } end;
- [0xB5] = function() return { "f32.convert_i64_u" } end;
- [0xB6] = function() return { "f32.demote_f64" } end;
- [0xB7] = function() return { "f64.convert_i32_s" } end;
- [0xB8] = function() return { "f64.convert_i32_u" } end;
- [0xB9] = function() return { "f64.convert_i64_s" } end;
- [0xBA] = function() return { "f64.convert_i64_u" } end;
- [0xBB] = function() return { "f64.promote_f32" } end;
- [0xBC] = function() return { "i32.reinterpret_f32" } end;
- [0xBD] = function() return { "i64.reinterpret_f64" } end;
- [0xBE] = function() return { "f32.reinterpret_i32" } end;
- [0xBF] = function() return { "f64.reinterpret_i64" } end;
- }
-end
-
-function parse_memarg(r)
- local a = r:read_uint(32)
- local o = r:read_uint(32)
-
- return { "memarg"; align = a; offset = o }
-end
-
-function parse_expr(r)
- local instrs = {}
- while true do
- if r:peek_byte() == 0x0B then
- r:read_byte()
- break
- else
- table.insert(instrs, parse_instr(r))
- end
- end
-
- return instrs
-end
-
-function parse_typeidx(r) return r:read_uint(32) end
-function parse_funcidx(r) return r:read_uint(32) end
-function parse_tableidx(r) return r:read_uint(32) end
-function parse_memidx(r) return r:read_uint(32) end
-function parse_globalidx(r) return r:read_uint(32) end
-function parse_localidx(r) return r:read_uint(32) end
-function parse_labelidx(r)
- local idx = r:read_uint(32)
- local block = r.label_stack:at(idx)
- return { "labelidx", labelidx = idx, block = block }
-end
-
-function parse_section(r, expectN, B)
- if r:peek_byte() ~= expectN then return end
-
- local N = r:read_byte()
- local size = r:read_uint(32)
- local cont = B(r)
-
- return { "section", contents = cont, size = size }
-end
-
-function parse_customsec(r)
- local csect = parse_section(r, 0, parse_custom)
- if not csect then return nil end
-
- for i=1, csect.size do
- -- Discard csect.size bytes
- r:read_byte()
- end
- return csect
-end
-
-function parse_custom(r)
- local name = parse_name(r)
-
- return { "custom", name = name }
-end
-
-function parse_typesec(r)
- return parse_section(r, 1, parse_vector(parse_functype))
-end
-
-function parse_importsec(r)
- return parse_section(r, 2, parse_vector(parse_import))
-end
-
-function parse_import(r)
- local mod = parse_name(r)
- local nm = parse_name(r)
- local d = parse_importdesc(r)
-
- return { "import", mod = mod, name = nm, desc = d }
-end
-
-function parse_importdesc(r)
- local t = r:read_byte()
- if t == 0x00 then return { "func", x = parse_typeidx(r) }
- elseif t == 0x01 then return { "table", tt = parse_tabletype(r) }
- elseif t == 0x02 then return { "mem", mt = parse_memtype(r) }
- elseif t == 0x03 then return { "global", gt = parse_globaltype(r) }
- end
-
- error("bad importdesc")
-end
-
-function parse_funcsec(r)
- return parse_section(r, 3, parse_vector(parse_typeidx))
-end
-
-function parse_tablesec(r)
- return parse_section(r, 4, parse_vector(parse_table))
-end
-
-function parse_table(r)
- local tt = parse_tabletype(r)
- return { "table", type_ = tt }
-end
-
-function parse_memsec(r)
- return parse_section(r, 5, parse_vector(parse_mem))
-end
-
-function parse_mem(r)
- local mt = parse_memtype(r)
- return { "mem", type_ = mt }
-end
-
-function parse_globalsec(r)
- return parse_section(r, 6, parse_vector(parse_global))
-end
-
-function parse_global(r)
- local gt = parse_globaltype(r)
- local e = parse_expr(r)
- return { "global", type_ = gt, init = e }
-end
-
-function parse_exportsec(r)
- return parse_section(r, 7, parse_vector(parse_export))
-end
-
-function parse_export(r)
- local nm = parse_name(r)
- local d = parse_exportdesc(r)
- return { "export", name = nm, desc = d }
-end
-
-function parse_exportdesc(r)
- local t = r:read_byte()
- if t == 0x00 then return { "func", x = parse_typeidx(r) }
- elseif t == 0x01 then return { "table", tt = parse_tableidx(r) }
- elseif t == 0x02 then return { "mem", mt = parse_memidx(r) }
- elseif t == 0x03 then return { "global", gt = parse_globalidx(r) }
- end
-
- error("bad exportdesc: ", t)
-end
-
-function parse_startsec(r)
- return parse_section(r, 8, parse_start)
-end
-
-function parse_start(r)
- local x = parse_funcidx(r)
- return { "start", func = x }
-end
-
-function parse_elemsec(r)
- return parse_section(r, 9, parse_vector(parse_elem))
-end
-
-function parse_elem(r)
- local x = parse_tableidx(r)
- local e = parse_expr(r)
- local y = parse_vector(parse_funcidx)(r)
-
- return { "elem", table = x, offset = e, init = y }
-end
-
-function parse_codesec(r)
- return parse_section(r, 10, parse_vector(parse_code))
-end
-
-function parse_code(r)
- local size = r:read_uint(32)
- local code = parse_func(r)
- return code
-end
-
-function parse_func(r)
- local t = parse_vector(parse_locals)(r)
- local e = parse_expr(r)
-
- local localidx = 0
- local locals = {}
- for _, v in ipairs(t) do
- for _, l in ipairs(v) do
- l.localidx = localidx
- table.insert(locals, l)
- localidx = localidx + 1
- end
- end
-
- return { "func", locals = locals, body = e }
-end
-
-function parse_locals(r)
- local n = r:read_uint(32)
- local t = parse_valtype(r)
-
- --TODO: Make a list of values with names like local0, local1, ...
-
- local locals = {}
- for i = 0, n - 1 do
- table.insert(locals, {
- name = "local" .. i,
- type_ = t,
- localidx = i
- })
- end
-
- return locals
-end
-
-function parse_datasec(r)
- return parse_section(r, 11, parse_vector(parse_data))
-end
-
-function parse_data(r)
- local x = parse_memidx(r)
- local e = parse_expr(r)
- local b = parse_vector(parse_byte)(r)
-
- return { "data", data = x, offset = e, init = b }
-end
-
-function parse_magic(r)
- assert(r:read_byte() == 0x00, "magic string is wrong")
- assert(r:read_byte() == 0x61, "magic string is wrong")
- assert(r:read_byte() == 0x73, "magic string is wrong")
- assert(r:read_byte() == 0x6D, "magic string is wrong")
-end
-
-function parse_version(r)
- assert(r:read_byte() == 0x01, "version is wrong")
- assert(r:read_byte() == 0x00, "version is wrong")
- assert(r:read_byte() == 0x00, "version is wrong")
- assert(r:read_byte() == 0x00, "version is wrong")
-end
-
-function parse_module(r)
- parse_magic(r)
- parse_version(r)
-
- local functypes = parse_typesec(r)
- local imports = parse_importsec(r)
- local typeidxs = parse_funcsec(r)
- local tables = parse_tablesec(r)
- local mems = parse_memsec(r)
- local globals = parse_globalsec(r)
- local exports = parse_exportsec(r)
- local start = parse_startsec(r)
- local elems = parse_elemsec(r)
- local codes = parse_codesec(r)
- local data = parse_datasec(r)
-
- local funcs = {}
- local funcidx = 0
-
- if imports then
- for k, v in ipairs(imports.contents) do
- if v.desc[1] == "func" then
- funcs[funcidx] = {
- name = build_str(v.mod) .. "." .. build_str(v.name);
- funcidx = funcidx;
- type_ = functypes.contents[v.desc.x + 1];
- }
- funcidx = funcidx + 1
- end
- end
- end
-
- if codes then
- for i=1, #codes.contents do
- local locals = codes.contents[i].locals;
- local type_ = functypes.contents[typeidxs.contents[i] + 1];
- local param_types = type_.param_types;
-
- local new_locals = {}
- local new_local_idx = 0
- for _, p in ipairs(param_types) do
- table.insert(new_locals, {
- name = "param" .. new_local_idx,
- type_ = p;
- localidx = new_local_idx
- })
- new_local_idx = new_local_idx + 1
- end
- for _, l in ipairs(locals) do
- l.localidx = new_local_idx
- table.insert(new_locals, l)
- new_local_idx = new_local_idx + 1
- end
-
- funcs[funcidx] = {
- funcidx = funcidx;
- name = "func" .. funcidx;
- type_ = functypes.contents[typeidxs.contents[i] + 1];
- locals = new_locals;
- body = codes.contents[i].body
- }
- funcidx = funcidx + 1
- end
- end
-
- return {
- "module";
- types = functypes;
- tables = tables;
- mems = mems;
- globals = globas;
- exports = exports;
- start = start;
- elems = elems;
- data = data;
- imports = imports;
- funcs = funcs;
- }
-end
-
-
--- Reader util class used for interfacing
--- with the parse_helper c library
-class "Parser" {
- init = function(self, filename)
- self.wasm_file = parse_helper.open_file(filename)
- self.label_stack = Stack()
- end;
-
- read_byte = function(self)
- return parse_helper.read_byte(self.wasm_file)
- end;
-
- peek_byte = function(self)
- return parse_helper.peek_byte(self.wasm_file)
- end;
-
- -- NOTE: N is unused
- read_uint = function(self, N)
- return parse_helper.parse_uleb128(self.wasm_file)
- end;
-
- read_sint = function(self, N)
- return parse_helper.parse_sleb128(self.wasm_file, N)
- end;
-
- read_float = function(self, N)
- if N >= 32 then
- self:read_byte()
- self:read_byte()
- self:read_byte()
- self:read_byte()
- end
- if N >= 64 then
- self:read_byte()
- self:read_byte()
- self:read_byte()
- self:read_byte()
- end
- return 0.0;
- end;
-}
-
-function decompile(filepath)
- local reader = Parser(filepath)
-
- return parse_module(reader)
-end
-
-return module { decompile }
-After experimenting with using Lua for the parser, I think it will be better to write the parser in C / C++
-and then have a function that converts all the data to a lua table that will be used for visualization / examination.
+[X] Rewrite function text builder into a more abstract system
+[ ] Implement arrows from call sites to other functions
+[ ] Implement internal arrows from "br" to destination
+[ ] Implement right click context menu
+ * Own region dismissed on mouse leave
+ * Starts centered on mouse
require "lualib.oop"
import {
- decompile = "decompile:";
-
- Ui = "ui:Ui";
- UiRegion = "ui:UiRegion";
- ScrollingUiRegion = "ui:ScrollingUiRegion";
- DraggableRect = "ui:DraggableRect";
-
- render_text = "ui:render_text";
-
- scissor_points = "utils:scissor_points";
pprint = "lualib.pprint";
-}
-
-TEXT_COLORS = {
- keyword = { 0.7, 0.7, 1.0 };
- value = { 0.8, 1.0, 0.8 };
- text = { 0.9, 0.9, 0.9 };
- jumppoint = { 1, 0.5, 0.5 };
-
- header_background = { 0.1, 0.1, 0.1 };
- background = { 0.15, 0.15, 0.15 };
-}
-function build_func_header_text(func, mod)
- local text = {}
+ decompile = "src.wasm.decompile:";
+ WasmCodeGenerator = "src.wasm.text:";
- table.insert(text, { text = "[" .. func.funcidx .. "] ", color = TEXT_COLORS.text })
- table.insert(text, { text = func.name, color = TEXT_COLORS.keyword })
- table.insert(text, { text = ": ", color = TEXT_COLORS.text })
-
- local type_ = func.type_
- for _, t in ipairs(type_.param_types) do
- table.insert(text, { text = t .. " " })
- end
-
- if #type_.param_types == 0 then
- table.insert(text, { text = "void " })
- end
-
- table.insert(text, { text = "-> " })
-
- for _, t in ipairs(type_.result_types) do
- table.insert(text, { text = t .. " " })
- end
+ Ui = "src.ui.ui:Ui";
+ UiRegion = "src.ui.ui:UiRegion";
+ ScrollingUiRegion = "src.ui.ui:ScrollingUiRegion";
+ DraggableRect = "src.ui.ui:DraggableRect";
+ render_text = "src.ui.ui:render_text";
- if #type_.result_types == 0 then
- table.insert(text, { text = "void" })
- end
-
- table.insert(text, { newline = true })
-
- return text
-end
-
-local line = 1
-
-function add_line_number(textlist, newline)
- if newline == nil then newline = true end
- table.insert(textlist, { newline = newline })
- table.insert(textlist, { text = ("%4d"):format(line), color = TEXT_COLORS.text, background = TEXT_COLORS.header_background, post_indent = true })
- table.insert(textlist, { text = " ", post_indent = true })
- line = line + 1
-end
-
-function instr_to_text(textlist, instr, func, mod)
- if instr[1] == "block" then
- for _, ins in ipairs(instr.instrs) do
- instr_to_text(textlist, ins, func, mod)
- add_line_number(textlist)
- end
- table.insert(textlist, { text = "label ", color = TEXT_COLORS.jumppoint })
- table.insert(textlist, { text = instr.label, color = TEXT_COLORS.value })
-
-
- elseif instr[1] == "loop" then
- table.insert(textlist, { text = "loop ", color = TEXT_COLORS.jumppoint })
- table.insert(textlist, { text = instr.label, color = TEXT_COLORS.value, change_indent = 1 })
-
- for _, ins in ipairs(instr.instrs) do
- add_line_number(textlist)
- instr_to_text(textlist, ins, func, mod)
- end
- table.insert(textlist, { change_indent = -1 })
+ scissor_points = "src.utils:scissor_points";
- elseif instr[1] == "if" then
- table.insert(textlist, { text = "if", color = TEXT_COLORS.jumppoint, change_indent = 1 })
- for _, ins in ipairs(instr.instrs) do
- add_line_number(textlist)
- instr_to_text(textlist, ins, func, mod)
- end
- table.insert(textlist, { change_indent = -1 })
-
- if #instr.else_instrs > 0 then
- add_line_number(textlist)
- table.insert(textlist, { text = "else", color = TEXT_COLORS.jumppoint, change_indent = 1 })
- for _, ins in ipairs(instr.else_instrs) do
- add_line_number(textlist)
- instr_to_text(textlist, ins, func, mod)
- end
- table.insert(textlist, { change_indent = -1 })
- end
-
- elseif instr[1] == "call" then
- table.insert(textlist, { text = instr[1] .. " ", color = TEXT_COLORS.keyword })
- table.insert(textlist, { text = mod.funcs[instr.x].name, color = TEXT_COLORS.value })
-
- elseif instr[1]:match("local") then
- table.insert(textlist, { text = instr[1] .. " ", color = TEXT_COLORS.keyword })
- table.insert(textlist, { text = func.locals[instr.x + 1].name, color = TEXT_COLORS.value })
-
- else
- table.insert(textlist, { text = instr[1] .. " ", color = TEXT_COLORS.keyword })
- if instr.x then
- if type(instr.x) == "table" then
- if instr.x[1] == "memarg" then
- table.insert(textlist, {
- text = ("align=0x%02x offset=0x%04x"):format(instr.x.align, instr.x.offset);
- color = TEXT_COLORS.value
- })
- end
-
- if instr.x[1] == "labelidx" then
- table.insert(textlist, { text = instr.x.block.label .. " ", color = TEXT_COLORS.value })
- table.insert(textlist, { text = ("[0x%02x]"):format(instr.x.labelidx), color = TEXT_COLORS.text })
- end
- else
- table.insert(textlist, { text = tostring(instr.x), color = TEXT_COLORS.value })
- end
- end
- end
-
- return line
-end
-
-function build_func_body_text(func, mod)
- local text = {}
- line = 1
-
- table.insert(text, { text = " Locals:", color = TEXT_COLORS.keyword, newline = true, change_indent = 2 })
- for _, loc in ipairs(func.locals) do
- table.insert(text, { text = loc.type_ .. " " .. loc.name, color = TEXT_COLORS.text , newline = true })
- end
-
- if #func.locals == 0 then
- table.insert(text, { text = "no locals", color = TEXT_COLORS.text, newline = true })
- end
-
- table.insert(text, { newline = true, change_indent = -2 })
- table.insert(text, { text = " Body:", color = TEXT_COLORS.keyword })
-
- for _, instr in ipairs(func.body) do
- add_line_number(text)
- instr_to_text(text, instr, func, mod)
- end
-
- return text
-end
+ COLORS = "conf:COLOR_SCHEME";
+}
class "FunctionBlock" [DraggableRect] {
init = function(self, func, x, y)
- DraggableRect.init(self, x, y, 400, 400)
+ DraggableRect.init(self, x, y, 400, 22)
- self.header_text = build_func_header_text(func, mod)
+ WasmCodeGenerator:setup(func, mod, COLORS)
+ self.header_text = WasmCodeGenerator:build_header()
if func.body then
- self.body_text = build_func_body_text(func, mod)
+ self.body_text = WasmCodeGenerator:build_body()
else
- self.rect.h = 48
self.body_text = {
- { text = "imported function", color = TEXT_COLORS.text }
+ { text = "imported function", color = COLORS.code }
}
end
+
self.scroll = 0
+ self.body_visible = false
+ self.old_height = 400
+ end;
+
+ onclick = function(self, button, x, y)
+ DraggableRect.onclick(self, button, x, y)
+
+ if y <= 24 and button == 2 then
+ self.body_visible = not self.body_visible
+ self.rect.h = self.body_visible and self.old_height or 22
+ end
end;
onwheel = function(self, _, dy)
if y >= 24 and button == 2 then
self.rect.w = math.max(100, x)
- self.rect.h = math.max(22, y)
+ if self.body_visible then
+ self.rect.h = math.max(22, y)
+ self.old_height = self.rect.h
+ end
end
end;
draw = function(self)
love.graphics.setColor(0, 0, 0)
love.graphics.rectangle("fill", 0, 0, self.rect.w, self.rect.h)
- love.graphics.setColor(self.selected and TEXT_COLORS.background or TEXT_COLORS.header_background)
+ love.graphics.setColor(self.selected and COLORS.background or COLORS.dark_background)
love.graphics.rectangle("fill", 2, 2, self.rect.w - 4, self.rect.h - 4)
- love.graphics.setColor(TEXT_COLORS.header_background)
+ love.graphics.setColor(COLORS.dark_background)
love.graphics.rectangle("fill", 2, 2, self.rect.w - 4, 18)
love.graphics.intersectScissor(scissor_points(0, 0, self.rect.w, self.rect.h))
local x, y = render_text(2, 2, self.header_text)
- if self.body_text then
+ if self.body_visible and self.body_text then
love.graphics.intersectScissor(scissor_points(2, y - 1, self.rect.w - 4, self.rect.h - 22))
render_text(2, y + 4 - self.scroll, self.body_text)
end
ui_region.rect.y = 0
ui_region.rect.w = 1200
ui_region.rect.h = 900
- ui_region.background_color = { 0.1, 0.1, 0.1 }
- ui_region.line_color = { 0.15, 0.15, 0.2 }
+ ui_region.background_color = COLORS.background
+ ui_region.line_color = COLORS.dark_background
local x = 0
local y = 0
for _, func in pairs(mod.funcs) do
local block = FunctionBlock(func, x, y)
- block.order = 600 - y
+ block.order = #mod.funcs * 20 + 1 - y
ui_region:add_object(block)
y = y + 20
end
--- /dev/null
+import {
+ Rectangle = "src.utils:Rectangle";
+ uuid = "src.utils:uuidv4";
+ revipairs = "src.utils:revipairs";
+ scissor_points = "src.utils:scissor_points";
+}
+
+class "Ui" {
+ init = function(self)
+ self.regions = {}
+ self.active_region = nil
+ end;
+
+ add_region = function(self, region)
+ table.insert(self.regions, region)
+ table.sort(self.regions)
+ end;
+
+ onmousedown = function(self, button, x, y)
+ local region = nil
+ for _, r in revipairs(self.regions) do
+ if r.rect:contains(x, y) then
+ region = r
+ break
+ end
+ end
+
+ if region ~= nil then
+ region:onmousedown(button, x - region.rect.x, y - region.rect.y)
+ end
+ end;
+
+ onmouseup = function(self, button, x, y)
+ local region = nil
+ for _, r in revipairs(self.regions) do
+ if r.rect:contains(x, y) then
+ region = r
+ break
+ end
+ end
+
+ if region ~= nil then
+ region:onmouseup(button, x - region.rect.x, y - region.rect.y)
+ end
+ end;
+
+ onmousemove = function(self, x, y, dx, dy)
+ local region = nil
+ for _, r in revipairs(self.regions) do
+ if r.rect:contains(x, y) then
+ region = r
+ break
+ end
+ end
+
+ if region ~= nil then
+ if region ~= self.active_region then
+ if self.active_region ~= nil then
+ self.active_region:onmouseleave()
+ end
+
+ region:onmouseenter()
+ end
+ region:onmousemove(x - region.rect.x, y - region.rect.y, dx, dy)
+ end
+
+ self.active_region = region
+ end;
+
+ onmousewheel = function(self, dx, dy)
+ if not self.active_region then return end
+ self.active_region:onmousewheel(dx, dy)
+ end;
+
+ onkeydown = function(self, key)
+ if not self.active_region then return end
+ self.active_region:onkeydown(key)
+ end;
+
+ onkeyup = function(self, key)
+ if not self.active_region then return end
+ self.active_region:onkeyup(key)
+ end;
+
+ onresize = function(self, new_width, new_height)
+ end;
+
+ update = function(self, dt)
+ for _, region in ipairs(self.regions) do
+ region:update(dt);
+ end
+ end;
+
+ draw = function(self)
+ for _, region in ipairs(self.regions) do
+ love.graphics.push()
+ love.graphics.setScissor(scissor_points(region.rect.x, region.rect.y, region.rect.w, region.rect.h))
+ love.graphics.translate(region.rect.x, region.rect.y)
+ region:draw()
+ love.graphics.pop()
+ end
+ end;
+}
+
+class "UiRegion" {
+ init = function(self)
+ self.active = false
+
+ self.selected_object = nil
+ self.objects = {}
+
+ self.rect = Rectangle(0, 0, 0, 0)
+ self.background_color = { 0, 0, 0 }
+ self.layer = 0
+
+ -- Internals
+ self.is_mouse_down = false
+ self.fire_onclick = false
+ end;
+
+ add_object = function(self, obj)
+ table.insert(self.objects, obj)
+ end;
+
+ -- Used for sorting in the Ui
+ __lt = function(self, other)
+ return self.layer < other.layer
+ end;
+
+ _obj_rect = function(self, obj)
+ return obj.rect
+ end;
+
+ onmousedown = function(self, button, x, y)
+ self.is_mouse_down = button
+ self.fire_onclick = true
+ local new_selected = nil
+
+ for _, obj in revipairs(self.objects) do
+ if self:_obj_rect(obj):contains(x, y) then
+ new_selected = obj
+ break
+ end
+ end
+
+ if self.selected_object ~= new_selected then
+ if self.selected_object ~= nil then
+ self.selected_object:onunselect()
+ end
+
+ self.selected_object = new_selected
+
+ if self.selected_object ~= nil then
+ self.selected_object.order = 0
+
+ for _, obj in ipairs(self.objects) do
+ if obj ~= self.selected_obj then
+ obj.order = obj.order + 1
+ end
+ end
+
+ table.sort(self.objects, function(a, b)
+ return a.order > b.order
+ end)
+
+ self.selected_object:onselect()
+ end
+ end
+ end;
+
+ onmouseup = function(self, button, x, y)
+ self.is_mouse_down = false
+
+ if self.selected_object ~= nil and self.fire_onclick then
+ local obj_rect = self:_obj_rect(self.selected_object)
+ self.selected_object:onclick(button, x - obj_rect.x, y - obj_rect.y)
+ end
+ end;
+ onmousemove = function(self, x, y, dx, dy)
+ if self.is_mouse_down and self.selected_object ~= nil then
+ local obj_rect = self:_obj_rect(self.selected_object)
+ self.selected_object:ondrag(self.is_mouse_down, x - obj_rect.x, y - obj_rect.y, dx, dy)
+ self.fire_onclick = false
+ end
+ end;
+ onmouseenter = function(self)
+ self.active = true
+ end;
+
+ onmouseleave = function(self)
+ self.active = false;
+ self.is_mouse_down = false
+ self.fire_onclick = false
+ self.selected_object = nil
+ end;
+
+ onmousewheel = function(self, dx, dy)
+ if self.selected_object ~= nil then
+ self.selected_object:onwheel(dx, dy)
+ end
+ end;
+
+ onkeydown = function(self, key) end;
+ onkeyup = function(self, key) end;
+
+ update = function(self, dt)
+ for _, obj in ipairs(self.objects) do
+ obj:update(dt)
+ end
+ end;
+
+ draw = function(self)
+ if self.background_color ~= nil then
+ love.graphics.setColor(self.background_color)
+ love.graphics.rectangle("fill", 0, 0, self.rect.w, self.rect.h)
+ end
+
+ for _, obj in ipairs(self.objects) do
+ love.graphics.push()
+ love.graphics.translate(obj.rect.x, obj.rect.y)
+ obj:draw()
+ love.graphics.setScissor()
+ love.graphics.pop()
+ end
+ end;
+}
+
+class "ScrollingUiRegion" [UiRegion] {
+ init = function(self)
+ UiRegion.init(self)
+
+ self.offset = { x = 0; y = 0; }
+ self.line_color = { 0, 0, 0 }
+
+ self.zoom = 1
+ end;
+
+ _obj_rect = function(self, obj)
+ return obj.rect
+ end;
+
+ _transform_mouse = function(self, x, y)
+ return
+ ((x + self.rect.x) - self.rect.w / 2) / self.zoom + self.rect.w / 2 - self.offset.x - self.rect.x,
+ ((y + self.rect.y) - self.rect.h / 2) / self.zoom + self.rect.h / 2 - self.offset.y - self.rect.y
+ end;
+
+ onmousedown = function(self, button, x, y)
+ local mx, my = self:_transform_mouse(x, y)
+ UiRegion.onmousedown(self, button, mx, my)
+ end;
+
+ onmouseup = function(self, button, x, y)
+ local mx, my = self:_transform_mouse(x, y)
+ UiRegion.onmouseup(self, button, mx, my)
+ end;
+
+ onmousemove = function(self, x, y, dx, dy)
+ local mx, my = self:_transform_mouse(x, y)
+ UiRegion.onmousemove(self, mx, my, dx / self.zoom, dy / self.zoom)
+
+ if self.is_mouse_down and self.selected_object == nil then
+ self.offset.x = self.offset.x + dx / self.zoom
+ self.offset.y = self.offset.y + dy / self.zoom
+ end
+ end;
+
+ onmousewheel = function(self, dx, dy)
+ UiRegion.onmousewheel(self, dx, dy)
+
+ if self.selected_object == nil then
+ self.zoom = self.zoom * (dy > 0 and 1.05 or (1 / 1.05))
+ if self.zoom >= 1 then self.zoom = 1 end
+ if self.zoom <= (1 / 1.05) ^ 30 then self.zoom = (1 / 1.05) ^ 30 end
+ end
+ end;
+
+ update = function(self, dt)
+ UiRegion.update(self, dt)
+
+ if self.active then
+ local speed = 300 / self.zoom
+ if love.keyboard.isDown "left" then
+ self.offset.x = self.offset.x + speed * dt
+ end
+ if love.keyboard.isDown "right" then
+ self.offset.x = self.offset.x - speed * dt
+ end
+ if love.keyboard.isDown "up" then
+ self.offset.y = self.offset.y + speed * dt
+ end
+ if love.keyboard.isDown "down" then
+ self.offset.y = self.offset.y - speed * dt
+ end
+ end
+ end;
+
+ draw = function(self)
+ love.graphics.push()
+
+ if self.background_color ~= nil then
+ love.graphics.setColor(self.background_color)
+ love.graphics.rectangle("fill", 0, 0, self.rect.w, self.rect.h)
+ end
+ local tmpbgcolor = self.background_color
+ self.background_color = nil
+
+ love.graphics.translate(self.rect.w / 2, self.rect.h / 2)
+ love.graphics.scale(self.zoom, self.zoom)
+ love.graphics.translate(-self.rect.w / 2, -self.rect.h / 2)
+
+
+ local xoff = self.offset.x
+ local yoff = self.offset.y
+ local spacing = 64
+
+ while xoff >= spacing do xoff = xoff - spacing end
+ while yoff >= spacing do yoff = yoff - spacing end
+ while xoff <= -spacing do xoff = xoff + spacing end
+ while yoff <= -spacing do yoff = yoff + spacing end
+
+ love.graphics.setColor(self.line_color)
+ for x = -60, 100 do
+ love.graphics.line(x * spacing + xoff, -self.rect.h * 2, x * spacing + xoff, self.rect.h * 4)
+ end
+ for y = -60, 100 do
+ love.graphics.line(-self.rect.w * 2, y * spacing + yoff, self.rect.w * 4, y * spacing + yoff)
+ end
+
+ love.graphics.translate(self.offset.x, self.offset.y)
+ UiRegion.draw(self)
+ love.graphics.pop()
+
+ self.background_color = tmpbgcolor
+ end;
+}
+
+class "UiObject" {
+ init = function(self)
+ self.rect = Rectangle(0, 0, 0, 0)
+ self.uuid = uuid()
+ self.selected = false
+ self.order = math.random(1, 1024)
+ end;
+
+ onclick = function(self, button, x, y) end;
+ ondrag = function(self, button, x, y, dx, dy) end;
+ onwheel = function(self, dx, dy) end;
+
+ onselect = function(self)
+ self.selected = true
+ end;
+ onunselect = function(self)
+ self.selected = false
+ end;
+
+ onkey = function(self, button) end;
+
+ update = function(dt) end;
+ draw = function() end;
+}
+
+class "DraggableRect" [UiObject] {
+ init = function(self, x, y, w, h)
+ UiObject.init(self)
+
+ self.rect = Rectangle(x, y, w, h)
+ end;
+
+ ondrag = function(self, button, x, y, dx, dy)
+ if button == 1 then
+ self.rect.x = self.rect.x + dx
+ self.rect.y = self.rect.y + dy
+ end
+ end;
+
+ draw = function(self)
+ if self.selected then
+ love.graphics.setColor(0, 0, 1)
+ love.graphics.rectangle("fill", -2, -2, self.rect.w + 4, self.rect.h + 4)
+ end
+ love.graphics.setColor(1, 1, 1)
+ love.graphics.rectangle("fill", 0, 0, self.rect.w, self.rect.h)
+
+ render_text(0, 0, {
+ { text = "1 Hello ", color = { 1, 0, 0 } };
+ { text = "world", color = { 0, 0, 1 } };
+ { newline = true };
+ { text = "2 Nother line", color = { 0, 0.5, 0 } };
+ { newline = true };
+ { newline = true };
+ { text = "3 nother Nother line", color = { 0, 0.5, 0 } };
+ })
+ end;
+}
+
+programming_font = false
+function render_text(x, y, text)
+ if not programming_font then
+ programming_font = love.graphics.newFont("res/FiraCode-Regular.ttf", 14)
+ love.graphics.setFont(programming_font)
+ end
+
+ local origx = x
+ local fheight = programming_font:getHeight()
+ local indent_level = 0
+ local indentation = ""
+ local fresh_newline = true
+
+ for _, txt in ipairs(text) do
+ if txt.text then
+ local printed = txt.text
+ if fresh_newline then
+ if txt.post_indent then
+ printed = txt.text .. indentation
+ else
+ printed = indentation .. txt.text
+ end
+ fresh_newline = false
+ end
+
+ if txt.background then
+ love.graphics.setColor(txt.background)
+ love.graphics.rectangle("fill", x, y, programming_font:getWidth(txt.text), fheight + 2);
+ end
+
+ if txt.color then
+ love.graphics.setColor(txt.color)
+ end
+ love.graphics.print(printed, x, y)
+
+ x = x + programming_font:getWidth(printed)
+ end
+
+ if txt.newline then
+ y = y + fheight + 2;
+ x = origx
+ fresh_newline = true
+ end
+
+ if txt.change_indent then
+ indent_level = indent_level + txt.change_indent
+ indentation = ""
+ for i=1,indent_level do
+ indentation = indentation .. " "
+ end
+ end
+ end
+
+ return x, y
+end
+
+return module {
+ Ui = Ui;
+ UiRegion = UiRegion;
+ ScrollingUiRegion = ScrollingUiRegion;
+ UiObject = UiObject;
+ DraggableRect = DraggableRect;
+
+ render_text = render_text;
+}
--- /dev/null
+import {}
+
+-- This is unsued in favor of the C equivalent
+function buffered_read(file, buffer_size)
+ buffer_size = buffer_size or 4 * 1024
+
+ local buffer = {}
+ local ptr = -1
+
+ return coroutine.wrap(function()
+ while true do
+ if ptr <= 0 or ptr >= buffer_size then
+ ptr = 0
+ local str = file:read(buffer_size)
+ if not str then break end
+
+ buffer = {}
+ for c in (str or ''):gmatch '.' do
+ buffer[#buffer + 1] = c
+ end
+ end
+
+ ptr = ptr + 1
+ if ptr > #buffer then break end
+ coroutine.yield(buffer[ptr]:byte())
+ end
+ return
+ end)
+end
+
+function build_str(tbl_int)
+ if #tbl_int < 30 then
+ return string.char(unpack(tbl_int))
+ else
+ -- This is slower but may be needed for long strings
+ local str = ""
+ for _, c in ipairs(tbl_int) do
+ str = str .. string.char(c)
+ end
+ return str
+ end
+end
+
+function uuidv4()
+ local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
+ return string.gsub(template, '[xy]', function (c)
+ local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb)
+ return string.format('%x', v)
+ end)
+end
+
+function random_str(n)
+ local str = ""
+ for i=1, n do
+ str = str .. string.char(math.random(0,25) + 0x41 + 0x20 * math.random(0, 1))
+ end
+ return str
+end
+
+function revipairs(t)
+ local max = 1
+ while t[max] ~= nil do
+ max = max + 1
+ end
+ local function ripairs_it(t, i)
+ i = i - 1
+ local v = t[i]
+ if v ~= nil then
+ return i, v
+ else
+ return nil
+ end
+ end
+ return ripairs_it, t, max
+end
+
+class "Rectangle" {
+ init = function(self, x, y, w, h)
+ self.x = x
+ self.y = y
+ self.w = w
+ self.h = h
+ end;
+
+ contains = function(self, x, y)
+ return x >= self.x and x <= self.x + self.w
+ and y >= self.y and y <= self.y + self.h
+ end;
+
+ intersects = function(self, other)
+ return self.x <= other.x + other.w
+ and self.x + self.w >= other.x
+ and self.y <= other.y + other.h
+ and self.y + self.h >= other.y
+ end;
+
+ __lt = function(self, other) return self:intersects(other) end
+}
+
+class "Stack" {
+ init = function(self)
+ self.data = {}
+ end;
+
+ push = function(self, it)
+ table.insert(self.data, it)
+ end;
+
+ pop = function(self)
+ local rt = table.remove(self.data)
+ return rt
+ end;
+
+ at = function(self, x)
+ return self.data[#self.data - x]
+ end
+}
+
+function scissor_points(x, y, w, h)
+ local nx, ny = love.graphics.transformPoint(x, y)
+ local tx, ty = love.graphics.transformPoint(w + x, h + y)
+ return nx, ny, tx - nx, ty - ny
+end
+
+return module {
+ buffered_read = buffered_read;
+ build_str = build_str;
+ random_str = random_str;
+
+ uuidv4 = uuidv4;
+ revipairs = revipairs;
+
+ scissor_points = scissor_points;
+
+ Rectangle = Rectangle;
+ Stack = Stack;
+}
--- /dev/null
+-- Recursive decent parser for the WASM v1.0 binary format
+-- Brendan Hansen 2020
+
+-- Look in clib folder for C-based libraries
+package.cpath = package.cpath .. [[;./clib/?.so]]
+
+import {
+ parse_helper = "parse_helper";
+ pprint = "lualib.pprint";
+
+ build_str = "src.utils:build_str";
+ random_str = "src.utils:random_str";
+ Stack = "src.utils:Stack";
+}
+
+function parse_valtype(r)
+ local val = r:read_byte()
+
+ local valtypes_map = {
+ [0x7f] = "i32";
+ [0x7E] = "i64";
+ [0x7D] = "f32";
+ [0x7C] = "f64";
+ }
+
+ return valtypes_map[val]
+end
+
+function parse_vector(tp)
+ return function(r)
+ local n = r:read_uint(32)
+
+ local v = {}
+ for i=1, n do
+ table.insert(v, tp(r))
+ end
+
+ return v
+ end
+end
+
+function parse_byte(r)
+ return r:read_byte()
+end
+
+function parse_name(r)
+ local name = parse_vector(parse_byte)(r)
+ return name
+end
+
+function parse_blocktype(r)
+ if r:peek_byte() == 0x40 then
+ r:read_byte()
+ return {}
+ else
+ return { parse_valtype(r) }
+ end
+end
+
+function parse_functype(r)
+ assert(r:read_byte() == 0x60, "functype expected 0x60 as first byte")
+
+ local params = parse_vector(parse_valtype)(r)
+ local results = parse_vector(parse_valtype)(r)
+
+ return { param_types = params, result_types = results }
+end
+
+function parse_limits(r)
+ local t = r:read_byte()
+
+ local min = r:read_uint(32)
+ local max = nil
+ if t == 0x01 then
+ max = r:read_uint(32)
+ end
+
+ return { min = min, max = max }
+end
+
+function parse_memtype(r)
+ local lims = parse_limits(r)
+
+ return lims
+end
+
+function parse_tabletype(r)
+ local elemtype = parse_elemtype(r)
+ local limits = parse_limits(r)
+
+ return { lim = limits, et = elemtype }
+end
+
+function parse_elemtype(r)
+ assert(r:read_byte() == 0x70, "elemtype should be 0x70")
+ return "funcref"
+end
+
+function parse_globaltype(r)
+ local valtype = parse_valtype(r)
+ local ismut = parse_mut(r) == 0x01
+
+ return { t = valtype, m = ismut }
+end
+
+function parse_mut(r)
+ local v = r:read_byte()
+ assert(v == 0x00 or v == 0x01, "mut should be 0x00 or 0x01")
+ return v
+end
+
+function parse_instr(r)
+ local instr = r:read_byte()
+
+ return match(instr) {
+ [0x00] = function() return { "unreachable" } end;
+ [0x01] = function() return { "nop" } end;
+ [0x02] = function()
+ local rt = parse_blocktype(r)
+ local instrs = {}
+ local name = random_str(8)
+
+ local block = { "block", label = name, rt = rt }
+ r.label_stack:push(block)
+
+ while true do
+ if r:peek_byte() == 0x0B then
+ r:read_byte()
+ break
+ else
+ table.insert(instrs, parse_instr(r))
+ end
+ end
+
+ r.label_stack:pop()
+
+ block.instrs = instrs
+ return block
+ end;
+ [0x03] = function()
+ local rt = parse_blocktype(r)
+ local instrs = {}
+ local name = random_str(8)
+
+ local block = { "loop", label = name, rt = rt }
+ r.label_stack:push(block)
+
+ while true do
+ if r:peek_byte() == 0x0B then
+ r:read_byte()
+ break
+ else
+ table.insert(instrs, parse_instr(r))
+ end
+ end
+
+ r.label_stack:pop()
+
+ block.instrs = instrs
+ return block
+ end;
+ [0x04] = function()
+ local rt = parse_blocktype(r)
+ local instrs = {}
+ local else_instrs = {}
+ local inelse = false
+
+ while true do
+ local peek = r:peek_byte()
+ if peek == 0x0B then
+ r:read_byte()
+ break
+ elseif peek == 0x05 then
+ r:read_byte()
+ inelse = true
+ else
+ if not inelse then
+ table.insert(instrs, parse_instr(r))
+ else
+ table.insert(else_instrs, parse_instr(r))
+ end
+ end
+ end
+
+ return { "if", rt = rt, instrs = instrs, else_instrs = else_instrs }
+ end;
+
+ [0x0C] = function() return { "br", x = parse_labelidx(r) } end;
+ [0x0D] = function() return { "br_if", x = parse_labelidx(r) } end;
+ [0x0E] = function()
+ local labels = parse_vector(parse_labelidx)(r)
+ local labeln = parse_labelidx(r)
+
+ return { "br_table", labels = labels, labeln = labeln }
+ end;
+
+ [0x0F] = function() return { "return" } end;
+
+ [0x10] = function() return { "call", x = parse_funcidx(r) } end;
+ [0x11] = function()
+ local x = parse_typeidx(r)
+ -- assert(r:read_byte() ~= 0x00, "call_indirect expects 0x00 at end")
+ return { "call_indirect", x = x, table = r:read_byte() }
+ end;
+
+ [0x1A] = function() return { "drop" } end;
+ [0x1B] = function() return { "select" } end;
+
+ [0x20] = function() return { "local.get", x = parse_localidx(r) } end;
+ [0x21] = function() return { "local.set", x = parse_localidx(r) } end;
+ [0x22] = function() return { "local.tee", x = parse_localidx(r) } end;
+ [0x23] = function() return { "global.get", x = parse_globalidx(r) } end;
+ [0x24] = function() return { "global.set", x = parse_globalidx(r) } end;
+
+ [0x28] = function() return { "i32.load", x = parse_memarg(r) } end;
+ [0x29] = function() return { "i64.load", x = parse_memarg(r) } end;
+ [0x2A] = function() return { "f32.load", x = parse_memarg(r) } end;
+ [0x2B] = function() return { "f64.load", x = parse_memarg(r) } end;
+ [0x2C] = function() return { "i32.load8_s", x = parse_memarg(r) } end;
+ [0x2D] = function() return { "i32.load8_u", x = parse_memarg(r) } end;
+ [0x2E] = function() return { "i32.load16_s", x = parse_memarg(r) } end;
+ [0x2F] = function() return { "i32.load16_u", x = parse_memarg(r) } end;
+ [0x30] = function() return { "i64.load8_s", x = parse_memarg(r) } end;
+ [0x31] = function() return { "i64.load8_u", x = parse_memarg(r) } end;
+ [0x32] = function() return { "i64.load16_s", x = parse_memarg(r) } end;
+ [0x33] = function() return { "i64.load16_u", x = parse_memarg(r) } end;
+ [0x34] = function() return { "i64.load32_s", x = parse_memarg(r) } end;
+ [0x35] = function() return { "i64.load32_u", x = parse_memarg(r) } end;
+ [0x36] = function() return { "i32.store", x = parse_memarg(r) } end;
+ [0x37] = function() return { "i64.store", x = parse_memarg(r) } end;
+ [0x38] = function() return { "f32.store", x = parse_memarg(r) } end;
+ [0x39] = function() return { "f64.store", x = parse_memarg(r) } end;
+ [0x3A] = function() return { "i32.store8", x = parse_memarg(r) } end;
+ [0x3B] = function() return { "i32.store16", x = parse_memarg(r) } end;
+ [0x3C] = function() return { "i64.store8", x = parse_memarg(r) } end;
+ [0x3D] = function() return { "i64.store16", x = parse_memarg(r) } end;
+ [0x3E] = function() return { "i64.store32", x = parse_memarg(r) } end;
+ [0x3F] = function()
+ assert(r:read_byte() == 0x00, "memory.size expects 0x00")
+ return { "memory.size" }
+ end;
+ [0x40] = function()
+ assert(r:read_byte() == 0x00, "memory.grow expects 0x00")
+ return { "memory.grow" }
+ end;
+
+ [0x41] = function() return { "i32.const", x = r:read_sint(32) } end;
+ [0x42] = function() return { "i64.const", x = r:read_sint(64) } end;
+ [0x43] = function() return { "f32.const", x = r:read_float(32) } end;
+ [0x44] = function() return { "f64.const", x = r:read_float(64) } end;
+
+ [0x45] = function() return { "i32.eqz" } end;
+ [0x46] = function() return { "i32.eq" } end;
+ [0x47] = function() return { "i32.ne" } end;
+ [0x48] = function() return { "i32.lt_s" } end;
+ [0x49] = function() return { "i32.lt_u" } end;
+ [0x4A] = function() return { "i32.gt_s" } end;
+ [0x4B] = function() return { "i32.gt_u" } end;
+ [0x4C] = function() return { "i32.le_s" } end;
+ [0x4D] = function() return { "i32.le_u" } end;
+ [0x4E] = function() return { "i32.ge_s" } end;
+ [0x4F] = function() return { "i32.ge_u" } end;
+ [0x50] = function() return { "i64.eqz" } end;
+ [0x51] = function() return { "i64.eq" } end;
+ [0x52] = function() return { "i64.ne" } end;
+ [0x53] = function() return { "i64.lt_s" } end;
+ [0x54] = function() return { "i64.lt_u" } end;
+ [0x55] = function() return { "i64.gt_s" } end;
+ [0x56] = function() return { "i64.gt_u" } end;
+ [0x57] = function() return { "i64.le_s" } end;
+ [0x58] = function() return { "i64.le_u" } end;
+ [0x59] = function() return { "i64.ge_s" } end;
+ [0x5A] = function() return { "i64.ge_u" } end;
+ [0x5B] = function() return { "f32.eq" } end;
+ [0x5C] = function() return { "f32.ne" } end;
+ [0x5D] = function() return { "f32.lt" } end;
+ [0x5E] = function() return { "f32.gt" } end;
+ [0x5F] = function() return { "f32.le" } end;
+ [0x60] = function() return { "f32.ge" } end;
+ [0x61] = function() return { "f64.eq" } end;
+ [0x62] = function() return { "f64.ne" } end;
+ [0x63] = function() return { "f64.lt" } end;
+ [0x64] = function() return { "f64.gt" } end;
+ [0x65] = function() return { "f64.le" } end;
+ [0x66] = function() return { "f64.ge" } end;
+ [0x67] = function() return { "i32.clz" } end;
+ [0x68] = function() return { "i32.ctz" } end;
+ [0x69] = function() return { "i32.popcnt" } end;
+ [0x6A] = function() return { "i32.add" } end;
+ [0x6B] = function() return { "i32.sub" } end;
+ [0x6C] = function() return { "i32.mul" } end;
+ [0x6D] = function() return { "i32.div_s" } end;
+ [0x6E] = function() return { "i32.div_u" } end;
+ [0x6F] = function() return { "i32.rem_s" } end;
+ [0x70] = function() return { "i32.rem_u" } end;
+ [0x71] = function() return { "i32.and" } end;
+ [0x72] = function() return { "i32.or" } end;
+ [0x73] = function() return { "i32.xor" } end;
+ [0x74] = function() return { "i32.shl" } end;
+ [0x75] = function() return { "i32.shr_s" } end;
+ [0x76] = function() return { "i32.shr_u" } end;
+ [0x77] = function() return { "i32.rotl" } end;
+ [0x78] = function() return { "i32.rotr" } end;
+ [0x79] = function() return { "i64.clz" } end;
+ [0x7A] = function() return { "i64.ctz" } end;
+ [0x7B] = function() return { "i64.popcnt" } end;
+ [0x7C] = function() return { "i64.add" } end;
+ [0x7D] = function() return { "i64.sub" } end;
+ [0x7E] = function() return { "i64.mul" } end;
+ [0x7F] = function() return { "i64.div_s" } end;
+ [0x80] = function() return { "i64.div_u" } end;
+ [0x81] = function() return { "i64.rem_s" } end;
+ [0x82] = function() return { "i64.rem_u" } end;
+ [0x83] = function() return { "i64.and" } end;
+ [0x84] = function() return { "i64.or" } end;
+ [0x85] = function() return { "i64.xor" } end;
+ [0x86] = function() return { "i64.shl" } end;
+ [0x87] = function() return { "i64.shr_s" } end;
+ [0x88] = function() return { "i64.shr_u" } end;
+ [0x89] = function() return { "i64.rotl" } end;
+ [0x8A] = function() return { "i64.rotr" } end;
+ [0x8B] = function() return { "f32.abs" } end;
+ [0x8C] = function() return { "f32.neg" } end;
+ [0x8D] = function() return { "f32.ceil" } end;
+ [0x8E] = function() return { "f32.floor" } end;
+ [0x8F] = function() return { "f32.trunc" } end;
+ [0x90] = function() return { "f32.nearest" } end;
+ [0x91] = function() return { "f32.sqrt" } end;
+ [0x92] = function() return { "f32.add" } end;
+ [0x93] = function() return { "f32.sub" } end;
+ [0x94] = function() return { "f32.mul" } end;
+ [0x95] = function() return { "f32.div" } end;
+ [0x96] = function() return { "f32.min" } end;
+ [0x97] = function() return { "f32.max" } end;
+ [0x98] = function() return { "f32.copysign" } end;
+ [0x99] = function() return { "f64.abs" } end;
+ [0x9A] = function() return { "f64.neg" } end;
+ [0x9B] = function() return { "f64.ceil" } end;
+ [0x9C] = function() return { "f64.floor" } end;
+ [0x9D] = function() return { "f64.trunc" } end;
+ [0x9E] = function() return { "f64.nearest" } end;
+ [0x9F] = function() return { "f64.sqrt" } end;
+ [0xA0] = function() return { "f64.add" } end;
+ [0xA1] = function() return { "f64.sub" } end;
+ [0xA2] = function() return { "f64.mul" } end;
+ [0xA3] = function() return { "f64.div" } end;
+ [0xA4] = function() return { "f64.min" } end;
+ [0xA5] = function() return { "f64.max" } end;
+ [0xA6] = function() return { "f64.copysign" } end;
+
+ [0xA7] = function() return { "i32.wrap_i64" } end;
+ [0xA8] = function() return { "i32.trunc_f32_s" } end;
+ [0xA9] = function() return { "i32.trunc_f32_u" } end;
+ [0xAA] = function() return { "i32.trunc_f64_s" } end;
+ [0xAB] = function() return { "i32.trunc_f64_u" } end;
+ [0xAC] = function() return { "i64.extend_i32_s" } end;
+ [0xAD] = function() return { "i64.extend_i32_u" } end;
+ [0xAE] = function() return { "i64.trunc_f32_s" } end;
+ [0xAF] = function() return { "i64.trunc_f32_u" } end;
+ [0xB0] = function() return { "i64.trunc_f64_s" } end;
+ [0xB1] = function() return { "i64.trunc_f64_u" } end;
+ [0xB2] = function() return { "f32.convert_i32_s" } end;
+ [0xB3] = function() return { "f32.convert_i32_u" } end;
+ [0xB4] = function() return { "f32.convert_i64_s" } end;
+ [0xB5] = function() return { "f32.convert_i64_u" } end;
+ [0xB6] = function() return { "f32.demote_f64" } end;
+ [0xB7] = function() return { "f64.convert_i32_s" } end;
+ [0xB8] = function() return { "f64.convert_i32_u" } end;
+ [0xB9] = function() return { "f64.convert_i64_s" } end;
+ [0xBA] = function() return { "f64.convert_i64_u" } end;
+ [0xBB] = function() return { "f64.promote_f32" } end;
+ [0xBC] = function() return { "i32.reinterpret_f32" } end;
+ [0xBD] = function() return { "i64.reinterpret_f64" } end;
+ [0xBE] = function() return { "f32.reinterpret_i32" } end;
+ [0xBF] = function() return { "f64.reinterpret_i64" } end;
+ }
+end
+
+function parse_memarg(r)
+ local a = r:read_uint(32)
+ local o = r:read_uint(32)
+
+ return { "memarg"; align = a; offset = o }
+end
+
+function parse_expr(r)
+ local instrs = {}
+ while true do
+ if r:peek_byte() == 0x0B then
+ r:read_byte()
+ break
+ else
+ table.insert(instrs, parse_instr(r))
+ end
+ end
+
+ return instrs
+end
+
+function parse_typeidx(r) return r:read_uint(32) end
+function parse_funcidx(r) return r:read_uint(32) end
+function parse_tableidx(r) return r:read_uint(32) end
+function parse_memidx(r) return r:read_uint(32) end
+function parse_globalidx(r) return r:read_uint(32) end
+function parse_localidx(r) return r:read_uint(32) end
+function parse_labelidx(r)
+ local idx = r:read_uint(32)
+ local block = r.label_stack:at(idx)
+ if block == nil then block = { label = "return " } end
+ return { "labelidx", labelidx = idx, block = block }
+end
+
+function parse_section(r, expectN, B)
+ if r:peek_byte() ~= expectN then return end
+
+ local N = r:read_byte()
+ local size = r:read_uint(32)
+ local cont = B(r)
+
+ return { "section", contents = cont, size = size }
+end
+
+function parse_customsec(r)
+ local csect = parse_section(r, 0, parse_custom)
+ if not csect then return nil end
+
+ for i=1, csect.size do
+ -- Discard csect.size bytes
+ r:read_byte()
+ end
+ return csect
+end
+
+function parse_custom(r)
+ local name = parse_name(r)
+
+ return { "custom", name = name }
+end
+
+function parse_typesec(r)
+ return parse_section(r, 1, parse_vector(parse_functype))
+end
+
+function parse_importsec(r)
+ return parse_section(r, 2, parse_vector(parse_import))
+end
+
+function parse_import(r)
+ local mod = parse_name(r)
+ local nm = parse_name(r)
+ local d = parse_importdesc(r)
+
+ return { "import", mod = mod, name = nm, desc = d }
+end
+
+function parse_importdesc(r)
+ local t = r:read_byte()
+ if t == 0x00 then return { "func", x = parse_typeidx(r) }
+ elseif t == 0x01 then return { "table", tt = parse_tabletype(r) }
+ elseif t == 0x02 then return { "mem", mt = parse_memtype(r) }
+ elseif t == 0x03 then return { "global", gt = parse_globaltype(r) }
+ end
+
+ error("bad importdesc")
+end
+
+function parse_funcsec(r)
+ return parse_section(r, 3, parse_vector(parse_typeidx))
+end
+
+function parse_tablesec(r)
+ return parse_section(r, 4, parse_vector(parse_table))
+end
+
+function parse_table(r)
+ local tt = parse_tabletype(r)
+ return { "table", type_ = tt }
+end
+
+function parse_memsec(r)
+ return parse_section(r, 5, parse_vector(parse_mem))
+end
+
+function parse_mem(r)
+ local mt = parse_memtype(r)
+ return { "mem", type_ = mt }
+end
+
+function parse_globalsec(r)
+ return parse_section(r, 6, parse_vector(parse_global))
+end
+
+function parse_global(r)
+ local gt = parse_globaltype(r)
+ local e = parse_expr(r)
+ return { "global", type_ = gt, init = e }
+end
+
+function parse_exportsec(r)
+ return parse_section(r, 7, parse_vector(parse_export))
+end
+
+function parse_export(r)
+ local nm = parse_name(r)
+ local d = parse_exportdesc(r)
+ return { "export", name = nm, desc = d }
+end
+
+function parse_exportdesc(r)
+ local t = r:read_byte()
+ if t == 0x00 then return { "func", x = parse_typeidx(r) }
+ elseif t == 0x01 then return { "table", tt = parse_tableidx(r) }
+ elseif t == 0x02 then return { "mem", mt = parse_memidx(r) }
+ elseif t == 0x03 then return { "global", gt = parse_globalidx(r) }
+ end
+
+ error("bad exportdesc: ", t)
+end
+
+function parse_startsec(r)
+ return parse_section(r, 8, parse_start)
+end
+
+function parse_start(r)
+ local x = parse_funcidx(r)
+ return { "start", func = x }
+end
+
+function parse_elemsec(r)
+ return parse_section(r, 9, parse_vector(parse_elem))
+end
+
+function parse_elem(r)
+ local x = parse_tableidx(r)
+ local e = parse_expr(r)
+ local y = parse_vector(parse_funcidx)(r)
+
+ return { "elem", table = x, offset = e, init = y }
+end
+
+function parse_codesec(r)
+ return parse_section(r, 10, parse_vector(parse_code))
+end
+
+function parse_code(r)
+ local size = r:read_uint(32)
+ local code = parse_func(r)
+ return code
+end
+
+function parse_func(r)
+ local t = parse_vector(parse_locals)(r)
+ local e = parse_expr(r)
+
+ local localidx = 0
+ local locals = {}
+ for _, v in ipairs(t) do
+ for _, l in ipairs(v) do
+ l.localidx = localidx
+ table.insert(locals, l)
+ localidx = localidx + 1
+ end
+ end
+
+ return { "func", locals = locals, body = e }
+end
+
+function parse_locals(r)
+ local n = r:read_uint(32)
+ local t = parse_valtype(r)
+
+ --TODO: Make a list of values with names like local0, local1, ...
+
+ local locals = {}
+ for i = 0, n - 1 do
+ table.insert(locals, {
+ name = "local" .. i,
+ type_ = t,
+ localidx = i
+ })
+ end
+
+ return locals
+end
+
+function parse_datasec(r)
+ return parse_section(r, 11, parse_vector(parse_data))
+end
+
+function parse_data(r)
+ local x = parse_memidx(r)
+ local e = parse_expr(r)
+ local b = parse_vector(parse_byte)(r)
+
+ return { "data", data = x, offset = e, init = b }
+end
+
+function parse_magic(r)
+ assert(r:read_byte() == 0x00, "magic string is wrong")
+ assert(r:read_byte() == 0x61, "magic string is wrong")
+ assert(r:read_byte() == 0x73, "magic string is wrong")
+ assert(r:read_byte() == 0x6D, "magic string is wrong")
+end
+
+function parse_version(r)
+ assert(r:read_byte() == 0x01, "version is wrong")
+ assert(r:read_byte() == 0x00, "version is wrong")
+ assert(r:read_byte() == 0x00, "version is wrong")
+ assert(r:read_byte() == 0x00, "version is wrong")
+end
+
+function parse_module(r)
+ parse_magic(r)
+ parse_version(r)
+
+ local functypes = parse_typesec(r)
+ local imports = parse_importsec(r)
+ local typeidxs = parse_funcsec(r)
+ local tables = parse_tablesec(r)
+ local mems = parse_memsec(r)
+ local globals = parse_globalsec(r)
+ local exports = parse_exportsec(r)
+ local start = parse_startsec(r)
+ local elems = parse_elemsec(r)
+ local codes = parse_codesec(r)
+ local data = parse_datasec(r)
+
+ local funcs = {}
+ local funcidx = 0
+
+ if imports then
+ for k, v in ipairs(imports.contents) do
+ if v.desc[1] == "func" then
+ funcs[funcidx] = {
+ name = build_str(v.mod) .. "." .. build_str(v.name);
+ funcidx = funcidx;
+ type_ = functypes.contents[v.desc.x + 1];
+ }
+ funcidx = funcidx + 1
+ end
+ end
+ end
+
+ if codes then
+ for i=1, #codes.contents do
+ local locals = codes.contents[i].locals;
+ local type_ = functypes.contents[typeidxs.contents[i] + 1];
+ local param_types = type_.param_types;
+
+ local new_locals = {}
+ local new_local_idx = 0
+ for _, p in ipairs(param_types) do
+ table.insert(new_locals, {
+ name = "param" .. new_local_idx,
+ type_ = p;
+ localidx = new_local_idx
+ })
+ new_local_idx = new_local_idx + 1
+ end
+ for _, l in ipairs(locals) do
+ l.localidx = new_local_idx
+ table.insert(new_locals, l)
+ new_local_idx = new_local_idx + 1
+ end
+
+ funcs[funcidx] = {
+ funcidx = funcidx;
+ name = "func" .. funcidx;
+ type_ = functypes.contents[typeidxs.contents[i] + 1];
+ locals = new_locals;
+ body = codes.contents[i].body
+ }
+ funcidx = funcidx + 1
+ end
+ end
+
+ return {
+ "module";
+ types = functypes;
+ tables = tables;
+ mems = mems;
+ globals = globas;
+ exports = exports;
+ start = start;
+ elems = elems;
+ data = data;
+ imports = imports;
+ funcs = funcs;
+ }
+end
+
+
+-- Reader util class used for interfacing
+-- with the parse_helper c library
+class "Parser" {
+ init = function(self, filename)
+ self.wasm_file = parse_helper.open_file(filename)
+ self.label_stack = Stack()
+ end;
+
+ read_byte = function(self)
+ return parse_helper.read_byte(self.wasm_file)
+ end;
+
+ peek_byte = function(self)
+ return parse_helper.peek_byte(self.wasm_file)
+ end;
+
+ -- NOTE: N is unused
+ read_uint = function(self, N)
+ return parse_helper.parse_uleb128(self.wasm_file)
+ end;
+
+ read_sint = function(self, N)
+ return parse_helper.parse_sleb128(self.wasm_file, N)
+ end;
+
+ read_float = function(self, N)
+ if N >= 32 then
+ self:read_byte()
+ self:read_byte()
+ self:read_byte()
+ self:read_byte()
+ end
+ if N >= 64 then
+ self:read_byte()
+ self:read_byte()
+ self:read_byte()
+ self:read_byte()
+ end
+ return 0.0;
+ end;
+}
+
+function decompile(filepath)
+ local reader = Parser(filepath)
+
+ return parse_module(reader)
+end
+
+return module { decompile }
--- /dev/null
+import {
+}
+
+WasmCodeGenerator = singleton {
+ init = function(self) end;
+
+ setup = function(self, func, mod, colors)
+ self.func = func
+ self.mod = mod
+ self.colors = colors;
+ self.line = 1
+ end;
+
+ build_header = function(self)
+ local text = {}
+
+ table.insert(text, { text = "[" .. self.func.funcidx .. "] ", color = self.colors.code })
+ table.insert(text, { text = self.func.name, color = self.colors.keyword })
+ table.insert(text, { text = ": ", color = self.colors.code })
+
+ local type_ = self.func.type_
+ for _, t in ipairs(type_.param_types) do
+ table.insert(text, { text = t .. " " })
+ end
+
+ if #type_.param_types == 0 then
+ table.insert(text, { text = "void " })
+ end
+
+ table.insert(text, { text = "-> " })
+
+ for _, t in ipairs(type_.result_types) do
+ table.insert(text, { text = t .. " " })
+ end
+
+ if #type_.result_types == 0 then
+ table.insert(text, { text = "void" })
+ end
+
+ table.insert(text, { newline = true })
+
+ return text
+ end;
+
+ add_line_number = function(self, textlist, newline)
+ if newline == nil then newline = true end
+
+ table.insert(textlist, { newline = newline })
+ table.insert(textlist, {
+ text = ("%4d"):format(line),
+ color = self.colors.code,
+ background = self.colors.dark_background,
+ post_indent = true
+ })
+ table.insert(textlist, { text = " ", post_indent = true })
+
+ line = line + 1
+ end;
+
+ instr_to_text = function(self, textlist, instr)
+ if instr[1] == "block" then
+ table.insert(textlist, { change_indent = 1 })
+ for _, ins in ipairs(instr.instrs) do
+ self:instr_to_text(textlist, ins)
+ end
+ table.insert(textlist, { change_indent = -1 })
+ self:add_line_number(textlist)
+ table.insert(textlist, { text = "label ", color = self.colors.jumppoint })
+ table.insert(textlist, { text = instr.label, color = self.colors.value })
+
+ elseif instr[1] == "loop" then
+ self:add_line_number(textlist)
+ table.insert(textlist, { text = "loop ", color = self.colors.jumppoint })
+ table.insert(textlist, { text = instr.label, color = self.colors.value, change_indent = 1 })
+
+ for _, ins in ipairs(instr.instrs) do
+ self:instr_to_text(textlist, ins)
+ end
+ table.insert(textlist, { change_indent = -1 })
+
+ elseif instr[1] == "if" then
+ self:add_line_number(textlist)
+ table.insert(textlist, { text = "if", color = self.colors.jumppoint, change_indent = 1 })
+ for _, ins in ipairs(instr.instrs) do
+ self:instr_to_text(textlist, ins)
+ end
+ table.insert(textlist, { change_indent = -1 })
+
+ if #instr.else_instrs > 0 then
+ self:add_line_number(textlist)
+ table.insert(textlist, { text = "else", color = self.colors.jumppoint, change_indent = 1 })
+ for _, ins in ipairs(instr.else_instrs) do
+ self:instr_to_text(textlist, ins)
+ end
+ table.insert(textlist, { change_indent = -1 })
+ end
+
+ elseif instr[1] == "call" then
+ self:add_line_number(textlist)
+ table.insert(textlist, { text = instr[1] .. " ", color = self.colors.keyword })
+ table.insert(textlist, { text = self.mod.funcs[instr.x].name, color = self.colors.value })
+
+ elseif instr[1]:match("local") then
+ self:add_line_number(textlist)
+ table.insert(textlist, { text = instr[1] .. " ", color = self.colors.keyword })
+ table.insert(textlist, { text = self.func.locals[instr.x + 1].name, color = self.colors.value })
+
+ else
+ self:add_line_number(textlist)
+ table.insert(textlist, { text = instr[1] .. " ", color = self.colors.keyword })
+ if instr.x then
+ if type(instr.x) == "table" then
+ if instr.x[1] == "memarg" then
+ table.insert(textlist, {
+ text = ("align=0x%02x offset=0x%04x"):format(instr.x.align, instr.x.offset);
+ color = self.colors.value
+ })
+ end
+
+ if instr.x[1] == "labelidx" then
+ if instr.x.block then
+ table.insert(textlist, { text = instr.x.block.label .. " ", color = self.colors.value })
+ end
+ table.insert(textlist, { text = ("[0x%02x]"):format(instr.x.labelidx), color = self.colors.code })
+ end
+ else
+ table.insert(textlist, { text = tostring(instr.x), color = self.colors.value })
+ end
+ end
+ end
+
+ return line
+ end;
+
+ build_body = function(self)
+ local text = {}
+ line = 1
+
+ table.insert(text, { text = " Locals:", color = self.colors.keyword, newline = true, change_indent = 2 })
+ for _, loc in ipairs(self.func.locals) do
+ table.insert(text, { text = loc.type_ .. " " .. loc.name, color = self.colors.code , newline = true })
+ end
+
+ if #self.func.locals == 0 then
+ table.insert(text, { text = "no locals", color = self.colors.code, newline = true })
+ end
+
+ table.insert(text, { newline = true, change_indent = -2 })
+ table.insert(text, { text = " Body:", color = self.colors.keyword })
+
+ for _, instr in ipairs(self.func.body) do
+ self:instr_to_text(text, instr)
+ end
+
+ return text
+ end;
+}
+
+return module {
+ WasmCodeGenerator;
+}
+
+++ /dev/null
-import {
- Rectangle = "utils:Rectangle";
- uuid = "utils:uuidv4";
- revipairs = "utils:revipairs";
- scissor_points = "utils:scissor_points";
-}
-
-class "Ui" {
- init = function(self)
- self.regions = {}
- self.active_region = nil
- end;
-
- add_region = function(self, region)
- table.insert(self.regions, region)
- table.sort(self.regions)
- end;
-
- onmousedown = function(self, button, x, y)
- local region = nil
- for _, r in revipairs(self.regions) do
- if r.rect:contains(x, y) then
- region = r
- break
- end
- end
-
- if region ~= nil then
- region:onmousedown(button, x - region.rect.x, y - region.rect.y)
- end
- end;
-
- onmouseup = function(self, button, x, y)
- local region = nil
- for _, r in revipairs(self.regions) do
- if r.rect:contains(x, y) then
- region = r
- break
- end
- end
-
- if region ~= nil then
- region:onmouseup(button, x - region.rect.x, y - region.rect.y)
- end
- end;
-
- onmousemove = function(self, x, y, dx, dy)
- local region = nil
- for _, r in revipairs(self.regions) do
- if r.rect:contains(x, y) then
- region = r
- break
- end
- end
-
- if region ~= nil then
- if region ~= self.active_region then
- if self.active_region ~= nil then
- self.active_region:onmouseleave()
- end
-
- region:onmouseenter()
- end
- region:onmousemove(x - region.rect.x, y - region.rect.y, dx, dy)
- end
-
- self.active_region = region
- end;
-
- onmousewheel = function(self, dx, dy)
- if not self.active_region then return end
- self.active_region:onmousewheel(dx, dy)
- end;
-
- onkeydown = function(self, key)
- if not self.active_region then return end
- self.active_region:onkeydown(key)
- end;
-
- onkeyup = function(self, key)
- if not self.active_region then return end
- self.active_region:onkeyup(key)
- end;
-
- onresize = function(self, new_width, new_height)
- end;
-
- update = function(self, dt)
- for _, region in ipairs(self.regions) do
- region:update(dt);
- end
- end;
-
- draw = function(self)
- for _, region in ipairs(self.regions) do
- love.graphics.push()
- love.graphics.setScissor(scissor_points(region.rect.x, region.rect.y, region.rect.w, region.rect.h))
- love.graphics.translate(region.rect.x, region.rect.y)
- region:draw()
- love.graphics.pop()
- end
- end;
-}
-
-class "UiRegion" {
- init = function(self)
- self.active = false
-
- self.selected_object = nil
- self.objects = {}
-
- self.rect = Rectangle(0, 0, 0, 0)
- self.background_color = { 0, 0, 0 }
- self.layer = 0
-
- -- Internals
- self.is_mouse_down = false
- self.fire_onclick = false
- end;
-
- add_object = function(self, obj)
- table.insert(self.objects, obj)
- end;
-
- -- Used for sorting in the Ui
- __lt = function(self, other)
- return self.layer < other.layer
- end;
-
- _obj_rect = function(self, obj)
- return obj.rect
- end;
-
- onmousedown = function(self, button, x, y)
- self.is_mouse_down = button
- self.fire_onclick = true
- local new_selected = nil
-
- for _, obj in revipairs(self.objects) do
- if self:_obj_rect(obj):contains(x, y) then
- new_selected = obj
- break
- end
- end
-
- if self.selected_object ~= new_selected then
- if self.selected_object ~= nil then
- self.selected_object:onunselect()
- end
-
- self.selected_object = new_selected
-
- if self.selected_object ~= nil then
- self.selected_object.order = 0
-
- for _, obj in ipairs(self.objects) do
- if obj ~= self.selected_obj then
- obj.order = obj.order + 1
- end
- end
-
- table.sort(self.objects, function(a, b)
- return a.order > b.order
- end)
-
- self.selected_object:onselect()
- end
- end
- end;
-
- onmouseup = function(self, button, x, y)
- self.is_mouse_down = false
-
- if self.selected_object ~= nil and self.fire_onclick then
- local obj_rect = self:_obj_rect(self.selected_object)
- self.selected_object:onclick(button, x - obj_rect.x, y - obj_rect.y)
- end
- end;
- onmousemove = function(self, x, y, dx, dy)
- if self.is_mouse_down and self.selected_object ~= nil then
- local obj_rect = self:_obj_rect(self.selected_object)
- self.selected_object:ondrag(self.is_mouse_down, x - obj_rect.x, y - obj_rect.y, dx, dy)
- self.fire_onclick = false
- end
- end;
- onmouseenter = function(self)
- self.active = true
- end;
-
- onmouseleave = function(self)
- self.active = false;
- self.is_mouse_down = false
- self.fire_onclick = false
- self.selected_object = nil
- end;
-
- onmousewheel = function(self, dx, dy)
- if self.selected_object ~= nil then
- self.selected_object:onwheel(dx, dy)
- end
- end;
-
- onkeydown = function(self, key) end;
- onkeyup = function(self, key) end;
-
- update = function(self, dt)
- for _, obj in ipairs(self.objects) do
- obj:update(dt)
- end
- end;
-
- draw = function(self)
- if self.background_color ~= nil then
- love.graphics.setColor(self.background_color)
- love.graphics.rectangle("fill", 0, 0, self.rect.w, self.rect.h)
- end
-
- for _, obj in ipairs(self.objects) do
- love.graphics.push()
- love.graphics.translate(obj.rect.x, obj.rect.y)
- obj:draw()
- love.graphics.setScissor()
- love.graphics.pop()
- end
- end;
-}
-
-class "ScrollingUiRegion" [UiRegion] {
- init = function(self)
- UiRegion.init(self)
-
- self.offset = { x = 0; y = 0; }
- self.line_color = { 0, 0, 0 }
-
- self.zoom = 1
- end;
-
- _obj_rect = function(self, obj)
- return obj.rect
- end;
-
- _transform_mouse = function(self, x, y)
- return
- ((x + self.rect.x) - self.rect.w / 2) / self.zoom + self.rect.w / 2 - self.offset.x - self.rect.x,
- ((y + self.rect.y) - self.rect.h / 2) / self.zoom + self.rect.h / 2 - self.offset.y - self.rect.y
- end;
-
- onmousedown = function(self, button, x, y)
- print(x, y)
- local mx, my = self:_transform_mouse(x, y)
- print(mx, my)
- UiRegion.onmousedown(self, button, mx, my)
- end;
-
- onmouseup = function(self, button, x, y)
- local mx, my = self:_transform_mouse(x, y)
- UiRegion.onmouseup(self, button, mx, my)
- end;
-
- onmousemove = function(self, x, y, dx, dy)
- local mx, my = self:_transform_mouse(x, y)
- UiRegion.onmousemove(self, mx, my, dx / self.zoom, dy / self.zoom)
-
- if self.is_mouse_down and self.selected_object == nil then
- self.offset.x = self.offset.x + dx / self.zoom
- self.offset.y = self.offset.y + dy / self.zoom
- end
- end;
-
- onmousewheel = function(self, dx, dy)
- UiRegion.onmousewheel(self, dx, dy)
-
- if self.selected_object == nil then
- self.zoom = self.zoom * (dy > 0 and 1.05 or (1 / 1.05))
- if self.zoom >= 1 then self.zoom = 1 end
- if self.zoom <= (1 / 1.05) ^ 20 then self.zoom = (1 / 1.05) ^ 20 end
- end
- end;
-
- update = function(self, dt)
- UiRegion.update(self, dt)
-
- if self.active then
- local speed = 300 / self.zoom
- if love.keyboard.isDown "left" then
- self.offset.x = self.offset.x + speed * dt
- end
- if love.keyboard.isDown "right" then
- self.offset.x = self.offset.x - speed * dt
- end
- if love.keyboard.isDown "up" then
- self.offset.y = self.offset.y + speed * dt
- end
- if love.keyboard.isDown "down" then
- self.offset.y = self.offset.y - speed * dt
- end
- end
- end;
-
- draw = function(self)
- love.graphics.push()
-
- if self.background_color ~= nil then
- love.graphics.setColor(self.background_color)
- love.graphics.rectangle("fill", 0, 0, self.rect.w, self.rect.h)
- end
- local tmpbgcolor = self.background_color
- self.background_color = nil
-
- love.graphics.translate(self.rect.w / 2, self.rect.h / 2)
- love.graphics.scale(self.zoom, self.zoom)
- love.graphics.translate(-self.rect.w / 2, -self.rect.h / 2)
-
-
- local xoff = self.offset.x
- local yoff = self.offset.y
- local spacing = 64
-
- while xoff >= spacing do xoff = xoff - spacing end
- while yoff >= spacing do yoff = yoff - spacing end
- while xoff <= -spacing do xoff = xoff + spacing end
- while yoff <= -spacing do yoff = yoff + spacing end
-
- love.graphics.setColor(self.line_color)
- for x = -40, 80 do
- love.graphics.line(x * spacing + xoff, -self.rect.h, x * spacing + xoff, self.rect.h * 2)
- end
- for y = -40, 80 do
- love.graphics.line(-self.rect.w, y * spacing + yoff, self.rect.w * 2, y * spacing + yoff)
- end
-
- love.graphics.translate(self.offset.x, self.offset.y)
- UiRegion.draw(self)
- love.graphics.pop()
-
- self.background_color = tmpbgcolor
- end;
-}
-
-class "UiObject" {
- init = function(self)
- self.rect = Rectangle(0, 0, 0, 0)
- self.uuid = uuid()
- self.selected = false
- self.order = math.random(1, 1024)
- end;
-
- onclick = function(self, button, x, y) end;
- ondrag = function(self, button, x, y, dx, dy) end;
- onwheel = function(self, dx, dy) end;
-
- onselect = function(self)
- self.selected = true
- end;
- onunselect = function(self)
- self.selected = false
- end;
-
- onkey = function(self, button) end;
-
- update = function(dt) end;
- draw = function() end;
-}
-
-class "DraggableRect" [UiObject] {
- init = function(self, x, y, w, h)
- UiObject.init(self)
-
- self.rect = Rectangle(x, y, w, h)
- end;
-
- ondrag = function(self, button, x, y, dx, dy)
- if button == 1 then
- self.rect.x = self.rect.x + dx
- self.rect.y = self.rect.y + dy
- end
- end;
-
- draw = function(self)
- if self.selected then
- love.graphics.setColor(0, 0, 1)
- love.graphics.rectangle("fill", -2, -2, self.rect.w + 4, self.rect.h + 4)
- end
- love.graphics.setColor(1, 1, 1)
- love.graphics.rectangle("fill", 0, 0, self.rect.w, self.rect.h)
-
- render_text(0, 0, {
- { text = "1 Hello ", color = { 1, 0, 0 } };
- { text = "world", color = { 0, 0, 1 } };
- { newline = true };
- { text = "2 Nother line", color = { 0, 0.5, 0 } };
- { newline = true };
- { newline = true };
- { text = "3 nother Nother line", color = { 0, 0.5, 0 } };
- })
- end;
-}
-
-programming_font = false
-function render_text(x, y, text)
- if not programming_font then
- programming_font = love.graphics.newFont("res/FiraCode-Regular.ttf", 14)
- love.graphics.setFont(programming_font)
- end
-
- local origx = x
- local fheight = programming_font:getHeight()
- local indent_level = 0
- local indentation = ""
- local fresh_newline = true
-
- for _, txt in ipairs(text) do
- if txt.text then
- local printed = txt.text
- if fresh_newline then
- if txt.post_indent then
- printed = txt.text .. indentation
- else
- printed = indentation .. txt.text
- end
- fresh_newline = false
- end
-
- if txt.background then
- love.graphics.setColor(txt.background)
- love.graphics.rectangle("fill", x, y, programming_font:getWidth(txt.text), fheight + 2);
- end
-
- if txt.color then
- love.graphics.setColor(txt.color)
- end
- love.graphics.print(printed, x, y)
-
- x = x + programming_font:getWidth(printed)
- end
-
- if txt.newline then
- y = y + fheight + 2;
- x = origx
- fresh_newline = true
- end
-
- if txt.change_indent then
- indent_level = indent_level + txt.change_indent
- indentation = ""
- for i=1,indent_level do
- indentation = indentation .. " "
- end
- end
- end
-
- return x, y
-end
-
-return module {
- Ui = Ui;
- UiRegion = UiRegion;
- ScrollingUiRegion = ScrollingUiRegion;
- UiObject = UiObject;
- DraggableRect = DraggableRect;
-
- render_text = render_text;
-}
+++ /dev/null
-import {}
-
--- This is unsued in favor of the C equivalent
-function buffered_read(file, buffer_size)
- buffer_size = buffer_size or 4 * 1024
-
- local buffer = {}
- local ptr = -1
-
- return coroutine.wrap(function()
- while true do
- if ptr <= 0 or ptr >= buffer_size then
- ptr = 0
- local str = file:read(buffer_size)
- if not str then break end
-
- buffer = {}
- for c in (str or ''):gmatch '.' do
- buffer[#buffer + 1] = c
- end
- end
-
- ptr = ptr + 1
- if ptr > #buffer then break end
- coroutine.yield(buffer[ptr]:byte())
- end
- return
- end)
-end
-
-function build_str(tbl_int)
- if #tbl_int < 30 then
- return string.char(unpack(tbl_int))
- else
- -- This is slower but may be needed for long strings
- local str = ""
- for _, c in ipairs(tbl_int) do
- str = str .. string.char(c)
- end
- return str
- end
-end
-
-function uuidv4()
- local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
- return string.gsub(template, '[xy]', function (c)
- local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb)
- return string.format('%x', v)
- end)
-end
-
-function random_str(n)
- local str = ""
- for i=1, n do
- str = str .. string.char(math.random(0,25) + 0x41 + 0x20 * math.random(0, 1))
- end
- return str
-end
-
-function revipairs(t)
- local max = 1
- while t[max] ~= nil do
- max = max + 1
- end
- local function ripairs_it(t, i)
- i = i - 1
- local v = t[i]
- if v ~= nil then
- return i, v
- else
- return nil
- end
- end
- return ripairs_it, t, max
-end
-
-class "Rectangle" {
- init = function(self, x, y, w, h)
- self.x = x
- self.y = y
- self.w = w
- self.h = h
- end;
-
- contains = function(self, x, y)
- return x >= self.x and x <= self.x + self.w
- and y >= self.y and y <= self.y + self.h
- end;
-
- intersects = function(self, other)
- return self.x <= other.x + other.w
- and self.x + self.w >= other.x
- and self.y <= other.y + other.h
- and self.y + self.h >= other.y
- end;
-
- __lt = function(self, other) return self:intersects(other) end
-}
-
-class "Stack" {
- init = function(self)
- self.data = {}
- end;
-
- push = function(self, it)
- table.insert(self.data, it)
- end;
-
- pop = function(self)
- local rt = table.remove(self.data)
- return rt
- end;
-
- at = function(self, x)
- return self.data[#self.data - x]
- end
-}
-
-function scissor_points(x, y, w, h)
- local nx, ny = love.graphics.transformPoint(x, y)
- local tx, ty = love.graphics.transformPoint(w + x, h + y)
- return nx, ny, tx - nx, ty - ny
-end
-
-return module {
- buffered_read = buffered_read;
- build_str = build_str;
- random_str = random_str;
-
- uuidv4 = uuidv4;
- revipairs = revipairs;
-
- scissor_points = scissor_points;
-
- Rectangle = Rectangle;
- Stack = Stack;
-}