Added back drawing lines for function references
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 24 Apr 2020 20:21:31 +0000 (15:21 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 24 Apr 2020 20:21:31 +0000 (15:21 -0500)
app.lua
conf.lua
src/globals.lua
src/ui/components.lua
src/ui/text.lua
src/wasm/decompile.lua
src/wasm/parse.lua
src/wasm/text.lua
testing.lua

diff --git a/app.lua b/app.lua
index 9b3e2234fa0c2ebe0098b867381bcb067ef5a304..b79f72d5ac8b90e7daa2ad909b03c6a2e887bea2 100644 (file)
--- 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)
index 752af6d28097aa6f3ef0da73c0856e55295982bf..1848af32ed24bb0481817592c4c9db27c8fd66ba 100644 (file)
--- 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
index 424eb621e3d49782d18eea7a4728d57d876ab31a..63a4d66ef46db45f38859b2cf1f088769f172cb8 100644 (file)
@@ -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
index 97940a4f1d14eb8f504efed8dd16ce3486cdbbeb..92065a981600d817e28621e420489fca3b01b0ed 100644 (file)
@@ -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;
index 53ca9790eb29191082c80e8a07caa0c5dda46740..78d6f965b3dd04f1e2cefda2e47d341ff5513bb1 100644 (file)
@@ -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;
+}
index 40a48b110c35fd23b780f00ab3892dc0ef35d97d..2ebf6699486dc2ada17923be49e10f5b560f918a 100644 (file)
@@ -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
index 0f74b4eaba2894bcc314a0fb4907ef9e9e7d589c..9e8fad9305f8c99c3e6baf6ff90feca759f42074 100644 (file)
@@ -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, {
index 881144a7ec6a83eec4880e8fdc69682cbab91f8a..ddd1144710531d9cbc25b24491db7ded940c3e11 100644 (file)
@@ -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 {
index 51caa053bc180f13a0eaee5332e00bde2b03e5a5..c3cc4348eafff1a656546f71ffa6fb48b29bc92b 100644 (file)
@@ -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