MediaWiki REL1_34
LuaCommon.php
Go to the documentation of this file.
1<?php
2
3use UtfNormal\Validator;
4use Wikimedia\ScopedCallback;
5
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
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 ) {
348 $cache = ObjectCache::getLocalServerInstance( 'hash' );
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 {
625 $title = Title::newFromText( $title );
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}
serialize()
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
if( $line===false) $args
Definition cdb.php:64
MediaWiki exception.
static isNonincludable( $index)
It is not possible to use pages from this namespace as template?
Wikitext scripting infrastructure for MediaWiki: base classes.
Definition Base.php:29
getLibraryPaths( $engine, array $coreLibraryPaths=[])
Load a list of all paths libraries can be in for this engine.
Definition Base.php:262
getDefaultExceptionParams()
Definition Base.php:146
newException( $message, array $params=[])
Definition Base.php:139
getLibraries( $engine, array $coreLibraries=[])
Load a list of all libraries supported by this engine.
Definition Base.php:249
fetchModuleFromParser(Title $title)
Load a module from some parser-defined template loading mechanism and register a parser output depend...
Definition Base.php:164
An exception class which represents an error in the script.
Definition Common.php:136
Class that represents a module.
Definition Base.php:296
string bool $chunkName
Definition Base.php:310
formatHtmlLogs( $logs, $localize)
Format the logged data for HTML output.
callParserFunction( $frameId, $function, $args)
Handler for callParserFunction()
Scribunto_LuaInterpreter $interpreter
Definition LuaCommon.php:39
getExpandedArgument( $frameId, $name)
Handler for getExpandedArgument()
loadPHPLibrary( $name)
Handler for the loadPHPLibrary() callback.
doCachedExpansion( $frame, $input, $cacheKey)
newChildFrame( $frameId, $title, array $args)
Handler for newChildFrame()
newModule( $text, $chunkName)
Definition LuaCommon.php:90
getInterpreter()
Get the current interpreter object.
isSubsting()
Return whether the parser is currently substing.
frameExists( $frameId)
Handler for frameExists()
load()
Initialise the interpreter and the base environment.
getCodeEditorLanguage()
Get the language for Ace code editor.
expandTemplate( $frameId, $titleText, $args)
Handler for expandTemplate()
incrementExpensiveFunctionCount()
Increment the expensive function count, and throw if limit exceeded.
newInterpreter()
Create a new interpreter object.
preprocess( $frameId, $text)
Handler for preprocess()
registerInterface( $moduleFileName, $interfaceFuncs, $setupOptions=[])
Register a Lua Library.
static array $libraryPaths
Paths for modules that may be loaded from Lua.
Definition LuaCommon.php:28
static newAutodetectEngine(array $options)
If luasandbox is installed and usable then use it, otherwise.
Definition LuaCommon.php:62
checkString( $funcName, $args, $index0)
Workalike for luaL_checkstring()
runConsole(array $params)
Run an interactive console request.
addWarning( $text)
Adds a warning to be displayed upon preview.
checkType( $funcName, $args, $index0, $type, $msgType)
Workalike for luaL_checktype()
getFrameById( $frameId)
Helper function for the implementation of frame methods.
executeFunctionChunk( $chunk, $frame)
Execute a module function chunk.
getAllExpandedArguments( $frameId)
Handler for getAllExpandedArguments()
checkNumber( $funcName, $args, $index0)
Workalike for luaL_checknumber()
normalizeModuleFileName( $fileName)
Normalize a lua module to its full path.
newLuaError( $message, $params=[])
Definition LuaCommon.php:99
setupCurrentFrames(PPFrame $frame=null)
Replaces the list of current frames, and return a ScopedCallback that will reset them when it goes ou...
getLuaLibDir()
Return the base path for Lua modules.
static array $libraryClasses
Libraries to load.
Definition LuaCommon.php:11
getGeSHiLanguage()
Get the language for GeSHi syntax highlighter.
loadPackage( $name)
Handler for the loadPackage() callback.
const MAX_EXPAND_CACHE_SIZE
Definition LuaCommon.php:53
getFrameTitle( $frameId)
Handler for getTitle()
setTTL( $ttl)
Handler for setTTL()
executeModule( $chunk, $functionName, $frame)
Execute a module chunk in a new isolated environment, and return the specified function.
getLogBuffer()
Get data logged by modules.
instantiatePHPLibrary( $name, $def, $loadDeferred)
Instantiate and register a library.
getPerformanceCharacteristics()
Get performance characteristics of the Lua engine/interpreter.
loadLibraryFromFile( $fileName)
Load a library from the given file and execute it in the base environment.
__construct( $message, array $options=[])
getScriptTraceHtml( $options=[])
__construct(Scribunto_LuaEngine $engine, $code, $chunkName)
invoke( $name, $frame)
Invoke a function within the module.
Scribunto_LuaEngine $engine
validate()
Validates the script and returns a Status object containing the syntax errors for the given code.
getInitChunk()
Get the chunk which, when called, will return the export table.
static checkLuaSandboxVersion()
Check that php-luasandbox is available and of a recent-enough version.
Definition Engine.php:240
static newEngine( $options)
Create a new engine object with specified parameters.
Definition Common.php:15
getNamespace()
Get the namespace index, i.e.
Definition Title.php:1037
hasContentModel( $id)
Convenience method for checking a title's content model name.
Definition Title.php:1082
const NS_TEMPLATE
Definition Defines.php:79
$cache
Definition mcc.php:33