14 'phpCallsRequireSerialization' =>
false,
31 $versions = LuaSandbox::getVersionInfo();
32 $software[
'[https://www.mediawiki.org/wiki/LuaSandbox LuaSandbox]'] =
33 $versions[
'LuaSandbox'];
34 $software[
'[http://www.lua.org/ Lua]'] = str_replace(
'Lua ',
'', $versions[
'Lua'] );
35 if ( isset( $versions[
'LuaJIT'] ) ) {
36 $software[
'[http://luajit.org/ LuaJIT]'] = str_replace(
'LuaJIT ',
'', $versions[
'LuaJIT'] );
42 switch ( $resource ) {
44 return $this->interpreter->getPeakMemoryUsage();
46 return $this->interpreter->getCPUUsage();
56 $t = $this->interpreter->getCPUUsage();
57 $ret[
'scribunto-limitreport-timeusage'] = [
58 sprintf(
"%.3f",
$t ),
59 sprintf(
"%.3f", $this->options[
'cpuLimit'] )
61 $ret[
'scribunto-limitreport-memusage'] = [
62 $this->interpreter->getPeakMemoryUsage(),
63 $this->options[
'memoryLimit'],
68 $ret[
'scribunto-limitreport-logs'] = $logs;
75 $percentProfile = $this->interpreter->getProfilerFunctionReport(
78 if ( !count( $percentProfile ) ) {
81 $timeProfile = $this->interpreter->getProfilerFunctionReport(
86 $cumulativePercent = 0;
87 $num = $otherTime = $otherPercent = 0;
88 foreach ( $percentProfile as $name => $percent ) {
89 $time = $timeProfile[$name] * 1000;
91 if ( $cumulativePercent <= 99 && $num <= 10 ) {
93 if ( preg_match(
'/^<mw.lua:(\d+)>$/', $name, $m ) ) {
95 if ( preg_match(
'/^\s*(local\s+)?function ([a-zA-Z0-9_.]*)/',
$line, $m ) ) {
96 $name = $m[2] .
' ' . $name;
99 $lines[] = [ $name, sprintf(
'%.0f', $time ), sprintf(
'%.1f', $percent ) ];
102 $otherPercent += $percent;
104 $cumulativePercent += $percent;
107 $lines[] = [
'[others]', sprintf(
'%.0f', $otherTime ), sprintf(
'%.1f', $otherPercent ) ];
109 $ret[
'scribunto-limitreport-profile'] =
$lines;
115 foreach ( $data as $k => $v ) {
118 if ( isset( $data[
'scribunto-limitreport-logs'] ) ) {
125 $lang = $localize ?
$wgLang : Language::factory(
'en' );
127 case 'scribunto-limitreport-logs':
133 case 'scribunto-limitreport-memusage':
134 $value = array_map( [
$lang,
'formatSize' ], $value );
138 if ( $key !==
'scribunto-limitreport-profile' ) {
142 $keyMsg =
wfMessage(
'scribunto-limitreport-profile' );
143 $msMsg =
wfMessage(
'scribunto-limitreport-profile-ms' );
144 $percentMsg =
wfMessage(
'scribunto-limitreport-profile-percent' );
146 $keyMsg->inLanguage(
'en' )->useDatabase(
false );
147 $msMsg->inLanguage(
'en' )->useDatabase(
false );
148 $percentMsg->inLanguage(
'en' )->useDatabase(
false );
154 $percentMsg->exists();
157 $report .= Html::openElement(
'tr' ) .
158 Html::rawElement(
'th', [
'colspan' => 2 ], $keyMsg->parse() ) .
159 Html::closeElement(
'tr' ) .
160 Html::openElement(
'tr' ) .
161 Html::openElement(
'td', [
'colspan' => 2 ] ) .
162 Html::openElement(
'table' );
163 foreach ( $value as
$line ) {
166 if ( preg_match(
'/^(.*?) *<([^<>]+):(\d+)>$/', $name, $m ) ) {
168 $title = Title::newFromText( $m[2] );
172 $location = htmlspecialchars(
"<{$m[2]}:{$m[3]}>" );
176 $ms->params(
$line[1] );
177 $pct = clone $percentMsg;
178 $pct->params(
$line[2] );
179 $report .= Html::openElement(
'tr' ) .
180 Html::element(
'td',
null, $name ) .
181 Html::rawElement(
'td',
null, $location ) .
182 Html::rawElement(
'td', [
'align' =>
'right' ], $ms->parse() ) .
183 Html::rawElement(
'td', [
'align' =>
'right' ], $pct->parse() ) .
184 Html::closeElement(
'tr' );
186 $report .= Html::closeElement(
'table' ) .
187 Html::closeElement(
'td' ) .
188 Html::closeElement(
'tr' );
190 $report .= $keyMsg->text() .
":\n";
191 foreach ( $value as
$line ) {
193 $ms->params(
$line[1] );
194 $pct = clone $percentMsg;
195 $pct->params(
$line[2] );
196 $report .= sprintf(
" %-59s %11s %11s\n",
$line[0], $ms->text(), $pct->text() );
204 if ( !isset( $this->lineCache[
'mw.lua'] ) ) {
205 $this->lineCache[
'mw.lua'] = file( $this->
getLuaLibDir() .
'/mw.lua' );
207 return $this->lineCache[
'mw.lua'][$lineNum - 1];
241 if ( !extension_loaded(
'luasandbox' ) ) {
243 'The luasandbox extension is not present, this engine cannot be used.' );
246 if ( !is_callable(
'LuaSandbox::getVersionInfo' ) ) {
248 'The luasandbox extension is too old (version 1.6+ is required), ' .
249 'this engine cannot be used.'
258 $this->sandbox =
new LuaSandbox;
259 $this->sandbox->setMemoryLimit( $options[
'memoryLimit'] );
260 $this->sandbox->setCPULimit( $options[
'cpuLimit'] );
261 if ( !isset( $options[
'profilerPeriod'] ) ) {
262 $options[
'profilerPeriod'] = 0.02;
264 if ( $options[
'profilerPeriod'] ) {
265 $this->profilerEnabled =
true;
266 $this->sandbox->enableProfiler( $options[
'profilerPeriod'] );
272 if ( isset( $e->luaTrace ) ) {
273 $opts[
'trace'] = $e->luaTrace;
275 $message = $e->getMessage();
276 if ( preg_match(
'/^(.*?):(\d+): (.*)$/', $message, $m ) ) {
277 $opts[
'module'] = $m[1];
278 $opts[
'line'] = $m[2];
281 return $this->engine->newLuaError( $message, $opts );
292 return $this->sandbox->loadString( $text, $chunkName );
293 }
catch ( LuaSandboxError $e ) {
300 foreach ( $functions as $funcName => $callback ) {
301 $realLibrary[$funcName] = [
305 $this->sandbox->registerLibrary( $name, $realLibrary );
307 # TODO: replace this with
308 # $this->sandbox->registerVirtualLibrary(
309 # $name, [ $this, 'callback' ], $functions );
314 $ret = $func->call( ...
$args );
315 if ( $ret ===
false ) {
320 __METHOD__ .
': LuaSandboxFunction::call returned false' );
323 }
catch ( LuaSandboxTimeoutError $e ) {
324 throw $this->engine->newException(
'scribunto-common-timeout' );
325 }
catch ( LuaSandboxError $e ) {
331 return $this->sandbox->wrapPhpFunction( $callable );
335 return $object instanceof LuaSandboxFunction;
339 return $this->sandbox->getPeakMemoryUsage();
343 return $this->sandbox->getCPUUsage();
347 if ( $this->profilerEnabled ) {
351 self::SAMPLES => LuaSandbox::SAMPLES,
352 self::SECONDS => LuaSandbox::SECONDS,
353 self::PERCENT => LuaSandbox::PERCENT,
356 return $this->sandbox->getProfilerFunctionReport( $unitsMap[$units] );
363 $this->sandbox->pauseUsageTimer();
367 $this->sandbox->unpauseUsageTimer();
391 return ( $this->callback )( ...$args );
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
static link( $target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
setLimitReportData( $key, $value)
Sets parser limit report data for a key.
formatHtmlLogs( $logs, $localize)
Format the logged data for HTML output.
load()
Initialise the interpreter and the base environment.
getLuaLibDir()
Return the base path for Lua modules.
getLogBuffer()
Get data logged by modules.
__call( $funcName, $args)
We use __call with a variable function name so that LuaSandbox will be able to return a meaningful fu...
getPerformanceCharacteristics()
Get performance characteristics of the Lua engine/interpreter.
Scribunto_LuaSandboxInterpreter $interpreter
newInterpreter()
Create a new interpreter object.
getResourceUsage( $resource)
Get CPU and memory usage information, if the script engine provides it.
reportLimitData(ParserOutput $output)
Add limit report data to a ParserOutput object.
getSoftwareInfo(array &$software)
Get software information for Special:Version.
formatLimitData( $key, &$value, &$report, $isHTML, $localize)
Format limit report data.
registerLibrary( $name, array $functions)
Register a library of functions.
static checkLuaSandboxVersion()
Check that php-luasandbox is available and of a recent-enough version.
wrapPhpFunction( $callable)
Wrap a PHP callable as a Lua function, which can be passed back into Lua.
isLuaFunction( $object)
Test whether an object is a Lua function.
loadString( $text, $chunkName)
callFunction( $func,... $args)
Call a Lua function.
pauseUsageTimer()
Pause CPU usage and limits.
getProfilerFunctionReport( $units)
convertSandboxError(LuaSandboxError $e)
Scribunto_LuaEngine $engine
__construct( $engine, array $options)
unpauseUsageTimer()
Unpause CPU usage and limits.
hasContentModel( $id)
Convenience method for checking a title's content model name.
if(!isset( $args[0])) $lang