diff options
Diffstat (limited to 'indra/llcommon')
-rw-r--r-- | indra/llcommon/CMakeLists.txt | 1 | ||||
-rw-r--r-- | indra/llcommon/fsyspath.h | 74 | ||||
-rw-r--r-- | indra/llcommon/llleaplistener.cpp | 46 | ||||
-rw-r--r-- | indra/llcommon/llstring.h | 5 | ||||
-rw-r--r-- | indra/llcommon/lua_function.cpp | 61 | ||||
-rw-r--r-- | indra/llcommon/lua_function.h | 3 | ||||
-rw-r--r-- | indra/llcommon/lualistener.cpp | 1 |
7 files changed, 166 insertions, 25 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index aed9ee080b..aa0b66f2f4 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -127,6 +127,7 @@ set(llcommon_HEADER_FILES commoncontrol.h ctype_workaround.h fix_macros.h + fsyspath.h function_types.h indra_constants.h lazyeventapi.h diff --git a/indra/llcommon/fsyspath.h b/indra/llcommon/fsyspath.h new file mode 100644 index 0000000000..aa4e0132bc --- /dev/null +++ b/indra/llcommon/fsyspath.h @@ -0,0 +1,74 @@ +/** + * @file fsyspath.h + * @author Nat Goodspeed + * @date 2024-04-03 + * @brief Adapt our UTF-8 std::strings for std::filesystem::path + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Copyright (c) 2024, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_FSYSPATH_H) +#define LL_FSYSPATH_H + +#include <filesystem> + +// While std::filesystem::path can be directly constructed from std::string on +// both Posix and Windows, that's not what we want on Windows. Per +// https://en.cppreference.com/w/cpp/filesystem/path/path: + +// ... the method of conversion to the native character set depends on the +// character type used by source. +// +// * If the source character type is char, the encoding of the source is +// assumed to be the native narrow encoding (so no conversion takes place on +// POSIX systems). +// * If the source character type is char8_t, conversion from UTF-8 to native +// filesystem encoding is used. (since C++20) +// * If the source character type is wchar_t, the input is assumed to be the +// native wide encoding (so no conversion takes places on Windows). + +// The trouble is that on Windows, from std::string ("source character type is +// char"), the "native narrow encoding" isn't UTF-8, so file paths containing +// non-ASCII characters get mangled. +// +// Once we're building with C++20, we could pass a UTF-8 std::string through a +// vector<char8_t> to engage std::filesystem::path's own UTF-8 conversion. But +// sigh, as of 2024-04-03 we're not yet there. +// +// Anyway, encapsulating the important UTF-8 conversions in our own subclass +// allows us to migrate forward to C++20 conventions without changing +// referencing code. + +class fsyspath: public std::filesystem::path +{ + using super = std::filesystem::path; + +public: + // default + fsyspath() {} + // construct from UTF-8 encoded std::string + fsyspath(const std::string& path): super(std::filesystem::u8path(path)) {} + // construct from UTF-8 encoded const char* + fsyspath(const char* path): super(std::filesystem::u8path(path)) {} + // construct from existing path + fsyspath(const super& path): super(path) {} + + fsyspath& operator=(const super& p) { super::operator=(p); return *this; } + fsyspath& operator=(const std::string& p) + { + super::operator=(std::filesystem::u8path(p)); + return *this; + } + fsyspath& operator=(const char* p) + { + super::operator=(std::filesystem::u8path(p)); + return *this; + } + + // shadow base-class string() method with UTF-8 aware method + std::string string() const { return super::u8string(); } +}; + +#endif /* ! defined(LL_FSYSPATH_H) */ diff --git a/indra/llcommon/llleaplistener.cpp b/indra/llcommon/llleaplistener.cpp index 55e4752c5d..9b9b0f5121 100644 --- a/indra/llcommon/llleaplistener.cpp +++ b/indra/llcommon/llleaplistener.cpp @@ -70,44 +70,46 @@ LLLeapListener::LLLeapListener(const std::string_view& caller, const Callback& c { LLSD need_name(LLSDMap("name", LLSD())); add("newpump", - "Instantiate a new LLEventPump named like [\"name\"] and listen to it.\n" - "[\"type\"] == \"LLEventStream\", \"LLEventMailDrop\" et al.\n" - "Events sent through new LLEventPump will be decorated with [\"pump\"]=name.\n" - "Returns actual name in [\"name\"] (may be different if collision).", +R"-(Instantiate a new LLEventPump named like ["name"] and listen to it. +["type"] == "LLEventStream", "LLEventMailDrop" et al. +Events sent through new LLEventPump will be decorated with ["pump"]=name. +Returns actual name in ["name"] (may be different if collision).)-", &LLLeapListener::newpump, need_name); LLSD need_source_listener(LLSDMap("source", LLSD())("listener", LLSD())); add("listen", - "Listen to an existing LLEventPump named [\"source\"], with listener name\n" - "[\"listener\"].\n" - "By default, send events on [\"source\"] to the plugin, decorated\n" - "with [\"pump\"]=[\"source\"].\n" - "If [\"dest\"] specified, send undecorated events on [\"source\"] to the\n" - "LLEventPump named [\"dest\"].\n" - "Returns [\"status\"] boolean indicating whether the connection was made.", +R"-(Listen to an existing LLEventPump named ["source"], with listener name +["listener"]. +If ["tweak"] is specified as true, tweak listener name for uniqueness. +By default, send events on ["source"] to the plugin, decorated +with ["pump"]=["source"]. +If ["dest"] specified, send undecorated events on ["source"] to the +LLEventPump named ["dest"]. +Returns ["status"] boolean indicating whether the connection was made, +plus ["listener"] reporting (possibly tweaked) listener name.)-", &LLLeapListener::listen, need_source_listener); add("stoplistening", - "Disconnect a connection previously established by \"listen\".\n" - "Pass same [\"source\"] and [\"listener\"] arguments.\n" - "Returns [\"status\"] boolean indicating whether such a listener existed.", +R"-(Disconnect a connection previously established by "listen". +Pass same ["source"] and ["listener"] arguments. +Returns ["status"] boolean indicating whether such a listener existed.)-", &LLLeapListener::stoplistening, need_source_listener); add("ping", - "No arguments, just a round-trip sanity check.", +"No arguments, just a round-trip sanity check.", &LLLeapListener::ping); add("getAPIs", - "Enumerate all LLEventAPI instances by name and description.", +"Enumerate all LLEventAPI instances by name and description.", &LLLeapListener::getAPIs); add("getAPI", - "Get name, description, dispatch key and operations for LLEventAPI [\"api\"].", +R"-(Get name, description, dispatch key and operations for LLEventAPI ["api"].)-", &LLLeapListener::getAPI, LLSD().with("api", LLSD())); add("getFeatures", - "Return an LLSD map of feature strings (deltas from baseline LEAP protocol)", +"Return an LLSD map of feature strings (deltas from baseline LEAP protocol)", static_cast<void (LLLeapListener::*)(const LLSD&) const>(&LLLeapListener::getFeatures)); add("getFeature", - "Return the feature value with key [\"feature\"]", +R"-(Return the feature value with key ["feature"])-", &LLLeapListener::getFeature, LLSD().with("feature", LLSD())); } @@ -119,6 +121,7 @@ LLLeapListener::~LLLeapListener() // value_type, and Bad Things would happen if you copied an // LLTempBoundListener. (Destruction of the original would disconnect the // listener, invalidating every stored connection.) + LL_DEBUGS("LLLeapListener") << "~LLLeapListener(\"" << mCaller << "\")" << LL_ENDL; for (ListenersMap::value_type& pair : mListeners) { pair.second.disconnect(); @@ -155,6 +158,11 @@ void LLLeapListener::listen(const LLSD& request) std::string source_name = request["source"]; std::string dest_name = request["dest"]; std::string listener_name = request["listener"]; + if (request["tweak"].asBoolean()) + { + listener_name = LLEventPump::inventName(listener_name); + } + reply["listener"] = listener_name; LLEventPump & source = LLEventPumps::instance().obtain(source_name); diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 0eb2770004..14aa51cb4a 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -37,6 +37,7 @@ #include <algorithm> #include <vector> #include <map> +#include <type_traits> #include "llformat.h" #include "stdtypes.h" @@ -542,7 +543,7 @@ public: template <typename TO> inline operator TO() const { - return ll_convert_impl<TO, FROM>()(mRef); + return ll_convert_impl<TO, std::decay_t<const FROM>>()(mRef); } }; @@ -551,7 +552,7 @@ public: template<typename TO, typename FROM> TO ll_convert_to(const FROM& in) { - return ll_convert_impl<TO, FROM>()(in); + return ll_convert_impl<TO, std::decay_t<const FROM>>()(in); } // degenerate case diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index e731408c7d..7a5668f384 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -17,13 +17,13 @@ // std headers #include <algorithm> #include <exception> -#include <filesystem> #include <iomanip> // std::quoted #include <map> #include <memory> // std::unique_ptr #include <typeinfo> // external library headers // other Linden headers +#include "fsyspath.h" #include "hexdump.h" #include "lleventcoro.h" #include "llsd.h" @@ -68,6 +68,15 @@ int lluau::loadstring(lua_State *L, const std::string &desc, const std::string & return luau_load(L, desc.data(), bytecode.get(), bytecodeSize, 0); } +fsyspath lluau::source_path(lua_State* 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); + return ar.source; +} + /***************************************************************************** * Lua <=> C++ conversions *****************************************************************************/ @@ -484,11 +493,15 @@ bool LuaState::checkLua(const std::string& desc, int r) std::pair<int, LLSD> LuaState::expr(const std::string& desc, const std::string& text) { if (! checkLua(desc, lluau::dostring(mState, desc, text))) + { + LL_WARNS("Lua") << desc << " error: " << mError << LL_ENDL; return { -1, mError }; + } // here we believe there was no error -- did the Lua fragment leave // anything on the stack? std::pair<int, LLSD> result{ lua_gettop(mState), {} }; + LL_INFOS("Lua") << desc << " done, " << result.first << " results." << LL_ENDL; if (result.first) { // aha, at least one entry on the stack! @@ -501,6 +514,7 @@ std::pair<int, LLSD> LuaState::expr(const std::string& desc, const std::string& } catch (const std::exception& error) { + LL_WARNS("Lua") << desc << " error converting result: " << error.what() << LL_ENDL; // lua_tollsd() is designed to be called from a lua_function(), // that is, from a C++ function called by Lua. In case of error, // it throws a Lua error to be caught by the Lua runtime. expr() @@ -519,15 +533,18 @@ std::pair<int, LLSD> LuaState::expr(const std::string& desc, const std::string& else { // multiple entries on the stack + int index; try { - for (int index = 1; index <= result.first; ++index) + for (index = 1; index <= result.first; ++index) { result.second.append(lua_tollsd(mState, index)); } } catch (const std::exception& error) { + LL_WARNS("Lua") << desc << " error converting result " << index << ": " + << error.what() << LL_ENDL; // see above comments regarding lua_State's error status initLuaState(); return { -1, stringize(LLError::Log::classname(error), ": ", error.what()) }; @@ -560,7 +577,7 @@ std::pair<int, LLSD> LuaState::expr(const std::string& desc, const std::string& // the next call to lua_next." // https://www.lua.org/manual/5.1/manual.html#lua_next if (lua_type(mState, -2) == LUA_TSTRING && - std::filesystem::path(lua_tostdstring(mState, -2)).stem() == "fiber") + fsyspath(lua_tostdstring(mState, -2)).stem() == "fiber") { found = true; break; @@ -577,9 +594,13 @@ std::pair<int, LLSD> LuaState::expr(const std::string& desc, const std::string& { // there's a fiber.run() function sitting on the top of the stack // -- call it with no arguments, discarding anything it returns - LL_DEBUGS("Lua") << "Calling fiber.run()" << LL_ENDL; + LL_INFOS("Lua") << desc << " p.s. fiber.run()" << LL_ENDL; if (! checkLua(desc, lua_pcall(mState, 0, 0, 0))) + { + LL_WARNS("Lua") << desc << " p.s. fiber.run() error: " << mError << LL_ENDL; return { -1, mError }; + } + LL_INFOS("Lua") << desc << " p.s. done." << LL_ENDL; } } // pop everything again @@ -685,6 +706,38 @@ std::pair<LuaFunction::Registry&, LuaFunction::Lookup&> LuaFunction::getState() } /***************************************************************************** +* source_path() +*****************************************************************************/ +lua_function(source_path, "return the source path of the running Lua script") +{ + luaL_checkstack(L, 1, nullptr); + lua_pushstdstring(L, lluau::source_path(L).u8string()); + return 1; +} + +/***************************************************************************** +* source_dir() +*****************************************************************************/ +lua_function(source_dir, "return the source directory of the running Lua script") +{ + luaL_checkstack(L, 1, nullptr); + lua_pushstdstring(L, lluau::source_path(L).parent_path().u8string()); + return 1; +} + +/***************************************************************************** +* abspath() +*****************************************************************************/ +lua_function(abspath, + "for given filesystem path relative to running script, return absolute path") +{ + auto path{ lua_tostdstring(L, 1) }; + lua_pop(L, 1); + lua_pushstdstring(L, (lluau::source_path(L).parent_path() / path).u8string()); + return 1; +} + +/***************************************************************************** * check_stop() *****************************************************************************/ lua_function(check_stop, "ensure that a Lua script responds to viewer shutdown") diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 868c13c3f1..ec1e6cdb10 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -16,6 +16,7 @@ #include "luau/lua.h" #include "luau/luaconf.h" #include "luau/lualib.h" +#include "fsyspath.h" #include "stringize.h" #include <exception> // std::uncaught_exceptions() #include <memory> // std::shared_ptr @@ -49,6 +50,8 @@ namespace lluau // 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); + + fsyspath source_path(lua_State* L); } // namespace lluau std::string lua_tostdstring(lua_State* L, int index); diff --git a/indra/llcommon/lualistener.cpp b/indra/llcommon/lualistener.cpp index 018a31d5a8..82e32860db 100644 --- a/indra/llcommon/lualistener.cpp +++ b/indra/llcommon/lualistener.cpp @@ -104,6 +104,7 @@ LuaListener::PumpData LuaListener::getNext() { try { + LLCoros::TempStatus status("get_event_next()"); return mQueue.pop(); } catch (const LLThreadSafeQueueInterrupt&) |