From 8b50a3b36bc1a012cdd7e2f02dfc8a6762d30c4e Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Sun, 30 Sep 2012 15:42:36 +0000 Subject: [PATCH] Improve compatibility with lua 5.2. --- src/core/org/luaj/vm2/lib/OsLib.java | 139 ++++++++++-------- src/jse/org/luaj/vm2/lib/jse/JseOsLib.java | 24 ++- src/jse/org/luaj/vm2/lib/jse/JseProcess.java | 116 +++++++++++++++ test/junit/org/luaj/vm2/ScriptDrivenTest.java | 67 +-------- test/lua/oslib.lua | 3 +- 5 files changed, 209 insertions(+), 140 deletions(-) create mode 100644 src/jse/org/luaj/vm2/lib/jse/JseProcess.java diff --git a/src/core/org/luaj/vm2/lib/OsLib.java b/src/core/org/luaj/vm2/lib/OsLib.java index 043dfeea..b80f391a 100644 --- a/src/core/org/luaj/vm2/lib/OsLib.java +++ b/src/core/org/luaj/vm2/lib/OsLib.java @@ -23,9 +23,19 @@ package org.luaj.vm2.lib; import java.io.IOException; +import org.luaj.vm2.Globals; import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaValue; import org.luaj.vm2.Varargs; +import org.luaj.vm2.lib.PackageLib.java_searcher; +import org.luaj.vm2.lib.PackageLib.loadlib; +import org.luaj.vm2.lib.PackageLib.lua_searcher; +import org.luaj.vm2.lib.PackageLib.preload_searcher; +import org.luaj.vm2.lib.PackageLib.require; +import org.luaj.vm2.lib.PackageLib.searchpath; +import org.luaj.vm2.lib.jme.JmePlatform; +import org.luaj.vm2.lib.jse.JseOsLib; +import org.luaj.vm2.lib.jse.JsePlatform; /** * Subclass of {@link LibFunction} which implements the standard lua {@code os} library. @@ -72,22 +82,21 @@ import org.luaj.vm2.Varargs; * @see JmePlatform * @see http://www.lua.org/manual/5.1/manual.html#5.8 */ -public class OsLib extends VarArgFunction { +public class OsLib extends OneArgFunction { public static String TMP_PREFIX = ".luaj"; public static String TMP_SUFFIX = "tmp"; - private static final int INIT = 0; - private static final int CLOCK = 1; - private static final int DATE = 2; - private static final int DIFFTIME = 3; - private static final int EXECUTE = 4; - private static final int EXIT = 5; - private static final int GETENV = 6; - private static final int REMOVE = 7; - private static final int RENAME = 8; - private static final int SETLOCALE = 9; - private static final int TIME = 10; - private static final int TMPNAME = 11; + private static final int CLOCK = 0; + private static final int DATE = 1; + private static final int DIFFTIME = 2; + private static final int EXECUTE = 3; + private static final int EXIT = 4; + private static final int GETENV = 5; + private static final int REMOVE = 6; + private static final int RENAME = 7; + private static final int SETLOCALE = 8; + private static final int TIME = 9; + private static final int TMPNAME = 10; private static final String[] NAMES = { "clock", @@ -106,61 +115,69 @@ public class OsLib extends VarArgFunction { private static final long t0 = System.currentTimeMillis(); private static long tmpnames = t0; + protected Globals globals; + /** * Create and OsLib instance. */ public OsLib() { } - - public LuaValue init(LuaValue env) { - LuaTable t = new LuaTable(); - bind(t, this.getClass(), NAMES, CLOCK); - env.set("os", t); - env.get("package").get("loaded").set("os", t); - return t; + + public LuaValue call(LuaValue env) { + globals = env.checkglobals(); + LuaTable os = new LuaTable(); + for (int i = 0; i < NAMES.length; ++i) + os.set(NAMES[i], new OsLibFunc(i, NAMES[i])); + env.set("os", os); + globals.package_.loaded.set("os", os); + return os; } - public Varargs invoke(Varargs args) { - try { - switch ( opcode ) { - case INIT: - return init(args.arg1()); - case CLOCK: - return valueOf(clock()); - case DATE: { - String s = args.optjstring(1, null); - double t = args.optdouble(2,-1); - return valueOf( date(s, t==-1? System.currentTimeMillis()/1000.: t) ); - } - case DIFFTIME: - return valueOf(difftime(args.checkdouble(1),args.checkdouble(2))); - case EXECUTE: - return valueOf(execute(args.optjstring(1, null))); - case EXIT: - exit(args.optint(1, 0)); + class OsLibFunc extends VarArgFunction { + public OsLibFunc(int opcode, String name) { + this.opcode = opcode; + this.name = name; + } + public Varargs invoke(Varargs args) { + try { + switch ( opcode ) { + case CLOCK: + return valueOf(clock()); + case DATE: { + String s = args.optjstring(1, null); + double t = args.optdouble(2,-1); + return valueOf( date(s, t==-1? System.currentTimeMillis()/1000.: t) ); + } + case DIFFTIME: + return valueOf(difftime(args.checkdouble(1),args.checkdouble(2))); + case EXECUTE: + return execute(args.optjstring(1, null)); + case EXIT: + exit(args.optint(1, 0)); + return NONE; + case GETENV: { + final String val = getenv(args.checkjstring(1)); + return val!=null? valueOf(val): NIL; + } + case REMOVE: + remove(args.checkjstring(1)); + return LuaValue.TRUE; + case RENAME: + rename(args.checkjstring(1), args.checkjstring(2)); + return LuaValue.TRUE; + case SETLOCALE: { + String s = setlocale(args.optjstring(1,null), args.optjstring(2, "all")); + return s!=null? valueOf(s): NIL; + } + case TIME: + return valueOf(time(args.arg1().isnil()? null: args.checktable(1))); + case TMPNAME: + return valueOf(tmpname()); + } return NONE; - case GETENV: { - final String val = getenv(args.checkjstring(1)); - return val!=null? valueOf(val): NIL; + } catch ( IOException e ) { + return varargsOf(NIL, valueOf(e.getMessage())); } - case REMOVE: - remove(args.checkjstring(1)); - return LuaValue.TRUE; - case RENAME: - rename(args.checkjstring(1), args.checkjstring(2)); - return LuaValue.TRUE; - case SETLOCALE: { - String s = setlocale(args.optjstring(1,null), args.optjstring(2, "all")); - return s!=null? valueOf(s): NIL; - } - case TIME: - return valueOf(time(args.arg1().isnil()? null: args.checktable(1))); - case TMPNAME: - return valueOf(tmpname()); - } - return NONE; - } catch ( IOException e ) { - return varargsOf(NIL, valueOf(e.getMessage())); } } @@ -219,8 +236,8 @@ public class OsLib extends VarArgFunction { * is available and zero otherwise. * @param command command to pass to the system */ - protected int execute(String command) { - return 0; + protected Varargs execute(String command) { + return varargsOf(NIL, valueOf("exit"), ONE); } /** diff --git a/src/jse/org/luaj/vm2/lib/jse/JseOsLib.java b/src/jse/org/luaj/vm2/lib/jse/JseOsLib.java index 9952cd1c..082d7c20 100644 --- a/src/jse/org/luaj/vm2/lib/jse/JseOsLib.java +++ b/src/jse/org/luaj/vm2/lib/jse/JseOsLib.java @@ -24,6 +24,7 @@ import java.io.File; import java.io.IOException; import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; import org.luaj.vm2.lib.LibFunction; /** @@ -79,23 +80,20 @@ public class JseOsLib extends org.luaj.vm2.lib.OsLib { public JseOsLib() { } - protected int execute(String command) { - Runtime r = Runtime.getRuntime(); + protected Varargs execute(String command) { + int exitValue; try { - final Process p = r.exec(command); - try { - p.waitFor(); - return p.exitValue(); - } finally { - p.destroy(); - } + exitValue = new JseProcess(command, null, globals.STDOUT, globals.STDERR).waitFor(); } catch (IOException ioe) { - return EXEC_IOEXCEPTION; + exitValue = EXEC_IOEXCEPTION; } catch (InterruptedException e) { - return EXEC_INTERRUPTED; + exitValue = EXEC_INTERRUPTED; } catch (Throwable t) { - return EXEC_ERROR; - } + exitValue = EXEC_ERROR; + } + if (exitValue == 0) + return varargsOf(TRUE, valueOf("exit"), ZERO); + return varargsOf(NIL, valueOf("signal"), valueOf(exitValue)); } protected void remove(String filename) throws IOException { diff --git a/src/jse/org/luaj/vm2/lib/jse/JseProcess.java b/src/jse/org/luaj/vm2/lib/jse/JseProcess.java new file mode 100644 index 00000000..63b24495 --- /dev/null +++ b/src/jse/org/luaj/vm2/lib/jse/JseProcess.java @@ -0,0 +1,116 @@ +/******************************************************************************* +* Copyright (c) 2012 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib.jse; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** Analog of Process that pipes input and output to client-specified streams. + */ +public class JseProcess { + + final Process process; + final Thread input,output,error; + + /** Construct a process around a command, with specified streams to redirect input and output to. + * + * @param cmd The command to execute, including arguments, if any + * @param stdin Optional InputStream to read from as process input, or null if input is not needed. + * @param stdout Optional OutputStream to copy process output to, or null if output is ignored. + * @param stderr Optinoal OutputStream to copy process stderr output to, or null if output is ignored. + * @throws IOException If the system process could not be created. + * @see Process + */ + public JseProcess(String[] cmd, InputStream stdin, OutputStream stdout, OutputStream stderr) throws IOException { + this(Runtime.getRuntime().exec(cmd), stdin, stdout, stderr); + } + + /** Construct a process around a command, with specified streams to redirect input and output to. + * + * @param cmd The command to execute, including arguments, if any + * @param stdin Optional InputStream to read from as process input, or null if input is not needed. + * @param stdout Optional OutputStream to copy process output to, or null if output is ignored. + * @param stderr Optinoal OutputStream to copy process stderr output to, or null if output is ignored. + * @throws IOException If the system process could not be created. + * @see Process + */ + public JseProcess(String cmd, InputStream stdin, OutputStream stdout, OutputStream stderr) throws IOException { + this(Runtime.getRuntime().exec(cmd), stdin, stdout, stderr); + } + + private JseProcess(Process process, InputStream stdin, OutputStream stdout, OutputStream stderr) { + this.process = process; + input = stdin == null? null: copyBytes(stdin, process.getOutputStream(), null, process.getOutputStream()); + output = stdout == null? null: copyBytes(process.getInputStream(), stdout, process.getInputStream(), null); + error = stderr == null? null: copyBytes(process.getErrorStream(), stderr, process.getErrorStream(), null); + } + + /** Get the exit value of the process. */ + public int exitValue() { + return process.exitValue(); + } + + /** Wait for the process to complete, and all pending output to finish. + * @return The exit status. + * @throws InterruptedException + */ + public int waitFor() throws InterruptedException { + int r = process.waitFor(); + if (input != null) + input.join(); + if (output != null) + output.join(); + if (error != null) + error.join(); + process.destroy(); + return r; + } + + /** Create a thread to copy bytes from input to output. */ + private Thread copyBytes(final InputStream input, + final OutputStream output, final InputStream ownedInput, + final OutputStream ownedOutput) { + Thread t = (new Thread() { + public void run() { + try { + byte[] buf = new byte[1024]; + int r; + try { + while ((r = input.read(buf)) >= 0) { + output.write(buf, 0, r); + } + } finally { + if (ownedInput != null) + ownedInput.close(); + if (ownedOutput != null) + ownedOutput.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + t.start(); + return t; + } +} diff --git a/test/junit/org/luaj/vm2/ScriptDrivenTest.java b/test/junit/org/luaj/vm2/ScriptDrivenTest.java index dd49ca35..2ae2a476 100644 --- a/test/junit/org/luaj/vm2/ScriptDrivenTest.java +++ b/test/junit/org/luaj/vm2/ScriptDrivenTest.java @@ -34,8 +34,8 @@ import java.net.URL; import junit.framework.TestCase; -import org.luaj.vm2.lib.BaseLib; import org.luaj.vm2.lib.ResourceFinder; +import org.luaj.vm2.lib.jse.JseProcess; import org.luaj.vm2.luajc.LuaJC; abstract @@ -235,69 +235,8 @@ public class ScriptDrivenTest extends TestCase implements ResourceFinder { throws IOException, InterruptedException { Runtime r = Runtime.getRuntime(); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - final Process p = r.exec(cmd); - try { - // start a thread to write the given input to the subprocess. - Thread inputCopier = (new Thread() { - public void run() { - try { - OutputStream processStdIn = p.getOutputStream(); - try { - copy(input, processStdIn); - } finally { - processStdIn.close(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - }); - inputCopier.start(); - - // start another thread to read output from the subprocess. - Thread outputCopier = (new Thread() { - public void run() { - try { - InputStream processStdOut = p.getInputStream(); - try { - copy(processStdOut, baos); - } finally { - processStdOut.close(); - } - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - }); - outputCopier.start(); - - // start another thread to read output from the subprocess. - Thread errorCopier = (new Thread() { - public void run() { - try { - InputStream processError = p.getErrorStream(); - try { - copy(processError, System.err); - } finally { - processError.close(); - } - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - }); - errorCopier.start(); - - p.waitFor(); - inputCopier.join(); - outputCopier.join(); - errorCopier.join(); - - return new String(baos.toByteArray()); - - } finally { - p.destroy(); - } + new JseProcess(cmd, input, baos, System.err).waitFor(); + return new String(baos.toByteArray()); } private String readString(InputStream is) throws IOException { diff --git a/test/lua/oslib.lua b/test/lua/oslib.lua index a46c4bdb..4ca5da54 100644 --- a/test/lua/oslib.lua +++ b/test/lua/oslib.lua @@ -8,13 +8,12 @@ -- local pcall = function(...) local s,e,f = pcall(...) - return s,type(e) + return s,type(e),type(f) end print( 'os', type(os) ) print( 'os.clock()', pcall( os.clock ) ) print( 'os.date()', pcall( os.date ) ) print( 'os.difftime(123000, 21500)', pcall( os.difftime, 123000, 21250 ) ) -print( 'os.execute("bogus")', pcall( os.execute, '' ) ) print( 'os.getenv()', pcall( os.getenv ) ) print( 'os.getenv("bogus.key")', pcall( os.getenv, 'bogus.key' ) ) local s,p = pcall( os.tmpname )