Age | Commit message (Collapse) | Author |
|
|
|
Introduce `ScriptCommand` class that parses a command line into a script name
and optional args, using bash-like quoting and escaping. `ScriptCommand`
searches for a file with that script name on a passed list of directories; the
directories may be specified relative to a particular base directory.
`ScriptCommand` supports the special case of a script name containing unescaped
spaces. It guarantees that either the returned script file exists, or its
`error()` string is non-empty.
Replace `LLLeap::create()` logic, from which `ScriptCommand` was partly
derived, with a `ScriptCommand` instance.
Make `LLLUAmanager::runScriptFile()` use a `ScriptCommand` instance to parse
the passed command line.
Subsume `LLAppViewer::init()` script-path-searching logic for `--luafile`
into `ScriptCommand`. In fact that lambda now simply calls
`LLLUAmanager::runScriptFile()`.
Make `lluau::dostring()` accept an optional vector of script argument strings.
Following PUC-Rio Lua convention, pass these arguments into a Lua script as
the predefined global `arg`, and also as the script's `...` argument.
`LuaState::expr()` also accepts and passes through script argument strings.
Change the log tag for the Lua script interruption message: if we want it, we
can still enable it, but we don't necessarily want it along with all other
"Lua" DEBUG messages.
Remove `LuaState::script_finished_fn`, which isn't used any more. Also remove
the corresponding `LLLUAmanager::script_finished_fn`. This allows us to
simplify `~LuaState()` slightly, as well as the parameter signatures for
`LLLUAmanager::runScriptFile()` and `runScriptLine()`.
|
|
|
|
Consensus seems to be that (a) string_view is, in effect, already a reference,
(b) it's small enough to make pass-by-value reasonable and (c) the optimizer
can reason about values way better than it can about references.
|
|
|
|
|
|
|
|
|
|
|
|
Use a static unordered_map to allow a function receiving (lua_State* L) to
look up the LuaState instance managing that lua_State. We've thought about
this from time to time already. LuaState's constructor creates the map entry;
its destructor removes it; the new static getParent(lua_State* L) method
performs the lookup.
Migrate lluau::set_interrupts_counter() and check_interrupts_counter() into
LuaState member functions. Add a new mInterrupts counter for them.
Importantly, LuaState::check_interrupts_counter(), which is indirectly called
by a lua_callbacks().interrupt function, no longer performs any Lua stack
operations. Empirically, it seems the Lua engine is capable of interrupting
itself at a moment when re-entry confuses it.
Change previous lluau::set_interrupts_counter(L, 0) calls to
LuaState::getParent(L).set_interrupts_counter(0).
Also add LuaStackDelta class, and a lua_checkdelta() helper macro, to verify
that the Lua data stack depth on exit from a block differs from the depth on
entry by exactly the expected amount. Sprinkle lua_checkdelta() macros in
likely places.
|
|
luaL_checkstack() accepts a third parameter which is included in the stack
overflow error message. We've been passing nullptr, leading to messages of the
form "stack overflow ((null))". lluau_checkstack() implicitly passes
__FUNCTION__, so we can distinguish which underlying luaL_checkstack() call
encountered the stack overflow condition.
Also, when calling each atexit() function, pass Luau's debug.traceback()
function as the lua_pcall() error handler. This should help diagnose errors in
atexit() functions.
|
|
Remove LLLUAmanager::mumbleScriptLine() LuaState& parameters. Make
startScriptLine(), waitScriptLine() and runScriptLine() exactly parallel to
startScriptFile(), waitScriptFile() and runScriptFile(). That means that
runScriptLine()'s C++ coroutine instantiates and destroys its own LuaState,
which means that LL.atexit() functions will run on the Lua-specific C++
coroutine rather than (say) the viewer's main coroutine.
Introduce LLLUAmanager::script_result typedef for std::pair<int, LLSD> and use
in method returns.
Remove LuaState::initLuaState(); move its logic back into the constructor.
Remove initLuaState() calls in the expr() error cases: they're moot now that
we won't get subsequent expr() calls on the same LuaState instance.
Remove LLFloaterLUADebug "Use clean lua_State" checkbox and the cleanLuaState()
method. Remove mState member.
Remove explicit LuaState declarations from LLLUAmanager tests. Adapt one test
for implicit LuaState: it was directly calling LuaState::obtainListener() to
discover the LuaListener's reply-pump name. But since that test also captures
two leap.request() calls from the Lua script, it can just look at the "reply"
key in either of those requests.
|
|
Remove LL_TEST special case from require() code (to search in the viewer's
source tree). Instead, make llluamanager_test.cpp append to LuaRequirePath to
get the same effect.
|
|
|
|
Remove AutorunLuaScriptFile and the LLLUAmanager::runScriptOnLogin() method
that checked it.
Instead, iterate over LuaAutorunPath directories at viewer startup, iterate
over *.lua files in each and implicitly run those.
LuaCommandPath and LuaRequirePath are not yet implemented.
|
|
|
|
Instead of deriving LuaListener from LLInstanceTracker with an int key,
generating a unique int key and storing that key in the Registry, use new
lua_emplace<LuaState>() to store the LuaListener directly in a Lua userdata
object in the Lua Registry.
Because lua_emplace<T>() uses LL.atexit() to guarantee that ~LuaState will
destroy the T object, we no longer need ~LuaState() to make a special call
specifically to destroy the LuaListener, if any. So we no longer need
LuaState::getListener() separate from obtainListener().
Since LuaListener is no longer an LLInstanceTracker subclass, make
LuaState::obtainListener() return LuaListener& rather than LuaListener::ptr_t.
|
|
Make viewer_manifest.py copy them into the viewer install image.
Make the require() function look for them there.
|
|
stringize() constructs, populates and destroys a std::ostringstream, which is
actually less efficient than directly allocating a std::string big enough for
the result of operator+().
Maybe someday we'll specialize stringize(p0, p1) for the case in which they're
both string-like, and invoke operator+() for that situation...
|
|
Use in LuaState::expr() so we can catch a runaway in-memory Lua chunk as well
as a script read from a file.
|
|
Make LLCoros constructor echo "LLApp" status-change events on new "LLCoros"
event pump.
Rename LLCoros::kill() to killreq() because this operation only registers a
request for the named coroutine to terminate next time it calls checkStop().
Add a new CoroData member to record the name of the coroutine requesting
termination. killreq() sets that and also posts "killreq" to "LLCoros".
Add an optional final-cleanup callback to LLCoros::checkStop(). Make
checkStop() check for a pending killreq() request as well as viewer
termination. Introduce new LLCoros::Killed exception for that case.
Introduce LLCoros::getStopListener(), with two overloads, to encapsulate some
of the messy logic to listen (perhaps temporarily) for viewer shutdown. Both
overloads are for use by code at the source end of a queue or promise or other
resource for which coroutines might still be waiting at viewer shutdown time.
One overload is specifically for when the caller knows the name of the one and
only coroutine that will wait on the resource (e.g. because the caller IS that
coroutine). That overload honors killreq().
Use getStopListener() to simplify the four existing places where we set up
such a listener. Add a fifth: also make WorkQueue listen for viewer shutdown
(resolving a TODO comment).
Remove LLLUAmanager::terminateScript(), getTerminationList() and the static
sTerminationList. In the Lua interrupt callback, instead of checking
sTerminationList, call LLCoros::checkStop().
Change LLFloaterLUAScripts terminate-script logic to call LLCoros::killreq()
instead of posting on "LLLua" and calling LLLUAmanager::terminateScript().
Drop LLApp::setStatus() posting to "LLLua" LLEventPump: the above makes that
moot.
|
|
|
|
|
|
|
|
|
|
Lua scripts floater
|
|
Our std::strings are UTF-8 encoded, so conversion from std::string to
std::filesystem::path must use UTF-8 decoding. The native Windows
std::filesystem::path constructor and assignment operator accepting
std::string use "native narrow encoding," which mangles path strings
containing UTF-8 encoded non-ASCII characters.
fsyspath's std::string constructor and assignment operator explicitly engage
std::filesystem::u8path() to handle encoding. u8path() is deprecated in C++20,
but once we adapt fsyspath's conversion to C++20 conventions, consuming code
need not be modified.
|
|
|
|
Make LLRequireResolver capture std::filesystem::path instances, instead of
std::strings, for the path to resolve and the source directory. Store the
running script's containing directory instead of calling parent_path() over
and over.
Demote Lua LL.post_on() logging to DEBUG level instead of INFO.
|
|
|
|
|
|
Run each script file with new LuaState
|
|
|
|
|
|
|
|
|
|
The problem with running a `require()` module on a Lua coroutine is that it
prohibits calling `leap.request()` at module load time. When a coroutine calls
`leap.request()`, it must yield back to Lua's main thread -- but a `require()`
module is forbidden from yielding.
Running on Lua's main thread means that (after potentially giving time slices
to other ready coroutines) `fiber.lua` will request the response event from
the viewer, and continue processing the loaded module without having to yield.
|
|
|
|
The code to save the loaded module was using the wrong key.
|
|
Push throwing Lua errors down into LLRequireResolver::findModule() and
findModuleImpl() so their callers don't have to handle the error case. That
eliminates finishrequire().
require() itself now only retrieves (and pops) the passed module name and
calls LLRequireResolver::resolveRequire() to do the actual work.
resolveRequire() is now void. It only instantiates LLRequireResolver and calls
its findModule().
findModule() is now also void. It's guaranteed to either push the loaded Lua
module or throw a Lua error. In particular, when findPathImpl() cannot find
the specified module, findModule() throws an error. That replaces
ModuleStatus::NotFound.
Since std::filesystem::path::append() aka operator/() detects when its right
operand is absolute and, in that case, discards the left operand, we no longer
need resolveAndStoreDefaultPaths(): we can just invoke that operation inline.
When findModule() pushes _MODULES on the Lua stack, it uses LuaRemover (below)
to ensure that _MODULES is removed again no matter how findModules() exits.
findModuleImpl() now accepts the candidate pathname as its argument. That
eliminates mAbsolutePath.
findModuleImpl() now returns only bool: true means the module was found and
loaded and pushed on the Lua stack, false means not found and nothing was
pushed; no return means an error was reported.
Push running a newly found module's source file down into findModuleImpl().
That eliminates the distinction between Cached and FileRead, which obviates
ModuleStatus: a bool return means either "previously cached" or "we read it,
compiled it, loaded it and ran it." That also eliminates the need to store the
module's textual content in mSourceCode.
Similarly, once loading the module succeeds, findModuleImpl() caches it in
_MODULES right away. That eliminates ResolvedRequire since we need not pass
the full pathname of the found module (or its contents) back up through the
call chain.
Move require() code that runs the new module into private runModule() method,
called by findModuleImpl() in the not-cached case. runModule() is the only
remaining method that can push either a string error message or the desired
module, because of its funny stack manipulations. That means the check for a
string error message on the stack top can move down to findModuleImpl().
Add LuaRemover class to ensure that on exit from some particular C++ block,
the specified Lua stack entry will definitely be removed. This is different
from LuaPopper in that it engages lua_remove() rather than lua_pop().
Also ditch obsolete await_event() Lua entry point.
|
|
|
|
|
|
|
|
|
|
Don't set up a Lua callback to receive incoming events, a la listen_events().
Don't listen on an arbitrary event pump, a la await_event().
Instead, the new get_event_pumps() entry point simply delivers the reply pump
and command pump names (as listen_events() did) without storing a Lua
callback.
Make LuaListener capture incoming events on the reply pump in a queue. This
avoids the problem of multiple events arriving too quickly for the Lua script
to retrieve. If the queue gets too big, discard the excess instead of blocking
the caller of post().
Then the new get_event_next() entry point retrieves the next (pump, data) pair
from the queue, blocking the Lua script until a suitable event arrives. This
is closer to the use of stdin for a LEAP plugin. It also addresses the
question: what should the Lua script's C++ coroutine do while waiting for an
incoming reply pump event?
Recast llluamanager_test.cpp for this new, more straightforward API.
Move LLLeap's and LuaListener's reply LLEventPump into LLLeapListener, which
they both use. This simplifies LLLeapListener's API, which was a little
convoluted: the caller supplied a connect callback to allow LLLeapListener to
connect some listener to the caller's reply pump. Now, instead, the caller
simply passes a bool(pumpname, data) callback to receive events incoming on
LLLeapListener's own reply pump.
Fix a latent bug in LLLeapListener: if a plugin called listen() more than once
with the same listener name, the new connection would not have been saved.
While at it, replace some older Boost features in LLLeapListener and LLLeap.
|
|
|
|
|
|
leaphelp() (no argument) shows a list of all LEAP APIs.
leaphelp(API) shows further help for a specific API.
Both forms query LuaListener's LeapListener and report its responses. In
future we might reimplement leaphelp() as a Lua function.
Add LuaState::getListener() method, which checks whether there's a LuaListener
associated with this LuaState and returns a pointer if so.
Add LuaState::obtainListener() method, which finds or creates a LuaListener
for this LuaState and returns its pointer.
Both the above use logic migrated from the Lua listen_events() entry point,
which now calls obtainListener() instead.
|
|
Since the description shows up in the log output, it's better to see just the
script filename than to see "runScriptFile('filename')".
|
|
Extend the LuaFunction::Registry map to store helptext as well as the function
pointer.
Add help text to every existing lua_function() invocation.
|