Finallized text conversion
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 12 Apr 2020 04:40:12 +0000 (23:40 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 12 Apr 2020 04:40:12 +0000 (23:40 -0500)
src/utils.lua
src/wasm/exprs.lua
testing.lua

index db8880fed85bca5dd3a9d2e2734977d793d44794..0889f6fbf733352e2ffe0486f9f33a7820f815a1 100644 (file)
@@ -120,6 +120,14 @@ class "Stack" {
                self.data = {}
        end;
 
+       size = function(self)
+               return #self.data
+       end;
+
+       empty = function(self)
+               return #self.data == 0
+       end;
+
        at = function(self, x)
                return self.data[#self.data - x]
        end
index eb10f91ab4ce9fd607d4568d662b77ffc2100406..5f957aeb67a7ef65aaec8d70cafd7d52b146550d 100644 (file)
@@ -1,5 +1,6 @@
 import {
        Stack = "src.utils:Stack";
+       pprint = "lualib.pprint";
 }
 
 
@@ -43,7 +44,7 @@ local stack_opts = {
        --call_indirect is dynamic
 
        ["drop"]                                = { 1, 0 };
-       ["select"]                              = { 3, 0 };
+       ["select"]                              = { 3, 1 };
 
        ["local.get"]                   = { 0, 1 };
        ["local.set"]                   = { 1, 0 };
@@ -209,63 +210,372 @@ local stack_opts = {
        ["f64.reinterpret_i64"] = { 1, 1 };
 }
 
+local build_instr_list
+
+function binop(op)
+       return function(stack, instr)
+               local right = stack:pop()
+               local left = stack:pop()
+               stack:push("(" .. left .. " " .. op .. " " .. right .. ")")
+       end
+end
+
+function cast(to)
+       return function(stack, instr)
+               local thing = stack:pop()
+               stack:push("<" .. to .. ">" .. thing)
+       end
+end
+
+function func_call(name, nparams)
+       nparams = nparams or 1
+       return function(stack, instr)
+               local str = name .. "("
+               local args = ""
+               for i=1,nparams do
+                       local val = stack:pop()
+                       args = ", " .. val .. args
+               end
+               str = str .. args:sub(3) .. ")"
+               stack:push(str)
+       end
+end
+
+function const(t)
+       return function(stack, instr)
+               stack:push(tostring(instr.x) .. t)
+       end
+end
+
+function expr_return(stack, instr)
+       if stack:size() >= 1 then
+               return "return " .. stack:pop()
+       else
+               return "return"
+       end
+end
+
+function expr_br(stack, instr)
+       return "br <" .. instr.x.block.label .. ">"
+end
+
+function expr_br_if(stack, instr)
+       return "br_if <" .. instr.x.block.label .. "> (" .. stack:pop() .. ")"
+end
+
+function expr_br_table(stack, instr)
+       error "Cannot handle br_table"
+end
+
+function expr_drop(stack, instr)
+       return "drop (" .. stack:pop() .. ")"
+end
+
+function expr_select(stack, instr)
+       stack:push("select (" .. stack:pop() .. [[) {
+       0: ]] .. stack:pop() .. [[
+       1: ]] .. stack:pop() .. [[
+}]])
+end
+
+function expr_local_get(stack, instr, func)
+       stack:push(func.locals[instr.x + 1].name)
+end
+
+function expr_local_set(stack, instr, func)
+       local varname = func.locals[instr.x + 1].name
+       return varname .. " = " .. stack:pop()
+end
+
+function expr_local_tee(stack, instr, func)
+       local varname = func.locals[instr.x + 1].name
+       stack:push(varname .. " = " .. stack:pop())
+end
+
+function expr_global_get(stack, instr)
+       stack:push("global" .. instr.x)
+end
+
+function expr_global_set(stack, instr)
+       return "global" .. instr.x .. " = " .. stack:pop()
+end
+
+function expr_load(stack, instr)
+       stack:push("[" .. stack:pop() .. "]")
+end
+
+function expr_store(stack, instr)
+       local value = stack:pop()
+       local location = stack:pop()
+       return "[" .. location .. "] <- " .. value
+end
+
+function indent(lines, prefix)
+       local newlines = {}
+       for _, line in ipairs(lines) do
+               table.insert(newlines, prefix .. line)
+       end
+       return newlines
+end
+
+function expr_block(stack, instr, func, mod)
+       local lines = indent(
+               build_instr_list(instr.instrs, func, mod),
+               "  "
+       )
+       table.insert(lines, 1, "block <" .. instr.label .. "> {")
+       table.insert(lines, "}")
+       return lines
+end
+
+function expr_loop(stack, instr, func, mod)
+       local lines = indent(
+               build_instr_list(instr.instrs, func, mod),
+               "  "
+       )
+       table.insert(lines, 1, "loop <" .. instr.label .. "> {")
+       table.insert(lines, "}")
+       return lines
+end
+
+function expr_if(stack, instr, func, mod)
+       local lines = indent(
+               build_instr_list(instr.instrs, func, mod),
+               "  "
+       )
+       table.insert(lines, 1, "if <" .. instr.label .. "> (" .. stack:pop() .. ") {")
+       if #instr.else_instrs > 0 then
+               table.insert(lines, "} else {")
+               local else_lines = indent(
+                       build_instr_list(instr.else_instrs, func, mod),
+                       "  "
+               )
+               for _, line in ipairs(else_lines) do
+                       table.insert(lines, line)
+               end
+       end
+       table.insert(lines, "}")
+       return lines
+end
+
+function expr_call(stack, instr, func, mod)
+       local nparams = #mod.funcs[instr.x].type_.param_types
+       local nreturns = #mod.funcs[instr.x].type_.result_types
+       local name = mod.funcs[instr.x].name
+
+       local res = func_call(name, nparams)(stack, instr, func, mod)
+       if nreturns >= 1 then
+               stack:push(res)
+       else
+               return res
+       end
+end
+
+function call_indirect(stack, instr)
+       error "Cannot handle call indirect"
+end
+
+local expr_generators = {
+--  Name                                       Inputs, Outputs
+       ["unreachable"]                 = { 0, 0 };
+       ["nop"]                                 = { 0, 0 };
+
+       ["block"]                               = expr_block;
+       ["loop"]                                = expr_loop;
+       ["if"]                                  = expr_if;
+
+       ["br"]                                  = expr_br;
+       ["br_if"]                               = expr_br_if;
+       ["br_table"]                    = expr_br_table;
+       ["return"]                              = expr_return;
+       ["call"]                                = expr_call;
+       --call_indirect is dynamic
+
+       ["drop"]                                = expr_drop;
+       ["select"]                              = expr_select;
+
+       ["local.get"]                   = expr_local_get;
+       ["local.set"]                   = expr_local_set;
+       ["local.tee"]                   = expr_local_tee;
+       ["global.get"]                  = expr_global_get;
+       ["global.set"]                  = expr_global_set;
+
+       ["i32.load"]                    = expr_load;
+       ["i64.load"]                    = expr_load;
+       ["f32.load"]                    = expr_load;
+       ["f64.load"]                    = expr_load;
+       ["i32.load8_s"]                 = expr_load;
+       ["i32.load8_u"]                 = expr_load;
+       ["i32.load16_s"]                = expr_load;
+       ["i32.load16_u"]                = expr_load;
+       ["i64.load8_s"]                 = expr_load;
+       ["i64.load8_u"]                 = expr_load;
+       ["i64.load16_s"]                = expr_load;
+       ["i64.load16_u"]                = expr_load;
+       ["i64.load32_s"]                = expr_load;
+       ["i64.load32_u"]                = expr_load;
+       ["i32.store"]                   = expr_store;
+       ["i64.store"]                   = expr_store;
+       ["f32.store"]                   = expr_store;
+       ["f64.store"]                   = expr_store;
+       ["i32.store8"]                  = expr_store;
+       ["i32.store16"]                 = expr_store;
+       ["i64.store8"]                  = expr_store;
+       ["i64.store16"]                 = expr_store;
+       ["i64.store32"]                 = expr_store;
+
+       ["memory.size"]                 = func_call("memory.size", 0);
+       ["memory.grow"]                 = func_call "memory.grow";
+
+       ["i32.const"]                   = const "i32";
+       ["i64.const"]                   = const "i64";
+       ["f32.const"]                   = const "f32";
+       ["f64.const"]                   = const "f64";
+
+       ["i32.eqz"]                             = func_call "eqz";
+       ["i32.eq"]                              = binop "==";
+       ["i32.ne"]                              = binop "!=";
+       ["i32.lt_s"]                    = binop "<";
+       ["i32.lt_u"]                    = binop "<";
+       ["i32.gt_s"]                    = binop ">";
+       ["i32.gt_u"]                    = binop ">";
+       ["i32.le_s"]                    = binop "<=";
+       ["i32.le_u"]                    = binop "<=";
+       ["i32.ge_s"]                    = binop ">=";
+       ["i32.ge_u"]                    = binop ">=";
+       ["i64.eqz"]                             = func_call "eqz";
+       ["i64.eq"]                              = binop "==";
+       ["i64.ne"]                              = binop "!=";
+       ["i64.lt_s"]                    = binop "<";
+       ["i64.lt_u"]                    = binop "<";
+       ["i64.gt_s"]                    = binop ">";
+       ["i64.gt_u"]                    = binop ">";
+       ["i64.le_s"]                    = binop "<=";
+       ["i64.le_u"]                    = binop "<=";
+       ["i64.ge_s"]                    = binop ">=";
+       ["i64.ge_u"]                    = binop ">=";
+       ["f32.eq"]                              = binop "==";
+       ["f32.ne"]                              = binop "!=";
+       ["f32.lt"]                              = binop "<";
+       ["f32.gt"]                              = binop ">";
+       ["f32.le"]                              = binop "<=";
+       ["f32.ge"]                              = binop ">=";
+       ["f64.eq"]                              = binop "==";
+       ["f64.ne"]                              = binop "!=";
+       ["f64.lt"]                              = binop "<";
+       ["f64.gt"]                              = binop ">";
+       ["f64.le"]                              = binop "<=";
+       ["f64.ge"]                              = binop ">=";
+       ["i32.clz"]                             = func_call "clz";
+       ["i32.ctz"]                             = func_call "ctz";
+       ["i32.popcnt"]                  = func_call "popcnt";
+       ["i32.add"]                             = binop "+";
+       ["i32.sub"]                             = binop "-";
+       ["i32.mul"]                             = binop "*";
+       ["i32.div_s"]                   = binop "/";
+       ["i32.div_u"]                   = binop "/";
+       ["i32.rem_s"]                   = binop "%";
+       ["i32.rem_u"]                   = binop "%";
+       ["i32.and"]                             = binop "&";
+       ["i32.or"]                              = binop "|";
+       ["i32.xor"]                             = binop "^";
+       ["i32.shl"]                             = binop "<<";
+       ["i32.shr_s"]                   = binop ">>";
+       ["i32.shr_u"]                   = binop ">>>";
+       ["i32.rotl"]                    = binop "rotl";
+       ["i32.rotr"]                    = binop "rotr";
+       ["i64.clz"]                             = func_call "clz";
+       ["i64.ctz"]                             = func_call "ctz";
+       ["i64.popcnt"]                  = func_call "popcnt";
+       ["i64.add"]                             = binop "+";
+       ["i64.sub"]                             = binop "-";
+       ["i64.mul"]                             = binop "*";
+       ["i64.div_s"]                   = binop "/";
+       ["i64.div_u"]                   = binop "/";
+       ["i64.rem_s"]                   = binop "%";
+       ["i64.rem_u"]                   = binop "%";
+       ["i64.and"]                             = binop "&";
+       ["i64.or"]                              = binop "|";
+       ["i64.xor"]                             = binop "^";
+       ["i64.shl"]                             = binop "<<";
+       ["i64.shr_s"]                   = binop ">>";
+       ["i64.shr_u"]                   = binop ">>>";
+       ["i64.rotl"]                    = binop "rotl";
+       ["i64.rotr"]                    = binop "rotr";
+       ["f32.abs"]                             = func_call "abs";
+       ["f32.neg"]                             = func_call "neg"; -- TODO: make this better
+       ["f32.ceil"]                    = func_call "ceil";
+       ["f32.floor"]                   = func_call "floor";
+       ["f32.trunc"]                   = func_call "trunc";
+       ["f32.nearest"]                 = func_call "nearest";
+       ["f32.sqrt"]                    = func_call "sqrt";
+       ["f32.add"]                             = binop "+";
+       ["f32.sub"]                             = binop "-";
+       ["f32.mul"]                             = binop "*";
+       ["f32.div"]                             = binop "/";
+       ["f32.min"]                             = func_call("min", 2);
+       ["f32.max"]                             = func_call("max", 2);
+       ["f32.copysign"]                = func_call("copysign", 2);
+       ["f64.abs"]                             = func_call "abs";
+       ["f64.neg"]                             = func_call "neg";
+       ["f64.ceil"]                    = func_call "ceil";
+       ["f64.floor"]                   = func_call "floor";
+       ["f64.trunc"]                   = func_call "trunc";
+       ["f64.nearest"]                 = func_call "nearest";
+       ["f64.sqrt"]                    = func_call "sqrt";
+       ["f64.add"]                             = binop "+";
+       ["f64.sub"]                             = binop "-";
+       ["f64.mul"]                             = binop "*";
+       ["f64.div"]                             = binop "/";
+       ["f64.min"]                             = func_call("min", 2);
+       ["f64.max"]                             = func_call("max", 2);
+       ["f64.copysign"]                = func_call("copysign", 2);
+
+       ["i32.wrap_i64"]                = cast "i32";
+       ["i32.trunc_f32_s"]             = cast "i32";
+       ["i32.trunc_f32_u"]             = cast "i32";
+       ["i32.trunc_f64_s"]             = cast "i32";
+       ["i32.trunc_f64_u"]             = cast "i32";
+       ["i64.extend_i32_s"]    = cast "i64";
+       ["i64.extend_i32_u"]    = cast "i64";
+       ["i64.trunc_f32_s"]             = cast "i64";
+       ["i64.trunc_f32_u"]             = cast "i64";
+       ["i64.trunc_f64_s"]             = cast "i64";
+       ["i64.trunc_f64_u"]             = cast "i64";
+       ["f32.convert_i32_s"]   = cast "f32";
+       ["f32.convert_i32_u"]   = cast "f32";
+       ["f32.convert_i64_s"]   = cast "f32";
+       ["f32.convert_i64_u"]   = cast "f32";
+       ["f32.demote_f64"]              = cast "f32";
+       ["f64.convert_i32_s"]   = cast "f64";
+       ["f64.convert_i32_u"]   = cast "f64";
+       ["f64.convert_i64_s"]   = cast "f64";
+       ["f64.convert_i64_u"]   = cast "f64";
+       ["f64.promote_f32"]             = cast "f64";
+       ["i32.reinterpret_f32"] = cast "i32";
+       ["i64.reinterpret_f64"] = cast "i64";
+       ["f32.reinterpret_i32"] = cast "f32";
+       ["f64.reinterpret_i64"] = cast "f64";
+}
+
 
 -- This works... ish but we should use proper symbols (+-*/) and other things
-function build_instr_list(instrs)
+function build_instr_list(instrs, func, mod)
        local lines = {}
        local stack = Stack()
 
        for _, instr in ipairs(instrs) do
-               local instr_name = instr[1]
-               if stack_opts[instr_name] then
-                       -- For now, everything will be built in a function calling syntax
-                       local str = ""
-                       for i=1, stack_opts[instr_name][1] do
-                               str = str .. ", "
-                               str = str .. stack:pop()
-                       end
-
-                       str = instr_name .. "(" .. str:sub(3) .. ")"
-
-                       if stack_opts[instr_name][2] == 1 then
-                               stack:push(str)
-                       else
-                               table.insert(lines, str)
-                       end
-               end
-
-               if instr_name == "block" or instr_name == "loop" then
-                       -- have a .instrs which is the instruction list
-                       table.insert(lines, instr_name .. " { [" .. instr.label .. "]")
-                       local instr_lines = build_instr_list(instr.instrs)
-                       for _, line in ipairs(instr_lines) do
-                               table.insert(lines, "  " .. line)
-                       end
-                       table.insert(lines, "}")
-               end
-               if instr_name == "if" then
-                       local str = instr_name .. " ("
-                       str = str .. stack:pop()
-                       str = str .. " ) { [" .. instr.label .. "]"
-                       table.insert(lines, str)
-
-                       local instr_lines = build_instr_list(instr.instrs)
-                       for _, line in ipairs(instr_lines) do
-                               table.insert(lines, "  " .. line)
-                       end
-
-                       if instr_name == "if" and #instr.else_instrs > 0 then
-                               table.insert(lines, "} else {")
-                               instr_lines = build_instr_list(instr.else_instrs)
-                               for _, line in ipairs(instr_lines) do
-                                       table.insert(lines, "  " .. line)
-                               end
-                       end
-
-                       table.insert(lines, "}")
+               local res = expr_generators[instr[1]](stack, instr, func, mod)
+               if type(res) == "string" then
+                       table.insert(lines, res)
+               elseif type(res) == "table" then
+                       for _, line in ipairs(res) do table.insert(lines, line) end
                end
+       end
 
-               if instr_name == "call" then
+       if stack:size() ~= 0 then
+               while not stack:empty() do
+                       table.insert(lines, stack:pop())
                end
        end
 
@@ -273,7 +583,7 @@ function build_instr_list(instrs)
 end
 
 function build_expr(wasm_func, wasm_mod)
-       local lines = build_instr_list(wasm_func.body)
+       local lines = build_instr_list(wasm_func.body, wasm_func, wasm_mod)
 
        return lines
 end
index 033ef797eafe09e0ee4a356c8031d676d1972ed8..10362d8f7a02db451da4b004789e8fbdbe43ca66 100644 (file)
@@ -10,10 +10,14 @@ import {
 
 local mod = decompile(arg[1])
 mod = analyze(mod)
+-- pprint(mod)
 
 for i, func in ipairs(mod.funcs) do
        if func.body then
                print(func.name .. " -----------------------------------")
-               pprint(build_expr(func, mod))
+               local res = build_expr(func, mod)
+               for _, r in ipairs(res) do
+                       print(r)
+               end
        end
 end