r38841 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r38840‎ | r38841 | r38842 >
Date:04:39, 8 August 2008
Author:krimpet
Status:old
Tags:
Comment:
Importing experimental Lua extension
Modified paths:
  • /trunk/extensions/Lua (added) (history)
  • /trunk/extensions/Lua/COPYING (added) (history)
  • /trunk/extensions/Lua/Lua.body.php (added) (history)
  • /trunk/extensions/Lua/Lua.i18n.php (added) (history)
  • /trunk/extensions/Lua/Lua.php (added) (history)
  • /trunk/extensions/Lua/LuaWrapper.lua (added) (history)
  • /trunk/extensions/Lua/README (added) (history)

Diff [purge]

Index: trunk/extensions/Lua/LuaWrapper.lua
@@ -0,0 +1,213 @@
 2+#!/usr/bin/env lua
 3+-- Lua parser extensions for MediaWiki - Wrapper for Lua interpreter
 4+--
 5+-- @author Fran Rogers
 6+-- @package MediaWiki
 7+-- @addtogroup Extensions
 8+-- @license See 'COPYING'
 9+-- @file
 10+
 11+function make_sandbox()
 12+ local function dummy(...)
 13+ return nil
 14+ end
 15+
 16+ local function deepcopy(object, override)
 17+ local lookup_table = {}
 18+ local function _copy(object, override)
 19+ if type(object) ~= "table" then
 20+ return object
 21+ elseif lookup_table[object] then
 22+ return lookup_table[object]
 23+ end
 24+ local new_table = {}
 25+ lookup_table[object] = new_table
 26+ for index, value in pairs(object) do
 27+ if override ~= nil then
 28+ value = override
 29+ end
 30+ new_table[_copy(index)] = _copy(value, override)
 31+ end
 32+ return setmetatable(new_table, _copy(getmetatable(object), override))
 33+ end
 34+ return _copy(object, override)
 35+ end
 36+
 37+ local env = {}
 38+
 39+ local function _escape(s)
 40+ s = string.gsub(s, "\\", "\\\\")
 41+ s = string.gsub(s, "\'", "\\\'")
 42+ return s
 43+ end
 44+
 45+ env._OUTPUT = ""
 46+
 47+ local function writewrapper(...)
 48+ local out = ""
 49+ for n = 1, select("#", ...) do
 50+ if out == "" then
 51+ out = tostring(select(n, ...))
 52+ else
 53+ out = out .. tostring(select(n, ...))
 54+ end
 55+ end
 56+ env._OUTPUT = env._OUTPUT .. out
 57+ end
 58+
 59+ local function outputwrapper(file)
 60+ if file == nil then
 61+ local file = {}
 62+ file.close = dummy
 63+ file.lines = dummy
 64+ file.read = dummy
 65+ file.flush = dummy
 66+ file.seek = dummy
 67+ file.setvbuf = dummy
 68+ function file:write(...) writewrapper(...); end
 69+ return file
 70+ else
 71+ return nil
 72+ end
 73+ end
 74+
 75+ local function printwrapper(...)
 76+ local out = ""
 77+ for n = 1, select("#", ...) do
 78+ if out == "" then
 79+ out = tostring(select(n, ...))
 80+ else
 81+ out = out .. '\t' .. tostring(select(n, ...))
 82+ end
 83+ end
 84+ env._OUTPUT =env._OUTPUT .. out .. "\n"
 85+ end
 86+
 87+ local oldloadstring = loadstring
 88+ local function safeloadstring(s, chunkname)
 89+ local f, message = oldloadstring(s, chunkname)
 90+ if not f then
 91+ return f, message
 92+ end
 93+ setfenv(f, getfenv(2))
 94+ return f
 95+ end
 96+
 97+ env.assert = _G.assert
 98+ env.error = _G.error
 99+ env._G = env
 100+ env.ipairs = _G.ipairs
 101+ env.loadstring = safeloadstring
 102+ env.next = _G.next
 103+ env.pairs = _G.pairs
 104+ env.pcall = _G.pcall
 105+ env.print = printwrapper
 106+ env.write = writewrapper
 107+ env.select = _G.select
 108+ env.tonumber = _G.tonumber
 109+ env.tostring = _G.tostring
 110+ env.type = _G.type
 111+ env.unpack = _G.unpack
 112+ env._VERSION = _G._VERSION
 113+ env.xpcall = _G.xpcall
 114+ env.coroutine = deepcopy(_G.coroutine)
 115+ env.string = deepcopy(_G.string)
 116+ env.string.dump = nil
 117+ env.table = deepcopy(_G.table)
 118+ env.math = deepcopy(_G.math)
 119+ env.io = {}
 120+ env.io.write = writewrapper
 121+ env.io.flush = dummy
 122+ env.io.type = typewrapper
 123+ env.io.output = outputwrapper
 124+ env.io.stdout = outputwrapper()
 125+ env.os = {}
 126+ env.os.clock = _G.os.clock
 127+ -- env.os.date = _G.os.date
 128+ env.os.difftime = _G.os.difftime
 129+ env.os.time = _G.os.time
 130+
 131+ return env
 132+end
 133+
 134+function make_hook(maxlines, maxcalls, diefunc)
 135+ local lines = 0
 136+ local calls = 0
 137+ function _hook(event, ...)
 138+ if event == "call" then
 139+ calls = calls + 1
 140+ if calls > maxcalls then
 141+ diefunc("RECURSION_LIMIT")
 142+ end
 143+ elseif event == "return" then
 144+ calls = calls - 1
 145+ elseif event == "line" then
 146+ lines = lines + 1
 147+ if lines > maxlines then
 148+ diefunc("LOC_LIMIT")
 149+ end
 150+ end
 151+ end
 152+ return _hook
 153+end
 154+
 155+function wrap(chunk, env, hook)
 156+ local err = nil
 157+ env._OUTPUT = ""
 158+ setfenv(chunk, env)
 159+ debug.sethook(hook, "crl")
 160+ res = xpcall(chunk, function(s) err = s; end)
 161+ debug.sethook()
 162+ return res, err
 163+end
 164+
 165+function main()
 166+ if #arg ~= 2 then
 167+ io.stderr:write(string.format("usage: %s MAXLINES MAXCALLS\n", arg[0]))
 168+ os.exit(1)
 169+ end
 170+
 171+ io.stdout:setvbuf("no")
 172+ function _die(reason)
 173+ io.stdout:write("'", reason, "', false\n.\n")
 174+ os.exit(1)
 175+ end
 176+ hook = make_hook(tonumber(arg[1]), tonumber(arg[2]),
 177+ _die)
 178+ local env = make_sandbox()
 179+ while true do
 180+ local chunkstr = ""
 181+ while true do
 182+ local line = io.stdin:read("*l")
 183+ if chunkstr == "" and line == nil then
 184+ return nil
 185+ elseif line == "." or line == nil then
 186+ break
 187+ elseif chunkstr ~= "" then
 188+ chunkstr = chunkstr .. "\n" .. line
 189+ else
 190+ chunkstr = line
 191+ end
 192+ end
 193+
 194+ local chunk
 195+ local err
 196+ chunk, err = loadstring(chunkstr)
 197+
 198+ if err == nil then
 199+ local res
 200+ res, err = wrap(chunk, env, hook)
 201+ end
 202+
 203+ if err == nil then
 204+ io.stdout:write("'", env._OUTPUT, "', true\n.\n")
 205+ else
 206+ io.stdout:write("'", err, "', false\n.\n")
 207+ end
 208+ end
 209+ exit(0)
 210+end
 211+
 212+if arg ~= nil then
 213+ main()
 214+end
