#include "traceback.hpp" #include #include #define TRACEBACK_LEVELS1 12 #define TRACEBACK_LEVELS2 10 // 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); int top = lua_gettop(L); // 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"); } } // Append the traceback. lua_Debug ar; int firstpart = 1; bool any = false; for (int level = 0; lua_getstack(L, level, &ar); level++) { if (level > TRACEBACK_LEVELS1 && firstpart) { /* no more than `LEVELS2' more levels? */ if (!lua_getstack(L, level + TRACEBACK_LEVELS2, &ar)) level--; /* keep going */ else { lua_pushliteral(L, "\n\t..."); /* too many levels */ while (lua_getstack(L, level + TRACEBACK_LEVELS2, &ar)) /* find last levels */ level++; } firstpart = 0; continue; } lua_getinfo(L, "Snl", &ar); if ((!any) && (*ar.what == 'C') && (ar.name != 0)) { if (strcmp(ar.name, "__newindex") == 0) continue; } if ((ar.currentline > 0) || (*ar.namewhat != 0) || (*ar.what != 'C')) { any = true; lua_pushliteral(L, "\n\t"); if (strcmp(ar.short_src, "")==0) { lua_pushstring(L, "in the console"); } else { lua_pushfstring(L, "in %s", ar.short_src); if (ar.currentline > 0) lua_pushfstring(L, " line %d", ar.currentline); } if (*ar.namewhat != '\0') /* is there a name? */ lua_pushfstring(L, " in function " LUA_QS, ar.name); else { if (*ar.what == 'm') /* main? */ lua_pushfstring(L, " in top-level expression "); else if (*ar.what == 'C' || *ar.what == 't') lua_pushliteral(L, " in unknown C function"); else lua_pushfstring(L, " in function on line %d", ar.linedefined); } if (1 + lua_gettop(L) - top > 5) { lua_concat(L, 1 + lua_gettop(L) - top); } } } lua_pushstring(L, "\n"); if (1 + lua_gettop(L) - top > 1) { lua_concat(L, 1 + lua_gettop(L) - top); } return 1; } eng::string traceback_pcall(lua_State *L, int narg, int nret) { int status; int base = lua_gettop(L) - narg; /* function index */ lua_pushcfunction(L, traceback_coroutine); /* push traceback function */ 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 ""; }