mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-24 10:40:46 +00:00
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
This commit is contained in:
parent
b6fdabc21f
commit
25f6255d28
4 changed files with 134 additions and 96 deletions
|
@ -16,12 +16,14 @@ typedef struct
|
||||||
// -- functions --
|
// -- functions --
|
||||||
|
|
||||||
// helpers taking the lua_State directly:
|
// helpers taking the lua_State directly:
|
||||||
void L_SetupDebugTraceback(lua_State *L);
|
|
||||||
void L_PushDebugTraceback(lua_State *L);
|
void L_PushDebugTraceback(lua_State *L);
|
||||||
void L_CheckAndRegisterFunction(lua_State *L, void *regkeyaddr);
|
void L_CheckAndRegisterFunction(lua_State *L, void *regkeyaddr);
|
||||||
|
int L_HandleError(lua_State *L, int errcode, void (*ErrorPrintFunc)(const char *));
|
||||||
|
|
||||||
// Callback on Lua error. <str> must be used immediately or strdup'd.
|
// Callback on Lua error. <str> must be used immediately or strdup'd.
|
||||||
void (*L_ErrorFunc)(const char *str);
|
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 *));
|
int L_CreateState(L_State *estate, const char *name, void (*StateSetupFunc)(lua_State *));
|
||||||
void L_DestroyState(L_State *estate);
|
void L_DestroyState(L_State *estate);
|
||||||
|
|
|
@ -25,7 +25,7 @@ void L_CheckAndRegisterFunction(lua_State *L, void *regkeyaddr)
|
||||||
lua_settable(L, LUA_REGISTRYINDEX); // "registry[regkeyaddr] = <lua function>", pop 2
|
lua_settable(L, LUA_REGISTRYINDEX); // "registry[regkeyaddr] = <lua function>", pop 2
|
||||||
}
|
}
|
||||||
|
|
||||||
void L_SetupDebugTraceback(lua_State *L)
|
static void L_SetupDebugTraceback(lua_State *L)
|
||||||
{
|
{
|
||||||
// get debug.traceback
|
// get debug.traceback
|
||||||
lua_getglobal(L, "debug");
|
lua_getglobal(L, "debug");
|
||||||
|
@ -103,7 +103,17 @@ int L_CreateState(L_State *estate, const char *name, void (*StateSetupFunc)(lua_
|
||||||
return -2;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -120,7 +130,55 @@ void L_DestroyState(L_State *estate)
|
||||||
estate->L = NULL;
|
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_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)
|
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_State *L = estate->L;
|
||||||
|
|
||||||
// -- lua --
|
// -- lua --
|
||||||
Bassert(lua_gettop(L)==0);
|
Bassert(lua_gettop(L)==1); // on top: a traceback function
|
||||||
|
|
||||||
L_PushDebugTraceback(L);
|
|
||||||
|
|
||||||
i = luaL_loadstring(L, buf);
|
i = luaL_loadstring(L, buf);
|
||||||
Bassert(lua_gettop(L)==2);
|
Bassert(lua_gettop(L)==2);
|
||||||
|
@ -138,47 +194,26 @@ int L_RunString(L_State *estate, char *buf, int dofreebuf)
|
||||||
Bfree(buf);
|
Bfree(buf);
|
||||||
|
|
||||||
if (i == LUA_ERRMEM)
|
if (i == LUA_ERRMEM)
|
||||||
{
|
L_OutOfMemFunc();
|
||||||
lua_pop(L, 2);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == LUA_ERRSYNTAX)
|
if (i == LUA_ERRSYNTAX)
|
||||||
{
|
{
|
||||||
OSD_Printf(OSD_ERROR "state \"%s\" syntax error: %s\n", estate->name,
|
OSD_Printf(OSD_ERROR "state \"%s\" syntax error: %s\n", estate->name,
|
||||||
lua_tostring(L, -1)); // get err msg
|
lua_tostring(L, -1)); // get err msg
|
||||||
lua_pop(L, 2); // pop errmsg and debug.traceback
|
lua_pop(L, 1); // pop errmsg
|
||||||
return 3;
|
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);
|
if (i != 0)
|
||||||
Bassert(lua_gettop(L) == 1+!!i);
|
L_HandleError(L, i, &L_ErrorPrint);
|
||||||
Bassert(i != LUA_ERRERR); // we expect debug.traceback not to fail
|
|
||||||
|
|
||||||
if (i == LUA_ERRMEM) // XXX: should be more sophisticated. Clean up stack? Do GC?
|
Bassert(lua_gettop(L)==1);
|
||||||
{
|
|
||||||
lua_pop(L, 2);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == LUA_ERRRUN)
|
return i ? 4 : 0;
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -1: alloc failure
|
// -1: alloc failure
|
||||||
|
|
|
@ -31,8 +31,8 @@ double g_eventTotalMs[MAXEVENTS], g_actorTotalMs[MAXTILES];
|
||||||
|
|
||||||
|
|
||||||
// forward-decls...
|
// forward-decls...
|
||||||
static int32_t SetEvent_luacf(lua_State *L);
|
static int32_t SetEvent_CF(lua_State *L);
|
||||||
static int32_t SetActor_luacf(lua_State *L);
|
static int32_t SetActor_CF(lua_State *L);
|
||||||
|
|
||||||
// in lpeg.o
|
// in lpeg.o
|
||||||
extern int luaopen_lpeg(lua_State *L);
|
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)
|
void El_ClearErrors(void)
|
||||||
{
|
{
|
||||||
int32_t i;
|
int32_t i;
|
||||||
|
@ -194,27 +200,48 @@ void El_DisplayErrors(void)
|
||||||
|
|
||||||
////////// STATE CREATION/DESTRUCTIION //////////
|
////////// 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)
|
static void El_StateSetup(lua_State *L)
|
||||||
{
|
{
|
||||||
luaL_openlibs(L); // NOTE: we set up the sandbox in defs.ilua
|
|
||||||
luaopen_lpeg(L);
|
luaopen_lpeg(L);
|
||||||
lua_pop(L, lua_gettop(L)); // pop off whatever lpeg leaves on the stack
|
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
|
// create misc. global functions in the Lua state
|
||||||
lua_pushcfunction(L, SetEvent_luacf);
|
lua_pushcfunction(L, SetEvent_CF);
|
||||||
lua_setglobal(L, "gameevent_internal");
|
lua_setglobal(L, "gameevent_internal");
|
||||||
lua_pushcfunction(L, SetActor_luacf);
|
lua_pushcfunction(L, SetActor_CF);
|
||||||
lua_setglobal(L, "gameactor_internal");
|
lua_setglobal(L, "gameactor_internal");
|
||||||
|
|
||||||
Bassert(lua_gettop(L)==0);
|
Bassert(lua_gettop(L)==0);
|
||||||
|
|
||||||
|
lua_pushcfunction(L, &our_traceback_CF);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0: success, <0: failure
|
// 0: success, <0: failure
|
||||||
int32_t El_CreateState(L_State *estate, const char *name)
|
int32_t El_CreateState(L_State *estate, const char *name)
|
||||||
{
|
{
|
||||||
L_ErrorFunc = El_OnError;
|
L_ErrorFunc = El_OnError;
|
||||||
|
L_OutOfMemFunc = El_OnOutOfMem;
|
||||||
|
|
||||||
return L_CreateState(estate, name, &El_StateSetup);
|
return L_CreateState(estate, name, &El_StateSetup);
|
||||||
}
|
}
|
||||||
|
@ -228,7 +255,7 @@ void El_DestroyState(L_State *estate)
|
||||||
////////// Lua_CFunctions //////////
|
////////// Lua_CFunctions //////////
|
||||||
|
|
||||||
// gameevent(EVENT_..., lua_function)
|
// gameevent(EVENT_..., lua_function)
|
||||||
static int32_t SetEvent_luacf(lua_State *L)
|
static int32_t SetEvent_CF(lua_State *L)
|
||||||
{
|
{
|
||||||
int32_t eventidx;
|
int32_t eventidx;
|
||||||
|
|
||||||
|
@ -243,7 +270,7 @@ static int32_t SetEvent_luacf(lua_State *L)
|
||||||
}
|
}
|
||||||
|
|
||||||
// gameactor(actortile, strength, act, mov, movflags, lua_function)
|
// 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;
|
int32_t actortile, strength, movflags;
|
||||||
const con_action_t *act;
|
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 iActor, int32_t iPlayer, int32_t lDist)
|
||||||
{
|
{
|
||||||
int32_t i;
|
int32_t i;
|
||||||
#ifdef DEBUGGINGAIDS
|
#if !defined NDEBUG
|
||||||
L_PushDebugTraceback(L);
|
int32_t top = lua_gettop(L);
|
||||||
#endif
|
#endif
|
||||||
// get the Lua function from the registry
|
// get the Lua function from the registry
|
||||||
lua_pushlightuserdata(L, keyaddr);
|
lua_pushlightuserdata(L, keyaddr);
|
||||||
|
@ -293,22 +320,24 @@ static int32_t call_regd_function3(lua_State *L, void *keyaddr,
|
||||||
lua_pushinteger(L, lDist);
|
lua_pushinteger(L, lDist);
|
||||||
|
|
||||||
// -- call it! --
|
// -- call it! --
|
||||||
|
|
||||||
#ifdef DEBUGGINGAIDS
|
#ifdef DEBUGGINGAIDS
|
||||||
i = lua_pcall(L, 3, 0, 1);
|
i = lua_pcall(L, 3, 0, 1);
|
||||||
#else
|
#else
|
||||||
i = lua_pcall(L, 3, 0, 0);
|
i = lua_pcall(L, 3, 0, 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (i == LUA_ERRMEM)
|
Bassert(lua_gettop(L) == top+(i!=0));
|
||||||
{
|
|
||||||
lua_pop(L, 1);
|
|
||||||
// XXX: should be more sophisticated. Clean up stack? Do GC?
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
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)
|
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...
|
// 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);
|
i = call_regd_function3(L, &g_elEvents[eventidx], iActor, iPlayer, lDist);
|
||||||
g_elCallDepth--;
|
g_elCallDepth--;
|
||||||
|
|
||||||
if (i == LUA_ERRRUN)
|
if (i != 0)
|
||||||
{
|
{
|
||||||
const char *errstr;
|
g_eventIdx = eventidx;
|
||||||
|
return L_HandleError(L, i, &El_EventErrorPrint);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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)
|
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;
|
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);
|
i = call_regd_function3(L, &g_elActors[actortile], iActor, iPlayer, lDist);
|
||||||
g_elCallDepth--;
|
g_elCallDepth--;
|
||||||
|
|
||||||
if (i == LUA_ERRRUN)
|
if (i != 0)
|
||||||
{
|
{
|
||||||
const char *errstr;
|
g_actorTile = actortile;
|
||||||
|
g_iActor = iActor;
|
||||||
if (lua_isboolean(L, -1))
|
return L_HandleError(L, i, &El_ActorErrorPrint);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -5,16 +5,9 @@
|
||||||
#include "lunatic_m32.h"
|
#include "lunatic_m32.h"
|
||||||
|
|
||||||
|
|
||||||
static void Em_StateSetup(lua_State *L)
|
|
||||||
{
|
|
||||||
luaL_openlibs(L);
|
|
||||||
L_SetupDebugTraceback(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int Em_CreateState(L_State *estate)
|
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)
|
void Em_DestroyState(L_State *estate)
|
||||||
|
|
Loading…
Reference in a new issue