Added missed Classes

This commit is contained in:
UnlegitDqrk
2026-03-01 12:57:18 +01:00
parent 405fd633fd
commit ba3d1d8ef9
43 changed files with 9482 additions and 0 deletions

View File

@@ -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.
* <p>
* 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.
* <p>
* To use basic library functions that include a {@link ResourceFinder} based on
* directory lookup, use {@link org.luaj.vm2.libs.jse.JseBaseLib} instead.
* <p>
* 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()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* } </pre>
* <p>
* 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:
* <pre> {@code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* } </pre>
* Doing so will ensure the library is properly initialized
* and loaded into the globals table.
* <p>
* 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 <a href="http://www.lua.org/manual/5.2/manual.html#6.1">Lua 5.2 Base Lib Reference</a>
*/
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++];
}
}
}

View File

@@ -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.
* <p>
* 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()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("bit32").get("bnot").call( LuaValue.valueOf(2) ) );
* } </pre>
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@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) ) );
* } </pre>
* <p>
* 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 <a href="http://www.lua.org/manual/5.2/manual.html#6.7">Lua 5.2 Bitwise Operation Lib Reference</a>
*/
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);
}
}

View File

@@ -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.
* <p>
* 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.
* <p>
* 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()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("coroutine").get("running").call() );
* } </pre>
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@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() );
* } </pre>
* <p>
* @see LibFunction
* @see org.luaj.vm2.libs.jse.JsePlatform
* @see org.luaj.vm2.libs.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.2">Lua 5.2 Coroutine Lib Reference</a>
*/
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() );
}
}
}
}

View File

