diff --git a/core/src/main/java/org/luaj/vm2/libs/BaseLib.java b/core/src/main/java/org/luaj/vm2/libs/BaseLib.java new file mode 100644 index 00000000..c94bc753 --- /dev/null +++ b/core/src/main/java/org/luaj/vm2/libs/BaseLib.java @@ -0,0 +1,485 @@ +/******************************************************************************* +* Copyright (c) 2009 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.libs; + +import java.io.IOException; +import java.io.InputStream; + +import org.luaj.vm2.Globals; +import org.luaj.vm2.Lua; +import org.luaj.vm2.LuaError; +import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaThread; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; + +/** + * Subclass of {@link LibFunction} which implements the lua basic library functions. + *
+ * This contains all library functions listed as "basic functions" in the lua documentation for JME. + * The functions dofile and loadfile use the + * {@link Globals#finder} instance to find resource files. + * Since JME has no file system by default, {@link BaseLib} implements + * {@link ResourceFinder} using {@link Class#getResource(String)}, + * which is the closest equivalent on JME. + * The default loader chain in {@link PackageLib} will use these as well. + *
+ * To use basic library functions that include a {@link ResourceFinder} based on + * directory lookup, use {@link org.luaj.vm2.libs.jse.JseBaseLib} instead. + *
+ * Typically, this library is included as part of a call to either + * {@link org.luaj.vm2.libs.jse.JsePlatform#standardGlobals()} or + * {@link org.luaj.vm2.libs.jme.JmePlatform#standardGlobals()} + *
{@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * globals.get("print").call(LuaValue.valueOf("hello, world"));
+ * }
+ * + * For special cases where the smallest possible footprint is desired, + * a minimal set of libraries could be loaded + * directly via {@link Globals#load(LuaValue)} using code such as: + *
{@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.get("print").call(LuaValue.valueOf("hello, world"));
+ * }
+ * Doing so will ensure the library is properly initialized
+ * and loaded into the globals table.
+ * + * This is a direct port of the corresponding library in C. + * @see org.luaj.vm2.libs.jse.JseBaseLib + * @see ResourceFinder + * @see Globals#finder + * @see LibFunction + * @see org.luaj.vm2.libs.jse.JsePlatform + * @see org.luaj.vm2.libs.jme.JmePlatform + * @see Lua 5.2 Base Lib Reference + */ +public class BaseLib extends TwoArgFunction implements ResourceFinder { + + Globals globals; + + + /** Perform one-time initialization on the library by adding base functions + * to the supplied environment, and returning it as the return value. + * @param modname the module name supplied if this is loaded via 'require'. + * @param env the environment to load into, which must be a Globals instance. + */ + public LuaValue call(LuaValue modname, LuaValue env) { + globals = env.checkglobals(); + globals.finder = this; + globals.baselib = this; + env.set( "_G", env ); + env.set( "_VERSION", Lua._VERSION ); + env.set("assert", new _assert()); + env.set("collectgarbage", new collectgarbage()); + env.set("dofile", new dofile()); + env.set("error", new error()); + env.set("getmetatable", new getmetatable()); + env.set("load", new load()); + env.set("loadfile", new loadfile()); + env.set("pcall", new pcall()); + env.set("print", new print(this)); + env.set("rawequal", new rawequal()); + env.set("rawget", new rawget()); + env.set("rawlen", new rawlen()); + env.set("rawset", new rawset()); + env.set("select", new select()); + env.set("setmetatable", new setmetatable()); + env.set("tonumber", new tonumber()); + env.set("tostring", new tostring()); + env.set("type", new type()); + env.set("xpcall", new xpcall()); + + next next; + env.set("next", next = new next()); + env.set("pairs", new pairs(next)); + env.set("ipairs", new ipairs()); + + return env; + } + + /** ResourceFinder implementation + * + * Tries to open the file as a resource, which can work for JSE and JME. + */ + public InputStream findResource(String filename) { + return getClass().getResourceAsStream(filename.startsWith("/")? filename: "/"+filename); + } + + + // "assert", // ( v [,message] ) -> v, message | ERR + static final class _assert extends VarArgFunction { + public Varargs invoke(Varargs args) { + if ( !args.arg1().toboolean() ) + error( args.narg()>1? args.optjstring(2,"assertion failed!"): "assertion failed!" ); + return args; + } + } + + // "collectgarbage", // ( opt [,arg] ) -> value + static final class collectgarbage extends VarArgFunction { + public Varargs invoke(Varargs args) { + String s = args.optjstring(1, "collect"); + if ( "collect".equals(s) ) { + System.gc(); + return ZERO; + } else if ( "count".equals(s) ) { + Runtime rt = Runtime.getRuntime(); + long used = rt.totalMemory() - rt.freeMemory(); + return varargsOf(valueOf(used/1024.), valueOf(used%1024)); + } else if ( "step".equals(s) ) { + System.gc(); + return LuaValue.TRUE; + } else { + argerror(1, "invalid option '" + s + "'"); + } + return NIL; + } + } + + // "dofile", // ( filename ) -> result1, ... + final class dofile extends VarArgFunction { + public Varargs invoke(Varargs args) { + args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil"); + String filename = args.isstring(1)? args.tojstring(1): null; + Varargs v = filename == null? + loadStream( globals.STDIN, "=stdin", "bt", globals ): + loadFile( args.checkjstring(1), "bt", globals ); + return v.isnil(1)? error(v.tojstring(2)): v.arg1().invoke(); + } + } + + // "error", // ( message [,level] ) -> ERR + static final class error extends TwoArgFunction { + public LuaValue call(LuaValue arg1, LuaValue arg2) { + if (arg1.isnil()) throw new LuaError(NIL); + if (!arg1.isstring() || arg2.optint(1) == 0) throw new LuaError(arg1); + throw new LuaError(arg1.tojstring(), arg2.optint(1)); + } + } + + // "getmetatable", // ( object ) -> table + static final class getmetatable extends LibFunction { + public LuaValue call() { + return argerror(1, "value expected"); + } + public LuaValue call(LuaValue arg) { + LuaValue mt = arg.getmetatable(); + return mt!=null? mt.rawget(METATABLE).optvalue(mt): NIL; + } + } + // "load", // ( ld [, source [, mode [, env]]] ) -> chunk | nil, msg + final class load extends VarArgFunction { + public Varargs invoke(Varargs args) { + LuaValue ld = args.arg1(); + if (!ld.isstring() && !ld.isfunction()) { + throw new LuaError("bad argument #1 to 'load' (string or function expected, got " + ld.typename() + ")"); + } + String source = args.optjstring(2, ld.isstring()? ld.tojstring(): "=(load)"); + String mode = args.optjstring(3, "bt"); + LuaValue env = args.optvalue(4, globals); + return loadStream(ld.isstring()? ld.strvalue().toInputStream(): + new StringInputStream(ld.checkfunction()), source, mode, env); + } + } + + // "loadfile", // ( [filename [, mode [, env]]] ) -> chunk | nil, msg + final class loadfile extends VarArgFunction { + public Varargs invoke(Varargs args) { + args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil"); + String filename = args.isstring(1)? args.tojstring(1): null; + String mode = args.optjstring(2, "bt"); + LuaValue env = args.optvalue(3, globals); + return filename == null? + loadStream( globals.STDIN, "=stdin", mode, env ): + loadFile( filename, mode, env ); + } + } + + // "pcall", // (f, arg1, ...) -> status, result1, ... + final class pcall extends VarArgFunction { + public Varargs invoke(Varargs args) { + LuaValue func = args.checkvalue(1); + if (globals != null && globals.debuglib != null) + globals.debuglib.onCall(this); + try { + return varargsOf(TRUE, func.invoke(args.subargs(2))); + } catch ( LuaError le ) { + final LuaValue m = le.getMessageObject(); + return varargsOf(FALSE, m!=null? m: NIL); + } catch ( Exception e ) { + final String m = e.getMessage(); + return varargsOf(FALSE, valueOf(m!=null? m: e.toString())); + } finally { + if (globals != null && globals.debuglib != null) + globals.debuglib.onReturn(); + } + } + } + + // "print", // (...) -> void + final class print extends VarArgFunction { + final BaseLib baselib; + print(BaseLib baselib) { + this.baselib = baselib; + } + public Varargs invoke(Varargs args) { + LuaValue tostring = globals.get("tostring"); + for ( int i=1, n=args.narg(); i<=n; i++ ) { + if ( i>1 ) globals.STDOUT.print( '\t' ); + LuaString s = tostring.call( args.arg(i) ).strvalue(); + globals.STDOUT.print(s.tojstring()); + } + globals.STDOUT.print('\n'); + return NONE; + } + } + + + // "rawequal", // (v1, v2) -> boolean + static final class rawequal extends LibFunction { + public LuaValue call() { + return argerror(1, "value expected"); + } + public LuaValue call(LuaValue arg) { + return argerror(2, "value expected"); + } + public LuaValue call(LuaValue arg1, LuaValue arg2) { + return valueOf(arg1.raweq(arg2)); + } + } + + // "rawget", // (table, index) -> value + static final class rawget extends TableLibFunction { + public LuaValue call(LuaValue arg) { + return argerror(2, "value expected"); + } + public LuaValue call(LuaValue arg1, LuaValue arg2) { + return arg1.checktable().rawget(arg2); + } + } + + + // "rawlen", // (v) -> value + static final class rawlen extends LibFunction { + public LuaValue call(LuaValue arg) { + return valueOf(arg.rawlen()); + } + } + + // "rawset", // (table, index, value) -> table + static final class rawset extends TableLibFunction { + public LuaValue call(LuaValue table) { + return argerror(2,"value expected"); + } + public LuaValue call(LuaValue table, LuaValue index) { + return argerror(3,"value expected"); + } + public LuaValue call(LuaValue table, LuaValue index, LuaValue value) { + LuaTable t = table.checktable(); + if (!index.isvalidkey()) argerror(2, "table index is nil"); + t.rawset(index, value); + return t; + } + } + + // "select", // (f, ...) -> value1, ... + static final class select extends VarArgFunction { + public Varargs invoke(Varargs args) { + int n = args.narg()-1; + if ( args.arg1().equals(valueOf("#")) ) + return valueOf(n); + int i = args.checkint(1); + if ( i == 0 || i < -n ) + argerror(1,"index out of range"); + return args.subargs(i<0? n+i+2: i+1); + } + } + + // "setmetatable", // (table, metatable) -> table + static final class setmetatable extends TableLibFunction { + public LuaValue call(LuaValue table) { + return argerror(2,"nil or table expected"); + } + public LuaValue call(LuaValue table, LuaValue metatable) { + final LuaValue mt0 = table.checktable().getmetatable(); + if ( mt0!=null && !mt0.rawget(METATABLE).isnil() ) + error("cannot change a protected metatable"); + return table.setmetatable(metatable.isnil()? null: metatable.checktable()); + } + } + + // "tonumber", // (e [,base]) -> value + static final class tonumber extends LibFunction { + public LuaValue call(LuaValue e) { + return e.tonumber(); + } + public LuaValue call(LuaValue e, LuaValue base) { + if (base.isnil()) + return e.tonumber(); + final int b = base.checkint(); + if ( b < 2 || b > 36 ) + argerror(2, "base out of range"); + return e.checkstring().tonumber(b); + } + } + + // "tostring", // (e) -> value + static final class tostring extends LibFunction { + public LuaValue call(LuaValue arg) { + LuaValue h = arg.metatag(TOSTRING); + if ( ! h.isnil() ) + return h.call(arg); + LuaValue v = arg.tostring(); + if ( ! v.isnil() ) + return v; + return valueOf(arg.tojstring()); + } + } + + // "type", // (v) -> value + static final class type extends LibFunction { + public LuaValue call(LuaValue arg) { + return valueOf(arg.typename()); + } + } + + // "xpcall", // (f, err) -> result1, ... + final class xpcall extends VarArgFunction { + public Varargs invoke(Varargs args) { + final LuaThread t = globals.running; + final LuaValue preverror = t.errorfunc; + t.errorfunc = args.checkvalue(2); + try { + if (globals != null && globals.debuglib != null) + globals.debuglib.onCall(this); + try { + return varargsOf(TRUE, args.arg1().invoke(args.subargs(3))); + } catch ( LuaError le ) { + final LuaValue m = le.getMessageObject(); + return varargsOf(FALSE, m!=null? m: NIL); + } catch ( Exception e ) { + final String m = e.getMessage(); + return varargsOf(FALSE, valueOf(m!=null? m: e.toString())); + } finally { + if (globals != null && globals.debuglib != null) + globals.debuglib.onReturn(); + } + } finally { + t.errorfunc = preverror; + } + } + } + + // "pairs" (t) -> iter-func, t, nil + static final class pairs extends VarArgFunction { + final next next; + pairs(next next) { + this.next = next; + } + public Varargs invoke(Varargs args) { + return varargsOf( next, args.checktable(1), NIL ); + } + } + + // // "ipairs", // (t) -> iter-func, t, 0 + static final class ipairs extends VarArgFunction { + inext inext = new inext(); + public Varargs invoke(Varargs args) { + return varargsOf( inext, args.checktable(1), ZERO ); + } + } + + // "next" ( table, [index] ) -> next-index, next-value + static final class next extends VarArgFunction { + public Varargs invoke(Varargs args) { + return args.checktable(1).next(args.arg(2)); + } + } + + // "inext" ( table, [int-index] ) -> next-index, next-value + static final class inext extends VarArgFunction { + public Varargs invoke(Varargs args) { + return args.checktable(1).inext(args.arg(2)); + } + } + + /** + * Load from a named file, returning the chunk or nil,error of can't load + * @param env + * @param mode + * @return Varargs containing chunk, or NIL,error-text on error + */ + public Varargs loadFile(String filename, String mode, LuaValue env) { + InputStream is = globals.finder.findResource(filename); + if ( is == null ) + return varargsOf(NIL, valueOf("cannot open "+filename+": No such file or directory")); + try { + return loadStream(is, "@"+filename, mode, env); + } finally { + try { + is.close(); + } catch ( Exception e ) { + e.printStackTrace(); + } + } + } + + public Varargs loadStream(InputStream is, String chunkname, String mode, LuaValue env) { + try { + if ( is == null ) + return varargsOf(NIL, valueOf("not found: "+chunkname)); + return globals.load(is, chunkname, mode, env); + } catch (Exception e) { + return varargsOf(NIL, valueOf(e.getMessage())); + } + } + + + private static class StringInputStream extends InputStream { + final LuaValue func; + byte[] bytes; + int offset, remaining = 0; + StringInputStream(LuaValue func) { + this.func = func; + } + public int read() throws IOException { + if ( remaining < 0 ) + return -1; + if ( remaining == 0 ) { + LuaValue s = func.call(); + if ( s.isnil() ) + return remaining = -1; + LuaString ls = s.strvalue(); + bytes = ls.m_bytes; + offset = ls.m_offset; + remaining = ls.m_length; + if (remaining <= 0) + return -1; + } + --remaining; + return 0xFF&bytes[offset++]; + } + } +} diff --git a/core/src/main/java/org/luaj/vm2/libs/Bit32Lib.java b/core/src/main/java/org/luaj/vm2/libs/Bit32Lib.java new file mode 100644 index 00000000..556918aa --- /dev/null +++ b/core/src/main/java/org/luaj/vm2/libs/Bit32Lib.java @@ -0,0 +1,224 @@ +/******************************************************************************* +* 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.libs; + +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; + +/** + * Subclass of LibFunction that implements the Lua standard {@code bit32} library. + *
+ * Typically, this library is included as part of a call to either + * {@link org.luaj.vm2.libs.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.libs.jme.JmePlatform#standardGlobals()} + *
{@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * System.out.println( globals.get("bit32").get("bnot").call( LuaValue.valueOf(2) ) );
+ * }
+ * + * To instantiate and use it directly, + * link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as: + *
{@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.load(new PackageLib());
+ * globals.load(new Bit32Lib());
+ * System.out.println( globals.get("bit32").get("bnot").call( LuaValue.valueOf(2) ) );
+ * }
+ * + * This has been implemented to match as closely as possible the behavior in the corresponding library in C. + * @see LibFunction + * @see org.luaj.vm2.libs.jse.JsePlatform + * @see org.luaj.vm2.libs.jme.JmePlatform + * @see Lua 5.2 Bitwise Operation Lib Reference + */ +public class Bit32Lib extends TwoArgFunction { + + public Bit32Lib() { + } + + /** Perform one-time initialization on the library by creating a table + * containing the library functions, adding that table to the supplied environment, + * adding the table to package.loaded, and returning table as the return value. + * @param modname the module name supplied if this is loaded via 'require'. + * @param env the environment to load into, which must be a Globals instance. + */ + public LuaValue call(LuaValue modname, LuaValue env) { + LuaTable t = new LuaTable(); + bind(t, Bit32LibV.class, new String[] { + "band", "bnot", "bor", "btest", "bxor", "extract", "replace" + }); + bind(t, Bit32Lib2.class, new String[] { + "arshift", "lrotate", "lshift", "rrotate", "rshift" + }); + env.set("bit32", t); + if (!env.get("package").isnil()) env.get("package").get("loaded").set("bit32", t); + return t; + } + + static final class Bit32LibV extends VarArgFunction { + public Varargs invoke(Varargs args) { + switch ( opcode ) { + case 0: return Bit32Lib.band( args ); + case 1: return Bit32Lib.bnot( args ); + case 2: return Bit32Lib.bor( args ); + case 3: return Bit32Lib.btest( args ); + case 4: return Bit32Lib.bxor( args ); + case 5: + return Bit32Lib.extract( args.checkint(1), args.checkint(2), args.optint(3, 1) ); + case 6: + return Bit32Lib.replace( args.checkint(1), args.checkint(2), + args.checkint(3), args.optint(4, 1) ); + } + return NIL; + } + } + + static final class Bit32Lib2 extends TwoArgFunction { + + public LuaValue call(LuaValue arg1, LuaValue arg2) { + switch ( opcode ) { + case 0: return Bit32Lib.arshift(arg1.checkint(), arg2.checkint()); + case 1: return Bit32Lib.lrotate(arg1.checkint(), arg2.checkint()); + case 2: return Bit32Lib.lshift(arg1.checkint(), arg2.checkint()); + case 3: return Bit32Lib.rrotate(arg1.checkint(), arg2.checkint()); + case 4: return Bit32Lib.rshift(arg1.checkint(), arg2.checkint()); + } + return NIL; + } + + } + + static LuaValue arshift(int x, int disp) { + if (disp >= 0) { + return bitsToValue(x >> disp); + } else { + return bitsToValue(x << -disp); + } + } + + static LuaValue rshift(int x, int disp) { + if (disp >= 32 || disp <= -32) { + return ZERO; + } else if (disp >= 0) { + return bitsToValue(x >>> disp); + } else { + return bitsToValue(x << -disp); + } + } + + static LuaValue lshift(int x, int disp) { + if (disp >= 32 || disp <= -32) { + return ZERO; + } else if (disp >= 0) { + return bitsToValue(x << disp); + } else { + return bitsToValue(x >>> -disp); + } + } + + static Varargs band( Varargs args ) { + int result = -1; + for ( int i = 1; i <= args.narg(); i++ ) { + result &= args.checkint(i); + } + return bitsToValue( result ); + } + + static Varargs bnot( Varargs args ) { + return bitsToValue( ~args.checkint(1) ); + } + + static Varargs bor( Varargs args ) { + int result = 0; + for ( int i = 1; i <= args.narg(); i++ ) { + result |= args.checkint(i); + } + return bitsToValue( result ); + } + + static Varargs btest( Varargs args ) { + int bits = -1; + for ( int i = 1; i <= args.narg(); i++ ) { + bits &= args.checkint(i); + } + return valueOf( bits != 0 ); + } + + static Varargs bxor( Varargs args ) { + int result = 0; + for ( int i = 1; i <= args.narg(); i++ ) { + result ^= args.checkint(i); + } + return bitsToValue( result ); + } + + static LuaValue lrotate(int x, int disp) { + if (disp < 0) { + return rrotate(x, -disp); + } else { + disp = disp & 31; + return bitsToValue((x << disp) | (x >>> (32 - disp))); + } + } + + static LuaValue rrotate(int x, int disp) { + if (disp < 0) { + return lrotate(x, -disp); + } else { + disp = disp & 31; + return bitsToValue((x >>> disp) | (x << (32 - disp))); + } + } + + static LuaValue extract(int n, int field, int width) { + if (field < 0) { + argerror(2, "field cannot be negative"); + } + if (width < 0) { + argerror(3, "width must be postive"); + } + if (field + width > 32) { + error("trying to access non-existent bits"); + } + return bitsToValue((n >>> field) & (-1 >>> (32 - width))); + } + + static LuaValue replace(int n, int v, int field, int width) { + if (field < 0) { + argerror(3, "field cannot be negative"); + } + if (width < 0) { + argerror(4, "width must be postive"); + } + if (field + width > 32) { + error("trying to access non-existent bits"); + } + int mask = (-1 >>> (32 - width)) << field; + n = (n & ~mask) | ((v << field) & mask); + return bitsToValue(n); + } + + private static LuaValue bitsToValue( int x ) { + return ( x < 0 ) ? valueOf((double) ((long) x & 0xFFFFFFFFL)) : valueOf(x); + } +} diff --git a/core/src/main/java/org/luaj/vm2/libs/CoroutineLib.java b/core/src/main/java/org/luaj/vm2/libs/CoroutineLib.java new file mode 100644 index 00000000..f70607ca --- /dev/null +++ b/core/src/main/java/org/luaj/vm2/libs/CoroutineLib.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (c) 2007-2011 LuaJ. 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.libs; + +import org.luaj.vm2.Globals; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaThread; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; + +/** + * Subclass of {@link LibFunction} which implements the lua standard {@code coroutine} + * library. + *
+ * The coroutine library in luaj has the same behavior as the + * coroutine library in C, but is implemented using Java Threads to maintain + * the call state between invocations. Therefore it can be yielded from anywhere, + * similar to the "Coco" yield-from-anywhere patch available for C-based lua. + * However, coroutines that are yielded but never resumed to complete their execution + * may not be collected by the garbage collector. + *
+ * Typically, this library is included as part of a call to either + * {@link org.luaj.vm2.libs.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.libs.jme.JmePlatform#standardGlobals()} + *
{@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * System.out.println( globals.get("coroutine").get("running").call() );
+ * }
+ * + * To instantiate and use it directly, + * link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as: + *
{@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.load(new PackageLib());
+ * globals.load(new CoroutineLib());
+ * System.out.println( globals.get("coroutine").get("running").call() );
+ * }
+ * + * @see LibFunction + * @see org.luaj.vm2.libs.jse.JsePlatform + * @see org.luaj.vm2.libs.jme.JmePlatform + * @see Lua 5.2 Coroutine Lib Reference + */ +public class CoroutineLib extends TwoArgFunction { + + static int coroutine_count = 0; + + Globals globals; + + /** Perform one-time initialization on the library by creating a table + * containing the library functions, adding that table to the supplied environment, + * adding the table to package.loaded, and returning table as the return value. + * @param modname the module name supplied if this is loaded via 'require'. + * @param env the environment to load into, which must be a Globals instance. + */ + public LuaValue call(LuaValue modname, LuaValue env) { + globals = env.checkglobals(); + LuaTable coroutine = new LuaTable(); + coroutine.set("create", new Create()); + coroutine.set("resume", new Resume()); + coroutine.set("running", new Running()); + coroutine.set("status", new Status()); + coroutine.set("yield", new Yield()); + coroutine.set("wrap", new Wrap()); + env.set("coroutine", coroutine); + if (!env.get("package").isnil()) env.get("package").get("loaded").set("coroutine", coroutine); + return coroutine; + } + + final class Create extends LibFunction { + public LuaValue call(LuaValue f) { + return new LuaThread(globals, f.checkfunction()); + } + } + + static final class Resume extends VarArgFunction { + public Varargs invoke(Varargs args) { + final LuaThread t = args.checkthread(1); + return t.resume( args.subargs(2) ); + } + } + + final class Running extends VarArgFunction { + public Varargs invoke(Varargs args) { + final LuaThread r = globals.running; + return varargsOf(r, valueOf(r.isMainThread())); + } + } + + static final class Status extends LibFunction { + public LuaValue call(LuaValue t) { + LuaThread lt = t.checkthread(); + return valueOf( lt.getStatus() ); + } + } + + private final class Yield extends VarArgFunction { + public Varargs invoke(Varargs args) { + return globals.yield( args ); + } + } + + final class Wrap extends LibFunction { + public LuaValue call(LuaValue f) { + final LuaValue func = f.checkfunction(); + final LuaThread thread = new LuaThread(globals, func); + return new wrapper(thread); + } + } + + static final class wrapper extends VarArgFunction { + final LuaThread luathread; + wrapper(LuaThread luathread) { + this.luathread = luathread; + } + public Varargs invoke(Varargs args) { + final Varargs result = luathread.resume(args); + if ( result.arg1().toboolean() ) { + return result.subargs(2); + } else { + return error( result.arg(2).tojstring() ); + } + } + } +} \ No newline at end of file diff --git a/core/src/main/java/org/luaj/vm2/libs/DebugLib.java b/core/src/main/java/org/luaj/vm2/libs/DebugLib.java new file mode 100644 index 00000000..9f07f9af --- /dev/null +++ b/core/src/main/java/org/luaj/vm2/libs/DebugLib.java @@ -0,0 +1,954 @@ +/******************************************************************************* + * Copyright (c) 2009-2011 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.libs; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.luaj.vm2.Globals; +import org.luaj.vm2.Lua; +import org.luaj.vm2.LuaBoolean; +import org.luaj.vm2.LuaClosure; +import org.luaj.vm2.LuaError; +import org.luaj.vm2.LuaFunction; +import org.luaj.vm2.LuaNil; +import org.luaj.vm2.LuaNumber; +import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaThread; +import org.luaj.vm2.LuaUserdata; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Print; +import org.luaj.vm2.Prototype; +import org.luaj.vm2.Varargs; + +/** + * Subclass of {@link LibFunction} which implements the lua standard {@code debug} + * library. + *
+ * The debug library in luaj tries to emulate the behavior of the corresponding C-based lua library. + * To do this, it must maintain a separate stack of calls to {@link LuaClosure} and {@link LibFunction} + * instances. + * Especially when lua-to-java bytecode compiling is being used + * via a {@link org.luaj.vm2.Globals.Compiler} such as {@link org.luaj.vm2.luajc.LuaJC}, + * this cannot be done in all cases. + *
+ * Typically, this library is included as part of a call to either + * {@link org.luaj.vm2.libs.jse.JsePlatform#debugGlobals()} or + * {@link org.luaj.vm2.libs.jme.JmePlatform#debugGlobals()} + *
{@code
+ * Globals globals = JsePlatform.debugGlobals();
+ * System.out.println( globals.get("debug").get("traceback").call() );
+ * }
+ * + * To instantiate and use it directly, + * link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as: + *
{@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.load(new PackageLib());
+ * globals.load(new DebugLib());
+ * System.out.println( globals.get("debug").get("traceback").call() );
+ * }
+ *
+ * This library exposes the entire state of lua code, and provides method to see and modify
+ * all underlying lua values within a Java VM so should not be exposed to client code
+ * in a shared server environment.
+ *
+ * @see LibFunction
+ * @see org.luaj.vm2.libs.jse.JsePlatform
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ * @see Lua 5.2 Debug Lib Reference
+ */
+public class DebugLib extends TwoArgFunction {
+ public static boolean CALLS;
+ public static boolean TRACE;
+ static {
+ try { CALLS = (null != System.getProperty("CALLS")); } catch (Exception e) {}
+ try { TRACE = (null != System.getProperty("TRACE")); } catch (Exception e) {}
+ }
+
+ static final LuaString LUA = valueOf("Lua");
+ private static final LuaString QMARK = valueOf("?");
+ private static final LuaString CALL = valueOf("call");
+ private static final LuaString LINE = valueOf("line");
+ private static final LuaString COUNT = valueOf("count");
+ private static final LuaString RETURN = valueOf("return");
+
+ static final LuaString FUNC = valueOf("func");
+ static final LuaString ISTAILCALL = valueOf("istailcall");
+ static final LuaString ISVARARG = valueOf("isvararg");
+ static final LuaString NUPS = valueOf("nups");
+ static final LuaString NPARAMS = valueOf("nparams");
+ static final LuaString NAME = valueOf("name");
+ static final LuaString NAMEWHAT = valueOf("namewhat");
+ static final LuaString WHAT = valueOf("what");
+ static final LuaString SOURCE = valueOf("source");
+ static final LuaString SHORT_SRC = valueOf("short_src");
+ static final LuaString LINEDEFINED = valueOf("linedefined");
+ static final LuaString LASTLINEDEFINED = valueOf("lastlinedefined");
+ static final LuaString CURRENTLINE = valueOf("currentline");
+ static final LuaString ACTIVELINES = valueOf("activelines");
+
+ Globals globals;
+
+ /** Perform one-time initialization on the library by creating a table
+ * containing the library functions, adding that table to the supplied environment,
+ * adding the table to package.loaded, and returning table as the return value.
+ * @param modname the module name supplied if this is loaded via 'require'.
+ * @param env the environment to load into, which must be a Globals instance.
+ */
+ public LuaValue call(LuaValue modname, LuaValue env) {
+ globals = env.checkglobals();
+ globals.debuglib = this;
+ LuaTable debug = new LuaTable();
+ debug.set("debug", new debug());
+ debug.set("gethook", new gethook());
+ debug.set("getinfo", new getinfo());
+ debug.set("getlocal", new getlocal());
+ debug.set("getmetatable", new getmetatable());
+ debug.set("getregistry", new getregistry());
+ debug.set("getupvalue", new getupvalue());
+ debug.set("getuservalue", new getuservalue());
+ debug.set("sethook", new sethook());
+ debug.set("setlocal", new setlocal());
+ debug.set("setmetatable", new setmetatable());
+ debug.set("setupvalue", new setupvalue());
+ debug.set("setuservalue", new setuservalue());
+ debug.set("traceback", new traceback());
+ debug.set("upvalueid", new upvalueid());
+ debug.set("upvaluejoin", new upvaluejoin());
+ env.set("debug", debug);
+ if (!env.get("package").isnil()) env.get("package").get("loaded").set("debug", debug);
+ return debug;
+ }
+
+ // debug.debug()
+ static final class debug extends ZeroArgFunction {
+ public LuaValue call() {
+ return NONE;
+ }
+ }
+
+ // debug.gethook ([thread])
+ final class gethook extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ LuaThread t = args.narg() > 0 ? args.checkthread(1): globals.running;
+ LuaThread.State s = t.state;
+ return varargsOf(
+ s.hookfunc != null? s.hookfunc: NIL,
+ valueOf((s.hookcall?"c":"")+(s.hookline?"l":"")+(s.hookrtrn?"r":"")),
+ valueOf(s.hookcount));
+ }
+ }
+
+ // debug.getinfo ([thread,] f [, what])
+ final class getinfo extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ int a=1;
+ LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
+ LuaValue func = args.arg(a++);
+ String what = args.optjstring(a++, "flnStu");
+ DebugLib.CallStack callstack = callstack(thread);
+
+ // find the stack info
+ DebugLib.CallFrame frame;
+ if ( func.isnumber() ) {
+ frame = callstack.getCallFrame(func.toint());
+ if (frame == null)
+ return NONE;
+ func = frame.f;
+ } else if ( func.isfunction() ) {
+ frame = callstack.findCallFrame(func);
+ } else {
+ return argerror(a-2, "function or level");
+ }
+
+ // start a table
+ DebugInfo ar = callstack.auxgetinfo(what, (LuaFunction) func, frame);
+ LuaTable info = new LuaTable();
+ if (what.indexOf('S') >= 0) {
+ info.set(WHAT, LUA);
+ info.set(SOURCE, valueOf(ar.source));
+ info.set(SHORT_SRC, valueOf(ar.short_src));
+ info.set(LINEDEFINED, valueOf(ar.linedefined));
+ info.set(LASTLINEDEFINED, valueOf(ar.lastlinedefined));
+ }
+ if (what.indexOf('l') >= 0) {
+ info.set( CURRENTLINE, valueOf(ar.currentline) );
+ }
+ if (what.indexOf('u') >= 0) {
+ info.set(NUPS, valueOf(ar.nups));
+ info.set(NPARAMS, valueOf(ar.nparams));
+ info.set(ISVARARG, ar.isvararg? ONE: ZERO);
+ }
+ if (what.indexOf('n') >= 0) {
+ info.set(NAME, LuaValue.valueOf(ar.name!=null? ar.name: "?"));
+ info.set(NAMEWHAT, LuaValue.valueOf(ar.namewhat));
+ }
+ if (what.indexOf('t') >= 0) {
+ info.set(ISTAILCALL, ZERO);
+ }
+ if (what.indexOf('L') >= 0) {
+ LuaTable lines = new LuaTable();
+ info.set(ACTIVELINES, lines);
+ DebugLib.CallFrame cf;
+ for (int l = 1; (cf=callstack.getCallFrame(l)) != null; ++l)
+ if (cf.f == func)
+ lines.insert(-1, valueOf(cf.currentline()));
+ }
+ if (what.indexOf('f') >= 0) {
+ if (func != null)
+ info.set( FUNC, func );
+ }
+ return info;
+ }
+ }
+
+ // debug.getlocal ([thread,] f, local)
+ final class getlocal extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ int a=1;
+ LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
+ int level = args.checkint(a++);
+ int local = args.checkint(a++);
+ CallFrame f = callstack(thread).getCallFrame(level);
+ return f != null? f.getLocal(local): NONE;
+ }
+ }
+
+ // debug.getmetatable (value)
+ static final class getmetatable extends LibFunction {
+ public LuaValue call(LuaValue v) {
+ LuaValue mt = v.getmetatable();
+ return mt != null? mt: NIL;
+ }
+ }
+
+ // debug.getregistry ()
+ final class getregistry extends ZeroArgFunction {
+ public LuaValue call() {
+ return globals;
+ }
+ }
+
+ // debug.getupvalue (f, up)
+ static final class getupvalue extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ LuaValue func = args.checkfunction(1);
+ int up = args.checkint(2);
+ if ( func instanceof LuaClosure ) {
+ LuaClosure c = (LuaClosure) func;
+ LuaString name = findupvalue(c, up);
+ if ( name != null ) {
+ return varargsOf(name, c.upValues[up-1].getValue() );
+ }
+ }
+ return NIL;
+ }
+ }
+
+ // debug.getuservalue (u)
+ static final class getuservalue extends LibFunction {
+ public LuaValue call(LuaValue u) {
+ return u.isuserdata()? u: NIL;
+ }
+ }
+
+
+ // debug.sethook ([thread,] hook, mask [, count])
+ final class sethook extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ int a=1;
+ LuaThread t = args.isthread(a)? args.checkthread(a++): globals.running;
+ LuaValue func = args.optfunction(a++, null);
+ String str = args.optjstring(a++,"");
+ int count = args.optint(a++,0);
+ boolean call=false,line=false,rtrn=false;
+ for ( int i=0; i
+ * It contains the implementation of the io library support that is common to
+ * the JSE and JME platforms.
+ * In practice on of the concrete IOLib subclasses is chosen:
+ * {@link org.luaj.vm2.libs.jse.JseIoLib} for the JSE platform, and
+ * {@link org.luaj.vm2.libs.jme.JmeIoLib} for the JME platform.
+ *
+ * The JSE implementation conforms almost completely to the C-based lua library,
+ * while the JME implementation follows closely except in the area of random-access files,
+ * which are difficult to support properly on JME.
+ *
+ * Typically, this library is included as part of a call to either
+ * {@link org.luaj.vm2.libs.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.libs.jme.JmePlatform#standardGlobals()}
+ *
+ * To instantiate and use it directly,
+ * link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
+ *
+ * This has been implemented to match as closely as possible the behavior in the corresponding library in C.
+ * @see LibFunction
+ * @see org.luaj.vm2.libs.jse.JsePlatform
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ * @see org.luaj.vm2.libs.jse.JseIoLib
+ * @see org.luaj.vm2.libs.jme.JmeIoLib
+ * @see http://www.lua.org/manual/5.1/manual.html#5.7
+ */
+abstract
+public class IoLib extends TwoArgFunction {
+
+ abstract
+ protected class File extends LuaValue{
+ abstract public void write( LuaString string ) throws IOException;
+ abstract public void flush() throws IOException;
+ abstract public boolean isstdfile();
+ abstract public void close() throws IOException;
+ abstract public boolean isclosed();
+ // returns new position
+ abstract public int seek(String option, int bytecount) throws IOException;
+ abstract public void setvbuf(String mode, int size);
+ // get length remaining to read
+ abstract public int remaining() throws IOException;
+ // peek ahead one character
+ abstract public int peek() throws IOException, EOFException;
+ // return char if read, -1 if eof, throw IOException on other exception
+ abstract public int read() throws IOException, EOFException;
+ // return number of bytes read if positive, false if eof, throw IOException on other exception
+ abstract public int read(byte[] bytes, int offset, int length) throws IOException;
+
+ public boolean eof() throws IOException {
+ try {
+ return peek() < 0;
+ } catch (EOFException e) { return true; }
+ }
+
+ // delegate method access to file methods table
+ public LuaValue get( LuaValue key ) {
+ return filemethods.get(key);
+ }
+
+ // essentially a userdata instance
+ public int type() {
+ return LuaValue.TUSERDATA;
+ }
+ public String typename() {
+ return "userdata";
+ }
+
+ // displays as "file" type
+ public String tojstring() {
+ return "file: " + Integer.toHexString(hashCode());
+ }
+
+ public void finalize() throws Throwable {
+ try {
+ if (!isclosed()) {
+ try {
+ close();
+ } catch (IOException ignore) {}
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+ }
+
+ /** Enumerated value representing stdin */
+ protected static final int FTYPE_STDIN = 0;
+ /** Enumerated value representing stdout */
+ protected static final int FTYPE_STDOUT = 1;
+ /** Enumerated value representing stderr */
+ protected static final int FTYPE_STDERR = 2;
+ /** Enumerated value representing a file type for a named file */
+ protected static final int FTYPE_NAMED = 3;
+
+ /**
+ * Wrap the standard input.
+ * @return File
+ * @throws IOException
+ */
+ abstract protected File wrapStdin() throws IOException;
+
+ /**
+ * Wrap the standard output.
+ * @return File
+ * @throws IOException
+ */
+ abstract protected File wrapStdout() throws IOException;
+
+ /**
+ * Wrap the standard error output.
+ * @return File
+ * @throws IOException
+ */
+ abstract protected File wrapStderr() throws IOException;
+
+ /**
+ * Open a file in a particular mode.
+ * @param filename
+ * @param readMode true if opening in read mode
+ * @param appendMode true if opening in append mode
+ * @param updateMode true if opening in update mode
+ * @param binaryMode true if opening in binary mode
+ * @return File object if successful
+ * @throws IOException if could not be opened
+ */
+ abstract protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException;
+
+ /**
+ * Open a temporary file.
+ * @return File object if successful
+ * @throws IOException if could not be opened
+ */
+ abstract protected File tmpFile() throws IOException;
+
+ /**
+ * Start a new process and return a file for input or output
+ * @param prog the program to execute
+ * @param mode "r" to read, "w" to write
+ * @return File to read to or write from
+ * @throws IOException if an i/o exception occurs
+ */
+ abstract protected File openProgram(String prog, String mode) throws IOException;
+
+ private File infile = null;
+ private File outfile = null;
+ private File errfile = null;
+
+ private static final LuaValue STDIN = valueOf("stdin");
+ private static final LuaValue STDOUT = valueOf("stdout");
+ private static final LuaValue STDERR = valueOf("stderr");
+ private static final LuaValue FILE = valueOf("file");
+ private static final LuaValue CLOSED_FILE = valueOf("closed file");
+
+ private static final int IO_CLOSE = 0;
+ private static final int IO_FLUSH = 1;
+ private static final int IO_INPUT = 2;
+ private static final int IO_LINES = 3;
+ private static final int IO_OPEN = 4;
+ private static final int IO_OUTPUT = 5;
+ private static final int IO_POPEN = 6;
+ private static final int IO_READ = 7;
+ private static final int IO_TMPFILE = 8;
+ private static final int IO_TYPE = 9;
+ private static final int IO_WRITE = 10;
+
+ private static final int FILE_CLOSE = 11;
+ private static final int FILE_FLUSH = 12;
+ private static final int FILE_LINES = 13;
+ private static final int FILE_READ = 14;
+ private static final int FILE_SEEK = 15;
+ private static final int FILE_SETVBUF = 16;
+ private static final int FILE_WRITE = 17;
+
+ private static final int IO_INDEX = 18;
+ private static final int LINES_ITER = 19;
+
+ public static final String[] IO_NAMES = {
+ "close",
+ "flush",
+ "input",
+ "lines",
+ "open",
+ "output",
+ "popen",
+ "read",
+ "tmpfile",
+ "type",
+ "write",
+ };
+
+ public static final String[] FILE_NAMES = {
+ "close",
+ "flush",
+ "lines",
+ "read",
+ "seek",
+ "setvbuf",
+ "write",
+ };
+
+ LuaTable filemethods;
+
+ protected Globals globals;
+
+ public LuaValue call(LuaValue modname, LuaValue env) {
+ globals = env.checkglobals();
+
+ // io lib functions
+ LuaTable t = new LuaTable();
+ bind(t, IoLibV.class, IO_NAMES );
+
+ // create file methods table
+ filemethods = new LuaTable();
+ bind(filemethods, IoLibV.class, FILE_NAMES, FILE_CLOSE );
+
+ // set up file metatable
+ LuaTable mt = new LuaTable();
+ bind(mt, IoLibV.class, new String[] { "__index" }, IO_INDEX );
+ t.setmetatable( mt );
+
+ // all functions link to library instance
+ setLibInstance( t );
+ setLibInstance( filemethods );
+ setLibInstance( mt );
+
+ // return the table
+ env.set("io", t);
+ if (!env.get("package").isnil()) env.get("package").get("loaded").set("io", t);
+ return t;
+ }
+
+ private void setLibInstance(LuaTable t) {
+ LuaValue[] k = t.keys();
+ for ( int i=0, n=k.length; i
+ * To provide for common implementations in JME and JSE,
+ * library functions are typically grouped on one or more library classes
+ * and an opcode per library function is defined and used to key the switch
+ * to the correct function within the library.
+ *
+ * Since lua functions can be called with too few or too many arguments,
+ * and there are overloaded {@link LuaValue#call()} functions with varying
+ * number of arguments, a Java function exposed in lua needs to handle the
+ * argument fixup when a function is called with a number of arguments
+ * differs from that expected.
+ *
+ * To simplify the creation of library functions,
+ * there are 5 direct subclasses to handle common cases based on number of
+ * argument values and number of return return values.
+ *
+ * To be a Java library that can be loaded via {@code require}, it should have
+ * a public constructor that returns a {@link LuaValue} that, when executed,
+ * initializes the library.
+ *
+ * For example, the following code will implement a library called "hyperbolic"
+ * with two functions, "sinh", and "cosh":
+
+ * To test it, a script such as this can be used:
+ *
+ * It should produce something like:
+ *
+ * See the source code in any of the library functions
+ * such as {@link BaseLib} or {@link TableLib} for other examples.
+ */
+abstract public class LibFunction extends LuaFunction {
+
+ /** User-defined opcode to differentiate between instances of the library function class.
+ *
+ * Subclass will typicall switch on this value to provide the specific behavior for each function.
+ */
+ protected int opcode;
+
+ /** The common name for this function, useful for debugging.
+ *
+ * Binding functions initialize this to the name to which it is bound.
+ */
+ protected String name;
+
+ /** Default constructor for use by subclasses */
+ protected LibFunction() {
+ }
+
+ public String tojstring() {
+ return name != null ? "function: " + name : super.tojstring();
+ }
+
+ /**
+ * Bind a set of library functions.
+ *
+ * An array of names is provided, and the first name is bound
+ * with opcode = 0, second with 1, etc.
+ * @param env The environment to apply to each bound function
+ * @param factory the Class to instantiate for each bound function
+ * @param names array of String names, one for each function.
+ * @see #bind(LuaValue, Class, String[], int)
+ */
+ protected void bind(LuaValue env, Class factory, String[] names ) {
+ bind( env, factory, names, 0 );
+ }
+
+ /**
+ * Bind a set of library functions, with an offset
+ *
+ * An array of names is provided, and the first name is bound
+ * with opcode = {@code firstopcode}, second with {@code firstopcode+1}, etc.
+ * @param env The environment to apply to each bound function
+ * @param factory the Class to instantiate for each bound function
+ * @param names array of String names, one for each function.
+ * @param firstopcode the first opcode to use
+ * @see #bind(LuaValue, Class, String[])
+ */
+ protected void bind(LuaValue env, Class factory, String[] names, int firstopcode ) {
+ try {
+ for ( int i=0, n=names.length; i
+ * The implementations of {@code exp()} and {@code pow()} are constructed by
+ * hand for JME, so will be slower and less accurate than when executed on the JSE platform.
+ *
+ * Typically, this library is included as part of a call to either
+ * {@link org.luaj.vm2.libs.jse.JsePlatform#standardGlobals()} or
+ * {@link org.luaj.vm2.libs.jme.JmePlatform#standardGlobals()}
+ *
+ * To instantiate and use it directly,
+ * link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
+ *
+ * This has been implemented to match as closely as possible the behavior in the corresponding library in C.
+ * @see LibFunction
+ * @see org.luaj.vm2.libs.jse.JsePlatform
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ * @see org.luaj.vm2.libs.jse.JseMathLib
+ * @see Lua 5.2 Math Lib Reference
+ */
+public class MathLib extends TwoArgFunction {
+
+ /** Pointer to the latest MathLib instance, used only to dispatch
+ * math.exp to tha correct platform math library.
+ */
+ public static MathLib MATHLIB = null;
+
+ /** Construct a MathLib, which can be initialized by calling it with a
+ * modname string, and a global environment table as arguments using
+ * {@link #call(LuaValue, LuaValue)}. */
+ public MathLib() {
+ MATHLIB = this;
+ }
+
+ /** Perform one-time initialization on the library by creating a table
+ * containing the library functions, adding that table to the supplied environment,
+ * adding the table to package.loaded, and returning table as the return value.
+ * @param modname the module name supplied if this is loaded via 'require'.
+ * @param env the environment to load into, typically a Globals instance.
+ */
+ public LuaValue call(LuaValue modname, LuaValue env) {
+ LuaTable math = new LuaTable(0,30);
+ math.set("abs", new abs());
+ math.set("ceil", new ceil());
+ math.set("cos", new cos());
+ math.set("deg", new deg());
+ math.set("exp", new exp(this));
+ math.set("floor", new floor());
+ math.set("fmod", new fmod());
+ math.set("frexp", new frexp());
+ math.set("huge", LuaDouble.POSINF );
+ math.set("ldexp", new ldexp());
+ math.set("max", new max());
+ math.set("min", new min());
+ math.set("modf", new modf());
+ math.set("pi", Math.PI );
+ math.set("pow", new pow());
+ random r;
+ math.set("random", r = new random());
+ math.set("randomseed", new randomseed(r));
+ math.set("rad", new rad());
+ math.set("sin", new sin());
+ math.set("sqrt", new sqrt());
+ math.set("tan", new tan());
+ env.set("math", math);
+ if (!env.get("package").isnil()) env.get("package").get("loaded").set("math", math);
+ return math;
+ }
+
+ abstract protected static class UnaryOp extends OneArgFunction {
+ public LuaValue call(LuaValue arg) {
+ return valueOf(call(arg.checkdouble()));
+ }
+ abstract protected double call(double d);
+ }
+
+ abstract protected static class BinaryOp extends TwoArgFunction {
+ public LuaValue call(LuaValue x, LuaValue y) {
+ return valueOf(call(x.checkdouble(), y.checkdouble()));
+ }
+ abstract protected double call(double x, double y);
+ }
+
+ static final class abs extends UnaryOp { protected double call(double d) { return Math.abs(d); } }
+ static final class ceil extends UnaryOp { protected double call(double d) { return Math.ceil(d); } }
+ static final class cos extends UnaryOp { protected double call(double d) { return Math.cos(d); } }
+ static final class deg extends UnaryOp { protected double call(double d) { return Math.toDegrees(d); } }
+ static final class floor extends UnaryOp { protected double call(double d) { return Math.floor(d); } }
+ static final class rad extends UnaryOp { protected double call(double d) { return Math.toRadians(d); } }
+ static final class sin extends UnaryOp { protected double call(double d) { return Math.sin(d); } }
+ static final class sqrt extends UnaryOp { protected double call(double d) { return Math.sqrt(d); } }
+ static final class tan extends UnaryOp { protected double call(double d) { return Math.tan(d); } }
+
+ static final class exp extends UnaryOp {
+ final MathLib mathlib;
+ exp(MathLib mathlib) {
+ this.mathlib = mathlib;
+ }
+ protected double call(double d) {
+ return mathlib.dpow_lib(Math.E,d);
+ }
+ }
+
+ static final class fmod extends TwoArgFunction {
+ public LuaValue call(LuaValue xv, LuaValue yv) {
+ if (xv.islong() && yv.islong()) {
+ return valueOf(xv.tolong() % yv.tolong());
+ }
+ return valueOf(xv.checkdouble() % yv.checkdouble());
+ }
+ }
+ static final class ldexp extends BinaryOp {
+ protected double call(double x, double y) {
+ // This is the behavior on os-x, windows differs in rounding behavior.
+ return x * Double.longBitsToDouble((((long) y) + 1023) << 52);
+ }
+ }
+ static final class pow extends BinaryOp {
+ protected double call(double x, double y) {
+ return MathLib.dpow_default(x, y);
+ }
+ }
+
+ static class frexp extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ double x = args.checkdouble(1);
+ if ( x == 0 ) return varargsOf(ZERO,ZERO);
+ long bits = Double.doubleToLongBits( x );
+ double m = ((bits & (~(-1L<<52))) + (1L<<52)) * ((bits >= 0)? (.5 / (1L<<52)): (-.5 / (1L<<52)));
+ double e = (((int) (bits >> 52)) & 0x7ff) - 1022;
+ return varargsOf( valueOf(m), valueOf(e) );
+ }
+ }
+
+ static class max extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ LuaValue m = args.checkvalue(1);
+ for ( int i=2,n=args.narg(); i<=n; ++i ) {
+ LuaValue v = args.checkvalue(i);
+ if (m.lt_b(v)) m = v;
+ }
+ return m;
+ }
+ }
+
+ static class min extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ LuaValue m = args.checkvalue(1);
+ for ( int i=2,n=args.narg(); i<=n; ++i ) {
+ LuaValue v = args.checkvalue(i);
+ if (v.lt_b(m)) m = v;
+ }
+ return m;
+ }
+ }
+
+ static class modf extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ LuaValue n = args.arg1();
+ /* number is its own integer part, no fractional part */
+ if (n.islong()) return varargsOf(n, valueOf(0.0));
+ double x = n.checkdouble();
+ /* integer part (rounds toward zero) */
+ double intPart = ( x > 0 ) ? Math.floor( x ) : Math.ceil( x );
+ /* fractional part (test needed for inf/-inf) */
+ double fracPart = x == intPart ? 0.0 : x - intPart;
+ return varargsOf( valueOf(intPart), valueOf(fracPart) );
+ }
+ }
+
+ static class random extends LibFunction {
+ Random random = new Random();
+ public LuaValue call() {
+ return valueOf( random.nextDouble() );
+ }
+ public LuaValue call(LuaValue a) {
+ int m = a.checkint();
+ if (m<1) argerror(1, "interval is empty");
+ return valueOf( 1 + random.nextInt(m) );
+ }
+ public LuaValue call(LuaValue a, LuaValue b) {
+ int m = a.checkint();
+ int n = b.checkint();
+ if (n
+ * Subclasses need only implement {@link LuaValue#call(LuaValue)} to complete this class,
+ * simplifying development.
+ * All other uses of {@link #call()}, {@link #invoke(Varargs)},etc,
+ * are routed through this method by this class,
+ * dropping or extending arguments with {@code nil} values as required.
+ *
+ * If more than one argument are required, or no arguments are required,
+ * or variable argument or variable return values,
+ * then use one of the related function
+ * {@link ZeroArgFunction}, {@link TwoArgFunction}, {@link ThreeArgFunction}, or {@link VarArgFunction}.
+ *
+ * See {@link LibFunction} for more information on implementation libraries and library functions.
+ * @see #call(LuaValue)
+ * @see LibFunction
+ * @see ZeroArgFunction
+ * @see TwoArgFunction
+ * @see ThreeArgFunction
+ * @see VarArgFunction
+ */
+abstract public class OneArgFunction extends LibFunction {
+
+ abstract public LuaValue call(LuaValue arg);
+
+ /** Default constructor */
+ public OneArgFunction() {
+ }
+
+ public final LuaValue call() {
+ return call(NIL);
+ }
+
+ public final LuaValue call(LuaValue arg1, LuaValue arg2) {
+ return call(arg1);
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
+ return call(arg1);
+ }
+
+ public Varargs invoke(Varargs varargs) {
+ return call(varargs.arg1());
+ }
+}
diff --git a/core/src/main/java/org/luaj/vm2/libs/OsLib.java b/core/src/main/java/org/luaj/vm2/libs/OsLib.java
new file mode 100644
index 00000000..6b21c0bb
--- /dev/null
+++ b/core/src/main/java/org/luaj/vm2/libs/OsLib.java
@@ -0,0 +1,524 @@
+/*******************************************************************************
+* Copyright (c) 2009 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.libs;
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.Date;
+
+import org.luaj.vm2.Buffer;
+import org.luaj.vm2.Globals;
+import org.luaj.vm2.LuaTable;
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.Varargs;
+
+/**
+ * Subclass of {@link LibFunction} which implements the standard lua {@code os} library.
+ *
+ * It is a usable base with simplified stub functions
+ * for library functions that cannot be implemented uniformly
+ * on Jse and Jme.
+ *
+ * This can be installed as-is on either platform, or extended
+ * and refined to be used in a complete Jse implementation.
+ *
+ * Because the nature of the {@code os} library is to encapsulate
+ * os-specific features, the behavior of these functions varies considerably
+ * from their counterparts in the C platform.
+ *
+ * The following functions have limited implementations of features
+ * that are not supported well on Jme:
+ *
+ * Typically, this library is included as part of a call to either
+ * {@link org.luaj.vm2.libs.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.libs.jme.JmePlatform#standardGlobals()}
+ *
+ * To instantiate and use it directly,
+ * link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
+ *
+ * @see LibFunction
+ * @see org.luaj.vm2.libs.jse.JseOsLib
+ * @see org.luaj.vm2.libs.jse.JsePlatform
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ * @see http://www.lua.org/manual/5.1/manual.html#5.8
+ */
+public class OsLib extends TwoArgFunction {
+ public static final String TMP_PREFIX = ".luaj";
+ public static final String TMP_SUFFIX = "tmp";
+
+ 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",
+ "date",
+ "difftime",
+ "execute",
+ "exit",
+ "getenv",
+ "remove",
+ "rename",
+ "setlocale",
+ "time",
+ "tmpname",
+ };
+
+ private static final long t0 = System.currentTimeMillis();
+ private static long tmpnames = t0;
+
+ protected Globals globals;
+
+ /**
+ * Create and OsLib instance.
+ */
+ public OsLib() {
+ }
+
+ /** Perform one-time initialization on the library by creating a table
+ * containing the library functions, adding that table to the supplied environment,
+ * adding the table to package.loaded, and returning table as the return value.
+ * @param modname the module name supplied if this is loaded via 'require'.
+ * @param env the environment to load into, typically a Globals instance.
+ */
+ public LuaValue call(LuaValue modname, 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);
+ if (!env.get("package").isnil()) env.get("package").get("loaded").set("os", os);
+ return os;
+ }
+
+ 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, "%c");
+ double t = args.isnumber(2)? args.todouble(2): time(null);
+ if (s.equals("*t")) {
+ Calendar d = Calendar.getInstance();
+ d.setTime(new Date((long)(t*1000)));
+ LuaTable tbl = LuaValue.tableOf();
+ tbl.set("year", LuaValue.valueOf(d.get(Calendar.YEAR)));
+ tbl.set("month", LuaValue.valueOf(d.get(Calendar.MONTH)+1));
+ tbl.set("day", LuaValue.valueOf(d.get(Calendar.DAY_OF_MONTH)));
+ tbl.set("hour", LuaValue.valueOf(d.get(Calendar.HOUR_OF_DAY)));
+ tbl.set("min", LuaValue.valueOf(d.get(Calendar.MINUTE)));
+ tbl.set("sec", LuaValue.valueOf(d.get(Calendar.SECOND)));
+ tbl.set("wday", LuaValue.valueOf(d.get(Calendar.DAY_OF_WEEK)));
+ tbl.set("yday", LuaValue.valueOf(d.get(0x6))); // Day of year
+ tbl.set("isdst", LuaValue.valueOf(isDaylightSavingsTime(d)));
+ return tbl;
+ }
+ return valueOf( date(s, t==-1? time(null): 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.opttable(1, null)));
+ case TMPNAME:
+ return valueOf(tmpname());
+ }
+ return NONE;
+ } catch ( IOException e ) {
+ return varargsOf(NIL, valueOf(e.getMessage()));
+ }
+ }
+ }
+
+ /**
+ * @return an approximation of the amount in seconds of CPU time used by
+ * the program. For luaj this simple returns the elapsed time since the
+ * OsLib class was loaded.
+ */
+ protected double clock() {
+ return (System.currentTimeMillis()-t0) / 1000.;
+ }
+
+ /**
+ * Returns the number of seconds from time t1 to time t2.
+ * In POSIX, Windows, and some other systems, this value is exactly t2-t1.
+ * @param t2
+ * @param t1
+ * @return diffeence in time values, in seconds
+ */
+ protected double difftime(double t2, double t1) {
+ return t2 - t1;
+ }
+
+ /**
+ * If the time argument is present, this is the time to be formatted
+ * (see the os.time function for a description of this value).
+ * Otherwise, date formats the current time.
+ *
+ * Date returns the date as a string,
+ * formatted according to the same rules as ANSII strftime, but without
+ * support for %g, %G, or %V.
+ *
+ * When called without arguments, date returns a reasonable date and
+ * time representation that depends on the host system and on the
+ * current locale (that is, os.date() is equivalent to os.date("%c")).
+ *
+ * @param format
+ * @param time time since epoch, or -1 if not supplied
+ * @return a LString or a LTable containing date and time,
+ * formatted according to the given string format.
+ */
+ public String date(String format, double time) {
+ Calendar d = Calendar.getInstance();
+ d.setTime(new Date((long)(time*1000)));
+ if (format.startsWith("!")) {
+ time -= timeZoneOffset(d);
+ d.setTime(new Date((long)(time*1000)));
+ format = format.substring(1);
+ }
+ byte[] fmt = format.getBytes();
+ final int n = fmt.length;
+ Buffer result = new Buffer(n);
+ byte c;
+ for ( int i = 0; i < n; ) {
+ switch ( c = fmt[i++ ] ) {
+ case '\n':
+ result.append( "\n" );
+ break;
+ default:
+ result.append( c );
+ break;
+ case '%':
+ if (i >= n) break;
+ switch ( c = fmt[i++ ] ) {
+ default:
+ LuaValue.argerror(1, "invalid conversion specifier '%"+c+"'");
+ break;
+ case '%':
+ result.append( (byte)'%' );
+ break;
+ case 'a':
+ result.append(WeekdayNameAbbrev[d.get(Calendar.DAY_OF_WEEK)-1]);
+ break;
+ case 'A':
+ result.append(WeekdayName[d.get(Calendar.DAY_OF_WEEK)-1]);
+ break;
+ case 'b':
+ result.append(MonthNameAbbrev[d.get(Calendar.MONTH)]);
+ break;
+ case 'B':
+ result.append(MonthName[d.get(Calendar.MONTH)]);
+ break;
+ case 'c':
+ result.append(date("%a %b %d %H:%M:%S %Y", time));
+ break;
+ case 'd':
+ result.append(String.valueOf(100+d.get(Calendar.DAY_OF_MONTH)).substring(1));
+ break;
+ case 'H':
+ result.append(String.valueOf(100+d.get(Calendar.HOUR_OF_DAY)).substring(1));
+ break;
+ case 'I':
+ result.append(String.valueOf(100+(d.get(Calendar.HOUR_OF_DAY)%12)).substring(1));
+ break;
+ case 'j': { // day of year.
+ Calendar y0 = beginningOfYear(d);
+ int dayOfYear = (int) ((d.getTime().getTime() - y0.getTime().getTime()) / (24 * 3600L * 1000L));
+ result.append(String.valueOf(1001+dayOfYear).substring(1));
+ break;
+ }
+ case 'm':
+ result.append(String.valueOf(101+d.get(Calendar.MONTH)).substring(1));
+ break;
+ case 'M':
+ result.append(String.valueOf(100+d.get(Calendar.MINUTE)).substring(1));
+ break;
+ case 'p':
+ result.append(d.get(Calendar.HOUR_OF_DAY) < 12? "AM": "PM");
+ break;
+ case 'S':
+ result.append(String.valueOf(100+d.get(Calendar.SECOND)).substring(1));
+ break;
+ case 'U':
+ result.append(String.valueOf(weekNumber(d, 0)));
+ break;
+ case 'w':
+ result.append(String.valueOf((d.get(Calendar.DAY_OF_WEEK)+6)%7));
+ break;
+ case 'W':
+ result.append(String.valueOf(weekNumber(d, 1)));
+ break;
+ case 'x':
+ result.append(date("%m/%d/%y", time));
+ break;
+ case 'X':
+ result.append(date("%H:%M:%S", time));
+ break;
+ case 'y':
+ result.append(String.valueOf(d.get(Calendar.YEAR)).substring(2));
+ break;
+ case 'Y':
+ result.append(String.valueOf(d.get(Calendar.YEAR)));
+ break;
+ case 'z': {
+ final int tzo = timeZoneOffset(d) / 60;
+ final int a = Math.abs(tzo);
+ final String h = String.valueOf(100 + a / 60).substring(1);
+ final String m = String.valueOf(100 + a % 60).substring(1);
+ result.append((tzo>=0? "+": "-") + h + m);
+ break;
+ }
+ }
+ }
+ }
+ return result.tojstring();
+ }
+
+ private static final String[] WeekdayNameAbbrev = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+ private static final String[] WeekdayName = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
+ private static final String[] MonthNameAbbrev = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ private static final String[] MonthName = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
+
+ private Calendar beginningOfYear(Calendar d) {
+ Calendar y0 = Calendar.getInstance();
+ y0.setTime(d.getTime());
+ y0.set(Calendar.MONTH, 0);
+ y0.set(Calendar.DAY_OF_MONTH, 1);
+ y0.set(Calendar.HOUR_OF_DAY, 0);
+ y0.set(Calendar.MINUTE, 0);
+ y0.set(Calendar.SECOND, 0);
+ y0.set(Calendar.MILLISECOND, 0);
+ return y0;
+ }
+
+ private int weekNumber(Calendar d, int startDay) {
+ Calendar y0 = beginningOfYear(d);
+ y0.set(Calendar.DAY_OF_MONTH, 1 + (startDay + 8 - y0.get(Calendar.DAY_OF_WEEK)) % 7);
+ if (y0.after(d)) {
+ y0.set(Calendar.YEAR, y0.get(Calendar.YEAR) - 1);
+ y0.set(Calendar.DAY_OF_MONTH, 1 + (startDay + 8 - y0.get(Calendar.DAY_OF_WEEK)) % 7);
+ }
+ long dt = d.getTime().getTime() - y0.getTime().getTime();
+ return 1 + (int) (dt / (7L * 24L * 3600L * 1000L));
+ }
+
+ private int timeZoneOffset(Calendar d) {
+ int localStandarTimeMillis = (
+ d.get(Calendar.HOUR_OF_DAY) * 3600 +
+ d.get(Calendar.MINUTE) * 60 +
+ d.get(Calendar.SECOND)) * 1000;
+ return d.getTimeZone().getOffset(
+ 1,
+ d.get(Calendar.YEAR),
+ d.get(Calendar.MONTH),
+ d.get(Calendar.DAY_OF_MONTH),
+ d.get(Calendar.DAY_OF_WEEK),
+ localStandarTimeMillis) / 1000;
+ }
+
+ private boolean isDaylightSavingsTime(Calendar d) {
+ return timeZoneOffset(d) != d.getTimeZone().getRawOffset() / 1000;
+ }
+
+ /**
+ * This function is equivalent to the C function system.
+ * It passes command to be executed by an operating system shell.
+ * It returns a status code, which is system-dependent.
+ * If command is absent, then it returns nonzero if a shell
+ * is available and zero otherwise.
+ * @param command command to pass to the system
+ */
+ protected Varargs execute(String command) {
+ return varargsOf(NIL, valueOf("exit"), ONE);
+ }
+
+ /**
+ * Calls the C function exit, with an optional code, to terminate the host program.
+ * @param code
+ */
+ protected void exit(int code) {
+ System.exit(code);
+ }
+
+ /**
+ * Returns the value of the process environment variable varname,
+ * or the System property value for varname,
+ * or null if the variable is not defined in either environment.
+ *
+ * The default implementation, which is used by the JmePlatform,
+ * only queryies System.getProperty().
+ *
+ * The JsePlatform overrides this behavior and returns the
+ * environment variable value using System.getenv() if it exists,
+ * or the System property value if it does not.
+ *
+ * A SecurityException may be thrown if access is not allowed
+ * for 'varname'.
+ * @param varname
+ * @return String value, or null if not defined
+ */
+ protected String getenv(String varname) {
+ return System.getProperty(varname);
+ }
+
+ /**
+ * Deletes the file or directory with the given name.
+ * Directories must be empty to be removed.
+ * If this function fails, it throws and IOException
+ *
+ * @param filename
+ * @throws IOException if it fails
+ */
+ protected void remove(String filename) throws IOException {
+ throw new IOException( "not implemented" );
+ }
+
+ /**
+ * Renames file or directory named oldname to newname.
+ * If this function fails,it throws and IOException
+ *
+ * @param oldname old file name
+ * @param newname new file name
+ * @throws IOException if it fails
+ */
+ protected void rename(String oldname, String newname) throws IOException {
+ throw new IOException( "not implemented" );
+ }
+
+ /**
+ * Sets the current locale of the program. locale is a string specifying
+ * a locale; category is an optional string describing which category to change:
+ * "all", "collate", "ctype", "monetary", "numeric", or "time"; the default category
+ * is "all".
+ *
+ * If locale is the empty string, the current locale is set to an implementation-
+ * defined native locale. If locale is the string "C", the current locale is set
+ * to the standard C locale.
+ *
+ * When called with null as the first argument, this function only returns the
+ * name of the current locale for the given category.
+ *
+ * @param locale
+ * @param category
+ * @return the name of the new locale, or null if the request
+ * cannot be honored.
+ */
+ protected String setlocale(String locale, String category) {
+ return "C";
+ }
+
+ /**
+ * Returns the current time when called without arguments,
+ * or a time representing the date and time specified by the given table.
+ * This table must have fields year, month, and day,
+ * and may have fields hour, min, sec, and isdst
+ * (for a description of these fields, see the os.date function).
+ * @param table
+ * @return long value for the time
+ */
+ protected double time(LuaTable table) {
+ Date d;
+ if (table == null) {
+ d = new Date();
+ } else {
+ Calendar c = Calendar.getInstance();
+ c.set(Calendar.YEAR, table.get("year").checkint());
+ c.set(Calendar.MONTH, table.get("month").checkint()-1);
+ c.set(Calendar.DAY_OF_MONTH, table.get("day").checkint());
+ c.set(Calendar.HOUR_OF_DAY, table.get("hour").optint(12));
+ c.set(Calendar.MINUTE, table.get("min").optint(0));
+ c.set(Calendar.SECOND, table.get("sec").optint(0));
+ c.set(Calendar.MILLISECOND, 0);
+ d = c.getTime();
+ }
+ return d.getTime() / 1000.;
+ }
+
+ /**
+ * Returns a string with a file name that can be used for a temporary file.
+ * The file must be explicitly opened before its use and explicitly removed
+ * when no longer needed.
+ *
+ * On some systems (POSIX), this function also creates a file with that name,
+ * to avoid security risks. (Someone else might create the file with wrong
+ * permissions in the time between getting the name and creating the file.)
+ * You still have to open the file to use it and to remove it (even if you
+ * do not use it).
+ *
+ * @return String filename to use
+ */
+ protected String tmpname() {
+ synchronized ( OsLib.class ) {
+ return TMP_PREFIX+(tmpnames++)+TMP_SUFFIX;
+ }
+ }
+}
diff --git a/core/src/main/java/org/luaj/vm2/libs/PackageLib.java b/core/src/main/java/org/luaj/vm2/libs/PackageLib.java
new file mode 100644
index 00000000..710e263e
--- /dev/null
+++ b/core/src/main/java/org/luaj/vm2/libs/PackageLib.java
@@ -0,0 +1,381 @@
+/*******************************************************************************
+* Copyright (c) 2010-2011 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.libs;
+
+import java.io.InputStream;
+
+import org.luaj.vm2.Globals;
+import org.luaj.vm2.LuaFunction;
+import org.luaj.vm2.LuaString;
+import org.luaj.vm2.LuaTable;
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.Varargs;
+
+/**
+ * Subclass of {@link LibFunction} which implements the lua standard package and module
+ * library functions.
+ *
+ *
+ * To instantiate and use it directly,
+ * link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
+ *
+ * @see LibFunction
+ * @see BaseLib
+ * @see org.luaj.vm2.libs.jse.JseBaseLib
+ * @see org.luaj.vm2.libs.jse.JsePlatform
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ * @see Lua 5.2 Package Lib Reference
+ */
+public class PackageLib extends TwoArgFunction {
+
+ /** The default value to use for package.path. This can be set with the system property
+ *
+ * This is used by required to load files that are part of
+ * the application, and implemented by BaseLib
+ * for both the Jme and Jse platforms.
+ *
+ * The Jme version of base lib {@link BaseLib}
+ * implements {@link Globals#finder} via {@link Class#getResourceAsStream(String)},
+ * while the Jse version {@link org.luaj.vm2.libs.jse.JseBaseLib} implements it using {@link java.io.File#File(String)}.
+ *
+ * The io library does not use this API for file manipulation.
+ *
+ * @see BaseLib
+ * @see Globals#finder
+ * @see org.luaj.vm2.libs.jse.JseBaseLib
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ * @see org.luaj.vm2.libs.jse.JsePlatform
+ */
+public interface ResourceFinder {
+
+ /**
+ * Try to open a file, or return null if not found.
+ *
+ * @see BaseLib
+ * @see org.luaj.vm2.libs.jse.JseBaseLib
+ *
+ * @param filename
+ * @return InputStream, or null if not found.
+ */
+ public InputStream findResource( String filename );
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/luaj/vm2/libs/StringLib.java b/core/src/main/java/org/luaj/vm2/libs/StringLib.java
new file mode 100644
index 00000000..86de6509
--- /dev/null
+++ b/core/src/main/java/org/luaj/vm2/libs/StringLib.java
@@ -0,0 +1,1223 @@
+/*******************************************************************************
+* Copyright (c) 2009-2011 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.libs;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.luaj.vm2.Buffer;
+import org.luaj.vm2.LuaClosure;
+import org.luaj.vm2.LuaString;
+import org.luaj.vm2.LuaTable;
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.Varargs;
+import org.luaj.vm2.compiler.DumpState;
+
+/**
+ * Subclass of {@link LibFunction} which implements the lua standard {@code string}
+ * library.
+ *
+ * Typically, this library is included as part of a call to either
+ * {@link org.luaj.vm2.libs.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.libs.jme.JmePlatform#standardGlobals()}
+ *
+ * To instantiate and use it directly,
+ * link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
+ *
+ * This is a direct port of the corresponding library in C.
+ * @see LibFunction
+ * @see org.luaj.vm2.libs.jse.JsePlatform
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ * @see Lua 5.2 String Lib Reference
+ */
+public class StringLib extends TwoArgFunction {
+
+ /** Construct a StringLib, which can be initialized by calling it with a
+ * modname string, and a global environment table as arguments using
+ * {@link #call(LuaValue, LuaValue)}. */
+ public StringLib() {
+ }
+
+ /** Perform one-time initialization on the library by creating a table
+ * containing the library functions, adding that table to the supplied environment,
+ * adding the table to package.loaded, and returning table as the return value.
+ * Creates a metatable that uses __INDEX to fall back on itself to support string
+ * method operations.
+ * If the shared strings metatable instance is null, will set the metatable as
+ * the global shared metatable for strings.
+ *
+ * All tables and metatables are read-write by default so if this will be used in
+ * a server environment, sandboxing should be used. In particular, the
+ * {@link LuaString#s_metatable} table should probably be made read-only.
+ * @param modname the module name supplied if this is loaded via 'require'.
+ * @param env the environment to load into, typically a Globals instance.
+ */
+ public LuaValue call(LuaValue modname, LuaValue env) {
+ LuaTable string = new LuaTable();
+ string.set("byte", new _byte());
+ string.set("char", new _char());
+ string.set("dump", new dump());
+ string.set("find", new find());
+ string.set("format", new format());
+ string.set("gmatch", new gmatch());
+ string.set("gsub", new gsub());
+ string.set("len", new len());
+ string.set("lower", new lower());
+ string.set("match", new match());
+ string.set("rep", new rep());
+ string.set("reverse", new reverse());
+ string.set("sub", new sub());
+ string.set("upper", new upper());
+
+ env.set("string", string);
+ if (!env.get("package").isnil()) env.get("package").get("loaded").set("string", string);
+ if (LuaString.s_metatable == null) {
+ LuaString.s_metatable = LuaValue.tableOf(new LuaValue[] { INDEX, string });
+ }
+ return string;
+ }
+
+ /**
+ * string.byte (s [, i [, j]])
+ *
+ * Returns the internal numerical codes of the
+ * characters s[i], s[i+1], ..., s[j]. The default value for i is 1; the
+ * default value for j is i.
+ *
+ * Note that numerical codes are not necessarily portable across platforms.
+ *
+ * @param args the calling args
+ */
+ static final class _byte extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ LuaString s = args.checkstring(1);
+ int l = s.m_length;
+ int posi = posrelat( args.optint(2,1), l );
+ int pose = posrelat( args.optint(3,posi), l );
+ int n,i;
+ if (posi <= 0) posi = 1;
+ if (pose > l) pose = l;
+ if (posi > pose) return NONE; /* empty interval; return no values */
+ n = (int)(pose - posi + 1);
+ if (posi + n <= pose) /* overflow? */
+ error("string slice too long");
+ LuaValue[] v = new LuaValue[n];
+ for (i=0; i
+ * Typically, this library is included as part of a call to either
+ * {@link org.luaj.vm2.libs.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.libs.jme.JmePlatform#standardGlobals()}
+ *
+ * To instantiate and use it directly,
+ * link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
+ *
+ * This has been implemented to match as closely as possible the behavior in the corresponding library in C.
+ * @see LibFunction
+ * @see org.luaj.vm2.libs.jse.JsePlatform
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ * @see Lua 5.2 Table Lib Reference
+ */
+public class TableLib extends TwoArgFunction {
+
+ /** Perform one-time initialization on the library by creating a table
+ * containing the library functions, adding that table to the supplied environment,
+ * adding the table to package.loaded, and returning table as the return value.
+ * @param modname the module name supplied if this is loaded via 'require'.
+ * @param env the environment to load into, typically a Globals instance.
+ */
+ public LuaValue call(LuaValue modname, LuaValue env) {
+ LuaTable table = new LuaTable();
+ table.set("concat", new concat());
+ table.set("insert", new insert());
+ table.set("pack", new pack());
+ table.set("remove", new remove());
+ table.set("sort", new sort());
+ table.set("unpack", new unpack());
+ env.set("table", table);
+ if (!env.get("package").isnil()) env.get("package").get("loaded").set("table", table);
+ return NIL;
+ }
+
+ // "concat" (table [, sep [, i [, j]]]) -> string
+ static class concat extends TableLibFunction {
+ public LuaValue call(LuaValue list) {
+ return list.checktable().concat(EMPTYSTRING,1,list.length());
+ }
+ public LuaValue call(LuaValue list, LuaValue sep) {
+ return list.checktable().concat(sep.checkstring(),1,list.length());
+ }
+ public LuaValue call(LuaValue list, LuaValue sep, LuaValue i) {
+ return list.checktable().concat(sep.checkstring(),i.checkint(),list.length());
+ }
+ public LuaValue call(LuaValue list, LuaValue sep, LuaValue i, LuaValue j) {
+ return list.checktable().concat(sep.checkstring(),i.checkint(),j.checkint());
+ }
+ }
+
+ // "insert" (table, [pos,] value)
+ static class insert extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ switch (args.narg()) {
+ case 2: {
+ LuaTable table = args.checktable(1);
+ table.insert(table.length()+1,args.arg(2));
+ return NONE;
+ }
+ case 3: {
+ LuaTable table = args.checktable(1);
+ int pos = args.checkint(2);
+ int max = table.length() + 1;
+ if (pos < 1 || pos > max) argerror(2, "position out of bounds: " + pos + " not between 1 and " + max);
+ table.insert(pos, args.arg(3));
+ return NONE;
+ }
+ default: {
+ return error("wrong number of arguments to 'table.insert': " + args.narg() + " (must be 2 or 3)");
+ }
+ }
+ }
+ }
+
+ // "pack" (...) -> table
+ static class pack extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ LuaValue t = tableOf(args, 1);
+ t.set("n", args.narg());
+ return t;
+ }
+ }
+
+ // "remove" (table [, pos]) -> removed-ele
+ static class remove extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ LuaTable table = args.checktable(1);
+ int size = table.length();
+ int pos = args.optint(2, size);
+ if (pos != size && (pos < 1 || pos > size + 1)) {
+ argerror(2, "position out of bounds: " + pos + " not between 1 and " + (size + 1));
+ }
+ return table.remove(pos);
+ }
+ }
+
+ // "sort" (table [, comp])
+ static class sort extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ args.checktable(1).sort(
+ args.isnil(2)? NIL: args.checkfunction(2));
+ return NONE;
+ }
+ }
+
+
+ // "unpack", // (list [,i [,j]]) -> result1, ...
+ static class unpack extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ LuaTable t = args.checktable(1);
+ // do not waste resource for calc rawlen if arg3 is not nil
+ int len = args.arg(3).isnil() ? t.length() : 0;
+ return t.unpack(args.optint(2, 1), args.optint(3, len));
+ }
+ }
+}
diff --git a/core/src/main/java/org/luaj/vm2/libs/TableLibFunction.java b/core/src/main/java/org/luaj/vm2/libs/TableLibFunction.java
new file mode 100644
index 00000000..fd6ed393
--- /dev/null
+++ b/core/src/main/java/org/luaj/vm2/libs/TableLibFunction.java
@@ -0,0 +1,9 @@
+package org.luaj.vm2.libs;
+
+import org.luaj.vm2.LuaValue;
+
+class TableLibFunction extends LibFunction {
+ public LuaValue call() {
+ return argerror(1, "table expected, got no value");
+ }
+}
diff --git a/core/src/main/java/org/luaj/vm2/libs/ThreeArgFunction.java b/core/src/main/java/org/luaj/vm2/libs/ThreeArgFunction.java
new file mode 100644
index 00000000..4c8063b8
--- /dev/null
+++ b/core/src/main/java/org/luaj/vm2/libs/ThreeArgFunction.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+* Copyright (c) 2009 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.libs;
+
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.Varargs;
+
+/** Abstract base class for Java function implementations that take two arguments and
+ * return one value.
+ *
+ * Subclasses need only implement {@link LuaValue#call(LuaValue,LuaValue,LuaValue)} to complete this class,
+ * simplifying development.
+ * All other uses of {@link #call()}, {@link #invoke(Varargs)},etc,
+ * are routed through this method by this class,
+ * dropping or extending arguments with {@code nil} values as required.
+ *
+ * If more or less than three arguments are required,
+ * or variable argument or variable return values,
+ * then use one of the related function
+ * {@link ZeroArgFunction}, {@link OneArgFunction}, {@link TwoArgFunction}, or {@link VarArgFunction}.
+ *
+ * See {@link LibFunction} for more information on implementation libraries and library functions.
+ * @see #call(LuaValue,LuaValue,LuaValue)
+ * @see LibFunction
+ * @see ZeroArgFunction
+ * @see OneArgFunction
+ * @see TwoArgFunction
+ * @see VarArgFunction
+ */
+abstract public class ThreeArgFunction extends LibFunction {
+
+ abstract public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3);
+
+ /** Default constructor */
+ public ThreeArgFunction() {
+ }
+
+ public final LuaValue call() {
+ return call(NIL, NIL, NIL);
+ }
+
+ public final LuaValue call(LuaValue arg) {
+ return call(arg, NIL, NIL);
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2) {
+ return call(arg1, arg2, NIL);
+ }
+
+ public Varargs invoke(Varargs varargs) {
+ return call(varargs.arg1(),varargs.arg(2),varargs.arg(3));
+ }
+
+}
diff --git a/core/src/main/java/org/luaj/vm2/libs/TwoArgFunction.java b/core/src/main/java/org/luaj/vm2/libs/TwoArgFunction.java
new file mode 100644
index 00000000..2f9b2c59
--- /dev/null
+++ b/core/src/main/java/org/luaj/vm2/libs/TwoArgFunction.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+* Copyright (c) 2009 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.libs;
+
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.Varargs;
+
+/** Abstract base class for Java function implementations that take two arguments and
+ * return one value.
+ *
+ * Subclasses need only implement {@link LuaValue#call(LuaValue,LuaValue)} to complete this class,
+ * simplifying development.
+ * All other uses of {@link #call()}, {@link #invoke(Varargs)},etc,
+ * are routed through this method by this class,
+ * dropping or extending arguments with {@code nil} values as required.
+ *
+ * If more or less than two arguments are required,
+ * or variable argument or variable return values,
+ * then use one of the related function
+ * {@link ZeroArgFunction}, {@link OneArgFunction}, {@link ThreeArgFunction}, or {@link VarArgFunction}.
+ *
+ * See {@link LibFunction} for more information on implementation libraries and library functions.
+ * @see #call(LuaValue,LuaValue)
+ * @see LibFunction
+ * @see ZeroArgFunction
+ * @see OneArgFunction
+ * @see ThreeArgFunction
+ * @see VarArgFunction
+ */
+abstract public class TwoArgFunction extends LibFunction {
+
+ abstract public LuaValue call(LuaValue arg1, LuaValue arg2);
+
+ /** Default constructor */
+ public TwoArgFunction() {
+ }
+
+ public final LuaValue call() {
+ return call(NIL, NIL);
+ }
+
+ public final LuaValue call(LuaValue arg) {
+ return call(arg, NIL);
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
+ return call(arg1, arg2);
+ }
+
+ public Varargs invoke(Varargs varargs) {
+ return call(varargs.arg1(),varargs.arg(2));
+ }
+
+}
diff --git a/core/src/main/java/org/luaj/vm2/libs/VarArgFunction.java b/core/src/main/java/org/luaj/vm2/libs/VarArgFunction.java
new file mode 100644
index 00000000..a1118ccf
--- /dev/null
+++ b/core/src/main/java/org/luaj/vm2/libs/VarArgFunction.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+* Copyright (c) 2009 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.libs;
+
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.Varargs;
+
+/** Abstract base class for Java function implementations that takes varaiable arguments and
+ * returns multiple return values.
+ *
+ * Subclasses need only implement {@link LuaValue#invoke(Varargs)} to complete this class,
+ * simplifying development.
+ * All other uses of {@link #call(LuaValue)}, {@link #invoke()},etc,
+ * are routed through this method by this class,
+ * converting arguments to {@link Varargs} and
+ * dropping or extending return values with {@code nil} values as required.
+ *
+ * If between one and three arguments are required, and only one return value is returned,
+ * {@link ZeroArgFunction}, {@link OneArgFunction}, {@link TwoArgFunction}, or {@link ThreeArgFunction}.
+ *
+ * See {@link LibFunction} for more information on implementation libraries and library functions.
+ * @see #invoke(Varargs)
+ * @see LibFunction
+ * @see ZeroArgFunction
+ * @see OneArgFunction
+ * @see TwoArgFunction
+ * @see ThreeArgFunction
+ */
+abstract public class VarArgFunction extends LibFunction {
+
+ public VarArgFunction() {
+ }
+
+ public LuaValue call() {
+ return invoke(NONE).arg1();
+ }
+
+ public LuaValue call(LuaValue arg) {
+ return invoke(arg).arg1();
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2) {
+ return invoke(varargsOf(arg1,arg2)).arg1();
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
+ return invoke(varargsOf(arg1,arg2,arg3)).arg1();
+ }
+
+ /**
+ * Subclass responsibility.
+ * May not have expected behavior for tail calls.
+ * Should not be used if:
+ * - function has a possibility of returning a TailcallVarargs
+ * @param args the arguments to the function call.
+ */
+ public Varargs invoke(Varargs args) {
+ return onInvoke(args).eval();
+ }
+
+ public Varargs onInvoke(Varargs args) {
+ return invoke(args);
+ }
+}
diff --git a/core/src/main/java/org/luaj/vm2/libs/ZeroArgFunction.java b/core/src/main/java/org/luaj/vm2/libs/ZeroArgFunction.java
new file mode 100644
index 00000000..f34f51e4
--- /dev/null
+++ b/core/src/main/java/org/luaj/vm2/libs/ZeroArgFunction.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+* Copyright (c) 2009-2011 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.libs;
+
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.Varargs;
+
+/** Abstract base class for Java function implementations that take no arguments and
+ * return one value.
+ *
+ * Subclasses need only implement {@link LuaValue#call()} to complete this class,
+ * simplifying development.
+ * All other uses of {@link #call(LuaValue)}, {@link #invoke(Varargs)},etc,
+ * are routed through this method by this class.
+ *
+ * If one or more arguments are required, or variable argument or variable return values,
+ * then use one of the related function
+ * {@link OneArgFunction}, {@link TwoArgFunction}, {@link ThreeArgFunction}, or {@link VarArgFunction}.
+ *
+ * See {@link LibFunction} for more information on implementation libraries and library functions.
+ * @see #call()
+ * @see LibFunction
+ * @see OneArgFunction
+ * @see TwoArgFunction
+ * @see ThreeArgFunction
+ * @see VarArgFunction
+ */
+abstract public class ZeroArgFunction extends LibFunction {
+
+ abstract public LuaValue call();
+
+ /** Default constructor */
+ public ZeroArgFunction() {
+ }
+
+ public LuaValue call(LuaValue arg) {
+ return call();
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2) {
+ return call();
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
+ return call();
+ }
+
+ public Varargs invoke(Varargs varargs) {
+ return call();
+ }
+}
diff --git a/jme/src/main/java/org/luaj/vm2/libs/jme/JmeIoLib.java b/jme/src/main/java/org/luaj/vm2/libs/jme/JmeIoLib.java
new file mode 100644
index 00000000..8c60a940
--- /dev/null
+++ b/jme/src/main/java/org/luaj/vm2/libs/jme/JmeIoLib.java
@@ -0,0 +1,230 @@
+/*******************************************************************************
+* Copyright (c) 2009-2011 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.libs.jme;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.microedition.io.Connector;
+import javax.microedition.io.StreamConnection;
+
+import org.luaj.vm2.Globals;
+import org.luaj.vm2.LuaString;
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.libs.IoLib;
+import org.luaj.vm2.libs.LibFunction;
+
+/**
+ * Subclass of {@link IoLib} and therefore {@link LibFunction} which implements the lua standard {@code io}
+ * library for the JSE platform.
+ *
+ * The implementation of the is based on CLDC 1.0 and StreamConnection.
+ * However, seek is not supported.
+ *
+ * Typically, this library is included as part of a call to
+ * {@link JmePlatform#standardGlobals()}
+ *
+ * For special cases where the smallest possible footprint is desired,
+ * a minimal set of libraries could be loaded
+ * directly via {@link Globals#load(LuaValue)} using code such as:
+ * However, other libraries such as MathLib are not loaded in this case.
+ *
+ * This has been implemented to match as closely as possible the behavior in the corresponding library in C.
+ * @see LibFunction
+ * @see org.luaj.vm2.libs.jse.JsePlatform
+ * @see JmePlatform
+ * @see IoLib
+ * @see org.luaj.vm2.libs.jse.JseIoLib
+ * @see Lua 5.2 I/O Lib Reference
+ */
+public class JmeIoLib extends IoLib {
+
+ protected File wrapStdin() throws IOException {
+ return new FileImpl(globals.STDIN);
+ }
+
+ protected File wrapStdout() throws IOException {
+ return new FileImpl(globals.STDOUT);
+ }
+
+ protected File wrapStderr() throws IOException {
+ return new FileImpl(globals.STDERR);
+ }
+
+ protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException {
+ String url = "file:///" + filename;
+ int mode = readMode? Connector.READ: Connector.READ_WRITE;
+ StreamConnection conn = (StreamConnection) Connector.open( url, mode );
+ File f = readMode?
+ new FileImpl(conn, conn.openInputStream(), null):
+ new FileImpl(conn, conn.openInputStream(), conn.openOutputStream());
+ /*
+ if ( appendMode ) {
+ f.seek("end",0);
+ } else {
+ if ( ! readMode )
+ conn.truncate(0);
+ }
+ */
+ return f;
+ }
+
+ private static void notimplemented() throws IOException {
+ throw new IOException("not implemented");
+ }
+
+ protected File openProgram(String prog, String mode) throws IOException {
+ notimplemented();
+ return null;
+ }
+
+ protected File tmpFile() throws IOException {
+ notimplemented();
+ return null;
+ }
+
+ private final class FileImpl extends File {
+ private final StreamConnection conn;
+ private final InputStream is;
+ private final OutputStream os;
+ private boolean closed = false;
+ private boolean nobuffer = false;
+ private int lookahead = -1;
+ private FileImpl( StreamConnection conn, InputStream is, OutputStream os ) {
+ this.conn = conn;
+ this.is = is;
+ this.os = os;
+ }
+ private FileImpl( InputStream i ) {
+ this( null, i, null );
+ }
+ private FileImpl( OutputStream o ) {
+ this( null, null, o );
+ }
+ public String tojstring() {
+ return "file ("+this.hashCode()+")";
+ }
+ public boolean isstdfile() {
+ return conn == null;
+ }
+ public void close() throws IOException {
+ closed = true;
+ if ( conn != null ) {
+ conn.close();
+ }
+ }
+ public void flush() throws IOException {
+ if ( os != null )
+ os.flush();
+ }
+ public void write(LuaString s) throws IOException {
+ if ( os != null )
+ os.write( s.m_bytes, s.m_offset, s.m_length );
+ else
+ notimplemented();
+ if ( nobuffer )
+ flush();
+ }
+ public boolean isclosed() {
+ return closed;
+ }
+ public int seek(String option, int pos) throws IOException {
+ /*
+ if ( conn != null ) {
+ if ( "set".equals(option) ) {
+ conn.seek(pos);
+ return (int) conn.getFilePointer();
+ } else if ( "end".equals(option) ) {
+ conn.seek(conn.length()+1+pos);
+ return (int) conn.length()+1;
+ } else {
+ conn.seek(conn.getFilePointer()+pos);
+ return (int) conn.getFilePointer();
+ }
+ }
+ */
+ notimplemented();
+ return 0;
+ }
+ public void setvbuf(String mode, int size) {
+ nobuffer = "no".equals(mode);
+ }
+
+ // get length remaining to read
+ public int remaining() throws IOException {
+ return -1;
+ }
+
+ // peek ahead one character
+ public int peek() throws IOException {
+ if ( lookahead < 0 )
+ lookahead = is.read();
+ return lookahead;
+ }
+
+ // return char if read, -1 if eof, throw IOException on other exception
+ public int read() throws IOException {
+ if ( lookahead >= 0 ) {
+ int c = lookahead;
+ lookahead = -1;
+ return c;
+ }
+ if ( is != null )
+ return is.read();
+ notimplemented();
+ return 0;
+ }
+
+ // return number of bytes read if positive, -1 if eof, throws IOException
+ public int read(byte[] bytes, int offset, int length) throws IOException {
+ int n,i=0;
+ if (is!=null) {
+ if ( length > 0 && lookahead >= 0 ) {
+ bytes[offset] = (byte) lookahead;
+ lookahead = -1;
+ i += 1;
+ }
+ for ( ; i
+ * The JME platform, being limited, cannot implement all libraries in all aspects. The main limitations are
+ *
+ * It is used to allocate either a set of standard globals using
+ * {@link #standardGlobals()} or debug globals using {@link #debugGlobals()}
+ *
+ * A simple example of initializing globals and using them from Java is:
+ *
+ * Once globals are created, a simple way to load and run a script is:
+ *
+ * although {@code require} could also be used:
+ *
+ * The standard globals will contain all standard libraries in their JME flavors:
+ *
+ * The debug globals are simply the standard globals plus the {@code debug} library {@link DebugLib}.
+ *
+ *
+ * The class ensures that initialization is done in the correct order.
+ *
+ * @see Globals
+ * @see org.luaj.vm2.libs.jse.JsePlatform
+ */
+public class JmePlatform {
+
+ /**
+ * Create a standard set of globals for JME including all the libraries.
+ *
+ * @return Table of globals initialized with the standard JME libraries
+ * @see #debugGlobals()
+ * @see org.luaj.vm2.libs.jse.JsePlatform
+ * @see JmePlatform
+ */
+ public static Globals standardGlobals() {
+ Globals globals = new Globals();
+ globals.load(new BaseLib());
+ globals.load(new PackageLib());
+ globals.load(new Bit32Lib());
+ globals.load(new OsLib());
+ globals.load(new MathLib());
+ globals.load(new TableLib());
+ globals.load(new StringLib());
+ globals.load(new CoroutineLib());
+ globals.load(new JmeIoLib());
+ LoadState.install(globals);
+ LuaC.install(globals);
+ return globals;
+ }
+
+ /** Create standard globals including the {@link DebugLib} library.
+ *
+ * @return Table of globals initialized with the standard JSE and debug libraries
+ * @see #standardGlobals()
+ * @see org.luaj.vm2.libs.jse.JsePlatform
+ * @see JmePlatform
+ * @see DebugLib
+ */
+ public static Globals debugGlobals() {
+ Globals globals = standardGlobals();
+ globals.load(new DebugLib());
+ return globals;
+ }
+}
diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/CoerceJavaToLua.java b/jse/src/main/java/org/luaj/vm2/libs/jse/CoerceJavaToLua.java
new file mode 100644
index 00000000..66f7558c
--- /dev/null
+++ b/jse/src/main/java/org/luaj/vm2/libs/jse/CoerceJavaToLua.java
@@ -0,0 +1,196 @@
+/*******************************************************************************
+* Copyright (c) 2009-2011 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.libs.jse;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.luaj.vm2.LuaDouble;
+import org.luaj.vm2.LuaInteger;
+import org.luaj.vm2.LuaString;
+import org.luaj.vm2.LuaUserdata;
+import org.luaj.vm2.LuaValue;
+
+/**
+ * Helper class to coerce values from Java to lua within the luajava library.
+ *
+ * This class is primarily used by the {@link LuajavaLib},
+ * but can also be used directly when working with Java/lua bindings.
+ *
+ * To coerce scalar types, the various, generally the {@code valueOf(type)} methods
+ * on {@link LuaValue} may be used:
+ *
+ * To coerce arrays of objects and lists, the {@code listOf(..)} and {@code tableOf(...)} methods
+ * on {@link LuaValue} may be used:
+ *
+ * Integral types {@code boolean}, {@code byte}, {@code char}, and {@code int}
+ * will become {@link LuaInteger};
+ * {@code long}, {@code float}, and {@code double} will become {@link LuaDouble};
+ * {@code String} and {@code byte[]} will become {@link LuaString};
+ * types inheriting from {@link LuaValue} will be returned without coercion;
+ * other types will become {@link LuaUserdata}.
+ * @param o Java object needing conversion
+ * @return {@link LuaValue} corresponding to the supplied Java value.
+ * @see LuaValue
+ * @see LuaInteger
+ * @see LuaDouble
+ * @see LuaString
+ * @see LuaUserdata
+ */
+ public static LuaValue coerce(Object o) {
+ if ( o == null )
+ return LuaValue.NIL;
+ Class clazz = o.getClass();
+ Coercion c = (Coercion) COERCIONS.get( clazz );
+ if ( c == null ) {
+ c = clazz.isArray()? arrayCoercion:
+ o instanceof LuaValue ? luaCoercion:
+ instanceCoercion;
+ COERCIONS.put( clazz, c );
+ }
+ return c.coerce(o);
+ }
+
+ static final Coercion instanceCoercion = new InstanceCoercion();
+
+ static final Coercion arrayCoercion = new ArrayCoercion();
+
+ static final Coercion luaCoercion = new LuaCoercion() ;
+}
diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/CoerceLuaToJava.java b/jse/src/main/java/org/luaj/vm2/libs/jse/CoerceLuaToJava.java
new file mode 100644
index 00000000..b3380152
--- /dev/null
+++ b/jse/src/main/java/org/luaj/vm2/libs/jse/CoerceLuaToJava.java
@@ -0,0 +1,372 @@
+/*******************************************************************************
+ * Copyright (c) 2009-2011 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.libs.jse;
+
+import java.lang.reflect.Array;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.luaj.vm2.LuaString;
+import org.luaj.vm2.LuaTable;
+import org.luaj.vm2.LuaValue;
+
+/**
+ * Helper class to coerce values from lua to Java within the luajava library.
+ *
+ * This class is primarily used by the {@link LuajavaLib},
+ * but can also be used directly when working with Java/lua bindings.
+ *
+ * To coerce to specific Java values, generally the {@code toType()} methods
+ * on {@link LuaValue} may be used:
+ *
+ * For data in lua tables, the various methods on {@link LuaTable} can be used directly
+ * to convert data to something more useful.
+ *
+ * @see LuajavaLib
+ * @see CoerceJavaToLua
+ */
+public class CoerceLuaToJava {
+
+ static int SCORE_NULL_VALUE = 0x10;
+ static int SCORE_WRONG_TYPE = 0x100;
+ static int SCORE_UNCOERCIBLE = 0x10000;
+
+ static interface Coercion {
+ public int score( LuaValue value );
+ public Object coerce( LuaValue value );
+ };
+
+ /**
+ * Coerce a LuaValue value to a specified java class
+ * @param value LuaValue to coerce
+ * @param clazz Class to coerce into
+ * @return Object of type clazz (or a subclass) with the corresponding value.
+ */
+ public static Object coerce(LuaValue value, Class clazz) {
+ return getCoercion(clazz).coerce(value);
+ }
+
+ static final Map COERCIONS = Collections.synchronizedMap(new HashMap());
+
+ static final class BoolCoercion implements Coercion {
+ public String toString() {
+ return "BoolCoercion()";
+ }
+ public int score( LuaValue value ) {
+ switch ( value.type() ) {
+ case LuaValue.TBOOLEAN:
+ return 0;
+ }
+ return 1;
+ }
+
+ public Object coerce(LuaValue value) {
+ return value.toboolean()? Boolean.TRUE: Boolean.FALSE;
+ }
+ }
+
+ static final class NumericCoercion implements Coercion {
+ static final int TARGET_TYPE_BYTE = 0;
+ static final int TARGET_TYPE_CHAR = 1;
+ static final int TARGET_TYPE_SHORT = 2;
+ static final int TARGET_TYPE_INT = 3;
+ static final int TARGET_TYPE_LONG = 4;
+ static final int TARGET_TYPE_FLOAT = 5;
+ static final int TARGET_TYPE_DOUBLE = 6;
+ static final String[] TYPE_NAMES = { "byte", "char", "short", "int", "long", "float", "double" };
+ final int targetType;
+ public String toString() {
+ return "NumericCoercion("+TYPE_NAMES[targetType]+")";
+ }
+ NumericCoercion(int targetType) {
+ this.targetType = targetType;
+ }
+ public int score( LuaValue value ) {
+ int fromStringPenalty = 0;
+ if ( value.type() == LuaValue.TSTRING ) {
+ value = value.tonumber();
+ if ( value.isnil() ) {
+ return SCORE_UNCOERCIBLE;
+ }
+ fromStringPenalty = 4;
+ }
+ if ( value.isint() ) {
+ switch ( targetType ) {
+ case TARGET_TYPE_BYTE: {
+ int i = value.toint();
+ return fromStringPenalty + ((i==(byte)i)? 0: SCORE_WRONG_TYPE);
+ }
+ case TARGET_TYPE_CHAR: {
+ int i = value.toint();
+ return fromStringPenalty + ((i==(byte)i)? 1: (i==(char)i)? 0: SCORE_WRONG_TYPE);
+ }
+ case TARGET_TYPE_SHORT: {
+ int i = value.toint();
+ return fromStringPenalty +
+ ((i==(byte)i)? 1: (i==(short)i)? 0: SCORE_WRONG_TYPE);
+ }
+ case TARGET_TYPE_INT: {
+ int i = value.toint();
+ return fromStringPenalty +
+ ((i==(byte)i)? 2: ((i==(char)i) || (i==(short)i))? 1: 0);
+ }
+ case TARGET_TYPE_FLOAT: return fromStringPenalty + 1;
+ case TARGET_TYPE_LONG: return fromStringPenalty + 1;
+ case TARGET_TYPE_DOUBLE: return fromStringPenalty + 2;
+ default: return SCORE_WRONG_TYPE;
+ }
+ } else if ( value.isnumber() ) {
+ switch ( targetType ) {
+ case TARGET_TYPE_BYTE: return SCORE_WRONG_TYPE;
+ case TARGET_TYPE_CHAR: return SCORE_WRONG_TYPE;
+ case TARGET_TYPE_SHORT: return SCORE_WRONG_TYPE;
+ case TARGET_TYPE_INT: return SCORE_WRONG_TYPE;
+ case TARGET_TYPE_LONG: {
+ double d = value.todouble();
+ return fromStringPenalty + ((d==(long)d)? 0: SCORE_WRONG_TYPE);
+ }
+ case TARGET_TYPE_FLOAT: {
+ double d = value.todouble();
+ return fromStringPenalty + ((d==(float)d)? 0: SCORE_WRONG_TYPE);
+ }
+ case TARGET_TYPE_DOUBLE: {
+ double d = value.todouble();
+ return fromStringPenalty + (((d==(long)d) || (d==(float)d))? 1: 0);
+ }
+ default: return SCORE_WRONG_TYPE;
+ }
+ } else {
+ return SCORE_UNCOERCIBLE;
+ }
+ }
+
+ public Object coerce(LuaValue value) {
+ switch ( targetType ) {
+ case TARGET_TYPE_BYTE: return Byte.valueOf( (byte) value.toint() );
+ case TARGET_TYPE_CHAR: return Character.valueOf( (char) value.toint() );
+ case TARGET_TYPE_SHORT: return Short.valueOf( (short) value.toint() );
+ case TARGET_TYPE_INT: return Integer.valueOf( (int) value.toint() );
+ case TARGET_TYPE_LONG: return Long.valueOf( (long) value.todouble() );
+ case TARGET_TYPE_FLOAT: return Float.valueOf( (float) value.todouble() );
+ case TARGET_TYPE_DOUBLE: return Double.valueOf( (double) value.todouble() );
+ default: return null;
+ }
+ }
+ }
+
+ static final class StringCoercion implements Coercion {
+ public static final int TARGET_TYPE_STRING = 0;
+ public static final int TARGET_TYPE_BYTES = 1;
+ final int targetType;
+ public StringCoercion(int targetType) {
+ this.targetType = targetType;
+ }
+ public String toString() {
+ return "StringCoercion("+(targetType==TARGET_TYPE_STRING? "String": "byte[]")+")";
+ }
+ public int score(LuaValue value) {
+ switch ( value.type() ) {
+ case LuaValue.TSTRING:
+ return value.checkstring().isValidUtf8()?
+ (targetType==TARGET_TYPE_STRING? 0: 1):
+ (targetType==TARGET_TYPE_BYTES? 0: SCORE_WRONG_TYPE);
+ case LuaValue.TNIL:
+ return SCORE_NULL_VALUE;
+ default:
+ return targetType == TARGET_TYPE_STRING? SCORE_WRONG_TYPE: SCORE_UNCOERCIBLE;
+ }
+ }
+ public Object coerce(LuaValue value) {
+ if ( value.isnil() )
+ return null;
+ if ( targetType == TARGET_TYPE_STRING )
+ return value.tojstring();
+ LuaString s = value.checkstring();
+ byte[] b = new byte[s.m_length];
+ s.copyInto(0, b, 0, b.length);
+ return b;
+ }
+ }
+
+ static final class ArrayCoercion implements Coercion {
+ final Class componentType;
+ final Coercion componentCoercion;
+ public ArrayCoercion(Class componentType) {
+ this.componentType = componentType;
+ this.componentCoercion = getCoercion(componentType);
+ }
+ public String toString() {
+ return "ArrayCoercion("+componentType.getName()+")";
+ }
+ public int score(LuaValue value) {
+ switch ( value.type() ) {
+ case LuaValue.TTABLE:
+ return value.length()==0? 0: componentCoercion.score( value.get(1) );
+ case LuaValue.TUSERDATA:
+ return inheritanceLevels( componentType, value.touserdata().getClass().getComponentType() );
+ case LuaValue.TNIL:
+ return SCORE_NULL_VALUE;
+ default:
+ return SCORE_UNCOERCIBLE;
+ }
+ }
+ public Object coerce(LuaValue value) {
+ switch ( value.type() ) {
+ case LuaValue.TTABLE: {
+ int n = value.length();
+ Object a = Array.newInstance(componentType, n);
+ for ( int i=0; i
+ * This class is not used directly.
+ * It is returned by calls to {@link CoerceJavaToLua#coerce(Object)}
+ * when an array is supplied.
+ * @see CoerceJavaToLua
+ * @see CoerceLuaToJava
+ */
+class JavaArray extends LuaUserdata {
+
+ private static final class LenFunction extends OneArgFunction {
+ public LuaValue call(LuaValue u) {
+ return LuaValue.valueOf(Array.getLength(((LuaUserdata)u).m_instance));
+ }
+ }
+
+ static final LuaValue LENGTH = valueOf("length");
+
+ static final LuaTable array_metatable;
+ static {
+ array_metatable = new LuaTable();
+ array_metatable.rawset(LuaValue.LEN, new LenFunction());
+ }
+
+ JavaArray(Object instance) {
+ super(instance);
+ setmetatable(array_metatable);
+ }
+
+ public LuaValue get(LuaValue key) {
+ if ( key.equals(LENGTH) )
+ return valueOf(Array.getLength(m_instance));
+ if ( key.isint() ) {
+ int i = key.toint() - 1;
+ return i>=0 && i
+ * This class is not used directly.
+ * It is returned by calls to {@link CoerceJavaToLua#coerce(Object)}
+ * when a Class is supplied.
+ * @see CoerceJavaToLua
+ * @see CoerceLuaToJava
+ */
+class JavaClass extends JavaInstance implements CoerceJavaToLua.Coercion {
+
+ static final Map classes = Collections.synchronizedMap(new HashMap());
+
+ static final LuaValue NEW = valueOf("new");
+
+ Map fields;
+ Map methods;
+ Map innerclasses;
+
+ static JavaClass forClass(Class c) {
+ JavaClass j = (JavaClass) classes.get(c);
+ if ( j == null )
+ classes.put( c, j = new JavaClass(c) );
+ return j;
+ }
+
+ JavaClass(Class c) {
+ super(c);
+ this.jclass = this;
+ }
+
+ public LuaValue coerce(Object javaValue) {
+ return this;
+ }
+
+ Field getField(LuaValue key) {
+ if ( fields == null ) {
+ Map m = new HashMap();
+ Field[] f = ((Class)m_instance).getFields();
+ for ( int i=0; i
+ * This class is not used directly.
+ * It is returned by calls to {@link JavaClass#new(LuaValue key)}
+ * when the value of key is "new".
+ * @see CoerceJavaToLua
+ * @see CoerceLuaToJava
+ */
+class JavaConstructor extends JavaMember {
+
+ static final Map constructors = Collections.synchronizedMap(new HashMap());
+
+ static JavaConstructor forConstructor(Constructor c) {
+ JavaConstructor j = (JavaConstructor) constructors.get(c);
+ if ( j == null )
+ constructors.put( c, j = new JavaConstructor(c) );
+ return j;
+ }
+
+ public static LuaValue forConstructors(JavaConstructor[] array) {
+ return new Overload(array);
+ }
+
+ final Constructor constructor;
+
+ private JavaConstructor(Constructor c) {
+ super( c.getParameterTypes(), c.getModifiers() );
+ this.constructor = c;
+ }
+
+ public Varargs invoke(Varargs args) {
+ Object[] a = convertArgs(args);
+ try {
+ return CoerceJavaToLua.coerce( constructor.newInstance(a) );
+ } catch (InvocationTargetException e) {
+ throw new LuaError(e.getTargetException());
+ } catch (Exception e) {
+ return LuaValue.error("coercion error "+e);
+ }
+ }
+
+ /**
+ * LuaValue that represents an overloaded Java constructor.
+ *
+ * On invocation, will pick the best method from the list, and invoke it.
+ *
+ * This class is not used directly.
+ * It is returned by calls to calls to {@link JavaClass#get(LuaValue key)}
+ * when key is "new" and there is more than one public constructor.
+ */
+ static class Overload extends VarArgFunction {
+ final JavaConstructor[] constructors;
+ public Overload(JavaConstructor[] c) {
+ this.constructors = c;
+ }
+
+ public Varargs invoke(Varargs args) {
+ JavaConstructor best = null;
+ int score = CoerceLuaToJava.SCORE_UNCOERCIBLE;
+ for ( int i=0; i
+ * This class is not used directly.
+ * It is returned by calls to {@link CoerceJavaToLua#coerce(Object)}
+ * when a subclass of Object is supplied.
+ * @see CoerceJavaToLua
+ * @see CoerceLuaToJava
+ */
+class JavaInstance extends LuaUserdata {
+
+ JavaClass jclass;
+
+ JavaInstance(Object instance) {
+ super(instance);
+ }
+
+ public LuaValue get(LuaValue key) {
+ if ( jclass == null )
+ jclass = JavaClass.forClass(m_instance.getClass());
+ Field f = jclass.getField(key);
+ if ( f != null )
+ try {
+ return CoerceJavaToLua.coerce(f.get(m_instance));
+ } catch (Exception e) {
+ throw new LuaError(e);
+ }
+ LuaValue m = jclass.getMethod(key);
+ if ( m != null )
+ return m;
+ Class c = jclass.getInnerClass(key);
+ if ( c != null )
+ return JavaClass.forClass(c);
+ return super.get(key);
+ }
+
+ public void set(LuaValue key, LuaValue value) {
+ if ( jclass == null )
+ jclass = JavaClass.forClass(m_instance.getClass());
+ Field f = jclass.getField(key);
+ if ( f != null )
+ try {
+ f.set(m_instance, CoerceLuaToJava.coerce(value, f.getType()));
+ return;
+ } catch (Exception e) {
+ throw new LuaError(e);
+ }
+ super.set(key, value);
+ }
+
+}
diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/JavaMember.java b/jse/src/main/java/org/luaj/vm2/libs/jse/JavaMember.java
new file mode 100644
index 00000000..cc03f754
--- /dev/null
+++ b/jse/src/main/java/org/luaj/vm2/libs/jse/JavaMember.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+* Copyright (c) 2011 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.libs.jse;
+
+import org.luaj.vm2.Varargs;
+import org.luaj.vm2.libs.VarArgFunction;
+import org.luaj.vm2.libs.jse.CoerceLuaToJava.Coercion;
+
+/**
+ * Java method or constructor.
+ *
+ * Primarily handles argument coercion for parameter lists including scoring of compatibility and
+ * java varargs handling.
+ *
+ * This class is not used directly.
+ * It is an abstract base class for {@link JavaConstructor} and {@link JavaMethod}.
+ * @see JavaConstructor
+ * @see JavaMethod
+ * @see CoerceJavaToLua
+ * @see CoerceLuaToJava
+ */
+abstract
+class JavaMember extends VarArgFunction {
+
+ static final int METHOD_MODIFIERS_VARARGS = 0x80;
+
+ final Coercion[] fixedargs;
+ final Coercion varargs;
+
+ protected JavaMember(Class[] params, int modifiers) {
+ boolean isvarargs = ((modifiers & METHOD_MODIFIERS_VARARGS) != 0);
+ fixedargs = new Coercion[isvarargs? params.length-1: params.length];
+ for ( int i=0; i
+ * This class is not used directly.
+ * It is returned by calls to calls to {@link JavaInstance#get(LuaValue key)}
+ * when a method is named.
+ * @see CoerceJavaToLua
+ * @see CoerceLuaToJava
+ */
+class JavaMethod extends JavaMember {
+
+ static final Map methods = Collections.synchronizedMap(new HashMap());
+
+ static JavaMethod forMethod(Method m) {
+ JavaMethod j = (JavaMethod) methods.get(m);
+ if ( j == null )
+ methods.put( m, j = new JavaMethod(m) );
+ return j;
+ }
+
+ static LuaFunction forMethods(JavaMethod[] m) {
+ return new Overload(m);
+ }
+
+ final Method method;
+
+ private JavaMethod(Method m) {
+ super( m.getParameterTypes(), m.getModifiers() );
+ this.method = m;
+ try {
+ if (!m.isAccessible())
+ m.setAccessible(true);
+ } catch (SecurityException s) {
+ }
+ }
+
+ public LuaValue call() {
+ return error("method cannot be called without instance");
+ }
+
+ public LuaValue call(LuaValue arg) {
+ return invokeMethod(arg.checkuserdata(), LuaValue.NONE);
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2) {
+ return invokeMethod(arg1.checkuserdata(), arg2);
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
+ return invokeMethod(arg1.checkuserdata(), LuaValue.varargsOf(arg2, arg3));
+ }
+
+ public Varargs invoke(Varargs args) {
+ return invokeMethod(args.checkuserdata(1), args.subargs(2));
+ }
+
+ LuaValue invokeMethod(Object instance, Varargs args) {
+ Object[] a = convertArgs(args);
+ try {
+ return CoerceJavaToLua.coerce( method.invoke(instance, a) );
+ } catch (InvocationTargetException e) {
+ throw new LuaError(e.getTargetException());
+ } catch (Exception e) {
+ return LuaValue.error("coercion error "+e);
+ }
+ }
+
+ /**
+ * LuaValue that represents an overloaded Java method.
+ *
+ * On invocation, will pick the best method from the list, and invoke it.
+ *
+ * This class is not used directly.
+ * It is returned by calls to calls to {@link JavaInstance#get(LuaValue key)}
+ * when an overloaded method is named.
+ */
+ static class Overload extends LuaFunction {
+
+ final JavaMethod[] methods;
+
+ Overload(JavaMethod[] methods) {
+ this.methods = methods;
+ }
+
+ public LuaValue call() {
+ return error("method cannot be called without instance");
+ }
+
+ public LuaValue call(LuaValue arg) {
+ return invokeBestMethod(arg.checkuserdata(), LuaValue.NONE);
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2) {
+ return invokeBestMethod(arg1.checkuserdata(), arg2);
+ }
+
+ public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
+ return invokeBestMethod(arg1.checkuserdata(), LuaValue.varargsOf(arg2, arg3));
+ }
+
+ public Varargs invoke(Varargs args) {
+ return invokeBestMethod(args.checkuserdata(1), args.subargs(2));
+ }
+
+ private LuaValue invokeBestMethod(Object instance, Varargs args) {
+ JavaMethod best = null;
+ int score = CoerceLuaToJava.SCORE_UNCOERCIBLE;
+ for ( int i=0; i
+ * Typically, this library is included as part of a call to
+ * {@link JsePlatform#standardGlobals()}
+ *
+ * For special cases where the smallest possible footprint is desired,
+ * a minimal set of libraries could be loaded
+ * directly via {@link Globals#load(LuaValue)} using code such as:
+ * However, other libraries such as PackageLib are not loaded in this case.
+ *
+ * This is a direct port of the corresponding library in C.
+ * @see Globals
+ * @see BaseLib
+ * @see ResourceFinder
+ * @see Globals#finder
+ * @see LibFunction
+ * @see JsePlatform
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ * @see Lua 5.2 Base Lib Reference
+ */
+
+public class JseBaseLib extends BaseLib {
+
+
+ /** Perform one-time initialization on the library by creating a table
+ * containing the library functions, adding that table to the supplied environment,
+ * adding the table to package.loaded, and returning table as the return value.
+ * Specifically, extend the library loading to set the default value for {@link Globals#STDIN}
+ * @param modname the module name supplied if this is loaded via 'require'.
+ * @param env the environment to load into, which must be a Globals instance.
+ */
+ public LuaValue call(LuaValue modname, LuaValue env) {
+ super.call(modname, env);
+ env.checkglobals().STDIN = System.in;
+ return env;
+ }
+
+
+ /**
+ * Try to open a file in the current working directory,
+ * or fall back to base opener if not found.
+ *
+ * This implementation attempts to open the file using new File(filename).
+ * It falls back to the base implementation that looks it up as a resource
+ * in the class path if not found as a plain file.
+ *
+ * @see BaseLib
+ * @see ResourceFinder
+ *
+ * @param filename
+ * @return InputStream, or null if not found.
+ */
+ public InputStream findResource(String filename) {
+ File f = new File(filename);
+ if ( ! f.exists() )
+ return super.findResource(filename);
+ try {
+ return new BufferedInputStream(new FileInputStream(f));
+ } catch ( IOException ioe ) {
+ return null;
+ }
+ }
+}
+
diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/JseIoLib.java b/jse/src/main/java/org/luaj/vm2/libs/jse/JseIoLib.java
new file mode 100644
index 00000000..4ced4734
--- /dev/null
+++ b/jse/src/main/java/org/luaj/vm2/libs/jse/JseIoLib.java
@@ -0,0 +1,343 @@
+/*******************************************************************************
+* Copyright (c) 2009 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.libs.jse;
+
+import java.io.BufferedInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.RandomAccessFile;
+
+import org.luaj.vm2.Globals;
+import org.luaj.vm2.LuaError;
+import org.luaj.vm2.LuaString;
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.libs.IoLib;
+import org.luaj.vm2.libs.LibFunction;
+
+/**
+ * Subclass of {@link IoLib} and therefore {@link LibFunction} which implements the lua standard {@code io}
+ * library for the JSE platform.
+ *
+ * It uses RandomAccessFile to implement seek on files.
+ *
+ * Typically, this library is included as part of a call to
+ * {@link JsePlatform#standardGlobals()}
+ *
+ * For special cases where the smallest possible footprint is desired,
+ * a minimal set of libraries could be loaded
+ * directly via {@link Globals#load(LuaValue)} using code such as:
+ * However, other libraries such as MathLib are not loaded in this case.
+ *
+ * This has been implemented to match as closely as possible the behavior in the corresponding library in C.
+ * @see LibFunction
+ * @see JsePlatform
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ * @see IoLib
+ * @see org.luaj.vm2.libs.jme.JmeIoLib
+ * @see Lua 5.2 I/O Lib Reference
+ */
+public class JseIoLib extends IoLib {
+
+ protected File wrapStdin() throws IOException {
+ return new StdinFile();
+ }
+
+ protected File wrapStdout() throws IOException {
+ return new StdoutFile(FTYPE_STDOUT);
+ }
+
+ protected File wrapStderr() throws IOException {
+ return new StdoutFile(FTYPE_STDERR);
+ }
+
+ protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException {
+ RandomAccessFile f = new RandomAccessFile(filename,readMode? "r": "rw");
+ if ( appendMode ) {
+ f.seek(f.length());
+ } else {
+ if ( ! readMode )
+ f.setLength(0);
+ }
+ return new FileImpl( f );
+ }
+
+ protected File openProgram(String prog, String mode) throws IOException {
+ final Process p = Runtime.getRuntime().exec(prog);
+ return "w".equals(mode)?
+ new FileImpl( p.getOutputStream() ):
+ new FileImpl( p.getInputStream() );
+ }
+
+ protected File tmpFile() throws IOException {
+ java.io.File f = java.io.File.createTempFile(".luaj","bin");
+ f.deleteOnExit();
+ return new FileImpl( new RandomAccessFile(f,"rw") );
+ }
+
+ private static void notimplemented() {
+ throw new LuaError("not implemented");
+ }
+
+
+ private final class FileImpl extends File {
+ private final RandomAccessFile file;
+ private final InputStream is;
+ private final OutputStream os;
+ private boolean closed = false;
+ private boolean nobuffer = false;
+ private FileImpl( RandomAccessFile file, InputStream is, OutputStream os ) {
+ this.file = file;
+ this.is = is!=null? is.markSupported()? is: new BufferedInputStream(is): null;
+ this.os = os;
+ }
+ private FileImpl( RandomAccessFile f ) {
+ this( f, null, null );
+ }
+ private FileImpl( InputStream i ) {
+ this( null, i, null );
+ }
+ private FileImpl( OutputStream o ) {
+ this( null, null, o );
+ }
+ public String tojstring() {
+ return "file (" + (this.closed ? "closed" : String.valueOf(this.hashCode())) + ")";
+ }
+ public boolean isstdfile() {
+ return file == null;
+ }
+ public void close() throws IOException {
+ closed = true;
+ if ( file != null ) {
+ file.close();
+ }
+ }
+ public void flush() throws IOException {
+ if ( os != null )
+ os.flush();
+ }
+ public void write(LuaString s) throws IOException {
+ if ( os != null )
+ os.write( s.m_bytes, s.m_offset, s.m_length );
+ else if ( file != null )
+ file.write( s.m_bytes, s.m_offset, s.m_length );
+ else
+ notimplemented();
+ if ( nobuffer )
+ flush();
+ }
+ public boolean isclosed() {
+ return closed;
+ }
+ public int seek(String option, int pos) throws IOException {
+ if ( file != null ) {
+ if ( "set".equals(option) ) {
+ file.seek(pos);
+ } else if ( "end".equals(option) ) {
+ file.seek(file.length()+pos);
+ } else {
+ file.seek(file.getFilePointer()+pos);
+ }
+ return (int) file.getFilePointer();
+ }
+ notimplemented();
+ return 0;
+ }
+ public void setvbuf(String mode, int size) {
+ nobuffer = "no".equals(mode);
+ }
+
+ // get length remaining to read
+ public int remaining() throws IOException {
+ return file!=null? (int) (file.length()-file.getFilePointer()): -1;
+ }
+
+ // peek ahead one character
+ public int peek() throws IOException {
+ if ( is != null ) {
+ is.mark(1);
+ int c = is.read();
+ is.reset();
+ return c;
+ } else if ( file != null ) {
+ long fp = file.getFilePointer();
+ int c = file.read();
+ file.seek(fp);
+ return c;
+ }
+ notimplemented();
+ return 0;
+ }
+
+ // return char if read, -1 if eof, throw IOException on other exception
+ public int read() throws IOException {
+ if ( is != null )
+ return is.read();
+ else if ( file != null ) {
+ return file.read();
+ }
+ notimplemented();
+ return 0;
+ }
+
+ // return number of bytes read if positive, -1 if eof, throws IOException
+ public int read(byte[] bytes, int offset, int length) throws IOException {
+ if (file!=null) {
+ return file.read(bytes, offset, length);
+ } else if (is!=null) {
+ return is.read(bytes, offset, length);
+ } else {
+ notimplemented();
+ }
+ return length;
+ }
+ }
+
+ private final class StdoutFile extends File {
+ private final int file_type;
+
+ private StdoutFile(int file_type) {
+ this.file_type = file_type;
+ }
+
+ public String tojstring() {
+ return "file ("+this.hashCode()+")";
+ }
+
+ private final PrintStream getPrintStream() {
+ return file_type == FTYPE_STDERR?
+ globals.STDERR:
+ globals.STDOUT;
+ }
+
+ public void write(LuaString string) throws IOException {
+ getPrintStream().write(string.m_bytes, string.m_offset, string.m_length);
+ }
+
+ public void flush() throws IOException {
+ getPrintStream().flush();
+ }
+
+ public boolean isstdfile() {
+ return true;
+ }
+
+ public void close() throws IOException {
+ // do not close std files.
+ }
+
+ public boolean isclosed() {
+ return false;
+ }
+
+ public int seek(String option, int bytecount) throws IOException {
+ return 0;
+ }
+
+ public void setvbuf(String mode, int size) {
+ }
+
+ public int remaining() throws IOException {
+ return 0;
+ }
+
+ public int peek() throws IOException, EOFException {
+ return 0;
+ }
+
+ public int read() throws IOException, EOFException {
+ return 0;
+ }
+
+ public int read(byte[] bytes, int offset, int length)
+ throws IOException {
+ return 0;
+ }
+ }
+
+ private final class StdinFile extends File {
+ private StdinFile() {
+ }
+
+ public String tojstring() {
+ return "file ("+this.hashCode()+")";
+ }
+
+ public void write(LuaString string) throws IOException {
+ }
+
+ public void flush() throws IOException {
+ }
+
+ public boolean isstdfile() {
+ return true;
+ }
+
+ public void close() throws IOException {
+ // do not close std files.
+ }
+
+ public boolean isclosed() {
+ return false;
+ }
+
+ public int seek(String option, int bytecount) throws IOException {
+ return 0;
+ }
+
+ public void setvbuf(String mode, int size) {
+ }
+
+ public int remaining() throws IOException {
+ return -1;
+ }
+
+ public int peek() throws IOException, EOFException {
+ globals.STDIN.mark(1);
+ int c = globals.STDIN.read();
+ globals.STDIN.reset();
+ return c;
+ }
+
+ public int read() throws IOException, EOFException {
+ return globals.STDIN.read();
+ }
+
+ public int read(byte[] bytes, int offset, int length)
+ throws IOException {
+ return globals.STDIN.read(bytes, offset, length);
+ }
+ }
+}
diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/JseMathLib.java b/jse/src/main/java/org/luaj/vm2/libs/jse/JseMathLib.java
new file mode 100644
index 00000000..d802a50d
--- /dev/null
+++ b/jse/src/main/java/org/luaj/vm2/libs/jse/JseMathLib.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+* Copyright (c) 2009-2011 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.libs.jse;
+
+import org.luaj.vm2.Globals;
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.libs.LibFunction;
+import org.luaj.vm2.libs.TwoArgFunction;
+
+/**
+ * Subclass of {@link LibFunction} which implements the lua standard {@code math}
+ * library.
+ *
+ * It contains all lua math functions, including those not available on the JME platform.
+ * See {@link org.luaj.vm2.libs.MathLib} for the exception list.
+ *
+ * Typically, this library is included as part of a call to
+ * {@link JsePlatform#standardGlobals()}
+ *
+ * For special cases where the smallest possible footprint is desired,
+ * a minimal set of libraries could be loaded
+ * directly via {@link Globals#load(LuaValue)} using code such as:
+ * However, other libraries such as CoroutineLib are not loaded in this case.
+ *
+ * This has been implemented to match as closely as possible the behavior in the corresponding library in C.
+ * @see LibFunction
+ * @see JsePlatform
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ * @see JseMathLib
+ * @see Lua 5.2 Math Lib Reference
+ */
+public class JseMathLib extends org.luaj.vm2.libs.MathLib {
+
+ public JseMathLib() {}
+
+
+ /** Perform one-time initialization on the library by creating a table
+ * containing the library functions, adding that table to the supplied environment,
+ * adding the table to package.loaded, and returning table as the return value.
+ * Specifically, adds all library functions that can be implemented directly
+ * in JSE but not JME: acos, asin, atan, atan2, cosh, exp, log, pow, sinh, and tanh.
+ * @param modname the module name supplied if this is loaded via 'require'.
+ * @param env the environment to load into, which must be a Globals instance.
+ */
+ public LuaValue call(LuaValue modname, LuaValue env) {
+ super.call(modname, env);
+ LuaValue math = env.get("math");
+ math.set("acos", new acos());
+ math.set("asin", new asin());
+ LuaValue atan = new atan2();
+ math.set("atan", atan);
+ math.set("atan2", atan);
+ math.set("cosh", new cosh());
+ math.set("exp", new exp());
+ math.set("log", new log());
+ math.set("pow", new pow());
+ math.set("sinh", new sinh());
+ math.set("tanh", new tanh());
+ return math;
+ }
+
+ static final class acos extends UnaryOp { protected double call(double d) { return Math.acos(d); } }
+ static final class asin extends UnaryOp { protected double call(double d) { return Math.asin(d); } }
+ static final class atan2 extends TwoArgFunction {
+ public LuaValue call(LuaValue x, LuaValue y) {
+ return valueOf(Math.atan2(x.checkdouble(), y.optdouble(1)));
+ }
+ }
+ static final class cosh extends UnaryOp { protected double call(double d) { return Math.cosh(d); } }
+ static final class exp extends UnaryOp { protected double call(double d) { return Math.exp(d); } }
+ static final class log extends TwoArgFunction {
+ public LuaValue call(LuaValue x, LuaValue base) {
+ double nat = Math.log(x.checkdouble());
+ double b = base.optdouble(Math.E);
+ if (b != Math.E) nat /= Math.log(b);
+ return valueOf(nat);
+ }
+ }
+ static final class pow extends BinaryOp { protected double call(double x, double y) { return Math.pow(x, y); } }
+ static final class sinh extends UnaryOp { protected double call(double d) { return Math.sinh(d); } }
+ static final class tanh extends UnaryOp { protected double call(double d) { return Math.tanh(d); } }
+
+ /** Faster, better version of pow() used by arithmetic operator ^ */
+ public double dpow_lib(double a, double b) {
+ return Math.pow(a, b);
+ }
+
+
+}
+
diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/JseOsLib.java b/jse/src/main/java/org/luaj/vm2/libs/jse/JseOsLib.java
new file mode 100644
index 00000000..37433e85
--- /dev/null
+++ b/jse/src/main/java/org/luaj/vm2/libs/jse/JseOsLib.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+* Copyright (c) 2009 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.libs.jse;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.luaj.vm2.Globals;
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.Varargs;
+import org.luaj.vm2.libs.LibFunction;
+import org.luaj.vm2.libs.OsLib;
+
+/**
+ * Subclass of {@link LibFunction} which implements the standard lua {@code os} library.
+ *
+ * This contains more complete implementations of the following functions
+ * using features that are specific to JSE:
+ *
+ * Because the nature of the {@code os} library is to encapsulate
+ * os-specific features, the behavior of these functions varies considerably
+ * from their counterparts in the C platform.
+ *
+ * Typically, this library is included as part of a call to
+ * {@link JsePlatform#standardGlobals()}
+ *
+ * For special cases where the smallest possible footprint is desired,
+ * a minimal set of libraries could be loaded
+ * directly via {@link Globals#load(LuaValue)} using code such as:
+ * However, other libraries such as MathLib are not loaded in this case.
+ *
+ * @see LibFunction
+ * @see OsLib
+ * @see JsePlatform
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ * @see Lua 5.2 OS Lib Reference
+ */
+public class JseOsLib extends OsLib {
+
+ /** return code indicating the execute() threw an I/O exception */
+ public static final int EXEC_IOEXCEPTION = 1;
+
+ /** return code indicating the execute() was interrupted */
+ public static final int EXEC_INTERRUPTED = -2;
+
+ /** return code indicating the execute() threw an unknown exception */
+ public static final int EXEC_ERROR = -3;
+
+ /** public constructor */
+ public JseOsLib() {
+ }
+
+ protected String getenv(String varname) {
+ String s = System.getenv(varname);
+ return s != null? s : System.getProperty(varname);
+ }
+
+ protected Varargs execute(String command) {
+ int exitValue;
+ try {
+ exitValue = new JseProcess(command, null, globals.STDOUT, globals.STDERR).waitFor();
+ } catch (IOException ioe) {
+ exitValue = EXEC_IOEXCEPTION;
+ } catch (InterruptedException e) {
+ exitValue = EXEC_INTERRUPTED;
+ } catch (Throwable t) {
+ 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 {
+ File f = new File(filename);
+ if ( ! f.exists() )
+ throw new IOException("No such file or directory");
+ if ( ! f.delete() )
+ throw new IOException("Failed to delete");
+ }
+
+ protected void rename(String oldname, String newname) throws IOException {
+ File f = new File(oldname);
+ if ( ! f.exists() )
+ throw new IOException("No such file or directory");
+ if ( ! f.renameTo(new File(newname)) )
+ throw new IOException("Failed to rename");
+ }
+
+ protected String tmpname() {
+ try {
+ File f = File.createTempFile(TMP_PREFIX ,TMP_SUFFIX);
+ return f.getAbsolutePath();
+ } catch ( IOException ioe ) {
+ return super.tmpname();
+ }
+ }
+
+}
diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.java b/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.java
new file mode 100644
index 00000000..c1c82f9f
--- /dev/null
+++ b/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright (c) 2009-2011 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.libs.jse;
+
+import org.luaj.vm2.Globals;
+import org.luaj.vm2.LoadState;
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.Varargs;
+import org.luaj.vm2.compiler.LuaC;
+import org.luaj.vm2.libs.Bit32Lib;
+import org.luaj.vm2.libs.CoroutineLib;
+import org.luaj.vm2.libs.DebugLib;
+import org.luaj.vm2.libs.PackageLib;
+import org.luaj.vm2.libs.ResourceFinder;
+import org.luaj.vm2.libs.StringLib;
+import org.luaj.vm2.libs.TableLib;
+
+/** The {@link JsePlatform} class is a convenience class to standardize
+ * how globals tables are initialized for the JSE platform.
+ *
+ * It is used to allocate either a set of standard globals using
+ * {@link #standardGlobals()} or debug globals using {@link #debugGlobals()}
+ *
+ * A simple example of initializing globals and using them from Java is:
+ *
+ * Once globals are created, a simple way to load and run a script is:
+ *
+ * although {@code require} could also be used:
+ *
+ * The standard globals will contain all standard libraries plus {@code luajava}:
+ *
+ * The debug globals are simply the standard globals plus the {@code debug} library {@link DebugLib}.
+ *
+ * The class ensures that initialization is done in the correct order.
+ *
+ * @see Globals
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ */
+public class JsePlatform {
+
+ /**
+ * Create a standard set of globals for JSE including all the libraries.
+ *
+ * @return Table of globals initialized with the standard JSE libraries
+ * @see #debugGlobals()
+ * @see JsePlatform
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ */
+ public static Globals standardGlobals() {
+ Globals globals = new Globals();
+ globals.load(new JseBaseLib());
+ globals.load(new PackageLib());
+ globals.load(new Bit32Lib());
+ globals.load(new TableLib());
+ globals.load(new JseStringLib());
+ globals.load(new CoroutineLib());
+ globals.load(new JseMathLib());
+ globals.load(new JseIoLib());
+ globals.load(new JseOsLib());
+ globals.load(new LuajavaLib());
+ LoadState.install(globals);
+ LuaC.install(globals);
+ return globals;
+ }
+
+ /** Create standard globals including the {@link DebugLib} library.
+ *
+ * @return Table of globals initialized with the standard JSE and debug libraries
+ * @see #standardGlobals()
+ * @see JsePlatform
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ * @see DebugLib
+ */
+ public static Globals debugGlobals() {
+ Globals globals = standardGlobals();
+ globals.load(new DebugLib());
+ return globals;
+ }
+
+
+ /** Simple wrapper for invoking a lua function with command line arguments.
+ * The supplied function is first given a new Globals object as its environment
+ * then the program is run with arguments.
+ * @return {@link Varargs} containing any values returned by mainChunk.
+ */
+ public static Varargs luaMain(LuaValue mainChunk, String[] args) {
+ Globals g = standardGlobals();
+ int n = args.length;
+ LuaValue[] vargs = new LuaValue[args.length];
+ for (int i = 0; i < n; ++i)
+ vargs[i] = LuaValue.valueOf(args[i]);
+ LuaValue arg = LuaValue.listOf(vargs);
+ arg.set("n", n);
+ g.set("arg", arg);
+ mainChunk.initupvalue1(g);
+ return mainChunk.invoke(LuaValue.varargsOf(vargs));
+ }
+}
diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/JseProcess.java b/jse/src/main/java/org/luaj/vm2/libs/jse/JseProcess.java
new file mode 100644
index 00000000..4b0d77f9
--- /dev/null
+++ b/jse/src/main/java/org/luaj/vm2/libs/jse/JseProcess.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+* 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.libs.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 CopyThread(output, ownedOutput, ownedInput, input));
+ t.start();
+ return t;
+ }
+
+ private static final class CopyThread extends Thread {
+ private final OutputStream output;
+ private final OutputStream ownedOutput;
+ private final InputStream ownedInput;
+ private final InputStream input;
+
+ private CopyThread(OutputStream output, OutputStream ownedOutput,
+ InputStream ownedInput, InputStream input) {
+ this.output = output;
+ this.ownedOutput = ownedOutput;
+ this.ownedInput = ownedInput;
+ this.input = input;
+ }
+
+ 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();
+ }
+ }
+ }
+
+}
diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/JseStringLib.java b/jse/src/main/java/org/luaj/vm2/libs/jse/JseStringLib.java
new file mode 100644
index 00000000..a787871f
--- /dev/null
+++ b/jse/src/main/java/org/luaj/vm2/libs/jse/JseStringLib.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+* Copyright (c) 2009 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.libs.jse;
+
+public class JseStringLib extends org.luaj.vm2.libs.StringLib {
+
+ /** public constructor */
+ public JseStringLib() {
+ }
+
+ protected String format(String src, double x) {
+ String out;
+ try {
+ out = String.format(src, new Object[] {Double.valueOf(x)});
+ } catch (Throwable e) {
+ out = super.format(src, x);
+ }
+ return out;
+ }
+}
diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/LuajavaLib.java b/jse/src/main/java/org/luaj/vm2/libs/jse/LuajavaLib.java
new file mode 100644
index 00000000..1406c96b
--- /dev/null
+++ b/jse/src/main/java/org/luaj/vm2/libs/jse/LuajavaLib.java
@@ -0,0 +1,214 @@
+/*******************************************************************************
+* Copyright (c) 2009 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.libs.jse;
+
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.luaj.vm2.Globals;
+import org.luaj.vm2.LuaError;
+import org.luaj.vm2.LuaTable;
+import org.luaj.vm2.LuaValue;
+import org.luaj.vm2.Varargs;
+import org.luaj.vm2.compiler.LuaC;
+import org.luaj.vm2.libs.LibFunction;
+import org.luaj.vm2.libs.VarArgFunction;
+
+/**
+ * Subclass of {@link LibFunction} which implements the features of the luajava package.
+ *
+ * Luajava is an approach to mixing lua and java using simple functions that bind
+ * java classes and methods to lua dynamically. The API is documented on the
+ * luajava documentation pages.
+ *
+ *
+ * Typically, this library is included as part of a call to
+ * {@link JsePlatform#standardGlobals()}
+ *
+ * To instantiate and use it directly,
+ * link it into your globals table via {@link Globals#load} using code such as:
+ *
+ *
+ * The {@code luajava} library is available
+ * on all JSE platforms via the call to {@link JsePlatform#standardGlobals()}
+ * and the luajava api's are simply invoked from lua.
+ * Because it makes extensive use of Java's reflection API, it is not available
+ * on JME, but can be used in Android applications.
+ *
+ * This has been implemented to match as closely as possible the behavior in the corresponding library in C.
+ *
+ * @see LibFunction
+ * @see JsePlatform
+ * @see org.luaj.vm2.libs.jme.JmePlatform
+ * @see LuaC
+ * @see CoerceJavaToLua
+ * @see CoerceLuaToJava
+ * @see http://www.keplerproject.org/luajava/manual.html#luareference
+ */
+public class LuajavaLib extends VarArgFunction {
+
+ static final int INIT = 0;
+ static final int BINDCLASS = 1;
+ static final int NEWINSTANCE = 2;
+ static final int NEW = 3;
+ static final int CREATEPROXY = 4;
+ static final int LOADLIB = 5;
+
+ static final String[] NAMES = {
+ "bindClass",
+ "newInstance",
+ "new",
+ "createProxy",
+ "loadLib",
+ };
+
+ static final int METHOD_MODIFIERS_VARARGS = 0x80;
+
+ public LuajavaLib() {
+ }
+
+ public Varargs invoke(Varargs args) {
+ try {
+ switch ( opcode ) {
+ case INIT: {
+ // LuaValue modname = args.arg1();
+ LuaValue env = args.arg(2);
+ LuaTable t = new LuaTable();
+ bind( t, this.getClass(), NAMES, BINDCLASS );
+ env.set("luajava", t);
+ if (!env.get("package").isnil()) env.get("package").get("loaded").set("luajava", t);
+ return t;
+ }
+ case BINDCLASS: {
+ final Class clazz = classForName(args.checkjstring(1));
+ return JavaClass.forClass(clazz);
+ }
+ case NEWINSTANCE:
+ case NEW: {
+ // get constructor
+ final LuaValue c = args.checkvalue(1);
+ final Class clazz = (opcode==NEWINSTANCE? classForName(c.tojstring()): (Class) c.checkuserdata(Class.class));
+ final Varargs consargs = args.subargs(2);
+ return JavaClass.forClass(clazz).getConstructor().invoke(consargs);
+ }
+
+ case CREATEPROXY: {
+ final int niface = args.narg()-1;
+ if ( niface <= 0 )
+ throw new LuaError("no interfaces");
+ final LuaValue lobj = args.checktable(niface+1);
+
+ // get the interfaces
+ final Class[] ifaces = new Class[niface];
+ for ( int i=0; i {@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
+ * }
+ * In this example the platform-specific {@link org.luaj.vm2.libs.jse.JseIoLib} library will be loaded, which will include
+ * the base functionality provided by this class, whereas the {@link org.luaj.vm2.libs.jse.JsePlatform} would load the
+ * {@link org.luaj.vm2.libs.jse.JseIoLib}.
+ * {@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.load(new PackageLib());
+ * globals.load(new OsLib());
+ * globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
+ * }
+ *
+ *
+ * {@code
+ * import org.luaj.vm2.LuaValue;
+ * import org.luaj.vm2.lib.*;
+ *
+ * public class hyperbolic extends TwoArgFunction {
+ *
+ * public hyperbolic() {}
+ *
+ * public LuaValue call(LuaValue modname, LuaValue env) {
+ * LuaValue library = tableOf();
+ * library.set( "sinh", new sinh() );
+ * library.set( "cosh", new cosh() );
+ * env.set( "hyperbolic", library );
+ * return library;
+ * }
+ *
+ * static class sinh extends OneArgFunction {
+ * public LuaValue call(LuaValue x) {
+ * return LuaValue.valueOf(Math.sinh(x.checkdouble()));
+ * }
+ * }
+ *
+ * static class cosh extends OneArgFunction {
+ * public LuaValue call(LuaValue x) {
+ * return LuaValue.valueOf(Math.cosh(x.checkdouble()));
+ * }
+ * }
+ *}
+ *}
+ * The default constructor is used to instantiate the library
+ * in response to {@code require 'hyperbolic'} statement,
+ * provided it is on Java"s class path.
+ * This instance is then invoked with 2 arguments: the name supplied to require(),
+ * and the environment for this function. The library may ignore these, or use
+ * them to leave side effects in the global environment, for example.
+ * In the previous example, two functions are created, 'sinh', and 'cosh', and placed
+ * into a global table called 'hyperbolic' using the supplied 'env' argument.
+ * {@code
+ * local t = require('hyperbolic')
+ * print( 't', t )
+ * print( 'hyperbolic', hyperbolic )
+ * for k,v in pairs(t) do
+ * print( 'k,v', k,v )
+ * end
+ * print( 'sinh(.5)', hyperbolic.sinh(.5) )
+ * print( 'cosh(.5)', hyperbolic.cosh(.5) )
+ * }
+ * {@code
+ * t table: 3dbbd23f
+ * hyperbolic table: 3dbbd23f
+ * k,v cosh function: 3dbbd128
+ * k,v sinh function: 3dbbd242
+ * sinh(.5) 0.5210953
+ * cosh(.5) 1.127626
+ * }
+ *
+ *
+ * {@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) );
+ * }
+ * When using {@link org.luaj.vm2.libs.jse.JsePlatform} as in this example,
+ * the subclass {@link org.luaj.vm2.libs.jse.JseMathLib} will
+ * be included, which also includes this base functionality.
+ * {@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.load(new PackageLib());
+ * globals.load(new MathLib());
+ * System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) );
+ * }
+ * Doing so will ensure the library is properly initialized
+ * and loaded into the globals table.
+ *
+ *
+ * {@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * System.out.println( globals.get("os").get("time").call() );
+ * }
+ * In this example the platform-specific {@link org.luaj.vm2.libs.jse.JseOsLib} library will be loaded, which will include
+ * the base functionality provided by this class.
+ * {@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.load(new PackageLib());
+ * globals.load(new OsLib());
+ * System.out.println( globals.get("os").get("time").call() );
+ * }
+ * Lua Environment Variables
+ * The following variables are available to lua scrips when this library has been loaded:
+ *
+ *
+ *
+ * "package.loaded" Lua table of loaded modules.
+ * "package.path" Search path for lua scripts.
+ * "package.preload" Lua table of uninitialized preload functions.
+ * "package.searchers" Lua table of functions that search for object to load.
+ * Java Environment Variables
+ * These Java environment variables affect the library behavior:
+ *
+ *
+ *
+ * "luaj.package.path" Initial value for "package.path". Default value is "?.lua"
+ * Loading
+ * Typically, this library is included as part of a call to either
+ * {@link org.luaj.vm2.libs.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.libs.jme.JmePlatform#standardGlobals()}
+ * {@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * System.out.println( globals.get("require").call"foo") );
+ * }
+ * {@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.load(new PackageLib());
+ * System.out.println( globals.get("require").call("foo") );
+ * }
+ * Limitations
+ * This library has been implemented to match as closely as possible the behavior in the corresponding library in C.
+ * However, the default filesystem search semantics are different and delegated to the bas library
+ * as outlined in the {@link BaseLib} and {@link org.luaj.vm2.libs.jse.JseBaseLib} documentation.
+ * "luaj.package.path", and is "?.lua" by default. */
+ public static final String DEFAULT_LUA_PATH;
+ static {
+ String path = null;
+ try {
+ path = System.getProperty("luaj.package.path");
+ } catch (Exception e) {
+ System.out.println(e.toString());
+ }
+ if (path == null) {
+ path = "?.lua";
+ }
+ DEFAULT_LUA_PATH = path;
+ }
+
+ static final LuaString _LOADED = valueOf("loaded");
+ private static final LuaString _LOADLIB = valueOf("loadlib");
+ static final LuaString _PRELOAD = valueOf("preload");
+ static final LuaString _PATH = valueOf("path");
+ static final LuaString _SEARCHPATH = valueOf("searchpath");
+ static final LuaString _SEARCHERS = valueOf("searchers");
+
+ /** The globals that were used to load this library. */
+ Globals globals;
+
+ /** The table for this package. */
+ LuaTable package_;
+
+ /** Loader that loads from {@code preload} table if found there */
+ public preload_searcher preload_searcher;
+
+ /** Loader that loads as a lua script using the lua path currently in {@link path} */
+ public lua_searcher lua_searcher;
+
+ /** Loader that loads as a Java class. Class must have public constructor and be a LuaValue. */
+ public java_searcher java_searcher;
+
+ private static final LuaString _SENTINEL = valueOf("\u0001");
+
+ private static final String FILE_SEP = System.getProperty("file.separator");
+
+ public PackageLib() {}
+
+ /** Perform one-time initialization on the library by adding package functions
+ * to the supplied environment, and returning it as the return value.
+ * It also creates the package.preload and package.loaded tables for use by
+ * other libraries.
+ * @param modname the module name supplied if this is loaded via 'require'.
+ * @param env the environment to load into, typically a Globals instance.
+ */
+ public LuaValue call(LuaValue modname, LuaValue env) {
+ globals = env.checkglobals();
+ globals.set("require", new require());
+ package_ = new LuaTable();
+ package_.set(_LOADED, new LuaTable());
+ package_.set(_PRELOAD, new LuaTable());
+ package_.set(_PATH, LuaValue.valueOf(DEFAULT_LUA_PATH));
+ package_.set(_LOADLIB, new loadlib());
+ package_.set(_SEARCHPATH, new searchpath());
+ LuaTable searchers = new LuaTable();
+ searchers.set(1, preload_searcher = new preload_searcher());
+ searchers.set(2, lua_searcher = new lua_searcher());
+ searchers.set(3, java_searcher = new java_searcher());
+ package_.set(_SEARCHERS, searchers);
+ package_.set("config", FILE_SEP + "\n;\n?\n!\n-\n");
+ package_.get(_LOADED).set("package", package_);
+ env.set("package", package_);
+ globals.package_ = this;
+ return env;
+ }
+
+ /** Allow packages to mark themselves as loaded */
+ public void setIsLoaded(String name, LuaTable value) {
+ package_.get(_LOADED).set(name, value);
+ }
+
+
+ /** Set the lua path used by this library instance to a new value.
+ * Merely sets the value of {@link path} to be used in subsequent searches. */
+ public void setLuaPath( String newLuaPath ) {
+ package_.set(_PATH, LuaValue.valueOf(newLuaPath));
+ }
+
+ public String tojstring() {
+ return "package";
+ }
+
+ // ======================== Package loading =============================
+
+ /**
+ * require (modname)
+ *
+ * Loads the given module. The function starts by looking into the package.loaded table
+ * to determine whether modname is already loaded. If it is, then require returns the value
+ * stored at package.loaded[modname]. Otherwise, it tries to find a loader for the module.
+ *
+ * To find a loader, require is guided by the package.searchers sequence.
+ * By changing this sequence, we can change how require looks for a module.
+ * The following explanation is based on the default configuration for package.searchers.
+ *
+ * First require queries package.preload[modname]. If it has a value, this value
+ * (which should be a function) is the loader. Otherwise require searches for a Lua loader using
+ * the path stored in package.path. If that also fails, it searches for a Java loader using
+ * the classpath, using the public default constructor, and casting the instance to LuaFunction.
+ *
+ * Once a loader is found, require calls the loader with two arguments: modname and an extra value
+ * dependent on how it got the loader. If the loader came from a file, this extra value is the file name.
+ * If the loader is a Java instance of LuaFunction, this extra value is the environment.
+ * If the loader returns any non-nil value, require assigns the returned value to package.loaded[modname].
+ * If the loader does not return a non-nil value and has not assigned any value to package.loaded[modname],
+ * then require assigns true to this entry.
+ * In any case, require returns the final value of package.loaded[modname].
+ *
+ * If there is any error loading or running the module, or if it cannot find any loader for the module,
+ * then require raises an error.
+ */
+ public class require extends OneArgFunction {
+ public LuaValue call( LuaValue arg ) {
+ LuaString name = arg.checkstring();
+ LuaValue loaded = package_.get(_LOADED);
+ LuaValue result = loaded.get(name);
+ if ( result.toboolean() ) {
+ if ( result == _SENTINEL )
+ error("loop or previous error loading module '"+name+"'");
+ return result;
+ }
+
+ /* else must load it; iterate over available loaders */
+ LuaTable tbl = package_.get(_SEARCHERS).checktable();
+ StringBuffer sb = new StringBuffer();
+ Varargs loader = null;
+ for ( int i=1; true; i++ ) {
+ LuaValue searcher = tbl.get(i);
+ if ( searcher.isnil() ) {
+ error( "module '"+name+"' not found: "+name+sb );
+ }
+
+ /* call loader with module name as argument */
+ loader = searcher.invoke(name);
+ if ( loader.isfunction(1) )
+ break;
+ if ( loader.isstring(1) )
+ sb.append( loader.tojstring(1) );
+ }
+
+ // load the module using the loader
+ loaded.set(name, _SENTINEL);
+ result = loader.arg1().call(name, loader.arg(2));
+ if ( ! result.isnil() )
+ loaded.set( name, result );
+ else if ( (result = loaded.get(name)) == _SENTINEL )
+ loaded.set( name, result = LuaValue.TRUE );
+ return result;
+ }
+ }
+
+ public static class loadlib extends VarArgFunction {
+ public Varargs invoke( Varargs args ) {
+ args.checkstring(1);
+ return varargsOf(NIL, valueOf("dynamic libraries not enabled"), valueOf("absent"));
+ }
+ }
+
+ public class preload_searcher extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ LuaString name = args.checkstring(1);
+ LuaValue val = package_.get(_PRELOAD).get(name);
+ return val.isnil()?
+ valueOf("\n\tno field package.preload['"+name+"']"):
+ val;
+ }
+ }
+
+ public class lua_searcher extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ LuaString name = args.checkstring(1);
+
+ // get package path
+ LuaValue path = package_.get(_PATH);
+ if ( ! path.isstring() )
+ return valueOf("package.path is not a string");
+
+ // get the searchpath function.
+ Varargs v = package_.get(_SEARCHPATH).invoke(varargsOf(name, path));
+
+ // Did we get a result?
+ if (!v.isstring(1))
+ return v.arg(2).tostring();
+ LuaString filename = v.arg1().strvalue();
+
+ // Try to load the file.
+ v = globals.loadfile(filename.tojstring());
+ if ( v.arg1().isfunction() )
+ return LuaValue.varargsOf(v.arg1(), filename);
+
+ // report error
+ return varargsOf(NIL, valueOf("'"+filename+"': "+v.arg(2).tojstring()));
+ }
+ }
+
+ public class searchpath extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ String name = args.checkjstring(1);
+ String path = args.checkjstring(2);
+ String sep = args.optjstring(3, ".");
+ String rep = args.optjstring(4, FILE_SEP);
+
+ // check the path elements
+ int e = -1;
+ int n = path.length();
+ StringBuffer sb = null;
+ name = name.replace(sep.charAt(0), rep.charAt(0));
+ while ( e < n ) {
+
+ // find next template
+ int b = e+1;
+ e = path.indexOf(';',b);
+ if ( e < 0 )
+ e = path.length();
+ String template = path.substring(b,e);
+
+ // create filename
+ int q = template.indexOf('?');
+ String filename = template;
+ if ( q >= 0 ) {
+ filename = template.substring(0,q) + name + template.substring(q+1);
+ }
+
+ // try opening the file
+ InputStream is = globals.finder.findResource(filename);
+ if (is != null) {
+ try { is.close(); } catch ( java.io.IOException ioe ) {}
+ return valueOf(filename);
+ }
+
+ // report error
+ if ( sb == null )
+ sb = new StringBuffer();
+ sb.append( "\n\t"+filename );
+ }
+ return varargsOf(NIL, valueOf(sb.toString()));
+ }
+ }
+
+ public class java_searcher extends VarArgFunction {
+ public Varargs invoke(Varargs args) {
+ String name = args.checkjstring(1);
+ String classname = toClassname( name );
+ Class c = null;
+ LuaValue v = null;
+ try {
+ c = Class.forName(classname);
+ v = (LuaValue) c.newInstance();
+ if (v.isfunction())
+ ((LuaFunction)v).initupvalue1(globals);
+ return varargsOf(v, globals);
+ } catch ( ClassNotFoundException cnfe ) {
+ return valueOf("\n\tno class '"+classname+"'" );
+ } catch ( Exception e ) {
+ return valueOf("\n\tjava load failed on '"+classname+"', "+e );
+ }
+ }
+ }
+
+ /** Convert lua filename to valid class name */
+ public static final String toClassname( String filename ) {
+ int n=filename.length();
+ int j=n;
+ if ( filename.endsWith(".lua") )
+ j -= 4;
+ for ( int k=0; k {@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * System.out.println( globals.get("string").get("upper").call( LuaValue.valueOf("abcde") ) );
+ * }
+ * {@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.load(new PackageLib());
+ * globals.load(new JseStringLib());
+ * System.out.println( globals.get("string").get("upper").call( LuaValue.valueOf("abcde") ) );
+ * }
+ * {@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * System.out.println( globals.get("table").get("length").call( LuaValue.tableOf() ) );
+ * }
+ * {@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.load(new PackageLib());
+ * globals.load(new TableLib());
+ * System.out.println( globals.get("table").get("length").call( LuaValue.tableOf() ) );
+ * }
+ * {@code
+ * Globals globals = JmePlatform.standardGlobals();
+ * globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
+ * }
+ * {@code
+ * Globals globals = new Globals();
+ * globals.load(new JmeBaseLib());
+ * globals.load(new PackageLib());
+ * globals.load(new JmeIoLib());
+ * globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
+ * }
+ *
+ *
+ * {@code
+ * Globals global = JmePlatform.standardGlobals();
+ * global.get("print").call(LuaValue.valueOf("hello, world"));
+ * }
+ * {@code
+ * LoadState.load( getClass().getResourceAsStream("main.lua"), "main.lua", globals ).call();
+ * }
+ * {@code
+ * globals.get("require").call(LuaValue.valueOf("main"));
+ * }
+ * For this to succeed, the file "main.lua" must be a resource in the class path.
+ * See {@link BaseLib} for details on finding scripts using {@link ResourceFinder}.
+ *
+ *
+ * In addition, the {@link LuaC} compiler is installed so lua files may be loaded in their source form.
+ *
+ *
+ *
+ *
+ * The method {@link CoerceJavaToLua#coerce(Object)} looks as the type and dimesioning
+ * of the argument and tries to guess the best fit for corrsponding lua scalar,
+ * table, or table of tables.
+ *
+ * @see CoerceJavaToLua#coerce(Object)
+ * @see LuajavaLib
+ */
+public class CoerceJavaToLua {
+
+ static interface Coercion {
+ public LuaValue coerce( Object javaValue );
+ };
+
+ private static final class BoolCoercion implements Coercion {
+ public LuaValue coerce( Object javaValue ) {
+ Boolean b = (Boolean) javaValue;
+ return b.booleanValue()? LuaValue.TRUE: LuaValue.FALSE;
+ }
+ }
+
+ private static final class IntCoercion implements Coercion {
+ public LuaValue coerce( Object javaValue ) {
+ Number n = (Number) javaValue;
+ return LuaInteger.valueOf( n.intValue() );
+ }
+ }
+
+ private static final class CharCoercion implements Coercion {
+ public LuaValue coerce( Object javaValue ) {
+ Character c = (Character) javaValue;
+ return LuaInteger.valueOf( c.charValue() );
+ }
+ }
+
+ private static final class DoubleCoercion implements Coercion {
+ public LuaValue coerce( Object javaValue ) {
+ Number n = (Number) javaValue;
+ return LuaDouble.valueOf( n.doubleValue() );
+ }
+ }
+
+ private static final class StringCoercion implements Coercion {
+ public LuaValue coerce( Object javaValue ) {
+ return LuaString.valueOf( javaValue.toString() );
+ }
+ }
+
+ private static final class BytesCoercion implements Coercion {
+ public LuaValue coerce( Object javaValue ) {
+ return LuaValue.valueOf((byte[]) javaValue);
+ }
+ }
+
+ private static final class ClassCoercion implements Coercion {
+ public LuaValue coerce( Object javaValue ) {
+ return JavaClass.forClass((Class) javaValue);
+ }
+ }
+
+ private static final class InstanceCoercion implements Coercion {
+ public LuaValue coerce(Object javaValue) {
+ return new JavaInstance(javaValue);
+ }
+ }
+
+ private static final class ArrayCoercion implements Coercion {
+ public LuaValue coerce(Object javaValue) {
+ // should be userdata?
+ return new JavaArray(javaValue);
+ }
+ }
+
+ private static final class LuaCoercion implements Coercion {
+ public LuaValue coerce( Object javaValue ) {
+ return (LuaValue) javaValue;
+ }
+ }
+
+
+ static final Map COERCIONS = Collections.synchronizedMap(new HashMap());
+
+ static {
+ Coercion boolCoercion = new BoolCoercion() ;
+ Coercion intCoercion = new IntCoercion() ;
+ Coercion charCoercion = new CharCoercion() ;
+ Coercion doubleCoercion = new DoubleCoercion() ;
+ Coercion stringCoercion = new StringCoercion() ;
+ Coercion bytesCoercion = new BytesCoercion() ;
+ Coercion classCoercion = new ClassCoercion() ;
+ COERCIONS.put( Boolean.class, boolCoercion );
+ COERCIONS.put( Byte.class, intCoercion );
+ COERCIONS.put( Character.class, charCoercion );
+ COERCIONS.put( Short.class, intCoercion );
+ COERCIONS.put( Integer.class, intCoercion );
+ COERCIONS.put( Long.class, doubleCoercion );
+ COERCIONS.put( Float.class, doubleCoercion );
+ COERCIONS.put( Double.class, doubleCoercion );
+ COERCIONS.put( String.class, stringCoercion );
+ COERCIONS.put( byte[].class, bytesCoercion );
+ COERCIONS.put( Class.class, classCoercion );
+ }
+
+ /**
+ * Coerse a Java object to a corresponding lua value.
+ *
+ *
+ * {@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * globals.get("print").call(LuaValue.valueOf("hello, world"));
+ * }
+ * {@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.get("print").call(LuaValue.valueOf("hello, world"));
+ * }
+ * {@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
+ * }
+ * {@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.load(new PackageLib());
+ * globals.load(new JseIoLib());
+ * globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
+ * }
+ * {@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) );
+ * }
+ * {@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.load(new PackageLib());
+ * globals.load(new JseMathLib());
+ * System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) );
+ * }
+ *
+ *
+ * {@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * System.out.println( globals.get("os").get("time").call() );
+ * }
+ * {@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.load(new PackageLib());
+ * globals.load(new JseOsLib());
+ * System.out.println( globals.get("os").get("time").call() );
+ * }
+ * {@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * globals.get("print").call(LuaValue.valueOf("hello, world"));
+ * }
+ * {@code
+ * globals.load( new FileInputStream("main.lua"), "main.lua" ).call();
+ * }
+ * {@code
+ * globals.get("require").call(LuaValue.valueOf("main"));
+ * }
+ * For this to succeed, the file "main.lua" must be in the current directory or a resource.
+ * See {@link JseBaseLib} for details on finding scripts using {@link ResourceFinder}.
+ *
+ *
+ * In addition, the {@link LuaC} compiler is installed so lua files may be loaded in their source form.
+ * {@code
+ * Globals globals = JsePlatform.standardGlobals();
+ * System.out.println( globals.get("luajava").get("bindClass").call( LuaValue.valueOf("java.lang.System") ).invokeMethod("currentTimeMillis") );
+ * }
+ * {@code
+ * Globals globals = new Globals();
+ * globals.load(new JseBaseLib());
+ * globals.load(new PackageLib());
+ * globals.load(new LuajavaLib());
+ * globals.load(
+ * "sys = luajava.bindClass('java.lang.System')\n"+
+ * "print ( sys:currentTimeMillis() )\n", "main.lua" ).call();
+ * }
+ *