- [NEW] [debugger] Quan hi ha una excepció, ho notifica al adapter i li dona la info necessaria

This commit is contained in:
2026-03-31 18:21:05 +02:00
parent 6e5f9fb1a8
commit 7739b563f3
2 changed files with 174 additions and 8 deletions

181
lua.cpp
View File

@@ -7,7 +7,7 @@
#include <algorithm>
#include <vector>
#include <stack>
#include <set>
#include <unordered_map>
#include <string>
#include <mutex>
@@ -32,6 +32,17 @@ struct FrameInfo {
//std::vector<Variable> upvalues;
};
struct StackFrameInfo {
std::string file;
int line;
std::string function;
};
std::string lastExceptionTraceback;
std::vector<StackFrameInfo> lastExceptionStack;
std::string lastExceptionMessage;
bool hasException = false;
std::set<std::string> exceptionFilters;// {"all"};
std::vector<FrameInfo> g_frames;
lua_State *L;
@@ -792,6 +803,78 @@ void sendDebugResponse(const std::string& type, const json& payload) {
std::cout << "@@DEBUG@@" << msg.dump() << std::endl;
}
void parseLuaTraceback(const char* tb, std::vector<StackFrameInfo>& out) {
out.clear();
if (!tb) return;
std::stringstream ss(tb);
std::string line;
bool inFrames = false;
while (std::getline(ss, line)) {
// Quitar espacios iniciales y tabs
while (!line.empty() && (line[0] == ' ' || line[0] == '\t'))
line.erase(line.begin());
// Saltar la primera línea (mensaje de error)
if (!inFrames) {
if (line.rfind("stack traceback:", 0) == 0) {
inFrames = true;
}
continue;
}
// Formato esperado:
// [string "modules.ia.hero"]:189: in field 'ia'
//
// O:
// [string "main"]:34: in function <[string "main"]:32>
if (line.rfind("[string \"", 0) != 0)
continue;
// Extraer chunk name
size_t q1 = line.find('"');
size_t q2 = line.find('"', q1 + 1);
if (q1 == std::string::npos || q2 == std::string::npos)
continue;
std::string chunk = line.substr(q1 + 1, q2 - q1 - 1);
// Extraer línea
size_t colon1 = line.find(':', q2 + 1);
if (colon1 == std::string::npos)
continue;
size_t colon2 = line.find(':', colon1 + 1);
if (colon2 == std::string::npos)
continue;
int lineNumber = std::stoi(line.substr(colon1 + 1, colon2 - colon1 - 1));
// Extraer nombre de función (si existe)
std::string func = "unknown";
size_t inField = line.find("in field '");
if (inField != std::string::npos) {
size_t start = inField + 10;
size_t end = line.find("'", start);
func = line.substr(start, end - start);
}
size_t inFunc = line.find("in function <");
if (inFunc != std::string::npos) {
func = "anonymous";
}
// Convertir chunk → ruta real
std::string filePath = chunkToPath(chunk);
out.push_back({ filePath, lineNumber, func });
}
}
void processDebugCommand(const std::string& line) {
//printf("COMANDO PROCESADO: %s\n", line.c_str());
if (!line.starts_with("@@DEBUGCMD@@"))
@@ -855,8 +938,33 @@ void processDebugCommand(const std::string& line) {
g_paused = false;
}
else if (cmd == "stackTrace") {
json result = getStackTrace(L);
sendDebugResponse("stackTrace", result);
if (hasException) {
parseLuaTraceback(lastExceptionTraceback.c_str(), lastExceptionStack);
json frames = json::array();
for (size_t i = 0; i < lastExceptionStack.size(); i++) {
frames.push_back({
{ "id", (int)i + 1 },
{ "name", lastExceptionStack[i].function },
{ "line", lastExceptionStack[i].line },
{ "column", 1 },
{ "source", {
{ "path", lastExceptionStack[i].file }
}}
});
}
json result = {
{ "stackFrames", frames },
{ "totalFrames", frames.size() }
};
sendDebugResponse("stackTrace", result);
} else {
json result = getStackTrace(L);
sendDebugResponse("stackTrace", result);
}
}
else if (cmd == "locals") {
int frame = j.value("frame", 0);
@@ -931,7 +1039,12 @@ void processDebugCommand(const std::string& line) {
sendDebugResponse("setVariable", result);
}
else if (cmd == "setExceptionFilters") {
exceptionFilters.clear();
for (auto& f : j["filters"]) {
exceptionFilters.insert(f.get<std::string>());
}
}
}
bool checkBreakpointCondition(lua_State* L, const Breakpoint& bp) {
@@ -2719,19 +2832,71 @@ void lua_call_init() {
}
}
void sendExceptionStoppedEvent(const std::string& msg) {
json payload = {
{ "reason", "exception" },
{ "text", msg }
};
sendDebugResponse("stopped", payload);
}
int luaErrorHandler(lua_State* L) {
const char* msg = lua_tostring(L, 1);
if (!msg) msg = "Unknown error";
// Construir traceback
luaL_traceback(L, L, msg, 1);
const char* tb = lua_tostring(L, -1);
if (tb) {
lastExceptionTraceback = tb;
} else {
lastExceptionTraceback = msg;
}
// Devolver el traceback a Lua (aunque luego Lua lo sustituya)
return 1;
}
void lua_call_update() {
if (!update_exists) return;
function_has_breakpoints = false;
while (!funBreakStack.empty()) funBreakStack.pop();
lua_process_debugger_commands();
//lua_process_debugger_commands();
delta_time = float(SDL_GetTicks() - last_update)/1000.0f;
last_update = SDL_GetTicks();
lua_getglobal(L, "mini");
lua_getfield(L, -1, "update");
if (lua_pcall(L, 0, 0, 0)) {
log_msg(LOG_LUART, "%s\n", lua_tostring(L, -1));
lua_pop(L,1);
lua_pushcfunction(L, luaErrorHandler);
lua_insert(L, -2); // poner handler debajo de la función
int status = lua_pcall(L, 0, 0, -2);
lua_pop(L, 1); // quitar handler
if (status != LUA_OK) {
//log_msg(LOG_LUART, "%s\n", lua_tostring(L, -1));
//lua_pop(L,1);
is_playing = false;
lastExceptionMessage = lastExceptionTraceback;
// Limpiar stack anterior
//lastExceptionStack.clear();
// Parsear traceback en frames
//parseLuaTraceback(tb, lastExceptionStack);
lua_pop(L, 1); // pop traceback y error
hasException = true;
if (exceptionFilters.count("all") || exceptionFilters.count("uncaught")) {
sendExceptionStoppedEvent(lastExceptionMessage);
return;
}
sendLogOutput(lastExceptionMessage);
return;
}
}

View File

@@ -791,6 +791,7 @@ int main(int argc,char*argv[]){
if (SDL_GetTicks()-dt>13) {
dt = SDL_GetTicks();
lua_process_debugger_commands();
if (lua_is_playing()) {
lua_call_update();
} else {