@@ -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.
* <p>
* 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.
* <p>
* 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()}
* <pre> {@code
* Globals globals = JsePlatform.debugGlobals();
* System.out.println( globals.get("debug").get("traceback").call() );
* } </pre>
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@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() );
* } </pre>
* <p>
* 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 <a href="http://www.lua.org/manual/5.2/manual.html#6.10">Lua 5.2 Debug Lib Reference</a>
*/
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<str.length(); i++ )
switch ( str.charAt(i) ) {
case 'c': call=true; break;
case 'l': line=true; break;
case 'r': rtrn=true; break;
}
LuaThread.State s = t.state;
s.hookfunc = func;
s.hookcall = call;
s.hookline = line;
s.hookcount = count;
s.hookrtrn = rtrn;
return NONE;
}
}
// debug.setlocal ([thread,] level, local, value)
final class setlocal 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++);
LuaValue value = args.arg(a++);
CallFrame f = callstack(thread).getCallFrame(level);
return f != null? f.setLocal(local, value): NONE;
}
}
// debug.setmetatable (value, table)
static final class setmetatable extends TwoArgFunction {
public LuaValue call(LuaValue value, LuaValue table) {
LuaValue mt = table.opttable(null);
switch ( value.type() ) {
case TNIL: LuaNil.s_metatable = mt; break;
case TNUMBER: LuaNumber.s_metatable = mt; break;
case TBOOLEAN: LuaBoolean.s_metatable = mt; break;
case TSTRING: LuaString.s_metatable = mt; break;
case TFUNCTION: LuaFunction.s_metatable = mt; break;
case TTHREAD: LuaThread.s_metatable = mt; break;
default: value.setmetatable( mt );
}
return value;
}
}
// debug.setupvalue (f, up, value)
static final class setupvalue extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue func = args.checkfunction(1);
int up = args.checkint(2);
LuaValue value = args.arg(3);
if ( func instanceof LuaClosure ) {
LuaClosure c = (LuaClosure) func;
LuaString name = findupvalue(c, up);
if ( name != null ) {
c.upValues[up-1].setValue(value);
return name;
}
}
return NIL;
}
}
// debug.setuservalue (udata, value)
static final class setuservalue extends VarArgFunction {
public Varargs invoke(Varargs args) {
Object o = args.checkuserdata(1);
LuaValue v = args.checkvalue(2);
LuaUserdata u = (LuaUserdata) args.arg1();
u.m_instance = v.checkuserdata();
u.m_metatable = v.getmetatable();
return NONE;
}
}
// debug.traceback ([thread,] [message [, level]])
final class traceback extends VarArgFunction {
public Varargs invoke(Varargs args) {
int a=1;
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
String message = args.optjstring(a++, null);
int level = args.optint(a++,1);
String tb = callstack(thread).traceback(level);
return valueOf(message!=null? message+"\n"+tb: tb);
}
}
// debug.upvalueid (f, n)
static final class upvalueid 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;
if ( c.upValues != null && up > 0 && up <= c.upValues.length ) {
return valueOf(c.upValues[up-1].hashCode());
}
}
return NIL;
}
}
// debug.upvaluejoin (f1, n1, f2, n2)
static final class upvaluejoin extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaClosure f1 = args.checkclosure(1);
int n1 = args.checkint(2);
LuaClosure f2 = args.checkclosure(3);
int n2 = args.checkint(4);
if (n1 < 1 || n1 > f1.upValues.length)
argerror("index out of range");
if (n2 < 1 || n2 > f2.upValues.length)
argerror("index out of range");
f1.upValues[n1-1] = f2.upValues[n2-1];
return NONE;
}
}
public void onCall(LuaFunction f) {
LuaThread.State s = globals.running.state;
if (s.inhook) return;
callstack().onCall(f);
if (s.hookcall) callHook(s, CALL, NIL);
}
public void onCall(LuaClosure c, Varargs varargs, LuaValue[] stack) {
LuaThread.State s = globals.running.state;
if (s.inhook) return;
callstack().onCall(c, varargs, stack);
if (s.hookcall) callHook(s, CALL, NIL);
}
public void onInstruction(int pc, Varargs v, int top) {
LuaThread.State s = globals.running.state;
if (s.inhook) return;
callstack().onInstruction(pc, v, top);
if (s.hookfunc == null) return;
if (s.hookcount > 0)
if (++s.bytecodes % s.hookcount == 0)
callHook(s, COUNT, NIL);
if (s.hookline) {
int newline = callstack().currentline();
if ( newline != s.lastline ) {
s.lastline = newline;
callHook(s, LINE, LuaValue.valueOf(newline));
}
}
}
public void onReturn() {
LuaThread.State s = globals.running.state;
if (s.inhook) return;
callstack().onReturn();
if (s.hookrtrn) callHook(s, RETURN, NIL);
}
public String traceback(int level) {
return callstack().traceback(level);
}
public CallFrame getCallFrame(int level) {
return callstack().getCallFrame(level);
}
void callHook(LuaThread.State s, LuaValue type, LuaValue arg) {
if (s.inhook || s.hookfunc == null) return;
s.inhook = true;
try {
s.hookfunc.call(type, arg);
} catch (LuaError e) {
throw e;
} catch (RuntimeException e) {
throw new LuaError(e);
} finally {
s.inhook = false;
}
}
CallStack callstack() {
return callstack(globals.running);
}
CallStack callstack(LuaThread t) {
if (t.callstack == null)
t.callstack = new CallStack();
return (CallStack) t.callstack;
}
static class DebugInfo {
String name; /* (n) */
String namewhat; /* (n) 'global', 'local', 'field', 'method' */
String what; /* (S) 'Lua', 'C', 'main', 'tail' */
String source; /* (S) */
int currentline; /* (l) */
int linedefined; /* (S) */
int lastlinedefined; /* (S) */
short nups; /* (u) number of upvalues */
short nparams;/* (u) number of parameters */
boolean isvararg; /* (u) */
boolean istailcall; /* (t) */
String short_src; /* (S) */
CallFrame cf; /* active function */
public void funcinfo(LuaFunction f) {
if (f.isclosure()) {
Prototype p = f.checkclosure().p;
this.source = p.source != null ? p.source.tojstring() : "=?";
this.linedefined = p.linedefined;
this.lastlinedefined = p.lastlinedefined;
this.what = (this.linedefined == 0) ? "main" : "Lua";
this.short_src = p.shortsource();
} else {
this.source = "=[Java]";
this.linedefined = -1;
this.lastlinedefined = -1;
this.what = "Java";
this.short_src = f.name();
}
}
}
public static class CallStack {
final static CallFrame[] EMPTY = {};
CallFrame[] frame = EMPTY;
int calls = 0;
Lock lock = new ReentrantLock();
CallStack() {}
int currentline() {
lock.lock();
try {
return calls > 0? frame[calls-1].currentline(): -1;
} finally {
lock.unlock();
}
}
private CallFrame pushcall() {
lock.lock();
try {
if (calls >= frame.length) {
int n = Math.max(4, frame.length * 3 / 2);
CallFrame[] f = new CallFrame[n];
System.arraycopy(frame, 0, f, 0, frame.length);
for (int i = frame.length; i < n; ++i)
f[i] = new CallFrame();
frame = f;
for (int i = 1; i < n; ++i)
f[i].previous = f[i-1];
}
return frame[calls++];
} finally {
lock.unlock();
}
}
final void onCall(LuaFunction function) {
lock.lock();
try {
pushcall().set(function);
} finally {
lock.unlock();
}
}
final void onCall(LuaClosure function, Varargs varargs, LuaValue[] stack) {
lock.lock();
try {
pushcall().set(function, varargs, stack);
} finally {
lock.unlock();
}
}
final void onReturn() {
lock.lock();
try {
if (calls > 0)
frame[--calls].reset();
} finally {
lock.unlock();
}
}
final void onInstruction(int pc, Varargs v, int top) {
lock.lock();
try {
if (calls > 0)
frame[calls-1].instr(pc, v, top);
} finally {
lock.unlock();
}
}
/**
* Get the traceback starting at a specific level.
* @param level
* @return String containing the traceback.
*/
String traceback(int level) {
lock.lock();
try {
StringBuffer sb = new StringBuffer();
sb.append( "stack traceback:" );
for (DebugLib.CallFrame c; (c = getCallFrame(level++)) != null; ) {
sb.append("\n\t");
sb.append( c.shortsource() );
sb.append( ':' );
if (c.currentline() > 0)
sb.append( c.currentline()+":" );
sb.append( " in " );
DebugInfo ar = auxgetinfo("n", c.f, c);
if (c.linedefined() == 0)
sb.append("main chunk");
else if ( ar.name != null ) {
sb.append( "function '" );
sb.append( ar.name );
sb.append( '\'' );
} else {
sb.append( "function <" );
sb.append( c.shortsource() );
sb.append( ':' );
sb.append( c.linedefined() );
sb.append( '>' );
}
}
sb.append("\n\t[Java]: in ?");
return sb.toString();
} finally {
lock.unlock();
}
}
DebugLib.CallFrame getCallFrame(int level) {
lock.lock();
try {
if (level < 1 || level > calls)
return null;
return frame[calls-level];
} finally {
lock.unlock();
}
}
DebugLib.CallFrame findCallFrame(LuaValue func) {
lock.lock();
try {
for (int i = 1; i <= calls; ++i)
if (frame[calls-i].f == func)
return frame[i];
return null;
} finally {
lock.unlock();
}
}
DebugInfo auxgetinfo(String what, LuaFunction f, CallFrame ci) {
lock.lock();
try {
DebugInfo ar = new DebugInfo();
for (int i = 0, n = what.length(); i < n; ++i) {
switch (what.charAt(i)) {
case 'S':
ar.funcinfo(f);
break;
case 'l':
ar.currentline = ci != null && ci.f.isclosure()? ci.currentline(): -1;
break;
case 'u':
if (f != null && f.isclosure()) {
Prototype p = f.checkclosure().p;
ar.nups = (short) p.upvalues.length;
ar.nparams = (short) p.numparams;
ar.isvararg = p.is_vararg != 0;
} else {
ar.nups = 0;
ar.isvararg = true;
ar.nparams = 0;
}
break;
case 't':
ar.istailcall = false;
break;
case 'n': {
/* calling function is a known Lua function? */
if (ci != null && ci.previous != null) {
if (ci.previous.f.isclosure()) {
NameWhat nw = getfuncname(ci.previous);
if (nw != null) {
ar.name = nw.name;
ar.namewhat = nw.namewhat;
}
}
}
if (ar.namewhat == null) {
ar.namewhat = ""; /* not found */
ar.name = null;
}
break;
}
case 'L':
case 'f':
break;
default:
// TODO: return bad status.
break;
}
}
return ar;
} finally {
lock.unlock();
}
}
}
public static class CallFrame {
LuaFunction f;
int pc;
int top;
Varargs v;
LuaValue[] stack;
CallFrame previous;
void set(LuaClosure function, Varargs varargs, LuaValue[] stack) {
this.f = function;
this.v = varargs;
this.stack = stack;
}
public String shortsource() {
return f.isclosure()? f.checkclosure().p.shortsource(): "[Java]";
}
void set(LuaFunction function) {
this.f = function;
}
void reset() {
this.f = null;
this.v = null;
this.stack = null;
}
void instr(int pc, Varargs v, int top) {
this.pc = pc;
this.v = v;
this.top = top;
if (TRACE)
Print.printState(f.checkclosure(), pc, stack, top, v);
}
Varargs getLocal(int i) {
LuaString name = getlocalname(i);
if ( i >= 1 && i <= stack.length && stack[i-1] != null )
return varargsOf( name == null ? NIL : name, stack[i-1] );
else
return NIL;
}
Varargs setLocal(int i, LuaValue value) {
LuaString name = getlocalname(i);
if ( i >= 1 && i <= stack.length && stack[i-1] != null ) {
stack[i-1] = value;
return name == null ? NIL : name;
} else {
return NIL;
}
}
public int currentline() {
if ( !f.isclosure() ) return -1;
int[] li = f.checkclosure().p.lineinfo;
return li==null || pc<0 || pc>=li.length? -1: li[pc];
}
String sourceline() {
if ( !f.isclosure() ) return f.tojstring();
return f.checkclosure().p.shortsource() + ":" + currentline();
}
int linedefined() {
return f.isclosure()? f.checkclosure().p.linedefined: -1;
}
LuaString getlocalname(int index) {
if ( !f.isclosure() ) return null;
return f.checkclosure().p.getlocalname(index, pc);
}
}
static LuaString findupvalue(LuaClosure c, int up) {
if ( c.upValues != null && up > 0 && up <= c.upValues.length ) {
if ( c.p.upvalues != null && up <= c.p.upvalues.length )
return c.p.upvalues[up-1].name;
else
return LuaString.valueOf( "."+up );
}
return null;
}
static void lua_assert(boolean x) {
if (!x) throw new RuntimeException("lua_assert failed");
}
static class NameWhat {
final String name;
final String namewhat;
NameWhat(String name, String namewhat) {
this.name = name;
this.namewhat = namewhat;
}
}
// Return the name info if found, or null if no useful information could be found.
static NameWhat getfuncname(DebugLib.CallFrame frame) {
if (!frame.f.isclosure())
return new NameWhat(frame.f.classnamestub(), "Java");
Prototype p = frame.f.checkclosure().p;
int pc = frame.pc;
int i = p.code[pc]; /* calling instruction */
LuaString tm;
switch (Lua.GET_OPCODE(i)) {
case Lua.OP_CALL:
case Lua.OP_TAILCALL: /* get function name */
return getobjname(p, pc, Lua.GETARG_A(i));
case Lua.OP_TFORCALL: /* for iterator */
return new NameWhat("(for iterator)", "(for iterator");
/* all other instructions can call only through metamethods */
case Lua.OP_SELF:
case Lua.OP_GETTABUP:
case Lua.OP_GETTABLE: tm = LuaValue.INDEX; break;
case Lua.OP_SETTABUP:
case Lua.OP_SETTABLE: tm = LuaValue.NEWINDEX; break;
case Lua.OP_EQ: tm = LuaValue.EQ; break;
case Lua.OP_ADD: tm = LuaValue.ADD; break;
case Lua.OP_SUB: tm = LuaValue.SUB; break;
case Lua.OP_MUL: tm = LuaValue.MUL; break;
case Lua.OP_DIV: tm = LuaValue.DIV; break;
case Lua.OP_MOD: tm = LuaValue.MOD; break;
case Lua.OP_POW: tm = LuaValue.POW; break;
case Lua.OP_UNM: tm = LuaValue.UNM; break;
case Lua.OP_LEN: tm = LuaValue.LEN; break;
case Lua.OP_LT: tm = LuaValue.LT; break;
case Lua.OP_LE: tm = LuaValue.LE; break;
case Lua.OP_CONCAT: tm = LuaValue.CONCAT; break;
default:
return null; /* else no useful name can be found */
}
return new NameWhat( tm.tojstring(), "metamethod" );
}
// return NameWhat if found, null if not
public static NameWhat getobjname(Prototype p, int lastpc, int reg) {
int pc = lastpc; // currentpc(L, ci);
LuaString name = p.getlocalname(reg + 1, pc);
if (name != null) /* is a local? */
return new NameWhat( name.tojstring(), "local" );
/* else try symbolic execution */
pc = findsetreg(p, lastpc, reg);
if (pc != -1) { /* could find instruction? */
int i = p.code[pc];
switch (Lua.GET_OPCODE(i)) {
case Lua.OP_MOVE: {
int a = Lua.GETARG_A(i);
int b = Lua.GETARG_B(i); /* move from `b' to `a' */
if (b < a)
return getobjname(p, pc, b); /* get name for `b' */
break;
}
case Lua.OP_GETTABUP:
case Lua.OP_GETTABLE: {
int k = Lua.GETARG_C(i); /* key index */
int t = Lua.GETARG_B(i); /* table index */
LuaString vn = (Lua.GET_OPCODE(i) == Lua.OP_GETTABLE) /* name of indexed variable */
? p.getlocalname(t + 1, pc)
: (t < p.upvalues.length ? p.upvalues[t].name : QMARK);
String jname = kname(p, pc, k);
return new NameWhat( jname, vn != null && vn.eq_b(ENV)? "global": "field" );
}
case Lua.OP_GETUPVAL: {
int u = Lua.GETARG_B(i); /* upvalue index */
name = u < p.upvalues.length ? p.upvalues[u].name : QMARK;
return name == null ? null : new NameWhat( name.tojstring(), "upvalue" );
}
case Lua.OP_LOADK:
case Lua.OP_LOADKX: {
int b = (Lua.GET_OPCODE(i) == Lua.OP_LOADK) ? Lua.GETARG_Bx(i)
: Lua.GETARG_Ax(p.code[pc + 1]);
if (p.k[b].isstring()) {
name = p.k[b].strvalue();
return new NameWhat( name.tojstring(), "constant" );
}
break;
}
case Lua.OP_SELF: {
int k = Lua.GETARG_C(i); /* key index */
String jname = kname(p, pc, k);
return new NameWhat( jname, "method" );
}
default:
break;
}
}
return null; /* no useful name found */
}
static String kname(Prototype p, int pc, int c) {
if (Lua.ISK(c)) { /* is 'c' a constant? */
LuaValue k = p.k[Lua.INDEXK(c)];
if (k.isstring()) { /* literal constant? */
return k.tojstring(); /* it is its own name */
} /* else no reasonable name found */
} else { /* 'c' is a register */
NameWhat what = getobjname(p, pc, c); /* search for 'c' */
if (what != null && "constant".equals(what.namewhat)) { /* found a constant name? */
return what.name; /* 'name' already filled */
}
/* else no reasonable name found */
}
return "?"; /* no reasonable name found */
}
/*
** try to find last instruction before 'lastpc' that modified register 'reg'
*/
static int findsetreg (Prototype p, int lastpc, int reg) {
int pc;
int setreg = -1; /* keep last instruction that changed 'reg' */
for (pc = 0; pc < lastpc; pc++) {
int i = p.code[pc];
int op = Lua.GET_OPCODE(i);
int a = Lua.GETARG_A(i);
switch (op) {
case Lua.OP_LOADNIL: {
int b = Lua.GETARG_B(i);
if (a <= reg && reg <= a + b) /* set registers from 'a' to 'a+b' */
setreg = pc;
break;
}
case Lua.OP_TFORCALL: {
if (reg >= a + 2) setreg = pc; /* affect all regs above its base */
break;
}
case Lua.OP_CALL:
case Lua.OP_TAILCALL: {
if (reg >= a) setreg = pc; /* affect all registers above base */
break;
}
case Lua.OP_JMP: {
int b = Lua.GETARG_sBx(i);
int dest = pc + 1 + b;
/* jump is forward and do not skip `lastpc'? */
if (pc < dest && dest <= lastpc)
pc += b; /* do the jump */
break;
}
case Lua.OP_TEST: {
if (reg == a) setreg = pc; /* jumped code can change 'a' */
break;
}
case Lua.OP_SETLIST: { // Lua.testAMode(Lua.OP_SETLIST) == false
if ( ((i>>14)&0x1ff) == 0 ) pc++; // if c == 0 then c stored in next op -> skip
break;
}
default:
if (Lua.testAMode(op) && reg == a) /* any instruction that set A */
setreg = pc;
break;
}
}
return setreg;
}
}

