static void update_inner_tables_once(lua_State *L, int src, int dst, int updated) { lutro_checked_stack_begin(); assert(lua_istable(L, src)); assert(lua_istable(L, dst)); assert(lua_istable(L, updated)); src = lua_absindex(L, src); dst = lua_absindex(L, dst); updated = lua_absindex(L, updated); lua_pushnil(L); while (lua_next(L, src)) { lua_pushvalue(L, -2); lua_gettable(L, dst); if (!lua_compare(L, -2, -1, LUA_OPEQ) && lua_istable(L, -2)) { deep_update_once(L, -1, -2, updated); lua_pushvalue(L, -3); lua_pushvalue(L, -2); lua_settable(L, dst); } lua_pop(L, 2); } lutro_checked_stack_assert(0); }
void deep_update_once(lua_State *L, int src, int dst, int updated) { assert(lua_istable(L, src)); assert(lua_istable(L, dst)); assert(lua_istable(L, updated)); src = lua_absindex(L, src); dst = lua_absindex(L, dst); updated = lua_absindex(L, updated); lutro_checked_stack_begin(); lua_pushvalue(L, dst); lua_gettable(L, updated); if (lua_isnoneornil(L, -1)) { lua_pop(L, 1); lua_pushvalue(L, dst); lua_pushboolean(L, 1); lua_settable(L, updated); int top = lua_gettop(L); int src_mt = lua_getmetatable(L, src); int dst_mt = lua_getmetatable(L, dst); if (src_mt && dst_mt) deep_update_once(L, src_mt, dst_mt, updated); lua_pop(L, lua_gettop(L) - top); lua_pushnil(L); while (lua_next(L, src)) { if (lua_istable(L, -1)) { lua_pushvalue(L, -2); lua_gettable(L, dst); deep_update_once(L, -2, -1, updated); lua_pop(L, 2); } else { lua_pushvalue(L, -2); lua_insert(L, -2); lua_settable(L, dst); } } } else lua_pop(L, 1); lutro_checked_stack_assert(0); }
// copies stuff from table dst to table src static inline void shallow_update(lua_State *L, int src, int dst) { lutro_checked_stack_begin(); assert(lua_istable(L, src)); assert(lua_istable(L, dst)); src = lua_absindex(L, src); dst = lua_absindex(L, dst); lua_pushnil(L); while (lua_next(L, src)) { lua_pushvalue(L, -2); lua_insert(L, -2); lua_settable(L, dst); } lutro_checked_stack_assert(0); }
void lutro_init() { L = luaL_newstate(); luaL_openlibs(L); #ifdef HAVE_JIT luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC|LUAJIT_MODE_ON); #endif lutro_checked_stack_begin(); init_settings(L); lutro_preload(L, lutro_core_preload, "lutro"); lutro_preload(L, lutro_graphics_preload, "lutro.graphics"); lutro_preload(L, lutro_audio_preload, "lutro.audio"); lutro_preload(L, lutro_input_preload, "lutro.input"); lutro_preload(L, lutro_filesystem_preload, "lutro.filesystem"); lutro_preload(L, lutro_timer_preload, "lutro.timer"); #ifdef HAVE_INOTIFY lutro_preload(L, lutro_live_preload, "lutro.live"); #endif // if any of these requires fail, the checked stack assertion at the end will // be triggered. remember that assertions are only avaialable in debug mode. lutro_require(L, "lutro", 1); lutro_require(L, "lutro.graphics", 1); lutro_require(L, "lutro.audio", 1); lutro_require(L, "lutro.input", 1); lutro_require(L, "lutro.filesystem", 1); lutro_require(L, "lutro.timer", 1); #ifdef HAVE_INOTIFY lutro_require(L, "lutro.live", 1); #endif lutro_checked_stack_assert(0); }
// TODO: check if there's anything else that needs to be backed up static int live_hotswap(lua_State *L, const char *filename) { char modname[PATH_MAX_LENGTH]; int success = 1; lutro_checked_stack_begin(); // backup _G then drop it lua_pushglobaltable(L); lua_newtable(L); shallow_update(L, -2, -1); lua_remove(L, -2); lutro_relpath_to_modname(modname, filename); // backup module state get_package_loaded(L, modname); // force reloading of the module lua_pushnil(L); set_package_loaded(L, modname); if(!lutro_require(L, modname, 0)) { fprintf(stderr, "lutro.live lua error: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); lua_pushglobaltable(L); shallow_update(L, -3, -1); lua_pop(L, 1); // error and backups success = 0; } else { lua_newtable(L); // to keep track of updated keys if (lua_istable(L, -3)) deep_update_once(L, -2, -3, -1); // drop newmod lua_remove(L, -2); // restore _G then drop it lua_pushglobaltable(L); update_inner_tables_once(L, -4, -1, -2); lua_pop(L, 1); lua_pop(L, 1); // drop updated key table } // restore oldmod set_package_loaded(L, modname); lua_pop(L, 1); // _G backup if (success && settings.live_call_load) { lua_getglobal(L, "lutro"); lua_getfield(L, -1, "load"); // assume load is defined. if(lua_pcall(L, 0, 0, 0)) { // TODO: dump stacktrace fprintf(stderr, "%s\n", lua_tostring(L, -1)); lua_pop(L, 1); } lua_pop(L, 1); } lutro_checked_stack_assert(0); return success; }