Lunatic: make baselib functions used in defs.c local, be more disciplined with stack.

Also, print a backtrace if we return to C with an error.

git-svn-id: https://svn.eduke32.com/eduke32@2827 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
helixhorned 2012-07-13 18:20:35 +00:00
parent 420e61d0f8
commit b86dbc0818
3 changed files with 97 additions and 37 deletions

View file

@ -504,7 +504,11 @@ end
-- _G tweaks -- pull in only 'safe' stuff -- _G tweaks -- pull in only 'safe' stuff
local G_ = {} -- our soon-to-be global environment local G_ = {} -- our soon-to-be global environment
local oG = _G -- old genv, to access thrown-out functions later in this chunk
-- Old global environment, to access thrown-out functions later in this chunk.
-- Also, inside this file, we must refer to functions like 'pairs' by using
-- this table, since user code could later do pairs=nil.
local oG = _G
G_.coroutine = coroutine G_.coroutine = coroutine
G_.assert = assert G_.assert = assert
@ -565,16 +569,25 @@ local lunacon = require("lunacon")
-- change the environment of this chunk to the table G_ -- change the environment of this chunk to the table G_
-- NOTE: all references to global variables from this point on
-- (also in functions created after this point) refer to G_ !
setfenv(1, G_) setfenv(1, G_)
-- We MUST remember to call base lib functions through oG here.
-- 'tostring' is a special case: it is used in 'print', i.e. the global 'tostring',
-- which is looked up in the current environment! Thus, we must REMOVE it and
-- 'print' for release, or write our own, atomic 'print'.
local error = oG.error
local type = oG.type
local pairs = oG.pairs
-- print keys and values of a table -- print keys and values of a table
local function printkv(label, table) local function printkv(label, table)
print('========== Keys and values of '..label) oG.print('========== Keys and values of '..label)
for k,v in pairs(table) do for k,v in oG.pairs(table) do
print(k .. ': ' .. tostring(v)) oG.print(k .. ': ' .. tostring(v))
end end
print('----------') oG.print('----------')
end end
--printkv('_G AFTER SETFENV', _G) --printkv('_G AFTER SETFENV', _G)
@ -854,7 +867,7 @@ local function loadGamevarsString(string)
gamevarNames = {}; -- clear gamevars gamevarNames = {}; -- clear gamevars
--]=] --]=]
assert(oG.loadstring(string))() oG.assert(oG.loadstring(string))()
end end
@ -876,11 +889,11 @@ DBG_.loadGamevarsString = loadGamevarsString
-- XXX: but user modules will want to do "function thisfunc() ... " -- XXX: but user modules will want to do "function thisfunc() ... "
oG.setmetatable( oG.setmetatable(
G_, { G_, {
__newindex = function (_, n) __newindex = function (_1, n, _2)
error("attempt to write to undeclared variable "..n, 2) error("attempt to write to undeclared variable '"..n.."'", 2)
end, end,
__index = function (_, n) __index = function (_, n)
error("attempt to read undeclared variable "..n, 2) error("attempt to read undeclared variable '"..n.."'", 2)
end, end,
}) })

View file

