summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llcommon/lua_function.cpp70
-rw-r--r--indra/llcommon/lua_function.h2
-rw-r--r--indra/newview/llfloaterluadebug.cpp13
-rw-r--r--indra/newview/llfloaterluadebug.h2
-rw-r--r--indra/newview/llluamanager.cpp48
-rw-r--r--indra/newview/llluamanager.h20
-rw-r--r--indra/newview/scripts/lua/require/fiber.lua6
-rw-r--r--indra/newview/skins/default/xui/en/floater_lua_debug.xml9
-rw-r--r--indra/newview/tests/llluamanager_test.cpp40
9 files changed, 59 insertions, 151 deletions
diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp
index 2d08de68c5..42bba80ed5 100644
--- a/indra/llcommon/lua_function.cpp
+++ b/indra/llcommon/lua_function.cpp
@@ -478,19 +478,11 @@ void lua_pushllsd(lua_State* L, const LLSD& data)
*****************************************************************************/
LuaState::LuaState(script_finished_fn cb):
mCallback(cb),
- mState(nullptr)
+ mState(luaL_newstate())
{
- initLuaState();
-}
-
-void LuaState::initLuaState()
-{
- if (mState)
- {
- lua_close(mState);
- }
- mState = luaL_newstate();
luaL_openlibs(mState);
+ // publish to this new lua_State all the LL entry points we defined using
+ // the lua_function() macro
LuaFunction::init(mState);
// Try to make print() write to our log.
lua_register(mState, "print", LuaFunction::get("print_info"));
@@ -607,8 +599,7 @@ std::pair<int, LLSD> LuaState::expr(const std::string& desc, const std::string&
// we instead of the Lua runtime catch it, our lua_State retains
// its internal error status. Any subsequent lua_pcall() calls
// with this lua_State will report error regardless of whether the
- // chunk runs successfully. Get a new lua_State().
- initLuaState();
+ // chunk runs successfully.
return { -1, stringize(LLError::Log::classname(error), ": ", error.what()) };
}
}
@@ -628,65 +619,12 @@ std::pair<int, LLSD> LuaState::expr(const std::string& desc, const std::string&
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()) };
}
}
}
// pop everything
lua_settop(mState, 0);
-
- // If we ran a script that loaded the fiber module, finish up with a call
- // to fiber.run(). That allows a script to kick off some number of fibers,
- // do some work on the main thread and then fall off the end of the script
- // without explicitly appending a call to fiber.run(). run() ensures the
- // rest of the fibers run to completion (or error).
- luaL_checkstack(mState, 4, nullptr);
- // Push _MODULES table on stack
- luaL_findtable(mState, LUA_REGISTRYINDEX, "_MODULES", 1);
- int index = lua_gettop(mState);
- bool found = false;
- // Did this chunk already require('fiber')? To find out, we must search
- // the _MODULES table, because our require() implementation uses the
- // pathname of the module file as the key. Push nil key to start.
- lua_pushnil(mState);
- while (lua_next(mState, index) != 0)
- {
- // key is at index -2, value at index -1
- // "While traversing a table, do not call lua_tolstring directly on a
- // key, unless you know that the key is actually a string. Recall that
- // lua_tolstring changes the value at the given index; this confuses
- // the next call to lua_next."
- // https://www.lua.org/manual/5.1/manual.html#lua_next
- if (lua_type(mState, -2) == LUA_TSTRING &&
- fsyspath(lua_tostdstring(mState, -2)).stem() == "fiber")
- {
- found = true;
- break;
- }
- // pop value so key is at top for lua_next()
- lua_pop(mState, 1);
- }
- if (found)
- {
- // okay, index -1 is a table loaded from a file 'fiber.xxx' --
- // does it have a function named 'run'?
- auto run_type{ lua_getfield(mState, -1, "run") };
- if (run_type == LUA_TFUNCTION)
- {
- // there's a fiber.run() function sitting on the top of the stack
- // -- call it with no arguments, discarding anything it returns
- 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
- lua_settop(mState, 0);
return result;
}
diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h
index b3b1f40ae5..7f7a7566f3 100644
--- a/indra/llcommon/lua_function.h
+++ b/indra/llcommon/lua_function.h
@@ -87,8 +87,6 @@ public:
~LuaState();
- void initLuaState();
-
bool checkLua(const std::string& desc, int r);
// expr() is for when we want to capture any results left on the stack
diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp
index f715327ec8..9981126e4f 100644
--- a/indra/newview/llfloaterluadebug.cpp
+++ b/indra/newview/llfloaterluadebug.cpp
@@ -27,7 +27,6 @@
#include "llfloaterluadebug.h"
-#include "llcheckboxctrl.h"
#include "lllineeditor.h"
#include "lltexteditor.h"
#include "llviewermenufile.h" // LLFilePickerReplyThread
@@ -92,8 +91,7 @@ void LLFloaterLUADebug::onExecuteClicked()
mResultOutput->setValue("");
std::string cmd = mLineInput->getText();
- cleanLuaState();
- LLLUAmanager::runScriptLine(mState, cmd, [this](int count, const LLSD& result)
+ LLLUAmanager::runScriptLine(cmd, [this](int count, const LLSD& result)
{
completion(count, result);
});
@@ -174,12 +172,3 @@ void LLFloaterLUADebug::completion(int count, const LLSD& result)
sep = ", ";
}
}
-
-void LLFloaterLUADebug::cleanLuaState()
-{
- if(getChild<LLCheckBoxCtrl>("clean_lua_state")->get())
- {
- //Reinit to clean lua_State
- mState.initLuaState();
- }
-}
diff --git a/indra/newview/llfloaterluadebug.h b/indra/newview/llfloaterluadebug.h
index ae30b7cf25..e513d9a095 100644
--- a/indra/newview/llfloaterluadebug.h
+++ b/indra/newview/llfloaterluadebug.h
@@ -58,14 +58,12 @@ class LLFloaterLUADebug :
private:
void completion(int count, const LLSD& result);
- void cleanLuaState();
LLTempBoundListener mOutConnection;
LLTextEditor* mResultOutput;
LLLineEditor* mLineInput;
LLLineEditor* mScriptPath;
- LuaState mState;
U32 mAck{ 0 };
bool mExecuting{ false };
};
diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp
index ccfa08078e..7014c59e4e 100644
--- a/indra/newview/llluamanager.cpp
+++ b/indra/newview/llluamanager.cpp
@@ -162,24 +162,25 @@ lua_function(get_event_next,
return 2;
}
-LLCoros::Future<std::pair<int, LLSD>>
+LLCoros::Future<LLLUAmanager::script_result>
LLLUAmanager::startScriptFile(const std::string& filename)
{
// Despite returning from startScriptFile(), we need this Promise to
// remain alive until the callback has fired.
- auto promise{ std::make_shared<LLCoros::Promise<std::pair<int, LLSD>>>() };
+ auto promise{ std::make_shared<LLCoros::Promise<script_result>>() };
runScriptFile(filename,
[promise](int count, LLSD result)
{ promise->set_value({ count, result }); });
return LLCoros::getFuture(*promise);
}
-std::pair<int, LLSD> LLLUAmanager::waitScriptFile(const std::string& filename)
+LLLUAmanager::script_result LLLUAmanager::waitScriptFile(const std::string& filename)
{
return startScriptFile(filename).get();
}
-void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn result_cb, script_finished_fn finished_cb)
+void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn result_cb,
+ script_finished_fn finished_cb)
{
// A script_result_fn will be called when LuaState::expr() completes.
LLCoros::instance().launch(filename, [filename, result_cb, finished_cb]()
@@ -212,39 +213,25 @@ void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn r
});
}
-void LLLUAmanager::runScriptLine(const std::string& chunk, script_finished_fn cb)
-{
- // A script_finished_fn is used to initialize the LuaState.
- // It will be called when the LuaState is destroyed.
- LuaState L(cb);
- runScriptLine(L, chunk);
-}
-
-void LLLUAmanager::runScriptLine(const std::string& chunk, script_result_fn cb)
-{
- LuaState L;
- // A script_result_fn will be called when LuaState::expr() completes.
- runScriptLine(L, chunk, cb);
-}
-
-LLCoros::Future<std::pair<int, LLSD>>
-LLLUAmanager::startScriptLine(LuaState& L, const std::string& chunk)
+LLCoros::Future<LLLUAmanager::script_result>
+LLLUAmanager::startScriptLine(const std::string& chunk)
{
// Despite returning from startScriptLine(), we need this Promise to
// remain alive until the callback has fired.
- auto promise{ std::make_shared<LLCoros::Promise<std::pair<int, LLSD>>>() };
- runScriptLine(L, chunk,
+ auto promise{ std::make_shared<LLCoros::Promise<script_result>>() };
+ runScriptLine(chunk,
[promise](int count, LLSD result)
{ promise->set_value({ count, result }); });
return LLCoros::getFuture(*promise);
}
-std::pair<int, LLSD> LLLUAmanager::waitScriptLine(LuaState& L, const std::string& chunk)
+LLLUAmanager::script_result LLLUAmanager::waitScriptLine(const std::string& chunk)
{
- return startScriptLine(L, chunk).get();
+ return startScriptLine(chunk).get();
}
-void LLLUAmanager::runScriptLine(LuaState& L, const std::string& chunk, script_result_fn cb)
+void LLLUAmanager::runScriptLine(const std::string& chunk, script_result_fn result_cb,
+ script_finished_fn finished_cb)
{
// find a suitable abbreviation for the chunk string
std::string shortchunk{ chunk };
@@ -256,12 +243,15 @@ void LLLUAmanager::runScriptLine(LuaState& L, const std::string& chunk, script_r
shortchunk = stringize(shortchunk.substr(0, shortlen), "...");
std::string desc{ "lua: " + shortchunk };
- LLCoros::instance().launch(desc, [&L, desc, chunk, cb]()
+ LLCoros::instance().launch(desc, [desc, chunk, result_cb, finished_cb]()
{
+ // A script_finished_fn is used to initialize the LuaState.
+ // It will be called when the LuaState is destroyed.
+ LuaState L(finished_cb);
auto [count, result] = L.expr(desc, chunk);
- if (cb)
+ if (result_cb)
{
- cb(count, result);
+ result_cb(count, result);
}
});
}
diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h
index dcbb91f799..50f922a80f 100644
--- a/indra/newview/llluamanager.h
+++ b/indra/newview/llluamanager.h
@@ -55,32 +55,32 @@ public:
// count > 1 with result.isArray() means the script returned multiple
// results, represented as the entries of the result array.
typedef std::function<void(int count, const LLSD& result)> script_result_fn;
+ // same semantics as script_result_fn parameters
+ typedef std::pair<int, LLSD> script_result;
- static void runScriptFile(const std::string &filename, script_result_fn result_cb = {}, script_finished_fn finished_cb = {});
+ static void runScriptFile(const std::string &filename, script_result_fn result_cb = {},
+ script_finished_fn finished_cb = {});
// Start running a Lua script file, returning an LLCoros::Future whose
// get() method will pause the calling coroutine until it can deliver the
// (count, result) pair described above. Between startScriptFile() and
// Future::get(), the caller and the Lua script coroutine will run
// concurrently.
- static LLCoros::Future<std::pair<int, LLSD>>
- startScriptFile(const std::string& filename);
+ static LLCoros::Future<script_result> startScriptFile(const std::string& filename);
// Run a Lua script file, and pause the calling coroutine until it completes.
// The return value is the (count, result) pair described above.
- static std::pair<int, LLSD> waitScriptFile(const std::string& filename);
+ static script_result waitScriptFile(const std::string& filename);
- static void runScriptLine(const std::string &chunk, script_finished_fn cb = {});
- static void runScriptLine(const std::string &chunk, script_result_fn cb);
- static void runScriptLine(LuaState& L, const std::string &chunk, script_result_fn cb = {});
+ static void runScriptLine(const std::string &chunk, script_result_fn result_cb = {},
+ script_finished_fn finished_cb = {});
// Start running a Lua chunk, returning an LLCoros::Future whose
// get() method will pause the calling coroutine until it can deliver the
// (count, result) pair described above. Between startScriptLine() and
// Future::get(), the caller and the Lua script coroutine will run
// concurrently.
- static LLCoros::Future<std::pair<int, LLSD>>
- startScriptLine(LuaState& L, const std::string& chunk);
+ static LLCoros::Future<script_result> startScriptLine(const std::string& chunk);
// Run a Lua chunk, and pause the calling coroutine until it completes.
// The return value is the (count, result) pair described above.
- static std::pair<int, LLSD> waitScriptLine(LuaState& L, const std::string& chunk);
+ static script_result waitScriptLine(const std::string& chunk);
static const std::map<std::string, std::string> getScriptNames() { return sScriptNames; }
diff --git a/indra/newview/scripts/lua/require/fiber.lua b/indra/newview/scripts/lua/require/fiber.lua
index cae27b936b..b3c684dd67 100644
--- a/indra/newview/scripts/lua/require/fiber.lua
+++ b/indra/newview/scripts/lua/require/fiber.lua
@@ -337,4 +337,10 @@ function fiber.run()
return idle_done
end
+-- Make sure we finish up with a call to run(). That allows a consuming script
+-- to kick off some number of fibers, do some work on the main thread and then
+-- fall off the end of the script without explicitly calling fiber.run().
+-- run() ensures the rest of the fibers run to completion (or error).
+LL.atexit(fiber.run)
+
return fiber
diff --git a/indra/newview/skins/default/xui/en/floater_lua_debug.xml b/indra/newview/skins/default/xui/en/floater_lua_debug.xml
index 012ea6f254..15027f1647 100644
--- a/indra/newview/skins/default/xui/en/floater_lua_debug.xml
+++ b/indra/newview/skins/default/xui/en/floater_lua_debug.xml
@@ -25,15 +25,6 @@
width="100">
LUA string:
</text>
- <check_box
- follows="right|top"
- height="15"
- label="Use clean lua_State"
- layout="topleft"
- top="10"
- right ="-70"
- name="clean_lua_state"
- width="70"/>
<line_editor
border_style="line"
border_thickness="1"
diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp
index 824ddd445b..26a4ac95e3 100644
--- a/indra/newview/tests/llluamanager_test.cpp
+++ b/indra/newview/tests/llluamanager_test.cpp
@@ -123,11 +123,10 @@ namespace tut
void object::test<1>()
{
set_test_name("test Lua results");
- LuaState L;
for (auto& luax : lua_expressions)
{
auto [count, result] =
- LLLUAmanager::waitScriptLine(L, "return " + luax.expr);
+ LLLUAmanager::waitScriptLine("return " + luax.expr);
auto desc{ stringize("waitScriptLine(", luax.desc, "): ") };
// if count < 0, report Lua error message
ensure_equals(desc + result.asString(), count, 1);
@@ -144,8 +143,7 @@ namespace tut
"data = ", construct, "\n"
"LL.post_on('testpump', data)\n"
));
- LuaState L;
- auto [count, result] = LLLUAmanager::waitScriptLine(L, lua);
+ auto [count, result] = LLLUAmanager::waitScriptLine(lua);
// We woke up again ourselves because the coroutine running Lua has
// finished. But our Lua chunk didn't actually return anything, so we
// expect count to be 0 and result to be undefined.
@@ -182,11 +180,10 @@ namespace tut
"LL.post_on('testpump', data)\n"
"LL.post_on('testpump', 'exit')\n"
);
- LuaState L;
// It's important to let the startScriptLine() coroutine run
// concurrently with ours until we've had a chance to post() our
// reply.
- auto future = LLLUAmanager::startScriptLine(L, lua);
+ auto future = LLLUAmanager::startScriptLine(lua);
StringVec expected{
"entry",
"get_event_pumps()",
@@ -217,8 +214,7 @@ namespace tut
"pump, data = LL.get_event_next()\n"
"return data\n"
);
- LuaState L;
- auto future = LLLUAmanager::startScriptLine(L, lua);
+ auto future = LLLUAmanager::startScriptLine(lua);
// We woke up again ourselves because the coroutine running Lua has
// reached the get_event_next() call, which suspends the calling C++
// coroutine (including the Lua code running on it) until we post
@@ -356,8 +352,7 @@ namespace tut
sendReply(data, data);
}));
- LuaState L;
- auto [count, result] = LLLUAmanager::waitScriptLine(L, lua);
+ auto [count, result] = LLLUAmanager::waitScriptLine(lua);
ensure_equals("Lua script didn't return item", count, 1);
ensure_equals("echo failed", result, llsd::map("a", "a", "b", "b"));
}
@@ -371,18 +366,18 @@ namespace tut
"\n"
"fiber = require('fiber')\n"
"leap = require('leap')\n"
- "-- debug = require('printf')\n"
"local function debug(...) end\n"
+ "-- debug = require('printf')\n"
"\n"
"-- negative priority ensures catchall is always last\n"
- "catchall = leap.WaitFor:new(-1, 'catchall')\n"
+ "catchall = leap.WaitFor(-1, 'catchall')\n"
"function catchall:filter(pump, data)\n"
" debug('catchall:filter(%s, %s)', pump, data)\n"
" return data\n"
"end\n"
"\n"
"-- but first, catch events with 'special' key\n"
- "catch_special = leap.WaitFor:new(2, 'catch_special')\n"
+ "catch_special = leap.WaitFor(2, 'catch_special')\n"
"function catch_special:filter(pump, data)\n"
" debug('catch_special:filter(%s, %s)', pump, data)\n"
" return if data['special'] ~= nil then data else nil\n"
@@ -418,6 +413,12 @@ namespace tut
"fiber.launch('catch_special', drain, catch_special)\n"
"fiber.launch('requester(a)', requester, 'a')\n"
"fiber.launch('requester(b)', requester, 'b')\n"
+ // A script can normally count on an implicit fiber.run() call
+ // because fiber.lua calls LL.atexit(fiber.run). But atexit()
+ // functions are called by ~LuaState(), which (in the code below)
+ // won't be called until *after* we expect to interact with the
+ // various fibers. So make an explicit call for test purposes.
+ "fiber.run()\n"
);
LLSD requests;
@@ -429,10 +430,7 @@ namespace tut
requests.append(data);
}));
- LuaState L;
- auto future = LLLUAmanager::startScriptLine(L, lua);
- auto replyname{ L.obtainListener().getReplyName() };
- auto& replypump{ LLEventPumps::instance().obtain(replyname) };
+ auto future = LLLUAmanager::startScriptLine(lua);
// LuaState::expr() periodically interrupts a running chunk to ensure
// the rest of our coroutines get cycles. Nonetheless, for this test
// we have to wait until both requester() coroutines have posted and
@@ -444,6 +442,8 @@ namespace tut
llcoro::suspend();
}
ensure_equals("didn't get both requests", requests.size(), 2);
+ auto replyname{ requests[0]["reply"].asString() };
+ auto& replypump{ LLEventPumps::instance().obtain(replyname) };
// moreover, we expect they arrived in the order they were created
ensure_equals("a wasn't first", requests[0]["name"].asString(), "a");
ensure_equals("b wasn't second", requests[1]["name"].asString(), "b");
@@ -468,8 +468,7 @@ namespace tut
"\n"
"LL.get_event_next()\n"
);
- LuaState L;
- auto future = LLLUAmanager::startScriptLine(L, lua);
+ auto future = LLLUAmanager::startScriptLine(lua);
// Poke LLTestApp to send its preliminary shutdown message.
mApp.setQuitting();
// but now we have to give the startScriptLine() coroutine a chance to run
@@ -491,8 +490,7 @@ namespace tut
" x = 1\n"
"end\n"
);
- LuaState L;
- auto [count, result] = LLLUAmanager::waitScriptLine(L, lua);
+ auto [count, result] = LLLUAmanager::waitScriptLine(lua);
// We expect the above erroneous script has been forcibly terminated
// because it ran too long without doing any actual work.
ensure_equals(desc + " count: " + result.asString(), count, -1);