Specifically:
* String conversion in non-URL contexts (e.g. .prefixedText) uses spaces
instead of underscores.
* Setting .fragment now applies the same transformations that are done
(in PHP) by mw.title.new.
Bug: 56217
Change-Id: I12e354636bcde3327864088175fb4de61aecc81a
The PHP call that makes mw.site.namespaces work case-insensitively
doesn't handle non-standard spaces/underscores. So standardize them
before the call.
Bug: 56216
Change-Id: I4758478b126858fb581614f64eb15472f42fef51
A few edge cases were being incorrectly handled:
* mw.ustring.sub( 'abc', 1, 0 ) returned 'a', not ''.
* mw.ustring.codepoint( 'abc', 1, 0 ) returned 97, not no results.
* mw.ustring.codepoint( 'abc', 4, 4 ) returned 99, not no results.
* mw.ustring.gcodepoint had the same issues as mw.ustring.codepoint.
Change-Id: Ib8c0ef5a8073106eb7d90d0aa0513be4525dca08
Negative values for 'i' in mw.ustring.byteoffset are supposed to count
from the end of the string. But in LuaSandbox, it was actually counting
from two bytes before the end of the string due to a typo.
Fix that, and add some tests for it.
Bug: 50176
Change-Id: Iceee1022a55abd7a08df1ea7843e1277eb02798b
The "%f[set]" frontier pattern has been in Lua 5.1 since the beginning,
but was undocumented until Lua 5.2. And the code is even unchanged from
5.1.0 to 5.2.1. So there's no reason not to implement it in ustring too.
Note the changes to UstringLibrary.php are somewhat large, because it
splits the "convert a Lua bracketed charset to PCRE" code into a
separate function and it changes the handling of mw.ustring.find's and
mw.ustring.match's 'init' parameter from "substring, match from 0, then
add back on $init" to "use preg_match's $offset and use \G instead of ^
where this matters". Both of these are necessary to properly support
%f.
This also fixes a bug in the pure-Lua code (not used in Scribunto)
exposed by the unit tests for %f where %z was matching '\1' rather than
'\0' and %Z everything except '\1' instead of everything except '\0'.
Bug: 48331
Change-Id: Ie0b95ef5b734db53d6adc9de5dae4874f8944c08
The following errors are fixed:
* PHP warning and wrong return value with empty pattern and plain
* Incorrect offsets returned when init is larger than the string length
* Incorrect captured offsets returned when init is excessively negative
Bug: 47365
Change-Id: I9741418287dc727747326d6a19678370ce155a2b
Users are reporting disappointment that using pairs on title objects
doesn't let them see the fields available. It's easy enough to add a
__pairs metamethod to allow pairs to work on title objects, so let's do
that.
The same can be done for mw.uri objects.
For mw.message objects, we can easily enough change the implementation
to be like mw.language objects, which doesn't have this problem.
For mw.site.stats, we may as well just remove the load-on-demand feature
since it will be demanded as soon as the environment is cloned for the
first module.
Change-Id: Ie5a3b5684b2bb6c090f9994baa03977687688929
mw.title.new( pageid ) should not throw an error for an nonexisting
pageid, just return nil. Similarly, it should always return nil for 0,
rather than returning the last non-existent title created.
Change-Id: I3cdbb24fc785aef0f8e75fba1feccd26ac5b7370
This exists for some common text-processing functions that aren't
included in string (and therefore also aren't in mw.ustring), as well as
a logical place for the "unstrip" function requested in bug 45085.
Bug: 45085
Change-Id: I47356215fcc8ddeed5f901cd933a30021394bd78
It would be helpful for debugging if a frame object could be gotten in
the console. To that end, add an empty frame when running in the console
and allow it to be returned by mw.getCurrentFrame().
It would also be helpful to be able to create frames with arbitrary
arguments, again for testing. Fortunately support for creating child
frames with arbitrary arguments already exists in core, so we can just
use it.
And for good measure, be sure to restore the $engine->currentFrames
array even if the Lua code throws an exception.
Change-Id: I1dc8602d63af75424f267c42a3743fabbc1827f7
Currently, the time taken to parse the arguments passed to a Lua
function from #invoke will be counted against Lua's 10-second limit.
This is counterintuitive, and can remove incentive for users to convert
templates to Lua since they may have to convert a whole stack at once.
Note this requires change I11881232 to mediawiki/php/luasandbox to
actually have any effect.
Bug: 45684
Change-Id: I773950e4c399b8a1cfa6d1cde781a069d286b3bd
Since the content of a page is being loaded, this should be recorded in
templatelinks so things can be updated properly when that page is
edited.
Change-Id: I3e720fee2705f6c08ac0456c3cab0ed4ede84536
In the unit tests, the engine is given a parser, but the parser is not
being associated back with the engine. So in some cases, that could lead
to *another*, default engine being created for certain operations.
Fix that.
Change-Id: I79995c2635d9e470931b84dc1854dae26772bbe3
Apparently assigning to $wgHooks at the top of the file breaks every
other hook, somehow. So we need to set it in setUp(), but prevent the
interwiki prefix from being queried beforehand when the dataProvider is
run. Fun.
Change-Id: I8535942e3a0e47d9a5b2ea11c1b0b63c5acf2cc5
* mw.ustring.sub( '', 1 ) errors in LuaStandalone
* Default value for ustring.maxStringLength and ustring.maxPatternLength
should be infinity, not nil
* mw.ustring.find() returns one value instead of two in "plain" mode.
Change-Id: I5e65c4ec3a05f0e6930ce7ab7fd4ac72bea95e7f
Clean up the modules in engines/LuaCommon/lualib:
* Fix luabit/bit.lua to return its table instead of trying to set the
global directly.
* Fix luabit/hex.lua to return its table instead of trying to set the
global directly.
* luabit/noki.lua is useless for our purposes
* luabit/utf8.lua is redundant to mw.ustring
* stringtools uses coroutines, which we don't support
Also fix a parser test that has apparently been broken for a long time.
Change-Id: I1284cddb6e9b94327964cb1077d8dbdf7def6d06
Note that fetching any title besides the one for the current page is
considered "expensive". It also records the title fetched in the
ParserOutput so it will be listed in pagelinks, just like #ifexists.
This also moves the ToString test formatter into TestFramework.lua, so
TitleLibraryTests.lua can use it too.
Change-Id: I799f3289a37fe1349b6bca5758829acf82cb718f
In phpunit 3.6.10, assertEquals considered two NaNs to be equivalent.
Somewhere between that and phpunit 3.7.14, this behavior changed so NaNs
are no longer considered equivalent.
Change-Id: I2c664498eb34cf5119a2eaaa96a6be57b821ab94
People sometimes want to load large tables of constant data from a
module. Using require(), this has to be reparsed every time, which can
be slow.
mw.loadData() will load the just data once, and return a table with a
metatable cleverly designed to give read-only access to the loaded data.
Change-Id: Icec192bdbe6cfca7902fd5cb5d0e217bd8399637
Allowing a module to call mw.makeProtectedEnvFuncs() lets it bypass the
allowEnvFuncs setting. It can also be used to manipulate the global
tables that other modules' sandboxes will be copied from.
And for paranoia's sake, let's tighten up what setfenv is allowed to
set. This requires changing a unit test, because it is no longer
sane to do something like
env.setfenv, env.getfenv = mw.makeProtectedEnvFuncs( { [env] = true }, {} )
Nothing real does this, it was only in the unit test.
Change-Id: I8e0d83bb0980ee869af3ac4413afd211717ca92f
The Lua manual says this:
For this function, a '^' at the start of a pattern does not work as an
anchor, as this would prevent the iteration.
I had interpreted that to mean that a pattern starting with '^' would
never match in gmatch. But further testing reveals that the '^' is just
treated as a literal character: string.gmatch( "foo ^bar baz", "^%a+" )
will match "^bar".
Change-Id: Id91d6ee2db753ce1d6a4f6ae27764691d9e9fdc4
It's easy to forget a 'local' somewhere and accidentally leak a global
variable. Add a unit test to catch that.
Change-Id: I3a8dda22f108d88039f9562a1da7a739850bb14b
This is a reimplementation of Lua's string library with support for
UTF-8.
The entire ustring library is implemented in pure Lua. PHP callbacks are
also available for overrides: in LuaSandbox these are used for almost
all functions, while in LuaStandalone they are used only for the pattern
matching. Also, ustring.upper and ustring.lower are overridden using
mw.language's .uc and .lc if available.
It also includes a bunch of unit tests.
Note that if you download the normalization tests, they may fail under
LuaSandbox if you have PHP's intl extension installed and libicu on your
system is too old.
Change-Id: Ie76fdf8d3a85d0a3d2a41b0d3b7afe433f247af0
Rework the LuaEngine tests to be entirely modular, so that every library
need not add itself to one monolithic file. This also allows other
extensions that add Lua modules to make unit tests without having to
somehow inject them into a test class owned by Scribunto.
The approach taken is similar to that used for Selenium for running
tests against multiple browsers.
Change-Id: I294b2a8195759c0e4fa211f879305a8eb66d9c9a
To allow Lua libraries to mark functions as expensive, add an
incrementExpensiveFunctionCount() method to Scribunto_LuaEngine that
will call the corresponding Parser method and throw an error if the
limit is exceeded.
Also allow libraries to do the same thing from Lua by calling
mw.incrementExpensiveFunctionCount().
Change-Id: I56fded32b1077eff3980371e9abc9b3b7581f7b5
The listing of the standard modules in package.loaded seems to have been
removed to avoid leaking information to loaded modules. However, since
the *entire* environment is cloned, *including* package.loaded itself,
this does not seem to actually be a problem. But for good measure, also
add a unit test to verify that the version of the standard module tables
referenced from package.loaded is the same as that in _G.
This change also cleans up some unused local variables and an unused
local function from the package module.
Change-Id: I7ec8227b3273059e8f65ad735c215bfd0c623e64
In Lua, a table entry with a nil value is the same as a table entry that
doesn't exist. So when serializing for transfer to PHP, these keys will
be skipped. For a table as an associative array this isn't much of a
problem, but for a table as a list it means we have missing indexes.
Some of Lua's functions for handling "lists" (i.e. tables with numeric
keys) also have a problem when the list contains nils.
To work around these issues when passing argument lists and return value
lists, pass the number of elements along with the sparse list. On the
PHP end we can use this to fill in the missing nulls, and on the Lua
end we can pass this count to unpack() to avoid the problems on the Lua
side.
Change-Id: I858e3905a06e377693301da2b8bc534808f00e3e
Trivial fix, the parameter order to assertEquals() was backwards so if
the test failed it would indicate the "Actual" results as "Expected" and
vice versa.
Change-Id: Ibfe12591a58b10e0321aafea576c36cfa674f51d
Lua 5.2 introduces a nice feature where a metatable can override the
standard behavior of the pairs() and ipairs() functions. That would be
very useful in allowing a more standard syntax for our frame.args, and
it's very easy to do both in C and in Lua.
Change-Id: I37efc59a0c8876ee16184807e15fafbc07e2d288
Built-in modules (e.g. the upcoming ustring) should be able to extend
the string metatable, but user modules (and the debug console) should
not be able to.
But currently built-in modules cannot extend the string metatable in
LuaStandalone, while in LuaSandbox the string metatable can be extended
in the console. Fix this and add unit tests.
Change-Id: I15f5598fed318f2fe26b08ec47e16053dddc13c4
The existing unit tests work, but the setup is really not amenable to
the addition of additional tests in a modular fashion. This splits
things out so there is a framework for tests in Lua, and all a module
has to do on the Lua side is supply a list of functions to call and
results to expect. And then on the php side, only one array entry and
two short functions need to be added to LuaSandboxEngineTest to run the
tests.
Change-Id: Ib241b246aa0c7223c33887b38a5858582d7d31b0
Currently, the only way for PHP to pass a function to Lua is to pass
back a function that it received from Lua. This means, for example, that
PHP cannot implement a Lua iterator function except by registering a
library holding the function or by using loadString.
This changeset adds Scribunto_LuaInterpreter::wrapPhpFunction (and
implements it for both LuaSandbox and LuaStandalone), which takes a PHP
callable and returns a Lua function wrapping it.
Note that fallback code is included so this does not depend on
I2e552799.
Change-Id: Ic0a98eec7cc17ef4b1acee032c0f42d617b998d2
We should include a unit test to try to ensure that we don't add
features to one environment and forget about the other.
Change-Id: I72b1acf8eea4a05e05fed1efeb0663a3eff9278a
There's no reason LuaStandalone cannot handle NaN and Inf.
Also, add some unit tests to check this.
Change-Id: I8570242a792e212489ad24dfc04fb1f2940190a5
* string.format() truncates the string at a null character, causing a
deadlock when Lua attempts to send null characters to PHP. Use
concatenation instead.
* Added test.
* Fixed an error reporting issue in the console, which I happened to
notice at the same time as the above bug.
Change-Id: I2e6061a04512557492bffbd04bc09ca3bc1d80d6
The Darwin does not support POSIX timer which is being used to limit CPU
usage. Running the tests on Mac OS X would never end so we are just
skipping it.
Change-Id: I56c3e8cd1cba15d33256192aa7e721e6448c7a2e
Optionally remove setfenv and getfenv from the global environment in
which user code runs. This will improve the forwards-compatibility of
user code with Lua 5.2.
Porting to Lua 5.2 would still be a daunting project, of questionable
value, but at least only the internal code would need updating, and not
thousands of on-wiki modules. Compared to the environment changes, the
rest of the Lua 5.2 changes are relatively easy to simulate for
backwards compatibility.
Removed module() from the package module, since it depends on setfenv().
The native version of it is deprecated in Lua 5.2 for that reason.
Change-Id: I978903ca98943ac941833da13fe5027949f6b429
* Implemented the new parser interface based on a frame object, as
described in the design document and wikitech-l.
* Added parser tests for the new interface.
* Removed {{script:}} parser function
* Allow named parameters to {{#invoke:}}
* Don't trim the return value
* If a function invoked by #invoke returns multiple values, concatenate
them into a single string.
* If there is an error during parse, show the error message as an HTML
comment as well as via JavaScript. This makes parser test construction
easier, and probably makes debugging easier also.
* Rename mw_internal to mw_php to clarify its role. It is now strictly a
private Lua -> PHP interface function table.
* Protect mw.setup() against multiple invocation.
* Fixed a bug in Scribunto_LuaStandaloneInterpreter::receiveMessage():
large packets caused fread() to return with less than the requested
amount of data, which previously caused an exception. It's necessary
to check for EOF and to repeat the read to get all data. The receive
function on the Lua side does not suffer from this problem.
* In the standalone engine, fixed a bug in the interpretation of null
return values from PHP callbacks. This should return no values to Lua.
* Updated the Lua unit tests to account for the fact that functions are
now forced to return strings.
* Updated the getfenv and setfenv tests to account for the extra stack
level introduced by mw.executeFunction().
Change-Id: If8fdecdfc91ebe7bd4b1dae8489ccbdeb6bbf5ce
* Use LuaSandbox::getPeakMemoryUsage() from r115086
* Fixed the debug.traceback function from da06273e, was nil
Change-Id: Iae4b195ffe25a522d4c37f9c8341e1d6ea3ae106
Fixed the issue noticed during testing of da06273e, and which resulted
in satest.setfenv1() being disabled. It's not possible to protect
environments by iterating through every stack level, calling getfenv()
at each one, because if any of the stack levels is a tail call, an error
is raised.
Such a tail call was introduced in da06273e, which is why the test broke.
Instead, just protect the actual specified environments, not their
callers. The callers will have to protect themselves.
Change-Id: If39104010ff2663c1bae5105cc8d37e276532100
* Added error backtrace collection to MWServer:handleCall()
* When there is an error on parse, show a short and simple inline error
message to the user, which when clicked, expands to a full error with
HTML-formatted backtrace.
* When an error is encountered during module validation, have the code
editor jump directly to the line. Requires r115011.
* Expose the code location of most errors to Scribunto, by parsing the
standard error message format.
* During module validation, abbreviate the error location if the error
is in the same module.
* Do not execute the module during validation, just parse it. Execution
does not really work without an active parse operation in progress.
It already caused a fatal error if you called require() from the main
chunk, and problems would have become more visible as more
parser-related APIs were added.
* LuaSandbox does not yet provide backtraces, but this is planned.
Change-Id: Id9f6564a41b310792b3fe3ebb527cbf8f8771bd1
* Added tests for the engine classes.
* Added some tests that run under Lua.
* In the chunk names, fixed truncation of module names at 60 bytes
by using an "=" prefix instead of @.
* Fixed a bug in mw.clone() which was causing the metatable to be set on
the source table instead of the destination.
* Put restricted setfenv/getfenv in the cloned environment rather than
the base environment, they work better that way.
* In setfenv(), check for getfenv() == nil, since that's what our own
restricted getfenv returns.
* Fixed getfenv() handling of numeric arguments: add one where
appropriate.
Change-Id: I2b356fd65a3fcb348c4e99a3a4267408fb995739
Package library:
* Added a simulation of the Lua 5.1 package library.
* Removed mw.import(), replaced it with a package loader. Packages can be
retrieved from the wiki, using require('Module:Foo'), or from files
distributed with Scribunto, using require('foo'). The "Module:" prefix allows
for source compatibility with existing Lua code.
* Added a couple of libraries from LuaForge: luabit and stringtools.
* Made fetchModuleFromParser() return null on error instead of throwing an
exception, to more easily support the desired behaviour of the package loader,
which needs to return null on error.
* Renamed mw.setupEnvironment() to mw.setup() since it is setting up things
other than the environment now.
* In MWServer:handleRegisterLibrary(), remove the feature which interprets dots
in library names, since LuaSandbox doesn't support this.
Improved module isolation and related refactoring:
* Expose restricted versions of getfenv() and setfenv() to user Lua code.
Requires luasandbox r114952.
* Don't cache the export list returned by module execution for later function
calls. This breaks isolation of #invoke calls, since the local variables are
persistent.
* Removed ScribuntoFunctionBase and its children, since it doesn't really have
a purpose if it can't cache anything. Instead, invoke functions using a module
method called invoke().
* Removed Module::initialize(), replaced it with a validate() function. This is
a more elegant interface and works better with the new module caching scheme.
* Use a Status object for the return value of Engine::validate() instead of an
array. Use the formatting facilities of the Status class.
Other:
* Removed "too many returns" error, doesn't fit in with Lua conventions.
* Use the standalone engine by default, so that the extension will work without
configuration for more people.
* Added an accessor for $engine->interpreter
* Fix mw.clone() to correctly clone metatables
* If the standalone interpreter exits due to an error, there are some contexts
where the initial error will be caught and ignored, and the user will see the
error from checkValid() instead. In this case, rethrow the original error for
a more informative message.
* Load mw.lua into the initial standalone environment, to reduce code
duplication between mw.lua and MWServer.lua.
* Fixed a bug in Scribunto_LuaStandaloneInterpreter::handleCall() for functions
that return no results.
* Fixed a bug in encodeLuaVar() for strings with "\r". Added test case.
* In MWServer.lua, don't call error() for internal errors, instead just print
the error and exit. This avoids a protocol violation when an error is
encountered from within handleCall().
* Added lots of documentation. Lua doc comments are in LuaDoc format.
Change-Id: Ie2fd572c362bedf02f45d3fa5352a5280e034740
* Added unit tests for the two Lua interpreter classes
* Fixed a bug in checkType()
* Have Scribunto_LuaSandboxInterpreter throw an exception on construct
when the extension doesn't exist, to match the standalone behaviour.
* In Scribunto_LuaSandboxInterpreter, removed debugging statements
accidentally left in.
* Convert LuaSandboxTimeoutError to the appropriate common error
message.
* Moved the option munging from the sandbox engine to the interpreter,
so that the interpreter can be unit tested separately.
* Use /bin/sh instead of bash for lua_ulimit.sh, since dash is smaller
and still supports ulimit.
* Use exec to run the lua binary, so that the vsize of the shell doesn't
add to the memory limit.
* Added a quit function to the standalone interpreter. Unused at present.
* Don't add a comma after the last element of a table in a Lua
expression.
* Make the SIGXCPU detection work: proc_open() runs the command via a
shell, which reports signals in the child via the exit status, so
proc_get_status() will never return a valid termsig element.
* In MWServer:call(), fixed a bug causing the return values to be
wrapped in an array.
* Fixed a misunderstanding of what select() does.
* In MWServer:getStatus(), fixed indexes so that vsize will be correct.
Removed RSS, since it wasn't used anyway and turns out to be measured
in multiples of the page size, and I couldn't be bothered trying to
fetch that from getconf. Return the PID and vsize as numbers rather
than strings.
* Added a simple table dump feature to MWServer:debug().
* Fixed brackets in MWServer:tostring().
* Added missing Linux 32-bit binary.
Change-Id: Ibf5f4656b1c0a9f81287d363184c3fe9d2abdafd