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
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)
}
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
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
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";
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;
-import { }
+import {
+ pprint = "lualib.pprint"
+}
Text = {}
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 {}
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;
+}
--]]
+
+-- TODO: Convert this to the new layout
+
local build_instr_list
function binop(op)
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
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
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
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
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),
" "
table.insert(lines, line)
end
end
- table.insert(lines, "}")
+ table.insert(lines, { "}" })
if #instr.rt == 0 then
return lines
else
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
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, {
import {
pprint = "lualib.pprint";
+ TextBuilder = "src.ui.text:TextBuilder";
}
-- The current line number during processing
text = text .. "void"
end
- return { text; fg = color }
+ return text
end
-- Ctx -> List TextObj
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
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)
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 {
parse = "src.wasm.parse:";
analyze = "src.wasm.analyze:";
decompile_func = "src.wasm.decompile:decompile_func";
+ Text = "src.ui.text:";
pprint = "lualib.pprint";
}
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