\ No newline at end of file
Property changes on: trunk/extensions/Lua/LuaWrapper.lua
___________________________________________________________________
Added: svn:executable
1215 +
Index: trunk/extensions/Lua/Lua.body.php
@@ -0,0 +1,179 @@
 2+<?php
 3+/**
 4+ * Lua parser extensions for MediaWiki - Body
 5+ *
 6+ * @author Fran Rogers
 7+ * @package MediaWiki
 8+ * @addtogroup Extensions
 9+ * @license See 'COPYING'
 10+ * @file
 11+ */
 12+
 13+function efLua_ParserInit() {
 14+ global $wgParser;
 15+ $wgParser->setHook( 'lua', 'efLua_Render' );
 16+ return true;
 17+}
 18+
 19+function efLua_FunctionSetup() {
 20+ global $wgParser;
 21+ $wgParser->setFunctionHook('luaexpr', 'efLua_RenderExpr');
 22+}
 23+
 24+function efLua_Magic(&$magicWords, $langCode) {
 25+ $magicWords['luaexpr'] = array(0, 'luaexpr');
 26+ return true;
 27+}
 28+
 29+function efLua_BeforeTidy(&$parser, &$text) {
 30+ if (!isset($wgLuaInterp)) {
 31+ efLua_Cleanup();
 32+ }
 33+ return TRUE;
 34+}
 35+
 36+function efLua_Render($input, $args, &$parser) {
 37+ $arglist = '';
 38+ foreach ($args as $key => $value)
 39+ $arglist .= (preg_replace('/\W/', '_', $key) . '=\'' .
 40+ addslashes($parser->recursiveTagParse($value)) .
 41+ '\';');
 42+ if ($arglist) {
 43+ try {
 44+ efLua_Eval($arglist);
 45+ } catch (LuaError $e) {
 46+ return $e->getMessage();
 47+ }
 48+ }
 49+
 50+ try {
 51+ return $parser->recursiveTagParse(efLua_Eval($input));
 52+ } catch (LuaError $e) {
 53+ return $e->getMessage();
 54+ }
 55+}
 56+
 57+function efLua_RenderExpr(&$parser, $param1 = FALSE) {
 58+ if ($param1 == FALSE)
 59+ return '';
 60+ try {
 61+ return efLua_Eval("io.write($param1)");
 62+ } catch (LuaError $e) {
 63+ return $e->getMessage();
 64+ }
 65+}
 66+
 67+function efLua_Eval($input) {
 68+ global $wgLuaExternalInterpreter, $wgLuaInterpDefunct,
 69+ $wgLuaInterp, $wgLuaWrapperFile;
 70+ if (isset($wgLuaInterpDefunct) && $wgLuaInterpDefunct) {
 71+ return '';
 72+ } else if (isset($wgLuaExternalInterpreter)) {
 73+ return efLua_EvalExternal($input);
 74+ } else if (!class_exists('lua')) {
 75+ throw new LuaError('pecl_notfound');
 76+ }
 77+
 78+ if (!isset($wgLuaInterp)) {
 79+ $wgLuaInterp = new lua;
 80+ try {
 81+ $wgLuaInterp->evaluatefile($wgLuaWrapperFile);
 82+
 83+ } catch (Exception $e) {
 84+ throw new LuaError('error_internal');
 85+ }
 86+ }
 87+}
 88+
 89+function efLua_Cleanup() {
 90+ global $wgLuaExternalInterpreter, $wgLuaInterpDefunct, $wgLuaInterp;
 91+ if (isset($wgLuaInterpDefunct) && $wgLuaInterpDefunct) {
 92+ return FALSE;
 93+ } else if (isset($wgLuaExternalInterpreter)) {
 94+ return efLua_CleanupExternal();
 95+ } else if (isset($wgLuaInterp)) {
 96+ $wgLuaInterpDefunct = TRUE;
 97+ return TRUE;
 98+ }
 99+}
 100+
 101+function efLua_EvalExternal($input) {
 102+ global $wgLuaExternalInterpreter, $wgLuaProc, $wgLuaPipes,
 103+ $wgLuaWrapperFile, $wgLuaMaxLines, $wgLuaMaxCalls,
 104+ $wgLuaMaxTime;
 105+ if (!isset($wgLuaProc)) {
 106+ $wgLuaInterp = TRUE;
 107+ $luacmd = "$wgLuaExternalInterpreter $wgLuaWrapperFile $wgLuaMaxLines $wgLuaMaxCalls";
 108+ $wgLuaProc = proc_open($luacmd,
 109+ array(0 => array('pipe', 'r'),
 110+ 1 => array('pipe', 'w')),
 111+ $wgLuaPipes, NULL, NULL);
 112+ if (!is_resource($wgLuaProc))
 113+ throw new LuaError('interp_notfound');
 114+ stream_set_blocking($wgLuaPipes[0], 0);
 115+ stream_set_blocking($wgLuaPipes[1], 0);
 116+ stream_set_write_buffer($wgLuaPipes[0], 0);
 117+ stream_set_write_buffer($wgLuaPipes[1], 0);
 118+ }
 119+
 120+ $input = trim(preg_replace('/(?<=\n|^)\.(?=\n|$)/', '. --', $input));
 121+ fwrite($wgLuaPipes[0], "$input\n.\n");
 122+ fflush($wgLuaPipes[0]);
 123+
 124+ $res = '';
 125+ $read = array($wgLuaPipes[1]);
 126+ $write = NULL;
 127+ $except = NULL;
 128+ while (!feof($wgLuaPipes[1])) {
 129+ if (false === ($num_changed_streams =
 130+ @stream_select($read, $write, $except,
 131+ $wgLuaMaxTime))) {
 132+ efLua_CleanupExternal();
 133+ throw new LuaError('overflow_time');
 134+ }
 135+ $line = fgets($wgLuaPipes[1]);
 136+ if ($line == ".\n")
 137+ break;
 138+ $res .= $line;
 139+ }
 140+
 141+ if (preg_match('/^\'(.*)\', (true|false)$/s', trim($res), $match) != 1) {
 142+ efLua_CleanupExternal();
 143+ throw new LuaError('error_internal');
 144+ }
 145+
 146+ $out = $match[1];
 147+ if ($match[2] == 'true') {
 148+ return (trim($out) != '') ? $out : '';
 149+ } else {
 150+ if ($out == 'RECURSION_LIMIT') {
 151+ efLua_CleanupExternal();
 152+ throw new LuaError('overflow_recursion');
 153+ } else if ($out == 'LOC_LIMIT') {
 154+ efLua_CleanupExternal();
 155+ throw new LuaError('overflow_loc');
 156+ } else {
 157+ $out = preg_replace('/^\[.+?\]:(.+?):/', '$1:', $out);
 158+ throw new LuaError('error', $out);
 159+ }
 160+ }
 161+}
 162+
 163+function efLua_CleanupExternal() {
 164+ global $wgLuaExternalInterpreter, $wgLuaInterpDefunct,
 165+ $wgLuaProc, $wgLuaPipes;
 166+ if (isset($wgLuaProc)) {
 167+ fclose($wgLuaPipes[0]);
 168+ fclose($wgLuaPipes[1]);
 169+ proc_close($wgLuaProc);
 170+ }
 171+ $wgLuaInterpDefunct = TRUE;
 172+ return TRUE;
 173+}
 174+
 175+class LuaError extends Exception {
 176+ public function __construct($msg, $parameter = ''){
 177+ wfLoadExtensionMessages( 'Lua' );
 178+ $this->message = '<strong class="error">' . wfMsgForContent( "lua_$msg", htmlspecialchars( $parameter ) ) . '</strong>';
 179+ }
 180+}
