From 11f42ba2f0ff4789bd877eeee15dd4d4b7e57838 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sun, 7 Apr 2024 22:38:43 +0300 Subject: [PATCH] Optimize require, streamline script running to always just call require (#478) Previously the precompilation cache was not used for scripts that were loaded through require. Now require first always tries to load precompiled script and otherwise falls back to default loading. As require is now able to access the precompiled scripts, we can default to it for running scripts. --- LuaEngine.cpp | 90 ++++++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/LuaEngine.cpp b/LuaEngine.cpp index c463f1b08c..012d67eb8c 100644 --- a/LuaEngine.cpp +++ b/LuaEngine.cpp @@ -102,6 +102,27 @@ void Eluna::CloseLua() continentDataRefs.clear(); } +static int PrecompiledLoader(lua_State* L) +{ + const char* modname = lua_tostring(L, 1); + if (modname == NULL) + return 0; + auto it = std::find_if(sElunaLoader->combined_scripts.begin(), sElunaLoader->combined_scripts.end(), [modname](const LuaScript& script) { return script.filename == modname; }); + if (it == sElunaLoader->combined_scripts.end()) { + lua_pushfstring(L, "\n\tno precompiled script '%s' found", modname); + return 1; + } + if (luaL_loadbuffer(L, reinterpret_cast(&it->bytecode[0]), it->bytecode.size(), it->filename.c_str())) + { + // Stack: modname, errmsg + return lua_error(L); + } + // Stack: modname, filefunction + lua_pushstring(L, it->filepath.c_str()); + // Stack: modname, filefunction, modpath + return 2; +} + void Eluna::OpenLua() { L = luaL_newstate(); @@ -125,7 +146,22 @@ void Eluna::OpenLua() lua_setfield(L, -2, "path"); lua_pushstring(L, sElunaLoader->lua_requirecpath.c_str()); lua_setfield(L, -2, "cpath"); - lua_pop(L, 1); + // Set package.loaders loader for precompiled scripts + lua_getfield(L, -1, "loaders"); + if (lua_isnil(L, -1)) { + // Lua 5.2+ uses searchers instead of loaders + lua_pop(L, 1); + lua_getfield(L, -1, "searchers"); + } + // insert the new loader to the loaders table by shifting other elements down by one + const int newLoaderIndex = 2; + for (int i = lua_rawlen(L, -1); i >= newLoaderIndex; --i) { + lua_rawgeti(L, -1, i); + lua_rawseti(L, -2, i + 1); + } + lua_pushcfunction(L, &PrecompiledLoader); + lua_rawseti(L, -2, newLoaderIndex); + lua_pop(L, 2); // pop loaders/searchers table, pop package table } void Eluna::CreateBindStores() @@ -210,11 +246,8 @@ void Eluna::RunScripts() std::unordered_map loaded; // filename, path - lua_getglobal(L, "package"); - // Stack: package - luaL_getsubtable(L, -1, "loaded"); - // Stack: package, modules - int modules = lua_gettop(L); + lua_getglobal(L, "require"); + // Stack: require for (auto it = sElunaLoader->combined_scripts.begin(); it != sElunaLoader->combined_scripts.end(); ++it) { @@ -237,47 +270,22 @@ void Eluna::RunScripts() } loaded[it->filename] = it->filepath; - lua_getfield(L, modules, it->filename.c_str()); - // Stack: package, modules, module - if (!lua_isnoneornil(L, -1)) - { - lua_pop(L, 1); - ELUNA_LOG_DEBUG("[Eluna]: `%s` was already loaded or required", it->filepath.c_str()); - continue; - } - lua_pop(L, 1); - // Stack: package, modules - - if (luaL_loadbuffer(L, reinterpret_cast(&it->bytecode[0]), it->bytecode.size(), it->filename.c_str())) - { - // Stack: package, modules, errmsg - ELUNA_LOG_ERROR("[Eluna]: Error loading `%s`", it->filepath.c_str()); - Report(L); - // Stack: package, modules - continue; - } - // Stack: package, modules, filefunc - - if (ExecuteCall(0, 1)) + // We call require on the filename to load the script + // A custom loader is used to load the script from the combined_scripts table + // The loader is set up in Eluna::OpenLua + lua_pushvalue(L, -1); // Stack: require, require + lua_pushstring(L, it->filename.c_str()); // Stack: require, require, filename + if (ExecuteCall(1, 0)) { - // Stack: package, modules, result - if (lua_isnoneornil(L, -1) || (lua_isboolean(L, -1) && !lua_toboolean(L, -1))) - { - // if result evaluates to false, change it to true - lua_pop(L, 1); - Push(true); - } - lua_setfield(L, modules, it->filename.c_str()); - // Stack: package, modules - - // successfully loaded and ran file + // Successfully called require on the script ELUNA_LOG_DEBUG("[Eluna]: Successfully loaded `%s`", it->filepath.c_str()); ++count; continue; } + // Stack: require } - // Stack: package, modules - lua_pop(L, 2); + // Stack: require + lua_pop(L, 1); ELUNA_LOG_INFO("[Eluna]: Executed %u Lua scripts in %u ms for map: %i, instance: %u", count, ElunaUtil::GetTimeDiff(oldMSTime), boundMapId, boundInstanceId); OnLuaStateOpen();