Skip to content

Commit 7f50a11

Browse files
committed
v0.1.6: implement open_results lua API and fix plugin bugs
This commit adds a new open_results API to the Lua engine, allowing plugins to display their output in a persistent, filterable, and clickable ImGui table rather than just printing to the Script Console. Clicking a row automatically navigates the disassembler view to the target address. Changes include: - LuaEngine: Added open_results(title, headers, rows) API. Implemented ResultsRow and ResultsWindow structures to store results. - App: Added render_results_windows() to display active plugin result tables natively within the UI. Includes dynamic filtering across columns and address parsing. - Scripting API: Implemented and registered missing Lua functions (get_comment, get_string, get_image_base, get_arch, get_segments, get_cursor, create_function) that were documented but unwritten. - Plugin Output Forwarding: Fixed invoke_menu_item() output not properly displaying in the Script Console by piping last_output() through ScriptConsole::append_output().
1 parent e069eec commit 7f50a11

5 files changed

Lines changed: 337 additions & 31 deletions

File tree

src/scripting/lua_engine.cpp

Lines changed: 188 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,156 @@ int l_goto(lua_State* L) {
254254
return 0;
255255
}
256256

257+
int l_get_comment(lua_State* L) {
258+
auto* db = get_db(L);
259+
if (!db) { lua_pushstring(L, ""); return 1; }
260+
va_t addr = static_cast<va_t>(luaL_checkinteger(L, 1));
261+
auto it = db->comments.find(addr);
262+
if (it != db->comments.end())
263+
lua_pushstring(L, it->second.c_str());
264+
else
265+
lua_pushstring(L, "");
266+
return 1;
267+
}
268+
269+
int l_get_string(lua_State* L) {
270+
auto* db = get_db(L);
271+
if (!db) { lua_pushnil(L); return 1; }
272+
va_t addr = static_cast<va_t>(luaL_checkinteger(L, 1));
273+
for (auto& [sa, ss] : db->strings) {
274+
if (sa == addr) {
275+
lua_pushstring(L, ss.c_str());
276+
return 1;
277+
}
278+
}
279+
lua_pushnil(L);
280+
return 1;
281+
}
282+
283+
int l_get_image_base(lua_State* L) {
284+
auto* db = get_db(L);
285+
if (!db) { lua_pushinteger(L, 0); return 1; }
286+
lua_pushinteger(L, static_cast<lua_Integer>(db->image_base));
287+
return 1;
288+
}
289+
290+
int l_get_arch(lua_State* L) {
291+
auto* img = get_img(L);
292+
if (!img) { lua_pushstring(L, "unknown"); return 1; }
293+
switch (img->arch) {
294+
case Arch::X86: lua_pushstring(L, "x86"); break;
295+
case Arch::X64: lua_pushstring(L, "x64"); break;
296+
case Arch::ARM: lua_pushstring(L, "arm"); break;
297+
case Arch::ARM64: lua_pushstring(L, "arm64"); break;
298+
case Arch::MIPS: lua_pushstring(L, "mips"); break;
299+
case Arch::PPC: lua_pushstring(L, "ppc"); break;
300+
default: lua_pushstring(L, "unknown"); break;
301+
}
302+
return 1;
303+
}
304+
305+
int l_get_segments(lua_State* L) {
306+
auto* img = get_img(L);
307+
if (!img) { lua_newtable(L); return 1; }
308+
lua_newtable(L);
309+
int idx = 1;
310+
for (auto& seg : img->segments) {
311+
lua_newtable(L);
312+
lua_pushstring(L, seg.name.c_str());
313+
lua_setfield(L, -2, "name");
314+
lua_pushinteger(L, static_cast<lua_Integer>(seg.va));
315+
lua_setfield(L, -2, "addr");
316+
lua_pushinteger(L, static_cast<lua_Integer>(seg.data.size()));
317+
lua_setfield(L, -2, "size");
318+
lua_pushinteger(L, static_cast<lua_Integer>(seg.flags));
319+
lua_setfield(L, -2, "flags");
320+
lua_rawseti(L, -2, idx++);
321+
}
322+
return 1;
323+
}
324+
325+
int l_get_cursor(lua_State* L) {
326+
// The cursor is held by DisasmView; we expose it via a registry pointer set by App
327+
lua_getfield(L, LUA_REGISTRYINDEX, "__hype_cursor");
328+
auto* cur = static_cast<va_t*>(lua_touserdata(L, -1));
329+
lua_pop(L, 1);
330+
if (!cur) { lua_pushinteger(L, 0); return 1; }
331+
lua_pushinteger(L, static_cast<lua_Integer>(*cur));
332+
return 1;
333+
}
334+
335+
int l_create_function(lua_State* L) {
336+
auto* db = get_db(L);
337+
if (!db) return 0;
338+
va_t addr = static_cast<va_t>(luaL_checkinteger(L, 1));
339+
std::lock_guard lk(db->mtx);
340+
if (!db->funcs.count(addr)) {
341+
Function f;
342+
f.entry = addr;
343+
f.name = fmt::format("sub_{:X}", addr - db->image_base);
344+
db->funcs[addr] = std::move(f);
345+
db->names[addr] = db->funcs[addr].name;
346+
}
347+
return 0;
348+
}
349+
350+
// -----------------------------------------------------------------------
351+
// open_results(title, headers_table, rows_table)
352+
// headers_table : {"Col1", "Col2", ...}
353+
// rows_table : { {addr=0x..., cols={"v1","v2",...}}, ... }
354+
// -----------------------------------------------------------------------
355+
int l_open_results(lua_State* L) {
356+
auto* eng = get_engine(L);
357+
if (!eng) return 0;
358+
359+
const char* title = luaL_checkstring(L, 1);
360+
luaL_checktype(L, 2, LUA_TTABLE);
361+
luaL_checktype(L, 3, LUA_TTABLE);
362+
363+
ResultsWindow w;
364+
w.title = title;
365+
366+
// Read headers
367+
int nhdr = static_cast<int>(lua_rawlen(L, 2));
368+
for (int i = 1; i <= nhdr; ++i) {
369+
lua_rawgeti(L, 2, i);
370+
const char* s = lua_tostring(L, -1);
371+
w.headers.push_back(s ? s : "");
372+
lua_pop(L, 1);
373+
}
374+
375+
// Read rows
376+
int nrows = static_cast<int>(lua_rawlen(L, 3));
377+
for (int i = 1; i <= nrows; ++i) {
378+
lua_rawgeti(L, 3, i); // push row table
379+
if (!lua_istable(L, -1)) { lua_pop(L, 1); continue; }
380+
381+
ResultsRow row;
382+
383+
lua_getfield(L, -1, "addr");
384+
row.addr = static_cast<va_t>(lua_tointeger(L, -1));
385+
lua_pop(L, 1);
386+
387+
lua_getfield(L, -1, "cols"); // push cols table
388+
if (lua_istable(L, -1)) {
389+
int ncols = static_cast<int>(lua_rawlen(L, -1));
390+
for (int c = 1; c <= ncols; ++c) {
391+
lua_rawgeti(L, -1, c);
392+
const char* s = lua_tostring(L, -1);
393+
row.cols.push_back(s ? s : "");
394+
lua_pop(L, 1);
395+
}
396+
}
397+
lua_pop(L, 1); // pop cols table
398+
lua_pop(L, 1); // pop row table
399+
400+
w.rows.push_back(std::move(row));
401+
}
402+
403+
eng->push_result_window(std::move(w));
404+
return 0;
405+
}
406+
257407
// -----------------------------------------------------------------------
258408
// Plugin registration Lua API
259409
// -----------------------------------------------------------------------
@@ -369,16 +519,24 @@ void LuaEngine::register_api() {
369519
lua_setfield(L_, LUA_REGISTRYINDEX, "__hype_nav");
370520

371521
// Core API
372-
lua_register(L_, "get_name", l_get_name);
373-
lua_register(L_, "set_name", l_set_name);
374-
lua_register(L_, "get_func", l_get_func);
375-
lua_register(L_, "get_insn", l_get_insn);
376-
lua_register(L_, "get_bytes", l_get_bytes);
377-
lua_register(L_, "set_comment", l_set_comment);
378-
lua_register(L_, "get_xrefs_to", l_get_xrefs_to);
379-
lua_register(L_, "get_functions", l_get_functions);
380-
lua_register(L_, "print", l_print);
381-
lua_register(L_, "goto_addr", l_goto);
522+
lua_register(L_, "get_name", l_get_name);
523+
lua_register(L_, "set_name", l_set_name);
524+
lua_register(L_, "get_func", l_get_func);
525+
lua_register(L_, "get_insn", l_get_insn);
526+
lua_register(L_, "get_bytes", l_get_bytes);
527+
lua_register(L_, "set_comment", l_set_comment);
528+
lua_register(L_, "get_comment", l_get_comment);
529+
lua_register(L_, "get_xrefs_to", l_get_xrefs_to);
530+
lua_register(L_, "get_functions", l_get_functions);
531+
lua_register(L_, "print", l_print);
532+
lua_register(L_, "goto_addr", l_goto);
533+
lua_register(L_, "get_string", l_get_string);
534+
lua_register(L_, "get_image_base", l_get_image_base);
535+
lua_register(L_, "get_arch", l_get_arch);
536+
lua_register(L_, "get_segments", l_get_segments);
537+
lua_register(L_, "get_cursor", l_get_cursor);
538+
lua_register(L_, "create_function",l_create_function);
539+
lua_register(L_, "open_results", l_open_results);
382540

383541
// Plugin registration API
384542
lua_register(L_, "register_plugin", l_register_plugin);
@@ -398,16 +556,24 @@ void LuaEngine::load_plugins(const std::filesystem::path& dir) {
398556
lua_setfield(L_, LUA_REGISTRYINDEX, "__hype_output");
399557

400558
// Register plugin API functions (safe even with null db_/img_)
401-
lua_register(L_, "get_name", l_get_name);
402-
lua_register(L_, "set_name", l_set_name);
403-
lua_register(L_, "get_func", l_get_func);
404-
lua_register(L_, "get_insn", l_get_insn);
405-
lua_register(L_, "get_bytes", l_get_bytes);
406-
lua_register(L_, "set_comment", l_set_comment);
407-
lua_register(L_, "get_xrefs_to", l_get_xrefs_to);
408-
lua_register(L_, "get_functions", l_get_functions);
409-
lua_register(L_, "print", l_print);
410-
lua_register(L_, "goto_addr", l_goto);
559+
lua_register(L_, "get_name", l_get_name);
560+
lua_register(L_, "set_name", l_set_name);
561+
lua_register(L_, "get_func", l_get_func);
562+
lua_register(L_, "get_insn", l_get_insn);
563+
lua_register(L_, "get_bytes", l_get_bytes);
564+
lua_register(L_, "set_comment", l_set_comment);
565+
lua_register(L_, "get_comment", l_get_comment);
566+
lua_register(L_, "get_xrefs_to", l_get_xrefs_to);
567+
lua_register(L_, "get_functions", l_get_functions);
568+
lua_register(L_, "print", l_print);
569+
lua_register(L_, "goto_addr", l_goto);
570+
lua_register(L_, "get_string", l_get_string);
571+
lua_register(L_, "get_image_base", l_get_image_base);
572+
lua_register(L_, "get_arch", l_get_arch);
573+
lua_register(L_, "get_segments", l_get_segments);
574+
lua_register(L_, "get_cursor", l_get_cursor);
575+
lua_register(L_, "create_function",l_create_function);
576+
lua_register(L_, "open_results", l_open_results);
411577
lua_register(L_, "register_plugin", l_register_plugin);
412578
lua_register(L_, "register_menu_item", l_register_menu_item);
413579
lua_register(L_, "register_hotkey", l_register_hotkey);
@@ -467,14 +633,8 @@ void LuaEngine::invoke_menu_item(int plugin_idx, int item_idx) {
467633
output_ += std::string("[plugin error] ") + (msg ? msg : "unknown") + "\n";
468634
lua_pop(L_, 1);
469635
}
470-
// output_ is forwarded to the Script Console by scriptc_ which reads it via execute()
471-
// For plugin callbacks we flush directly via the output panel pointer stored in registry
472-
lua_getfield(L_, LUA_REGISTRYINDEX, "__hype_output");
473-
auto* out_ptr = static_cast<std::string*>(lua_touserdata(L_, -1));
474-
lua_pop(L_, 1);
475-
if (out_ptr && out_ptr != &output_) {
476-
*out_ptr += output_;
477-
}
636+
// output_ holds everything l_print wrote during the call.
637+
// The caller (App::render_menubar) reads it via last_output().
478638
}
479639

480640
void LuaEngine::run_analysis_complete_callbacks() {

src/scripting/lua_engine.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,22 @@ struct PluginEntry {
2626
std::string error_msg;
2727
};
2828

29+
// One row in a plugin results window.
30+
struct ResultsRow {
31+
va_t addr; // address to navigate to when clicked
32+
std::vector<std::string> cols; // column values (must match headers count)
33+
};
34+
35+
// A results window opened by a plugin via open_results().
36+
struct ResultsWindow {
37+
std::string title;
38+
std::vector<std::string> headers;
39+
std::vector<ResultsRow> rows;
40+
bool open = true;
41+
char filter[256] = {};
42+
int selected = -1;
43+
};
44+
2945
class LuaEngine {
3046
public:
3147
LuaEngine();
@@ -45,10 +61,18 @@ class LuaEngine {
4561
const std::vector<PluginEntry>& plugins() const { return plugins_; }
4662
std::vector<PluginEntry>& plugins() { return plugins_; }
4763

64+
// Results windows — rendered by App each frame
65+
std::vector<ResultsWindow>& result_windows() { return result_windows_; }
66+
const std::vector<ResultsWindow>& result_windows() const { return result_windows_; }
67+
68+
// Returns whatever l_print accumulated during the last invoke_menu_item call.
69+
const std::string& last_output() const { return output_; }
70+
4871
// Called by Lua C-function wrappers inside the anonymous namespace
4972
const std::string& current_plugin_name() const { return current_plugin_; }
5073
void add_hotkey(const std::string& key_str, int ref) { hotkeys_.emplace_back(key_str, ref); }
5174
void add_analysis_cb(int ref) { on_analysis_cbs_.push_back(ref); }
75+
void push_result_window(ResultsWindow w) { result_windows_.push_back(std::move(w)); }
5276

5377
private:
5478
void register_api();
@@ -64,6 +88,9 @@ class LuaEngine {
6488
std::vector<std::pair<std::string, int>> hotkeys_; // {key_string, cb_ref}
6589
std::vector<int> on_analysis_cbs_; // Lua registry refs
6690
std::string current_plugin_; // set while loading a .lua file
91+
92+
// Results windows
93+
std::vector<ResultsWindow> result_windows_;
6794
};
6895

6996
} // namespace hype

0 commit comments

Comments
 (0)