Property changes on: trunk/extensions/Lua/Lua.body.php
___________________________________________________________________
Added: svn:eol-style
1181 + native
Index: trunk/extensions/Lua/Lua.i18n.php
@@ -0,0 +1,22 @@
 2+<?php
 3+/**
 4+ * Lua parser extensions for MediaWiki - Internationalization
 5+ *
 6+ * @author Fran Rogers
 7+ * @package MediaWiki
 8+ * @addtogroup Extensions
 9+ * @license See 'COPYING'
 10+ * @file
 11+ */
 12+
 13+$messages = array();
 14+
 15+$messages['en'] = array(
 16+ 'lua_desc' => 'Extends the parser with support for embedded blocks of [http://www.lua.org/ Lua] code',
 17+ 'lua_error' => 'Error on line $1',
 18+ 'lua_interp_notfound' => 'Lua interpreter not found',
 19+ 'lua_error_internal' => 'Internal error',
 20+ 'lua_overflow_recursion' => 'Recursion limit reached',
 21+ 'lua_overflow_loc' => 'Maximum lines of code limit reached',
 22+ 'lua_overflow_time' => 'Maximum execution time reached',
 23+);
Property changes on: trunk/extensions/Lua/Lua.i18n.php
___________________________________________________________________
Added: svn:eol-style
124 + native
Index: trunk/extensions/Lua/Lua.php
@@ -0,0 +1,47 @@
 2+<?php
 3+/**
 4+ * Lua parser extensions for MediaWiki
 5+ *
 6+ * @author Fran Rogers
 7+ * @package MediaWiki
 8+ * @addtogroup Extensions
 9+ * @license See 'COPYING'
 10+ * @file
 11+ */
 12+
 13+$wgExtensionCredits['parserhook'][] = array(
 14+ 'name' => 'Lua parser extensions',
 15+ 'author' => 'Fran Rogers',
 16+ 'svn-date' => '$LastChangedDate$',
 17+ 'svn-revision' => '$LastChangedRevision$',
 18+ 'url' => 'http://www.mediawiki.org/wiki/Extension:Lua',
 19+ 'description' => 'Extends the parser with support for embedded blocks ofLua code',
 20+ 'descriptionmsg' => 'lua_desc',
 21+);
 22+
 23+$wgExtensionMessagesFiles['Lua'] = dirname(__FILE__) . '/Lua.i18n.php';
 24+require_once(dirname(__FILE__) . '/Lua.body.php');
 25+$wgLuaWrapperFile = dirname(__FILE__) . '/LuaWrapper.lua';
 26+
 27+if (!isset($wgLuaExternalInterpreter))
 28+ $wgLuaExternalInterpreter = FALSE;
 29+if (!isset($wgLuaExternalInterpreter))
 30+ $wgLuaExternalInterpreter = FALSE;
 31+if (!isset($wgLuaMaxLines))
 32+ $wgLuaMaxLines = 1000000;
 33+if (!isset($wgLuaMaxCalls))
 34+ $wgLuaMaxCalls = 2000;
 35+if (!isset($wgLuaMaxTime))
 36+ $wgLuaMaxTime = 5;
 37+
 38+# Avoid unstubbing $wgParser on setHook() too early on modern (1.12+) MW versions, as per r35980
 39+if (defined('MW_SUPPORTS_PARSERFIRSTCALLINIT')) {
 40+ $wgHooks['ParserFirstCallInit'][] = 'efLua_ParserInit';
 41+} else { // Otherwise do things the old fashioned way
 42+ $wgExtensionFunctions[] = 'efLua_ParserInit';
 43+}
 44+# Define a setup function
 45+$wgExtensionFunctions[] = 'efLua_FunctionSetup';
 46+# Add a hook to initialise the magic word
 47+$wgHooks['LanguageGetMagic'][] = 'efLua_Magic';
 48+$wgHooks['ParserBeforeTidy'][] = 'efLua_BeforeTidy';
Property changes on: trunk/extensions/Lua/Lua.php
___________________________________________________________________
Added: svn:eol-style
149 + native
Index: trunk/extensions/Lua/COPYING
@@ -0,0 +1,18 @@
 2+Lua parser extensions for MediaWiki
 3+Copyright (C) 2008 Fran Rogers
 4+
 5+This software is provided 'as-is', without any express or implied
 6+warranty. In no event will the authors be held liable for any damages
 7+arising from the use of this software.
 8+
 9+Permission is granted to anyone to use this software for any purpose,
 10+including commercial applications, and to alter it and redistribute it
 11+freely, subject to the following restrictions:
 12+
 13+1. The origin of this software must not be misrepresented; you must not
 14+ claim that you wrote the original software. If you use this software
 15+ in a product, an acknowledgment in the product documentation would be
 16+ appreciated but is not required.
 17+2. Altered source versions must be plainly marked as such, and must not be
 18+ misrepresented as being the original software.
 19+3. This notice may not be removed or altered from any source distribution.
Index: trunk/extensions/Lua/README
@@ -0,0 +1,3 @@
 2+README forthcoming. :)
 3+
 4+See http://www.mediawiki.org/wiki/Extension:Lua for now.
\ No newline at end of file

Status & tagging log