View File

@@ -0,0 +1,688 @@
/*******************************************************************************
* 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.EOFException;
import java.io.IOException;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Abstract base class extending {@link LibFunction} which implements the
* core of the lua standard {@code io} library.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* } </pre>
* 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}.
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@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"));
* } </pre>
* <p>
* 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 <a href="http://www.lua.org/manual/5.1/manual.html#5.7">http://www.lua.org/manual/5.1/manual.html#5.7</a>
*/
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<n; i++ )
((IoLibV) t.get(k[i])).iolib = this;
}
static final class IoLibV extends VarArgFunction {
private File f;
public IoLib iolib;
private boolean toclose;
private Varargs args;
public IoLibV() {
}
public IoLibV(File f, String name, int opcode, IoLib iolib, boolean toclose, Varargs args) {
this(f, name, opcode, iolib);
this.toclose = toclose;
this.args = args.dealias();
}
public IoLibV(File f, String name, int opcode, IoLib iolib) {
super();
this.f = f;
this.name = name;
this.opcode = opcode;
this.iolib = iolib;
}
public Varargs invoke(Varargs args) {
try {
switch ( opcode ) {
case IO_FLUSH: return iolib._io_flush();
case IO_TMPFILE: return iolib._io_tmpfile();
case IO_CLOSE: return iolib._io_close(args.arg1());
case IO_INPUT: return iolib._io_input(args.arg1());
case IO_OUTPUT: return iolib._io_output(args.arg1());
case IO_TYPE: return iolib._io_type(args.arg1());
case IO_POPEN: return iolib._io_popen(args.checkjstring(1),args.optjstring(2,"r"));
case IO_OPEN: return iolib._io_open(args.checkjstring(1), args.optjstring(2,"r"));
case IO_LINES: return iolib._io_lines(args);
case IO_READ: return iolib._io_read(args);
case IO_WRITE: return iolib._io_write(args);
case FILE_CLOSE: return iolib._file_close(args.arg1());
case FILE_FLUSH: return iolib._file_flush(args.arg1());
case FILE_SETVBUF: return iolib._file_setvbuf(args.arg1(),args.checkjstring(2),args.optint(3,8192));
case FILE_LINES: return iolib._file_lines(args);
case FILE_READ: return iolib._file_read(args.arg1(),args.subargs(2));
case FILE_SEEK: return iolib._file_seek(args.arg1(),args.optjstring(2,"cur"),args.optint(3,0));
case FILE_WRITE: return iolib._file_write(args.arg1(),args.subargs(2));
case IO_INDEX: return iolib._io_index(args.arg(2));
case LINES_ITER: return iolib._lines_iter(f, toclose, this.args);
}
} catch ( IOException ioe ) {
if (opcode == LINES_ITER) {
String s = ioe.getMessage();
error(s != null ? s : ioe.toString());
}
return errorresult(ioe);
}
return NONE;
}
}
private File input() {
return infile!=null? infile: (infile=ioopenfile(FTYPE_STDIN, "-","r"));
}
// io.flush() -> bool
public Varargs _io_flush() throws IOException {
checkopen(output());
outfile.flush();
return LuaValue.TRUE;
}
// io.tmpfile() -> file
public Varargs _io_tmpfile() throws IOException {
return tmpFile();
}
// io.close([file]) -> void
public Varargs _io_close(LuaValue file) throws IOException {
File f = file.isnil()? output(): checkfile(file);
checkopen(f);
return ioclose(f);
}
// io.input([file]) -> file
public Varargs _io_input(LuaValue file) {
infile = file.isnil()? input():
file.isstring()? ioopenfile(FTYPE_NAMED, file.checkjstring(),"r"):
checkfile(file);
return infile;
}
// io.output(filename) -> file
public Varargs _io_output(LuaValue filename) {
outfile = filename.isnil()? output():
filename.isstring()? ioopenfile(FTYPE_NAMED, filename.checkjstring(),"w"):
checkfile(filename);
return outfile;
}
// io.type(obj) -> "file" | "closed file" | nil
public Varargs _io_type(LuaValue obj) {
File f = optfile(obj);
return f!=null?
f.isclosed()? CLOSED_FILE: FILE:
NIL;
}
// io.popen(prog, [mode]) -> file
public Varargs _io_popen(String prog, String mode) throws IOException {
if (!"r".equals(mode) && !"w".equals(mode)) argerror(2, "invalid value: '" + mode + "'; must be one of 'r' or 'w'");
return openProgram(prog, mode);
}
// io.open(filename, [mode]) -> file | nil,err
public Varargs _io_open(String filename, String mode) throws IOException {
return rawopenfile(FTYPE_NAMED, filename, mode);
}
// io.lines(filename, ...) -> iterator
public Varargs _io_lines(Varargs args) {
String filename = args.optjstring(1, null);
File infile = filename==null? input(): ioopenfile(FTYPE_NAMED, filename,"r");
checkopen(infile);
return lines(infile, filename != null, args.subargs(2));
}
// io.read(...) -> (...)
public Varargs _io_read(Varargs args) throws IOException {
checkopen(input());
return ioread(infile,args);
}
// io.write(...) -> void
public Varargs _io_write(Varargs args) throws IOException {
checkopen(output());
return iowrite(outfile,args);
}
// file:close() -> void
public Varargs _file_close(LuaValue file) throws IOException {
return ioclose(checkfile(file));
}
// file:flush() -> void
public Varargs _file_flush(LuaValue file) throws IOException {
checkfile(file).flush();
return LuaValue.TRUE;
}
// file:setvbuf(mode,[size]) -> void
public Varargs _file_setvbuf(LuaValue file, String mode, int size) {
if ("no".equals(mode)) {
} else if ("full".equals(mode)) {
} else if ("line".equals(mode)) {
} else {
argerror(1, "invalid value: '" + mode + "'; must be one of 'no', 'full' or 'line'");
}
checkfile(file).setvbuf(mode,size);
return LuaValue.TRUE;
}
// file:lines(...) -> iterator
public Varargs _file_lines(Varargs args) {
return lines(checkfile(args.arg1()), false, args.subargs(2));
}
// file:read(...) -> (...)
public Varargs _file_read(LuaValue file, Varargs subargs) throws IOException {
return ioread(checkfile(file),subargs);
}
// file:seek([whence][,offset]) -> pos | nil,error
public Varargs _file_seek(LuaValue file, String whence, int offset) throws IOException {
if ("set".equals(whence)) {
} else if ("end".equals(whence)) {
} else if ("cur".equals(whence)) {
} else {
argerror(1, "invalid value: '" + whence + "'; must be one of 'set', 'cur' or 'end'");
}
return valueOf( checkfile(file).seek(whence,offset) );
}
// file:write(...) -> void
public Varargs _file_write(LuaValue file, Varargs subargs) throws IOException {
return iowrite(checkfile(file),subargs);
}
// __index, returns a field
public Varargs _io_index(LuaValue v) {
return v.equals(STDOUT)?output():
v.equals(STDIN)? input():
v.equals(STDERR)? errput(): NIL;
}
// lines iterator(s,var) -> var'
public Varargs _lines_iter(LuaValue file, boolean toclose, Varargs args) throws IOException {
File f = optfile(file);
if ( f == null ) argerror(1, "not a file: " + file);
if ( f.isclosed() ) error("file is already closed");
Varargs ret = ioread(f, args);
if (toclose && ret.isnil(1) && f.eof()) f.close();
return ret;
}
private File output() {
return outfile!=null? outfile: (outfile=ioopenfile(FTYPE_STDOUT,"-","w"));
}
private File errput() {
return errfile!=null? errfile: (errfile=ioopenfile(FTYPE_STDERR,"-","w"));
}
private File ioopenfile(int filetype, String filename, String mode) {
try {
return rawopenfile(filetype, filename, mode);
} catch ( Exception e ) {
error("io error: "+e.getMessage());
return null;
}
}
private static Varargs ioclose(File f) throws IOException {
if ( f.isstdfile() )
return errorresult("cannot close standard file");
else {
f.close();
return successresult();
}
}
private static Varargs successresult() {
return LuaValue.TRUE;
}
static Varargs errorresult(Exception ioe) {
String s = ioe.getMessage();
return errorresult("io error: "+(s!=null? s: ioe.toString()));
}
private static Varargs errorresult(String errortext) {
return varargsOf(NIL, valueOf(errortext));
}
private Varargs lines(final File f, boolean toclose, Varargs args) {
try {
return new IoLibV(f,"lnext",LINES_ITER,this,toclose,args);
} catch ( Exception e ) {
return error("lines: "+e);
}
}
private static Varargs iowrite(File f, Varargs args) throws IOException {
for ( int i=1, n=args.narg(); i<=n; i++ )
f.write( args.checkstring(i) );
return f;
}
private Varargs ioread(File f, Varargs args) throws IOException {
int i,n=args.narg();
if (n == 0) return freadline(f,false);
LuaValue[] v = new LuaValue[n];
LuaValue ai,vi;
LuaString fmt;
for ( i=0; i<n; ) {
item: switch ( (ai = args.arg(i+1)).type() ) {
case LuaValue.TNUMBER:
vi = freadbytes(f,ai.toint());
break item;
case LuaValue.TSTRING:
fmt = ai.checkstring();
if ( fmt.m_length >= 2 && fmt.m_bytes[fmt.m_offset] == '*' ) {
switch ( fmt.m_bytes[fmt.m_offset+1] ) {
case 'n': vi = freadnumber(f); break item;
case 'l': vi = freadline(f,false); break item;
case 'L': vi = freadline(f,true); break item;
case 'a': vi = freadall(f); break item;
}
}
default:
return argerror( i+1, "(invalid format)" );
}
if ( (v[i++] = vi).isnil() )
break;
}
return i==0? NIL: varargsOf(v, 0, i);
}
private static File checkfile(LuaValue val) {
File f = optfile(val);
if ( f == null )
argerror(1,"file");
checkopen( f );
return f;
}
private static File optfile(LuaValue val) {
return (val instanceof File)? (File) val: null;
}
private static File checkopen(File file) {
if ( file.isclosed() )
error("attempt to use a closed file");
return file;
}
private File rawopenfile(int filetype, String filename, String mode) throws IOException {
int len = mode.length();
for (int i = 0; i < len; i++) { // [rwa][+]?b*
char ch = mode.charAt(i);
if (i == 0 && "rwa".indexOf(ch) >= 0) continue;
if (i == 1 && ch == '+') continue;
if (i >= 1 && ch == 'b') continue;
len = -1;
break;
}
if (len <= 0) argerror(2, "invalid mode: '" + mode + "'");
switch (filetype) {
case FTYPE_STDIN: return wrapStdin();
case FTYPE_STDOUT: return wrapStdout();
case FTYPE_STDERR: return wrapStderr();
}
boolean isreadmode = mode.startsWith("r");
boolean isappend = mode.startsWith("a");
boolean isupdate = mode.indexOf('+') > 0;
boolean isbinary = mode.endsWith("b");
return openFile( filename, isreadmode, isappend, isupdate, isbinary );
}
// ------------- file reading utilitied ------------------
public static LuaValue freadbytes(File f, int count) throws IOException {
if (count == 0) return f.eof() ? NIL : EMPTYSTRING;
byte[] b = new byte[count];
int r;
if ( ( r = f.read(b,0,b.length) ) < 0 )
return NIL;
return LuaString.valueUsing(b, 0, r);
}
public static LuaValue freaduntil(File f,boolean lineonly,boolean withend) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int c;
try {
if ( lineonly ) {
loop: while ( (c = f.read()) >= 0 ) {
switch ( c ) {
case '\r': if (withend) baos.write(c); break;
case '\n': if (withend) baos.write(c); break loop;
default: baos.write(c); break;
}
}
} else {
while ( (c = f.read()) >= 0 )
baos.write(c);
}
} catch ( EOFException e ) {
c = -1;
}
return ( c < 0 && baos.size() == 0 )?
(LuaValue) NIL:
(LuaValue) LuaString.valueUsing(baos.toByteArray());
}
public static LuaValue freadline(File f,boolean withend) throws IOException {
return freaduntil(f,true,withend);
}
public static LuaValue freadall(File f) throws IOException {
int n = f.remaining();
if ( n >= 0 ) {
return n == 0 ? EMPTYSTRING : freadbytes(f, n);
} else {
return freaduntil(f,false,false);
}
}
public static LuaValue freadnumber(File f) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
freadchars(f," \t\r\n",null);
freadchars(f,"-+",baos);
//freadchars(f,"0",baos);
//freadchars(f,"xX",baos);
freadchars(f,"0123456789",baos);
freadchars(f,".",baos);
freadchars(f,"0123456789",baos);
//freadchars(f,"eEfFgG",baos);
// freadchars(f,"+-",baos);
//freadchars(f,"0123456789",baos);
String s = baos.toString();
return s.length()>0? valueOf( Double.parseDouble(s) ): NIL;
}
private static void freadchars(File f, String chars, ByteArrayOutputStream baos) throws IOException {
int c;
while ( true ) {
c = f.peek();
if ( chars.indexOf(c) < 0 ) {
return;
}
f.read();
if ( baos != null )
baos.write( c );
}
}
}

