From 25f6255d28732127ce012a641b17320fec4ea069 Mon Sep 17 00:00:00 2001 From: helixhorned Date: Tue, 1 Jan 2013 15:24:11 +0000 Subject: [PATCH] Lunatic: fix stack discipline. Preventing a continuously growing stack top and inevitable program termination. Also, commonize the error handling to live on the engine side. git-svn-id: https://svn.eduke32.com/eduke32@3352 1a8010ca-5511-0410-912e-c29ae57300e0 --- polymer/eduke32/build/include/lunatic.h | 4 +- polymer/eduke32/build/src/lunatic.c | 105 ++++++++++------ polymer/eduke32/source/lunatic/lunatic_game.c | 112 ++++++++++-------- polymer/eduke32/source/lunatic/lunatic_m32.c | 9 +- 4 files changed, 134 insertions(+), 96 deletions(-) diff --git a/polymer/eduke32/build/include/lunatic.h b/polymer/eduke32/build/include/lunatic.h index d63f7bec4..333e4e805 100644 --- a/polymer/eduke32/build/include/lunatic.h +++ b/polymer/eduke32/build/include/lunatic.h @@ -16,12 +16,14 @@ typedef struct // -- functions -- // helpers taking the lua_State directly: -void L_SetupDebugTraceback(lua_State *L); void L_PushDebugTraceback(lua_State *L); void L_CheckAndRegisterFunction(lua_State *L, void *regkeyaddr); +int L_HandleError(lua_State *L, int errcode, void (*ErrorPrintFunc)(const char *)); // Callback on Lua error. must be used immediately or strdup'd. void (*L_ErrorFunc)(const char *str); +// Out-of-memory handler, supposed to terminate the host program. +void (*L_OutOfMemFunc)(void); int L_CreateState(L_State *estate, const char *name, void (*StateSetupFunc)(lua_State *)); void L_DestroyState(L_State *estate); diff --git a/polymer/eduke32/build/src/lunatic.c b/polymer/eduke32/build/src/lunatic.c index d072cce1d..a4846cf41 100644 --- a/polymer/eduke32/build/src/lunatic.c +++ b/polymer/eduke32/build/src/lunatic.c @@ -25,7 +25,7 @@ void L_CheckAndRegisterFunction(lua_State *L, void *regkeyaddr) lua_settable(L, LUA_REGISTRYINDEX); // "registry[regkeyaddr] = ", pop 2 } -void L_SetupDebugTraceback(lua_State *L) +static void L_SetupDebugTraceback(lua_State *L) { // get debug.traceback lua_getglobal(L, "debug"); @@ -103,7 +103,17 @@ int L_CreateState(L_State *estate, const char *name, void (*StateSetupFunc)(lua_ return -2; } - StateSetupFunc(L); + luaL_openlibs(L); + L_SetupDebugTraceback(L); + if (StateSetupFunc) + StateSetupFunc(L); + + if (lua_gettop(L)==0) + L_PushDebugTraceback(L); + // Otherwise, it is assumed that StateSetupFunc pushed a custom traceback + // function onto the stack. + + Bassert(lua_gettop(L)==1); return 0; } @@ -120,7 +130,55 @@ void L_DestroyState(L_State *estate) estate->L = NULL; } +static void L_OnOutOfMem(void) +{ + extern void uninitengine(void); + OSD_Printf("Out of memory in Lunatic.\n"); + uninitengine(); + exit(127); +} + void (*L_ErrorFunc)(const char *) = NULL; +void (*L_OutOfMemFunc)(void) = L_OnOutOfMem; + +int L_HandleError(lua_State *L, int errcode, void (*ErrorPrintFunc)(const char *)) +{ + if (errcode == LUA_ERRMEM) + L_OutOfMemFunc(); + + if (errcode == LUA_ERRRUN || errcode == LUA_ERRERR) + { + if (lua_isboolean(L, -1)) + { + int32_t killit = lua_toboolean(L, -1); + lua_pop(L, 1); + return killit; + } + else + { + const char *errstr = (lua_type(L, -1)==LUA_TSTRING) ? + lua_tostring(L, -1) : "??? (error message not a string)"; + + ErrorPrintFunc(errstr); + if (L_ErrorFunc) + L_ErrorFunc(errstr); + lua_pop(L, 1); + return -1; + } + } + + /* unreachable */ +#ifdef NDEBUG + return 0; +#else + Bassert(0); +#endif +} + +static void L_ErrorPrint(const char *errmsg) +{ + OSD_Printf(OSD_ERROR "runtime error: %s\n", errmsg); +} int L_RunString(L_State *estate, char *buf, int dofreebuf) { @@ -128,9 +186,7 @@ int L_RunString(L_State *estate, char *buf, int dofreebuf) lua_State *L = estate->L; // -- lua -- - Bassert(lua_gettop(L)==0); - - L_PushDebugTraceback(L); + Bassert(lua_gettop(L)==1); // on top: a traceback function i = luaL_loadstring(L, buf); Bassert(lua_gettop(L)==2); @@ -138,47 +194,26 @@ int L_RunString(L_State *estate, char *buf, int dofreebuf) Bfree(buf); if (i == LUA_ERRMEM) - { - lua_pop(L, 2); - return -1; - } + L_OutOfMemFunc(); if (i == LUA_ERRSYNTAX) { OSD_Printf(OSD_ERROR "state \"%s\" syntax error: %s\n", estate->name, lua_tostring(L, -1)); // get err msg - lua_pop(L, 2); // pop errmsg and debug.traceback + lua_pop(L, 1); // pop errmsg return 3; } - // -- call the lua chunk! -- + // call the lua chunk! + i = lua_pcall(L, 0, 0, 1); + Bassert(lua_gettop(L) == 1 + (i!=0)); - i = lua_pcall(L, 0, 0, -2); - Bassert(lua_gettop(L) == 1+!!i); - Bassert(i != LUA_ERRERR); // we expect debug.traceback not to fail + if (i != 0) + L_HandleError(L, i, &L_ErrorPrint); - if (i == LUA_ERRMEM) // XXX: should be more sophisticated. Clean up stack? Do GC? - { - lua_pop(L, 2); - return -1; - } + Bassert(lua_gettop(L)==1); - if (i == LUA_ERRRUN) - { - // get error message if possible - const char *errstr = (lua_type(L, -1)==LUA_TSTRING) ? - lua_tostring(L, -1) : "??? (errmsg not a string)"; - - OSD_Printf(OSD_ERROR "state \"%s\" runtime error: %s\n", estate->name, errstr); - if (L_ErrorFunc) - L_ErrorFunc(errstr); - lua_pop(L, 2); // pop errmsg and debug.traceback - return 4; - } - - lua_pop(L, 1); - - return 0; + return i ? 4 : 0; } // -1: alloc failure diff --git a/polymer/eduke32/source/lunatic/lunatic_game.c b/polymer/eduke32/source/lunatic/lunatic_game.c index 1cff69db2..733c18432 100644 --- a/polymer/eduke32/source/lunatic/lunatic_game.c +++ b/polymer/eduke32/source/lunatic/lunatic_game.c @@ -31,8 +31,8 @@ double g_eventTotalMs[MAXEVENTS], g_actorTotalMs[MAXTILES]; // forward-decls... -static int32_t SetEvent_luacf(lua_State *L); -static int32_t SetActor_luacf(lua_State *L); +static int32_t SetEvent_CF(lua_State *L); +static int32_t SetActor_CF(lua_State *L); // in lpeg.o extern int luaopen_lpeg(lua_State *L); @@ -171,6 +171,12 @@ static void El_OnError(const char *str) } } +static void El_OnOutOfMem(void) +{ + extern void G_GameExit(const char *msg); + G_GameExit("Out of memory in Lunatic."); +} + void El_ClearErrors(void) { int32_t i; @@ -194,27 +200,48 @@ void El_DisplayErrors(void) ////////// STATE CREATION/DESTRUCTIION ////////// +static int our_traceback_CF(lua_State *L) +{ + Bassert(lua_gettop(L)==1); + + if (lua_type(L, 1)==LUA_TBOOLEAN) + { + lua_pushvalue(L, 1); // duplicate it + return 1; // and tell Lua to return it + } + + Bassert(lua_type(L, 1)==LUA_TSTRING); + + // call debug.traceback with the string + L_PushDebugTraceback(L); + lua_pushvalue(L, 1); + lua_call(L, 1, 1); + Bassert(lua_gettop(L)==2); // Lua will pop off args + + return 1; +} + static void El_StateSetup(lua_State *L) { - luaL_openlibs(L); // NOTE: we set up the sandbox in defs.ilua luaopen_lpeg(L); lua_pop(L, lua_gettop(L)); // pop off whatever lpeg leaves on the stack - L_SetupDebugTraceback(L); - // create misc. global functions in the Lua state - lua_pushcfunction(L, SetEvent_luacf); + lua_pushcfunction(L, SetEvent_CF); lua_setglobal(L, "gameevent_internal"); - lua_pushcfunction(L, SetActor_luacf); + lua_pushcfunction(L, SetActor_CF); lua_setglobal(L, "gameactor_internal"); Bassert(lua_gettop(L)==0); + + lua_pushcfunction(L, &our_traceback_CF); } // 0: success, <0: failure int32_t El_CreateState(L_State *estate, const char *name) { L_ErrorFunc = El_OnError; + L_OutOfMemFunc = El_OnOutOfMem; return L_CreateState(estate, name, &El_StateSetup); } @@ -228,7 +255,7 @@ void El_DestroyState(L_State *estate) ////////// Lua_CFunctions ////////// // gameevent(EVENT_..., lua_function) -static int32_t SetEvent_luacf(lua_State *L) +static int32_t SetEvent_CF(lua_State *L) { int32_t eventidx; @@ -243,7 +270,7 @@ static int32_t SetEvent_luacf(lua_State *L) } // gameactor(actortile, strength, act, mov, movflags, lua_function) -static int32_t SetActor_luacf(lua_State *L) +static int32_t SetActor_CF(lua_State *L) { int32_t actortile, strength, movflags; const con_action_t *act; @@ -281,8 +308,8 @@ static int32_t call_regd_function3(lua_State *L, void *keyaddr, int32_t iActor, int32_t iPlayer, int32_t lDist) { int32_t i; -#ifdef DEBUGGINGAIDS - L_PushDebugTraceback(L); +#if !defined NDEBUG + int32_t top = lua_gettop(L); #endif // get the Lua function from the registry lua_pushlightuserdata(L, keyaddr); @@ -293,22 +320,24 @@ static int32_t call_regd_function3(lua_State *L, void *keyaddr, lua_pushinteger(L, lDist); // -- call it! -- - #ifdef DEBUGGINGAIDS i = lua_pcall(L, 3, 0, 1); #else i = lua_pcall(L, 3, 0, 0); #endif - if (i == LUA_ERRMEM) - { - lua_pop(L, 1); - // XXX: should be more sophisticated. Clean up stack? Do GC? - } + Bassert(lua_gettop(L) == top+(i!=0)); return i; } +static int32_t g_eventIdx = 0; +static void El_EventErrorPrint(const char *errmsg) +{ + OSD_Printf(OSD_ERROR "event \"%s\" runtime error: %s\n", + EventNames[g_eventIdx], errmsg); +} + int32_t El_CallEvent(L_State *estate, int32_t eventidx, int32_t iActor, int32_t iPlayer, int32_t lDist) { // XXX: estate must be the one where the events were registered... @@ -321,29 +350,22 @@ int32_t El_CallEvent(L_State *estate, int32_t eventidx, int32_t iActor, int32_t i = call_regd_function3(L, &g_elEvents[eventidx], iActor, iPlayer, lDist); g_elCallDepth--; - if (i == LUA_ERRRUN) + if (i != 0) { - const char *errstr; - - if (lua_isboolean(L, -1)) - { - lua_pop(L, 1); - return 0; - } - - errstr = lua_tostring(L, -1); - Bassert(lua_type(L, -1)==LUA_TSTRING); - OSD_Printf(OSD_ERROR "event \"%s\" (state \"%s\") runtime error: %s\n", - EventNames[eventidx], estate->name, errstr); - if (L_ErrorFunc) - L_ErrorFunc(errstr); - lua_pop(L, 1); - return -1; + g_eventIdx = eventidx; + return L_HandleError(L, i, &El_EventErrorPrint); } return 0; } +static int32_t g_actorTile, g_iActor; +static void El_ActorErrorPrint(const char *errmsg) +{ + OSD_Printf(OSD_ERROR "actor %d (sprite %d) runtime error: %s\n", + g_actorTile, g_iActor, errmsg); +} + int32_t El_CallActor(L_State *estate, int32_t actortile, int32_t iActor, int32_t iPlayer, int32_t lDist) { lua_State *const L = estate->L; @@ -353,25 +375,11 @@ int32_t El_CallActor(L_State *estate, int32_t actortile, int32_t iActor, int32_t i = call_regd_function3(L, &g_elActors[actortile], iActor, iPlayer, lDist); g_elCallDepth--; - if (i == LUA_ERRRUN) + if (i != 0) { - const char *errstr; - - if (lua_isboolean(L, -1)) - { - int32_t killit = lua_toboolean(L, -1); - lua_pop(L, 1); - return killit; - } - - Bassert(lua_type(L, -1)==LUA_TSTRING); - errstr = lua_tostring(L, -1); - OSD_Printf(OSD_ERROR "actor %d (sprite %d, state \"%s\") runtime error: %s\n", - actortile, iActor, estate->name, errstr); - if (L_ErrorFunc) - L_ErrorFunc(errstr); - lua_pop(L, 1); - return -1; + g_actorTile = actortile; + g_iActor = iActor; + return L_HandleError(L, i, &El_ActorErrorPrint); } return 0; diff --git a/polymer/eduke32/source/lunatic/lunatic_m32.c b/polymer/eduke32/source/lunatic/lunatic_m32.c index d2ce2e690..e274c13f1 100644 --- a/polymer/eduke32/source/lunatic/lunatic_m32.c +++ b/polymer/eduke32/source/lunatic/lunatic_m32.c @@ -5,16 +5,9 @@ #include "lunatic_m32.h" -static void Em_StateSetup(lua_State *L) -{ - luaL_openlibs(L); - L_SetupDebugTraceback(L); -} - - int Em_CreateState(L_State *estate) { - return L_CreateState(estate, "m32", &Em_StateSetup); + return L_CreateState(estate, "m32", NULL); } void Em_DestroyState(L_State *estate)