Rectangle = "src.utils:Rectangle";
wasm_text = "src.wasm.text";
+ wasm_exprs = "src.wasm.exprs";
globals = "src.globals";
COLORS = "conf:COLOR_SCHEME";
end
end
+local button = { extends = "rect" }
+function button:init(text)
+ self.text = text
+ self.color = COLORS.primary_dark
+ self.hovered_color = COLORS.secondary
+ self.text_color = COLORS.primary_text
+ self.click = function() print "Button clicked" end
+ self.hovered = false
+end
+
+function button:mousemoved(x, y, dx, dy)
+ self.hovered = self.rect:contains_trans(x, y)
+end
+
+function button:mousereleased(button, x, y)
+ if ui.focused == self and button == 1 and self.rect:contains_trans(x, y) then
+ self:click(button, x, y)
+ elseif ui.focused == self and button == 1 then
+ ui.focus(nil)
+ end
+end
+
+function button:predraw()
+ if self.hovered then
+ love.graphics.setColor(self.hovered_color)
+ else
+ love.graphics.setColor(self.color)
+ end
+ love.graphics.rectangle("fill", self.rect.x, self.rect.y, self.rect.w, self.rect.h)
+ love.graphics.setColor(self.text_color)
+ love.graphics.printf(self.text, self.rect.x, self.rect.y, self.rect.w, "center")
+end
+
+function button:postdraw() end
+
local function_block = { extends = "drag_rect" }
function function_block:init(wasm_function)
self.func = wasm_function
if ui.focused == self and button == 2 then self.resize_down = false end
if ui.focused == self and button == 3 then
- ui.insert_child(self, with(ui.make_element "deletable_rect") {
- rect = Rectangle(x, y, 80, 60);
- color = { 1, 0, 0 };
- })
+ ui.insert_child(self, ui.make_element("function_context_menu", x, y))
end
end
love.graphics.pop()
end
+local function_context_menu = { extends = "rect" }
+function function_context_menu:init(x, y)
+ self.rect = Rectangle(x, y, 200, 100)
+ self.color = COLORS.primary
+
+ ui.insert_child(self, with(ui.make_element("button", "View decompiled")) {
+ click = function_context_menu.btn_view_decompiled;
+ rect = Rectangle(0, 0, 200, 40)
+ })
+
+ ui.focus(self)
+end
+
+function function_context_menu:inserted()
+ assert(self.parent.type == "function_block", "function context menu only exists on function blocks")
+ ui.focus(self)
+end
+
+function function_context_menu:mousepressed(button, x, y)
+ if not self.rect:contains_trans(x, y) then
+ self.remove = true
+ return
+ end
+
+ return rect.mousepressed(self, button, x, y)
+end
+
+function function_context_menu:btn_view_decompiled(button, x, y)
+ local wasm_func = self.parent.parent.func
+ print(wasm_func.name .. " ------------------------------------------------")
+ local lines = wasm_exprs.build_expr(wasm_func, globals.wasm_module)
+ for _, r in ipairs(lines) do print(r) end
+end
+
local scrolling_area = {}
function scrolling_area:init()
self.offset = { x = 0; y = 0 }
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)
return module {
--]]
--- TODO: Maybe add type checking at some point?
-local stack_opts = {
--- Name Inputs, Outputs
- ["unreachable"] = { 0, 0 };
- ["nop"] = { 0, 0 };
-
- --block is dynamic
- --loop is dynamic
- --if and else are dynamic
-
- ["br"] = { 0, 0 };
- ["br_if"] = { 1, 0 };
- ["br_table"] = { 1, 0 };
- ["return"] = { 0, 0 };
- --call is dynamic
- --call_indirect is dynamic
-
- ["drop"] = { 1, 0 };
- ["select"] = { 3, 1 };
-
- ["local.get"] = { 0, 1 };
- ["local.set"] = { 1, 0 };
- ["local.tee"] = { 1, 1 };
- ["global.get"] = { 0, 1 };
- ["global.set"] = { 1, 0 };
-
- ["i32.load"] = { 1, 1 };
- ["i64.load"] = { 1, 1 };
- ["f32.load"] = { 1, 1 };
- ["f64.load"] = { 1, 1 };
- ["i32.load8_s"] = { 1, 1 };
- ["i32.load8_u"] = { 1, 1 };
- ["i32.load16_s"] = { 1, 1 };
- ["i32.load16_u"] = { 1, 1 };
- ["i64.load8_s"] = { 1, 1 };
- ["i64.load8_u"] = { 1, 1 };
- ["i64.load16_s"] = { 1, 1 };
- ["i64.load16_u"] = { 1, 1 };
- ["i64.load32_s"] = { 1, 1 };
- ["i64.load32_u"] = { 1, 1 };
- ["i32.store"] = { 2, 0 };
- ["i64.store"] = { 2, 0 };
- ["f32.store"] = { 2, 0 };
- ["f64.store"] = { 2, 0 };
- ["i32.store8"] = { 2, 0 };
- ["i32.store16"] = { 2, 0 };
- ["i64.store8"] = { 2, 0 };
- ["i64.store16"] = { 2, 0 };
- ["i64.store32"] = { 2, 0 };
-
- ["memory.size"] = { 0, 1 };
- ["memory.grow"] = { 1, 1 };
-
- ["i32.const"] = { 0, 1 };
- ["i64.const"] = { 0, 1 };
- ["f32.const"] = { 0, 1 };
- ["f64.const"] = { 0, 1 };
-
- ["i32.eqz"] = { 1, 1 };
- ["i32.eq"] = { 2, 1 };
- ["i32.ne"] = { 2, 1 };
- ["i32.lt_s"] = { 2, 1 };
- ["i32.lt_u"] = { 2, 1 };
- ["i32.gt_s"] = { 2, 1 };
- ["i32.gt_u"] = { 2, 1 };
- ["i32.le_s"] = { 2, 1 };
- ["i32.le_u"] = { 2, 1 };
- ["i32.ge_s"] = { 2, 1 };
- ["i32.ge_u"] = { 2, 1 };
- ["i64.eqz"] = { 1, 1 };
- ["i64.eq"] = { 2, 1 };
- ["i64.ne"] = { 2, 1 };
- ["i64.lt_s"] = { 2, 1 };
- ["i64.lt_u"] = { 2, 1 };
- ["i64.gt_s"] = { 2, 1 };
- ["i64.gt_u"] = { 2, 1 };
- ["i64.le_s"] = { 2, 1 };
- ["i64.le_u"] = { 2, 1 };
- ["i64.ge_s"] = { 2, 1 };
- ["i64.ge_u"] = { 2, 1 };
- ["f32.eq"] = { 2, 1 };
- ["f32.ne"] = { 2, 1 };
- ["f32.lt"] = { 2, 1 };
- ["f32.gt"] = { 2, 1 };
- ["f32.le"] = { 2, 1 };
- ["f32.ge"] = { 2, 1 };
- ["f64.eq"] = { 2, 1 };
- ["f64.ne"] = { 2, 1 };
- ["f64.lt"] = { 2, 1 };
- ["f64.gt"] = { 2, 1 };
- ["f64.le"] = { 2, 1 };
- ["f64.ge"] = { 2, 1 };
- ["i32.clz"] = { 1, 1 };
- ["i32.ctz"] = { 1, 1 };
- ["i32.popcnt"] = { 1, 1 };
- ["i32.add"] = { 2, 1 };
- ["i32.sub"] = { 2, 1 };
- ["i32.mul"] = { 2, 1 };
- ["i32.div_s"] = { 2, 1 };
- ["i32.div_u"] = { 2, 1 };
- ["i32.rem_s"] = { 2, 1 };
- ["i32.rem_u"] = { 2, 1 };
- ["i32.and"] = { 2, 1 };
- ["i32.or"] = { 2, 1 };
- ["i32.xor"] = { 2, 1 };
- ["i32.shl"] = { 2, 1 };
- ["i32.shr_s"] = { 2, 1 };
- ["i32.shr_u"] = { 2, 1 };
- ["i32.rotl"] = { 2, 1 };
- ["i32.rotr"] = { 2, 1 };
- ["i64.clz"] = { 1, 1 };
- ["i64.ctz"] = { 1, 1 };
- ["i64.popcnt"] = { 1, 1 };
- ["i64.add"] = { 2, 1 };
- ["i64.sub"] = { 2, 1 };
- ["i64.mul"] = { 2, 1 };
- ["i64.div_s"] = { 2, 1 };
- ["i64.div_u"] = { 2, 1 };
- ["i64.rem_s"] = { 2, 1 };
- ["i64.rem_u"] = { 2, 1 };
- ["i64.and"] = { 2, 1 };
- ["i64.or"] = { 2, 1 };
- ["i64.xor"] = { 2, 1 };
- ["i64.shl"] = { 2, 1 };
- ["i64.shr_s"] = { 2, 1 };
- ["i64.shr_u"] = { 2, 1 };
- ["i64.rotl"] = { 2, 1 };
- ["i64.rotr"] = { 2, 1 };
- ["f32.abs"] = { 1, 1 };
- ["f32.neg"] = { 1, 1 };
- ["f32.ceil"] = { 1, 1 };
- ["f32.floor"] = { 1, 1 };
- ["f32.trunc"] = { 1, 1 };
- ["f32.nearest"] = { 1, 1 };
- ["f32.sqrt"] = { 1, 1 };
- ["f32.add"] = { 2, 1 };
- ["f32.sub"] = { 2, 1 };
- ["f32.mul"] = { 2, 1 };
- ["f32.div"] = { 2, 1 };
- ["f32.min"] = { 2, 1 };
- ["f32.max"] = { 2, 1 };
- ["f32.copysign"] = { 2, 1 };
- ["f64.abs"] = { 1, 1 };
- ["f64.neg"] = { 1, 1 };
- ["f64.ceil"] = { 1, 1 };
- ["f64.floor"] = { 1, 1 };
- ["f64.trunc"] = { 1, 1 };
- ["f64.nearest"] = { 1, 1 };
- ["f64.sqrt"] = { 1, 1 };
- ["f64.add"] = { 2, 1 };
- ["f64.sub"] = { 2, 1 };
- ["f64.mul"] = { 2, 1 };
- ["f64.div"] = { 2, 1 };
- ["f64.min"] = { 2, 1 };
- ["f64.max"] = { 2, 1 };
- ["f64.copysign"] = { 2, 1 };
-
- ["i32.wrap_i64"] = { 1, 1 };
- ["i32.trunc_f32_s"] = { 1, 1 };
- ["i32.trunc_f32_u"] = { 1, 1 };
- ["i32.trunc_f64_s"] = { 1, 1 };
- ["i32.trunc_f64_u"] = { 1, 1 };
- ["i64.extend_i32_s"] = { 1, 1 };
- ["i64.extend_i32_u"] = { 1, 1 };
- ["i64.trunc_f32_s"] = { 1, 1 };
- ["i64.trunc_f32_u"] = { 1, 1 };
- ["i64.trunc_f64_s"] = { 1, 1 };
- ["i64.trunc_f64_u"] = { 1, 1 };
- ["f32.convert_i32_s"] = { 1, 1 };
- ["f32.convert_i32_u"] = { 1, 1 };
- ["f32.convert_i64_s"] = { 1, 1 };
- ["f32.convert_i64_u"] = { 1, 1 };
- ["f32.demote_f64"] = { 1, 1 };
- ["f64.convert_i32_s"] = { 1, 1 };
- ["f64.convert_i32_u"] = { 1, 1 };
- ["f64.convert_i64_s"] = { 1, 1 };
- ["f64.convert_i64_u"] = { 1, 1 };
- ["f64.promote_f32"] = { 1, 1 };
- ["i32.reinterpret_f32"] = { 1, 1 };
- ["i64.reinterpret_f64"] = { 1, 1 };
- ["f32.reinterpret_i32"] = { 1, 1 };
- ["f64.reinterpret_i64"] = { 1, 1 };
-}
-
local build_instr_list
function binop(op)
end
end
-function func_call(name, nparams)
- nparams = nparams or 1
+function func_call(name, nparams, nreturns)
+ if nparams == nil then nparams = 1 end
+ if nreturns == nil then nreturns = 1 end
return function(stack, instr)
local str = name .. "("
local args = ""
args = ", " .. val .. args
end
str = str .. args:sub(3) .. ")"
- stack:push(str)
+ if nreturns == 0 then
+ return str
+ else
+ stack:push(str)
+ end
end
end
end
function expr_br_table(stack, instr)
- error "Cannot handle br_table"
+ local lines = {}
+ 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 .. ">")
+ end
+ table.insert(mappings, "default => <" .. instr.labeln.block.label .. ">")
+ for _, line in ipairs(indent(mappings, " ")) do
+ table.insert(lines, line)
+ end
+ table.insert(lines, "}")
+ return lines
end
function expr_drop(stack, instr)
)
table.insert(lines, 1, "block <" .. instr.label .. "> {")
table.insert(lines, "}")
- return lines
+ if #instr.rt == 0 then
+ return lines
+ else
+ stack:push(table.concat(lines, "\n"))
+ end
end
function expr_loop(stack, instr, func, mod)
)
table.insert(lines, 1, "loop <" .. instr.label .. "> {")
table.insert(lines, "}")
- return lines
+ if #instr.rt == 0 then
+ return lines
+ else
+ stack:push(table.concat(lines, "\n"))
+ end
end
function expr_if(stack, instr, func, mod)
end
end
table.insert(lines, "}")
- return lines
+ if #instr.rt == 0 then
+ return lines
+ else
+ stack:push(table.concat(lines, "\n"))
+ end
end
function expr_call(stack, instr, func, mod)
local nreturns = #mod.funcs[instr.x].type_.result_types
local name = mod.funcs[instr.x].name
- local res = func_call(name, nparams)(stack, instr, func, mod)
+ local res = func_call(name, nparams, nreturns)(stack, instr, func, mod)
if nreturns >= 1 then
stack:push(res)
else
end
end
-function call_indirect(stack, instr)
- error "Cannot handle call indirect"
+function expr_call_indirect(stack, instr, func, mod)
+ local type_ = mod.types.contents[instr.x + 1]
+ local nparams = #type_.param_types
+ local nreturns = #type_.result_types
+ local addr = stack:pop()
+ local res = func_call("[" .. addr .. "]", nparams, nreturns)(stack, instr, func, mod)
+ if nreturns >= 1 then
+ stack:push(res)
+ else
+ return res
+ end
end
+function expr_nop() return "nop" end
+function expr_unreachable() return "unreachable" end
+
local expr_generators = {
-- Name Inputs, Outputs
- ["unreachable"] = { 0, 0 };
- ["nop"] = { 0, 0 };
+ ["unreachable"] = expr_unreachable;
+ ["nop"] = expr_nop;
["block"] = expr_block;
["loop"] = expr_loop;
["br_table"] = expr_br_table;
["return"] = expr_return;
["call"] = expr_call;
- --call_indirect is dynamic
+ ["call_indirect"] = expr_call_indirect;
["drop"] = expr_drop;
["select"] = expr_select;
["i64.rotl"] = binop "rotl";
["i64.rotr"] = binop "rotr";
["f32.abs"] = func_call "abs";
- ["f32.neg"] = func_call "neg"; -- TODO: make this better
+ ["f32.neg"] = func_call "-";
["f32.ceil"] = func_call "ceil";
["f32.floor"] = func_call "floor";
["f32.trunc"] = func_call "trunc";
["f32.max"] = func_call("max", 2);
["f32.copysign"] = func_call("copysign", 2);
["f64.abs"] = func_call "abs";
- ["f64.neg"] = func_call "neg";
+ ["f64.neg"] = func_call "-";
["f64.ceil"] = func_call "ceil";
["f64.floor"] = func_call "floor";
["f64.trunc"] = func_call "trunc";
["f64.reinterpret_i64"] = cast "f64";
}
-
--- This works... ish but we should use proper symbols (+-*/) and other things
function build_instr_list(instrs, func, mod)
local lines = {}
local stack = Stack()