2012-04-13 10:38:12 +00:00
|
|
|
mw = mw or {}
|
|
|
|
|
Added more Lua environment features
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
2012-04-18 03:46:18 +00:00
|
|
|
local packageCache
|
|
|
|
local packageModuleFunc
|
2012-05-22 03:56:07 +00:00
|
|
|
local php
|
2012-05-23 06:51:59 +00:00
|
|
|
local allowEnvFuncs = false
|
2012-07-14 04:23:42 +00:00
|
|
|
local logBuffer = ''
|
2013-02-21 21:31:23 +00:00
|
|
|
local loadedData = {}
|
2022-09-22 07:45:47 +00:00
|
|
|
local loadedJsonData = {}
|
2014-04-01 17:34:41 +00:00
|
|
|
local executeFunctionDepth = 0
|
Added more Lua environment features
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
2012-04-18 03:46:18 +00:00
|
|
|
|
2022-09-22 07:45:47 +00:00
|
|
|
|
2014-07-07 18:46:59 +00:00
|
|
|
--- Put an isolation-friendly package module into the specified environment
|
|
|
|
-- table. The package module will have an empty cache, because caching of
|
Added more Lua environment features
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
2012-04-18 03:46:18 +00:00
|
|
|
-- module functions from other cloned environments would break module isolation.
|
2014-07-07 18:46:59 +00:00
|
|
|
--
|
Added more Lua environment features
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
2012-04-18 03:46:18 +00:00
|
|
|
-- @param env The cloned environment
|
|
|
|
local function makePackageModule( env )
|
2013-04-17 02:39:37 +00:00
|
|
|
-- Remove loaders from env, we don't want it inheriting our loadPackage.
|
|
|
|
if env.package then
|
|
|
|
env.package.loaders = nil
|
|
|
|
end
|
|
|
|
|
Added more Lua environment features
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
2012-04-18 03:46:18 +00:00
|
|
|
-- Create the package globals in the given environment
|
|
|
|
setfenv( packageModuleFunc, env )()
|
|
|
|
|
|
|
|
-- Make a loader function
|
|
|
|
local function loadPackage( modName )
|
|
|
|
local init
|
|
|
|
if packageCache[modName] == 'missing' then
|
|
|
|
return nil
|
|
|
|
elseif packageCache[modName] == nil then
|
2014-09-26 18:11:21 +00:00
|
|
|
local lib = php.loadPHPLibrary( modName )
|
|
|
|
if lib ~= nil then
|
|
|
|
init = function ()
|
|
|
|
return mw.clone( lib )
|
|
|
|
end
|
|
|
|
else
|
|
|
|
init = php.loadPackage( modName )
|
|
|
|
if init == nil then
|
|
|
|
packageCache[modName] = 'missing'
|
|
|
|
return nil
|
|
|
|
end
|
Added more Lua environment features
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
2012-04-18 03:46:18 +00:00
|
|
|
end
|
|
|
|
packageCache[modName] = init
|
|
|
|
else
|
|
|
|
init = packageCache[modName]
|
|
|
|
end
|
|
|
|
|
|
|
|
setfenv( init, env )
|
|
|
|
return init
|
|
|
|
end
|
|
|
|
|
|
|
|
table.insert( env.package.loaders, loadPackage )
|
|
|
|
end
|
|
|
|
|
2014-07-07 18:46:59 +00:00
|
|
|
--- Set up the base environment. The PHP host calls this function after any
|
Added more Lua environment features
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
2012-04-18 03:46:18 +00:00
|
|
|
-- necessary host-side initialisation has been done.
|
2013-01-25 17:53:18 +00:00
|
|
|
function mw.setupInterface( options )
|
|
|
|
-- Don't allow any more calls
|
|
|
|
mw.setupInterface = nil
|
2012-05-22 03:56:07 +00:00
|
|
|
|
Added more Lua environment features
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
2012-04-18 03:46:18 +00:00
|
|
|
-- Don't allow getmetatable() on a non-table, since if you can get the metatable,
|
|
|
|
-- you can set values in it, breaking isolation
|
|
|
|
local old_getmetatable = getmetatable
|
|
|
|
function getmetatable(obj)
|
|
|
|
if type(obj) == 'table' then
|
|
|
|
return old_getmetatable(obj)
|
|
|
|
else
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-05-23 06:51:59 +00:00
|
|
|
if options.allowEnvFuncs then
|
|
|
|
allowEnvFuncs = true
|
|
|
|
end
|
|
|
|
|
2013-01-25 17:53:18 +00:00
|
|
|
-- Store the interface table
|
Added more Lua environment features
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
2012-04-18 03:46:18 +00:00
|
|
|
--
|
2013-01-25 17:53:18 +00:00
|
|
|
-- mw_interface.loadPackage() returns function values with their environment
|
Added more Lua environment features
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
2012-04-18 03:46:18 +00:00
|
|
|
-- set to the base environment, which would violate module isolation if they
|
2014-07-07 18:46:59 +00:00
|
|
|
-- were run from a cloned environment. We can only allow access to
|
2013-01-25 17:53:18 +00:00
|
|
|
-- mw_interface.loadPackage via our environment-setting wrapper.
|
Added more Lua environment features
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
2012-04-18 03:46:18 +00:00
|
|
|
--
|
2013-01-25 17:53:18 +00:00
|
|
|
php = mw_interface
|
|
|
|
mw_interface = nil
|
Added more Lua environment features
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
2012-04-18 03:46:18 +00:00
|
|
|
|
2012-05-22 03:56:07 +00:00
|
|
|
packageModuleFunc = php.loadPackage( 'package' )
|
2012-12-20 21:09:24 +00:00
|
|
|
makePackageModule( _G )
|
2012-12-27 16:22:44 +00:00
|
|
|
package.loaded.mw = mw
|
Added more Lua environment features
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
2012-04-18 03:46:18 +00:00
|
|
|
packageCache = {}
|
|
|
|
end
|
|
|
|
|
2014-07-02 15:56:53 +00:00
|
|
|
--- Create a table like the one os.date() returns, but with a metatable that sets TTLs as the values are looked at.
|
|
|
|
local function wrapDateTable( now )
|
|
|
|
return setmetatable( {}, {
|
|
|
|
__index = function( t, k )
|
|
|
|
if k == 'sec' then
|
|
|
|
php.setTTL( 1 )
|
|
|
|
elseif k == 'min' then
|
|
|
|
php.setTTL( 60 - now.sec )
|
|
|
|
elseif k == 'hour' then
|
|
|
|
php.setTTL( 3600 - now.min * 60 - now.sec )
|
|
|
|
elseif now[k] ~= nil then
|
|
|
|
php.setTTL( 86400 - now.hour * 3600 - now.min * 60 - now.sec )
|
|
|
|
end
|
|
|
|
t[k] = now[k]
|
|
|
|
return now[k]
|
|
|
|
end
|
|
|
|
} )
|
|
|
|
end
|
2014-06-01 03:15:42 +00:00
|
|
|
|
|
|
|
--- Wrappers for os.date() and os.time() that set the TTL of the output, if necessary
|
|
|
|
local function ttlDate( format, time )
|
|
|
|
if time == nil and ( format == nil or type( format ) == 'string' ) then
|
|
|
|
local now = os.date( format and format:sub( 1, 1 ) == '!' and '!*t' or '*t' )
|
2014-07-02 15:56:53 +00:00
|
|
|
if format == '!*t' or format == '*t' then
|
|
|
|
return wrapDateTable( now )
|
|
|
|
end
|
2014-06-01 03:15:42 +00:00
|
|
|
local cleanedFormat = format and format:gsub( '%%%%', '' )
|
2014-07-02 15:56:53 +00:00
|
|
|
if not format or cleanedFormat:find( '%%[EO]?[crsSTX+]' ) then
|
2014-06-01 03:15:42 +00:00
|
|
|
php.setTTL( 1 ) -- second
|
|
|
|
elseif cleanedFormat:find( '%%[EO]?[MR]' ) then
|
|
|
|
php.setTTL( 60 - now.sec ) -- minute
|
|
|
|
elseif cleanedFormat:find( '%%[EO]?[HIkl]' ) then
|
|
|
|
php.setTTL( 3600 - now.min * 60 - now.sec ) -- hour
|
|
|
|
elseif cleanedFormat:find( '%%[EO]?[pP]' ) then
|
|
|
|
php.setTTL( 43200 - ( now.hour % 12 ) * 3600 - now.min * 60 - now.sec ) -- am/pm
|
|
|
|
else
|
|
|
|
-- It's not worth the complexity to figure out the exact TTL of larger units than days.
|
|
|
|
-- If they haven't used anything shorter than days, then just set the TTL to expire at
|
|
|
|
-- the end of today.
|
|
|
|
php.setTTL( 86400 - now.hour * 3600 - now.min * 60 - now.sec )
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return os.date( format, time )
|
|
|
|
end
|
|
|
|
|
|
|
|
local function ttlTime( t )
|
|
|
|
if t == nil then
|
|
|
|
php.setTTL( 1 )
|
|
|
|
end
|
|
|
|
return os.time( t )
|
|
|
|
end
|
|
|
|
|
2021-03-18 00:45:18 +00:00
|
|
|
local function frameExists( frameId )
|
|
|
|
-- Optimization: don't call into PHP to check if frame 'empty' or 'current' exist: 'empty'
|
|
|
|
-- always exists, and 'current' will have been set up by the engine already before calling
|
|
|
|
-- into Lua code.
|
|
|
|
return frameId == 'empty' or frameId == 'current' or php.frameExists( frameId )
|
|
|
|
end
|
|
|
|
|
2013-02-19 00:49:08 +00:00
|
|
|
local function newFrame( frameId, ... )
|
2021-03-18 00:45:18 +00:00
|
|
|
if not frameExists( frameId ) then
|
2013-02-19 00:49:08 +00:00
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
2012-05-22 03:56:07 +00:00
|
|
|
local frame = {}
|
2013-02-19 00:49:08 +00:00
|
|
|
local parentFrameIds = { ... }
|
2012-05-22 03:56:07 +00:00
|
|
|
local argCache = {}
|
|
|
|
local argNames
|
2012-12-27 22:58:39 +00:00
|
|
|
local args_mt = {}
|
2012-05-22 03:56:07 +00:00
|
|
|
|
2012-07-02 07:27:18 +00:00
|
|
|
local function checkSelf( self, method )
|
|
|
|
if self ~= frame then
|
|
|
|
error( "frame:" .. method .. ": invalid frame object. " ..
|
|
|
|
"Did you call " .. method .. " with a dot instead of a colon, i.e. " ..
|
|
|
|
"frame." .. method .. "() instead of frame:" .. method .. "()?",
|
|
|
|
3 )
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-12-27 22:58:39 +00:00
|
|
|
-- Getter for args
|
2012-05-22 03:56:07 +00:00
|
|
|
local function getExpandedArgument( dummy, name )
|
|
|
|
name = tostring( name )
|
|
|
|
if argCache[name] == nil then
|
|
|
|
local arg = php.getExpandedArgument( frameId, name )
|
|
|
|
if arg == nil then
|
|
|
|
argCache[name] = false
|
|
|
|
else
|
|
|
|
argCache[name] = arg
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if argCache[name] == false then
|
|
|
|
return nil
|
|
|
|
else
|
|
|
|
return argCache[name]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-12-27 22:58:39 +00:00
|
|
|
args_mt.__index = getExpandedArgument
|
|
|
|
|
|
|
|
-- pairs handler for args
|
|
|
|
args_mt.__pairs = function ()
|
|
|
|
if not argNames then
|
|
|
|
local arguments = php.getAllExpandedArguments( frameId )
|
|
|
|
argNames = {}
|
|
|
|
for name, value in pairs( arguments ) do
|
|
|
|
table.insert( argNames, name )
|
|
|
|
argCache[name] = value
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local index = 0
|
|
|
|
return function ()
|
|
|
|
index = index + 1
|
|
|
|
if argNames[index] then
|
|
|
|
return argNames[index], argCache[argNames[index]]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- ipairs 'next' function for args
|
|
|
|
local function argsInext( dummy, i )
|
|
|
|
local value = getExpandedArgument( dummy, i + 1 )
|
|
|
|
if value then
|
|
|
|
return i + 1, value
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
args_mt.__ipairs = function () return argsInext, nil, 0 end
|
|
|
|
|
|
|
|
frame.args = {}
|
|
|
|
setmetatable( frame.args, args_mt )
|
|
|
|
|
2012-05-22 03:56:07 +00:00
|
|
|
local function newCallbackParserValue( callback )
|
2013-02-13 04:40:18 +00:00
|
|
|
local value = {}
|
2012-05-22 03:56:07 +00:00
|
|
|
local cache
|
|
|
|
|
|
|
|
function value:expand()
|
|
|
|
if not cache then
|
|
|
|
cache = callback()
|
|
|
|
end
|
|
|
|
return cache
|
|
|
|
end
|
|
|
|
|
|
|
|
return value
|
|
|
|
end
|
|
|
|
|
|
|
|
function frame:getArgument( opt )
|
2012-07-02 07:27:18 +00:00
|
|
|
checkSelf( self, 'getArgument' )
|
|
|
|
|
2012-05-22 03:56:07 +00:00
|
|
|
local name
|
|
|
|
if type( opt ) == 'table' then
|
|
|
|
name = opt.name
|
|
|
|
else
|
|
|
|
name = opt
|
|
|
|
end
|
|
|
|
|
2014-07-07 18:46:59 +00:00
|
|
|
return newCallbackParserValue(
|
|
|
|
function ()
|
2012-05-22 03:56:07 +00:00
|
|
|
return getExpandedArgument( nil, name )
|
|
|
|
end
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
function frame:getParent()
|
2012-07-02 07:27:18 +00:00
|
|
|
checkSelf( self, 'getParent' )
|
|
|
|
|
2013-02-19 00:49:08 +00:00
|
|
|
return newFrame( unpack( parentFrameIds ) )
|
|
|
|
end
|
|
|
|
|
2014-12-04 16:47:27 +00:00
|
|
|
local function checkArgs( name, args )
|
|
|
|
local ret = {}
|
|
|
|
for k, v in pairs( args ) do
|
|
|
|
local tp = type( k )
|
|
|
|
if tp ~= 'string' and tp ~= 'number' then
|
|
|
|
error( name .. ": arg keys must be strings or numbers, " .. tp .. " given", 3 )
|
|
|
|
end
|
|
|
|
tp = type( v )
|
|
|
|
if tp == 'boolean' then
|
|
|
|
ret[k] = v and '1' or ''
|
|
|
|
elseif tp == 'string' or tp == 'number' then
|
|
|
|
ret[k] = tostring( v )
|
|
|
|
else
|
|
|
|
error( name .. ": invalid type " .. tp .. " for arg '" .. k .. "'", 3 )
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return ret
|
|
|
|
end
|
|
|
|
|
2013-02-19 00:49:08 +00:00
|
|
|
function frame:newChild( opt )
|
|
|
|
checkSelf( self, 'newChild' )
|
|
|
|
|
|
|
|
if type( opt ) ~= 'table' then
|
|
|
|
error( "frame:newChild: the first parameter must be a table", 2 )
|
|
|
|
end
|
|
|
|
|
|
|
|
local title, args
|
|
|
|
if opt.title == nil then
|
|
|
|
title = false
|
2012-05-22 03:56:07 +00:00
|
|
|
else
|
2013-02-19 00:49:08 +00:00
|
|
|
title = tostring( opt.title )
|
2012-05-22 03:56:07 +00:00
|
|
|
end
|
2013-02-19 00:49:08 +00:00
|
|
|
if opt.args == nil then
|
|
|
|
args = {}
|
|
|
|
elseif type( opt.args ) ~= 'table' then
|
|
|
|
error( "frame:newChild: args must be a table", 2 )
|
|
|
|
else
|
2014-12-04 16:47:27 +00:00
|
|
|
args = checkArgs( 'frame:newChild', opt.args )
|
2013-02-19 00:49:08 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
local newFrameId = php.newChildFrame( frameId, title, args )
|
|
|
|
return newFrame( newFrameId, frameId, unpack( parentFrameIds ) )
|
2012-05-22 03:56:07 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function frame:expandTemplate( opt )
|
2012-07-02 07:27:18 +00:00
|
|
|
checkSelf( self, 'expandTemplate' )
|
|
|
|
|
2012-05-22 03:56:07 +00:00
|
|
|
local title
|
|
|
|
|
|
|
|
if type( opt ) ~= 'table' then
|
|
|
|
error( "frame:expandTemplate: the first parameter must be a table" )
|
|
|
|
end
|
|
|
|
if opt.title == nil then
|
|
|
|
error( "frame:expandTemplate: a title is required" )
|
|
|
|
else
|
2014-04-10 15:30:37 +00:00
|
|
|
if type( opt.title ) == 'table' and opt.title.namespace == 0 then
|
|
|
|
title = ':' .. tostring( opt.title )
|
|
|
|
else
|
|
|
|
title = tostring( opt.title )
|
|
|
|
end
|
2012-05-22 03:56:07 +00:00
|
|
|
end
|
|
|
|
local args
|
|
|
|
if opt.args == nil then
|
|
|
|
args = {}
|
|
|
|
elseif type( opt.args ) ~= 'table' then
|
2013-02-19 00:49:08 +00:00
|
|
|
error( "frame:expandTemplate: args must be a table" )
|
2012-05-22 03:56:07 +00:00
|
|
|
else
|
2014-12-04 16:47:27 +00:00
|
|
|
args = checkArgs( 'frame:expandTemplate', opt.args )
|
2012-05-22 03:56:07 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
return php.expandTemplate( frameId, title, args )
|
|
|
|
end
|
|
|
|
|
2013-03-04 05:42:17 +00:00
|
|
|
function frame:callParserFunction( name, args, ... )
|
|
|
|
checkSelf( self, 'callParserFunction' )
|
|
|
|
|
|
|
|
if type( name ) == 'table' then
|
|
|
|
name, args = name.name, name.args
|
|
|
|
if type( args ) ~= 'table' then
|
|
|
|
args = { args }
|
|
|
|
end
|
|
|
|
elseif type( args ) ~= 'table' then
|
|
|
|
args = { args, ... }
|
|
|
|
end
|
|
|
|
|
|
|
|
if name == nil then
|
|
|
|
error( "frame:callParserFunction: a function name is required", 2 )
|
|
|
|
elseif type( name ) == 'string' or type( name ) == 'number' then
|
|
|
|
name = tostring( name )
|
|
|
|
else
|
|
|
|
error( "frame:callParserFunction: function name must be a string or number", 2 )
|
|
|
|
end
|
|
|
|
|
2014-12-04 16:47:27 +00:00
|
|
|
args = checkArgs( 'frame:callParserFunction', args )
|
2013-03-04 05:42:17 +00:00
|
|
|
|
|
|
|
return php.callParserFunction( frameId, name, args )
|
|
|
|
end
|
|
|
|
|
|
|
|
function frame:extensionTag( name, content, args )
|
|
|
|
checkSelf( self, 'extensionTag' )
|
|
|
|
|
|
|
|
if type( name ) == 'table' then
|
|
|
|
name, content, args = name.name, name.content, name.args
|
|
|
|
end
|
|
|
|
|
|
|
|
if name == nil then
|
|
|
|
error( "frame:extensionTag: a function name is required", 2 )
|
|
|
|
elseif type( name ) == 'string' or type( name ) == 'number' then
|
|
|
|
name = tostring( name )
|
|
|
|
else
|
|
|
|
error( "frame:extensionTag: tag name must be a string or number", 2 )
|
|
|
|
end
|
|
|
|
|
|
|
|
if content == nil then
|
|
|
|
content = ''
|
|
|
|
elseif type( content ) == 'string' or type( content ) == 'number' then
|
|
|
|
content = tostring( content )
|
|
|
|
else
|
|
|
|
error( "frame:extensionTag: content must be a string or number", 2 )
|
|
|
|
end
|
|
|
|
|
|
|
|
if args == nil then
|
2013-10-07 18:41:35 +00:00
|
|
|
args = { content }
|
2013-03-04 05:42:17 +00:00
|
|
|
elseif type( args ) == 'string' or type( args ) == 'number' then
|
|
|
|
args = { content, args }
|
|
|
|
elseif type( args ) == 'table' then
|
2014-12-04 16:47:27 +00:00
|
|
|
args = checkArgs( 'frame:extensionTag', args )
|
2013-03-04 05:42:17 +00:00
|
|
|
table.insert( args, 1, content )
|
|
|
|
else
|
|
|
|
error( "frame:extensionTag: args must be a string, number, or table", 2 )
|
|
|
|
end
|
|
|
|
|
|
|
|
return php.callParserFunction( frameId, '#tag:' .. name, args )
|
|
|
|
end
|
|
|
|
|
2012-05-22 03:56:07 +00:00
|
|
|
function frame:preprocess( opt )
|
2012-07-02 07:27:18 +00:00
|
|
|
checkSelf( self, 'preprocess' )
|
|
|
|
|
2012-05-22 03:56:07 +00:00
|
|
|
local text
|
|
|
|
if type( opt ) == 'table' then
|
|
|
|
text = opt.text
|
|
|
|
else
|
|
|
|
text = opt
|
|
|
|
end
|
|
|
|
text = tostring( text )
|
|
|
|
return php.preprocess( frameId, text )
|
|
|
|
end
|
|
|
|
|
|
|
|
function frame:newParserValue( opt )
|
2012-07-02 07:27:18 +00:00
|
|
|
checkSelf( self, 'newParserValue' )
|
|
|
|
|
2012-05-22 03:56:07 +00:00
|
|
|
local text
|
|
|
|
if type( opt ) == 'table' then
|
|
|
|
text = opt.text
|
|
|
|
else
|
|
|
|
text = opt
|
|
|
|
end
|
|
|
|
|
|
|
|
return newCallbackParserValue(
|
2014-07-07 18:46:59 +00:00
|
|
|
function ()
|
2012-05-22 03:56:07 +00:00
|
|
|
return self:preprocess( text )
|
|
|
|
end
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
function frame:newTemplateParserValue( opt )
|
2012-07-02 07:27:18 +00:00
|
|
|
checkSelf( self, 'newTemplateParserValue' )
|
|
|
|
|
2012-05-22 03:56:07 +00:00
|
|
|
if type( opt ) ~= 'table' then
|
|
|
|
error( "frame:newTemplateParserValue: the first parameter must be a table" )
|
|
|
|
end
|
|
|
|
if opt.title == nil then
|
|
|
|
error( "frame:newTemplateParserValue: a title is required" )
|
|
|
|
end
|
2014-07-07 18:46:59 +00:00
|
|
|
return newCallbackParserValue(
|
2012-05-22 03:56:07 +00:00
|
|
|
function ()
|
|
|
|
return self:expandTemplate( opt )
|
|
|
|
end
|
2014-12-04 16:47:27 +00:00
|
|
|
)
|
2012-05-22 03:56:07 +00:00
|
|
|
end
|
|
|
|
|
2013-12-06 18:10:39 +00:00
|
|
|
function frame:getTitle()
|
|
|
|
checkSelf( self, 'getTitle' )
|
|
|
|
return php.getFrameTitle( frameId )
|
|
|
|
end
|
|
|
|
|
2012-12-27 22:58:39 +00:00
|
|
|
-- For backwards compat
|
2012-05-22 03:56:07 +00:00
|
|
|
function frame:argumentPairs()
|
2012-07-02 07:27:18 +00:00
|
|
|
checkSelf( self, 'argumentPairs' )
|
2012-12-27 22:58:39 +00:00
|
|
|
return pairs( self.args )
|
2012-05-22 03:56:07 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
return frame
|
|
|
|
end
|
|
|
|
|
2014-07-07 16:30:44 +00:00
|
|
|
--- Set up a cloned environment for execution of a module chunk, then execute
|
|
|
|
-- the module in that environment. This is called by the host to implement
|
|
|
|
-- {{#invoke}}.
|
|
|
|
--
|
|
|
|
-- @param chunk The module chunk
|
|
|
|
-- @param name The name of the function to be returned. Nil or false causes the entire export table to be returned
|
2019-10-01 20:30:26 +00:00
|
|
|
-- @param frame New frame to use; if nil, one will be created
|
2014-07-07 16:30:44 +00:00
|
|
|
-- @return boolean Whether the requested value was able to be returned
|
|
|
|
-- @return table|function|string The requested value, or if that was unable to be returned, the type of the value returned by the module
|
2019-10-01 20:30:26 +00:00
|
|
|
function mw.executeModule( chunk, name, frame )
|
2014-07-07 16:30:44 +00:00
|
|
|
local env = mw.clone( _G )
|
|
|
|
makePackageModule( env )
|
|
|
|
|
|
|
|
-- These are unsafe
|
|
|
|
env.mw.makeProtectedEnvFuncs = nil
|
|
|
|
env.mw.executeModule = nil
|
|
|
|
if name ~= false then -- console sets name to false when evaluating its code and nil when evaluating a module's
|
|
|
|
env.mw.getLogBuffer = nil
|
|
|
|
env.mw.clearLogBuffer = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
if allowEnvFuncs then
|
|
|
|
env.setfenv, env.getfenv = mw.makeProtectedEnvFuncs( {[_G] = true}, {} )
|
|
|
|
else
|
|
|
|
env.setfenv = nil
|
|
|
|
env.getfenv = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
env.os.date = ttlDate
|
|
|
|
env.os.time = ttlTime
|
|
|
|
|
2019-10-01 20:30:26 +00:00
|
|
|
frame = frame or newFrame( 'current', 'parent' )
|
|
|
|
env.mw.getCurrentFrame = function ()
|
|
|
|
return frame
|
|
|
|
end
|
|
|
|
|
2014-07-07 16:30:44 +00:00
|
|
|
setfenv( chunk, env )
|
|
|
|
|
|
|
|
local res = chunk()
|
|
|
|
|
|
|
|
if not name then -- catch console whether it's evaluating its own code or a module's
|
|
|
|
return true, res
|
|
|
|
end
|
|
|
|
if type(res) ~= 'table' then
|
|
|
|
return false, type(res)
|
|
|
|
end
|
|
|
|
return true, res[name]
|
|
|
|
end
|
|
|
|
|
2012-05-22 03:56:07 +00:00
|
|
|
function mw.executeFunction( chunk )
|
2019-10-22 16:38:30 +00:00
|
|
|
local getCurrentFrame = getfenv( chunk ).mw.getCurrentFrame
|
|
|
|
local frame
|
|
|
|
if getCurrentFrame then
|
|
|
|
-- Normal case
|
|
|
|
frame = getCurrentFrame()
|
|
|
|
else
|
|
|
|
-- If someone assigns a built-in method to the module's return table,
|
|
|
|
-- its env won't have mw.getCurrentFrame()
|
|
|
|
frame = newFrame( 'current', 'parent' )
|
|
|
|
end
|
2012-05-22 03:56:07 +00:00
|
|
|
|
2014-04-01 17:34:41 +00:00
|
|
|
if executeFunctionDepth == 0 then
|
|
|
|
-- math.random is defined as using C's rand(), and C's rand() uses 1 as
|
|
|
|
-- a seed if not explicitly seeded. So reseed with 1 for each top-level
|
|
|
|
-- #invoke to avoid people passing state via the RNG.
|
|
|
|
math.randomseed( 1 )
|
|
|
|
end
|
|
|
|
executeFunctionDepth = executeFunctionDepth + 1
|
|
|
|
|
2012-05-22 03:56:07 +00:00
|
|
|
local results = { chunk( frame ) }
|
2012-09-06 04:54:26 +00:00
|
|
|
|
2012-05-22 03:56:07 +00:00
|
|
|
local stringResults = {}
|
|
|
|
for i, result in ipairs( results ) do
|
|
|
|
stringResults[i] = tostring( result )
|
|
|
|
end
|
2014-04-01 17:34:41 +00:00
|
|
|
|
|
|
|
executeFunctionDepth = executeFunctionDepth - 1
|
|
|
|
|
2012-05-22 03:56:07 +00:00
|
|
|
return table.concat( stringResults )
|
|
|
|
end
|
|
|
|
|
2012-12-20 16:16:36 +00:00
|
|
|
function mw.allToString( ... )
|
|
|
|
local t = { ... }
|
2012-12-24 15:41:36 +00:00
|
|
|
for i = 1, select( '#', ... ) do
|
2012-12-20 16:16:36 +00:00
|
|
|
t[i] = tostring( t[i] )
|
|
|
|
end
|
|
|
|
return table.concat( t, '\t' )
|
|
|
|
end
|
|
|
|
|
|
|
|
function mw.log( ... )
|
|
|
|
logBuffer = logBuffer .. mw.allToString( ... ) .. '\n'
|
2012-07-14 04:23:42 +00:00
|
|
|
end
|
|
|
|
|
2014-07-09 01:20:40 +00:00
|
|
|
function mw.dumpObject( object )
|
2013-06-27 17:44:03 +00:00
|
|
|
local doneTable = {}
|
|
|
|
local doneObj = {}
|
|
|
|
local ct = {}
|
|
|
|
local function sorter( a, b )
|
|
|
|
local ta, tb = type( a ), type( b )
|
|
|
|
if ta ~= tb then
|
|
|
|
return ta < tb
|
|
|
|
end
|
|
|
|
if ta == 'string' or ta == 'number' then
|
|
|
|
return a < b
|
|
|
|
end
|
|
|
|
if ta == 'boolean' then
|
|
|
|
return tostring( a ) < tostring( b )
|
|
|
|
end
|
|
|
|
return false -- Incomparable
|
|
|
|
end
|
2014-07-09 01:20:40 +00:00
|
|
|
local function _dumpObject( object, indent, expandTable )
|
2013-06-27 17:44:03 +00:00
|
|
|
local tp = type( object )
|
|
|
|
if tp == 'number' or tp == 'nil' or tp == 'boolean' then
|
|
|
|
return tostring( object )
|
|
|
|
elseif tp == 'string' then
|
|
|
|
return string.format( "%q", object )
|
|
|
|
elseif tp == 'table' then
|
|
|
|
if not doneObj[object] then
|
|
|
|
local s = tostring( object )
|
|
|
|
if s == 'table' then
|
|
|
|
ct[tp] = ( ct[tp] or 0 ) + 1
|
|
|
|
doneObj[object] = 'table#' .. ct[tp]
|
|
|
|
else
|
|
|
|
doneObj[object] = s
|
|
|
|
doneTable[object] = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if doneTable[object] or not expandTable then
|
|
|
|
return doneObj[object]
|
|
|
|
end
|
|
|
|
doneTable[object] = true
|
|
|
|
|
|
|
|
local ret = { doneObj[object], ' {\n' }
|
|
|
|
local mt = getmetatable( object )
|
2022-05-02 10:27:35 +00:00
|
|
|
local indentString = " "
|
2013-06-27 17:44:03 +00:00
|
|
|
if mt then
|
2022-05-02 10:27:35 +00:00
|
|
|
ret[#ret + 1] = string.rep( indentString, indent + 2 )
|
2013-06-27 17:44:03 +00:00
|
|
|
ret[#ret + 1] = 'metatable = '
|
2014-07-09 01:20:40 +00:00
|
|
|
ret[#ret + 1] = _dumpObject( mt, indent + 2, false )
|
2013-06-27 17:44:03 +00:00
|
|
|
ret[#ret + 1] = "\n"
|
|
|
|
end
|
|
|
|
|
|
|
|
local doneKeys = {}
|
|
|
|
for key, value in ipairs( object ) do
|
|
|
|
doneKeys[key] = true
|
2022-05-02 10:27:35 +00:00
|
|
|
ret[#ret + 1] = string.rep( indentString, indent + 2 )
|
2014-07-09 01:20:40 +00:00
|
|
|
ret[#ret + 1] = _dumpObject( value, indent + 2, true )
|
2013-06-27 17:44:03 +00:00
|
|
|
ret[#ret + 1] = ',\n'
|
|
|
|
end
|
|
|
|
local keys = {}
|
|
|
|
for key in pairs( object ) do
|
|
|
|
if not doneKeys[key] then
|
|
|
|
keys[#keys + 1] = key
|
|
|
|
end
|
|
|
|
end
|
|
|
|
table.sort( keys, sorter )
|
|
|
|
for i = 1, #keys do
|
|
|
|
local key = keys[i]
|
2022-05-02 10:27:35 +00:00
|
|
|
ret[#ret + 1] = string.rep( indentString, indent + 2 )
|
2013-06-27 17:44:03 +00:00
|
|
|
ret[#ret + 1] = '['
|
2014-07-09 01:20:40 +00:00
|
|
|
ret[#ret + 1] = _dumpObject( key, indent + 3, false )
|
2013-06-27 17:44:03 +00:00
|
|
|
ret[#ret + 1] = '] = '
|
2014-07-09 01:20:40 +00:00
|
|
|
ret[#ret + 1] = _dumpObject( object[key], indent + 2, true )
|
2013-06-27 17:44:03 +00:00
|
|
|
ret[#ret + 1] = ",\n"
|
|
|
|
end
|
2022-05-02 10:27:35 +00:00
|
|
|
ret[#ret + 1] = string.rep( indentString, indent )
|
2013-06-27 17:44:03 +00:00
|
|
|
ret[#ret + 1] = '}'
|
|
|
|
return table.concat( ret )
|
|
|
|
else
|
|
|
|
if not doneObj[object] then
|
|
|
|
ct[tp] = ( ct[tp] or 0 ) + 1
|
|
|
|
doneObj[object] = tostring( object ) .. '#' .. ct[tp]
|
|
|
|
end
|
|
|
|
return doneObj[object]
|
|
|
|
end
|
|
|
|
end
|
2014-07-09 01:20:40 +00:00
|
|
|
return _dumpObject( object, 0, true )
|
|
|
|
end
|
|
|
|
|
|
|
|
function mw.logObject( object, prefix )
|
2013-06-27 17:44:03 +00:00
|
|
|
if prefix and prefix ~= '' then
|
|
|
|
logBuffer = logBuffer .. prefix .. ' = '
|
|
|
|
end
|
2014-07-09 01:20:40 +00:00
|
|
|
logBuffer = logBuffer .. mw.dumpObject( object ) .. '\n'
|
2013-06-27 17:44:03 +00:00
|
|
|
end
|
|
|
|
|
2012-07-14 04:23:42 +00:00
|
|
|
function mw.clearLogBuffer()
|
|
|
|
logBuffer = ''
|
|
|
|
end
|
|
|
|
|
|
|
|
function mw.getLogBuffer()
|
|
|
|
return logBuffer
|
|
|
|
end
|
|
|
|
|
2013-11-01 02:05:00 +00:00
|
|
|
function mw.isSubsting()
|
|
|
|
return php.isSubsting()
|
|
|
|
end
|
|
|
|
|
2013-02-01 20:43:32 +00:00
|
|
|
function mw.incrementExpensiveFunctionCount()
|
|
|
|
php.incrementExpensiveFunctionCount()
|
|
|
|
end
|
|
|
|
|
2016-06-16 20:20:35 +00:00
|
|
|
function mw.addWarning( text )
|
|
|
|
php.addWarning( text )
|
|
|
|
end
|
|
|
|
|
2013-02-21 21:31:23 +00:00
|
|
|
---
|
2022-09-22 07:45:47 +00:00
|
|
|
-- Wrapper for mw.loadData/loadJsonData. This creates the read-only dummy table
|
|
|
|
-- for accessing the real data.
|
2013-02-21 21:31:23 +00:00
|
|
|
--
|
|
|
|
-- @param data table Data to access
|
|
|
|
-- @param seen table|nil Table of already-seen tables.
|
2022-09-22 07:45:47 +00:00
|
|
|
-- @param name string Name of calling function
|
2013-02-21 21:31:23 +00:00
|
|
|
-- @return table
|
2022-09-22 07:45:47 +00:00
|
|
|
local function dataWrapper( data, seen, name )
|
2013-02-21 21:31:23 +00:00
|
|
|
local t = {}
|
|
|
|
seen = seen or { [data] = t }
|
|
|
|
|
|
|
|
local function pairsfunc( s, k )
|
|
|
|
k = next( data, k )
|
|
|
|
if k ~= nil then
|
|
|
|
return k, t[k]
|
|
|
|
end
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
local function ipairsfunc( s, i )
|
|
|
|
i = i + 1
|
|
|
|
if data[i] ~= nil then
|
|
|
|
return i, t[i]
|
|
|
|
end
|
|
|
|
return -- no nil to match default ipairs()
|
|
|
|
end
|
|
|
|
|
|
|
|
local mt = {
|
2015-06-28 02:38:23 +00:00
|
|
|
mw_loadData = true,
|
2013-02-21 21:31:23 +00:00
|
|
|
__index = function ( tt, k )
|
|
|
|
assert( t == tt )
|
|
|
|
local v = data[k]
|
|
|
|
if type( v ) == 'table' then
|
2022-09-22 07:45:47 +00:00
|
|
|
seen[v] = seen[v] or dataWrapper( v, seen, name )
|
2013-02-21 21:31:23 +00:00
|
|
|
return seen[v]
|
|
|
|
end
|
|
|
|
return v
|
|
|
|
end,
|
|
|
|
__newindex = function ( t, k, v )
|
2022-09-22 07:45:47 +00:00
|
|
|
error( "table from " .. name .. " is read-only", 2 )
|
2013-02-21 21:31:23 +00:00
|
|
|
end,
|
|
|
|
__pairs = function ( tt )
|
|
|
|
assert( t == tt )
|
|
|
|
return pairsfunc, t, nil
|
|
|
|
end,
|
|
|
|
__ipairs = function ( tt )
|
|
|
|
assert( t == tt )
|
|
|
|
return ipairsfunc, t, 0
|
|
|
|
end,
|
|
|
|
}
|
|
|
|
-- This is just to make setmetatable() fail
|
|
|
|
mt.__metatable = mt
|
|
|
|
|
|
|
|
return setmetatable( t, mt )
|
|
|
|
end
|
|
|
|
|
|
|
|
---
|
|
|
|
-- Validator for mw.loadData. This scans through the data looking for things
|
|
|
|
-- that are not supported, e.g. functions (which may be closures).
|
|
|
|
--
|
|
|
|
-- @param d table Data to access.
|
|
|
|
-- @param seen table|nil Table of already-seen tables.
|
|
|
|
-- @return string|nil Error message, if any
|
|
|
|
local function validateData( d, seen )
|
|
|
|
seen = seen or {}
|
|
|
|
local tp = type( d )
|
|
|
|
if tp == 'nil' or tp == 'boolean' or tp == 'number' or tp == 'string' then
|
|
|
|
return nil
|
|
|
|
elseif tp == 'table' then
|
|
|
|
if seen[d] then
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
seen[d] = true
|
|
|
|
if getmetatable( d ) ~= nil then
|
|
|
|
return "data for mw.loadData contains a table with a metatable"
|
|
|
|
end
|
|
|
|
for k, v in pairs( d ) do
|
|
|
|
if type( k ) == 'table' then
|
|
|
|
return "data for mw.loadData contains a table as a key"
|
|
|
|
end
|
|
|
|
local err = validateData( k, seen ) or validateData( v, seen )
|
|
|
|
if err then
|
|
|
|
return err
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return nil
|
|
|
|
else
|
|
|
|
return "data for mw.loadData contains unsupported data type '" .. tp .. "'"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function mw.loadData( module )
|
|
|
|
local data = loadedData[module]
|
|
|
|
if type( data ) == 'string' then
|
|
|
|
-- No point in re-validating
|
|
|
|
error( data, 2 )
|
|
|
|
end
|
|
|
|
if not data then
|
|
|
|
-- The point of this is to load big data, so don't save it in package.loaded
|
|
|
|
-- where it will have to be copied for all future modules.
|
|
|
|
local l = package.loaded[module]
|
2014-05-23 15:43:22 +00:00
|
|
|
local _
|
2014-07-07 16:30:44 +00:00
|
|
|
|
2019-10-01 20:30:26 +00:00
|
|
|
_, data = mw.executeModule( function() return require( module ) end, nil, newFrame( 'empty' ) )
|
2014-07-07 16:30:44 +00:00
|
|
|
|
2013-02-21 21:31:23 +00:00
|
|
|
package.loaded[module] = l
|
|
|
|
|
|
|
|
-- Validate data
|
|
|
|
local err
|
|
|
|
if type( data ) == 'table' then
|
|
|
|
err = validateData( data )
|
|
|
|
else
|
|
|
|
err = module .. ' returned ' .. type( data ) .. ', table expected'
|
|
|
|
end
|
|
|
|
if err then
|
|
|
|
loadedData[module] = err
|
|
|
|
error( err, 2 )
|
|
|
|
end
|
|
|
|
loadedData[module] = data
|
|
|
|
end
|
|
|
|
|
2022-09-22 07:45:47 +00:00
|
|
|
return dataWrapper( data, nil, 'mw.loadData' )
|
|
|
|
end
|
|
|
|
|
|
|
|
function mw.loadJsonData( module )
|
|
|
|
if type( module ) ~= "string" then
|
|
|
|
error( string.format( "bad argument #1 to 'mw.loadJsonData' (string expected, got %s)",
|
|
|
|
type( arg ) ), 2 )
|
|
|
|
end
|
|
|
|
local data = loadedJsonData[module]
|
|
|
|
if type( data ) == 'string' then
|
|
|
|
-- No point in re-validating
|
|
|
|
error( data, 2 )
|
|
|
|
end
|
|
|
|
if not data then
|
|
|
|
data = php.loadJsonData( module )
|
|
|
|
if type( data ) ~= "table" then
|
|
|
|
local err = module .. ' returned ' .. type( data ) .. ', table expected'
|
|
|
|
loadedJsonData[module] = err
|
|
|
|
error( err, 2 )
|
|
|
|
end
|
|
|
|
loadedJsonData[module] = data
|
|
|
|
end
|
|
|
|
|
|
|
|
return dataWrapper( data, nil, 'mw.loadJsonData' )
|
2013-02-21 21:31:23 +00:00
|
|
|
end
|
|
|
|
|
2012-04-13 10:38:12 +00:00
|
|
|
return mw
|