- [NEW] [debugger] Soport per a multiples nivells (frames) de stack
- [NEW] [debugger] Soport per a breakpoints condicionals - [NEW] [debugger] Soport per a modificar variables
This commit is contained in:
248
lua.cpp
248
lua.cpp
@@ -17,12 +17,30 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
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<Variable> locals;
|
||||
//std::vector<Variable> upvalues;
|
||||
};
|
||||
|
||||
std::vector<FrameInfo> g_frames;
|
||||
|
||||
lua_State *L;
|
||||
bool is_playing = false;
|
||||
bool init_exists = false;
|
||||
bool update_exists = false;
|
||||
|
||||
std::unordered_map<std::string, std::vector<int>> g_breakpoints;
|
||||
//std::unordered_map<std::string, std::vector<int>> g_breakpoints;
|
||||
std::unordered_map<std::string, std::vector<Breakpoint>> g_breakpoints;
|
||||
std::mutex g_breakMutex;
|
||||
|
||||
std::queue<std::string> 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<int> lines = j["lines"];
|
||||
std::lock_guard<std::mutex> 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<std::mutex> 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,22 +807,53 @@ 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<std::mutex> 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)
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user