summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rwxr-xr-xindra/cmake/run_build_test.py10
-rw-r--r--indra/llcommon/llevents.cpp8
-rw-r--r--indra/llcommon/llleap.cpp61
-rw-r--r--indra/llcommon/llleaplistener.cpp37
-rw-r--r--indra/llcommon/llleaplistener.h27
-rw-r--r--indra/llcommon/llstring.h39
-rw-r--r--indra/llcommon/lua_function.cpp230
-rw-r--r--indra/llcommon/lua_function.h34
-rw-r--r--indra/llcommon/lualistener.cpp76
-rw-r--r--indra/llcommon/lualistener.h35
-rw-r--r--indra/llcommon/stringize.h6
-rw-r--r--indra/llcommon/tests/lleventcoro_test.cpp20
-rw-r--r--indra/llui/lltexteditor.cpp3
-rw-r--r--indra/llui/lltexteditor.h14
-rw-r--r--indra/newview/llfloaterluadebug.cpp8
-rw-r--r--indra/newview/llluamanager.cpp90
-rw-r--r--indra/newview/tests/llluamanager_test.cpp35
-rw-r--r--indra/test/debug.h16
-rw-r--r--indra/test/lltut.h2
-rw-r--r--indra/test/print.h4
20 files changed, 464 insertions, 291 deletions
diff --git a/indra/cmake/run_build_test.py b/indra/cmake/run_build_test.py
index 1f040bded5..ef4d712254 100755
--- a/indra/cmake/run_build_test.py
+++ b/indra/cmake/run_build_test.py
@@ -122,19 +122,17 @@ def main(command, arguments=[], libpath=[], vars={}):
# Make sure we see all relevant output *before* child-process output.
sys.stdout.flush()
try:
- return subprocess.call(command_list)
- except OSError as err:
+ return subprocess.run(command_list).returncode
+ except FileNotFoundError as err:
# If the caller is trying to execute a test program that doesn't
# exist, we want to produce a reasonable error message rather than a
# traceback. This happens when the build is halted by errors, but
# CMake tries to proceed with testing anyway <eyeroll/>. However, do
# NOT attempt to handle any error but "doesn't exist."
- if err.errno != errno.ENOENT:
- raise
# In practice, the pathnames into CMake's build tree are so long as to
# obscure the name of the test program. Just log its basename.
- log.warn("No such program %s; check for preceding build errors" % \
- os.path.basename(command[0]))
+ log.warning("No such program %s; check for preceding build errors" %
+ os.path.basename(command[0]))
# What rc should we simulate for missing executable? Windows produces
# 9009.
return 9009
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index deb0776887..01bba7a620 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -43,6 +43,7 @@
#include <typeinfo>
#include <cmath>
#include <cctype>
+#include <iomanip> // std::quoted
// external library headers
#include <boost/range/iterator_range.hpp>
#if LL_WINDOWS
@@ -189,8 +190,13 @@ bool LLEventPumps::post(const std::string&name, const LLSD&message)
PumpMap::iterator found = mPumpMap.find(name);
if (found == mPumpMap.end())
+ {
+ LL_DEBUGS("LLEventPumps") << "LLEventPump(" << std::quoted(name) << ") not found"
+ << LL_ENDL;
return false;
+ }
+// LL_DEBUGS("LLEventPumps") << "posting to " << name << ": " << message << LL_ENDL;
return (*found).second->post(message);
}
@@ -405,7 +411,7 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
{
if (!mSignal)
{
- LL_WARNS() << "Can't connect listener" << LL_ENDL;
+ LL_WARNS("LLEventPump") << "Can't connect listener" << LL_ENDL;
// connect will fail, return dummy
return LLBoundListener();
}
diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp
index 8f88e728ce..f7f2981638 100644
--- a/indra/llcommon/llleap.cpp
+++ b/indra/llcommon/llleap.cpp
@@ -14,13 +14,11 @@
// associated header
#include "llleap.h"
// STL headers
-#include <sstream>
#include <algorithm>
+#include <memory>
+#include <sstream>
// std headers
// external library headers
-#include <boost/bind.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <boost/tokenizer.hpp>
// other Linden headers
#include "llerror.h"
#include "llstring.h"
@@ -53,18 +51,19 @@ public:
// We expect multiple LLLeapImpl instances. Definitely tweak
// mDonePump's name for uniqueness.
mDonePump("LLLeap", true),
- // Troubling thought: what if one plugin intentionally messes with
- // another plugin? LLEventPump names are in a single global namespace.
- // Try to make that more difficult by generating a UUID for the reply-
- // pump name -- so it should NOT need tweaking for uniqueness.
- mReplyPump(LLUUID::generateNewID().asString()),
mExpect(0),
// Instantiate a distinct LLLeapListener for this plugin. (Every
// plugin will want its own collection of managed listeners, etc.)
- // Pass it a callback to our connect() method, so it can send events
+ // Pass it our wstdin() method as its callback, so it can send events
// from a particular LLEventPump to the plugin without having to know
// this class or method name.
- mListener(new LLLeapListener(boost::bind(&LLLeapImpl::connect, this, _1, _2)))
+ mListener(
+ new LLLeapListener(
+ "LLLeap",
+ // Serialize any reply event to our child's stdin, suitably
+ // enriched with the pump name on which it was received.
+ [this](const std::string& pump, const LLSD& data)
+ { return wstdin(pump, data); }))
{
// Rule out unpopulated Params block
if (! cparams.executable.isProvided())
@@ -93,7 +92,7 @@ public:
}
// Listen for child "termination" right away to catch launch errors.
- mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::bad_launch, this, _1));
+ mDonePump.listen("LLLeap", [this](const LLSD& data){ return bad_launch(data); });
// Okay, launch child.
// Get a modifiable copy of params block to set files and postend.
@@ -113,7 +112,7 @@ public:
// Okay, launch apparently worked. Change our mDonePump listener.
mDonePump.stopListening("LLLeap");
- mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::done, this, _1));
+ mDonePump.listen("LLLeap", [this](const LLSD& data){ return done(data); });
// Child might pump large volumes of data through either stdout or
// stderr. Don't bother copying all that data into notification event.
@@ -123,17 +122,14 @@ public:
childout.setLimit(20);
childerr.setLimit(20);
- // Serialize any event received on mReplyPump to our child's stdin.
- mStdinConnection = connect(mReplyPump, "LLLeap");
-
// Listening on stdout is stateful. In general, we're either waiting
// for the length prefix or waiting for the specified length of data.
// We address that with two different listener methods -- one of which
// is blocked at any given time.
mStdoutConnection = childout.getPump()
- .listen("prefix", boost::bind(&LLLeapImpl::rstdout, this, _1));
+ .listen("prefix", [this](const LLSD& data){ return rstdout(data); });
mStdoutDataConnection = childout.getPump()
- .listen("data", boost::bind(&LLLeapImpl::rstdoutData, this, _1));
+ .listen("data", [this](const LLSD& data){ return rstdoutData(data); });
mBlocker.reset(new LLEventPump::Blocker(mStdoutDataConnection));
// Log anything sent up through stderr. When a typical program
@@ -142,7 +138,7 @@ public:
// interpreter behaves that way. More generally, though, a plugin
// author can log whatever s/he wants to the viewer log using stderr.
mStderrConnection = childerr.getPump()
- .listen("LLLeap", boost::bind(&LLLeapImpl::rstderr, this, _1));
+ .listen("LLLeap", [this](const LLSD& data){ return rstderr(data); });
// For our lifespan, intercept any LL_ERRS so we can notify plugin
mRecorder = LLError::addGenericRecorder(
@@ -151,7 +147,7 @@ public:
// Send child a preliminary event reporting our own reply-pump name --
// which would otherwise be pretty tricky to guess!
- wstdin(mReplyPump.getName(),
+ wstdin(mListener->getReplyPump().getName(),
LLSDMap
("command", mListener->getName())
// Include LLLeap features -- this may be important for child to
@@ -198,7 +194,7 @@ public:
return false;
}
- // Listener for events on mReplyPump: send to child stdin
+ // Listener for reply events: send to child stdin
bool wstdin(const std::string& pump, const LLSD& data)
{
LLSD packet(LLSDMap("pump", pump)("data", data));
@@ -355,7 +351,7 @@ public:
// request, send a reply. We happen to know who originated
// this request, and the reply LLEventPump of interest.
// Not our problem if the plugin ignores the reply event.
- data["reply"] = mReplyPump.getName();
+ data["reply"] = mListener->getReplyPump().getName();
sendReply(llsd::map("error",
stringize(LLError::Log::classname(err), ": ", err.what())),
data);
@@ -428,7 +424,7 @@ public:
LLSD event;
event["type"] = "error";
event["error"] = error;
- mReplyPump.post(event);
+ mListener->getReplyPump().post(event);
// All the above really accomplished was to buffer the serialized
// event in our WritePipe. Have to pump mainloop a couple times to
@@ -445,27 +441,14 @@ public:
}
private:
- /// We always want to listen on mReplyPump with wstdin(); under some
- /// circumstances we'll also echo other LLEventPumps to the plugin.
- LLBoundListener connect(LLEventPump& pump, const std::string& listener)
- {
- // Serialize any event received on the specified LLEventPump to our
- // child's stdin, suitably enriched with the pump name on which it was
- // received.
- return pump.listen(listener,
- boost::bind(&LLLeapImpl::wstdin, this, pump.getName(), _1));
- }
-
std::string mDesc;
LLEventStream mDonePump;
- LLEventStream mReplyPump;
LLProcessPtr mChild;
- LLTempBoundListener
- mStdinConnection, mStdoutConnection, mStdoutDataConnection, mStderrConnection;
- boost::scoped_ptr<LLEventPump::Blocker> mBlocker;
+ LLTempBoundListener mStdoutConnection, mStdoutDataConnection, mStderrConnection;
+ std::unique_ptr<LLEventPump::Blocker> mBlocker;
LLProcess::ReadPipe::size_type mExpect;
LLError::RecorderPtr mRecorder;
- boost::scoped_ptr<LLLeapListener> mListener;
+ std::unique_ptr<LLLeapListener> mListener;
};
// These must follow the declaration of LLLeapImpl, so they may as well be last.
diff --git a/indra/llcommon/llleaplistener.cpp b/indra/llcommon/llleaplistener.cpp
index 471f52e91c..55e4752c5d 100644
--- a/indra/llcommon/llleaplistener.cpp
+++ b/indra/llcommon/llleaplistener.cpp
@@ -54,12 +54,19 @@
return features;
}
-LLLeapListener::LLLeapListener(const ConnectFunc& connect):
+LLLeapListener::LLLeapListener(const std::string_view& caller, const Callback& callback):
// Each LEAP plugin has an instance of this listener. Make the command
// pump name difficult for other such plugins to guess.
LLEventAPI(LLUUID::generateNewID().asString(),
"Operations relating to the LLSD Event API Plugin (LEAP) protocol"),
- mConnect(connect)
+ mCaller(caller),
+ mCallback(callback),
+ // Troubling thought: what if one plugin intentionally messes with
+ // another plugin? LLEventPump names are in a single global namespace.
+ // Try to make that more difficult by generating a UUID for the reply-
+ // pump name -- so it should NOT need tweaking for uniqueness.
+ mReplyPump(LLUUID::generateNewID().asString()),
+ mReplyConn(connect(mReplyPump, mCaller))
{
LLSD need_name(LLSDMap("name", LLSD()));
add("newpump",
@@ -133,8 +140,7 @@ void LLLeapListener::newpump(const LLSD& request)
reply["name"] = name;
// Now listen on this new pump with our plugin listener
- std::string myname("llleap");
- saveListener(name, myname, mConnect(new_pump, myname));
+ saveListener(name, mCaller, connect(new_pump, mCaller));
}
catch (const LLEventPumps::BadType& error)
{
@@ -170,7 +176,7 @@ void LLLeapListener::listen(const LLSD& request)
{
// "dest" unspecified means to direct events on "source"
// to our plugin listener.
- saveListener(source_name, listener_name, mConnect(source, listener_name));
+ saveListener(source_name, listener_name, connect(source, listener_name));
}
reply["status"] = true;
}
@@ -292,10 +298,27 @@ void LLLeapListener::getFeature(const LLSD& request) const
}
}
+LLBoundListener LLLeapListener::connect(LLEventPump& pump, const std::string& listener)
+{
+ // Connect to source pump with an adapter that calls our callback with the
+ // pump name as well as the event data.
+ return pump.listen(
+ listener,
+ [callback=mCallback, pump=pump.getName()]
+ (const LLSD& data)
+ { return callback(pump, data); });
+}
+
void LLLeapListener::saveListener(const std::string& pump_name,
const std::string& listener_name,
const LLBoundListener& listener)
{
- mListeners.insert(ListenersMap::value_type(ListenersMap::key_type(pump_name, listener_name),
- listener));
+ // Don't use insert() or emplace() because, if this (pump_name,
+ // listener_name) pair is already in mListeners, we *want* to overwrite it.
+ auto& listener_entry{ mListeners[ListenersMap::key_type(pump_name, listener_name)] };
+ // If we already stored a connection for this pump and listener name,
+ // disconnect it before overwriting it. But if this entry was newly
+ // created, disconnect() will be a no-op.
+ listener_entry.disconnect();
+ listener_entry = listener;
}
diff --git a/indra/llcommon/llleaplistener.h b/indra/llcommon/llleaplistener.h
index 0ca5893657..040fb737b7 100644
--- a/indra/llcommon/llleaplistener.h
+++ b/indra/llcommon/llleaplistener.h
@@ -13,10 +13,9 @@
#define LL_LLLEAPLISTENER_H
#include "lleventapi.h"
+#include <functional>
#include <map>
#include <string>
-#include <boost/function.hpp>
-#include <boost/ptr_container/ptr_map.hpp>
/// Listener class implementing LLLeap query/control operations.
/// See https://jira.lindenlab.com/jira/browse/DEV-31978.
@@ -24,18 +23,16 @@ class LLLeapListener: public LLEventAPI
{
public:
/**
- * Decouple LLLeap by dependency injection. Certain LLLeapListener
- * operations must be able to cause LLLeap to listen on a specified
- * LLEventPump with the LLLeap listener that wraps incoming events in an
- * outer (pump=, data=) map and forwards them to the plugin. Very well,
- * define the signature for a function that will perform that, and make
- * our constructor accept such a function.
+ * Certain LLLeapListener operations listen on a specified LLEventPump.
+ * Accept a bool(pump, data) callback from our caller for when any such
+ * event is received.
*/
- typedef boost::function<LLBoundListener(LLEventPump&, const std::string& listener)>
- ConnectFunc;
- LLLeapListener(const ConnectFunc& connect);
+ using Callback = std::function<bool(const std::string& pump, const LLSD& data)>;
+ LLLeapListener(const std::string_view& caller, const Callback& callback);
~LLLeapListener();
+ LLEventPump& getReplyPump() { return mReplyPump; }
+
static LLSD getFeatures();
private:
@@ -48,10 +45,16 @@ private:
void getFeatures(const LLSD&) const;
void getFeature(const LLSD&) const;
+ LLBoundListener connect(LLEventPump& pump, const std::string& listener);
void saveListener(const std::string& pump_name, const std::string& listener_name,
const LLBoundListener& listener);
- ConnectFunc mConnect;
+ // The relative order of these next declarations is important because the
+ // constructor will initialize in this order.
+ std::string mCaller;
+ Callback mCallback;
+ LLEventStream mReplyPump;
+ LLTempBoundListener mReplyConn;
// In theory, listen() could simply call the relevant LLEventPump's
// listen() method, stoplistening() likewise. Lifespan issues make us
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<TO>(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 <typename FROM>
+class ll_convert
+{
+private:
+ const FROM& mRef;
+
+public:
+ ll_convert(const FROM& ref): mRef(ref) {}
+
+ template <typename TO>
+ inline operator TO() const
+ {
+ return ll_convert_impl<TO, FROM>()(mRef);
+ }
+};
+
+// When the TO type must be explicit, use a function template to get
+// ll_convert_to<TO>(from_value) API.
template<typename TO, typename FROM>
-TO ll_convert(const FROM& in)
+TO ll_convert_to(const FROM& in)
{
return ll_convert_impl<TO, FROM>()(in);
}
@@ -574,8 +601,8 @@ inline size_t ll_convert_length<char> (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<OUTSTR>(const char*)
-// and ll_convert<OUTSTR>(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<typename STRING>
STRING windows_message(unsigned long error)
{
- return ll_convert<STRING>(windows_message<std::wstring>(error));
+ return ll_convert(windows_message<std::wstring>(error));
}
/// There's only one real implementation
@@ -1780,7 +1807,7 @@ auto LLStringUtilBase<T>::getoptenv(const std::string& key) -> boost::optional<s
if (found)
{
// return populated boost::optional
- return { ll_convert<string_type>(*found) };
+ return { ll_convert_to<string_type>(*found) };
}
else
{
diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp
index 41bb7bac12..a5f1f582d9 100644
--- a/indra/llcommon/lua_function.cpp
+++ b/indra/llcommon/lua_function.cpp
@@ -16,15 +16,20 @@
// STL headers
// std headers
#include <algorithm>
+#include <iomanip> // std::quoted
#include <map>
#include <memory> // std::unique_ptr
// external library headers
// other Linden headers
#include "hexdump.h"
+#include "lleventcoro.h"
#include "llsd.h"
#include "llsdutil.h"
#include "lualistener.h"
+/*****************************************************************************
+* luau namespace
+*****************************************************************************/
namespace
{
// can't specify free function free() as a unique_ptr deleter
@@ -56,6 +61,9 @@ int lluau::loadstring(lua_State *L, const std::string &desc, const std::string &
return luau_load(L, desc.data(), bytecode.get(), bytecodeSize, 0);
}
+/*****************************************************************************
+* Lua <=> C++ conversions
+*****************************************************************************/
std::string lua_tostdstring(lua_State* L, int index)
{
size_t len;
@@ -88,7 +96,6 @@ LLSD lua_tollsd(lua_State* L, int index)
{
LL_DEBUGS("Lua") << "lua_tollsd(" << index << ") of " << lua_gettop(L) << " stack entries: "
<< lua_what(L, index) << LL_ENDL;
- DebugExit log_exit("lua_tollsd()");
switch (lua_type(L, index))
{
case LUA_TNONE:
@@ -417,6 +424,9 @@ void lua_pushllsd(lua_State* L, const LLSD& data)
}
}
+/*****************************************************************************
+* LuaState class
+*****************************************************************************/
LuaState::LuaState(script_finished_fn cb):
mCallback(cb),
mState(nullptr)
@@ -439,25 +449,15 @@ void LuaState::initLuaState()
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);
@@ -512,7 +512,48 @@ 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
+*****************************************************************************/
LuaPopper::~LuaPopper()
{
if (mCount)
@@ -521,15 +562,21 @@ LuaPopper::~LuaPopper()
}
}
+/*****************************************************************************
+* LuaFunction class
+*****************************************************************************/
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);
@@ -540,19 +587,148 @@ 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::Registry&, LuaFunction::Lookup&> 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("<unknown ", lua_typename(L, lua_type(L, idx)), ">") };
+ 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
+}
+
+/*****************************************************************************
+* 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)
{
switch (lua_type(self.L, self.index))
@@ -608,6 +784,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: [";
@@ -619,8 +798,3 @@ std::ostream& operator<<(std::ostream& out, const lua_stack& self)
out << ']';
return out;
}
-
-DebugExit::~DebugExit()
-{
- LL_DEBUGS("Lua") << "exit " << mName << LL_ENDL;
-}
diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h
index 0605eb12a6..08a2353d29 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
@@ -86,6 +89,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;
@@ -134,9 +148,13 @@ public:
static lua_CFunction get(const std::string& key);
-private:
+protected:
using Registry = std::map<std::string, std::pair<lua_CFunction, std::string>>;
- static Registry& getRegistry();
+ using Lookup = std::map<lua_CFunction, std::string>;
+ static std::pair<const Registry&, const Lookup&> getRState() { return getState(); }
+
+private:
+ static std::pair<Registry&, Lookup&> getState();
};
/**
@@ -198,16 +216,4 @@ private:
lua_State* L;
};
-// log exit from any block declaring an instance of DebugExit, regardless of
-// how control leaves that block
-struct DebugExit
-{
- DebugExit(const std::string& name): mName(name) {}
- DebugExit(const DebugExit&) = delete;
- DebugExit& operator=(const DebugExit&) = delete;
- ~DebugExit();
-
- std::string mName;
-};
-
#endif /* ! defined(LL_LUA_FUNCTION_H) */
diff --git a/indra/llcommon/lualistener.cpp b/indra/llcommon/lualistener.cpp
index 0fa03ffb3b..ed34133924 100644
--- a/indra/llcommon/lualistener.cpp
+++ b/indra/llcommon/lualistener.cpp
@@ -24,20 +24,23 @@
#include "llleaplistener.h"
#include "lua_function.h"
-LuaListener::LuaListener(lua_State* L):
- super(getUniqueKey()),
- mListener(
- new LLLeapListener(
- [L](LLEventPump& pump, const std::string& listener)
- { return connect(L, pump, listener); }))
+const int MAX_QSIZE = 1000;
+
+std::ostream& operator<<(std::ostream& out, const LuaListener& self)
{
- mReplyConnection = connect(L, mReplyPump, "LuaListener");
+ return out << "LuaListener(" << self.getReplyName() << ", " << self.getCommandName() << ")";
}
+LuaListener::LuaListener(lua_State* L):
+ super(getUniqueKey()),
+ mListener(new LLLeapListener(
+ "LuaListener",
+ [this](const std::string& pump, const LLSD& data)
+ { return queueEvent(pump, data); }))
+{}
+
LuaListener::~LuaListener()
-{
- LL_DEBUGS("Lua") << "~LuaListener('" << mReplyPump.getName() << "')" << LL_ENDL;
-}
+{}
int LuaListener::getUniqueKey()
{
@@ -54,50 +57,35 @@ int LuaListener::getUniqueKey()
return key;
}
-LLBoundListener LuaListener::connect(lua_State* L, LLEventPump& pump, const std::string& listener)
+std::string LuaListener::getReplyName() const
{
- return pump.listen(
- listener,
- [L, pumpname=pump.getName()](const LLSD& data)
- { return call_lua(L, pumpname, data); });
+ return mListener->getReplyPump().getName();
}
-bool LuaListener::call_lua(lua_State* L, const std::string& pump, const LLSD& data)
+std::string LuaListener::getCommandName() const
{
- LL_INFOS("Lua") << "LuaListener::call_lua('" << pump << "', " << data << ")" << LL_ENDL;
- if (! lua_checkstack(L, 3))
- {
- LL_WARNS("Lua") << "Cannot extend Lua stack to call listen_events() callback"
- << LL_ENDL;
- return false;
- }
- // push the registered Lua callback function stored in our registry as
- // "event.function"
- lua_getfield(L, LUA_REGISTRYINDEX, "event.function");
- llassert(lua_isfunction(L, -1));
- // pass pump name
- lua_pushstdstring(L, pump);
- // then the data blob
- lua_pushllsd(L, data);
- // call the registered Lua listener function; allow it to return bool;
- // no message handler
- auto status = lua_pcall(L, 2, 1, 0);
- bool result{ false };
- if (status != LUA_OK)
+ return mListener->getPumpName();
+}
+
+bool LuaListener::queueEvent(const std::string& pump, const LLSD& data)
+{
+ // Our Lua script might be stalled, or just fail to retrieve events. Don't
+ // grow this queue indefinitely. But don't set MAX_QSIZE as the queue
+ // capacity or we'd block the post() call trying to propagate this event!
+ if (auto size = mQueue.size(); size > MAX_QSIZE)
{
- LL_WARNS("Lua") << "Error in listen_events() callback: "
- << lua_tostdstring(L, -1) << LL_ENDL;
+ LL_WARNS("Lua") << "LuaListener queue for " << getReplyName()
+ << " exceeds " << MAX_QSIZE << ": " << size
+ << " -- discarding event" << LL_ENDL;
}
else
{
- result = lua_toboolean(L, -1);
+ mQueue.push(decltype(mQueue)::value_type(pump, data));
}
- // discard either the error message or the bool return value
- lua_pop(L, 1);
- return result;
+ return false;
}
-std::string LuaListener::getCommandName() const
+LuaListener::PumpData LuaListener::getNext()
{
- return mListener->getPumpName();
+ return mQueue.pop();
}
diff --git a/indra/llcommon/lualistener.h b/indra/llcommon/lualistener.h
index df88d55dd5..c13b7bbd5f 100644
--- a/indra/llcommon/lualistener.h
+++ b/indra/llcommon/lualistener.h
@@ -12,14 +12,13 @@
#if ! defined(LL_LUALISTENER_H)
#define LL_LUALISTENER_H
-#include "llevents.h"
#include "llinstancetracker.h"
-#include "lluuid.h"
+#include "llsd.h"
+#include "llthreadsafequeue.h"
+#include <iosfwd> // std::ostream
#include <memory> // std::unique_ptr
-
-#ifdef LL_TEST
-#include "lleventfilter.h"
-#endif
+#include <string>
+#include <utility> // std::pair
struct lua_State;
class LLLeapListener;
@@ -31,7 +30,6 @@ class LLLeapListener;
* inconvenience malicious Lua scripts wanting to mess with others. The idea
* is that a given lua_State stores in its Registry:
* - "event.listener": the int key of the corresponding LuaListener, if any
- * - "event.function": the Lua function to be called with incoming events
* The original thought was that LuaListener would itself store the Lua
* function -- but surprisingly, there is no C/C++ type in the API that stores
* a Lua function.
@@ -55,22 +53,25 @@ public:
~LuaListener();
- std::string getReplyName() const { return mReplyPump.getName(); }
+ std::string getReplyName() const;
std::string getCommandName() const;
+ /**
+ * LuaListener enqueues reply events from its LLLeapListener on mQueue.
+ * Call getNext() to retrieve the next such event. Blocks the calling
+ * coroutine if the queue is empty.
+ */
+ using PumpData = std::pair<std::string, LLSD>;
+ PumpData getNext();
+
+ friend std::ostream& operator<<(std::ostream& out, const LuaListener& self);
+
private:
static int getUniqueKey();
+ bool queueEvent(const std::string& pump, const LLSD& data);
- static LLBoundListener connect(lua_State* L, LLEventPump& pump, const std::string& listener);
-
- static bool call_lua(lua_State* L, const std::string& pump, const LLSD& data);
+ LLThreadSafeQueue<PumpData> mQueue;
-#ifndef LL_TEST
- LLEventStream mReplyPump{ LLUUID::generateNewID().asString() };
-#else
- LLEventLogProxyFor<LLEventStream> mReplyPump{ "luapump", false };
-#endif
- LLTempBoundListener mReplyConnection;
std::unique_ptr<LLLeapListener> mListener;
};
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>(T) already handles the trivial case
+// note that ll_convert_to<T>(T) already handles the trivial case
template <typename OUTCHAR, typename INCHAR>
struct gstringize_impl<OUTCHAR, std::basic_string<INCHAR>>
{
auto operator()(const std::basic_string<INCHAR>& arg)
{
- return ll_convert<std::basic_string<OUTCHAR>>(arg);
+ return ll_convert_to<std::basic_string<OUTCHAR>>(arg);
}
};
@@ -105,7 +105,7 @@ struct gstringize_impl<OUTCHAR, INCHAR*>
{
auto operator()(const INCHAR* arg)
{
- return ll_convert<std::basic_string<OUTCHAR>>(arg);
+ return ll_convert_to<std::basic_string<OUTCHAR>>(arg);
}
};
diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp
index 032923a108..c7a958da49 100644
--- a/indra/llcommon/tests/lleventcoro_test.cpp
+++ b/indra/llcommon/tests/lleventcoro_test.cpp
@@ -113,7 +113,7 @@ namespace tut
void test_data::explicit_wait(boost::shared_ptr<LLCoros::Promise<std::string>>& cbp)
{
- BEGIN
+ DEBUGIN
{
mSync.bump();
// The point of this test is to verify / illustrate suspending a
@@ -136,7 +136,7 @@ namespace tut
mSync.bump();
ensure_equals("Got it", stringdata, "received");
}
- END
+ DEBUGEND
}
template<> template<>
@@ -163,13 +163,13 @@ namespace tut
void test_data::waitForEventOn1()
{
- BEGIN
+ DEBUGIN
{
mSync.bump();
result = suspendUntilEventOn("source");
mSync.bump();
}
- END
+ DEBUGEND
}
template<> template<>
@@ -189,7 +189,7 @@ namespace tut
void test_data::coroPump()
{
- BEGIN
+ DEBUGIN
{
mSync.bump();
LLCoroEventPump waiter;
@@ -197,7 +197,7 @@ namespace tut
result = waiter.suspend();
mSync.bump();
}
- END
+ DEBUGEND
}
template<> template<>
@@ -217,7 +217,7 @@ namespace tut
void test_data::postAndWait1()
{
- BEGIN
+ DEBUGIN
{
mSync.bump();
result = postAndSuspend(LLSDMap("value", 17), // request event
@@ -226,7 +226,7 @@ namespace tut
"reply"); // request["reply"] = name
mSync.bump();
}
- END
+ DEBUGEND
}
template<> template<>
@@ -240,7 +240,7 @@ namespace tut
void test_data::coroPumpPost()
{
- BEGIN
+ DEBUGIN
{
mSync.bump();
LLCoroEventPump waiter;
@@ -248,7 +248,7 @@ namespace tut
immediateAPI.getPump(), "reply");
mSync.bump();
}
- END
+ DEBUGEND
}
template<> template<>
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 3d2a426913..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(LLWString & clean_string)
+template <>
+void LLTextEditor::pasteTextWithLinebreaks<LLWString>(const LLWString & clean_string)
{
std::basic_string<llwchar>::size_type start = 0;
std::basic_string<llwchar>::size_type pos = clean_string.find('\n',start);
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index ec2b9a4b7d..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"
@@ -294,10 +295,19 @@ private:
//
// Methods
//
- void pasteHelper(bool is_primary);
+ void pasteHelper(bool is_primary);
void cleanStringForPaste(LLWString & clean_string);
- void pasteTextWithLinebreaks(LLWString & clean_string);
+public:
+ template <typename STRINGTYPE>
+ void pasteTextWithLinebreaks(const STRINGTYPE& clean_string)
+ {
+ pasteTextWithLinebreaks<LLWString>(ll_convert(clean_string));
+ }
+ template <>
+ void pasteTextWithLinebreaks<LLWString>(const LLWString & clean_string);
+
+private:
void onKeyStroke();
// Concrete TextCmd sub-classes used by the LLTextEditor base class
diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp
index 2d9dc89340..b63600e0e6 100644
--- a/indra/newview/llfloaterluadebug.cpp
+++ b/indra/newview/llfloaterluadebug.cpp
@@ -58,7 +58,7 @@ BOOL LLFloaterLUADebug::postBuild()
.listen("LLFloaterLUADebug",
[mResultOutput=mResultOutput](const LLSD& data)
{
- mResultOutput->insertText(data.asString());
+ mResultOutput->pasteTextWithLinebreaks(data.asString());
mResultOutput->addLineBreakChar(true);
return false;
});
@@ -125,13 +125,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
@@ -139,7 +139,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 = ", ";
}
}
diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp
index 4463fe65f5..0c39f6c8df 100644
--- a/indra/newview/llluamanager.cpp
+++ b/indra/newview/llluamanager.cpp
@@ -167,82 +167,34 @@ lua_function(post_on, "post_on(pumpname, data): post specified data to specified
return 0;
}
-lua_function(listen_events,
- "listen_events(callback): call callback(pumpname, data) with events received\n"
- "on this Lua chunk's replypump.\n"
- "Returns replypump, commandpump: names of LLEventPumps specific to this chunk.")
+lua_function(get_event_pumps,
+ "get_event_pumps():\n"
+ "Returns replypump, commandpump: names of LLEventPumps specific to this chunk.\n"
+ "Events posted to replypump are queued for get_event_next().\n"
+ "post_on(commandpump, ...) to engage LLEventAPI operations (see helpleap()).")
{
- if (! lua_isfunction(L, 1))
- {
- luaL_typeerror(L, 1, "function");
- return 0;
- }
luaL_checkstack(L, 2, nullptr);
-
-/*==========================================================================*|
- // Get the lua_State* for the main thread of this state, in case we were
- // called from a coroutine thread. We're going to make callbacks into Lua
- // code, and we want to do it on the main thread rather than a (possibly
- // suspended) coroutine thread.
- // Registry table is at pseudo-index LUA_REGISTRYINDEX
- // Main thread is at registry key LUA_RIDX_MAINTHREAD
- auto regtype {lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD)};
- // Not finding the main thread at the documented place isn't a user error,
- // it's a Problem
- llassert_always(regtype == LUA_TTHREAD);
- lua_State* mainthread{ lua_tothread(L, -1) };
- // pop the main thread
- lua_pop(L, 1);
-|*==========================================================================*/
- // Luau is based on Lua 5.1, and Lua 5.1 apparently provides no way to get
- // 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");
- }
-
- // 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
- // stack, but we want to store it on the mainthread registry.
- if (L != mainthread)
- {
- // push 1 value (the Lua function) from L's stack to mainthread's
- lua_xmove(L, mainthread, 1);
- }
- lua_setfield(mainthread, LUA_REGISTRYINDEX, "event.function");
-
+ auto listener{ LuaState::obtainListener(L) };
// return the reply pump name and the command pump name on caller's lua_State
lua_pushstdstring(L, listener->getReplyName());
lua_pushstdstring(L, listener->getCommandName());
return 2;
}
+lua_function(get_event_next,
+ "get_event_next():\n"
+ "Returns the next (pumpname, data) pair from the replypump whose name\n"
+ "is returned by get_event_pumps(). Blocks the calling chunk until an\n"
+ "event becomes available.")
+{
+ luaL_checkstack(L, 2, nullptr);
+ auto listener{ LuaState::obtainListener(L) };
+ const auto& [pump, data]{ listener->getNext() };
+ lua_pushstdstring(L, pump);
+ lua_pushllsd(L, data);
+ 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")
@@ -370,7 +322,7 @@ void LLLUAmanager::runScriptLine(LuaState& L, const std::string& chunk, script_r
if (shortchunk.length() > shortlen)
shortchunk = stringize(shortchunk.substr(0, shortlen), "...");
- std::string desc{ stringize("runScriptLine('", shortchunk, "')") };
+ std::string desc{ stringize("lua: ", shortchunk) };
LLCoros::instance().launch(desc, [&L, desc, chunk, cb]()
{
auto [count, result] = L.expr(desc, chunk);
diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp
index 1f25fd5f3b..81ec186cf4 100644
--- a/indra/newview/tests/llluamanager_test.cpp
+++ b/indra/newview/tests/llluamanager_test.cpp
@@ -108,7 +108,6 @@ namespace tut
replypump.listen("llluamanager_test",
listener([&fromlua](const LLSD& data){ fromlua = data; })));
const std::string lua(stringize(
- "-- test LLSD synthesized by Lua\n",
"data = ", construct, "\n"
"post_on('testpump', data)\n"
));
@@ -134,7 +133,7 @@ namespace tut
template<> template<>
void object::test<3>()
{
- set_test_name("test post_on(), listen_events(), await_event()");
+ set_test_name("test post_on(), get_event_pumps(), get_event_next()");
StringVec posts;
LLEventStream replypump("testpump");
LLTempBoundListener conn(
@@ -142,17 +141,14 @@ namespace tut
listener([&posts](const LLSD& data)
{ posts.push_back(data.asString()); })));
const std::string lua(
- "-- test post_on,listen_events,await_event\n"
+ "-- test post_on,get_event_pumps,get_event_next\n"
"post_on('testpump', 'entry')\n"
- "callback = function(pump, data)\n"
- " -- just echo the data we received\n"
- " post_on('testpump', data)\n"
- "end\n"
- "post_on('testpump', 'listen_events()')\n"
- "replypump, cmdpump = listen_events(callback)\n"
+ "post_on('testpump', 'get_event_pumps()')\n"
+ "replypump, cmdpump = get_event_pumps()\n"
"post_on('testpump', replypump)\n"
- "post_on('testpump', 'await_event()')\n"
- "await_event(replypump)\n"
+ "post_on('testpump', 'get_event_next()')\n"
+ "pump, data = get_event_next()\n"
+ "post_on('testpump', data)\n"
"post_on('testpump', 'exit')\n"
);
LuaState L;
@@ -162,9 +158,9 @@ namespace tut
auto future = LLLUAmanager::startScriptLine(L, lua);
StringVec expected{
"entry",
- "listen_events()",
+ "get_event_pumps()",
"",
- "await_event()",
+ "get_event_next()",
"message",
"exit"
};
@@ -188,18 +184,15 @@ namespace tut
listener([&reply](const LLSD& post){ reply = post; })));
const std::string lua(
"-- test LLSD round trip\n"
- "callback = function(pump, data)\n"
- " -- just echo the data we received\n"
- " post_on('testpump', data)\n"
- "end\n"
- "replypump, cmdpump = listen_events(callback)\n"
+ "replypump, cmdpump = get_event_pumps()\n"
"post_on('testpump', replypump)\n"
- "await_event(replypump)\n"
+ "pump, data = get_event_next()\n"
+ "return data\n"
);
LuaState L;
auto future = LLLUAmanager::startScriptLine(L, lua);
// We woke up again ourselves because the coroutine running Lua has
- // reached the await_event() call, which suspends the calling C++
+ // reached the get_event_next() call, which suspends the calling C++
// coroutine (including the Lua code running on it) until we post
// something to that reply pump.
auto luapump{ reply.asString() };
@@ -208,7 +201,7 @@ namespace tut
// The C++ coroutine running the Lua script is now ready to run. Run
// it so it will echo the LLSD back to us.
auto [count, result] = future.get();
- ensure_equals(desc, reply, expect);
+ ensure_equals(desc, result, expect);
}
// Define an RTItem to be used for round-trip LLSD testing: what it is,
diff --git a/indra/test/debug.h b/indra/test/debug.h
index 76dbb973b2..f92cce3008 100644
--- a/indra/test/debug.h
+++ b/indra/test/debug.h
@@ -30,6 +30,7 @@
#define LL_DEBUG_H
#include "print.h"
+#include "stringize.h"
/*****************************************************************************
* Debugging stuff
@@ -52,8 +53,9 @@
class Debug
{
public:
- Debug(const std::string& block):
- mBlock(block),
+ template <typename... ARGS>
+ Debug(ARGS&&... args):
+ mBlock(stringize(std::forward<ARGS>(args)...)),
mLOGTEST(getenv("LOGTEST")),
// debug output enabled when LOGTEST is set AND non-empty
mEnabled(mLOGTEST && *mLOGTEST)
@@ -88,15 +90,15 @@ private:
// of the Debug block.
#define DEBUG Debug debug(LL_PRETTY_FUNCTION)
-// These BEGIN/END 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 BEGIN \
+// 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 END \
+#define DEBUGEND \
catch (...) \
{ \
debug("*** exceptional "); \
diff --git a/indra/test/lltut.h b/indra/test/lltut.h
index 9835565bb6..4ce450057c 100644
--- a/indra/test/lltut.h
+++ b/indra/test/lltut.h
@@ -31,6 +31,8 @@
#include "is_approx_equal_fraction.h" // instead of llmath.h
#include <cstring>
+#include <string>
+#include <vector>
class LLDate;
class LLSD;
diff --git a/indra/test/print.h b/indra/test/print.h
index 08e36caddf..749603907e 100644
--- a/indra/test/print.h
+++ b/indra/test/print.h
@@ -23,7 +23,9 @@ struct NONL_t {};
inline
void print()
{
+#ifdef LL_TEST
std::cerr << std::endl;
+#endif
}
// print(NONL) is a no-op
@@ -35,8 +37,10 @@ void print(NONL_t)
template <typename T, typename... ARGS>
void print(T&& first, ARGS&&... rest)
{
+#ifdef LL_TEST
std::cerr << first;
print(std::forward<ARGS>(rest)...);
+#endif
}
#endif /* ! defined(LL_PRINT_H) */