diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2024-02-13 17:34:00 -0500 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2024-02-13 17:34:00 -0500 |
commit | d583fb8badd8060c0f74bcb6e99bb6e7d08a67d0 (patch) | |
tree | 270b33a014a7b138f4ab35b2176038309ed4f0f7 | |
parent | ffabe80fd98ad5a13b8833153606f47941fc6e42 (diff) |
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.
-rw-r--r-- | indra/llcommon/lua_function.cpp | 130 | ||||
-rw-r--r-- | indra/llcommon/lua_function.h | 14 | ||||
-rw-r--r-- | 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<int, LLSD> 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 *****************************************************************************/ @@ -616,6 +646,74 @@ lua_function(help, } /***************************************************************************** +* 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 *****************************************************************************/ std::ostream& operator<<(std::ostream& out, const lua_what& self) 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 <memory> // std::shared_ptr #include <utility> // 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<LuaListener> getListener() { return getListener(mState); } + // Find or create LuaListener for this LuaState, returning its ptr_t. + std::shared_ptr<LuaListener> obtainListener() { return obtainListener(mState); } + // Return LuaListener for passed lua_State if we already have one, else + // empty shared_ptr. + static std::shared_ptr<LuaListener> getListener(lua_State* L); + // Find or create LuaListener for passed lua_State, returning its ptr_t. + static std::shared_ptr<LuaListener> 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 |