mirror of
https://github.com/brues-code/ClassicAPI.git
synced 2026-11-14 07:00:37 +00:00
debugstack([thread,] [start [, count1 [, count2]]]) returns the stack of a coroutine when one is passed first, with start defaulting to 0 (the coroutine's innermost level), as 4.3.4's Script_debugstack does. 1.12.1's debugstack (0x703760) has no thread argument. A thread fails its lua_isnumber and lua_isstring checks, so it returns the thread itself, and msg .. debugstack(co) raises "attempt to concatenate a thread value" in the coroutine error paths of WeakAuras, Questie's ThreadLib, MRT and Zygor. The engine function still builds the text, so its line format and its 12/10-level head and tail windows are unchanged, which keeps addons that parse debugstack() for their folder working. It reads levels through six calls on the state it runs on: lua_getstack (0x6FBAA0) at 0x70383F, 0x70386E, 0x703896, 0x7038AD and 0x703998, and lua_getinfo (0x6FBC70) at 0x7038CD. Those calls are redirected to read from the state the new global names for the call: the thread, or the calling state. The engine function has no callers of its own, so the global is its only way in and sets that state for every call; an error part way cannot leave a thread behind for the next call. The global removes the thread argument, inserts 0 as start when none is given, and calls the engine function directly in its own frame, so levels without a thread count exactly as before.
103 lines
3.8 KiB
C++
103 lines
3.8 KiB
C++
// This file is part of ClassicAPI.
|
|
//
|
|
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
|
// of the GNU General Public License as published by the Free Software Foundation, either
|
|
// version 3 of the License, or (at your option) any later version.
|
|
//
|
|
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
// PURPOSE. See the GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License along with
|
|
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
// `debugstack([thread,] [start [, count1 [, count2]]])` — the stack of a
|
|
// coroutine, as 4.3.4's Script_debugstack takes it: with a thread first,
|
|
// the levels are that thread's and `start` defaults to 0 (the thread's
|
|
// innermost level) instead of 1.
|
|
//
|
|
// 1.12's debugstack (FUN_SCRIPT_DEBUGSTACK) has no thread argument, and a
|
|
// thread falls through its argument checks so that it returns the thread
|
|
// itself: `msg .. debugstack(co)` raises "attempt to concatenate a thread
|
|
// value" in the coroutine error paths of WeakAuras, Questie's ThreadLib,
|
|
// MRT and Zygor.
|
|
//
|
|
// The engine's function still does the work, so its text and its head and
|
|
// tail windows stay exactly as they are. It reads levels through six calls
|
|
// (five lua_getstack, one lua_getinfo), and those calls are redirected to
|
|
// read from the state Script_debugstack names for the call: the thread, or
|
|
// the calling state when there is none. Script_debugstack replaces the
|
|
// global and is the engine function's only way in, so the state is set for
|
|
// every call; an error part way cannot leave a thread behind for the next.
|
|
//
|
|
// Registered on both Lua states, like the base-library helpers.
|
|
|
|
#include "Common.h"
|
|
#include "Game.h"
|
|
#include "Offsets.h"
|
|
|
|
#include <cstdint>
|
|
#include <initializer_list>
|
|
|
|
namespace BaseLib::DebugStack {
|
|
|
|
namespace {
|
|
|
|
namespace Lua = Game::Lua;
|
|
|
|
// The state whose levels the engine's debugstack reads, or null for its own.
|
|
void *g_levelsOf = nullptr;
|
|
|
|
int __fastcall GetStack(void *L, int level, void *ar) {
|
|
return Lua::GetStack(g_levelsOf ? g_levelsOf : L, level, ar);
|
|
}
|
|
|
|
int __fastcall GetInfo(void *L, const char *what, void *ar) {
|
|
return Lua::GetInfo(g_levelsOf ? g_levelsOf : L, what, ar);
|
|
}
|
|
|
|
int __fastcall Script_debugstack(void *L) {
|
|
const auto engineDebugStack =
|
|
reinterpret_cast<Lua::CFunction>(Offsets::FUN_SCRIPT_DEBUGSTACK);
|
|
g_levelsOf = nullptr;
|
|
if (Lua::Type(L, 1) == Lua::TYPE_THREAD) {
|
|
g_levelsOf = Lua::ToThread(L, 1);
|
|
Lua::Remove(L, 1);
|
|
if (!Lua::IsNumber(L, 1)) { // start defaults to 0 for a thread
|
|
Lua::PushNumber(L, 0);
|
|
Lua::Insert(L, 1);
|
|
}
|
|
}
|
|
const int results = engineDebugStack(L);
|
|
g_levelsOf = nullptr;
|
|
return results;
|
|
}
|
|
|
|
void Apply() {
|
|
const auto getStack = reinterpret_cast<const void *>(&GetStack);
|
|
for (const uintptr_t site :
|
|
{Offsets::CALL_DEBUGSTACK_GETSTACK_FIRST, Offsets::CALL_DEBUGSTACK_GETSTACK_TAIL_PROBE,
|
|
Offsets::CALL_DEBUGSTACK_GETSTACK_TAIL_START, Offsets::CALL_DEBUGSTACK_GETSTACK_TAIL_SKIP,
|
|
Offsets::CALL_DEBUGSTACK_GETSTACK_NEXT})
|
|
Common::RedirectCall(site, Offsets::LUA_GET_STACK, getStack);
|
|
Common::RedirectCall(Offsets::CALL_DEBUGSTACK_GETINFO, Offsets::LUA_GET_INFO,
|
|
reinterpret_cast<const void *>(&GetInfo));
|
|
}
|
|
|
|
void RegisterInGame() {
|
|
Apply();
|
|
Lua::RegisterGlobalFunction("debugstack", &Script_debugstack);
|
|
}
|
|
|
|
void RegisterGlue() {
|
|
Apply();
|
|
Lua::RegisterGlueFunction("debugstack", &Script_debugstack);
|
|
}
|
|
|
|
const Game::ModuleAutoRegister _autoreg{&RegisterInGame};
|
|
const Game::GlueModuleAutoRegister _autoregGlue{&RegisterGlue};
|
|
|
|
} // namespace
|
|
|
|
} // namespace BaseLib::DebugStack
|