From 79f2db371d0a95aeb0e3dfa24ea4fc3e0bc84b31 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Fri, 24 Apr 2020 15:21:31 -0500 Subject: [PATCH] Added back drawing lines for function references --- app.lua | 27 ++++---- conf.lua | 2 +- src/globals.lua | 5 +- src/ui/components.lua | 43 ++++++++++++ src/ui/text.lua | 93 +++++++++---------------- src/wasm/decompile.lua | 29 ++++---- src/wasm/parse.lua | 2 - src/wasm/text.lua | 152 ++++++++++++++++------------------------- testing.lua | 5 +- 9 files changed, 173 insertions(+), 185 deletions(-) diff --git a/app.lua b/app.lua index 9b3e223..b79f72d 100644 --- a/app.lua +++ b/app.lua @@ -28,6 +28,9 @@ function init() layer = 100; } + globals.ui.function_blocks_container = ui.make_element "node" + + ui.insert_child(globals.ui.scrolling_area, globals.ui.function_blocks_container) ui.insert_child(globals.ui.root, globals.ui.scrolling_area) end @@ -51,24 +54,24 @@ function open_file(path) globals.wasm_module = wasm_analyze(globals.wasm_module) -- Delete all old children - for _, chld in ipairs(globals.ui.scrolling_area.children) do + for _, chld in ipairs(globals.ui.function_blocks_container.children) do chld.remove = true end - ui.fire_propagating_event(globals.ui.scrolling_area, "dummy") + ui.fire_propagating_event(globals.ui.function_blocks_container, "dummy") local i = 0 for _, func in ipairs(globals.wasm_module.funcs) do - if not func.imported then - ui.insert_child( - globals.ui.scrolling_area, - with(ui.make_element("function_block", func, globals.wasm_module)) { - rect = Rectangle(0, i * 22, 400, 400); - layer = #globals.wasm_module.funcs - i - }) - - i = i + 1 - end + ui.insert_child( + globals.ui.function_blocks_container, + with(ui.make_element("function_block", func, globals.wasm_module)) { + rect = Rectangle(math.random() * 1000, math.random() * 1000, 400, 400); + layer = #globals.wasm_module.funcs - i + }) + + i = i + 1 end + + ui.insert_child(globals.ui.scrolling_area, ui.make_element "function_ref_lines") end function update(dt) diff --git a/conf.lua b/conf.lua index 752af6d..1848af3 100644 --- a/conf.lua +++ b/conf.lua @@ -40,7 +40,7 @@ local COLOR_SCHEMES = { } function love.conf(t) - t.window.title = "TBD" + t.window.title = "WebAssembly-32 Disassembler and Analyzer" t.window.width = 1200 t.window.height = 900 t.window.resizable = true diff --git a/src/globals.lua b/src/globals.lua index 424eb62..63a4d66 100644 --- a/src/globals.lua +++ b/src/globals.lua @@ -1,7 +1,8 @@ return { ui = { - root = {}; -- The root ui element - scrolling_area = {}; -- The scrolling/panning area of the ui + root = {}; -- The root ui element + scrolling_area = {}; -- The scrolling/panning area of the ui + function_blocks_container = {}; -- Dummy node for containing all function blocks }; wasm_module = {}; -- The active decompiled webassembly module diff --git a/src/ui/components.lua b/src/ui/components.lua index 97940a4..92065a9 100644 --- a/src/ui/components.lua +++ b/src/ui/components.lua @@ -3,6 +3,7 @@ import { ui_text = "src.ui.text:"; Rectangle = "src.utils:Rectangle"; + draw_arrow = "src.utils:draw_arrow"; wasm_text = "src.wasm.text"; wasm_decompile = "src.wasm.decompile"; @@ -422,12 +423,54 @@ function scrolling_area:postdraw() love.graphics.setScissor() end + +local function_ref_lines = {} +function function_ref_lines:init() + self.layer = -1 + + local func_blocks = globals.ui.function_blocks_container.children + local funcs = {} + for _, f in ipairs(func_blocks) do + print(f.func.funcidx - 1) + funcs[f.func.funcidx - 1] = f + end + + print "------------" + + self.arrows = {} + for _, from in ipairs(func_blocks) do + if from.func.callees then + for _, to in ipairs(from.func.callees) do + table.insert(self.arrows, { from = from; to = funcs[to]; color = COLORS.secondary }) + end + end + + if from.func.callers then + for _, to in ipairs(from.func.callers) do + table.insert(self.arrows, { from = funcs[to - 1]; to = from; color = COLORS.secondary_dark }) + end + end + end +end + +function function_ref_lines:predraw() + for _, arr in ipairs(self.arrows) do + local from = arr.from.rect + local to = arr.to.rect + love.graphics.setColor(arr.color) + draw_arrow( + from.x + from.w / 2, from.y + from.h / 2, + to.x + to.w / 2, to.y + to.h / 2) + end +end + ui.register_component "rect" (rect) ui.register_component "drag_rect" (drag_rect) ui.register_component "button" (button) ui.register_component "function_block" (function_block) ui.register_component "function_context_menu" (function_context_menu) ui.register_component "scrolling_area" (scrolling_area) +ui.register_component "function_ref_lines" (function_ref_lines) return module { rect = rect; diff --git a/src/ui/text.lua b/src/ui/text.lua index 53ca979..78d6f96 100644 --- a/src/ui/text.lua +++ b/src/ui/text.lua @@ -1,4 +1,6 @@ -import { } +import { + pprint = "lualib.pprint" +} Text = {} @@ -59,64 +61,6 @@ function Text.render_lines(x, y, lines, line_number) end --- TODO: Rewrite because this is bad -function Text.render_text_old(x, y, text) - local curr_font = love.graphics.getFont() - - local origx = x - local fheight = curr_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, curr_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 + curr_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 - --- For printing formatted text to the console -function Text.print_text(text) - error "Unimplemented" -end - FONTS = {} function Text.register_font(name, size) local fonts = FONTS[name] or {} @@ -130,4 +74,33 @@ function Text.set_font(name, size) end end -return module { Text } +class "TextBuilder" { + init = function(self) + self.lines = { {} } + end; + + newline = function(self, indent) + table.insert(self.lines, { + indent = indent; + }) + end; + + text = function(self, text, fg, bg) + table.insert(self.lines[#self.lines], { + text; + fg = fg; + bg = bg; + }) + end; + + append = function(self, other_builder) + for _, line in ipairs(other_builder.lines) do + table.insert(self.lines, line) + end + end; +} + +return module { + Text; + TextBuilder = TextBuilder; +} diff --git a/src/wasm/decompile.lua b/src/wasm/decompile.lua index 40a48b1..2ebf669 100644 --- a/src/wasm/decompile.lua +++ b/src/wasm/decompile.lua @@ -26,6 +26,9 @@ This should be quick, just need to look up all the instructions --]] + +-- TODO: Convert this to the new layout + local build_instr_list function binop(op) @@ -86,16 +89,16 @@ end function expr_br_table(stack, instr) local lines = {} - table.insert(lines, "br_table (" .. stack:pop() .. ") {") + table.insert(lines, { "br_table (" .. stack:pop() .. ") {" }) local mappings = {} for i, lab in ipairs(instr.labels) do - table.insert(mappings, tostring(i - 1) .. " => <" .. lab.block.label .. ">") + table.insert(mappings, { tostring(i - 1) .. " => <" .. lab.block.label .. ">" }) end - table.insert(mappings, "default => <" .. instr.labeln.block.label .. ">") + table.insert(mappings, { "default => <" .. instr.labeln.block.label .. ">" }) for _, line in ipairs(indent(mappings, " ")) do table.insert(lines, line) end - table.insert(lines, "}") + table.insert(lines, { "}" }) return lines end @@ -145,7 +148,7 @@ end function indent(lines, prefix) local newlines = {} for _, line in ipairs(lines) do - table.insert(newlines, prefix .. line) + table.insert(newlines, line) end return newlines end @@ -155,8 +158,8 @@ function expr_block(stack, instr, func, mod) build_instr_list(instr.instrs, func, mod), " " ) - table.insert(lines, 1, "block <" .. instr.label .. "> {") - table.insert(lines, "}") + table.insert(lines, 1, { "block <" .. instr.label .. "> {" }) + table.insert(lines, { "}" }) if #instr.rt == 0 then return lines else @@ -169,8 +172,8 @@ function expr_loop(stack, instr, func, mod) build_instr_list(instr.instrs, func, mod), " " ) - table.insert(lines, 1, "loop <" .. instr.label .. "> {") - table.insert(lines, "}") + table.insert(lines, 1, { "loop <" .. instr.label .. "> {" }) + table.insert(lines, { "}" }) if #instr.rt == 0 then return lines else @@ -183,9 +186,9 @@ function expr_if(stack, instr, func, mod) build_instr_list(instr.instrs, func, mod), " " ) - table.insert(lines, 1, "if <" .. instr.label .. "> (" .. stack:pop() .. ") {") + table.insert(lines, 1, { "if <" .. instr.label .. "> (" .. stack:pop() .. ") {" }) if #instr.else_instrs > 0 then - table.insert(lines, "} else {") + table.insert(lines, { "} else {" }) local else_lines = indent( build_instr_list(instr.else_instrs, func, mod), " " @@ -194,7 +197,7 @@ function expr_if(stack, instr, func, mod) table.insert(lines, line) end end - table.insert(lines, "}") + table.insert(lines, { "}" }) if #instr.rt == 0 then return lines else @@ -421,7 +424,7 @@ function build_instr_list(instrs, func, mod) for _, instr in ipairs(instrs) do local res = expr_generators[instr[1]](stack, instr, func, mod) if type(res) == "string" then - table.insert(lines, res) + table.insert(lines, { res }) elseif type(res) == "table" then for _, line in ipairs(res) do table.insert(lines, line) end end diff --git a/src/wasm/parse.lua b/src/wasm/parse.lua index 0f74b4e..9e8fad9 100644 --- a/src/wasm/parse.lua +++ b/src/wasm/parse.lua @@ -577,8 +577,6 @@ 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, { diff --git a/src/wasm/text.lua b/src/wasm/text.lua index 881144a..ddd1144 100644 --- a/src/wasm/text.lua +++ b/src/wasm/text.lua @@ -1,5 +1,6 @@ import { pprint = "lualib.pprint"; + TextBuilder = "src.ui.text:TextBuilder"; } -- The current line number during processing @@ -28,7 +29,7 @@ function build_type(type_, color) text = text .. "void" end - return { text; fg = color } + return text end -- Ctx -> List TextObj @@ -42,121 +43,89 @@ function build_header(ctx) table.insert(text, { ctx.func.name, fg = ctx.colors.keyword }) table.insert(text, { ": ", fg = ctx.colors.code }) - table.insert(text, build_type(ctx.func.type_, ctx.colors.code)) + table.insert(text, { build_type(ctx.func.type_), fg = ctx.colors.code }) return text end --- This will be moved into ui/text -function add_line_number(textlist, colors, newline) - if newline == nil then newline = true end - - table.insert(textlist, { newline = newline }) - table.insert(textlist, { - text = ("%4d"):format(line), - color = colors.code, - background = colors.dark_background, - post_indent = true - }) - table.insert(textlist, { text = " ", post_indent = true }) - - line = line + 1 -end - -- Ctx -> Instr -> List List TextObj function instr_to_text(ctx, instr) - local lines = {} + local tb = TextBuilder() if instr[1] == "block" then - table.insert(lines, { indent = 1 }) + tb:newline(1) for i, ins in ipairs(instr.instrs) do - local res = instr_to_text(ctx, ins) - table.append(lines, res) + local otb = instr_to_text(ctx, ins) + tb:append(otb) end - table.insert(lines, { - indent = -1; - { "label ", fg = ctx.colors.jumppoint }; - { instr.label, fg = ctx.colors.value }; - }) + tb:newline(-1) + tb:text("label ", ctx.colors.jumppoint) + tb:text(instr.label, ctx.colors.value) elseif instr[1] == "loop" then - table.insert(lines, { - { "loop ", fg = ctx.colors.jumppoint }; - { instr.label, fg = ctx.colors.value }; - }) - table.insert(lines, { indent = 1 }) + tb:text("label ", ctx.colors.jumppoint) + tb:text(instr.label, ctx.colors.value) + tb:newline(1) for i, ins in ipairs(instr.instrs) do - local res = instr_to_text(ctx, ins) - table.append(lines, res) + local otb = instr_to_text(ctx, ins) + tb:append(otb) end - table.insert(lines, { indent = -1 }) + tb:newline(-1) elseif instr[1] == "if" then - table.insert(lines, { - { "if ", fg = ctx.colors.jumppoint }; - { instr.label, fg = ctx.colors.value }; - }) - table.insert(lines, { indent = 1 }) + tb:text("if ", ctx.colors.jumppoint) + tb:text(instr.label, ctx.colors.value) + tb:newline(1) for i, ins in ipairs(instr.instrs) do - local res = instr_to_text(ctx, ins) - table.append(lines, res) + local otb = instr_to_text(ctx, ins) + tb:append(otb) end - table.insert(lines, { indent = -1 }) + tb:newline(-1) if #instr.else_instrs > 0 then - table.insert(lines, { "else", fg = ctx.colors.jumppoint }) - table.insert(lines, { indent = 1 }) + tb:text("else ", ctx.colors.jumppoint) + tb:newline(1) for i, ins in ipairs(instr.instrs) do - local res = instr_to_text(ctx, ins) - table.append(lines, res) + local otb = instr_to_text(ctx, ins) + tb:append(otb) end - table.insert(lines, { indent = -1 }) + tb:newline(-1) end elseif instr[1] == "call" then - table.insert(lines, { - { "call ", fg = ctx.colors.keyword }, - { ctx.mod.funcs[instr.x].name, fg = ctx.colors.value } - }) + tb:text("call ", ctx.colors.keyword) + tb:text(ctx.mod.funcs[instr.x].name, ctx.colors.value) elseif instr[1] == "call_indirect" then - table.insert(lines, { - { ("%s [%d] "):format(instr[1], instr.table), fg = ctx.colors.keyword }, - build_type(ctx.mod.types.contents[instr.x + 1], ctx.colors.code) - }) + tb:text(("%s [%d] "):format(instr[1], instr.table), ctx.colors.keyword) + tb:text(build_type(ctx.mod.types.contents[instr.x + 1], ctx.colors.code)) elseif instr[1]:match("local") then - table.insert(lines, { - { instr[1] .. " ", fg = ctx.colors.keyword }, - { ctx.func.locals[instr.x + 1].name, fg = ctx.colors.value } - }) + tb:text(instr[1] .. " ", ctx.colors.keyword) + tb:text(ctx.func.locals[instr.x + 1].name, ctx.colors.value) else - local line = {} - table.insert(line, { instr[1] .. " ", fg = ctx.colors.keyword }) + tb:text(instr[1] .. " ", ctx.colors.keyword) if instr.x then if type(instr.x) == "table" then if instr.x[1] == "memarg" then - table.insert(line, { - ("align=0x%02x offset=0x%04x"):format(instr.x.align, instr.x.offset); - fg = ctx.colors.value - }) + tb:text( + ("align=0x%02x offset=0x%04x"):format(instr.x.align, instr.x.offset), + ctx.colors.value) end if instr.x[1] == "labelidx" then if instr.x.block then - table.insert(line, { instr.x.block.label .. " ", fg = ctx.colors.value }) + tb:text(instr.x.block.label .. " ", ctx.colors.value) end - table.insert(line, { ("[0x%02x]"):format(instr.x.labelidx), fg = ctx.colors.code }) + tb:text(("[0x%02x]"):format(instr.x.labelidx), ctx.colors.code) end else - table.insert(line, { tostring(instr.x), fg = ctx.colors.value }) + tb:text(tostring(instr.x), ctx.colors.value) end end - - lines = { line } end - return lines + return tb end @@ -165,34 +134,27 @@ function build_body(ctx) return { { "Imported function", fg = ctx.colors.code } } end - local lines = {} - table.insert(lines, { - { " Locals:", fg = ctx.colors.keyword } - }) - table.insert(lines, { indent = 1 }) + local tb = TextBuilder() + tb:text(" Locals:", ctx.colors.keyword) + tb:newline(1) for _, loc in ipairs(ctx.func.locals) do - table.insert(lines, { - { loc.type_ .. " " .. loc.name, fg = ctx.colors.code } - }) + tb:text(loc.type_ .. " " .. loc.name, ctx.colors.code) + tb:newline() end if #ctx.func.locals == 0 then - table.insert(lines, { - { "no locals", fg = ctx.colors.code } - }) + tb:text("no locals", ctx.colors.code) end - table.insert(lines, { indent = -1 }) - table.insert(lines, { - { " Body:", fg = ctx.colors.keyword } - }) + tb:newline(-1) + tb:text(" Body:", ctx.colors.keyword) - local code_lines = {} + local code_tb = TextBuilder() for _, instr in ipairs(ctx.func.body) do - table.append(code_lines, instr_to_text(ctx, instr)) + code_tb:append(instr_to_text(ctx, instr)) end - return lines, code_lines + return tb, code_tb end function generate_text_format(func, mod, colors, line_start) @@ -202,8 +164,14 @@ function generate_text_format(func, mod, colors, line_start) colors = colors; } local heading = build_header(ctx) - local body_preface, body = build_body(ctx) - return { heading }, body_preface, body + local body_preface, body + if func.imported then + body_preface = {} + body = { { "Imported function" } } + else + body_preface, body = build_body(ctx) + end + return { heading }, body_preface.lines, body.lines end return module { diff --git a/testing.lua b/testing.lua index 51caa05..c3cc434 100644 --- a/testing.lua +++ b/testing.lua @@ -4,6 +4,7 @@ import { parse = "src.wasm.parse:"; analyze = "src.wasm.analyze:"; decompile_func = "src.wasm.decompile:decompile_func"; + Text = "src.ui.text:"; pprint = "lualib.pprint"; } @@ -16,8 +17,6 @@ for i, func in pairs(mod.funcs) do if func.body then print(func.name .. " -----------------------------------") local res = decompile_func(func, mod) - for _, r in ipairs(res) do - print(r) - end + Text.print_lines(res, io.write) end end -- 2.25.1