- [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:
250
lua.cpp
250
lua.cpp
@@ -17,12 +17,30 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <nlohmann/json.hpp>
|
#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;
|
lua_State *L;
|
||||||
bool is_playing = false;
|
bool is_playing = false;
|
||||||
bool init_exists = false;
|
bool init_exists = false;
|
||||||
bool update_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::mutex g_breakMutex;
|
||||||
|
|
||||||
std::queue<std::string> g_debugCommands;
|
std::queue<std::string> g_debugCommands;
|
||||||
@@ -138,12 +156,24 @@ int allocateRefForTable(lua_State* L, int index) {
|
|||||||
return ref;
|
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();
|
json vars = json::array();
|
||||||
|
|
||||||
lua_Debug ar;
|
lua_Debug ar;
|
||||||
// Obtenemos el frame 0 (el actual)
|
// Obtenemos el frame solicitado
|
||||||
if (!lua_getstack(L, 0, &ar)) {
|
if (!lua_getstack(L, frame, &ar)) {
|
||||||
|
printf("INVALID STACK FRAME\n");
|
||||||
return json{ {"variables", vars} };
|
return json{ {"variables", vars} };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,12 +191,9 @@ json getLocals(lua_State* L) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// El valor está ahora en la pila
|
|
||||||
json v;
|
json v;
|
||||||
|
|
||||||
v["name"] = name;
|
v["name"] = name;
|
||||||
|
|
||||||
// Convertimos el valor Lua a string y tipo
|
|
||||||
int type = lua_type(L, -1);
|
int type = lua_type(L, -1);
|
||||||
|
|
||||||
switch (type) {
|
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();
|
json vars = json::array();
|
||||||
|
|
||||||
lua_Debug ar;
|
lua_Debug ar;
|
||||||
if (!lua_getstack(L, 0, &ar)) {
|
if (!lua_getstack(L, frame, &ar)) {
|
||||||
return json{ {"variables", vars} };
|
return json{ {"variables", vars} };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,6 +586,112 @@ json evalExpression(lua_State* L, const std::string& expr) {
|
|||||||
return result;
|
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) {
|
void sendDebugResponse(const std::string& type, const json& payload) {
|
||||||
json msg = {
|
json msg = {
|
||||||
{ "type", type },
|
{ "type", type },
|
||||||
@@ -579,11 +712,32 @@ void processDebugCommand(const std::string& line) {
|
|||||||
|
|
||||||
if (cmd == "setBreakpoints") {
|
if (cmd == "setBreakpoints") {
|
||||||
std::string file = j["file"];
|
std::string file = j["file"];
|
||||||
std::vector<int> lines = j["lines"];
|
|
||||||
std::lock_guard<std::mutex> lock(g_breakMutex);
|
|
||||||
std::string chunk = pathToChunk(file);
|
std::string chunk = pathToChunk(file);
|
||||||
//printf("Es un BREAKPOINT: %s, %i\n", chunk.c_str(), lines[0]);
|
std::lock_guard<std::mutex> lock(g_breakMutex);
|
||||||
g_breakpoints[chunk] = lines;
|
|
||||||
|
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") {
|
else if (cmd == "continue") {
|
||||||
printf("CONTINUA\n\n");
|
printf("CONTINUA\n\n");
|
||||||
@@ -613,7 +767,8 @@ void processDebugCommand(const std::string& line) {
|
|||||||
sendDebugResponse("stackTrace", result);
|
sendDebugResponse("stackTrace", result);
|
||||||
}
|
}
|
||||||
else if (cmd == "locals") {
|
else if (cmd == "locals") {
|
||||||
json result = getLocals(L);
|
int frame = j.value("frame", 0);
|
||||||
|
json result = getLocals(L, frame);
|
||||||
json payload = {
|
json payload = {
|
||||||
{ "kind", "locals" },
|
{ "kind", "locals" },
|
||||||
{ "variables", result["variables"] }
|
{ "variables", result["variables"] }
|
||||||
@@ -621,7 +776,8 @@ void processDebugCommand(const std::string& line) {
|
|||||||
sendDebugResponse("variables", payload);
|
sendDebugResponse("variables", payload);
|
||||||
}
|
}
|
||||||
else if (cmd == "upvalues") {
|
else if (cmd == "upvalues") {
|
||||||
json result = getUpvalues(L);
|
int frame = j.value("frame", 0);
|
||||||
|
json result = getUpvalues(L, frame);
|
||||||
json payload = {
|
json payload = {
|
||||||
{ "kind", "upvalues" },
|
{ "kind", "upvalues" },
|
||||||
{ "variables", result["variables"] }
|
{ "variables", result["variables"] }
|
||||||
@@ -651,21 +807,52 @@ void processDebugCommand(const std::string& line) {
|
|||||||
};
|
};
|
||||||
sendDebugResponse("eval", payload);
|
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) {
|
bool isBreakpoint(const std::string& file, int line) {
|
||||||
std::lock_guard<std::mutex> lock(g_breakMutex);
|
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);
|
auto it = g_breakpoints.find(file);
|
||||||
if (it == g_breakpoints.end())
|
if (it == g_breakpoints.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (int bp : it->second) {
|
for (auto& bp : it->second) {
|
||||||
if (bp == line)
|
if (bp.line == line) {
|
||||||
return true;
|
if (checkBreakpointCondition(L, bp)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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) {
|
void pauseHere(const std::string& src, int line) {
|
||||||
g_paused = true;
|
g_paused = true;
|
||||||
g_pauseFile = src;
|
g_pauseFile = src;
|
||||||
g_pauseLine = line;
|
g_pauseLine = line;
|
||||||
g_stepMode = STEP_NONE;
|
g_stepMode = STEP_NONE;
|
||||||
|
|
||||||
|
captureStack(L);
|
||||||
|
|
||||||
sendBreakEvent(chunkToPath(src), line);
|
sendBreakEvent(chunkToPath(src), line);
|
||||||
|
|
||||||
while (g_paused) {
|
while (g_paused) {
|
||||||
|
|||||||
Reference in New Issue
Block a user