@ -20,6 +20,9 @@ uint8_t g_elEvents[MAXEVENTS];
// same thing for actors: // same thing for actors:
uint8_t g_elActors[MAXTILES]; uint8_t g_elActors[MAXTILES];
// Lua-registry key for debug.traceback
static uint8_t debug_traceback_key;
// forward-decls... // forward-decls...
static int32_t SetEvent_luacf(lua_State *L); static int32_t SetEvent_luacf(lua_State *L);
@ -29,14 +32,27 @@ static int32_t SetActor_luacf(lua_State *L);
extern int luaopen_lpeg(lua_State *L); extern int luaopen_lpeg(lua_State *L);
static void check_and_register_function(lua_State *L, void *keyaddr)
{
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushlightuserdata(L, keyaddr); // 3, push address
lua_pushvalue(L, -2); // 4, push copy of lua function
lua_settable(L, LUA_REGISTRYINDEX); // "registry[keyaddr] = <lua function>", pop 2
}
// 0: success, <0: failure // 0: success, <0: failure
int32_t El_CreateState(El_State *estate, const char *name) int32_t El_CreateState(El_State *estate, const char *name)
{ {
lua_State *L;
estate->name = Bstrdup(name); estate->name = Bstrdup(name);
if (!estate->name) if (!estate->name)
return -1; return -1;
estate->L = luaL_newstate(); L = estate->L = luaL_newstate();
if (!estate->L) if (!estate->L)
{ {
@ -45,14 +61,24 @@ int32_t El_CreateState(El_State *estate, const char *name)
return -2; return -2;
} }
luaL_openlibs(estate->L); // NOTE: we set up the sandbox in defs.ilua luaL_openlibs(L); // NOTE: we set up the sandbox in defs.ilua
luaopen_lpeg(estate->L); luaopen_lpeg(L);
lua_pop(L, lua_gettop(L)); // pop off whatever lpeg leaves on the stack
// get debug.traceback
lua_getglobal(L, "debug");
lua_getfield(L, -1, "traceback");
Bassert(lua_isfunction(L, -1));
check_and_register_function(L, &debug_traceback_key);
lua_pop(L, 2);
// create misc. global functions in the Lua state // create misc. global functions in the Lua state
lua_pushcfunction(estate->L, SetEvent_luacf); lua_pushcfunction(L, SetEvent_luacf);
lua_setglobal(estate->L, "gameevent"); lua_setglobal(L, "gameevent");
lua_pushcfunction(estate->L, SetActor_luacf); lua_pushcfunction(L, SetActor_luacf);
lua_setglobal(estate->L, "gameactor"); lua_setglobal(L, "gameactor");
Bassert(lua_gettop(L)==0);
return 0; return 0;
} }
@ -110,57 +136,69 @@ int32_t El_RunOnce(El_State *estate, const char *fn)
buf[flen] = 0; buf[flen] = 0;
// -- lua -- // -- lua --
Bassert(lua_gettop(L)==0);
// get debug.traceback
lua_pushlightuserdata(L, &debug_traceback_key);
lua_gettable(L, LUA_REGISTRYINDEX);
Bassert(lua_isfunction(L, -1));
i = luaL_loadstring(L, buf); i = luaL_loadstring(L, buf);
Bassert(lua_gettop(L)==2);
Bfree(buf); Bfree(buf);
if (i == LUA_ERRMEM) if (i == LUA_ERRMEM)
{
lua_pop(L, 2);
return -1; return -1;
}
if (i == LUA_ERRSYNTAX) if (i == LUA_ERRSYNTAX)
{ {
OSD_Printf("state \"%s\" syntax error: %s\n", estate->name, OSD_Printf("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, 1); lua_pop(L, 2); // pop errmsg and debug.traceback
return 3; return 3;
} }
// -- call the lua chunk! -- // -- call the lua chunk! --
i = lua_pcall(L, 0, 0, 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 == LUA_ERRMEM) // XXX: should be more sophisticated. Clean up stack? Do GC? if (i == LUA_ERRMEM) // XXX: should be more sophisticated. Clean up stack? Do GC?
{
lua_pop(L, 2);
return -1; return -1;
}
if (i == LUA_ERRRUN) if (i == LUA_ERRRUN)
{ {
Bassert(lua_type(L, -1)==LUA_TSTRING); Bassert(lua_type(L, -1)==LUA_TSTRING);
OSD_Printf("state \"%s\" runtime error: %s\n", estate->name, OSD_Printf("state \"%s\" runtime error: %s\n", estate->name,
lua_tostring(L, -1)); // get err msg lua_tostring(L, -1)); // get err msg
lua_pop(L, 1); lua_pop(L, 2); // pop errmsg and debug.traceback
return 4; return 4;
} }
lua_pop(L, 1);
return 0; return 0;
} }
static void check_and_register_function(lua_State *L, void *keyaddr)
{
luaL_checktype(L, 2, LUA_TFUNCTION);
lua_pushlightuserdata(L, keyaddr); // 3, push address
lua_pushvalue(L, 2); // 4, push copy of lua function
lua_settable(L, LUA_REGISTRYINDEX); // "registry[keyaddr] = <lua function>"
}
////////// Lua_CFunctions ////////// ////////// Lua_CFunctions //////////
// gameevent(EVENT_..., lua_function) // gameevent(EVENT_..., lua_function)
static int32_t SetEvent_luacf(lua_State *L) static int32_t SetEvent_luacf(lua_State *L)
{ {
int32_t eventidx = luaL_checkint(L, 1); int32_t eventidx;
if (lua_gettop(L) != 2)
luaL_error(L, "gameevent: must pass exactly two arguments");
eventidx = luaL_checkint(L, 1);
luaL_argcheck(L, (unsigned)eventidx < MAXEVENTS, 1, "must be an event number (0 .. MAXEVENTS-1)"); luaL_argcheck(L, (unsigned)eventidx < MAXEVENTS, 1, "must be an event number (0 .. MAXEVENTS-1)");
check_and_register_function(L, &g_elEvents[eventidx]); check_and_register_function(L, &g_elEvents[eventidx]);
@ -172,7 +210,12 @@ static int32_t SetEvent_luacf(lua_State *L)
// gameactor(<actortile>, lua_function) // gameactor(<actortile>, lua_function)
static int32_t SetActor_luacf(lua_State *L) static int32_t SetActor_luacf(lua_State *L)
{ {
int32_t actortile = luaL_checkint(L, 1); int32_t actortile;
if (lua_gettop(L) != 2)
luaL_error(L, "gameactor: must pass exactly two arguments");
actortile = luaL_checkint(L, 1);
luaL_argcheck(L, (unsigned)actortile < MAXTILES, 1, "must be an tile number (0 .. MAXTILES-1)"); luaL_argcheck(L, (unsigned)actortile < MAXTILES, 1, "must be an tile number (0 .. MAXTILES-1)");
check_and_register_function(L, &g_elActors[actortile]); check_and_register_function(L, &g_elActors[actortile]);
@ -188,8 +231,9 @@ static int32_t call_registered_function3(lua_State *L, void *keyaddr,
{ {
int32_t i; int32_t i;
lua_pushlightuserdata(L, keyaddr); // push address // get the Lua function from the registry
lua_gettable(L, LUA_REGISTRYINDEX); // get lua function lua_pushlightuserdata(L, keyaddr);
lua_gettable(L, LUA_REGISTRYINDEX);
lua_pushinteger(L, iActor); lua_pushinteger(L, iActor);
lua_pushinteger(L, iPlayer); lua_pushinteger(L, iPlayer);
@ -200,6 +244,7 @@ static int32_t call_registered_function3(lua_State *L, void *keyaddr,
i = lua_pcall(L, 3, 0, 0); i = lua_pcall(L, 3, 0, 0);
if (i == LUA_ERRMEM) if (i == LUA_ERRMEM)
{ {
lua_pop(L, 1);
// XXX: should be more sophisticated. Clean up stack? Do GC? // XXX: should be more sophisticated. Clean up stack? Do GC?
} }

View file

@ -89,10 +89,12 @@ if (vol==1 and lev==8) then
end end
--]] --]]
--tostring = nil -- REMEMBER
--DBG_.printkv('_G in test.elua', _G) --DBG_.printkv('_G in test.elua', _G)
checkfail('local i = sprite["qwe"]') -- indexing struct array with non-numeric type checkfail('local i = sprite["qwe"]') -- indexing struct array with non-numeric type
checkfail('print(sprite[100000].ceilingpal)') -- oob read access checkfail('print(sprite[100000].ceilingpal)') -- oob read access
checkfail('print(gv.sprite[0])') -- NOTE: gv.sprite doesn't fail, but we can't use it
checkfail('setmetatable(sprite, {})') -- set metatable forbidden checkfail('setmetatable(sprite, {})') -- set metatable forbidden
checkfail('sector[-1].ceilingpal = 4') -- oob write access checkfail('sector[-1].ceilingpal = 4') -- oob write access
checkfail('sector[0].wallnum = 0') -- wallnum member is read-only checkfail('sector[0].wallnum = 0') -- wallnum member is read-only
@ -114,8 +116,10 @@ checkfail('string.dump(gameevent)') -- string.dump is unavailable
checkfail('gv.luaJIT_setmode(nil, 0, 0)') checkfail('gv.luaJIT_setmode(nil, 0, 0)')
checkfail('gv.luaJIT_BC_con_lang') checkfail('gv.luaJIT_BC_con_lang')
checkfail('gv.yax_getbunch(0,0)') checkfail('gv.yax_getbunch(0,0)')
checkfail('gv.gethitickms = nil')
-- we don't have arrays in Lua-accessible structs now -- we don't have arrays in Lua-accessible structs now
checkfail('local i = actor[0].t_data[15]') checkfail('local i = actor[0].t_data[15]')
checkfail('local spr = sprite[0]; local x=spr+1') -- no pointer arithmetic!
printf('ceilingbunch of sector 0: %d', getbunch(0, gv.CEILING)) printf('ceilingbunch of sector 0: %d', getbunch(0, gv.CEILING))
@ -138,7 +142,7 @@ gameevent(gv.EVENT_ENTERLEVEL,
t = gv.gethitickms()-t t = gv.gethitickms()-t
-- x86: 40ns/call, x86_64: 290 ns/call -- x86_64: 40ns/call, x86: 290 ns/call
printf("%d gethitickms() calls took %.03f ms (%.03f us/call)", printf("%d gethitickms() calls took %.03f ms (%.03f us/call)",
N, t, (t*1000)/N) N, t, (t*1000)/N)
@ -169,8 +173,6 @@ gameactor(1680, -- LIZTROOP
-- sprite[i].ang = bit.band(sprite[i].ang-20, 2047) -- sprite[i].ang = bit.band(sprite[i].ang-20, 2047)
local spr = sprite[i] local spr = sprite[i]
-- print(type(spr)) --> cdata
assert(pcall("print(spr+1)") == false) -- no ptr arith!
local x,y,z = spr.x, spr.y, spr.z local x,y,z = spr.x, spr.y, spr.z
local t = gv.gethitickms() local t = gv.gethitickms()