View File

@@ -0,0 +1,222 @@
/*******************************************************************************
* 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.LuaError;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LuaFunction} common to Java functions exposed to lua.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <ul>
* <li>{@link ZeroArgFunction}</li>
* <li>{@link OneArgFunction}</li>
* <li>{@link TwoArgFunction}</li>
* <li>{@link ThreeArgFunction}</li>
* <li>{@link VarArgFunction}</li>
* </ul>
* <p>
* 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.
* <p>
* For example, the following code will implement a library called "hyperbolic"
* with two functions, "sinh", and "cosh":
<pre> {@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()));
* }
* }
*}
*}</pre>
* The default constructor is used to instantiate the library
* in response to {@code require 'hyperbolic'} statement,
* provided it is on Java&quot;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.
* <p>
* To test it, a script such as this can be used:
* <pre> {@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) )
* }</pre>
* <p>
* It should produce something like:
* <pre> {@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
* }</pre>
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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
* <p>
* 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<n; i++ ) {
LibFunction f = (LibFunction) factory.newInstance();
f.opcode = firstopcode + i;
f.name = names[i];
env.set(f.name, f);
}
} catch ( Exception e ) {
throw new LuaError( "bind failed: "+e );
}
}
/** Java code generation utility to allocate storage for upvalue, leave it empty */
protected static LuaValue[] newupe() {
return new LuaValue[1];
}
/** Java code generation utility to allocate storage for upvalue, initialize with nil */
protected static LuaValue[] newupn() {
return new LuaValue[] { NIL };
}
/** Java code generation utility to allocate storage for upvalue, initialize with value */
protected static LuaValue[] newupl(LuaValue v) {
return new LuaValue[] { v };
}
public LuaValue call() {
return argerror(1,"value expected");
}
public LuaValue call(LuaValue a) {
return call();
}
public LuaValue call(LuaValue a, LuaValue b) {
return call(a);
}
public LuaValue call(LuaValue a, LuaValue b, LuaValue c) {
return call(a,b);
}
public LuaValue call(LuaValue a, LuaValue b, LuaValue c, LuaValue d) {
return call(a,b,c);
}
public Varargs invoke(Varargs args) {
switch(args.narg()) {
case 0: return call();
case 1: return call(args.arg1());
case 2: return call(args.arg1(),args.arg(2));
case 3: return call(args.arg1(),args.arg(2),args.arg(3));
default: return call(args.arg1(),args.arg(2),args.arg(3),args.arg(4));
}
}
}

