From 0df7936ea50db2ee5680f75fa285f96fedf1f341 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 29 Feb 2024 12:00:58 -0500 Subject: Improve Debug class (indra/test/debug.h). Disable copy assignment operator as well as copy constructor. Use std::uncaught_exceptions() in destructor to report whether there's an in-flight exception at block exit. Since that was the whole point of the DEBUGIN / DEBUGEND macros, those become obsolete. Ditch them and their existing invocations. --- indra/llcommon/tests/lleventcoro_test.cpp | 95 +++++++++++++------------------ indra/test/debug.h | 21 ++----- 2 files changed, 42 insertions(+), 74 deletions(-) diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index c7a958da49..6ff895c1c1 100644 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -113,30 +113,27 @@ namespace tut void test_data::explicit_wait(boost::shared_ptr>& cbp) { - DEBUGIN - { - mSync.bump(); - // The point of this test is to verify / illustrate suspending a - // coroutine for something other than an LLEventPump. In other - // words, this shows how to adapt to any async operation that - // provides a callback-style notification (and prove that it - // works). + DEBUG; + mSync.bump(); + // The point of this test is to verify / illustrate suspending a + // coroutine for something other than an LLEventPump. In other + // words, this shows how to adapt to any async operation that + // provides a callback-style notification (and prove that it + // works). - // Perhaps we would send a request to a remote server and arrange - // for cbp->set_value() to be called on response. - // For test purposes, instead of handing 'callback' (or an - // adapter) off to some I/O subsystem, we'll just pass it back to - // our caller. - cbp = boost::make_shared>(); - LLCoros::Future future = LLCoros::getFuture(*cbp); + // Perhaps we would send a request to a remote server and arrange + // for cbp->set_value() to be called on response. + // For test purposes, instead of handing 'callback' (or an + // adapter) off to some I/O subsystem, we'll just pass it back to + // our caller. + cbp = boost::make_shared>(); + LLCoros::Future future = LLCoros::getFuture(*cbp); - // calling get() on the future causes us to suspend - debug("about to suspend"); - stringdata = future.get(); - mSync.bump(); - ensure_equals("Got it", stringdata, "received"); - } - DEBUGEND + // calling get() on the future causes us to suspend + debug("about to suspend"); + stringdata = future.get(); + mSync.bump(); + ensure_equals("Got it", stringdata, "received"); } template<> template<> @@ -163,13 +160,9 @@ namespace tut void test_data::waitForEventOn1() { - DEBUGIN - { - mSync.bump(); - result = suspendUntilEventOn("source"); - mSync.bump(); - } - DEBUGEND + mSync.bump(); + result = suspendUntilEventOn("source"); + mSync.bump(); } template<> template<> @@ -189,15 +182,11 @@ namespace tut void test_data::coroPump() { - DEBUGIN - { - mSync.bump(); - LLCoroEventPump waiter; - replyName = waiter.getName(); - result = waiter.suspend(); - mSync.bump(); - } - DEBUGEND + mSync.bump(); + LLCoroEventPump waiter; + replyName = waiter.getName(); + result = waiter.suspend(); + mSync.bump(); } template<> template<> @@ -217,16 +206,12 @@ namespace tut void test_data::postAndWait1() { - DEBUGIN - { - mSync.bump(); - result = postAndSuspend(LLSDMap("value", 17), // request event - immediateAPI.getPump(), // requestPump - "reply1", // replyPump - "reply"); // request["reply"] = name - mSync.bump(); - } - DEBUGEND + mSync.bump(); + result = postAndSuspend(LLSDMap("value", 17), // request event + immediateAPI.getPump(), // requestPump + "reply1", // replyPump + "reply"); // request["reply"] = name + mSync.bump(); } template<> template<> @@ -240,15 +225,11 @@ namespace tut void test_data::coroPumpPost() { - DEBUGIN - { - mSync.bump(); - LLCoroEventPump waiter; - result = waiter.postAndSuspend(LLSDMap("value", 17), - immediateAPI.getPump(), "reply"); - mSync.bump(); - } - DEBUGEND + mSync.bump(); + LLCoroEventPump waiter; + result = waiter.postAndSuspend(LLSDMap("value", 17), + immediateAPI.getPump(), "reply"); + mSync.bump(); } template<> template<> diff --git a/indra/test/debug.h b/indra/test/debug.h index f92cce3008..3c4f3cabb4 100644 --- a/indra/test/debug.h +++ b/indra/test/debug.h @@ -31,6 +31,7 @@ #include "print.h" #include "stringize.h" +#include // std::uncaught_exceptions() /***************************************************************************** * Debugging stuff @@ -65,10 +66,12 @@ public: // non-copyable Debug(const Debug&) = delete; + Debug& operator=(const Debug&) = delete; ~Debug() { - (*this)("exit"); + auto exceptional{ std::uncaught_exceptions()? "exceptional " : "" }; + (*this)(exceptional, "exit"); } template @@ -90,20 +93,4 @@ private: // of the Debug block. #define DEBUG Debug debug(LL_PRETTY_FUNCTION) -// These DEBUGIN/DEBUGEND macros are specifically for debugging output -- -// please don't assume you must use such for coroutines in general! They only -// help to make control flow (as well as exception exits) explicit. -#define DEBUGIN \ -{ \ - DEBUG; \ - try - -#define DEBUGEND \ - catch (...) \ - { \ - debug("*** exceptional "); \ - throw; \ - } \ -} - #endif /* ! defined(LL_DEBUG_H) */ -- cgit v1.2.3 From 9dae3e96ee6b0cb4139f368488da85f9961d1d4f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 29 Feb 2024 12:09:59 -0500 Subject: Refactor require() to make it easier to reason about Lua stack usage. Push throwing Lua errors down into LLRequireResolver::findModule() and findModuleImpl() so their callers don't have to handle the error case. That eliminates finishrequire(). require() itself now only retrieves (and pops) the passed module name and calls LLRequireResolver::resolveRequire() to do the actual work. resolveRequire() is now void. It only instantiates LLRequireResolver and calls its findModule(). findModule() is now also void. It's guaranteed to either push the loaded Lua module or throw a Lua error. In particular, when findPathImpl() cannot find the specified module, findModule() throws an error. That replaces ModuleStatus::NotFound. Since std::filesystem::path::append() aka operator/() detects when its right operand is absolute and, in that case, discards the left operand, we no longer need resolveAndStoreDefaultPaths(): we can just invoke that operation inline. When findModule() pushes _MODULES on the Lua stack, it uses LuaRemover (below) to ensure that _MODULES is removed again no matter how findModules() exits. findModuleImpl() now accepts the candidate pathname as its argument. That eliminates mAbsolutePath. findModuleImpl() now returns only bool: true means the module was found and loaded and pushed on the Lua stack, false means not found and nothing was pushed; no return means an error was reported. Push running a newly found module's source file down into findModuleImpl(). That eliminates the distinction between Cached and FileRead, which obviates ModuleStatus: a bool return means either "previously cached" or "we read it, compiled it, loaded it and ran it." That also eliminates the need to store the module's textual content in mSourceCode. Similarly, once loading the module succeeds, findModuleImpl() caches it in _MODULES right away. That eliminates ResolvedRequire since we need not pass the full pathname of the found module (or its contents) back up through the call chain. Move require() code that runs the new module into private runModule() method, called by findModuleImpl() in the not-cached case. runModule() is the only remaining method that can push either a string error message or the desired module, because of its funny stack manipulations. That means the check for a string error message on the stack top can move down to findModuleImpl(). Add LuaRemover class to ensure that on exit from some particular C++ block, the specified Lua stack entry will definitely be removed. This is different from LuaPopper in that it engages lua_remove() rather than lua_pop(). Also ditch obsolete await_event() Lua entry point. --- indra/llcommon/lua_function.cpp | 3 +- indra/llcommon/lua_function.h | 37 +++++++ indra/newview/llluamanager.cpp | 233 +++++++++++++++++++++------------------- indra/newview/llluamanager.h | 25 +---- 4 files changed, 164 insertions(+), 134 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index a5f1f582d9..78abb8ba7e 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -789,7 +789,8 @@ std::ostream& operator<<(std::ostream& out, const lua_what& self) *****************************************************************************/ std::ostream& operator<<(std::ostream& out, const lua_stack& self) { - const char* sep = "stack: ["; + out << "stack: ["; + const char* sep = ""; for (int index = 1; index <= lua_gettop(self.L); ++index) { out << sep << lua_what(self.L, index); diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 08a2353d29..07848e38af 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -17,6 +17,7 @@ #include "luau/luaconf.h" #include "luau/lualib.h" #include "stringize.h" +#include // std::uncaught_exceptions() #include // std::shared_ptr #include // std::pair @@ -216,4 +217,40 @@ private: lua_State* L; }; +// adapted from indra/test/debug.h +// can't generalize Debug::operator() target because it's a variadic template +class LuaLog +{ +public: + template + LuaLog(lua_State* L, ARGS&&... args): + L(L), + mBlock(stringize(std::forward(args)...)) + { + (*this)("entry ", lua_stack(L)); + } + + // non-copyable + LuaLog(const LuaLog&) = delete; + LuaLog& operator=(const LuaLog&) = delete; + + ~LuaLog() + { + auto exceptional{ std::uncaught_exceptions()? "exceptional " : "" }; + (*this)(exceptional, "exit ", lua_stack(L)); + } + + template + void operator()(ARGS&&... args) + { + LL_INFOS("Lua") << mBlock << ' '; + stream_to(LL_CONT, std::forward(args)...); + LL_ENDL; + } + +private: + lua_State* L; + const std::string mBlock; +}; + #endif /* ! defined(LL_LUA_FUNCTION_H) */ diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 0c39f6c8df..80d7eb4210 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -195,30 +195,6 @@ lua_function(get_event_next, return 2; } -lua_function(await_event, - "await_event(pumpname [, timeout [, value to return if timeout (default nil)]]):\n" - "pause the running Lua chunk until the next event on the named LLEventPump") -{ - auto pumpname{ lua_tostdstring(L, 1) }; - LLSD result; - if (lua_gettop(L) > 1) - { - auto timeout{ lua_tonumber(L, 2) }; - // with no 3rd argument, should be LLSD() - auto dftval{ lua_tollsd(L, 3) }; - lua_settop(L, 0); - result = llcoro::suspendUntilEventOnWithTimeout(pumpname, timeout, dftval); - } - else - { - // no timeout - lua_pop(L, 1); - result = llcoro::suspendUntilEventOn(pumpname); - } - lua_pushllsd(L, result); - return 1; -} - void LLLUAmanager::runScriptFile(const std::string& filename, script_finished_fn cb) { // A script_finished_fn is used to initialize the LuaState. @@ -361,14 +337,36 @@ std::string read_file(const std::string &name) if (in_file.is_open()) { - std::string text {std::istreambuf_iterator(in_file), {}}; - return text; + return std::string{std::istreambuf_iterator(in_file), {}}; } - return std::string(); + return {}; } -LLRequireResolver::LLRequireResolver(lua_State *L, const std::string& path) : mPathToResolve(path), L(L) +lua_function(require, "require(module_name) : load module_name.lua from known places") +{ + std::string name = lua_tostdstring(L, 1); + lua_pop(L, 1); + + // resolveRequire() does not return in case of error. + LLRequireResolver::resolveRequire(L, name); + + // resolveRequire() returned the newly-loaded module on the stack top. + // Return it. + return 1; +} + +// push loaded module or throw Lua error +void LLRequireResolver::resolveRequire(lua_State *L, std::string path) +{ + LLRequireResolver resolver(L, std::move(path)); + // findModule() pushes the loaded module or throws a Lua error. + resolver.findModule(); +} + +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 @@ -383,31 +381,59 @@ LLRequireResolver::LLRequireResolver(lua_State *L, const std::string& path) : mP luaL_argerrorL(L, 1, "cannot require a full path"); } -[[nodiscard]] LLRequireResolver::ResolvedRequire LLRequireResolver::resolveRequire(lua_State *L, std::string path) +/** + * Remove a particular stack index on exit from enclosing scope. + * If you pass a negative index (meaning relative to the current stack top), + * converts to an absolute index. The point of LuaRemover is to remove the + * entry at the specified index regardless of subsequent pushes to the stack. + */ +class LuaRemover { - LLRequireResolver resolver(L, std::move(path)); - ModuleStatus status = resolver.findModule(); - if (status != ModuleStatus::FileRead) - return ResolvedRequire {status}; - else - return ResolvedRequire {status, resolver.mAbsolutePath, resolver.mSourceCode}; -} +public: + LuaRemover(lua_State* L, int index): + mState(L), + mIndex(lua_absindex(L, index)) + {} + LuaRemover(const LuaRemover&) = delete; + LuaRemover& operator=(const LuaRemover&) = delete; + ~LuaRemover() + { + lua_remove(mState, mIndex); + } + +private: + lua_State* mState; + int mIndex; +}; -LLRequireResolver::ModuleStatus LLRequireResolver::findModule() +// push the loaded module or throw a Lua error +void LLRequireResolver::findModule() { - resolveAndStoreDefaultPaths(); + // If mPathToResolve is absolute, this replaces mSourceChunkname.parent_path. + auto absolutePath = (std::filesystem::path((mSourceChunkname)).parent_path() / mPathToResolve).u8string(); - // Put _MODULES table on stack for checking and saving to the cache + // Push _MODULES table on stack for checking and saving to the cache luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1); + // Remove that stack entry no matter how we exit + LuaRemover rm_MODULES(L, -1); + + // Check if the module is already in _MODULES table, read from file + // otherwise. + // findModuleImpl() pushes module if found, nothing if not, may throw Lua + // error. + if (findModuleImpl(absolutePath)) + return; - // Check if the module is already in _MODULES table, read from file otherwise - LLRequireResolver::ModuleStatus moduleStatus = findModuleImpl(); - - if (moduleStatus != LLRequireResolver::ModuleStatus::NotFound) - return moduleStatus; + // not already cached - prep error message just in case + auto fail{ + [L=L, path=mPathToResolve]() + { luaL_error(L, "could not find require('%s')", path.data()); }}; if (std::filesystem::path(mPathToResolve).is_absolute()) - return moduleStatus; + { + // no point searching known directories for an absolute path + fail(); + } std::vector lib_paths {gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua")}; @@ -416,30 +442,31 @@ LLRequireResolver::ModuleStatus LLRequireResolver::findModule() std::string absolutePathOpt = (std::filesystem::path(path) / mPathToResolve).u8string(); if (absolutePathOpt.empty()) - luaL_errorL(L, "error requiring module"); - - mAbsolutePath = absolutePathOpt; - - moduleStatus = findModuleImpl(); + luaL_error(L, "error requiring module '%s'", mPathToResolve.data()); - if (moduleStatus != LLRequireResolver::ModuleStatus::NotFound) - return moduleStatus; + if (findModuleImpl(absolutePathOpt)) + return; } - return LLRequireResolver::ModuleStatus::NotFound; + // not found + fail(); } -LLRequireResolver::ModuleStatus LLRequireResolver::findModuleImpl() +// expects _MODULES table on stack top (and leaves it there) +// - if found, pushes loaded module and returns true +// - not found, pushes nothing and returns false +// - may throw Lua error +bool LLRequireResolver::findModuleImpl(const std::string& absolutePath) { - std::string possibleSuffixedPaths[] = {mAbsolutePath + ".luau", mAbsolutePath + ".lua"}; + std::string possibleSuffixedPaths[] = {absolutePath + ".luau", absolutePath + ".lua"}; - for (auto suffixedPath : possibleSuffixedPaths) + for (const auto& suffixedPath : possibleSuffixedPaths) { // Check _MODULES cache for module lua_getfield(L, -1, suffixedPath.c_str()); if (!lua_isnil(L, -1)) { - return ModuleStatus::Cached; + return true; } lua_pop(L, 1); @@ -447,91 +474,73 @@ LLRequireResolver::ModuleStatus LLRequireResolver::findModuleImpl() std::string source = read_file(suffixedPath); if (!source.empty()) { - mAbsolutePath = suffixedPath; - mSourceCode = source; - return ModuleStatus::FileRead; - } - } + // Try to run the loaded source. This will leave either a string + // error message or the module contents on the stack top. + runModule(suffixedPath, source); - return ModuleStatus::NotFound; -} + // If the stack top is an error message string, raise it. + if (lua_isstring(L, -1)) + lua_error(L); -void LLRequireResolver::resolveAndStoreDefaultPaths() -{ - if (!std::filesystem::path(mPathToResolve).is_absolute()) - { - mAbsolutePath = (std::filesystem::path((mSourceChunkname)).parent_path() / mPathToResolve).u8string();; - } - else - { - mAbsolutePath = mPathToResolve; - } -} + // duplicate the new module: _MODULES newmodule newmodule + lua_pushvalue(L, -1); + // store _MODULES[found path] = newmodule + lua_setfield(L, -3, source.data()); -static int finishrequire(lua_State *L) -{ - if (lua_isstring(L, -1)) - lua_error(L); + return true; + } + } - return 1; + return false; } -lua_function(require, "require(module_name) : module_name can be fullpath or just the name, in both cases without .lua") +// push string error message or new module +void LLRequireResolver::runModule(const std::string& desc, const std::string& code) { - std::string name = lua_tostdstring(L, 1); - - LLRequireResolver::ResolvedRequire resolvedRequire = LLRequireResolver::resolveRequire(L, name); - - 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"); - - // 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 + // Here we just loaded a new module 'code', need to run it and get its result. + // 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_newthread() pushed the new thread object on GL's stack. Move to L's. lua_xmove(GL, L, 1); // new thread needs to have the globals sandboxed luaL_sandboxthread(ML); { - if (lluau::loadstring(ML, resolvedRequire.absolutePath.c_str(), resolvedRequire.sourceCode.c_str()) == LUA_OK) + // If loadstring() returns (! LUA_OK) then there's an error message on + // the stack. If it returns LUA_OK then the newly-loaded module code + // is on the stack. + if (lluau::loadstring(ML, desc, code) == LUA_OK) { + // luau uses Lua 5.3's version of lua_resume(): + // run the coroutine on ML, "from" L, passing no arguments. int status = lua_resume(ML, L, 0); if (status == LUA_OK) { - if (lua_gettop(ML) == LUA_OK) - lua_pushstring(ML, "module must return a value"); + if (lua_gettop(ML) == 0) + lua_pushfstring(ML, "module %s must return a value", desc.data()); else if (!lua_istable(ML, -1) && !lua_isfunction(ML, -1)) - lua_pushstring(ML, "module must return a table or function"); + lua_pushfstring(ML, "module %s must return a table or function, not %s", + desc.data(), lua_typename(ML, lua_type(ML, -1))); } else if (status == LUA_YIELD) { - lua_pushstring(ML, "module can not yield"); + lua_pushfstring(ML, "module %s can not yield", desc.data()); } else if (!lua_isstring(ML, -1)) { - lua_pushstring(ML, "unknown error while running module"); + lua_pushfstring(ML, "unknown error while running module %s", desc.data()); } } } - // there's now a return value on top of ML; L stack: _MODULES ML + // There's now a return value (string error message or module) on top of ML. + // Move return value to L's stack. lua_xmove(ML, L, 1); - lua_pushvalue(L, -1); - lua_setfield(L, -4, resolvedRequire.absolutePath.c_str()); - - //remove _MODULES from stack - lua_remove(L, -3); - // remove ML from stack + // remove ML from L's stack lua_remove(L, -2); - - // L stack: [module name] [result] - return finishrequire(L); +// // DON'T call lua_close(ML)! Since ML is only a thread of L, corrupts L too! +// lua_close(ML); } diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h index 43950ccee4..fb9f1b8141 100644 --- a/indra/newview/llluamanager.h +++ b/indra/newview/llluamanager.h @@ -87,24 +87,7 @@ public: class LLRequireResolver { public: - enum class ModuleStatus - { - Cached, - FileRead, - NotFound - }; - - struct ResolvedRequire - { - ModuleStatus status; - std::string absolutePath; - std::string sourceCode; - }; - - [[nodiscard]] ResolvedRequire static resolveRequire(lua_State *L, std::string path); - - std::string mAbsolutePath; - std::string mSourceCode; + static void resolveRequire(lua_State *L, std::string path); private: std::string mPathToResolve; @@ -112,10 +95,10 @@ class LLRequireResolver LLRequireResolver(lua_State *L, const std::string& path); - ModuleStatus findModule(); + void findModule(); lua_State *L; - void resolveAndStoreDefaultPaths(); - ModuleStatus findModuleImpl(); + bool findModuleImpl(const std::string& absolutePath); + void runModule(const std::string& desc, const std::string& code); }; #endif -- cgit v1.2.3 From 9bf2837505b7384ac18f1ae23de256729f561dfc Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 29 Feb 2024 14:01:02 -0500 Subject: Add indra/newview/scripts/lua directory, copied into viewer image. --- indra/newview/scripts/lua/testmod.lua | 2 ++ indra/newview/viewer_manifest.py | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 indra/newview/scripts/lua/testmod.lua diff --git a/indra/newview/scripts/lua/testmod.lua b/indra/newview/scripts/lua/testmod.lua new file mode 100644 index 0000000000..22626e4038 --- /dev/null +++ b/indra/newview/scripts/lua/testmod.lua @@ -0,0 +1,2 @@ +print('scripts/lua/testmod.lua') +return function () return 'hello from scripts/lua/testmod.lua' end diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 1fa4df1682..562c7166a2 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -166,6 +166,9 @@ class ViewerManifest(LLManifest): self.path("*/*/*/*.js") self.path("*/*/*.html") + with self.prefix(src_dst="scripts/lua"): + self.path("*.lua") + #build_data.json. Standard with exception handling is fine. If we can't open a new file for writing, we have worse problems #platform is computed above with other arg parsing build_data_dict = {"Type":"viewer","Version":'.'.join(self.args['version']), -- cgit v1.2.3 From c369d436bece8b3ff60768897e88a7f40fdb877d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 29 Feb 2024 15:07:25 -0500 Subject: Fix caching for loaded Lua require() modules. The code to save the loaded module was using the wrong key. --- indra/newview/llluamanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 80d7eb4210..cd663021a1 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -463,7 +463,7 @@ bool LLRequireResolver::findModuleImpl(const std::string& absolutePath) for (const auto& suffixedPath : possibleSuffixedPaths) { // Check _MODULES cache for module - lua_getfield(L, -1, suffixedPath.c_str()); + lua_getfield(L, -1, suffixedPath.data()); if (!lua_isnil(L, -1)) { return true; @@ -485,7 +485,7 @@ bool LLRequireResolver::findModuleImpl(const std::string& absolutePath) // duplicate the new module: _MODULES newmodule newmodule lua_pushvalue(L, -1); // store _MODULES[found path] = newmodule - lua_setfield(L, -3, source.data()); + lua_setfield(L, -3, suffixedPath.data()); return true; } -- cgit v1.2.3 From ba1f0060b42390bd4f63de7fe32b0d0e9360f633 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 29 Feb 2024 15:08:20 -0500 Subject: Clarify that the print output from testmod.lua is load-time. --- indra/newview/scripts/lua/testmod.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/scripts/lua/testmod.lua b/indra/newview/scripts/lua/testmod.lua index 22626e4038..60f7f80db1 100644 --- a/indra/newview/scripts/lua/testmod.lua +++ b/indra/newview/scripts/lua/testmod.lua @@ -1,2 +1,2 @@ -print('scripts/lua/testmod.lua') +print('loaded scripts/lua/testmod.lua') return function () return 'hello from scripts/lua/testmod.lua' end -- cgit v1.2.3 From d50d7c50651d5bcaf6f35349e2461351cf91224f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 29 Feb 2024 15:09:49 -0500 Subject: Add Queue.lua from roblox.com documentation. --- indra/newview/scripts/lua/Queue.lua | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 indra/newview/scripts/lua/Queue.lua diff --git a/indra/newview/scripts/lua/Queue.lua b/indra/newview/scripts/lua/Queue.lua new file mode 100644 index 0000000000..fa8737334c --- /dev/null +++ b/indra/newview/scripts/lua/Queue.lua @@ -0,0 +1,40 @@ +-- from https://create.roblox.com/docs/luau/queues#implementing-queues + +local Queue = {} +Queue.__index = Queue +​ +function Queue.new() + local self = setmetatable({}, Queue) +​ + self._first = 0 + self._last = -1 + self._queue = {} +​ + return self +end +​ +-- Check if the queue is empty +function Queue:IsEmpty() + return self._first > self._last +end +​ +-- Add a value to the queue +function Queue:Enqueue(value) + local last = self._last + 1 + self._last = last + self._queue[last] = value +end +​ +-- Remove a value from the queue +function Queue:Dequeue() + local first = self._first + if self:IsEmpty() then + return nil + end + local value = self._queue[first] + self._queue[first] = nil + self._first = first + 1 + return value +end +​ +return Queue -- cgit v1.2.3 From 6aee62f2bbdd06cf34c4042317cb52ec3c09570c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 29 Feb 2024 16:47:42 -0500 Subject: Fix wonky Unicode chars from web paste --- indra/newview/scripts/lua/Queue.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/indra/newview/scripts/lua/Queue.lua b/indra/newview/scripts/lua/Queue.lua index fa8737334c..e178ad9969 100644 --- a/indra/newview/scripts/lua/Queue.lua +++ b/indra/newview/scripts/lua/Queue.lua @@ -2,29 +2,29 @@ local Queue = {} Queue.__index = Queue -​ + function Queue.new() local self = setmetatable({}, Queue) -​ + self._first = 0 self._last = -1 self._queue = {} -​ + return self end -​ + -- Check if the queue is empty function Queue:IsEmpty() return self._first > self._last end -​ + -- Add a value to the queue function Queue:Enqueue(value) local last = self._last + 1 self._last = last self._queue[last] = value end -​ + -- Remove a value from the queue function Queue:Dequeue() local first = self._first @@ -36,5 +36,5 @@ function Queue:Dequeue() self._first = first + 1 return value end -​ + return Queue -- cgit v1.2.3