Files

117 lines
3.8 KiB
C++
Raw Permalink Normal View History

2021-01-02 13:31:18 -05:00
#include "traceback.hpp"
#include <cstring>
#include <cassert>
2021-01-02 13:31:18 -05:00
#define TRACEBACK_LEVELS1 12
#define TRACEBACK_LEVELS2 10
2021-02-17 13:38:22 -05:00
// Call this with the error message on top of the stack.
// The error message is replaced with a traceback.
//
int traceback_coroutine(lua_State *L) {
lua_checkstack(L, 20);
2021-01-02 13:31:18 -05:00
int top = lua_gettop(L);
2021-02-17 13:38:22 -05:00
// Convert message to a string
if (!lua_tostring(L, top)) {
luaL_callmeta(L, top, "__tostring");
// If callmeta didn't produce exactly one string, clear the stack
// and push "unknown error"
if ((lua_gettop(L) == top + 1) && (lua_tostring(L, -1))) {
lua_remove(L, top);
} else {
lua_settop(L, top - 1);
lua_pushstring(L, "unknown error");
}
2021-01-02 13:31:18 -05:00
}
// Append the traceback.
lua_Debug ar;
int firstpart = 1;
2021-02-25 14:58:29 -05:00
bool any = false;
for (int level = 0; lua_getstack(L, level, &ar); level++) {
2021-01-02 13:31:18 -05:00
if (level > TRACEBACK_LEVELS1 && firstpart) {
/* no more than `LEVELS2' more levels? */
if (!lua_getstack(L, level + TRACEBACK_LEVELS2, &ar))
2021-01-02 13:31:18 -05:00
level--; /* keep going */
else {
lua_pushliteral(L, "\n\t..."); /* too many levels */
while (lua_getstack(L, level + TRACEBACK_LEVELS2, &ar)) /* find last levels */
2021-01-02 13:31:18 -05:00
level++;
}
firstpart = 0;
continue;
}
2026-06-02 18:34:03 -04:00
lua_getinfo(L, "Snlf", &ar);
eng::string storedname;
{
lua_getfield(L, LUA_REGISTRYINDEX, "funcnames");
lua_pushvalue(L, -2);
lua_rawget(L, -2);
const char *fname = lua_tostring(L, -1);
if (fname) storedname = fname;
lua_pop(L, 3);
}
2021-02-25 14:58:29 -05:00
if ((!any) && (*ar.what == 'C') && (ar.name != 0)) {
if (strcmp(ar.name, "__newindex") == 0) continue;
}
2021-01-02 13:31:18 -05:00
if ((ar.currentline > 0) || (*ar.namewhat != 0) || (*ar.what != 'C')) {
2021-02-25 14:58:29 -05:00
any = true;
2021-01-02 13:31:18 -05:00
lua_pushliteral(L, "\n\t");
2026-06-02 18:34:03 -04:00
if (strcmp(ar.short_src, "<console>")==0) {
lua_pushstring(L, "in the console in");
} else if (strcmp(ar.short_src, "[C]")==0) {
lua_pushstring(L, "in builtin C++ ");
} else if (ar.currentline > 0) {
lua_pushfstring(L, "in %s line %d in", ar.short_src, ar.currentline);
} else {
lua_pushfstring(L, "in %s in", ar.short_src);
2026-05-21 19:40:30 -04:00
}
2026-06-02 18:34:03 -04:00
if (!storedname.empty()) {
lua_pushfstring(L, " function " LUA_QS, storedname.c_str());
} else if (*ar.namewhat != 0) {
lua_pushfstring(L, " function " LUA_QS, ar.name);
} else if (*ar.what == 'm') {
lua_pushfstring(L, " top-level expression ");
} else {
lua_pushliteral(L, " unknown function");
2021-01-02 13:31:18 -05:00
}
2026-06-02 18:34:03 -04:00
2021-02-17 13:38:22 -05:00
if (1 + lua_gettop(L) - top > 5) {
lua_concat(L, 1 + lua_gettop(L) - top);
2021-01-02 13:31:18 -05:00
}
}
}
lua_pushstring(L, "\n");
2021-02-17 13:38:22 -05:00
if (1 + lua_gettop(L) - top > 1) {
lua_concat(L, 1 + lua_gettop(L) - top);
2021-01-02 13:31:18 -05:00
}
2021-02-17 13:38:22 -05:00
return 1;
2021-01-02 13:31:18 -05:00
}
eng::string traceback_pcall(lua_State *L, int narg, int nret) {
2021-01-02 13:31:18 -05:00
int status;
int base = lua_gettop(L) - narg; /* function index */
lua_pushcfunction(L, traceback_coroutine); /* push traceback function */
2021-01-02 13:31:18 -05:00
lua_insert(L, base); /* put it under chunk and args */
status = lua_pcall(L, narg, nret, base);
lua_remove(L, base); /* remove traceback function */
if (status != LUA_OK) {
const char *msg = lua_tostring(L, -1);
if ((msg == NULL) || (msg[0] == 0)) {
msg = "unknown error";
}
eng::string result = msg;
assert(result != "attempt to yield from outside a coroutine");
return result;
}
return "";
2021-01-02 13:31:18 -05:00
}