View File

@@ -0,0 +1,306 @@
/*******************************************************************************
* 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.util.Random;
import org.luaj.vm2.LuaDouble;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code math}
* library.
* <p>
* It contains only the math library support that is possible on JME.
* For a more complete implementation based on math functions specific to JSE
* use {@link org.luaj.vm2.libs.jse.JseMathLib}.
* In Particular the following math functions are <b>not</b> implemented by this library:
* <ul>
* <li>acos</li>
* <li>asin</li>
* <li>atan</li>
* <li>cosh</li>
* <li>log</li>
* <li>sinh</li>
* <li>tanh</li>
* <li>atan2</li>
* </ul>
* <p>
* 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.
* <p>
* 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()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) );
* } </pre>
* 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.
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@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) ) );
* } </pre>
* Doing so will ensure the library is properly initialized
* and loaded into the globals table.
* <p>
* 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 <a href="http://www.lua.org/manual/5.2/manual.html#6.6">Lua 5.2 Math Lib Reference</a>
*/
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<m) argerror(2, "interval is empty");
return valueOf( m + random.nextInt(n+1-m) );
}
}
static class randomseed extends OneArgFunction {
final random random;
randomseed(random random) {
this.random = random;
}
public LuaValue call(LuaValue arg) {
long seed = arg.checklong();
random.random = new Random(seed);
return NONE;
}
}
/** compute power using installed math library, or default if there is no math library installed */
public static LuaValue dpow(double a, double b) {
return LuaDouble.valueOf(
MATHLIB!=null?
MATHLIB.dpow_lib(a,b):
dpow_default(a,b) );
}
public static double dpow_d(double a, double b) {
return MATHLIB!=null?
MATHLIB.dpow_lib(a,b):
dpow_default(a,b);
}
/**
* Hook to override default dpow behavior with faster implementation.
*/
public double dpow_lib(double a, double b) {
return dpow_default(a,b);
}
/**
* Default JME version computes using longhand heuristics.
*/
protected static double dpow_default(double a, double b) {
if ( b < 0 )
return 1 / dpow_default( a, -b );
double p = 1;
int whole = (int) b;
for ( double v=a; whole > 0; whole>>=1, v*=v )
if ( (whole & 1) != 0 )
p *= v;
if ( (b -= whole) > 0 ) {
int frac = (int) (0x10000 * b);
for ( ; (frac&0xffff)!=0; frac<<=1 ) {
a = Math.sqrt(a);
if ( (frac & 0x8000) != 0 )
p *= a;
}
}
return p;
}
}

