From e974e2755a32f3475736011ad222ec86698dec43 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 12 Feb 2024 14:25:14 -0500 Subject: Update luau package to public repo builds. --- autobuild.xml | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 1d6451e93d..03f559500c 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1669,11 +1669,11 @@ creds github hash - c326445818c4e2e2177d5efde1dd59e7af0d6c9a + 59bf3d96f9df4b6981c406abac5c46ae276f9b15 hash_algorithm sha1 url - https://api.github.com/repos/secondlife/3p-luau/releases/assets/150694132 + https://github.com/secondlife/3p-luau/releases/download/v0.609-9567db9/luau-0.609-9567db9-darwin64-9567db9.tar.zst name darwin64 @@ -1685,24 +1685,38 @@ creds github hash - f3bd40eb043f2f36636d847747db20d0168f42dc + 43e2cc2e6e94299f89655435002864925b640e16 hash_algorithm sha1 url - https://api.github.com/repos/secondlife/3p-luau/releases/assets/150694133 + https://github.com/secondlife/3p-luau/releases/download/v0.609-9567db9/luau-0.609-9567db9-windows64-9567db9.tar.zst name windows64 + linux64 + + archive + + hash + 549516ada483ddf276183f66b0a7c3d01e4ef1aa + hash_algorithm + sha1 + url + https://github.com/secondlife/3p-luau/releases/download/v0.609-9567db9/luau-0.609-9567db9-linux64-9567db9.tar.zst + + name + linux64 + license MIT license_file - LICENSES/lua.txt + LICENSES/luau.txt copyright Copyright (c) 2019-2023 Roblox Corporation, Copyright (c) 1994–2019 Lua.org, PUC-Rio. version - 0.609 + 0.609-9567db9 name luau description @@ -2509,8 +2523,6 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors https://bitbucket.org/lindenlab/3p-tracy source_type git - version - v0.8.1.578241 tut -- cgit v1.2.3 From b4583fac09657cb64ed02e82e12ce69c4ace225d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 12 Feb 2024 17:10:42 -0500 Subject: WIP: Changes towards supporting Lua console help text. --- indra/llcommon/lua_function.cpp | 65 +++++++++++++++++++++++++++++++++++-- indra/llcommon/lua_function.h | 4 ++- indra/llui/lltexteditor.cpp | 2 +- indra/llui/lltexteditor.h | 3 +- indra/newview/llfloaterluadebug.cpp | 8 ++--- 5 files changed, 73 insertions(+), 9 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 07e0c1fac2..455e853e8f 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -25,6 +25,9 @@ #include "llsdutil.h" #include "lualistener.h" +/***************************************************************************** +* luau namespace +*****************************************************************************/ namespace { // can't specify free function free() as a unique_ptr deleter @@ -53,6 +56,9 @@ int lluau::dostring(lua_State* L, const std::string& desc, const std::string& te return lua_pcall(L, 0, LUA_MULTRET, 0); } +/***************************************************************************** +* Lua <=> C++ conversions +*****************************************************************************/ std::string lua_tostdstring(lua_State* L, int index) { size_t len; @@ -414,6 +420,9 @@ void lua_pushllsd(lua_State* L, const LLSD& data) } } +/***************************************************************************** +* LuaState class +*****************************************************************************/ LuaState::LuaState(script_finished_fn cb): mCallback(cb), mState(luaL_newstate()) @@ -499,7 +508,9 @@ std::pair LuaState::expr(const std::string& desc, const std::string& return result; } - +/***************************************************************************** +* LuaPopper class +*****************************************************************************/ LuaPopper::~LuaPopper() { if (mCount) @@ -508,6 +519,9 @@ LuaPopper::~LuaPopper() } } +/***************************************************************************** +* LuaFunction class +*****************************************************************************/ LuaFunction::LuaFunction(const std::string_view& name, lua_CFunction function, const std::string_view& helptext) { @@ -539,7 +553,9 @@ LuaFunction::Registry& LuaFunction::getRegistry() return registry; } - +/***************************************************************************** +* lua_what +*****************************************************************************/ std::ostream& operator<<(std::ostream& out, const lua_what& self) { switch (lua_type(self.L, self.index)) @@ -595,6 +611,9 @@ std::ostream& operator<<(std::ostream& out, const lua_what& self) return out; } +/***************************************************************************** +* lua_stack +*****************************************************************************/ std::ostream& operator<<(std::ostream& out, const lua_stack& self) { const char* sep = "stack: ["; @@ -607,7 +626,49 @@ std::ostream& operator<<(std::ostream& out, const lua_stack& self) return out; } +/***************************************************************************** +* DebugExit +*****************************************************************************/ DebugExit::~DebugExit() { LL_DEBUGS("Lua") << "exit " << mName << LL_ENDL; } + +/***************************************************************************** +* help() +*****************************************************************************/ +lua_function(help, + "help(): list viewer's Lua functions\n" + "help(function): show help string for specific function") +{ + auto& luapump{ LLEventPumps::instance().obtain("lua output") }; + const auto& registered{ LuaFunction::getRegistered() }; + if (! lua_gettop(L)) + { + // no arguments passed: list all lua_functions + for (const auto& [name, pair] : registered) + { + const auto& [fptr, helptext] = pair; + luapump.post(helptext); + } + } + else + { + // arguments passed: list each of the specified lua_functions + for (int idx = 1, top = lua_gettop(L); idx <= top; ++idx) + { + auto arg{ lua_tostdstring(L, idx) }; + if (auto found = registered.find(arg); found != registered.end()) + { + luapump.post(found->second.second); + } + else + { + luapump.post(arg + ": NOT FOUND"); + } + } + // pop all arguments + lua_settop(L, 0); + } + return 0; // void return +} diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index c23bf533ba..3129b5eaca 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -131,8 +131,10 @@ public: static lua_CFunction get(const std::string& key); -private: using Registry = std::map>; + static const Registry& getRegistered() { return getRegistry(); } + +private: static Registry& getRegistry(); }; diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 3d2a426913..99154bab83 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1504,7 +1504,7 @@ void LLTextEditor::cleanStringForPaste(LLWString & clean_string) } -void LLTextEditor::pasteTextWithLinebreaks(LLWString & clean_string) +void LLTextEditor::pasteTextWithLinebreaks(const LLWString & clean_string) { std::basic_string::size_type start = 0; std::basic_string::size_type pos = clean_string.find('\n',start); diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index ec2b9a4b7d..6cc2c32d4e 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -289,6 +289,8 @@ protected: void updateLinkSegments(); void keepSelectionOnReturn(bool keep) { mKeepSelectionOnReturn = keep; } class LLViewBorder* mBorder; + void pasteTextWithLinebreaks(const LLWString & clean_string); +// void pasteTextWithLinebreaks(const std::string & clean_string); private: // @@ -296,7 +298,6 @@ private: // void pasteHelper(bool is_primary); void cleanStringForPaste(LLWString & clean_string); - void pasteTextWithLinebreaks(LLWString & clean_string); void onKeyStroke(); diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp index d7b46eaa71..245cc88f79 100644 --- a/indra/newview/llfloaterluadebug.cpp +++ b/indra/newview/llfloaterluadebug.cpp @@ -57,7 +57,7 @@ BOOL LLFloaterLUADebug::postBuild() .listen("LLFloaterLUADebug", [mResultOutput=mResultOutput](const LLSD& data) { - mResultOutput->insertText(data.asString()); + mResultOutput->pasteTextWithLinebreaks(data.asString()); mResultOutput->addLineBreakChar(true); return false; }); @@ -120,13 +120,13 @@ void LLFloaterLUADebug::completion(int count, const LLSD& result) { // error: show error message mResultOutput->insertText("*** "); - mResultOutput->insertText(result.asString()); + mResultOutput->pasteTextWithLinebreaks(result.asString()); return; } if (count == 1) { // single result - mResultOutput->insertText(stringize(result)); + mResultOutput->pasteTextWithLinebreaks(stringize(result)); return; } // 0 or multiple results @@ -134,7 +134,7 @@ void LLFloaterLUADebug::completion(int count, const LLSD& result) for (const auto& item : llsd::inArray(result)) { mResultOutput->insertText(sep); - mResultOutput->insertText(stringize(item)); + mResultOutput->pasteTextWithLinebreaks(stringize(item)); sep = ", "; } } -- cgit v1.2.3 From cd7e0531c0928bc61f8c4a56ee4747f214100d3c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 13 Feb 2024 10:40:57 -0500 Subject: Change ll_convert(string) to just ll_convert(string). As a function parameter, an assignment expression or a `return` expression, `ll_convert()` can infer its target type. When it's important to specify the TOTYPE explicitly, rename the old `ll_convert()` function template to `ll_convert_to()`. Fix existing usage. --- indra/llcommon/llstring.h | 39 +++++++++++++++++++++++++++++++++------ indra/llcommon/stringize.h | 6 +++--- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index b43093fbfc..dbd60bc9c7 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -516,9 +516,36 @@ struct ll_convert_impl TO operator()(const FROM& in) const; }; -// Use a function template to get the nice ll_convert(from_value) API. +/** + * somefunction(ll_convert(data)) + * target = ll_convert(data) + * totype otherfunc(const fromtype& data) + * { + * // ... + * return ll_convert(data); + * } + * all infer both the FROM type and the TO type. + */ +template +class ll_convert +{ +private: + const FROM& mRef; + +public: + ll_convert(const FROM& ref): mRef(ref) {} + + template + inline operator TO() const + { + return ll_convert_impl()(mRef); + } +}; + +// When the TO type must be explicit, use a function template to get +// ll_convert_to(from_value) API. template -TO ll_convert(const FROM& in) +TO ll_convert_to(const FROM& in) { return ll_convert_impl()(in); } @@ -574,8 +601,8 @@ inline size_t ll_convert_length (const char* zstr) { return std::strl // and longname(const string&, len) so calls written pre-ll_convert() will // work. Most of these overloads will be unified once we turn on C++17 and can // use std::string_view. -// It also uses aliasmacro to ensure that both ll_convert(const char*) -// and ll_convert(const string&) will work. +// It also uses aliasmacro to ensure that both ll_convert(const char*) +// and ll_convert(const string&) will work. #define ll_convert_forms(aliasmacro, OUTSTR, INSTR, longname) \ LL_COMMON_API OUTSTR longname(const INSTR::value_type* in, size_t len); \ inline auto longname(const INSTR& in, size_t len) \ @@ -807,7 +834,7 @@ LL_COMMON_API std::string ll_convert_string_to_utf8_string(const std::string& in template STRING windows_message(unsigned long error) { - return ll_convert(windows_message(error)); + return ll_convert(windows_message(error)); } /// There's only one real implementation @@ -1780,7 +1807,7 @@ auto LLStringUtilBase::getoptenv(const std::string& key) -> boost::optional(*found) }; + return { ll_convert_to(*found) }; } else { diff --git a/indra/llcommon/stringize.h b/indra/llcommon/stringize.h index c0b13135f9..63d44a7272 100644 --- a/indra/llcommon/stringize.h +++ b/indra/llcommon/stringize.h @@ -88,13 +88,13 @@ struct gstringize_impl }; // partially specialize for a single STRING argument - -// note that ll_convert(T) already handles the trivial case +// note that ll_convert_to(T) already handles the trivial case template struct gstringize_impl> { auto operator()(const std::basic_string& arg) { - return ll_convert>(arg); + return ll_convert_to>(arg); } }; @@ -105,7 +105,7 @@ struct gstringize_impl { auto operator()(const INCHAR* arg) { - return ll_convert>(arg); + return ll_convert_to>(arg); } }; -- cgit v1.2.3 From 3044a6e62e628fdb3c4b8832fd23e566216d7bb9 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 13 Feb 2024 12:24:13 -0500 Subject: Add help() function to Lua "builtins." help() with no argument lists all our viewer builtins. help(function, function, ...) shows help text for each named function. Each argument can be either a string or the function in question (e.g. help(help)). To support Lua-related text containing line breaks, make LLTextEditor:: pasteTextWithLinebreaks() a public template method. Change the existing implementation, which specifically accepts (const LLWString&), into its LLWString specialization. The generic template passes llconvert(arg) to that specialization, the one real implementation. Make LLFloaterLUADebug methods call pasteTextWithLinebreaks() instead of insertText(), which ignores newline characters. To allow help() to accept an actual function as well as a string name, add a lookup-by-function-pointer map to LuaFunction. (A Lua function does not store a name.) Make the constructor store an entry in the new lookup map as well as in the original registry map. Change LuaFunction::getRegistry() and getRegistered() to getState() and getRState(), respectively. Each returns a std::pair, but the first binds non-const references while the second binds const references. --- indra/llcommon/lua_function.cpp | 113 ++++++++++++++++++++++++---------------- indra/llcommon/lua_function.h | 6 ++- indra/llui/lltexteditor.cpp | 3 +- indra/llui/lltexteditor.h | 15 ++++-- 4 files changed, 86 insertions(+), 51 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 455e853e8f..467ca04449 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -525,12 +525,15 @@ LuaPopper::~LuaPopper() LuaFunction::LuaFunction(const std::string_view& name, lua_CFunction function, const std::string_view& helptext) { - getRegistry().emplace(name, Registry::mapped_type{ function, helptext }); + const auto& [registry, lookup] = getState(); + registry.emplace(name, Registry::mapped_type{ function, helptext }); + lookup.emplace(function, name); } void LuaFunction::init(lua_State* L) { - for (const auto& [name, pair]: getRegistry()) + const auto& [registry, lookup] = getRState(); + for (const auto& [name, pair]: registry) { const auto& [funcptr, helptext] = pair; lua_register(L, name.c_str(), funcptr); @@ -541,16 +544,75 @@ lua_CFunction LuaFunction::get(const std::string& key) { // use find() instead of subscripting to avoid creating an entry for // unknown key - const auto& registry{ getRegistry() }; + const auto& [registry, lookup] = getState(); auto found{ registry.find(key) }; return (found == registry.end())? nullptr : found->second.first; } -LuaFunction::Registry& LuaFunction::getRegistry() +std::pair LuaFunction::getState() { - // use a function-local static to ensure it's initialized + // use function-local statics to ensure they're initialized static Registry registry; - return registry; + static Lookup lookup; + return { registry, lookup }; +} + +/***************************************************************************** +* help() +*****************************************************************************/ +lua_function(help, + "help(): list viewer's Lua functions\n" + "help(function): show help string for specific function") +{ + auto& luapump{ LLEventPumps::instance().obtain("lua output") }; + const auto& [registry, lookup]{ LuaFunction::getRState() }; + if (! lua_gettop(L)) + { + // no arguments passed: list all lua_functions + for (const auto& [name, pair] : registry) + { + const auto& [fptr, helptext] = pair; + luapump.post(helptext); + } + } + else + { + // arguments passed: list each of the specified lua_functions + for (int idx = 1, top = lua_gettop(L); idx <= top; ++idx) + { + std::string arg{ stringize("") }; + if (lua_type(L, idx) == LUA_TSTRING) + { + arg = lua_tostdstring(L, idx); + } + else if (lua_type(L, idx) == LUA_TFUNCTION) + { + // Caller passed the actual function instead of its string + // name. A Lua function is an anonymous callable object; it + // has a name only by assigment. You can't ask Lua for a + // function's name, which is why our constructor maintains a + // reverse Lookup map. + auto function{ lua_tocfunction(L, idx) }; + if (auto found = lookup.find(function); found != lookup.end()) + { + // okay, pass found name to lookup below + arg = found->second; + } + } + + if (auto found = registry.find(arg); found != registry.end()) + { + luapump.post(found->second.second); + } + else + { + luapump.post(arg + ": NOT FOUND"); + } + } + // pop all arguments + lua_settop(L, 0); + } + return 0; // void return } /***************************************************************************** @@ -633,42 +695,3 @@ DebugExit::~DebugExit() { LL_DEBUGS("Lua") << "exit " << mName << LL_ENDL; } - -/***************************************************************************** -* help() -*****************************************************************************/ -lua_function(help, - "help(): list viewer's Lua functions\n" - "help(function): show help string for specific function") -{ - auto& luapump{ LLEventPumps::instance().obtain("lua output") }; - const auto& registered{ LuaFunction::getRegistered() }; - if (! lua_gettop(L)) - { - // no arguments passed: list all lua_functions - for (const auto& [name, pair] : registered) - { - const auto& [fptr, helptext] = pair; - luapump.post(helptext); - } - } - else - { - // arguments passed: list each of the specified lua_functions - for (int idx = 1, top = lua_gettop(L); idx <= top; ++idx) - { - auto arg{ lua_tostdstring(L, idx) }; - if (auto found = registered.find(arg); found != registered.end()) - { - luapump.post(found->second.second); - } - else - { - luapump.post(arg + ": NOT FOUND"); - } - } - // pop all arguments - lua_settop(L, 0); - } - return 0; // void return -} diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 3129b5eaca..b3a0bb0d7e 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -131,11 +131,13 @@ public: static lua_CFunction get(const std::string& key); +protected: using Registry = std::map>; - static const Registry& getRegistered() { return getRegistry(); } + using Lookup = std::map; + static std::pair getRState() { return getState(); } private: - static Registry& getRegistry(); + static std::pair getState(); }; /** diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 99154bab83..ccd04f83e7 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1504,7 +1504,8 @@ void LLTextEditor::cleanStringForPaste(LLWString & clean_string) } -void LLTextEditor::pasteTextWithLinebreaks(const LLWString & clean_string) +template <> +void LLTextEditor::pasteTextWithLinebreaks(const LLWString & clean_string) { std::basic_string::size_type start = 0; std::basic_string::size_type pos = clean_string.find('\n',start); diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 6cc2c32d4e..01b5ce5fbd 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -34,6 +34,7 @@ #include "llstyle.h" #include "lleditmenuhandler.h" #include "llviewborder.h" // for params +#include "llstring.h" #include "lltextbase.h" #include "lltextvalidate.h" @@ -289,16 +290,24 @@ protected: void updateLinkSegments(); void keepSelectionOnReturn(bool keep) { mKeepSelectionOnReturn = keep; } class LLViewBorder* mBorder; - void pasteTextWithLinebreaks(const LLWString & clean_string); -// void pasteTextWithLinebreaks(const std::string & clean_string); private: // // Methods // - void pasteHelper(bool is_primary); + void pasteHelper(bool is_primary); void cleanStringForPaste(LLWString & clean_string); +public: + template + void pasteTextWithLinebreaks(const STRINGTYPE& clean_string) + { + pasteTextWithLinebreaks(ll_convert(clean_string)); + } + template <> + void pasteTextWithLinebreaks(const LLWString & clean_string); + +private: void onKeyStroke(); // Concrete TextCmd sub-classes used by the LLTextEditor base class -- cgit v1.2.3 From d583fb8badd8060c0f74bcb6e99bb6e7d08a67d0 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 13 Feb 2024 17:34:00 -0500 Subject: Add leaphelp() Lua builtin function for help on LEAP operations. leaphelp() (no argument) shows a list of all LEAP APIs. leaphelp(API) shows further help for a specific API. Both forms query LuaListener's LeapListener and report its responses. In future we might reimplement leaphelp() as a Lua function. Add LuaState::getListener() method, which checks whether there's a LuaListener associated with this LuaState and returns a pointer if so. Add LuaState::obtainListener() method, which finds or creates a LuaListener for this LuaState and returns its pointer. Both the above use logic migrated from the Lua listen_events() entry point, which now calls obtainListener() instead. --- indra/llcommon/lua_function.cpp | 130 +++++++++++++++++++++++++++++++++++----- indra/llcommon/lua_function.h | 14 +++++ indra/newview/llluamanager.cpp | 29 +-------- 3 files changed, 129 insertions(+), 44 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 467ca04449..13210fe5a6 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -21,6 +21,7 @@ // external library headers // other Linden headers #include "hexdump.h" +#include "lleventcoro.h" #include "llsd.h" #include "llsdutil.h" #include "lualistener.h" @@ -435,25 +436,15 @@ LuaState::LuaState(script_finished_fn cb): LuaState::~LuaState() { - // Did somebody call listen_events() on this LuaState? + // Did somebody call obtainListener() on this LuaState? // That is, is there a LuaListener key in its registry? - auto keytype{ lua_getfield(mState, LUA_REGISTRYINDEX, "event.listener") }; - if (keytype == LUA_TNUMBER) + auto listener{ getListener() }; + if (listener) { - // We do have a LuaListener. Retrieve it. - int isint; - auto listener{ LuaListener::getInstance(lua_tointegerx(mState, -1, &isint)) }; - // pop the int "event.listener" key - lua_pop(mState, 1); // if we got a LuaListener instance, destroy it - // (if (! isint), lua_tointegerx() returned 0, but key 0 might - // validly designate someone ELSE's LuaListener) - if (isint && listener) - { - auto lptr{ listener.get() }; - listener.reset(); - delete lptr; - } + auto lptr{ listener.get() }; + listener.reset(); + delete lptr; } lua_close(mState); @@ -508,6 +499,45 @@ std::pair LuaState::expr(const std::string& desc, const std::string& return result; } +LuaListener::ptr_t LuaState::getListener(lua_State* L) +{ + // have to use one more stack slot + luaL_checkstack(L, 1, nullptr); + LuaListener::ptr_t listener; + // Does this lua_State already have a LuaListener stored in the registry? + auto keytype{ lua_getfield(L, LUA_REGISTRYINDEX, "event.listener") }; + llassert(keytype == LUA_TNIL || keytype == LUA_TNUMBER); + if (keytype == LUA_TNUMBER) + { + // We do already have a LuaListener. Retrieve it. + int isint; + listener = LuaListener::getInstance(lua_tointegerx(L, -1, &isint)); + // Nobody should have destroyed this LuaListener instance! + llassert(isint && listener); + } + // pop the int "event.listener" key + lua_pop(L, 1); + return listener; +} + +LuaListener::ptr_t LuaState::obtainListener(lua_State* L) +{ + auto listener{ getListener(L) }; + if (! listener) + { + // have to use one more stack slot + luaL_checkstack(L, 1, nullptr); + // instantiate a new LuaListener, binding the L state -- but use a + // no-op deleter: we do NOT want this ptr_t to manage the lifespan of + // this new LuaListener! + listener.reset(new LuaListener(L), [](LuaListener*){}); + // set its key in the field where we'll look for it later + lua_pushinteger(L, listener->getKey()); + lua_setfield(L, LUA_REGISTRYINDEX, "event.listener"); + } + return listener; +} + /***************************************************************************** * LuaPopper class *****************************************************************************/ @@ -615,6 +645,74 @@ lua_function(help, return 0; // void return } +/***************************************************************************** +* leaphelp() +*****************************************************************************/ +lua_function( + leaphelp, + "leaphelp(): list viewer's LEAP APIs\n" + "leaphelp(api): show help for specific api string name") +{ + LLSD request; + int top{ lua_gettop(L) }; + if (top) + { + request = llsd::map("op", "getAPI", "api", lua_tostdstring(L, 1)); + } + else + { + request = llsd::map("op", "getAPIs"); + } + // pop all args + lua_settop(L, 0); + + auto& outpump{ LLEventPumps::instance().obtain("lua output") }; + auto listener{ LuaState::obtainListener(L) }; + LLEventStream replyPump("leaphelp", true); + // ask the LuaListener's LeapListener and suspend calling coroutine until reply + auto reply{ llcoro::postAndSuspend(request, listener->getCommandName(), replyPump, "reply") }; + reply.erase("reqid"); + + if (auto error = reply["error"]; error.isString()) + { + outpump.post(error.asString()); + return 0; + } + + if (top) + { + // caller wants a specific API + outpump.post(stringize(reply["name"].asString(), ":\n", reply["desc"].asString())); + for (const auto& opmap : llsd::inArray(reply["ops"])) + { + std::ostringstream reqstr; + auto req{ opmap["required"] }; + if (req.isArray()) + { + const char* sep = " (requires "; + for (const auto& [reqkey, reqval] : llsd::inMap(req)) + { + reqstr << sep << reqkey; + sep = ", "; + } + reqstr << ")"; + } + outpump.post(stringize("---- ", reply["key"].asString(), " == '", + opmap["name"].asString(), "'", reqstr.str(), ":\n", + opmap["desc"].asString())); + } + } + else + { + // caller wants a list of APIs + for (const auto& [name, data] : llsd::inMap(reply)) + { + outpump.post(stringize("==== ", name, ":\n", data["desc"].asString())); + } + } + return 0; // void return +} + /***************************************************************************** * lua_what *****************************************************************************/ diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index b3a0bb0d7e..7973a769be 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -17,8 +17,11 @@ #include "luau/luaconf.h" #include "luau/lualib.h" #include "stringize.h" +#include // std::shared_ptr #include // std::pair +class LuaListener; + #define lua_register(L, n, f) (lua_pushcfunction(L, (f), n), lua_setglobal(L, (n))) #define lua_rawlen lua_objlen @@ -83,6 +86,17 @@ public: operator lua_State*() const { return mState; } + // Return LuaListener for this LuaState if we already have one, else empty + // shared_ptr. + std::shared_ptr getListener() { return getListener(mState); } + // Find or create LuaListener for this LuaState, returning its ptr_t. + std::shared_ptr obtainListener() { return obtainListener(mState); } + // Return LuaListener for passed lua_State if we already have one, else + // empty shared_ptr. + static std::shared_ptr getListener(lua_State* L); + // Find or create LuaListener for passed lua_State, returning its ptr_t. + static std::shared_ptr obtainListener(lua_State* L); + private: script_finished_fn mCallback; lua_State* mState; diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index d58ee5ca5f..c6a900aa81 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -197,34 +197,7 @@ lua_function(listen_events, // back to the main thread from a coroutine thread? lua_State* mainthread{ L }; - luaL_checkstack(mainthread, 1, nullptr); - LuaListener::ptr_t listener; - // Does the main thread already have a LuaListener stored in the registry? - // That is, has this Lua chunk already called listen_events()? - auto keytype{ lua_getfield(mainthread, LUA_REGISTRYINDEX, "event.listener") }; - llassert(keytype == LUA_TNIL || keytype == LUA_TNUMBER); - if (keytype == LUA_TNUMBER) - { - // We do already have a LuaListener. Retrieve it. - int isint; - listener = LuaListener::getInstance(lua_tointegerx(mainthread, -1, &isint)); - // pop the int "event.listener" key - lua_pop(mainthread, 1); - // Nobody should have destroyed this LuaListener instance! - llassert(isint && listener); - } - else - { - // pop the nil "event.listener" key - lua_pop(mainthread, 1); - // instantiate a new LuaListener, binding the mainthread state -- but - // use a no-op deleter: we do NOT want to delete this new LuaListener - // on return from listen_events()! - listener.reset(new LuaListener(mainthread), [](LuaListener*){}); - // set its key in the field where we'll look for it later - lua_pushinteger(mainthread, listener->getKey()); - lua_setfield(mainthread, LUA_REGISTRYINDEX, "event.listener"); - } + auto listener{ LuaState::obtainListener(mainthread) }; // Now that we've found or created our LuaListener, store the passed Lua // function as the callback. Beware: our caller passed the function on L's -- cgit v1.2.3