From: Brendan Hansen Date: Thu, 16 Apr 2020 21:33:27 +0000 (-0500) Subject: Rewrote text rendering to be slightly better X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=b68c8cba5bac020029e2f601a4ed5e2bca303da6;p=wasm-analyzer.git Rewrote text rendering to be slightly better --- diff --git a/conf.lua b/conf.lua index 8b13689..752af6d 100644 --- a/conf.lua +++ b/conf.lua @@ -50,5 +50,5 @@ end return { COLOR_SCHEMES = COLOR_SCHEMES; - COLOR_SCHEME = COLOR_SCHEMES.DARK; + COLOR_SCHEME = COLOR_SCHEMES.LIGHT; } diff --git a/src/ui/components.lua b/src/ui/components.lua index 74588c9..97940a4 100644 --- a/src/ui/components.lua +++ b/src/ui/components.lua @@ -116,9 +116,10 @@ local function_block = { extends = "drag_rect" } function function_block:init(wasm_function) self.func = wasm_function - local header_text, body_text = wasm_text.generate_text_format(self.func, globals.wasm_module, COLORS) + local header_text, body_text, code_text = wasm_text.generate_text_format(self.func, globals.wasm_module, COLORS) self.header_text = header_text self.body_text = body_text + self.code_text = code_text self.scroll = 0 self.body_visible = true @@ -226,11 +227,15 @@ function function_block:predraw() love.graphics.setColor(COLORS.dark_background) love.graphics.rectangle("fill", 2, 2, self.rect.w - 4, 18) - local x, y = ui_text.render_text(2, 2, self.header_text) + local x, y = ui_text.render_lines(2, 2, self.header_text) if self.body_visible and self.body_text then love.graphics.intersectScissor(2, y - 4, self.rect.w - 4, self.rect.h - 22) - ui_text.render_text(2, y - self.scroll, self.body_text) + x, y = ui_text.render_lines(2, y - self.scroll, self.body_text) + x, y = ui_text.render_lines(2, y, self.code_text, { + fg = COLORS.code, + bg = COLORS.dark_background + }) end love.graphics.pop() diff --git a/src/ui/text.lua b/src/ui/text.lua index 7a490fb..53ca979 100644 --- a/src/ui/text.lua +++ b/src/ui/text.lua @@ -1,11 +1,66 @@ import { } -FONTS = {} - Text = {} +function Text.render_lines(x, y, lines, line_number) + local font = love.graphics.getFont() + + local origx = x + local font_height = font:getHeight() + local tab_width = font:getWidth " " + local indent = 0 + local had_text = false + + local line_num = 1 + + for _, line in ipairs(lines) do + x = origx + if line_number then + love.graphics.setColor(line_number.bg) + love.graphics.rectangle("fill", x, y, tab_width, font_height + 2) + love.graphics.setColor(line_number.fg) + love.graphics.print(("%4d"):format(line_num), x, y) + x = x + tab_width + 10 + line_num = line_num + 1 + end + + if line.indent then indent = indent + line.indent end + x = x + tab_width * indent + + + had_text = false + for _, text_obj in ipairs(line) do + local text = text_obj[1] + -- TODO: This isn't great but it works for now + if not text then goto continue end + had_text = true + + local width = font:getWidth(text) + + if text_obj.bg then + love.graphics.setColor(text_obj.bg) + love.graphics.rectangle("fill", x, y, width, font_height + 2); + end + + if text_obj.fg then love.graphics.setColor(text_obj.fg) end + + love.graphics.print(text, x, y) + + x = x + width + ::continue:: + end + + if had_text then + y = y + font_height + 2 + end + end + + return x, y +end + + -- TODO: Rewrite because this is bad -function Text.render_text(x, y, text) +function Text.render_text_old(x, y, text) local curr_font = love.graphics.getFont() local origx = x @@ -57,6 +112,12 @@ function Text.render_text(x, y, text) 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 {} fonts[size] = love.graphics.newFont(("res/%s.ttf"):format(name), size) diff --git a/src/utils.lua b/src/utils.lua index 0889f6f..176b891 100644 --- a/src/utils.lua +++ b/src/utils.lua @@ -71,6 +71,12 @@ function revipairs(t) return ripairs_it, t, max end +function table.append(orig, new) + for _, e in ipairs(new) do + table.insert(orig, e) + end +end + class "Rectangle" { init = function(self, x, y, w, h) self.x = x diff --git a/src/wasm/decompile.lua b/src/wasm/decompile.lua index e4bf5e0..40a48b1 100644 --- a/src/wasm/decompile.lua +++ b/src/wasm/decompile.lua @@ -116,12 +116,12 @@ end function expr_local_set(stack, instr, func) local varname = func.locals[instr.x + 1].name - return varname .. " = " .. stack:pop() + 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()) + stack:push(varname .. " <- " .. stack:pop()) end function expr_global_get(stack, instr) @@ -129,7 +129,7 @@ function expr_global_get(stack, instr) end function expr_global_set(stack, instr) - return "global" .. instr.x .. " = " .. stack:pop() + return "global" .. instr.x .. " <- " .. stack:pop() end function expr_load(stack, instr) diff --git a/src/wasm/text.lua b/src/wasm/text.lua index 9abbbfb..881144a 100644 --- a/src/wasm/text.lua +++ b/src/wasm/text.lua @@ -8,42 +8,45 @@ import { -- (but I wouldn't paralleize it right now anyway) local line = 1 -function build_type(text, type_, color) +function build_type(type_, color) + local text = "" for _, t in ipairs(type_.param_types) do - table.insert(text, { text = t .. " ", color = color }) + text = text .. t .. " " end if #type_.param_types == 0 then - table.insert(text, { text = "void ", color = color }) + text = text .. "void " end - table.insert(text, { text = "-> " }) + text = text .. "-> " for _, t in ipairs(type_.result_types) do - table.insert(text, { text = t .. " " }) + text = text .. t .. " " end if #type_.result_types == 0 then - table.insert(text, { text = "void" }) + text = text .. "void" end + + return { text; fg = color } end -function build_header(func, mod, colors) +-- Ctx -> List TextObj +function build_header(ctx) local text = {} - if mod.start ~= nil and mod.start.contents.func == func.funcidx then - table.insert(text, { text = "*START* ", color = colors.primary_text }) + if ctx.mod.start ~= nil and ctx.mod.start.contents.func == ctx.func.funcidx then + table.insert(text, { "*START* ", fg = ctx.colors.primary_text }) end - table.insert(text, { text = "func[" .. func.funcidx .. "] ", color = colors.code }) - table.insert(text, { text = func.name, color = colors.keyword }) - table.insert(text, { text = ": ", color = colors.code }) - - build_type(text, func.type_, colors.code) - table.insert(text, { newline = true }) + table.insert(text, { "func[" .. ctx.func.funcidx .. "] ", fg = ctx.colors.code }) + 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)) return text end +-- This will be moved into ui/text function add_line_number(textlist, colors, newline) if newline == nil then newline = true end @@ -59,116 +62,148 @@ function add_line_number(textlist, colors, newline) line = line + 1 end -function instr_to_text(textlist, instr, func, mod, colors) +-- Ctx -> Instr -> List List TextObj +function instr_to_text(ctx, instr) + local lines = {} + if instr[1] == "block" then - table.insert(textlist, { change_indent = 1 }) - for _, ins in ipairs(instr.instrs) do - instr_to_text(textlist, ins, func, mod, colors) + table.insert(lines, { indent = 1 }) + for i, ins in ipairs(instr.instrs) do + local res = instr_to_text(ctx, ins) + table.append(lines, res) end - table.insert(textlist, { change_indent = -1 }) - add_line_number(textlist, colors) - table.insert(textlist, { text = "label ", color = colors.jumppoint }) - table.insert(textlist, { text = instr.label, color = colors.value }) + table.insert(lines, { + indent = -1; + { "label ", fg = ctx.colors.jumppoint }; + { instr.label, fg = ctx.colors.value }; + }) elseif instr[1] == "loop" then - add_line_number(textlist, colors) - table.insert(textlist, { text = "loop ", color = colors.jumppoint }) - table.insert(textlist, { text = instr.label, color = colors.value, change_indent = 1 }) - - for _, ins in ipairs(instr.instrs) do - instr_to_text(textlist, ins, func, mod, colors) + table.insert(lines, { + { "loop ", fg = ctx.colors.jumppoint }; + { instr.label, fg = ctx.colors.value }; + }) + table.insert(lines, { indent = 1 }) + for i, ins in ipairs(instr.instrs) do + local res = instr_to_text(ctx, ins) + table.append(lines, res) end - table.insert(textlist, { change_indent = -1 }) + table.insert(lines, { indent = -1 }) elseif instr[1] == "if" then - add_line_number(textlist, colors) - table.insert(textlist, { text = "if", color = colors.jumppoint, change_indent = 1 }) - for _, ins in ipairs(instr.instrs) do - instr_to_text(textlist, ins, func, mod, colors) + table.insert(lines, { + { "if ", fg = ctx.colors.jumppoint }; + { instr.label, fg = ctx.colors.value }; + }) + table.insert(lines, { indent = 1 }) + for i, ins in ipairs(instr.instrs) do + local res = instr_to_text(ctx, ins) + table.append(lines, res) end - table.insert(textlist, { change_indent = -1 }) + table.insert(lines, { indent = -1 }) if #instr.else_instrs > 0 then - add_line_number(textlist, colors) - table.insert(textlist, { text = "else", color = colors.jumppoint, change_indent = 1 }) - for _, ins in ipairs(instr.else_instrs) do - instr_to_text(textlist, ins, func, mod, colors) + table.insert(lines, { "else", fg = ctx.colors.jumppoint }) + table.insert(lines, { indent = 1 }) + for i, ins in ipairs(instr.instrs) do + local res = instr_to_text(ctx, ins) + table.append(lines, res) end - table.insert(textlist, { change_indent = -1 }) + table.insert(lines, { indent = -1 }) end elseif instr[1] == "call" then - add_line_number(textlist, colors) - table.insert(textlist, { text = instr[1] .. " ", color = colors.keyword }) - table.insert(textlist, { text = mod.funcs[instr.x].name, color = colors.value }) + table.insert(lines, { + { "call ", fg = ctx.colors.keyword }, + { ctx.mod.funcs[instr.x].name, fg = ctx.colors.value } + }) elseif instr[1] == "call_indirect" then - add_line_number(textlist, colors) - table.insert(textlist, { text = ("%s [%d] "):format(instr[1], instr.table), color = colors.keyword }) - build_type(textlist, mod.types.contents[instr.x + 1], colors.code) + 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) + }) elseif instr[1]:match("local") then - add_line_number(textlist, colors) - table.insert(textlist, { text = instr[1] .. " ", color = colors.keyword }) - table.insert(textlist, { text = func.locals[instr.x + 1].name, color = colors.value }) + table.insert(lines, { + { instr[1] .. " ", fg = ctx.colors.keyword }, + { ctx.func.locals[instr.x + 1].name, fg = ctx.colors.value } + }) else - add_line_number(textlist, colors) - table.insert(textlist, { text = instr[1] .. " ", color = colors.keyword }) + local line = {} + table.insert(line, { instr[1] .. " ", fg = ctx.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 = colors.value + table.insert(line, { + ("align=0x%02x offset=0x%04x"):format(instr.x.align, instr.x.offset); + fg = ctx.colors.value }) end if instr.x[1] == "labelidx" then if instr.x.block then - table.insert(textlist, { text = instr.x.block.label .. " ", color = colors.value }) + table.insert(line, { instr.x.block.label .. " ", fg = ctx.colors.value }) end - table.insert(textlist, { text = ("[0x%02x]"):format(instr.x.labelidx), color = colors.code }) + table.insert(line, { ("[0x%02x]"):format(instr.x.labelidx), fg = ctx.colors.code }) end else - table.insert(textlist, { text = tostring(instr.x), color = colors.value }) + table.insert(line, { tostring(instr.x), fg = ctx.colors.value }) end end + + lines = { line } end - return line + return lines end -function build_body(func, mod, colors) - if func.imported then - return { { text = "Imported function", colors = colors.code } } + +function build_body(ctx) + if ctx.func.imported then + return { { "Imported function", fg = ctx.colors.code } } end - local text = {} - table.insert(text, { text = " Locals:", color = colors.keyword, newline = true, change_indent = 2 }) - for _, loc in ipairs(func.locals) do - table.insert(text, { text = loc.type_ .. " " .. loc.name, color = colors.code , newline = true }) + local lines = {} + table.insert(lines, { + { " Locals:", fg = ctx.colors.keyword } + }) + table.insert(lines, { indent = 1 }) + for _, loc in ipairs(ctx.func.locals) do + table.insert(lines, { + { loc.type_ .. " " .. loc.name, fg = ctx.colors.code } + }) end - if #func.locals == 0 then - table.insert(text, { text = "no locals", color = colors.code, newline = true }) + if #ctx.func.locals == 0 then + table.insert(lines, { + { "no locals", fg = ctx.colors.code } + }) end - table.insert(text, { newline = true, change_indent = -2 }) - table.insert(text, { text = " Body:", color = colors.keyword }) + table.insert(lines, { indent = -1 }) + table.insert(lines, { + { " Body:", fg = ctx.colors.keyword } + }) - for _, instr in ipairs(func.body) do - instr_to_text(text, instr, func, mod, colors) + local code_lines = {} + for _, instr in ipairs(ctx.func.body) do + table.append(code_lines, instr_to_text(ctx, instr)) end - return text + return lines, code_lines end function generate_text_format(func, mod, colors, line_start) - line = line_start or 1 - local heading = build_header(func, mod, colors) - local body = build_body(func, mod, colors) - return heading, body + local ctx = { + func = func; + mod = mod; + colors = colors; + } + local heading = build_header(ctx) + local body_preface, body = build_body(ctx) + return { heading }, body_preface, body end return module { @@ -176,3 +211,84 @@ return module { build_header = build_header; build_body = build_body; } + + +-- function instr_to_text_old(textlist, instr, func, mod, colors) +-- if instr[1] == "block" then +-- table.insert(textlist, { change_indent = 1 }) +-- for _, ins in ipairs(instr.instrs) do +-- instr_to_text(textlist, ins, func, mod, colors) +-- end +-- table.insert(textlist, { change_indent = -1 }) +-- add_line_number(textlist, colors) +-- table.insert(textlist, { text = "label ", color = colors.jumppoint }) +-- table.insert(textlist, { text = instr.label, color = colors.value }) +-- +-- elseif instr[1] == "loop" then +-- add_line_number(textlist, colors) +-- table.insert(textlist, { text = "loop ", color = colors.jumppoint }) +-- table.insert(textlist, { text = instr.label, color = colors.value, change_indent = 1 }) +-- +-- for _, ins in ipairs(instr.instrs) do +-- instr_to_text(textlist, ins, func, mod, colors) +-- end +-- table.insert(textlist, { change_indent = -1 }) +-- +-- elseif instr[1] == "if" then +-- add_line_number(textlist, colors) +-- table.insert(textlist, { text = "if", color = colors.jumppoint, change_indent = 1 }) +-- for _, ins in ipairs(instr.instrs) do +-- instr_to_text(textlist, ins, func, mod, colors) +-- end +-- table.insert(textlist, { change_indent = -1 }) +-- +-- if #instr.else_instrs > 0 then +-- add_line_number(textlist, colors) +-- table.insert(textlist, { text = "else", color = colors.jumppoint, change_indent = 1 }) +-- for _, ins in ipairs(instr.else_instrs) do +-- instr_to_text(textlist, ins, func, mod, colors) +-- end +-- table.insert(textlist, { change_indent = -1 }) +-- end +-- +-- elseif instr[1] == "call" then +-- add_line_number(textlist, colors) +-- table.insert(textlist, { text = instr[1] .. " ", color = colors.keyword }) +-- table.insert(textlist, { text = mod.funcs[instr.x].name, color = colors.value }) +-- +-- elseif instr[1] == "call_indirect" then +-- add_line_number(textlist, colors) +-- table.insert(textlist, { text = ("%s [%d] "):format(instr[1], instr.table), color = colors.keyword }) +-- build_type(textlist, mod.types.contents[instr.x + 1], colors.code) +-- +-- elseif instr[1]:match("local") then +-- add_line_number(textlist, colors) +-- table.insert(textlist, { text = instr[1] .. " ", color = colors.keyword }) +-- table.insert(textlist, { text = func.locals[instr.x + 1].name, color = colors.value }) +-- +-- else +-- add_line_number(textlist, colors) +-- table.insert(textlist, { text = instr[1] .. " ", color = 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 = colors.value +-- }) +-- end +-- +-- if instr.x[1] == "labelidx" then +-- if instr.x.block then +-- table.insert(textlist, { text = instr.x.block.label .. " ", color = colors.value }) +-- end +-- table.insert(textlist, { text = ("[0x%02x]"):format(instr.x.labelidx), color = colors.code }) +-- end +-- else +-- table.insert(textlist, { text = tostring(instr.x), color = colors.value }) +-- end +-- end +-- end +-- +-- return line +-- end