View File

@@ -0,0 +1,72 @@
/*******************************************************************************
* 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 one argument and
* return one value.
* <p>
* 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.
* <p>
* 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}.
* <p>
* 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());
}
}

View File

@@ -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.
* <p>
* It is a usable base with simplified stub functions
* for library functions that cannot be implemented uniformly
* on Jse and Jme.
* <p>
* This can be installed as-is on either platform, or extended
* and refined to be used in a complete Jse implementation.
* <p>
* 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.
* <p>
* The following functions have limited implementations of features
* that are not supported well on Jme:
* <ul>
* <li>{@code execute()}</li>
* <li>{@code remove()}</li>
* <li>{@code rename()}</li>
* <li>{@code tmpname()}</li>
* </ul>
* <p>
* 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()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("os").get("time").call() );
* } </pre>
* 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.
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@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() );
* } </pre>
* <p>
* @see LibFunction
* @see org.luaj.vm2.libs.jse.JseOsLib
* @see org.luaj.vm2.libs.jse.JsePlatform
* @see org.luaj.vm2.libs.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.1/manual.html#5.8">http://www.lua.org/manual/5.1/manual.html#5.8</a>
*/
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;
}
}
}

View File

@@ -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.
*
* <h3>Lua Environment Variables</h3>
* The following variables are available to lua scrips when this library has been loaded:
* <ul>
* <li><code>"package.loaded"</code> Lua table of loaded modules.
* <li><code>"package.path"</code> Search path for lua scripts.
* <li><code>"package.preload"</code> Lua table of uninitialized preload functions.
* <li><code>"package.searchers"</code> Lua table of functions that search for object to load.
* </ul>
*
* <h3>Java Environment Variables</h3>
* These Java environment variables affect the library behavior:
* <ul>
* <li><code>"luaj.package.path"</code> Initial value for <code>"package.path"</code>. Default value is <code>"?.lua"</code>
* </ul>
*
* <h3>Loading</h3>
* 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()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("require").call"foo") );
* } </pre>
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* System.out.println( globals.get("require").call("foo") );
* } </pre>
* <h3>Limitations</h3>
* 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.
* <p>
* @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 <a href="http://www.lua.org/manual/5.2/manual.html#6.3">Lua 5.2 Package Lib Reference</a>
*/
public class PackageLib extends TwoArgFunction {
/** The default value to use for package.path. This can be set with the system property
* <code>"luaj.package.path"</code>, and is <code>"?.lua"</code> 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<j; k++ ) {
char c = filename.charAt(k);
if ( (!isClassnamePart(c)) || (c=='/') || (c=='\\') ) {
StringBuffer sb = new StringBuffer(j);
for ( int i=0; i<j; i++ ) {
c = filename.charAt(i);
sb.append(
(isClassnamePart(c))? c:
((c=='/') || (c=='\\'))? '.': '_' );
}
return sb.toString();
}
}
return n==j? filename: filename.substring(0,j);
}
private static final boolean isClassnamePart(char c) {
if ( (c>='a'&&c<='z') || (c>='A'&&c<='Z') || (c>='0'&&c<='9') )
return true;
switch ( c ) {
case '.':
case '$':
case '_':
return true;
default:
return false;
}
}
}

View File

@@ -0,0 +1,59 @@
/*******************************************************************************
* 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.InputStream;
import org.luaj.vm2.Globals;
/**
* Interface for opening application resource files such as scripts sources.
* <p>
* 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.
* <p>
* 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)}.
* <p>
* The io library does not use this API for file manipulation.
* <p>
* @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 );
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,158 @@
/*******************************************************************************
* 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.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code table}
* library.
*
* <p>
* 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()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("table").get("length").call( LuaValue.tableOf() ) );
* } </pre>
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@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() ) );
* } </pre>
* <p>
* 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 <a href="http://www.lua.org/manual/5.2/manual.html#6.5">Lua 5.2 Table Lib Reference</a>
*/
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));
}
}
}

View File

@@ -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");
}
}

View File

@@ -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.
* <p>
* 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.
* <p>
* 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}.
* <p>
* 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));
}
}

View File

@@ -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.
* <p>
* 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.
* <p>
* 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}.
* <p>
* 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));
}
}

View File

@@ -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.
* <p>
* 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.
* <p>
* If between one and three arguments are required, and only one return value is returned,
* {@link ZeroArgFunction}, {@link OneArgFunction}, {@link TwoArgFunction}, or {@link ThreeArgFunction}.
* <p>
* 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);
}
}

View File

@@ -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.
* <p>
* 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.
* <p>
* 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}.
* <p>
* 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();
}
}