2012-04-13 10:38:12 +00:00
|
|
|
<?php
|
|
|
|
|
2018-02-04 18:57:59 +00:00
|
|
|
use MediaWiki\Logger\LoggerFactory;
|
|
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
use Psr\Log\NullLogger;
|
|
|
|
|
2012-04-13 10:38:12 +00:00
|
|
|
class Scribunto_LuaStandaloneEngine extends Scribunto_LuaEngine {
|
2016-05-17 14:52:05 +00:00
|
|
|
protected static $clockTick;
|
2014-08-17 21:11:26 +00:00
|
|
|
public $initialStatus;
|
2012-04-13 10:38:12 +00:00
|
|
|
|
2018-04-09 04:39:06 +00:00
|
|
|
/**
|
|
|
|
* @var Scribunto_LuaStandaloneInterpreter
|
|
|
|
*/
|
|
|
|
protected $interpreter;
|
|
|
|
|
2012-04-13 10:38:12 +00:00
|
|
|
public function load() {
|
|
|
|
parent::load();
|
|
|
|
if ( php_uname( 's' ) === 'Linux' ) {
|
|
|
|
$this->initialStatus = $this->interpreter->getStatus();
|
|
|
|
} else {
|
|
|
|
$this->initialStatus = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-25 17:53:18 +00:00
|
|
|
public function getPerformanceCharacteristics() {
|
2017-06-15 17:19:00 +00:00
|
|
|
return [
|
2013-01-25 17:53:18 +00:00
|
|
|
'phpCallsRequireSerialization' => true,
|
2017-06-15 17:19:00 +00:00
|
|
|
];
|
2013-01-25 17:53:18 +00:00
|
|
|
}
|
|
|
|
|
2018-11-09 19:31:08 +00:00
|
|
|
public function reportLimitData( ParserOutput $output ) {
|
2013-03-14 12:42:42 +00:00
|
|
|
try {
|
|
|
|
$this->load();
|
|
|
|
} catch ( Exception $e ) {
|
|
|
|
return;
|
|
|
|
}
|
2014-07-09 14:48:59 +00:00
|
|
|
if ( $this->initialStatus ) {
|
|
|
|
$status = $this->interpreter->getStatus();
|
|
|
|
$output->setLimitReportData( 'scribunto-limitreport-timeusage',
|
2017-06-15 17:19:00 +00:00
|
|
|
[
|
2014-07-09 14:48:59 +00:00
|
|
|
sprintf( "%.3f", $status['time'] / $this->getClockTick() ),
|
2018-05-30 15:31:12 +00:00
|
|
|
// Strip trailing .0s
|
|
|
|
rtrim( rtrim( sprintf( "%.3f", $this->options['cpuLimit'] ), '0' ), '.' )
|
2017-06-15 17:19:00 +00:00
|
|
|
]
|
2014-07-09 14:48:59 +00:00
|
|
|
);
|
|
|
|
$output->setLimitReportData( 'scribunto-limitreport-virtmemusage',
|
2017-06-15 17:19:00 +00:00
|
|
|
[
|
2014-07-09 14:48:59 +00:00
|
|
|
$status['vsize'],
|
|
|
|
$this->options['memoryLimit']
|
2017-06-15 17:19:00 +00:00
|
|
|
]
|
2014-07-09 14:48:59 +00:00
|
|
|
);
|
|
|
|
$output->setLimitReportData( 'scribunto-limitreport-estmemusage',
|
|
|
|
$status['vsize'] - $this->initialStatus['vsize']
|
|
|
|
);
|
2013-03-14 12:42:42 +00:00
|
|
|
}
|
2013-03-25 15:56:38 +00:00
|
|
|
$logs = $this->getLogBuffer();
|
|
|
|
if ( $logs !== '' ) {
|
2015-01-23 21:36:55 +00:00
|
|
|
$output->addModules( 'ext.scribunto.logs' );
|
2013-03-25 15:56:38 +00:00
|
|
|
$output->setLimitReportData( 'scribunto-limitreport-logs', $logs );
|
|
|
|
}
|
2013-03-14 12:42:42 +00:00
|
|
|
}
|
|
|
|
|
2018-11-09 19:31:08 +00:00
|
|
|
public function formatLimitData( $key, &$value, &$report, $isHTML, $localize ) {
|
2013-03-14 12:42:42 +00:00
|
|
|
global $wgLang;
|
|
|
|
$lang = $localize ? $wgLang : Language::factory( 'en' );
|
|
|
|
switch ( $key ) {
|
2013-03-25 15:56:38 +00:00
|
|
|
case 'scribunto-limitreport-logs':
|
|
|
|
if ( $isHTML ) {
|
|
|
|
$report .= $this->formatHtmlLogs( $value, $localize );
|
|
|
|
}
|
|
|
|
return false;
|
2013-03-14 12:42:42 +00:00
|
|
|
case 'scribunto-limitreport-virtmemusage':
|
2017-06-15 17:19:00 +00:00
|
|
|
$value = array_map( [ $lang, 'formatSize' ], $value );
|
2013-03-14 12:42:42 +00:00
|
|
|
break;
|
|
|
|
case 'scribunto-limitreport-estmemusage':
|
2018-04-09 04:39:06 +00:00
|
|
|
/** @suppress PhanTypeMismatchArgument */
|
2013-03-14 12:42:42 +00:00
|
|
|
$value = $lang->formatSize( $value );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-06-15 23:34:35 +00:00
|
|
|
/**
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2018-11-09 19:31:08 +00:00
|
|
|
protected function getClockTick() {
|
2012-04-13 10:38:12 +00:00
|
|
|
if ( self::$clockTick === null ) {
|
2018-02-12 18:35:32 +00:00
|
|
|
Wikimedia\suppressWarnings();
|
2012-04-13 10:38:12 +00:00
|
|
|
self::$clockTick = intval( shell_exec( 'getconf CLK_TCK' ) );
|
2018-02-12 18:35:32 +00:00
|
|
|
Wikimedia\restoreWarnings();
|
2012-04-13 10:38:12 +00:00
|
|
|
if ( !self::$clockTick ) {
|
|
|
|
self::$clockTick = 100;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return self::$clockTick;
|
|
|
|
}
|
|
|
|
|
2012-06-15 23:34:35 +00:00
|
|
|
/**
|
|
|
|
* @return Scribunto_LuaStandaloneInterpreter
|
|
|
|
*/
|
2018-11-09 19:31:08 +00:00
|
|
|
protected function newInterpreter() {
|
2018-02-04 18:57:59 +00:00
|
|
|
return new Scribunto_LuaStandaloneInterpreter( $this, $this->options + [
|
|
|
|
'logger' => LoggerFactory::getInstance( 'Scribunto' )
|
|
|
|
] );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
2013-03-20 16:30:26 +00:00
|
|
|
|
2014-10-09 19:49:40 +00:00
|
|
|
public function getSoftwareInfo( array &$software ) {
|
2013-03-20 16:30:26 +00:00
|
|
|
$ver = Scribunto_LuaStandaloneInterpreter::getLuaVersion( $this->options );
|
|
|
|
if ( $ver !== null ) {
|
|
|
|
if ( substr( $ver, 0, 6 ) === 'LuaJIT' ) {
|
|
|
|
$software['[http://luajit.org/ LuaJIT]'] = str_replace( 'LuaJIT ', '', $ver );
|
|
|
|
} else {
|
|
|
|
$software['[http://www.lua.org/ Lua]'] = str_replace( 'Lua ', '', $ver );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class Scribunto_LuaStandaloneInterpreter extends Scribunto_LuaInterpreter {
|
2016-05-17 14:52:05 +00:00
|
|
|
protected static $nextInterpreterId = 0;
|
2012-04-13 10:38:12 +00:00
|
|
|
|
2014-10-10 09:02:03 +00:00
|
|
|
/**
|
|
|
|
* @var Scribunto_LuaStandaloneEngine
|
|
|
|
*/
|
|
|
|
public $engine;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public $enableDebug;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var resource
|
|
|
|
*/
|
|
|
|
public $proc;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var resource
|
|
|
|
*/
|
|
|
|
public $writePipe;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var resource
|
|
|
|
*/
|
|
|
|
public $readPipe;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var ScribuntoException
|
|
|
|
*/
|
|
|
|
public $exitError;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
public $id;
|
|
|
|
|
2018-02-04 18:57:59 +00:00
|
|
|
/**
|
|
|
|
* @var LoggerInterface
|
|
|
|
*/
|
|
|
|
protected $logger;
|
|
|
|
|
2018-04-09 04:39:06 +00:00
|
|
|
/**
|
|
|
|
* @var callable[]
|
|
|
|
*/
|
|
|
|
protected $callbacks;
|
|
|
|
|
2014-10-10 09:02:03 +00:00
|
|
|
/**
|
|
|
|
* @param Scribunto_LuaStandaloneEngine $engine
|
|
|
|
* @param array $options
|
|
|
|
* @throws MWException
|
|
|
|
* @throws Scribunto_LuaInterpreterNotFoundError
|
|
|
|
* @throws ScribuntoException
|
|
|
|
*/
|
2018-11-09 19:31:08 +00:00
|
|
|
public function __construct( $engine, array $options ) {
|
2013-11-01 00:04:12 +00:00
|
|
|
$this->id = self::$nextInterpreterId++;
|
|
|
|
|
Added tests and fixed bugs
* 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
2012-04-16 04:41:08 +00:00
|
|
|
if ( $options['errorFile'] === null ) {
|
|
|
|
$options['errorFile'] = wfGetNull();
|
|
|
|
}
|
2013-03-20 16:30:26 +00:00
|
|
|
|
Added tests and fixed bugs
* 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
2012-04-16 04:41:08 +00:00
|
|
|
if ( $options['luaPath'] === null ) {
|
|
|
|
$path = false;
|
|
|
|
|
2013-03-20 16:30:26 +00:00
|
|
|
// Note, if you alter these, also alter getLuaVersion() below
|
Added tests and fixed bugs
* 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
2012-04-16 04:41:08 +00:00
|
|
|
if ( PHP_OS == 'Linux' ) {
|
|
|
|
if ( PHP_INT_SIZE == 4 ) {
|
|
|
|
$path = 'lua5_1_5_linux_32_generic/lua';
|
|
|
|
} elseif ( PHP_INT_SIZE == 8 ) {
|
|
|
|
$path = 'lua5_1_5_linux_64_generic/lua';
|
|
|
|
}
|
2012-04-24 00:47:53 +00:00
|
|
|
} elseif ( PHP_OS == 'Windows' || PHP_OS == 'WINNT' || PHP_OS == 'Win32' ) {
|
Added tests and fixed bugs
* 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
2012-04-16 04:41:08 +00:00
|
|
|
if ( PHP_INT_SIZE == 4 ) {
|
2016-02-22 17:09:18 +00:00
|
|
|
$path = 'lua5_1_5_Win32_bin/lua5.1.exe';
|
Added tests and fixed bugs
* 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
2012-04-16 04:41:08 +00:00
|
|
|
} elseif ( PHP_INT_SIZE == 8 ) {
|
2016-02-22 17:09:18 +00:00
|
|
|
$path = 'lua5_1_5_Win64_bin/lua5.1.exe';
|
Added tests and fixed bugs
* 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
2012-04-16 04:41:08 +00:00
|
|
|
}
|
2012-06-03 11:57:19 +00:00
|
|
|
} elseif ( PHP_OS == 'Darwin' ) {
|
|
|
|
$path = 'lua5_1_5_mac_lion_fat_generic/lua';
|
Added tests and fixed bugs
* 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
2012-04-16 04:41:08 +00:00
|
|
|
}
|
|
|
|
if ( $path === false ) {
|
2013-07-25 00:04:56 +00:00
|
|
|
throw new Scribunto_LuaInterpreterNotFoundError(
|
2012-04-19 07:40:56 +00:00
|
|
|
'No Lua interpreter was given in the configuration, ' .
|
|
|
|
'and no bundled binary exists for this platform.' );
|
Added tests and fixed bugs
* 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
2012-04-16 04:41:08 +00:00
|
|
|
}
|
2016-05-17 14:52:05 +00:00
|
|
|
$options['luaPath'] = __DIR__ . "/binaries/$path";
|
2013-07-25 00:04:56 +00:00
|
|
|
|
2015-06-21 04:38:39 +00:00
|
|
|
if ( !is_executable( $options['luaPath'] ) ) {
|
2016-05-17 14:52:05 +00:00
|
|
|
throw new MWException(
|
|
|
|
sprintf( 'The lua binary (%s) is not executable.', $options['luaPath'] )
|
|
|
|
);
|
2013-07-25 00:04:56 +00:00
|
|
|
}
|
Added tests and fixed bugs
* 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
2012-04-16 04:41:08 +00:00
|
|
|
}
|
|
|
|
|
2012-04-13 10:38:12 +00:00
|
|
|
$this->engine = $engine;
|
|
|
|
$this->enableDebug = !empty( $options['debug'] );
|
2019-03-21 04:16:08 +00:00
|
|
|
$this->logger = $options['logger'] ?? new NullLogger();
|
2012-04-13 10:38:12 +00:00
|
|
|
|
|
|
|
$pipes = null;
|
|
|
|
$cmd = wfEscapeShellArg(
|
2013-07-25 00:04:56 +00:00
|
|
|
$options['luaPath'],
|
2016-05-17 14:52:05 +00:00
|
|
|
__DIR__ . '/mw_main.lua',
|
|
|
|
dirname( dirname( __DIR__ ) ),
|
2018-02-06 21:07:11 +00:00
|
|
|
$this->id,
|
|
|
|
PHP_INT_SIZE
|
2013-11-01 00:04:12 +00:00
|
|
|
);
|
2012-04-13 10:38:12 +00:00
|
|
|
if ( php_uname( 's' ) == 'Linux' ) {
|
|
|
|
// Limit memory and CPU
|
|
|
|
$cmd = wfEscapeShellArg(
|
2017-02-06 19:55:24 +00:00
|
|
|
'exec', # proc_open() passes $cmd to 'sh -c' on Linux, so add an 'exec' to bypass it
|
Added tests and fixed bugs
* 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
2012-04-16 04:41:08 +00:00
|
|
|
'/bin/sh',
|
2016-05-17 14:52:05 +00:00
|
|
|
__DIR__ . '/lua_ulimit.sh',
|
2012-04-13 10:38:12 +00:00
|
|
|
$options['cpuLimit'], # soft limit (SIGXCPU)
|
|
|
|
$options['cpuLimit'] + 1, # hard limit
|
|
|
|
intval( $options['memoryLimit'] / 1024 ),
|
|
|
|
$cmd );
|
|
|
|
}
|
|
|
|
|
2012-04-24 00:47:53 +00:00
|
|
|
if ( php_uname( 's' ) == 'Windows NT' ) {
|
|
|
|
// Like the passthru() in older versions of PHP,
|
|
|
|
// PHP's invokation of cmd.exe in proc_open() is broken:
|
|
|
|
// http://news.php.net/php.internals/21796
|
|
|
|
// Unlike passthru(), it is not fixed in any PHP version,
|
|
|
|
// so we use the fix similar to one in wfShellExec()
|
|
|
|
$cmd = '"' . $cmd . '"';
|
|
|
|
}
|
2013-07-25 00:04:56 +00:00
|
|
|
|
2018-09-03 18:08:53 +00:00
|
|
|
$this->logger->debug( __METHOD__ . ": creating interpreter: $cmd\n" );
|
2012-04-13 10:38:12 +00:00
|
|
|
|
2013-07-05 14:05:12 +00:00
|
|
|
// Check whether proc_open is available before trying to call it (e.g.
|
|
|
|
// PHP's disable_functions may have removed it)
|
|
|
|
if ( !function_exists( 'proc_open' ) ) {
|
|
|
|
throw $this->engine->newException( 'scribunto-luastandalone-proc-error-proc-open' );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear the "last error", so if proc_open fails we can know any
|
|
|
|
// warning was generated by that.
|
2018-04-18 14:30:21 +00:00
|
|
|
Wikimedia\suppressWarnings();
|
2018-04-02 22:35:18 +00:00
|
|
|
trigger_error( '' );
|
2018-04-18 14:30:21 +00:00
|
|
|
Wikimedia\restoreWarnings();
|
2013-07-05 14:05:12 +00:00
|
|
|
|
2012-04-13 10:38:12 +00:00
|
|
|
$this->proc = proc_open(
|
2013-07-25 00:04:56 +00:00
|
|
|
$cmd,
|
2017-06-15 17:19:00 +00:00
|
|
|
[
|
|
|
|
[ 'pipe', 'r' ],
|
|
|
|
[ 'pipe', 'w' ],
|
|
|
|
[ 'file', $options['errorFile'], 'a' ]
|
|
|
|
],
|
2012-04-13 10:38:12 +00:00
|
|
|
$pipes );
|
|
|
|
if ( !$this->proc ) {
|
2013-07-05 14:05:12 +00:00
|
|
|
$err = error_get_last();
|
|
|
|
if ( !empty( $err['message'] ) ) {
|
|
|
|
throw $this->engine->newException( 'scribunto-luastandalone-proc-error-msg',
|
2017-06-15 17:19:00 +00:00
|
|
|
[ 'args' => [ $err['message'] ] ] );
|
2013-07-05 14:05:12 +00:00
|
|
|
} else {
|
|
|
|
throw $this->engine->newException( 'scribunto-luastandalone-proc-error' );
|
|
|
|
}
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
$this->writePipe = $pipes[0];
|
|
|
|
$this->readPipe = $pipes[1];
|
|
|
|
}
|
|
|
|
|
2018-11-09 19:31:08 +00:00
|
|
|
public function __destruct() {
|
2012-04-13 10:38:12 +00:00
|
|
|
$this->terminate();
|
|
|
|
}
|
|
|
|
|
2014-10-10 09:02:03 +00:00
|
|
|
public static function getLuaVersion( array $options ) {
|
2013-03-20 16:30:26 +00:00
|
|
|
if ( $options['luaPath'] === null ) {
|
|
|
|
// We know which versions are distributed, no need to run them.
|
|
|
|
if ( PHP_OS == 'Linux' ) {
|
|
|
|
return 'Lua 5.1.5';
|
|
|
|
} elseif ( PHP_OS == 'Windows' || PHP_OS == 'WINNT' || PHP_OS == 'Win32' ) {
|
|
|
|
return 'Lua 5.1.4';
|
|
|
|
} elseif ( PHP_OS == 'Darwin' ) {
|
|
|
|
return 'Lua 5.1.5';
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ask the interpreter what version it is, using the "-v" option.
|
|
|
|
// The output is expected to be one line, something like these:
|
2017-07-08 13:42:23 +00:00
|
|
|
// Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio
|
|
|
|
// LuaJIT 2.0.0 -- Copyright (C) 2005-2012 Mike Pall. http://luajit.org/
|
2013-03-20 16:30:26 +00:00
|
|
|
$cmd = wfEscapeShellArg( $options['luaPath'] ) . ' -v';
|
|
|
|
$handle = popen( $cmd, 'r' );
|
|
|
|
if ( $handle ) {
|
|
|
|
$ret = fgets( $handle, 80 );
|
|
|
|
pclose( $handle );
|
2015-06-21 04:38:39 +00:00
|
|
|
if ( $ret && preg_match( '/^Lua(?:JIT)? \S+/', $ret, $m ) ) {
|
2013-03-20 16:30:26 +00:00
|
|
|
return $m[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2012-04-13 10:38:12 +00:00
|
|
|
public function terminate() {
|
|
|
|
if ( $this->proc ) {
|
2018-09-03 18:08:53 +00:00
|
|
|
$this->logger->debug( __METHOD__ . ": terminating\n" );
|
2012-04-13 10:38:12 +00:00
|
|
|
proc_terminate( $this->proc );
|
|
|
|
proc_close( $this->proc );
|
|
|
|
$this->proc = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Added tests and fixed bugs
* 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
2012-04-16 04:41:08 +00:00
|
|
|
public function quit() {
|
|
|
|
if ( !$this->proc ) {
|
|
|
|
return;
|
|
|
|
}
|
2017-06-15 17:19:00 +00:00
|
|
|
$this->dispatch( [ 'op' => 'quit' ] );
|
Added tests and fixed bugs
* 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
2012-04-16 04:41:08 +00:00
|
|
|
proc_close( $this->proc );
|
|
|
|
}
|
|
|
|
|
2017-02-06 19:55:24 +00:00
|
|
|
public function testquit() {
|
|
|
|
if ( !$this->proc ) {
|
|
|
|
return;
|
|
|
|
}
|
2017-06-15 17:19:00 +00:00
|
|
|
$this->dispatch( [ 'op' => 'testquit' ] );
|
2017-02-06 19:55:24 +00:00
|
|
|
proc_close( $this->proc );
|
|
|
|
}
|
|
|
|
|
2014-10-10 09:02:03 +00:00
|
|
|
/**
|
|
|
|
* @param string $text
|
|
|
|
* @param string $chunkName
|
|
|
|
* @return Scribunto_LuaStandaloneInterpreterFunction
|
|
|
|
*/
|
2012-04-13 10:38:12 +00:00
|
|
|
public function loadString( $text, $chunkName ) {
|
2013-11-01 00:04:12 +00:00
|
|
|
$this->cleanupLuaChunks();
|
|
|
|
|
2017-06-15 17:19:00 +00:00
|
|
|
$result = $this->dispatch( [
|
2012-04-13 10:38:12 +00:00
|
|
|
'op' => 'loadString',
|
|
|
|
'text' => $text,
|
|
|
|
'chunkName' => $chunkName
|
2017-06-15 17:19:00 +00:00
|
|
|
] );
|
2013-11-01 00:04:12 +00:00
|
|
|
return new Scribunto_LuaStandaloneInterpreterFunction( $this->id, $result[1] );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
|
2019-03-21 04:17:45 +00:00
|
|
|
public function callFunction( $func, ...$args ) {
|
2015-06-26 16:37:34 +00:00
|
|
|
if ( !( $func instanceof Scribunto_LuaStandaloneInterpreterFunction ) ) {
|
2018-09-03 18:08:53 +00:00
|
|
|
throw new MWException( __METHOD__ . ': invalid function type' );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
2013-11-01 00:04:12 +00:00
|
|
|
if ( $func->interpreterId !== $this->id ) {
|
2018-09-03 18:08:53 +00:00
|
|
|
throw new MWException( __METHOD__ . ': function belongs to a different interpreter' );
|
2013-11-01 00:04:12 +00:00
|
|
|
}
|
2012-04-13 10:38:12 +00:00
|
|
|
$args = func_get_args();
|
|
|
|
unset( $args[0] );
|
|
|
|
// $args is now conveniently a 1-based array, as required by the Lua server
|
|
|
|
|
2013-11-01 00:04:12 +00:00
|
|
|
$this->cleanupLuaChunks();
|
|
|
|
|
2017-06-15 17:19:00 +00:00
|
|
|
$result = $this->dispatch( [
|
2012-04-13 10:38:12 +00:00
|
|
|
'op' => 'call',
|
|
|
|
'id' => $func->id,
|
2013-01-28 21:05:18 +00:00
|
|
|
'nargs' => count( $args ),
|
2017-06-15 17:19:00 +00:00
|
|
|
'args' => $args,
|
|
|
|
] );
|
2012-04-13 10:38:12 +00:00
|
|
|
// Convert return values to zero-based
|
|
|
|
return array_values( $result );
|
|
|
|
}
|
|
|
|
|
2012-12-13 20:50:44 +00:00
|
|
|
public function wrapPhpFunction( $callable ) {
|
|
|
|
static $uid = 0;
|
|
|
|
$id = "anonymous*" . ++$uid;
|
|
|
|
$this->callbacks[$id] = $callable;
|
2017-06-15 17:19:00 +00:00
|
|
|
$ret = $this->dispatch( [
|
2012-12-13 20:50:44 +00:00
|
|
|
'op' => 'wrapPhpFunction',
|
2017-06-15 17:19:00 +00:00
|
|
|
'id' => $id,
|
|
|
|
] );
|
2012-12-13 20:50:44 +00:00
|
|
|
return $ret[1];
|
|
|
|
}
|
|
|
|
|
2013-11-01 00:04:12 +00:00
|
|
|
public function cleanupLuaChunks() {
|
|
|
|
if ( isset( Scribunto_LuaStandaloneInterpreterFunction::$anyChunksDestroyed[$this->id] ) ) {
|
|
|
|
unset( Scribunto_LuaStandaloneInterpreterFunction::$anyChunksDestroyed[$this->id] );
|
2017-06-15 17:19:00 +00:00
|
|
|
$this->dispatch( [
|
2013-11-01 00:04:12 +00:00
|
|
|
'op' => 'cleanupChunks',
|
|
|
|
'ids' => Scribunto_LuaStandaloneInterpreterFunction::$activeChunkIds[$this->id]
|
2017-06-15 17:19:00 +00:00
|
|
|
] );
|
2013-11-01 00:04:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-24 14:45:16 +00:00
|
|
|
public function isLuaFunction( $object ) {
|
|
|
|
return $object instanceof Scribunto_LuaStandaloneInterpreterFunction;
|
|
|
|
}
|
|
|
|
|
2014-10-10 09:02:03 +00:00
|
|
|
public function registerLibrary( $name, array $functions ) {
|
2018-12-05 14:56:06 +00:00
|
|
|
// Make sure all ids are unique, even when libraries share the same name
|
|
|
|
// which is especially relevant for "mw_interface" (T211203).
|
|
|
|
static $uid = 0;
|
|
|
|
$uid++;
|
|
|
|
|
2017-06-15 17:19:00 +00:00
|
|
|
$functionIds = [];
|
2012-04-13 10:38:12 +00:00
|
|
|
foreach ( $functions as $funcName => $callback ) {
|
2018-12-05 14:56:06 +00:00
|
|
|
$id = "$name-$funcName-$uid";
|
2012-04-13 10:38:12 +00:00
|
|
|
$this->callbacks[$id] = $callback;
|
|
|
|
$functionIds[$funcName] = $id;
|
|
|
|
}
|
2017-06-15 17:19:00 +00:00
|
|
|
$this->dispatch( [
|
2012-04-13 10:38:12 +00:00
|
|
|
'op' => 'registerLibrary',
|
|
|
|
'name' => $name,
|
2017-06-15 17:19:00 +00:00
|
|
|
'functions' => $functionIds,
|
|
|
|
] );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getStatus() {
|
2017-06-15 17:19:00 +00:00
|
|
|
$result = $this->dispatch( [
|
|
|
|
'op' => 'getStatus',
|
|
|
|
] );
|
2012-04-13 10:38:12 +00:00
|
|
|
return $result[1];
|
|
|
|
}
|
|
|
|
|
2013-03-06 23:28:24 +00:00
|
|
|
public function pauseUsageTimer() {
|
|
|
|
}
|
|
|
|
|
|
|
|
public function unpauseUsageTimer() {
|
|
|
|
}
|
|
|
|
|
2013-01-28 21:05:18 +00:00
|
|
|
/**
|
|
|
|
* Fill in missing nulls in a list received from Lua
|
|
|
|
*
|
2017-09-24 17:38:14 +00:00
|
|
|
* @param array $array List received from Lua
|
|
|
|
* @param int $count Number of values that should be in the list
|
2013-01-28 21:05:18 +00:00
|
|
|
* @return array Non-sparse array
|
|
|
|
*/
|
|
|
|
private static function fixNulls( array $array, $count ) {
|
|
|
|
if ( count( $array ) === $count ) {
|
|
|
|
return $array;
|
|
|
|
} else {
|
|
|
|
return array_replace( array_fill( 1, $count, null ), $array );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-13 10:38:12 +00:00
|
|
|
protected function handleCall( $message ) {
|
2013-01-28 21:05:18 +00:00
|
|
|
$message['args'] = self::fixNulls( $message['args'], $message['nargs'] );
|
2012-04-13 10:38:12 +00:00
|
|
|
try {
|
|
|
|
$result = $this->callback( $message['id'], $message['args'] );
|
|
|
|
} catch ( Scribunto_LuaError $e ) {
|
2017-06-15 17:19:00 +00:00
|
|
|
return [
|
2012-04-13 10:38:12 +00:00
|
|
|
'op' => 'error',
|
2017-06-15 17:19:00 +00:00
|
|
|
'value' => $e->getLuaMessage(),
|
|
|
|
];
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
2012-04-16 05:43:34 +00:00
|
|
|
|
|
|
|
// Convert to a 1-based array
|
2017-12-04 15:44:16 +00:00
|
|
|
if ( $result !== null && count( $result ) ) {
|
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
|
|
|
$result = array_combine( range( 1, count( $result ) ), $result );
|
2012-05-22 03:56:07 +00:00
|
|
|
} else {
|
2017-06-15 17:19:00 +00:00
|
|
|
$result = [];
|
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-04-16 05:43:34 +00:00
|
|
|
|
2017-06-15 17:19:00 +00:00
|
|
|
return [
|
2012-04-13 10:38:12 +00:00
|
|
|
'op' => 'return',
|
2013-01-28 21:05:18 +00:00
|
|
|
'nvalues' => count( $result ),
|
2012-04-16 05:43:34 +00:00
|
|
|
'values' => $result
|
2017-06-15 17:19:00 +00:00
|
|
|
];
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
|
2014-10-10 09:02:03 +00:00
|
|
|
protected function callback( $id, array $args ) {
|
2018-06-08 07:56:03 +00:00
|
|
|
return ( $this->callbacks[$id] )( ...$args );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function handleError( $message ) {
|
2017-06-15 17:19:00 +00:00
|
|
|
$opts = [];
|
2012-04-23 11:36:13 +00:00
|
|
|
if ( preg_match( '/^(.*?):(\d+): (.*)$/', $message['value'], $m ) ) {
|
|
|
|
$opts['module'] = $m[1];
|
|
|
|
$opts['line'] = $m[2];
|
|
|
|
$message['value'] = $m[3];
|
|
|
|
}
|
|
|
|
if ( isset( $message['trace'] ) ) {
|
|
|
|
$opts['trace'] = array_values( $message['trace'] );
|
|
|
|
}
|
|
|
|
throw $this->engine->newLuaError( $message['value'], $opts );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function dispatch( $msgToLua ) {
|
|
|
|
$this->sendMessage( $msgToLua );
|
|
|
|
while ( true ) {
|
|
|
|
$msgFromLua = $this->receiveMessage();
|
|
|
|
|
|
|
|
switch ( $msgFromLua['op'] ) {
|
|
|
|
case 'return':
|
2013-01-28 21:05:18 +00:00
|
|
|
return self::fixNulls( $msgFromLua['values'], $msgFromLua['nvalues'] );
|
2012-04-13 10:38:12 +00:00
|
|
|
case 'call':
|
|
|
|
$msgToLua = $this->handleCall( $msgFromLua );
|
|
|
|
$this->sendMessage( $msgToLua );
|
|
|
|
break;
|
|
|
|
case 'error':
|
|
|
|
$this->handleError( $msgFromLua );
|
|
|
|
return; // not reached
|
|
|
|
default:
|
2018-09-03 18:08:53 +00:00
|
|
|
$this->logger->error( __METHOD__ . ": invalid response op \"{$msgFromLua['op']}\"\n" );
|
2012-04-23 11:36:13 +00:00
|
|
|
throw $this->engine->newException( 'scribunto-luastandalone-decode-error' );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function sendMessage( $msg ) {
|
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
|
|
|
$this->debug( "TX ==> {$msg['op']}" );
|
2012-04-13 10:38:12 +00:00
|
|
|
$this->checkValid();
|
|
|
|
// Send the message
|
|
|
|
$encMsg = $this->encodeMessage( $msg );
|
|
|
|
if ( !fwrite( $this->writePipe, $encMsg ) ) {
|
|
|
|
// Write error, probably the process has terminated
|
2015-05-25 16:31:51 +00:00
|
|
|
// If it has, handleIOError() will throw. If not, throw an exception ourselves.
|
|
|
|
$this->handleIOError();
|
2012-04-23 11:36:13 +00:00
|
|
|
throw $this->engine->newException( 'scribunto-luastandalone-write-error' );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function receiveMessage() {
|
|
|
|
$this->checkValid();
|
|
|
|
// Read the header
|
|
|
|
$header = fread( $this->readPipe, 16 );
|
|
|
|
if ( strlen( $header ) !== 16 ) {
|
2015-05-25 16:31:51 +00:00
|
|
|
$this->handleIOError();
|
2012-04-23 11:36:13 +00:00
|
|
|
throw $this->engine->newException( 'scribunto-luastandalone-read-error' );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
$length = $this->decodeHeader( $header );
|
|
|
|
|
|
|
|
// Read the reply body
|
2012-05-22 03:56:07 +00:00
|
|
|
$body = '';
|
|
|
|
$lengthRemaining = $length;
|
|
|
|
while ( $lengthRemaining ) {
|
|
|
|
$buffer = fread( $this->readPipe, $lengthRemaining );
|
|
|
|
if ( $buffer === false || feof( $this->readPipe ) ) {
|
2015-05-25 16:31:51 +00:00
|
|
|
$this->handleIOError();
|
2012-05-22 03:56:07 +00:00
|
|
|
throw $this->engine->newException( 'scribunto-luastandalone-read-error' );
|
|
|
|
}
|
|
|
|
$body .= $buffer;
|
|
|
|
$lengthRemaining -= strlen( $buffer );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
2017-06-15 17:19:00 +00:00
|
|
|
$body = strtr( $body, [
|
2013-03-28 18:29:47 +00:00
|
|
|
'\\r' => "\r",
|
|
|
|
'\\n' => "\n",
|
|
|
|
'\\\\' => '\\',
|
2017-06-15 17:19:00 +00:00
|
|
|
] );
|
2012-04-13 10:38:12 +00:00
|
|
|
$msg = unserialize( $body );
|
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
|
|
|
$this->debug( "RX <== {$msg['op']}" );
|
2012-04-13 10:38:12 +00:00
|
|
|
return $msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function encodeMessage( $message ) {
|
|
|
|
$serialized = $this->encodeLuaVar( $message );
|
|
|
|
$length = strlen( $serialized );
|
|
|
|
$check = $length * 2 - 1;
|
|
|
|
|
|
|
|
return sprintf( '%08x%08x%s', $length, $check, $serialized );
|
|
|
|
}
|
|
|
|
|
2018-04-09 04:39:06 +00:00
|
|
|
/**
|
|
|
|
* @param mixed $var
|
|
|
|
* @param int $level
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
* @throws MWException
|
|
|
|
*/
|
2012-04-13 10:38:12 +00:00
|
|
|
protected function encodeLuaVar( $var, $level = 0 ) {
|
|
|
|
if ( $level > 100 ) {
|
2018-09-03 18:08:53 +00:00
|
|
|
throw new MWException( __METHOD__ . ': recursion depth limit exceeded' );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
$type = gettype( $var );
|
|
|
|
switch ( $type ) {
|
|
|
|
case 'boolean':
|
|
|
|
return $var ? 'true' : 'false';
|
|
|
|
case 'integer':
|
|
|
|
return $var;
|
|
|
|
case 'double':
|
|
|
|
if ( !is_finite( $var ) ) {
|
2012-12-13 18:37:09 +00:00
|
|
|
if ( is_nan( $var ) ) {
|
|
|
|
return '(0/0)';
|
|
|
|
}
|
|
|
|
if ( $var === INF ) {
|
|
|
|
return '(1/0)';
|
|
|
|
}
|
|
|
|
if ( $var === -INF ) {
|
|
|
|
return '(-1/0)';
|
|
|
|
}
|
2018-09-03 18:08:53 +00:00
|
|
|
throw new MWException( __METHOD__ . ': cannot convert non-finite number' );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
2018-02-06 21:07:11 +00:00
|
|
|
return sprintf( '%.17g', $var );
|
2012-04-13 10:38:12 +00:00
|
|
|
case 'string':
|
|
|
|
return '"' .
|
2017-06-15 17:19:00 +00:00
|
|
|
strtr( $var, [
|
2012-04-13 10:38:12 +00:00
|
|
|
'"' => '\\"',
|
|
|
|
'\\' => '\\\\',
|
|
|
|
"\n" => '\\n',
|
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
|
|
|
"\r" => '\\r',
|
2012-04-13 10:38:12 +00:00
|
|
|
"\000" => '\\000',
|
2017-06-15 17:19:00 +00:00
|
|
|
] ) .
|
2012-04-13 10:38:12 +00:00
|
|
|
'"';
|
|
|
|
case 'array':
|
|
|
|
$s = '{';
|
|
|
|
foreach ( $var as $key => $element ) {
|
Added tests and fixed bugs
* 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
2012-04-16 04:41:08 +00:00
|
|
|
if ( $s !== '{' ) {
|
|
|
|
$s .= ',';
|
|
|
|
}
|
2018-02-06 21:03:11 +00:00
|
|
|
|
|
|
|
// Lua's number type can't represent most integers beyond 2**53, so stringify such keys
|
|
|
|
if ( is_int( $key ) && ( $key > 9007199254740992 || $key < -9007199254740992 ) ) {
|
|
|
|
$key = sprintf( '%d', $key );
|
|
|
|
}
|
|
|
|
|
2012-04-13 10:38:12 +00:00
|
|
|
$s .= '[' . $this->encodeLuaVar( $key, $level + 1 ) . ']' .
|
Added tests and fixed bugs
* 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
2012-04-16 04:41:08 +00:00
|
|
|
'=' . $this->encodeLuaVar( $element, $level + 1 );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
$s .= '}';
|
|
|
|
return $s;
|
|
|
|
case 'object':
|
2013-11-01 00:04:12 +00:00
|
|
|
if ( !( $var instanceof Scribunto_LuaStandaloneInterpreterFunction ) ) {
|
2018-09-03 18:08:53 +00:00
|
|
|
throw new MWException( __METHOD__ . ': unable to convert object of type ' .
|
2012-04-13 10:38:12 +00:00
|
|
|
get_class( $var ) );
|
2013-11-01 00:04:12 +00:00
|
|
|
} elseif ( $var->interpreterId !== $this->id ) {
|
2016-05-17 14:52:05 +00:00
|
|
|
throw new MWException(
|
2018-09-03 18:08:53 +00:00
|
|
|
__METHOD__ . ': unable to convert function belonging to a different interpreter'
|
2016-05-17 14:52:05 +00:00
|
|
|
);
|
2013-11-01 00:04:12 +00:00
|
|
|
} else {
|
2018-09-03 18:08:53 +00:00
|
|
|
return 'chunks[' . intval( $var->id ) . ']';
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
case 'resource':
|
2018-09-03 18:08:53 +00:00
|
|
|
throw new MWException( __METHOD__ . ': unable to convert resource' );
|
2012-04-13 10:38:12 +00:00
|
|
|
case 'NULL':
|
|
|
|
return 'nil';
|
|
|
|
default:
|
2018-09-03 18:08:53 +00:00
|
|
|
throw new MWException( __METHOD__ . ': unable to convert variable of unknown type' );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function decodeHeader( $header ) {
|
|
|
|
$length = substr( $header, 0, 8 );
|
|
|
|
$check = substr( $header, 8, 8 );
|
|
|
|
if ( !preg_match( '/^[0-9a-f]+$/', $length ) || !preg_match( '/^[0-9a-f]+$/', $check ) ) {
|
2012-04-23 11:36:13 +00:00
|
|
|
throw $this->engine->newException( 'scribunto-luastandalone-decode-error' );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
$length = hexdec( $length );
|
|
|
|
$check = hexdec( $check );
|
|
|
|
if ( $length * 2 - 1 !== $check ) {
|
2012-04-23 11:36:13 +00:00
|
|
|
throw $this->engine->newException( 'scribunto-luastandalone-decode-error' );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
return $length;
|
|
|
|
}
|
|
|
|
|
2014-10-10 09:02:03 +00:00
|
|
|
/**
|
|
|
|
* @throws ScribuntoException
|
|
|
|
*/
|
2012-04-13 10:38:12 +00:00
|
|
|
protected function checkValid() {
|
|
|
|
if ( !$this->proc ) {
|
2018-02-04 18:57:59 +00:00
|
|
|
$this->logger->error( __METHOD__ . ": process already terminated\n" );
|
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
|
|
|
if ( $this->exitError ) {
|
|
|
|
throw $this->exitError;
|
|
|
|
} else {
|
2012-04-23 11:36:13 +00:00
|
|
|
throw $this->engine->newException( 'scribunto-luastandalone-gone' );
|
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-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-10 09:02:03 +00:00
|
|
|
/**
|
|
|
|
* @throws ScribuntoException
|
|
|
|
*/
|
2015-05-25 16:31:51 +00:00
|
|
|
protected function handleIOError() {
|
2012-04-13 10:38:12 +00:00
|
|
|
$this->checkValid();
|
2017-02-06 19:55:24 +00:00
|
|
|
|
|
|
|
// Terminate, fetch the status, then close. proc_close()'s return
|
|
|
|
// value isn't helpful here because there's no way to differentiate a
|
|
|
|
// signal-kill from a normal exit.
|
2015-05-25 16:31:51 +00:00
|
|
|
proc_terminate( $this->proc );
|
2017-02-06 19:55:24 +00:00
|
|
|
while ( true ) {
|
|
|
|
$status = proc_get_status( $this->proc );
|
|
|
|
if ( $status === false ) {
|
|
|
|
// WTF? Let the caller throw an appropriate error.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( !$status['running'] ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
usleep( 10000 ); // Give the killed process a chance to be scheduled
|
|
|
|
}
|
|
|
|
proc_close( $this->proc );
|
2015-05-25 16:31:51 +00:00
|
|
|
$this->proc = false;
|
2017-02-06 19:55:24 +00:00
|
|
|
|
|
|
|
// proc_open() sometimes uses a shell, check for shell-style signal reporting.
|
|
|
|
if ( !$status['signaled'] && ( $status['exitcode'] & 0x80 ) === 0x80 ) {
|
|
|
|
$status['signaled'] = true;
|
|
|
|
$status['termsig'] = $status['exitcode'] - 128;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( $status['signaled'] ) {
|
|
|
|
if ( defined( 'SIGXCPU' ) && $status['termsig'] === SIGXCPU ) {
|
2012-04-23 11:36:13 +00:00
|
|
|
$this->exitError = $this->engine->newException( 'scribunto-common-timeout' );
|
2012-04-13 10:38:12 +00:00
|
|
|
} else {
|
2015-05-25 16:31:51 +00:00
|
|
|
$this->exitError = $this->engine->newException( 'scribunto-luastandalone-signal',
|
2017-06-15 17:19:00 +00:00
|
|
|
[ 'args' => [ $status['termsig'] ] ] );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
2015-05-25 16:31:51 +00:00
|
|
|
} else {
|
|
|
|
$this->exitError = $this->engine->newException( 'scribunto-luastandalone-exited',
|
2017-06-15 17:19:00 +00:00
|
|
|
[ 'args' => [ $status['exitcode'] ] ] );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
2015-05-25 16:31:51 +00:00
|
|
|
throw $this->exitError;
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function debug( $msg ) {
|
|
|
|
if ( $this->enableDebug ) {
|
2018-02-04 18:57:59 +00:00
|
|
|
$this->logger->debug( "Lua: $msg\n" );
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Scribunto_LuaStandaloneInterpreterFunction {
|
2017-06-15 17:19:00 +00:00
|
|
|
public static $anyChunksDestroyed = [];
|
|
|
|
public static $activeChunkIds = [];
|
2012-04-13 10:38:12 +00:00
|
|
|
|
2014-10-10 09:02:03 +00:00
|
|
|
/**
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
public $interpreterId;
|
2013-11-01 00:04:12 +00:00
|
|
|
|
2014-10-10 09:02:03 +00:00
|
|
|
/**
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
public $id;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param int $interpreterId
|
|
|
|
* @param int $id
|
|
|
|
*/
|
2018-11-09 19:31:08 +00:00
|
|
|
public function __construct( $interpreterId, $id ) {
|
2013-11-01 00:04:12 +00:00
|
|
|
$this->interpreterId = $interpreterId;
|
2012-04-13 10:38:12 +00:00
|
|
|
$this->id = $id;
|
2013-11-01 00:04:12 +00:00
|
|
|
$this->incrementRefCount();
|
|
|
|
}
|
|
|
|
|
2018-11-09 19:31:08 +00:00
|
|
|
public function __clone() {
|
2013-11-01 00:04:12 +00:00
|
|
|
$this->incrementRefCount();
|
|
|
|
}
|
|
|
|
|
2018-11-09 19:31:08 +00:00
|
|
|
public function __wakeup() {
|
2013-11-01 00:04:12 +00:00
|
|
|
$this->incrementRefCount();
|
|
|
|
}
|
|
|
|
|
2018-11-09 19:31:08 +00:00
|
|
|
public function __destruct() {
|
2013-11-01 00:04:12 +00:00
|
|
|
$this->decrementRefCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
private function incrementRefCount() {
|
|
|
|
if ( !isset( self::$activeChunkIds[$this->interpreterId] ) ) {
|
2017-06-15 17:19:00 +00:00
|
|
|
self::$activeChunkIds[$this->interpreterId] = [ $this->id => 1 ];
|
2013-11-01 00:04:12 +00:00
|
|
|
} elseif ( !isset( self::$activeChunkIds[$this->interpreterId][$this->id] ) ) {
|
|
|
|
self::$activeChunkIds[$this->interpreterId][$this->id] = 1;
|
|
|
|
} else {
|
|
|
|
self::$activeChunkIds[$this->interpreterId][$this->id]++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function decrementRefCount() {
|
|
|
|
if ( isset( self::$activeChunkIds[$this->interpreterId][$this->id] ) ) {
|
|
|
|
if ( --self::$activeChunkIds[$this->interpreterId][$this->id] <= 0 ) {
|
|
|
|
unset( self::$activeChunkIds[$this->interpreterId][$this->id] );
|
|
|
|
self::$anyChunksDestroyed[$this->interpreterId] = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self::$anyChunksDestroyed[$this->interpreterId] = true;
|
|
|
|
}
|
2012-04-13 10:38:12 +00:00
|
|
|
}
|
|
|
|
}
|