diff --git a/lua.cpp b/lua.cpp index 9830f87..ad48475 100644 --- a/lua.cpp +++ b/lua.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -16,6 +17,11 @@ #include #include +lua_State *L; +bool is_playing = false; +bool init_exists = false; +bool update_exists = false; + std::unordered_map> g_breakpoints; std::mutex g_breakMutex; @@ -27,6 +33,19 @@ std::atomic g_paused = false; std::string g_pauseFile; int g_pauseLine = 0; +bool function_has_breakpoints = false; +std::stack funBreakStack; +bool debug_enabled = false; + +enum StepMode { + STEP_NONE, + STEP_INTO, + STEP_OVER, + STEP_OUT +}; + +StepMode g_stepMode = STEP_NONE; +int g_stepDepth = 0; namespace fs = std::filesystem; using json = nlohmann::json; @@ -77,6 +96,53 @@ std::string chunkToPath(const std::string& chunk) { return abs.lexically_normal().string(); } +int getStackDepth(lua_State* L) { + lua_Debug ar; + int depth = 0; + + while (lua_getstack(L, depth, &ar)) { + depth++; + } + + return depth; +} + +json getStackTrace(lua_State* L) { + json frames = json::array(); + lua_Debug ar; + + int depth = 0; + while (lua_getstack(L, depth, &ar)) { + lua_getinfo(L, "nSl", &ar); + + const char* src = ar.source; + if (src[0] == '@') + src++; + + json frame = { + { "file", src }, + { "line", ar.currentline }, + { "name", ar.name ? ar.name : "?" } + }; + + frames.push_back(frame); + depth++; + } + + return json{ + { "frames", frames } + }; +} + +void sendDebugResponse(const std::string& type, const json& payload) { + json msg = { + { "type", type }, + { "payload", payload } + }; + printf("STACKTRACE: %s", msg.dump().c_str()); + std::cout << "@@DEBUG@@" << msg.dump() << std::endl; +} + void processDebugCommand(const std::string& line) { //printf("COMANDO PROCESADO: %s\n", line.c_str()); if (!line.starts_with("@@DEBUGCMD@@")) @@ -96,8 +162,31 @@ void processDebugCommand(const std::string& line) { } else if (cmd == "continue") { printf("CONTINUA\n\n"); + g_stepMode = STEP_NONE; g_paused = false; } + else if (cmd == "pause") { + g_stepMode = STEP_INTO; + g_paused = false; + } + else if (cmd == "stepOver") { + g_stepMode = STEP_OVER; + g_stepDepth = getStackDepth(L); + g_paused = false; + } + else if (cmd == "stepInto") { + g_stepMode = STEP_INTO; + g_paused = false; + } + else if (cmd == "stepOut") { + g_stepMode = STEP_OUT; + g_stepDepth = getStackDepth(L); + g_paused = false; + } + else if (cmd == "stackTrace") { + json result = getStackTrace(L); + sendDebugResponse("stackTrace", result); + } } bool isBreakpoint(const std::string& file, int line) { @@ -161,34 +250,90 @@ std::string waitForDebugCommand() { } }*/ +bool shouldPauseForStepping(lua_State* L, lua_Debug* ar) { + int depth = getStackDepth(L); + + switch (g_stepMode) { + case STEP_INTO: + return true; // siempre parar en la siguiente línea + + case STEP_OVER: + return depth <= g_stepDepth; + + case STEP_OUT: + return depth < g_stepDepth; + + default: + return false; + } +} + +void pauseHere(const std::string& src, int line) { + g_paused = true; + g_pauseFile = src; + g_pauseLine = line; + g_stepMode = STEP_NONE; + + sendBreakEvent(chunkToPath(src), line); + + while (g_paused) { + std::string cmd = waitForDebugCommand(); + processDebugCommand(cmd); + } +} + extern "C" void luaHook(lua_State* L, lua_Debug* ar) { lua_getinfo(L, "Sl", ar); - - if (ar->currentline <= 0) - return; - + const char* src = ar->source; + if (src[0]=='=') return; - // Lua usa "@filename" para archivos reales - //if (src[0] == '@') - // src++; + if (ar->event == LUA_HOOKCALL) { + funBreakStack.push(function_has_breakpoints); + bool new_function_has_breakpoints = false; + { + std::lock_guard lock(g_breakMutex); + auto it = g_breakpoints.find(src); + new_function_has_breakpoints = (it != g_breakpoints.end() && !it->second.empty()); + } + if (new_function_has_breakpoints || g_stepMode != STEP_NONE) { + if (!function_has_breakpoints) + lua_sethook(L, luaHook, LUA_MASKCALL | LUA_MASKLINE | LUA_MASKRET, 0); + } else { + //if (function_has_breakpoints) + lua_sethook(L, luaHook, LUA_MASKCALL | LUA_MASKRET, 0); + } + function_has_breakpoints = new_function_has_breakpoints; + } + else if (ar->event == LUA_HOOKRET) { + // Siempre volver al hook base + bool new_function_has_breakpoints = funBreakStack.empty() ? false : funBreakStack.top(); + funBreakStack.pop(); + if (new_function_has_breakpoints || g_stepMode != STEP_NONE) { + if (!function_has_breakpoints) + lua_sethook(L, luaHook, LUA_MASKCALL | LUA_MASKLINE | LUA_MASKRET, 0); + } else { + //if (function_has_breakpoints) + lua_sethook(L, luaHook, LUA_MASKCALL | LUA_MASKRET, 0); + } + function_has_breakpoints = new_function_has_breakpoints; + } + else if (ar->event == LUA_HOOKLINE) { + if (ar->currentline <= 0) return; + const char* src = ar->source; + int line = ar->currentline; - int line = ar->currentline; + if (isBreakpoint(src, line)) { + pauseHere(src, line); + return; + } - if (isBreakpoint(src, line)) { - g_paused = true; - g_pauseFile = src; - g_pauseLine = line; - - std::string realPath = chunkToPath(src); - sendBreakEvent(realPath, line); - //sendBreakEvent(src, line); - - // Esperar comandos del Debug Adapter - while (g_paused) { - std::string cmd = waitForDebugCommand(); - printf("PROCESANDO COMANDO: %s\n", cmd.c_str()); - processDebugCommand(cmd); + // 2. Stepping + if (g_stepMode != STEP_NONE) { + if (shouldPauseForStepping(L, ar)) { + pauseHere(src, line); + return; + } } } } @@ -1287,10 +1432,6 @@ extern "C" { } -lua_State *L; -bool is_playing = false; -bool init_exists = false; -bool update_exists = false; bool lua_is_playing() { return is_playing; @@ -1643,6 +1784,7 @@ int MiniLoader(lua_State *L) { // 1. Convertir puntos en barras std::string path(name); + std::string regpath(name); std::replace(path.begin(), path.end(), '.', '/'); // 2. Detectar comodín "/*" @@ -1650,6 +1792,7 @@ int MiniLoader(lua_State *L) { if (path.size() >= 2 && path.substr(path.size()-2) == "/*") { load_all = true; path = path.substr(0, path.size()-2); // quitar "/*" + regpath = regpath.substr(0, regpath.size()-2); // quitar "/*" } if (load_all) { @@ -1658,7 +1801,7 @@ int MiniLoader(lua_State *L) { // Ejecutar todos los módulos for (auto &f : files) { std::string fullpath = path + "/" + f; - std::string registerpath = std::string(path + "." + f.substr(0,f.size()-4)); + std::string registerpath = std::string(regpath + "." + f.substr(0,f.size()-4)); int size; char* buffer = file_getfilebuffer(fullpath.c_str(), size); @@ -1713,7 +1856,7 @@ int MiniLoader(lua_State *L) { void lua_init(const char *main_lua_file) { L = luaL_newstate(); luaL_openlibs(L); - lua_sethook(L, luaHook, LUA_MASKLINE, 0); + push_lua_funcs(); lua_register(L, "mini_loader", MiniLoader); luaL_dostring(L, "table.insert(package.searchers,2,mini_loader)\n"); @@ -1765,6 +1908,8 @@ void lua_call_init() { void lua_call_update() { if (!update_exists) return; + function_has_breakpoints = false; + while (!funBreakStack.empty()) funBreakStack.pop(); lua_process_debugger_commands(); delta_time = float(SDL_GetTicks() - last_update)/1000.0f; last_update = SDL_GetTicks(); @@ -1816,3 +1961,13 @@ void lua_kill_thread() { g_running = false; stdinThread.request_stop(); } + +void lua_toggle_debug() { + if (debug_enabled) { + debug_enabled = false; + lua_sethook(L, NULL,0, 0); + } else { + debug_enabled = true; + lua_sethook(L, luaHook, LUA_MASKCALL | LUA_MASKRET, 0); + } +} diff --git a/lua.h b/lua.h index a50e8d6..f1f9a0b 100644 --- a/lua.h +++ b/lua.h @@ -7,3 +7,5 @@ void lua_call_init(); void lua_call_update(); void lua_quit(); void lua_kill_thread(); +void lua_toggle_debug(); +//void lua_disable_debug(); diff --git a/mini.cpp b/mini.cpp index 82d7a20..276f4ab 100644 --- a/mini.cpp +++ b/mini.cpp @@ -740,6 +740,8 @@ int main(int argc,char*argv[]){ //} else { // should_exit=true; //} + } else if (mini_eve.key.scancode == SDL_SCANCODE_F11) { + lua_toggle_debug(); } else if (mini_eve.key.scancode == SDL_SCANCODE_F5) { should_exit=true; } else {