MediaWiki  1.34.0
LuaCommon.php
Go to the documentation of this file.
1 <?php
2 
3 use UtfNormal\Validator;
4 use Wikimedia\ScopedCallback;
5 
6 abstract class Scribunto_LuaEngine extends ScribuntoEngineBase {
11  protected static $libraryClasses = [
12  'mw.site' => 'Scribunto_LuaSiteLibrary',
13  'mw.uri' => 'Scribunto_LuaUriLibrary',
14  'mw.ustring' => 'Scribunto_LuaUstringLibrary',
15  'mw.language' => 'Scribunto_LuaLanguageLibrary',
16  'mw.message' => 'Scribunto_LuaMessageLibrary',
17  'mw.title' => 'Scribunto_LuaTitleLibrary',
18  'mw.text' => 'Scribunto_LuaTextLibrary',
19  'mw.html' => 'Scribunto_LuaHtmlLibrary',
20  'mw.hash' => 'Scribunto_LuaHashLibrary',
21  ];
22 
28  protected static $libraryPaths = [
29  '.',
30  'luabit',
31  'ustring',
32  ];
33 
34  protected $loaded = false;
35 
39  protected $interpreter;
40 
44  protected $mw;
45 
49  protected $currentFrames = [];
50  protected $expandCache = [];
51  protected $availableLibraries = [];
52 
53  const MAX_EXPAND_CACHE_SIZE = 100;
54 
62  public static function newAutodetectEngine( array $options ) {
63  global $wgScribuntoEngineConf;
64  $engine = 'luastandalone';
65  try {
67  $engine = 'luasandbox';
69  // pass
71  // pass
72  }
73 
74  unset( $options['factory'] );
75 
76  return Scribunto::newEngine( $options + $wgScribuntoEngineConf[$engine] );
77  }
78 
83  abstract protected function newInterpreter();
84 
90  protected function newModule( $text, $chunkName ) {
91  return new Scribunto_LuaModule( $this, $text, $chunkName );
92  }
93 
99  public function newLuaError( $message, $params = [] ) {
100  return new Scribunto_LuaError( $message, $this->getDefaultExceptionParams() + $params );
101  }
102 
103  public function destroy() {
104  // Break reference cycles
105  $this->interpreter = null;
106  $this->mw = null;
107  $this->expandCache = null;
108  parent::destroy();
109  }
110 
114  public function load() {
115  if ( $this->loaded ) {
116  return;
117  }
118  $this->loaded = true;
119 
120  try {
121  $this->interpreter = $this->newInterpreter();
122 
123  $funcs = [
124  'loadPackage',
125  'loadPHPLibrary',
126  'frameExists',
127  'newChildFrame',
128  'getExpandedArgument',
129  'getAllExpandedArguments',
130  'expandTemplate',
131  'callParserFunction',
132  'preprocess',
133  'incrementExpensiveFunctionCount',
134  'isSubsting',
135  'getFrameTitle',
136  'setTTL',
137  'addWarning',
138  ];
139 
140  $lib = [];
141  foreach ( $funcs as $name ) {
142  $lib[$name] = [ $this, $name ];
143  }
144 
145  $this->registerInterface( 'mwInit.lua', [] );
146  $this->mw = $this->registerInterface( 'mw.lua', $lib,
147  [ 'allowEnvFuncs' => $this->options['allowEnvFuncs'] ] );
148 
149  $this->availableLibraries = $this->getLibraries( 'lua', self::$libraryClasses );
150  foreach ( $this->availableLibraries as $name => $def ) {
151  $this->instantiatePHPLibrary( $name, $def, false );
152  }
153  } catch ( Exception $ex ) {
154  $this->loaded = false;
155  $this->interpreter = null;
156  throw $ex;
157  }
158  }
159 
175  public function registerInterface( $moduleFileName, $interfaceFuncs, $setupOptions = [] ) {
176  $this->interpreter->registerLibrary( 'mw_interface', $interfaceFuncs );
177  $moduleFileName = $this->normalizeModuleFileName( $moduleFileName );
178  $package = $this->loadLibraryFromFile( $moduleFileName );
179  if ( !empty( $package['setupInterface'] ) ) {
180  $this->interpreter->callFunction( $package['setupInterface'], $setupOptions );
181  }
182  return $package;
183  }
184 
189  public function getLuaLibDir() {
190  return __DIR__ . '/lualib';
191  }
192 
201  protected function normalizeModuleFileName( $fileName ) {
202  if ( !preg_match( '<^(?:[a-zA-Z]:)?' . preg_quote( DIRECTORY_SEPARATOR ) . '>', $fileName ) ) {
203  $fileName = "{$this->getLuaLibDir()}/{$fileName}";
204  }
205  return $fileName;
206  }
207 
217  abstract public function getPerformanceCharacteristics();
218 
223  public function getInterpreter() {
224  $this->load();
225  return $this->interpreter;
226  }
227 
235  private function setupCurrentFrames( PPFrame $frame = null ) {
236  if ( !$frame ) {
237  $frame = $this->getParser()->getPreprocessor()->newFrame();
238  }
239 
240  $oldFrames = $this->currentFrames;
241  $oldExpandCache = $this->expandCache;
242  $this->currentFrames = [
243  'current' => $frame,
244  'parent' => $frame->parent ?? null,
245  ];
246  $this->expandCache = [];
247 
248  return new ScopedCallback( function () use ( $oldFrames, $oldExpandCache ) {
249  $this->currentFrames = $oldFrames;
250  $this->expandCache = $oldExpandCache;
251  } );
252  }
253 
262  public function executeModule( $chunk, $functionName, $frame ) {
263  $resetFrames = null;
264  if ( !$this->currentFrames || !isset( $this->currentFrames['current'] ) ) {
265  // Only reset frames if there isn't already current frame
266  // $resetFrames is a ScopedCallback, so it has a purpose even though it appears unused.
267  $resetFrames = $this->setupCurrentFrames( $frame );
268  }
269 
270  $retval = $this->getInterpreter()->callFunction(
271  $this->mw['executeModule'], $chunk, $functionName
272  );
273  if ( !$retval[0] ) {
274  // If we get here, it means we asked for an element from the table the module returned,
275  // but it returned something other than a table. In this case, $retval[1] contains the type
276  // of what it did returned, instead of the value we asked for.
277  throw $this->newException(
278  'scribunto-lua-notarrayreturn', [ 'args' => [ $retval[1] ] ]
279  );
280  }
281  return $retval[1];
282  }
283 
290  public function executeFunctionChunk( $chunk, $frame ) {
291  // $resetFrames is a ScopedCallback, so it has a purpose even though it appears unused.
292  $resetFrames = $this->setupCurrentFrames( $frame );
293 
294  return $this->getInterpreter()->callFunction(
295  $this->mw['executeFunction'],
296  $chunk );
297  }
298 
303  protected function getLogBuffer() {
304  if ( !$this->loaded ) {
305  return '';
306  }
307  try {
308  $log = $this->getInterpreter()->callFunction( $this->mw['getLogBuffer'] );
309  return $log[0];
310  } catch ( ScribuntoException $ex ) {
311  // Probably time expired, ignore it.
312  return '';
313  }
314  }
315 
322  protected function formatHtmlLogs( $logs, $localize ) {
323  $keyMsg = wfMessage( 'scribunto-limitreport-logs' );
324  if ( !$localize ) {
325  $keyMsg->inLanguage( 'en' )->useDatabase( false );
326  }
327  return Html::openElement( 'tr' ) .
328  Html::rawElement( 'th', [ 'colspan' => 2 ], $keyMsg->parse() ) .
329  Html::closeElement( 'tr' ) .
330  Html::openElement( 'tr' ) .
331  Html::openElement( 'td', [ 'colspan' => 2 ] ) .
332  Html::openElement( 'div', [ 'class' => 'mw-collapsible mw-collapsed' ] ) .
333  Html::element( 'pre', [ 'class' => 'scribunto-limitreport-logs' ], $logs ) .
334  Html::closeElement( 'div' ) .
335  Html::closeElement( 'td' ) .
336  Html::closeElement( 'tr' );
337  }
338 
344  protected function loadLibraryFromFile( $fileName ) {
345  static $cache = null;
346 
347  if ( !$cache ) {
349  }
350 
351  $mtime = filemtime( $fileName );
352  if ( $mtime === false ) {
353  throw new MWException( 'Lua file does not exist: ' . $fileName );
354  }
355 
356  $cacheKey = $cache->makeGlobalKey( __CLASS__, $fileName );
357  $fileData = $cache->get( $cacheKey );
358 
359  $code = false;
360  if ( $fileData ) {
361  list( $code, $cachedMtime ) = $fileData;
362  if ( $cachedMtime < $mtime ) {
363  $code = false;
364  }
365  }
366  if ( !$code ) {
367  $code = file_get_contents( $fileName );
368  if ( $code === false ) {
369  throw new MWException( 'Lua file does not exist: ' . $fileName );
370  }
371  $cache->set( $cacheKey, [ $code, $mtime ], 60 * 5 );
372  }
373 
374  # Prepending an "@" to the chunk name makes Lua think it is a filename
375  $module = $this->getInterpreter()->loadString( $code, '@' . basename( $fileName ) );
376  $ret = $this->getInterpreter()->callFunction( $module );
377  return $ret[0] ?? null;
378  }
379 
380  public function getGeSHiLanguage() {
381  return 'lua';
382  }
383 
384  public function getCodeEditorLanguage() {
385  return 'lua';
386  }
387 
388  public function runConsole( array $params ) {
389  // $resetFrames is a ScopedCallback, so it has a purpose even though it appears unused.
390  $resetFrames = $this->setupCurrentFrames();
391 
400  $code = "return function (__init, exe)\n" .
401  "if not exe then exe = function(...) return true, ... end end\n" .
402  "local p = select(2, exe(__init) )\n" .
403  "__init, exe = nil, nil\n" .
404  "local print = mw.log\n";
405  foreach ( $params['prevQuestions'] as $q ) {
406  if ( substr( $q, 0, 1 ) === '=' ) {
407  $code .= "print(" . substr( $q, 1 ) . ")";
408  } else {
409  $code .= $q;
410  }
411  $code .= "\n";
412  }
413  $code .= "mw.clearLogBuffer()\n";
414  if ( substr( $params['question'], 0, 1 ) === '=' ) {
415  // Treat a statement starting with "=" as a return statement, like in lua.c
416  $code .= "local ret = mw.allToString(" . substr( $params['question'], 1 ) . ")\n" .
417  "return ret, mw.getLogBuffer()\n";
418  } else {
419  $code .= $params['question'] . "\n" .
420  "return nil, mw.getLogBuffer()\n";
421  }
422  $code .= "end\n";
423 
424  if ( $params['title']->hasContentModel( CONTENT_MODEL_SCRIBUNTO ) ) {
425  $contentModule = $this->newModule(
426  $params['content'], $params['title']->getPrefixedDBkey() );
427  $contentInit = $contentModule->getInitChunk();
428  $contentExe = $this->mw['executeModule'];
429  } else {
430  $contentInit = $params['content'];
431  $contentExe = null;
432  }
433 
434  $consoleModule = $this->newModule(
435  $code,
436  wfMessage( 'scribunto-console-current-src' )->text()
437  );
438  $consoleInit = $consoleModule->getInitChunk();
439  $ret = $this->getInterpreter()->callFunction( $this->mw['executeModule'], $consoleInit, false );
440  $func = $ret[1];
441  $ret = $this->getInterpreter()->callFunction( $func, $contentInit, $contentExe );
442 
443  return [
444  'return' => $ret[0] ?? null,
445  'print' => $ret[1] ?? '',
446  ];
447  }
448 
459  public function checkType( $funcName, $args, $index0, $type, $msgType ) {
460  if ( !is_array( $type ) ) {
461  $type = [ $type ];
462  }
463  if ( !isset( $args[$index0] ) || !in_array( gettype( $args[$index0] ), $type, true ) ) {
464  $index1 = $index0 + 1;
465  throw new Scribunto_LuaError( "bad argument #$index1 to '$funcName' ($msgType expected)" );
466  }
467  }
468 
476  public function checkString( $funcName, $args, $index0 ) {
477  $this->checkType( $funcName, $args, $index0, 'string', 'string' );
478  }
479 
487  public function checkNumber( $funcName, $args, $index0 ) {
488  $this->checkType( $funcName, $args, $index0, [ 'integer', 'double' ], 'number' );
489  }
490 
499  private function instantiatePHPLibrary( $name, $def, $loadDeferred ) {
500  $def = $this->availableLibraries[$name];
501  if ( is_string( $def ) ) {
502  $class = new $def( $this );
503  } else {
504  if ( !$loadDeferred && !empty( $def['deferLoad'] ) ) {
505  return null;
506  }
507  if ( isset( $def['class'] ) ) {
508  $class = new $def['class']( $this );
509  } else {
510  throw new MWException( "No class for library \"$name\"" );
511  }
512  }
513  return $class->register();
514  }
515 
525  public function loadPHPLibrary( $name ) {
526  $args = func_get_args();
527  $this->checkString( 'loadPHPLibrary', $args, 0 );
528 
529  $ret = null;
530  if ( isset( $this->availableLibraries[$name] ) ) {
531  $ret = $this->instantiatePHPLibrary( $name, $this->availableLibraries[$name], true );
532  }
533 
534  return [ $ret ];
535  }
536 
546  public function loadPackage( $name ) {
547  $args = func_get_args();
548  $this->checkString( 'loadPackage', $args, 0 );
549 
550  # This is what Lua does for its built-in loaders
551  $luaName = str_replace( '.', '/', $name ) . '.lua';
552  $paths = $this->getLibraryPaths( 'lua', self::$libraryPaths );
553  foreach ( $paths as $path ) {
554  $fileName = $this->normalizeModuleFileName( "$path/$luaName" );
555  if ( !file_exists( $fileName ) ) {
556  continue;
557  }
558  $code = file_get_contents( $fileName );
559  $init = $this->interpreter->loadString( $code, "@$luaName" );
560  return [ $init ];
561  }
562 
563  $title = Title::newFromText( $name );
564  if ( !$title || !$title->hasContentModel( CONTENT_MODEL_SCRIBUNTO ) ) {
565  return [];
566  }
567 
568  $module = $this->fetchModuleFromParser( $title );
569  if ( $module ) {
570  // @phan-suppress-next-line PhanUndeclaredMethod
571  return [ $module->getInitChunk() ];
572  } else {
573  return [];
574  }
575  }
576 
585  protected function getFrameById( $frameId ) {
586  if ( $frameId === 'empty' ) {
587  return $this->getParser()->getPreprocessor()->newFrame();
588  } elseif ( isset( $this->currentFrames[$frameId] ) ) {
589  return $this->currentFrames[$frameId];
590  } else {
591  throw new Scribunto_LuaError( 'invalid frame ID' );
592  }
593  }
594 
602  public function frameExists( $frameId ) {
603  return [ $frameId === 'empty' || isset( $this->currentFrames[$frameId] ) ];
604  }
605 
616  public function newChildFrame( $frameId, $title, array $args ) {
617  if ( count( $this->currentFrames ) > 100 ) {
618  throw new Scribunto_LuaError( 'newChild: too many frames' );
619  }
620 
621  $frame = $this->getFrameById( $frameId );
622  if ( $title === false ) {
623  $title = $frame->getTitle();
624  } else {
626  if ( !$title ) {
627  throw new Scribunto_LuaError( 'newChild: invalid title' );
628  }
629  }
630  $args = $this->getParser()->getPreprocessor()->newPartNodeArray( $args );
631  $newFrame = $frame->newChild( $args, $title );
632  $newFrameId = 'frame' . count( $this->currentFrames );
633  $this->currentFrames[$newFrameId] = $newFrame;
634  return [ $newFrameId ];
635  }
636 
645  public function getFrameTitle( $frameId ) {
646  $frame = $this->getFrameById( $frameId );
647  return [ $frame->getTitle()->getPrefixedText() ];
648  }
649 
655  public function setTTL( $ttl ) {
656  $args = func_get_args();
657  $this->checkNumber( 'setTTL', $args, 0 );
658 
659  $frame = $this->getFrameById( 'current' );
660  $frame->setTTL( $ttl );
661  }
662 
670  public function getExpandedArgument( $frameId, $name ) {
671  $args = func_get_args();
672  $this->checkString( 'getExpandedArgument', $args, 0 );
673 
674  $frame = $this->getFrameById( $frameId );
675  $this->getInterpreter()->pauseUsageTimer();
676  $result = $frame->getArgument( $name );
677  if ( $result === false ) {
678  return [];
679  } else {
680  return [ $result ];
681  }
682  }
683 
690  public function getAllExpandedArguments( $frameId ) {
691  $frame = $this->getFrameById( $frameId );
692  $this->getInterpreter()->pauseUsageTimer();
693  return [ $frame->getArguments() ];
694  }
695 
705  public function expandTemplate( $frameId, $titleText, $args ) {
706  $frame = $this->getFrameById( $frameId );
707  $title = Title::newFromText( $titleText, NS_TEMPLATE );
708  if ( !$title ) {
709  throw new Scribunto_LuaError( "expandTemplate: invalid title \"$titleText\"" );
710  }
711 
712  if ( $frame->depth >= $this->parser->mOptions->getMaxTemplateDepth() ) {
713  throw new Scribunto_LuaError( 'expandTemplate: template depth limit exceeded' );
714  }
716  throw new Scribunto_LuaError( 'expandTemplate: template inclusion denied' );
717  }
718 
719  list( $dom, $finalTitle ) = $this->parser->getTemplateDom( $title );
720  if ( $dom === false ) {
721  throw new Scribunto_LuaError( "expandTemplate: template \"$titleText\" does not exist" );
722  }
723  if ( !$frame->loopCheck( $finalTitle ) ) {
724  throw new Scribunto_LuaError( 'expandTemplate: template loop detected' );
725  }
726 
727  $fargs = $this->getParser()->getPreprocessor()->newPartNodeArray( $args );
728  $newFrame = $frame->newChild( $fargs, $finalTitle );
729  $text = $this->doCachedExpansion( $newFrame, $dom,
730  [
731  'frameId' => $frameId,
732  'template' => $finalTitle->getPrefixedDBkey(),
733  'args' => $args
734  ] );
735  return [ $text ];
736  }
737 
748  public function callParserFunction( $frameId, $function, $args ) {
749  $frame = $this->getFrameById( $frameId );
750 
751  # Make zero-based, without screwing up named args
752  $args = array_merge( [], $args );
753 
754  # Sort, since we can't rely on the order coming in from Lua
755  uksort( $args, function ( $a, $b ) {
756  if ( is_int( $a ) !== is_int( $b ) ) {
757  return is_int( $a ) ? -1 : 1;
758  }
759  if ( is_int( $a ) ) {
760  return $a - $b;
761  }
762  return strcmp( $a, $b );
763  } );
764 
765  # Be user-friendly
766  $colonPos = strpos( $function, ':' );
767  if ( $colonPos !== false ) {
768  array_unshift( $args, trim( substr( $function, $colonPos + 1 ) ) );
769  $function = substr( $function, 0, $colonPos );
770  }
771  if ( !isset( $args[0] ) ) {
772  # It's impossible to call a parser function from wikitext without
773  # supplying an arg 0. Insist that one be provided via Lua, too.
774  throw new Scribunto_LuaError( 'callParserFunction: At least one unnamed parameter ' .
775  '(the parameter that comes after the colon in wikitext) ' .
776  'must be provided'
777  );
778  }
779 
780  $result = $this->parser->callParserFunction( $frame, $function, $args );
781  if ( !$result['found'] ) {
782  throw new Scribunto_LuaError( "callParserFunction: function \"$function\" was not found" );
783  }
784 
785  # Set defaults for various flags
786  $result += [
787  'nowiki' => false,
788  'isChildObj' => false,
789  'isLocalObj' => false,
790  'isHTML' => false,
791  'title' => false,
792  ];
793 
794  $text = $result['text'];
795  if ( $result['isChildObj'] ) {
796  $fargs = $this->getParser()->getPreprocessor()->newPartNodeArray( $args );
797  $newFrame = $frame->newChild( $fargs, $result['title'] );
798  if ( $result['nowiki'] ) {
799  $text = $newFrame->expand( $text, PPFrame::RECOVER_ORIG );
800  } else {
801  $text = $newFrame->expand( $text );
802  }
803  }
804  if ( $result['isLocalObj'] && $result['nowiki'] ) {
805  $text = $frame->expand( $text, PPFrame::RECOVER_ORIG );
806  $result['isLocalObj'] = false;
807  }
808 
809  # Replace raw HTML by a placeholder
810  if ( $result['isHTML'] ) {
811  $text = $this->parser->insertStripItem( $text );
812  } elseif ( $result['nowiki'] ) {
813  # Escape nowiki-style return values
814  // @phan-suppress-next-line SecurityCheck-DoubleEscaped
815  $text = wfEscapeWikiText( $text );
816  }
817 
818  if ( $result['isLocalObj'] ) {
819  $text = $frame->expand( $text );
820  }
821 
822  return [ "$text" ];
823  }
824 
833  public function preprocess( $frameId, $text ) {
834  $args = func_get_args();
835  $this->checkString( 'preprocess', $args, 0 );
836 
837  $frame = $this->getFrameById( $frameId );
838 
839  if ( !$frame ) {
840  throw new Scribunto_LuaError( 'attempt to call mw.preprocess with no frame' );
841  }
842 
843  // Don't count the time for expanding all the frame arguments against
844  // the Lua time limit.
845  $this->getInterpreter()->pauseUsageTimer();
846  $frame->getArguments();
847  $this->getInterpreter()->unpauseUsageTimer();
848 
849  $text = $this->doCachedExpansion( $frame, $text,
850  [
851  'frameId' => $frameId,
852  'inputText' => $text
853  ] );
854  return [ $text ];
855  }
856 
865  if ( !$this->getParser()->incrementExpensiveFunctionCount() ) {
866  throw new Scribunto_LuaError( "too many expensive function calls" );
867  }
868  return null;
869  }
870 
877  public function addWarning( $text ) {
878  $this->getParser()->getOutput()->addWarning( $text );
879  }
880 
887  public function isSubsting() {
888  // See Parser::braceSubstitution, OT_WIKI is the switch
889  return [ $this->getParser()->OutputType() === Parser::OT_WIKI ];
890  }
891 
892  private function doCachedExpansion( $frame, $input, $cacheKey ) {
893  $hash = md5( serialize( $cacheKey ) );
894  if ( isset( $this->expandCache[$hash] ) ) {
895  return $this->expandCache[$hash];
896  }
897 
898  if ( is_scalar( $input ) ) {
899  $input = str_replace( [ "\r\n", "\r" ], "\n", $input );
900  $dom = $this->parser->getPreprocessor()->preprocessToObj(
901  $input, $frame->depth ? Parser::PTD_FOR_INCLUSION : 0 );
902  } else {
903  $dom = $input;
904  }
905  $ret = $frame->expand( $dom );
906  if ( !$frame->isVolatile() ) {
907  if ( count( $this->expandCache ) > self::MAX_EXPAND_CACHE_SIZE ) {
908  reset( $this->expandCache );
909  $oldHash = key( $this->expandCache );
910  unset( $this->expandCache[$oldHash] );
911  }
912  $this->expandCache[$hash] = $ret;
913  }
914  return $ret;
915  }
916 }
917 
922  protected $initChunk;
923 
927  protected $engine;
928 
935  parent::__construct( $engine, $code, $chunkName );
936  }
937 
938  public function validate() {
939  try {
940  $this->getInitChunk();
941  } catch ( ScribuntoException $e ) {
942  return $e->toStatus();
943  }
944  return Status::newGood();
945  }
946 
951  public function getInitChunk() {
952  if ( !$this->initChunk ) {
953  $this->initChunk = $this->engine->getInterpreter()->loadString(
954  $this->code,
955  // Prepending an "=" to the chunk name avoids truncation or a "[string" prefix
956  '=' . $this->chunkName );
957  }
958  return $this->initChunk;
959  }
960 
969  public function invoke( $name, $frame ) {
970  $ret = $this->engine->executeModule( $this->getInitChunk(), $name, $frame );
971 
972  if ( !isset( $ret ) ) {
973  throw $this->engine->newException(
974  'scribunto-common-nosuchfunction', [ 'args' => [ $name ] ]
975  );
976  }
977  if ( !$this->engine->getInterpreter()->isLuaFunction( $ret ) ) {
978  throw $this->engine->newException(
979  'scribunto-common-notafunction', [ 'args' => [ $name ] ]
980  );
981  }
982 
983  $result = $this->engine->executeFunctionChunk( $ret, $frame );
984  if ( isset( $result[0] ) ) {
985  return $result[0];
986  } else {
987  return null;
988  }
989  }
990 }
991 
993  public $luaMessage, $lineMap = [];
994 
995  public function __construct( $message, array $options = [] ) {
996  $this->luaMessage = $message;
997  $options = $options + [ 'args' => [ Validator::cleanUp( $message ) ] ];
998  if ( isset( $options['module'] ) && isset( $options['line'] ) ) {
999  $msg = 'scribunto-lua-error-location';
1000  } else {
1001  $msg = 'scribunto-lua-error';
1002  }
1003 
1004  parent::__construct( $msg, $options );
1005  }
1006 
1007  public function getLuaMessage() {
1008  return $this->luaMessage;
1009  }
1010 
1011  public function setLineMap( $map ) {
1012  $this->lineMap = $map;
1013  }
1014 
1020  public function getScriptTraceHtml( $options = [] ) {
1021  if ( !isset( $this->params['trace'] ) ) {
1022  return false;
1023  }
1024  if ( isset( $options['msgOptions'] ) ) {
1025  $msgOptions = $options['msgOptions'];
1026  } else {
1027  $msgOptions = [];
1028  }
1029 
1030  $s = '<ol class="scribunto-trace">';
1031  foreach ( $this->params['trace'] as $info ) {
1032  $short_src = $srcdefined = $info['short_src'];
1033  $currentline = $info['currentline'];
1034 
1035  $src = htmlspecialchars( $short_src );
1036  if ( $currentline > 0 ) {
1037  $src .= ':' . htmlspecialchars( $currentline );
1038 
1039  $title = Title::newFromText( $short_src );
1040  if ( $title && $title->hasContentModel( CONTENT_MODEL_SCRIBUNTO ) ) {
1041  $title = $title->createFragmentTarget( 'mw-ce-l' . $currentline );
1042  $src = Html::rawElement( 'a',
1043  [ 'href' => $title->getFullURL( 'action=edit' ) ],
1044  $src );
1045  }
1046  }
1047 
1048  if ( strval( $info['namewhat'] ) !== '' ) {
1049  $functionMsg = wfMessage( 'scribunto-lua-in-function', wfEscapeWikiText( $info['name'] ) );
1050  in_array( 'content', $msgOptions ) ?
1051  $function = $functionMsg->inContentLanguage()->plain() :
1052  $function = $functionMsg->plain();
1053  } elseif ( $info['what'] == 'main' ) {
1054  $functionMsg = wfMessage( 'scribunto-lua-in-main' );
1055  in_array( 'content', $msgOptions ) ?
1056  $function = $functionMsg->inContentLanguage()->plain() :
1057  $function = $functionMsg->plain();
1058  } else {
1059  // C function, tail call, or a Lua function where Lua can't
1060  // guess the name
1061  $function = '?';
1062  }
1063 
1064  $backtraceLineMsg = wfMessage( 'scribunto-lua-backtrace-line' )
1065  ->rawParams( "<strong>$src</strong>" )
1066  ->params( $function );
1067  in_array( 'content', $msgOptions ) ?
1068  $backtraceLine = $backtraceLineMsg->inContentLanguage()->parse() :
1069  $backtraceLine = $backtraceLineMsg->parse();
1070 
1071  $s .= "<li>$backtraceLine</li>";
1072  }
1073  $s .= '</ol>';
1074  return $s;
1075  }
1076 }
Scribunto_LuaEngine\expandTemplate
expandTemplate( $frameId, $titleText, $args)
Handler for expandTemplate()
Definition: LuaCommon.php:705
Scribunto_LuaEngine\$expandCache
$expandCache
Definition: LuaCommon.php:50
Scribunto_LuaEngine\setTTL
setTTL( $ttl)
Handler for setTTL()
Definition: LuaCommon.php:655
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:316
MWNamespace\isNonincludable
static isNonincludable( $index)
It is not possible to use pages from this namespace as template?
Definition: MWNamespace.php:291
Scribunto_LuaEngine\loadLibraryFromFile
loadLibraryFromFile( $fileName)
Load a library from the given file and execute it in the base environment.
Definition: LuaCommon.php:344
Scribunto_LuaError\$lineMap
$lineMap
Definition: LuaCommon.php:993
Scribunto_LuaEngine\executeModule
executeModule( $chunk, $functionName, $frame)
Execute a module chunk in a new isolated environment, and return the specified function.
Definition: LuaCommon.php:262
Scribunto_LuaEngine\preprocess
preprocess( $frameId, $text)
Handler for preprocess()
Definition: LuaCommon.php:833
Scribunto_LuaEngine\loadPHPLibrary
loadPHPLibrary( $name)
Handler for the loadPHPLibrary() callback.
Definition: LuaCommon.php:525
Scribunto_LuaError
Definition: LuaCommon.php:992
Scribunto_LuaSandboxInterpreter\checkLuaSandboxVersion
static checkLuaSandboxVersion()
Check that php-luasandbox is available and of a recent-enough version.
Definition: Engine.php:240
Scribunto_LuaEngine\load
load()
Initialise the interpreter and the base environment.
Definition: LuaCommon.php:114
Scribunto_LuaEngine\newModule
newModule( $text, $chunkName)
Definition: LuaCommon.php:90
Scribunto_LuaEngine\incrementExpensiveFunctionCount
incrementExpensiveFunctionCount()
Increment the expensive function count, and throw if limit exceeded.
Definition: LuaCommon.php:864
Scribunto_LuaModule\getInitChunk
getInitChunk()
Get the chunk which, when called, will return the export table.
Definition: LuaCommon.php:951
Scribunto_LuaError\$luaMessage
$luaMessage
Definition: LuaCommon.php:993
Scribunto_LuaEngine\checkNumber
checkNumber( $funcName, $args, $index0)
Workalike for luaL_checknumber()
Definition: LuaCommon.php:487
Scribunto_LuaEngine\getLuaLibDir
getLuaLibDir()
Return the base path for Lua modules.
Definition: LuaCommon.php:189
NS_TEMPLATE
const NS_TEMPLATE
Definition: Defines.php:70
Scribunto_LuaEngine\executeFunctionChunk
executeFunctionChunk( $chunk, $frame)
Execute a module function chunk.
Definition: LuaCommon.php:290
Scribunto_LuaEngine\$currentFrames
array $currentFrames
Definition: LuaCommon.php:49
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1264
$s
$s
Definition: mergeMessageFileList.php:185
ScribuntoEngineBase\$options
array $options
Definition: Base.php:43
Scribunto_LuaEngine\addWarning
addWarning( $text)
Adds a warning to be displayed upon preview.
Definition: LuaCommon.php:877
Scribunto_LuaEngine\isSubsting
isSubsting()
Return whether the parser is currently substing.
Definition: LuaCommon.php:887
Scribunto_LuaEngine\getCodeEditorLanguage
getCodeEditorLanguage()
Get the language for Ace code editor.
Definition: LuaCommon.php:384
serialize
serialize()
Definition: ApiMessageTrait.php:138
Scribunto_LuaError\setLineMap
setLineMap( $map)
Definition: LuaCommon.php:1011
Scribunto_LuaEngine\checkString
checkString( $funcName, $args, $index0)
Workalike for luaL_checkstring()
Definition: LuaCommon.php:476
Scribunto_LuaModule\validate
validate()
Validates the script and returns a Status object containing the syntax errors for the given code.
Definition: LuaCommon.php:938
Scribunto_LuaEngine\doCachedExpansion
doCachedExpansion( $frame, $input, $cacheKey)
Definition: LuaCommon.php:892
ScribuntoModuleBase
Class that represents a module.
Definition: Base.php:296
Scribunto_LuaEngine\MAX_EXPAND_CACHE_SIZE
const MAX_EXPAND_CACHE_SIZE
Definition: LuaCommon.php:53
Scribunto_LuaModule\invoke
invoke( $name, $frame)
Invoke a function within the module.
Definition: LuaCommon.php:969
ScribuntoEngineBase\getLibraryPaths
getLibraryPaths( $engine, array $coreLibraryPaths=[])
Load a list of all paths libraries can be in for this engine.
Definition: Base.php:262
ScribuntoEngineBase\fetchModuleFromParser
fetchModuleFromParser(Title $title)
Load a module from some parser-defined template loading mechanism and register a parser output depend...
Definition: Base.php:164
Scribunto_LuaEngine\getPerformanceCharacteristics
getPerformanceCharacteristics()
Get performance characteristics of the Lua engine/interpreter.
Scribunto_LuaEngine\newLuaError
newLuaError( $message, $params=[])
Definition: LuaCommon.php:99
Scribunto_LuaInterpreterNotFoundError
Definition: LuaInterpreter.php:64
MWException
MediaWiki exception.
Definition: MWException.php:26
ScribuntoModuleBase\$chunkName
string bool $chunkName
Definition: Base.php:310
Scribunto_LuaModule\__construct
__construct(Scribunto_LuaEngine $engine, $code, $chunkName)
Definition: LuaCommon.php:934
ScribuntoEngineBase\getParser
getParser()
Definition: Base.php:231
Title\getNamespace
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:1037
Scribunto_LuaEngine\getFrameTitle
getFrameTitle( $frameId)
Handler for getTitle()
Definition: LuaCommon.php:645
Scribunto_LuaEngine
Definition: LuaCommon.php:6
PPFrame\RECOVER_ORIG
const RECOVER_ORIG
Definition: PPFrame.php:36
ScribuntoEngineBase\getLibraries
getLibraries( $engine, array $coreLibraries=[])
Load a list of all libraries supported by this engine.
Definition: Base.php:249
Scribunto_LuaEngine\newAutodetectEngine
static newAutodetectEngine(array $options)
If luasandbox is installed and usable then use it, otherwise.
Definition: LuaCommon.php:62
Scribunto_LuaEngine\getGeSHiLanguage
getGeSHiLanguage()
Get the language for GeSHi syntax highlighter.
Definition: LuaCommon.php:380
Scribunto_LuaEngine\newChildFrame
newChildFrame( $frameId, $title, array $args)
Handler for newChildFrame()
Definition: LuaCommon.php:616
$title
$title
Definition: testCompression.php:34
OT_WIKI
const OT_WIKI
Definition: Defines.php:165
Scribunto_LuaEngine\frameExists
frameExists( $frameId)
Handler for frameExists()
Definition: LuaCommon.php:602
Scribunto_LuaEngine\$loaded
$loaded
Definition: LuaCommon.php:34
Scribunto_LuaEngine\newInterpreter
newInterpreter()
Create a new interpreter object.
Scribunto_LuaEngine\setupCurrentFrames
setupCurrentFrames(PPFrame $frame=null)
Replaces the list of current frames, and return a ScopedCallback that will reset them when it goes ou...
Definition: LuaCommon.php:235
Scribunto_LuaEngine\callParserFunction
callParserFunction( $frameId, $function, $args)
Handler for callParserFunction()
Definition: LuaCommon.php:748
ScribuntoEngineBase\getDefaultExceptionParams
getDefaultExceptionParams()
Definition: Base.php:146
Scribunto_LuaEngine\destroy
destroy()
Definition: LuaCommon.php:103
Scribunto_LuaModule\$initChunk
mixed $initChunk
Definition: LuaCommon.php:922
Scribunto_LuaEngine\getAllExpandedArguments
getAllExpandedArguments( $frameId)
Handler for getAllExpandedArguments()
Definition: LuaCommon.php:690
Scribunto_LuaEngine\runConsole
runConsole(array $params)
Run an interactive console request.
Definition: LuaCommon.php:388
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
Scribunto_LuaEngine\normalizeModuleFileName
normalizeModuleFileName( $fileName)
Normalize a lua module to its full path.
Definition: LuaCommon.php:201
Scribunto_LuaInterpreterBadVersionError
Definition: LuaInterpreter.php:67
Scribunto_LuaEngine\instantiatePHPLibrary
instantiatePHPLibrary( $name, $def, $loadDeferred)
Instantiate and register a library.
Definition: LuaCommon.php:499
Scribunto_LuaError\getLuaMessage
getLuaMessage()
Definition: LuaCommon.php:1007
PPFrame
Definition: PPFrame.php:28
Scribunto_LuaEngine\$libraryPaths
static array $libraryPaths
Paths for modules that may be loaded from Lua.
Definition: LuaCommon.php:28
Title\hasContentModel
hasContentModel( $id)
Convenience method for checking a title's content model name.
Definition: Title.php:1082
ScribuntoException\toStatus
toStatus()
Definition: Common.php:195
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1551
Scribunto_LuaEngine\$interpreter
Scribunto_LuaInterpreter $interpreter
Definition: LuaCommon.php:39
Scribunto_LuaEngine\getExpandedArgument
getExpandedArgument( $frameId, $name)
Handler for getExpandedArgument()
Definition: LuaCommon.php:670
ScribuntoEngineBase
Wikitext scripting infrastructure for MediaWiki: base classes.
Definition: Base.php:29
ScribuntoException
An exception class which represents an error in the script.
Definition: Common.php:136
Scribunto_LuaError\__construct
__construct( $message, array $options=[])
Definition: LuaCommon.php:995
$args
if( $line===false) $args
Definition: cdb.php:64
Scribunto_LuaInterpreter
Definition: LuaInterpreter.php:3
Scribunto_LuaEngine\loadPackage
loadPackage( $name)
Handler for the loadPackage() callback.
Definition: LuaCommon.php:546
ScribuntoEngineBase\newException
newException( $message, array $params=[])
Definition: Base.php:139
$cache
$cache
Definition: mcc.php:33
Scribunto_LuaEngine\getFrameById
getFrameById( $frameId)
Helper function for the implementation of frame methods.
Definition: LuaCommon.php:585
Scribunto_LuaError\getScriptTraceHtml
getScriptTraceHtml( $options=[])
Definition: LuaCommon.php:1020
$path
$path
Definition: NoLocalSettings.php:25
Scribunto_LuaEngine\getLogBuffer
getLogBuffer()
Get data logged by modules.
Definition: LuaCommon.php:303
Scribunto_LuaEngine\registerInterface
registerInterface( $moduleFileName, $interfaceFuncs, $setupOptions=[])
Register a Lua Library.
Definition: LuaCommon.php:175
Scribunto_LuaEngine\$mw
array $mw
Definition: LuaCommon.php:44
Scribunto_LuaEngine\$availableLibraries
$availableLibraries
Definition: LuaCommon.php:51
Scribunto_LuaEngine\checkType
checkType( $funcName, $args, $index0, $type, $msgType)
Workalike for luaL_checktype()
Definition: LuaCommon.php:459
ScribuntoModuleBase\$code
string $code
Definition: Base.php:305
Scribunto_LuaEngine\getInterpreter
getInterpreter()
Get the current interpreter object.
Definition: LuaCommon.php:223
Scribunto_LuaEngine\formatHtmlLogs
formatHtmlLogs( $logs, $localize)
Format the logged data for HTML output.
Definition: LuaCommon.php:322
Scribunto_LuaModule\$engine
Scribunto_LuaEngine $engine
Definition: LuaCommon.php:927
Scribunto\newEngine
static newEngine( $options)
Create a new engine object with specified parameters.
Definition: Common.php:15
Scribunto_LuaEngine\$libraryClasses
static array $libraryClasses
Libraries to load.
Definition: LuaCommon.php:11
Scribunto_LuaModule
Definition: LuaCommon.php:918
ScribuntoEngineBase\$title
Title $title
Definition: Base.php:38
ObjectCache\getLocalServerInstance
static getLocalServerInstance( $fallback=CACHE_NONE)
Factory function for CACHE_ACCEL (referenced from DefaultSettings.php)
Definition: ObjectCache.php:268
$type
$type
Definition: testCompression.php:48