summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/CMakeLists.txt1
-rw-r--r--indra/llcommon/fsyspath.h74
-rw-r--r--indra/llcommon/llleaplistener.cpp46
-rw-r--r--indra/llcommon/llstring.h5
-rw-r--r--indra/llcommon/lua_function.cpp61
-rw-r--r--indra/llcommon/lua_function.h3
-rw-r--r--indra/llcommon/lualistener.cpp1
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&)