From b305fb6411939bf6afbe2ecf2c1bdf765c9247a9 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Tue, 20 Feb 2024 14:25:56 +0200 Subject: Initial require implementation --- indra/llcommon/lua_function.cpp | 9 -- indra/llcommon/lua_function.h | 9 ++ indra/newview/llluamanager.cpp | 207 ++++++++++++++++++++++++++++++++++++++++ indra/newview/llluamanager.h | 39 ++++++++ 4 files changed, 255 insertions(+), 9 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 07e0c1fac2..17d81e8fc7 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -25,15 +25,6 @@ #include "llsdutil.h" #include "lualistener.h" -namespace -{ - // can't specify free function free() as a unique_ptr deleter - struct freer - { - void operator()(void* ptr){ free(ptr); } - }; -} // anonymous namespace - int lluau::dostring(lua_State* L, const std::string& desc, const std::string& text) { { diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index c23bf533ba..f549137c3e 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -22,6 +22,15 @@ #define lua_register(L, n, f) (lua_pushcfunction(L, (f), n), lua_setglobal(L, (n))) #define lua_rawlen lua_objlen +namespace +{ + // can't specify free function free() as a unique_ptr deleter + struct freer + { + void operator()(void *ptr) { free(ptr); } + }; +} + namespace lluau { // luau defines luaL_error() as void, but we want to use the Lua idiom of diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index d58ee5ca5f..9b74060139 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -401,3 +401,210 @@ void LLLUAmanager::runScriptOnLogin() runScriptFile(filename); #endif // ! LL_TEST } + + +bool is_absolute_path(std::string_view path) +{ +#ifdef LL_WINDOWS + // Must either begin with "X:/", "X:\", "/", or "\", where X is a drive letter + return (path.size() >= 3 && isalpha(path[0]) && path[1] == ':' && (path[2] == '/' || path[2] == '\\')) || + (path.size() >= 1 && (path[0] == '/' || path[0] == '\\')); +#else + // Must begin with '/' + return path.size() >= 1 && path[0] == '/'; +#endif +} + +std::string join_paths(const std::string &lhs, const std::string &rhs) +{ + std::string result = lhs; + if (!result.empty() && result.back() != '/' && result.back() != '\\') + result += '/'; + result += rhs; + return result; +} + +std::string read_file(const std::string &name) +{ + llifstream in_file; + in_file.open(name.c_str()); + + if (in_file.is_open()) + { + std::string text {std::istreambuf_iterator(in_file), std::istreambuf_iterator()}; + return text; + } + + return std::string(); +} + +LLRequireResolver::LLRequireResolver(lua_State *L, std::string path) : mPathToResolve(std::move(path)), L(L) +{ + lua_Debug ar; + lua_getinfo(L, 1, "s", &ar); + mSourceChunkname = ar.source; + + std::replace(mPathToResolve.begin(), mPathToResolve.end(), '\\', '/'); +} + +[[nodiscard]] LLRequireResolver::ResolvedRequire LLRequireResolver::resolveRequire(lua_State *L, std::string path) +{ + LLRequireResolver resolver(L, std::move(path)); + ModuleStatus status = resolver.findModule(); + if (status != ModuleStatus::FileRead) + return ResolvedRequire {status}; + else + return ResolvedRequire {status, std::move(resolver.mChunkname), std::move(resolver.mAbsolutePath), std::move(resolver.mSourceCode)}; +} + +LLRequireResolver::ModuleStatus LLRequireResolver::findModule() +{ + resolveAndStoreDefaultPaths(); + + // Put _MODULES table on stack for checking and saving to the cache + luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1); + + LLRequireResolver::ModuleStatus moduleStatus = findModuleImpl(); + + if (moduleStatus != LLRequireResolver::ModuleStatus::NotFound) + return moduleStatus; + + if (is_absolute_path(mPathToResolve)) + return moduleStatus; + + std::vector lib_paths; + + lib_paths.push_back(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + + for (size_t i = 0; i < lib_paths.size(); ++i) + { + std::string absolutePathOpt = join_paths(lib_paths[i], mPathToResolve); + + if (absolutePathOpt.empty()) + luaL_errorL(L, "error requiring module"); + + mChunkname = absolutePathOpt; + mAbsolutePath = absolutePathOpt; + + moduleStatus = findModuleImpl(); + + if (moduleStatus != LLRequireResolver::ModuleStatus::NotFound) + return moduleStatus; + } + + return LLRequireResolver::ModuleStatus::NotFound; +} + +LLRequireResolver::ModuleStatus LLRequireResolver::findModuleImpl() +{ + std::string possibleSuffixes[] = {".luau", ".lua"}; + + size_t unsuffixedAbsolutePathSize = mAbsolutePath.size(); + + for (auto possibleSuffix : possibleSuffixes) + { + mAbsolutePath += possibleSuffix; + + // Check cache for module + lua_getfield(L, -1, mAbsolutePath.c_str()); + if (!lua_isnil(L, -1)) + { + return ModuleStatus::Cached; + } + lua_pop(L, 1); + + // Try to read the matching file + std::string source = read_file(mAbsolutePath); + if (!source.empty()) + { + mChunkname = "=" + mChunkname + possibleSuffix; + mSourceCode = source; + return ModuleStatus::FileRead; + } + + mAbsolutePath.resize(unsuffixedAbsolutePathSize); // truncate to remove suffix + } + + return ModuleStatus::NotFound; +} + +void LLRequireResolver::resolveAndStoreDefaultPaths() +{ + if (!is_absolute_path(mPathToResolve)) + { + std::string path = gDirUtilp->getDirName(mSourceChunkname); + std::replace(path.begin(), path.end(), '\\', '/'); + path = join_paths(path, mPathToResolve); + mAbsolutePath = path; + mChunkname = path; + } + else + { + mChunkname = mPathToResolve; + mAbsolutePath = mPathToResolve; + } +} + +static int finishrequire(lua_State *L) +{ + if (lua_isstring(L, -1)) + lua_error(L); + + return 1; +} + +lua_function(require, "require(module_name) : module_name can be fullpath or just the name, in both cases without .lua") +{ + std::string name = luaL_checkstring(L, 1); + + LLRequireResolver::ResolvedRequire resolvedRequire = LLRequireResolver::resolveRequire(L, std::move(name)); + + if (resolvedRequire.status == LLRequireResolver::ModuleStatus::Cached) + return finishrequire(L); + else if (resolvedRequire.status == LLRequireResolver::ModuleStatus::NotFound) + luaL_errorL(L, "error requiring module"); + + // module needs to run in a new thread, isolated from the rest + // note: we create ML on main thread so that it doesn't inherit environment of L + lua_State *GL = lua_mainthread(L); + lua_State *ML = lua_newthread(GL); + lua_xmove(GL, L, 1); + + // new thread needs to have the globals sandboxed + luaL_sandboxthread(ML); + + { + // now we can compile & run module on the new thread + size_t bytecodeSize = 0; + std::unique_ptr bytecode { + luau_compile(resolvedRequire.sourceCode.c_str(), resolvedRequire.sourceCode.length(), nullptr, &bytecodeSize)}; + + if (luau_load(ML, resolvedRequire.chunkName.c_str(), bytecode.get(), bytecodeSize, 0) == 0) + { + int status = lua_resume(ML, L, 0); + + if (status == 0) + { + if (lua_gettop(ML) == 0) + lua_pushstring(ML, "module must return a value"); + else if (!lua_istable(ML, -1) && !lua_isfunction(ML, -1)) + lua_pushstring(ML, "module must return a table or function"); + } + else if (status == LUA_YIELD) + { + lua_pushstring(ML, "module can not yield"); + } + else if (!lua_isstring(ML, -1)) + { + lua_pushstring(ML, "unknown error while running module"); + } + } + } + // there's now a return value on top of ML; L stack: _MODULES ML + lua_xmove(ML, L, 1); + lua_pushvalue(L, -1); + lua_setfield(L, -4, resolvedRequire.absolutePath.c_str()); + + // L stack: _MODULES ML result + return finishrequire(L); +} diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h index 12284cd0e4..c382de3c62 100644 --- a/indra/newview/llluamanager.h +++ b/indra/newview/llluamanager.h @@ -33,6 +33,9 @@ #include #include // std::pair +#include "luau/lua.h" +#include "luau/lualib.h" + class LuaState; class LLLUAmanager @@ -81,4 +84,40 @@ public: static void runScriptOnLogin(); }; +class LLRequireResolver +{ + public: + enum class ModuleStatus + { + Cached, + FileRead, + NotFound + }; + + struct ResolvedRequire + { + ModuleStatus status; + std::string chunkName; + std::string absolutePath; + std::string sourceCode; + }; + + [[nodiscard]] ResolvedRequire static resolveRequire(lua_State *L, std::string path); + + std::string mChunkname; + std::string mAbsolutePath; + std::string mSourceCode; + + private: + std::string mPathToResolve; + std::string mSourceChunkname; + + LLRequireResolver(lua_State *L, std::string path); + + ModuleStatus findModule(); + lua_State *L; + + void resolveAndStoreDefaultPaths(); + ModuleStatus findModuleImpl(); +}; #endif -- cgit v1.2.3 From 3a9d0e7fa9214b5f802dd558d1e4c7154bbadb3e Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Tue, 20 Feb 2024 18:18:04 +0200 Subject: Don't accept a full path as arg for require() --- indra/newview/llluamanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 9b74060139..164738bc3b 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -444,6 +444,9 @@ LLRequireResolver::LLRequireResolver(lua_State *L, std::string path) : mPathToRe lua_getinfo(L, 1, "s", &ar); mSourceChunkname = ar.source; + if (is_absolute_path(mPathToResolve)) + luaL_argerrorL(L, 1, "cannot require a full path"); + std::replace(mPathToResolve.begin(), mPathToResolve.end(), '\\', '/'); } -- cgit v1.2.3 From 32bf9c7b7a9d2b2428b052d74389ec48ccc427cf Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Wed, 21 Feb 2024 17:11:33 +0200 Subject: Add the option to use clean lua_State in "Lua debug" floater --- indra/llcommon/lua_function.cpp | 9 +++++++-- indra/llcommon/lua_function.h | 2 ++ indra/newview/llfloaterluadebug.cpp | 12 ++++++++++++ indra/newview/llfloaterluadebug.h | 1 + indra/newview/skins/default/xui/en/floater_lua_debug.xml | 9 +++++++++ 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 17d81e8fc7..e9b4bf0b89 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -406,9 +406,14 @@ void lua_pushllsd(lua_State* L, const LLSD& data) } LuaState::LuaState(script_finished_fn cb): - mCallback(cb), - mState(luaL_newstate()) + mCallback(cb) { + initLuaState(); +} + +void LuaState::initLuaState() +{ + mState = luaL_newstate(); luaL_openlibs(mState); LuaFunction::init(mState); // Try to make print() write to our log. diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index f549137c3e..54db92f73e 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -78,6 +78,8 @@ public: ~LuaState(); + void initLuaState(); + bool checkLua(const std::string& desc, int r); // expr() is for when we want to capture any results left on the stack diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp index 7d56d8e618..2d9dc89340 100644 --- a/indra/newview/llfloaterluadebug.cpp +++ b/indra/newview/llfloaterluadebug.cpp @@ -27,6 +27,7 @@ #include "llfloaterluadebug.h" +#include "llcheckboxctrl.h" #include "lllineeditor.h" #include "lltexteditor.h" #include "llviewermenufile.h" // LLFilePickerReplyThread @@ -79,6 +80,7 @@ void LLFloaterLUADebug::onExecuteClicked() mResultOutput->setValue(""); std::string cmd = mLineInput->getText(); + cleanLuaState(); LLLUAmanager::runScriptLine(mState, cmd, [this](int count, const LLSD& result) { completion(count, result); @@ -109,6 +111,7 @@ void LLFloaterLUADebug::runSelectedScript(const std::vector &filena if (!filepath.empty()) { mScriptPath->setText(filepath); + cleanLuaState(); LLLUAmanager::runScriptFile(mState, filepath, [this](int count, const LLSD& result) { completion(count, result); @@ -140,3 +143,12 @@ void LLFloaterLUADebug::completion(int count, const LLSD& result) sep = ", "; } } + +void LLFloaterLUADebug::cleanLuaState() +{ + if(getChild("clean_lua_state")->get()) + { + //Reinit to clean lua_State + mState.initLuaState(); + } +} diff --git a/indra/newview/llfloaterluadebug.h b/indra/newview/llfloaterluadebug.h index 69b334ae2d..7418174570 100644 --- a/indra/newview/llfloaterluadebug.h +++ b/indra/newview/llfloaterluadebug.h @@ -58,6 +58,7 @@ class LLFloaterLUADebug : private: void completion(int count, const LLSD& result); + void cleanLuaState(); LLTempBoundListener mOutConnection; diff --git a/indra/newview/skins/default/xui/en/floater_lua_debug.xml b/indra/newview/skins/default/xui/en/floater_lua_debug.xml index f03739f7c2..a028a1802c 100644 --- a/indra/newview/skins/default/xui/en/floater_lua_debug.xml +++ b/indra/newview/skins/default/xui/en/floater_lua_debug.xml @@ -25,6 +25,15 @@ width="100"> LUA string: + Date: Fri, 23 Feb 2024 16:57:00 +0200 Subject: require() code clean-up --- indra/llcommon/lua_function.cpp | 39 +++++++++++++----- indra/llcommon/lua_function.h | 10 +---- indra/llfilesystem/lldir.cpp | 5 +++ indra/llfilesystem/lldir.h | 1 + indra/newview/llluamanager.cpp | 89 ++++++++++++----------------------------- indra/newview/llluamanager.h | 4 +- 6 files changed, 61 insertions(+), 87 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index e9b4bf0b89..41bb7bac12 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -25,18 +25,20 @@ #include "llsdutil.h" #include "lualistener.h" -int lluau::dostring(lua_State* L, const std::string& desc, const std::string& text) +namespace { + // can't specify free function free() as a unique_ptr deleter + struct freer { - size_t bytecodeSize = 0; - // The char* returned by luau_compile() must be freed by calling free(). - // Use unique_ptr so the memory will be freed even if luau_load() throws. - std::unique_ptr bytecode{ - luau_compile(text.data(), text.length(), nullptr, &bytecodeSize)}; - auto r = luau_load(L, desc.data(), bytecode.get(), bytecodeSize, 0); - if (r != LUA_OK) - return r; - } // free bytecode + void operator()(void* ptr){ free(ptr); } + }; +} // anonymous namespace + +int lluau::dostring(lua_State* L, const std::string& desc, const std::string& text) +{ + auto r = loadstring(L, desc, text); + if (r != LUA_OK) + return r; // It's important to pass LUA_MULTRET as the expected number of return // values: if we pass any fixed number, we discard any returned values @@ -44,6 +46,16 @@ int lluau::dostring(lua_State* L, const std::string& desc, const std::string& te return lua_pcall(L, 0, LUA_MULTRET, 0); } +int lluau::loadstring(lua_State *L, const std::string &desc, const std::string &text) +{ + size_t bytecodeSize = 0; + // The char* returned by luau_compile() must be freed by calling free(). + // Use unique_ptr so the memory will be freed even if luau_load() throws. + std::unique_ptr bytecode{ + luau_compile(text.data(), text.length(), nullptr, &bytecodeSize)}; + return luau_load(L, desc.data(), bytecode.get(), bytecodeSize, 0); +} + std::string lua_tostdstring(lua_State* L, int index) { size_t len; @@ -406,13 +418,18 @@ void lua_pushllsd(lua_State* L, const LLSD& data) } LuaState::LuaState(script_finished_fn cb): - mCallback(cb) + mCallback(cb), + mState(nullptr) { initLuaState(); } void LuaState::initLuaState() { + if (mState) + { + lua_close(mState); + } mState = luaL_newstate(); luaL_openlibs(mState); LuaFunction::init(mState); diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 54db92f73e..0605eb12a6 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -22,15 +22,6 @@ #define lua_register(L, n, f) (lua_pushcfunction(L, (f), n), lua_setglobal(L, (n))) #define lua_rawlen lua_objlen -namespace -{ - // can't specify free function free() as a unique_ptr deleter - struct freer - { - void operator()(void *ptr) { free(ptr); } - }; -} - namespace lluau { // luau defines luaL_error() as void, but we want to use the Lua idiom of @@ -56,6 +47,7 @@ namespace lluau // rather than string_views because dostring() needs pointers to nul- // terminated char arrays. int dostring(lua_State* L, const std::string& desc, const std::string& text); + int loadstring(lua_State* L, const std::string& desc, const std::string& text); } // namespace lluau std::string lua_tostdstring(lua_State* L, int index); diff --git a/indra/llfilesystem/lldir.cpp b/indra/llfilesystem/lldir.cpp index 69b23f9cf8..e3769c6afb 100644 --- a/indra/llfilesystem/lldir.cpp +++ b/indra/llfilesystem/lldir.cpp @@ -469,6 +469,7 @@ static std::string ELLPathToString(ELLPath location) ENT(LL_PATH_DEFAULT_SKIN) ENT(LL_PATH_FONTS) ENT(LL_PATH_LAST) + ENT(LL_PATH_SCRIPTS) ; #undef ENT @@ -588,6 +589,10 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd case LL_PATH_FONTS: prefix = add(getAppRODataDir(), "fonts"); break; + + case LL_PATH_SCRIPTS: + prefix = add(getAppRODataDir(), "scripts"); + break; default: llassert(0); diff --git a/indra/llfilesystem/lldir.h b/indra/llfilesystem/lldir.h index b9a046ba33..7d418159d1 100644 --- a/indra/llfilesystem/lldir.h +++ b/indra/llfilesystem/lldir.h @@ -49,6 +49,7 @@ typedef enum ELLPath LL_PATH_DEFAULT_SKIN = 17, LL_PATH_FONTS = 18, LL_PATH_DUMP = 19, + LL_PATH_SCRIPTS = 20, LL_PATH_LAST } ELLPath; diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 164738bc3b..671410d420 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -46,6 +46,7 @@ extern LLUIListener sUIListener; #endif // ! LL_TEST #include +#include #include "luau/luacode.h" #include "luau/lua.h" @@ -307,8 +308,7 @@ void LLLUAmanager::runScriptFile(LuaState& L, const std::string& filename, scrip if (in_file.is_open()) { - std::string text{std::istreambuf_iterator(in_file), - std::istreambuf_iterator()}; + std::string text{std::istreambuf_iterator(in_file), {}}; auto [count, result] = L.expr(filename, text); if (cb) { @@ -402,28 +402,6 @@ void LLLUAmanager::runScriptOnLogin() #endif // ! LL_TEST } - -bool is_absolute_path(std::string_view path) -{ -#ifdef LL_WINDOWS - // Must either begin with "X:/", "X:\", "/", or "\", where X is a drive letter - return (path.size() >= 3 && isalpha(path[0]) && path[1] == ':' && (path[2] == '/' || path[2] == '\\')) || - (path.size() >= 1 && (path[0] == '/' || path[0] == '\\')); -#else - // Must begin with '/' - return path.size() >= 1 && path[0] == '/'; -#endif -} - -std::string join_paths(const std::string &lhs, const std::string &rhs) -{ - std::string result = lhs; - if (!result.empty() && result.back() != '/' && result.back() != '\\') - result += '/'; - result += rhs; - return result; -} - std::string read_file(const std::string &name) { llifstream in_file; @@ -431,20 +409,22 @@ std::string read_file(const std::string &name) if (in_file.is_open()) { - std::string text {std::istreambuf_iterator(in_file), std::istreambuf_iterator()}; + std::string text {std::istreambuf_iterator(in_file), {}}; return text; } return std::string(); } -LLRequireResolver::LLRequireResolver(lua_State *L, std::string path) : mPathToResolve(std::move(path)), L(L) +LLRequireResolver::LLRequireResolver(lua_State *L, const std::string& path) : mPathToResolve(path), L(L) { lua_Debug ar; lua_getinfo(L, 1, "s", &ar); mSourceChunkname = ar.source; - if (is_absolute_path(mPathToResolve)) + mPathToResolve = std::filesystem::path(mPathToResolve).lexically_normal().string(); + + if (std::filesystem::path(mPathToResolve).is_absolute()) luaL_argerrorL(L, 1, "cannot require a full path"); std::replace(mPathToResolve.begin(), mPathToResolve.end(), '\\', '/'); @@ -457,7 +437,7 @@ LLRequireResolver::LLRequireResolver(lua_State *L, std::string path) : mPathToRe if (status != ModuleStatus::FileRead) return ResolvedRequire {status}; else - return ResolvedRequire {status, std::move(resolver.mChunkname), std::move(resolver.mAbsolutePath), std::move(resolver.mSourceCode)}; + return ResolvedRequire {status, resolver.mAbsolutePath, resolver.mSourceCode}; } LLRequireResolver::ModuleStatus LLRequireResolver::findModule() @@ -467,26 +447,24 @@ LLRequireResolver::ModuleStatus LLRequireResolver::findModule() // Put _MODULES table on stack for checking and saving to the cache luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1); + // Check if the module is already in _MODULES table, read from file otherwise LLRequireResolver::ModuleStatus moduleStatus = findModuleImpl(); if (moduleStatus != LLRequireResolver::ModuleStatus::NotFound) return moduleStatus; - if (is_absolute_path(mPathToResolve)) + if (std::filesystem::path(mPathToResolve).is_absolute()) return moduleStatus; - std::vector lib_paths; - - lib_paths.push_back(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + std::vector lib_paths {gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua")}; - for (size_t i = 0; i < lib_paths.size(); ++i) + for (const auto& path : lib_paths) { - std::string absolutePathOpt = join_paths(lib_paths[i], mPathToResolve); + std::string absolutePathOpt = (std::filesystem::path(path) / mPathToResolve).u8string(); if (absolutePathOpt.empty()) luaL_errorL(L, "error requiring module"); - mChunkname = absolutePathOpt; mAbsolutePath = absolutePathOpt; moduleStatus = findModuleImpl(); @@ -500,16 +478,12 @@ LLRequireResolver::ModuleStatus LLRequireResolver::findModule() LLRequireResolver::ModuleStatus LLRequireResolver::findModuleImpl() { - std::string possibleSuffixes[] = {".luau", ".lua"}; - - size_t unsuffixedAbsolutePathSize = mAbsolutePath.size(); + std::string possibleSuffixedPaths[] = {mAbsolutePath + ".luau", mAbsolutePath + ".lua"}; - for (auto possibleSuffix : possibleSuffixes) + for (auto suffixedPath : possibleSuffixedPaths) { - mAbsolutePath += possibleSuffix; - - // Check cache for module - lua_getfield(L, -1, mAbsolutePath.c_str()); + // Check _MODULES cache for module + lua_getfield(L, -1, suffixedPath.c_str()); if (!lua_isnil(L, -1)) { return ModuleStatus::Cached; @@ -517,15 +491,12 @@ LLRequireResolver::ModuleStatus LLRequireResolver::findModuleImpl() lua_pop(L, 1); // Try to read the matching file - std::string source = read_file(mAbsolutePath); + std::string source = read_file(suffixedPath); if (!source.empty()) { - mChunkname = "=" + mChunkname + possibleSuffix; mSourceCode = source; return ModuleStatus::FileRead; } - - mAbsolutePath.resize(unsuffixedAbsolutePathSize); // truncate to remove suffix } return ModuleStatus::NotFound; @@ -533,17 +504,12 @@ LLRequireResolver::ModuleStatus LLRequireResolver::findModuleImpl() void LLRequireResolver::resolveAndStoreDefaultPaths() { - if (!is_absolute_path(mPathToResolve)) + if (!std::filesystem::path(mPathToResolve).is_absolute()) { - std::string path = gDirUtilp->getDirName(mSourceChunkname); - std::replace(path.begin(), path.end(), '\\', '/'); - path = join_paths(path, mPathToResolve); - mAbsolutePath = path; - mChunkname = path; + mAbsolutePath = (std::filesystem::path((mSourceChunkname)).parent_path() / mPathToResolve).u8string();; } else { - mChunkname = mPathToResolve; mAbsolutePath = mPathToResolve; } } @@ -558,9 +524,9 @@ static int finishrequire(lua_State *L) lua_function(require, "require(module_name) : module_name can be fullpath or just the name, in both cases without .lua") { - std::string name = luaL_checkstring(L, 1); + std::string name = lua_tostdstring(L, 1); - LLRequireResolver::ResolvedRequire resolvedRequire = LLRequireResolver::resolveRequire(L, std::move(name)); + LLRequireResolver::ResolvedRequire resolvedRequire = LLRequireResolver::resolveRequire(L, name); if (resolvedRequire.status == LLRequireResolver::ModuleStatus::Cached) return finishrequire(L); @@ -577,18 +543,13 @@ lua_function(require, "require(module_name) : module_name can be fullpath or jus luaL_sandboxthread(ML); { - // now we can compile & run module on the new thread - size_t bytecodeSize = 0; - std::unique_ptr bytecode { - luau_compile(resolvedRequire.sourceCode.c_str(), resolvedRequire.sourceCode.length(), nullptr, &bytecodeSize)}; - - if (luau_load(ML, resolvedRequire.chunkName.c_str(), bytecode.get(), bytecodeSize, 0) == 0) + if (lluau::loadstring(ML, resolvedRequire.absolutePath.c_str(), resolvedRequire.sourceCode.c_str()) == LUA_OK) { int status = lua_resume(ML, L, 0); - if (status == 0) + if (status == LUA_OK) { - if (lua_gettop(ML) == 0) + if (lua_gettop(ML) == LUA_OK) lua_pushstring(ML, "module must return a value"); else if (!lua_istable(ML, -1) && !lua_isfunction(ML, -1)) lua_pushstring(ML, "module must return a table or function"); diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h index c382de3c62..43950ccee4 100644 --- a/indra/newview/llluamanager.h +++ b/indra/newview/llluamanager.h @@ -97,14 +97,12 @@ class LLRequireResolver struct ResolvedRequire { ModuleStatus status; - std::string chunkName; std::string absolutePath; std::string sourceCode; }; [[nodiscard]] ResolvedRequire static resolveRequire(lua_State *L, std::string path); - std::string mChunkname; std::string mAbsolutePath; std::string mSourceCode; @@ -112,7 +110,7 @@ class LLRequireResolver std::string mPathToResolve; std::string mSourceChunkname; - LLRequireResolver(lua_State *L, std::string path); + LLRequireResolver(lua_State *L, const std::string& path); ModuleStatus findModule(); lua_State *L; -- cgit v1.2.3 From 758378859ce2f5bac2e472f5f6f24061bce7c153 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Mon, 26 Feb 2024 20:08:13 +0200 Subject: Clean-up and restoring correct path --- indra/newview/llluamanager.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 671410d420..54acb49b00 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -418,16 +418,17 @@ std::string read_file(const std::string &name) LLRequireResolver::LLRequireResolver(lua_State *L, const std::string& path) : mPathToResolve(path), L(L) { + //Luau lua_Debug and lua_getinfo() are different compared to default Lua: + //see https://github.com/luau-lang/luau/blob/80928acb92d1e4b6db16bada6d21b1fb6fa66265/VM/include/lua.h lua_Debug ar; lua_getinfo(L, 1, "s", &ar); mSourceChunkname = ar.source; - mPathToResolve = std::filesystem::path(mPathToResolve).lexically_normal().string(); + std::filesystem::path fs_path(mPathToResolve); + mPathToResolve = fs_path.lexically_normal().string(); - if (std::filesystem::path(mPathToResolve).is_absolute()) + if (fs_path.is_absolute()) luaL_argerrorL(L, 1, "cannot require a full path"); - - std::replace(mPathToResolve.begin(), mPathToResolve.end(), '\\', '/'); } [[nodiscard]] LLRequireResolver::ResolvedRequire LLRequireResolver::resolveRequire(lua_State *L, std::string path) @@ -494,6 +495,7 @@ LLRequireResolver::ModuleStatus LLRequireResolver::findModuleImpl() std::string source = read_file(suffixedPath); if (!source.empty()) { + mAbsolutePath = suffixedPath; mSourceCode = source; return ModuleStatus::FileRead; } -- cgit v1.2.3 From 01a94f9d8378bb1094f4272b37ed9b966703f0b6 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Mon, 26 Feb 2024 23:55:30 +0200 Subject: Clear the stack after requiring a module --- indra/newview/llluamanager.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 54acb49b00..4463fe65f5 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -530,8 +530,12 @@ lua_function(require, "require(module_name) : module_name can be fullpath or jus LLRequireResolver::ResolvedRequire resolvedRequire = LLRequireResolver::resolveRequire(L, name); - if (resolvedRequire.status == LLRequireResolver::ModuleStatus::Cached) + if (resolvedRequire.status == LLRequireResolver::ModuleStatus::Cached) + { + // remove _MODULES from stack + lua_remove(L, -2); return finishrequire(L); + } else if (resolvedRequire.status == LLRequireResolver::ModuleStatus::NotFound) luaL_errorL(L, "error requiring module"); @@ -571,6 +575,11 @@ lua_function(require, "require(module_name) : module_name can be fullpath or jus lua_pushvalue(L, -1); lua_setfield(L, -4, resolvedRequire.absolutePath.c_str()); - // L stack: _MODULES ML result + //remove _MODULES from stack + lua_remove(L, -3); + // remove ML from stack + lua_remove(L, -2); + + // L stack: [module name] [result] return finishrequire(L); } -- cgit v1.2.3