From 6f5bdd274aaafd05affb0b1bf1b4d26c54740f61 Mon Sep 17 00:00:00 2001 From: Raimon Zamora Date: Tue, 31 Mar 2026 13:03:53 +0200 Subject: [PATCH] - [NEW] [debugger] Soport per a multiples nivells (frames) de stack - [NEW] [debugger] Soport per a breakpoints condicionals - [NEW] [debugger] Soport per a modificar variables --- lua.cpp | 250 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 229 insertions(+), 21 deletions(-) diff --git a/lua.cpp b/lua.cpp index d0cee69..e537b71 100644 --- a/lua.cpp +++ b/lua.cpp @@ -17,12 +17,30 @@ #include #include +struct Breakpoint { + int line; + std::string condition; // puede estar vacío + std::string logMessage; // "" si no es logpoint + std::string hitCondition; // "" si no hay hit count +}; + +struct FrameInfo { + int level; // nivel en lua_getstack + std::string source; + int line; + //std::vector locals; + //std::vector upvalues; +}; + +std::vector g_frames; + lua_State *L; bool is_playing = false; bool init_exists = false; bool update_exists = false; -std::unordered_map> g_breakpoints; +//std::unordered_map> g_breakpoints; +std::unordered_map> g_breakpoints; std::mutex g_breakMutex; std::queue g_debugCommands; @@ -138,12 +156,24 @@ int allocateRefForTable(lua_State* L, int index) { return ref; } -json getLocals(lua_State* L) { +bool pushTableFromRef(lua_State* L, int ref) { + lua_rawgeti(L, LUA_REGISTRYINDEX, ref); + + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return false; + } + + return true; +} + +json getLocals(lua_State* L, int frame) { json vars = json::array(); lua_Debug ar; - // Obtenemos el frame 0 (el actual) - if (!lua_getstack(L, 0, &ar)) { + // Obtenemos el frame solicitado + if (!lua_getstack(L, frame, &ar)) { + printf("INVALID STACK FRAME\n"); return json{ {"variables", vars} }; } @@ -161,12 +191,9 @@ json getLocals(lua_State* L) { continue; } - // El valor está ahora en la pila json v; - v["name"] = name; - // Convertimos el valor Lua a string y tipo int type = lua_type(L, -1); switch (type) { @@ -226,11 +253,11 @@ json getLocals(lua_State* L) { }; } -json getUpvalues(lua_State* L) { +json getUpvalues(lua_State* L, int frame) { json vars = json::array(); lua_Debug ar; - if (!lua_getstack(L, 0, &ar)) { + if (!lua_getstack(L, frame, &ar)) { return json{ {"variables", vars} }; } @@ -559,6 +586,112 @@ json evalExpression(lua_State* L, const std::string& expr) { return result; } +json setVariable(lua_State* L, int frame, const std::string& scope, + const std::string& name, const std::string& valueStr) +{ + lua_Debug ar; + + if (!lua_getstack(L, frame, &ar)) { + return { {"error", "invalid frame"} }; + } + + lua_getinfo(L, "nSl", &ar); + + // Convertir valueStr a valor Lua + auto pushValue = [&](const std::string& s) { + // número + char* end; + double num = strtod(s.c_str(), &end); + if (*end == '\0') { + lua_pushnumber(L, num); + return; + } + + // boolean + if (s == "true") { lua_pushboolean(L, 1); return; } + if (s == "false") { lua_pushboolean(L, 0); return; } + + // nil + if (s == "nil") { lua_pushnil(L); return; } + + // string + lua_pushstring(L, s.c_str()); + }; + + if (scope == "locals") { + int i = 1; + const char* localName; + while ((localName = lua_getlocal(L, &ar, i)) != NULL) { + lua_pop(L, 1); // pop old value + + if (name == localName) { + pushValue(valueStr); + lua_setlocal(L, &ar, i); + return { {"value", valueStr} }; + } + + i++; + } + } + + if (scope == "upvalues") { + lua_getinfo(L, "f", &ar); // push function + + int i = 1; + const char* upName; + while ((upName = lua_getupvalue(L, -1, i)) != NULL) { + lua_pop(L, 1); // pop old value + + if (name == upName) { + pushValue(valueStr); + lua_setupvalue(L, -1, i); + lua_pop(L, 1); // pop function + return { {"value", valueStr} }; + } + + i++; + } + + lua_pop(L, 1); // pop function + } + + if (scope == "globals") { + pushValue(valueStr); + lua_setglobal(L, name.c_str()); + return { {"value", valueStr} }; + } + + return { {"error", "variable not found"} }; +} + +json setTableField(lua_State* L, int ref, const std::string& key, const std::string& valueStr) +{ + // Recuperar la tabla desde tu sistema de referencias + if (!pushTableFromRef(L, ref)) { + return { {"error", "invalid table ref"} }; + } + + // Convertir el valor + auto pushValue = [&](const std::string& s) { + char* end; + double num = strtod(s.c_str(), &end); + if (*end == '\0') { lua_pushnumber(L, num); return; } + if (s == "true") { lua_pushboolean(L, 1); return; } + if (s == "false") { lua_pushboolean(L, 0); return; } + if (s == "nil") { lua_pushnil(L); return; } + lua_pushstring(L, s.c_str()); + }; + + pushValue(valueStr); + + // tabla[key] = valor + lua_setfield(L, -2, key.c_str()); + + lua_pop(L, 1); // pop tabla + + return { {"value", valueStr} }; +} + void sendDebugResponse(const std::string& type, const json& payload) { json msg = { { "type", type }, @@ -579,11 +712,32 @@ void processDebugCommand(const std::string& line) { if (cmd == "setBreakpoints") { std::string file = j["file"]; - std::vector lines = j["lines"]; - std::lock_guard lock(g_breakMutex); std::string chunk = pathToChunk(file); - //printf("Es un BREAKPOINT: %s, %i\n", chunk.c_str(), lines[0]); - g_breakpoints[chunk] = lines; + std::lock_guard lock(g_breakMutex); + + g_breakpoints[chunk].clear(); + + for (auto& bp : j["breakpoints"]) { + Breakpoint b; + b.line = bp["line"]; + + if (bp.contains("condition") && bp["condition"].is_string()) + b.condition = bp["condition"]; + else + b.condition = ""; + + if (bp.contains("logMessage") && bp["logMessage"].is_string()) + b.logMessage = bp["logMessage"]; + else + b.logMessage = ""; + + if (bp.contains("hitCondition") && bp["hitCondition"].is_string()) + b.hitCondition = bp["hitCondition"]; + else + b.hitCondition = ""; + + g_breakpoints[chunk].push_back(b); + } } else if (cmd == "continue") { printf("CONTINUA\n\n"); @@ -613,7 +767,8 @@ void processDebugCommand(const std::string& line) { sendDebugResponse("stackTrace", result); } else if (cmd == "locals") { - json result = getLocals(L); + int frame = j.value("frame", 0); + json result = getLocals(L, frame); json payload = { { "kind", "locals" }, { "variables", result["variables"] } @@ -621,7 +776,8 @@ void processDebugCommand(const std::string& line) { sendDebugResponse("variables", payload); } else if (cmd == "upvalues") { - json result = getUpvalues(L); + int frame = j.value("frame", 0); + json result = getUpvalues(L, frame); json payload = { { "kind", "upvalues" }, { "variables", result["variables"] } @@ -651,21 +807,52 @@ void processDebugCommand(const std::string& line) { }; sendDebugResponse("eval", payload); } + else if (cmd == "setVariable") { + int frame = j.value("frame", 0); + std::string scope = j["scope"]; + std::string name = j["name"]; + std::string value = j["value"]; + + json result = setVariable(L, frame, scope, name, value); + + sendDebugResponse("setVariable", result); + } + else if (cmd == "setTableField") { + int ref = j["ref"]; + std::string key = j["key"]; + std::string valueStr = j["value"]; + + json result = setTableField(L, ref, key, valueStr); + + sendDebugResponse("setVariable", result); + } + +} + +bool checkBreakpointCondition(lua_State* L, const Breakpoint& bp) { + if (bp.condition.empty()) return true; + + json result = evalExpression(L, bp.condition); + if (result.contains("error")) return false; + if (result["type"] == "boolean") return result["value"] == "true"; + + // Si la condición no devuelve booleano, se considera false + return false; } bool isBreakpoint(const std::string& file, int line) { std::lock_guard lock(g_breakMutex); - //if (!g_breakpoints.empty()) { - // printf("file: %s\n", file.c_str()); - //} auto it = g_breakpoints.find(file); if (it == g_breakpoints.end()) return false; - for (int bp : it->second) { - if (bp == line) - return true; + for (auto& bp : it->second) { + if (bp.line == line) { + if (checkBreakpointCondition(L, bp)) { + return true; + } + } } return false; @@ -732,12 +919,33 @@ bool shouldPauseForStepping(lua_State* L, lua_Debug* ar) { } } +void captureStack(lua_State* L) { + g_frames.clear(); + + lua_Debug ar; + int level = 0; + + while (lua_getstack(L, level, &ar)) { + lua_getinfo(L, "nSl", &ar); + + FrameInfo fi; + fi.level = level; + fi.source = ar.source; + fi.line = ar.currentline; + + g_frames.push_back(fi); + level++; + } +} + void pauseHere(const std::string& src, int line) { g_paused = true; g_pauseFile = src; g_pauseLine = line; g_stepMode = STEP_NONE; + captureStack(L); + sendBreakEvent(chunkToPath(src), line); while (g_paused) {