From 9808ed64b8dda49c6c9691d81d6231dc72e457a1 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Sun, 1 Mar 2020 10:53:29 -0600 Subject: [PATCH] Fixed some bugs and added features --- conf.lua | 47 ++++++ docs/todo | 8 +- main.lua | 201 +++++------------------- ui.lua => src/ui/ui.lua | 20 ++- utils.lua => src/utils.lua | 0 decompile.lua => src/wasm/decompile.lua | 11 +- src/wasm/text.lua | 162 +++++++++++++++++++ 7 files changed, 267 insertions(+), 182 deletions(-) rename ui.lua => src/ui/ui.lua (95%) rename utils.lua => src/utils.lua (100%) rename decompile.lua => src/wasm/decompile.lua (98%) create mode 100644 src/wasm/text.lua diff --git a/conf.lua b/conf.lua index 3521b8e..e01fe28 100644 --- a/conf.lua +++ b/conf.lua @@ -1,6 +1,53 @@ + +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; +} diff --git a/docs/todo b/docs/todo index f3fb67e..52e7488 100644 --- a/docs/todo +++ b/docs/todo @@ -1,2 +1,6 @@ -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 diff --git a/main.lua b/main.lua index 67be148..81c37b3 100644 --- a/main.lua +++ b/main.lua @@ -2,179 +2,49 @@ 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) @@ -187,22 +57,25 @@ class "FunctionBlock" [DraggableRect] { 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 @@ -221,14 +94,14 @@ function love.load() 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 diff --git a/ui.lua b/src/ui/ui.lua similarity index 95% rename from ui.lua rename to src/ui/ui.lua index 1d3f1b7..3b6b7c4 100644 --- a/ui.lua +++ b/src/ui/ui.lua @@ -1,8 +1,8 @@ import { - Rectangle = "utils:Rectangle"; - uuid = "utils:uuidv4"; - revipairs = "utils:revipairs"; - scissor_points = "utils:scissor_points"; + Rectangle = "src.utils:Rectangle"; + uuid = "src.utils:uuidv4"; + revipairs = "src.utils:revipairs"; + scissor_points = "src.utils:scissor_points"; } class "Ui" { @@ -246,9 +246,7 @@ class "ScrollingUiRegion" [UiRegion] { 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; @@ -273,7 +271,7 @@ class "ScrollingUiRegion" [UiRegion] { 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 + if self.zoom <= (1 / 1.05) ^ 30 then self.zoom = (1 / 1.05) ^ 30 end end end; @@ -322,11 +320,11 @@ class "ScrollingUiRegion" [UiRegion] { 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) + 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 = -40, 80 do - love.graphics.line(-self.rect.w, y * spacing + yoff, self.rect.w * 2, y * spacing + yoff) + 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) diff --git a/utils.lua b/src/utils.lua similarity index 100% rename from utils.lua rename to src/utils.lua diff --git a/decompile.lua b/src/wasm/decompile.lua similarity index 98% rename from decompile.lua rename to src/wasm/decompile.lua index b08a4a1..08534db 100644 --- a/decompile.lua +++ b/src/wasm/decompile.lua @@ -8,9 +8,9 @@ import { parse_helper = "parse_helper"; pprint = "lualib.pprint"; - build_str = "utils:build_str"; - random_str = "utils:random_str"; - Stack = "utils:Stack"; + build_str = "src.utils:build_str"; + random_str = "src.utils:random_str"; + Stack = "src.utils:Stack"; } function parse_valtype(r) @@ -199,8 +199,8 @@ function parse_instr(r) [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 } + -- 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; @@ -406,6 +406,7 @@ 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 diff --git a/src/wasm/text.lua b/src/wasm/text.lua new file mode 100644 index 0000000..ed9d61e --- /dev/null +++ b/src/wasm/text.lua @@ -0,0 +1,162 @@ +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; +} + -- 2.25.1