[NOTHING CHANGED] Assorted fixes #48

Closed
asiekierka wants to merge 61 commits from asiekierka/master into master
Showing only changes of commit d2a92c07d3 - Show all commits

View File

@@ -1,485 +1,485 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved. * Copyright (c) 2009 Luaj.org. All rights reserved.
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * 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 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2.lib; package org.luaj.vm2.lib;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.luaj.vm2.Globals; import org.luaj.vm2.Globals;
import org.luaj.vm2.Lua; import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaError; import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaString; import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaThread; import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** /**
* Subclass of {@link LibFunction} which implements the lua basic library functions. * Subclass of {@link LibFunction} which implements the lua basic library functions.
* <p> * <p>
* This contains all library functions listed as "basic functions" in the lua documentation for JME. * This contains all library functions listed as "basic functions" in the lua documentation for JME.
* The functions dofile and loadfile use the * The functions dofile and loadfile use the
* {@link Globals#finder} instance to find resource files. * {@link Globals#finder} instance to find resource files.
* Since JME has no file system by default, {@link BaseLib} implements * Since JME has no file system by default, {@link BaseLib} implements
* {@link ResourceFinder} using {@link Class#getResource(String)}, * {@link ResourceFinder} using {@link Class#getResource(String)},
* which is the closest equivalent on JME. * which is the closest equivalent on JME.
* The default loader chain in {@link PackageLib} will use these as well. * The default loader chain in {@link PackageLib} will use these as well.
* <p> * <p>
* To use basic library functions that include a {@link ResourceFinder} based on * To use basic library functions that include a {@link ResourceFinder} based on
* directory lookup, use {@link org.luaj.vm2.lib.jse.JseBaseLib} instead. * directory lookup, use {@link org.luaj.vm2.lib.jse.JseBaseLib} instead.
* <p> * <p>
* Typically, this library is included as part of a call to either * Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code * <pre> {@code
* Globals globals = JsePlatform.standardGlobals(); * Globals globals = JsePlatform.standardGlobals();
* globals.get("print").call(LuaValue.valueOf("hello, world")); * globals.get("print").call(LuaValue.valueOf("hello, world"));
* } </pre> * } </pre>
* <p> * <p>
* For special cases where the smallest possible footprint is desired, * For special cases where the smallest possible footprint is desired,
* a minimal set of libraries could be loaded * a minimal set of libraries could be loaded
* directly via {@link Globals#load(LuaValue)} using code such as: * directly via {@link Globals#load(LuaValue)} using code such as:
* <pre> {@code * <pre> {@code
* Globals globals = new Globals(); * Globals globals = new Globals();
* globals.load(new JseBaseLib()); * globals.load(new JseBaseLib());
* globals.get("print").call(LuaValue.valueOf("hello, world")); * globals.get("print").call(LuaValue.valueOf("hello, world"));
* } </pre> * } </pre>
* Doing so will ensure the library is properly initialized * Doing so will ensure the library is properly initialized
* and loaded into the globals table. * and loaded into the globals table.
* <p> * <p>
* This is a direct port of the corresponding library in C. * This is a direct port of the corresponding library in C.
* @see org.luaj.vm2.lib.jse.JseBaseLib * @see org.luaj.vm2.lib.jse.JseBaseLib
* @see ResourceFinder * @see ResourceFinder
* @see Globals#finder * @see Globals#finder
* @see LibFunction * @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.1">Lua 5.2 Base Lib Reference</a> * @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 { public class BaseLib extends TwoArgFunction implements ResourceFinder {
Globals globals; Globals globals;
/** Perform one-time initialization on the library by adding base functions /** Perform one-time initialization on the library by adding base functions
* to the supplied environment, and returning it as the return value. * to the supplied environment, and returning it as the return value.
* @param modname the module name supplied if this is loaded via 'require'. * @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. * @param env the environment to load into, which must be a Globals instance.
*/ */
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals(); globals = env.checkglobals();
globals.finder = this; globals.finder = this;
globals.baselib = this; globals.baselib = this;
env.set( "_G", env ); env.set( "_G", env );
env.set( "_VERSION", Lua._VERSION ); env.set( "_VERSION", Lua._VERSION );
env.set("assert", new _assert()); env.set("assert", new _assert());
env.set("collectgarbage", new collectgarbage()); env.set("collectgarbage", new collectgarbage());
env.set("dofile", new dofile()); env.set("dofile", new dofile());
env.set("error", new error()); env.set("error", new error());
env.set("getmetatable", new getmetatable()); env.set("getmetatable", new getmetatable());
env.set("load", new load()); env.set("load", new load());
env.set("loadfile", new loadfile()); env.set("loadfile", new loadfile());
env.set("pcall", new pcall()); env.set("pcall", new pcall());
env.set("print", new print(this)); env.set("print", new print(this));
env.set("rawequal", new rawequal()); env.set("rawequal", new rawequal());
env.set("rawget", new rawget()); env.set("rawget", new rawget());
env.set("rawlen", new rawlen()); env.set("rawlen", new rawlen());
env.set("rawset", new rawset()); env.set("rawset", new rawset());
env.set("select", new select()); env.set("select", new select());
env.set("setmetatable", new setmetatable()); env.set("setmetatable", new setmetatable());
env.set("tonumber", new tonumber()); env.set("tonumber", new tonumber());
env.set("tostring", new tostring()); env.set("tostring", new tostring());
env.set("type", new type()); env.set("type", new type());
env.set("xpcall", new xpcall()); env.set("xpcall", new xpcall());
next next; next next;
env.set("next", next = new next()); env.set("next", next = new next());
env.set("pairs", new pairs(next)); env.set("pairs", new pairs(next));
env.set("ipairs", new ipairs()); env.set("ipairs", new ipairs());
return env; return env;
} }
/** ResourceFinder implementation /** ResourceFinder implementation
* *
* Tries to open the file as a resource, which can work for JSE and JME. * Tries to open the file as a resource, which can work for JSE and JME.
*/ */
public InputStream findResource(String filename) { public InputStream findResource(String filename) {
return getClass().getResourceAsStream(filename.startsWith("/")? filename: "/"+filename); return getClass().getResourceAsStream(filename.startsWith("/")? filename: "/"+filename);
} }
// "assert", // ( v [,message] ) -> v, message | ERR // "assert", // ( v [,message] ) -> v, message | ERR
static final class _assert extends VarArgFunction { static final class _assert extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
if ( !args.arg1().toboolean() ) if ( !args.arg1().toboolean() )
error( args.narg()>1? args.optjstring(2,"assertion failed!"): "assertion failed!" ); error( args.narg()>1? args.optjstring(2,"assertion failed!"): "assertion failed!" );
return args; return args;
} }
} }
// "collectgarbage", // ( opt [,arg] ) -> value // "collectgarbage", // ( opt [,arg] ) -> value
static final class collectgarbage extends VarArgFunction { static final class collectgarbage extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
String s = args.optjstring(1, "collect"); String s = args.optjstring(1, "collect");
if ( "collect".equals(s) ) { if ( "collect".equals(s) ) {
System.gc(); System.gc();
return ZERO; return ZERO;
} else if ( "count".equals(s) ) { } else if ( "count".equals(s) ) {
Runtime rt = Runtime.getRuntime(); Runtime rt = Runtime.getRuntime();
long used = rt.totalMemory() - rt.freeMemory(); long used = rt.totalMemory() - rt.freeMemory();
return varargsOf(valueOf(used/1024.), valueOf(used%1024)); return varargsOf(valueOf(used/1024.), valueOf(used%1024));
} else if ( "step".equals(s) ) { } else if ( "step".equals(s) ) {
System.gc(); System.gc();
return LuaValue.TRUE; return LuaValue.TRUE;
} else { } else {
argerror(1, "invalid option '" + s + "'"); argerror(1, "invalid option '" + s + "'");
} }
return NIL; return NIL;
} }
} }
// "dofile", // ( filename ) -> result1, ... // "dofile", // ( filename ) -> result1, ...
final class dofile extends VarArgFunction { final class dofile extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil"); 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 filename = args.isstring(1)? args.tojstring(1): null;
Varargs v = filename == null? Varargs v = filename == null?
loadStream( globals.STDIN, "=stdin", "bt", globals ): loadStream( globals.STDIN, "=stdin", "bt", globals ):
loadFile( args.checkjstring(1), "bt", globals ); loadFile( args.checkjstring(1), "bt", globals );
return v.isnil(1)? error(v.tojstring(2)): v.arg1().invoke(); return v.isnil(1)? error(v.tojstring(2)): v.arg1().invoke();
} }
} }
// "error", // ( message [,level] ) -> ERR // "error", // ( message [,level] ) -> ERR
static final class error extends TwoArgFunction { static final class error extends TwoArgFunction {
public LuaValue call(LuaValue arg1, LuaValue arg2) { public LuaValue call(LuaValue arg1, LuaValue arg2) {
if (arg1.isnil()) throw new LuaError(NIL); if (arg1.isnil()) throw new LuaError(NIL);
if (!arg1.isstring() || arg2.optint(1) == 0) throw new LuaError(arg1); if (!arg1.isstring() || arg2.optint(1) == 0) throw new LuaError(arg1);
throw new LuaError(arg1.tojstring(), arg2.optint(1)); throw new LuaError(arg1.tojstring(), arg2.optint(1));
} }
} }
// "getmetatable", // ( object ) -> table // "getmetatable", // ( object ) -> table
static final class getmetatable extends LibFunction { static final class getmetatable extends LibFunction {
public LuaValue call() { public LuaValue call() {
return argerror(1, "value expected"); return argerror(1, "value expected");
} }
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
LuaValue mt = arg.getmetatable(); LuaValue mt = arg.getmetatable();
return mt!=null? mt.rawget(METATABLE).optvalue(mt): NIL; return mt!=null? mt.rawget(METATABLE).optvalue(mt): NIL;
} }
} }
// "load", // ( ld [, source [, mode [, env]]] ) -> chunk | nil, msg // "load", // ( ld [, source [, mode [, env]]] ) -> chunk | nil, msg
final class load extends VarArgFunction { final class load extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue ld = args.arg1(); LuaValue ld = args.arg1();
if (!ld.isstring() && !ld.isfunction()) { if (!ld.isstring() && !ld.isfunction()) {
throw new LuaError("bad argument #1 to 'load' (string or function expected, got " + ld.typename() + ")"); 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 source = args.optjstring(2, ld.isstring()? ld.tojstring(): "=(load)");
String mode = args.optjstring(3, "bt"); String mode = args.optjstring(3, "bt");
LuaValue env = args.optvalue(4, globals); LuaValue env = args.optvalue(4, globals);
return loadStream(ld.isstring()? ld.strvalue().toInputStream(): return loadStream(ld.isstring()? ld.strvalue().toInputStream():
new StringInputStream(ld.checkfunction()), source, mode, env); new StringInputStream(ld.checkfunction()), source, mode, env);
} }
} }
// "loadfile", // ( [filename [, mode [, env]]] ) -> chunk | nil, msg // "loadfile", // ( [filename [, mode [, env]]] ) -> chunk | nil, msg
final class loadfile extends VarArgFunction { final class loadfile extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil"); 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 filename = args.isstring(1)? args.tojstring(1): null;
String mode = args.optjstring(2, "bt"); String mode = args.optjstring(2, "bt");
LuaValue env = args.optvalue(3, globals); LuaValue env = args.optvalue(3, globals);
return filename == null? return filename == null?
loadStream( globals.STDIN, "=stdin", mode, env ): loadStream( globals.STDIN, "=stdin", mode, env ):
loadFile( filename, mode, env ); loadFile( filename, mode, env );
} }
} }
// "pcall", // (f, arg1, ...) -> status, result1, ... // "pcall", // (f, arg1, ...) -> status, result1, ...
final class pcall extends VarArgFunction { final class pcall extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue func = args.checkvalue(1); LuaValue func = args.checkvalue(1);
if (globals != null && globals.debuglib != null) if (globals != null && globals.debuglib != null)
globals.debuglib.onCall(this); globals.debuglib.onCall(this);
try { try {
return varargsOf(TRUE, func.invoke(args.subargs(2))); return varargsOf(TRUE, func.invoke(args.subargs(2)));
} catch ( LuaError le ) { } catch ( LuaError le ) {
final LuaValue m = le.getMessageObject(); final LuaValue m = le.getMessageObject();
return varargsOf(FALSE, m!=null? m: NIL); return varargsOf(FALSE, m!=null? m: NIL);
} catch ( Exception e ) { } catch ( Exception e ) {
final String m = e.getMessage(); final String m = e.getMessage();
return varargsOf(FALSE, valueOf(m!=null? m: e.toString())); return varargsOf(FALSE, valueOf(m!=null? m: e.toString()));
} finally { } finally {
if (globals != null && globals.debuglib != null) if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn(); globals.debuglib.onReturn();
} }
} }
} }
// "print", // (...) -> void // "print", // (...) -> void
final class print extends VarArgFunction { final class print extends VarArgFunction {
final BaseLib baselib; final BaseLib baselib;
print(BaseLib baselib) { print(BaseLib baselib) {
this.baselib = baselib; this.baselib = baselib;
} }
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue tostring = globals.get("tostring"); LuaValue tostring = globals.get("tostring");
for ( int i=1, n=args.narg(); i<=n; i++ ) { for ( int i=1, n=args.narg(); i<=n; i++ ) {
if ( i>1 ) globals.STDOUT.print( '\t' ); if ( i>1 ) globals.STDOUT.print( '\t' );
LuaString s = tostring.call( args.arg(i) ).strvalue(); LuaString s = tostring.call( args.arg(i) ).strvalue();
globals.STDOUT.print(s.tojstring()); globals.STDOUT.print(s.tojstring());
} }
globals.STDOUT.print('\n'); globals.STDOUT.print('\n');
return NONE; return NONE;
} }
} }
// "rawequal", // (v1, v2) -> boolean // "rawequal", // (v1, v2) -> boolean
static final class rawequal extends LibFunction { static final class rawequal extends LibFunction {
public LuaValue call() { public LuaValue call() {
return argerror(1, "value expected"); return argerror(1, "value expected");
} }
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
return argerror(2, "value expected"); return argerror(2, "value expected");
} }
public LuaValue call(LuaValue arg1, LuaValue arg2) { public LuaValue call(LuaValue arg1, LuaValue arg2) {
return valueOf(arg1.raweq(arg2)); return valueOf(arg1.raweq(arg2));
} }
} }
// "rawget", // (table, index) -> value // "rawget", // (table, index) -> value
static final class rawget extends TableLibFunction { static final class rawget extends TableLibFunction {
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
return argerror(2, "value expected"); return argerror(2, "value expected");
} }
public LuaValue call(LuaValue arg1, LuaValue arg2) { public LuaValue call(LuaValue arg1, LuaValue arg2) {
return arg1.checktable().rawget(arg2); return arg1.checktable().rawget(arg2);
} }
} }
// "rawlen", // (v) -> value // "rawlen", // (v) -> value
static final class rawlen extends LibFunction { static final class rawlen extends LibFunction {
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
return valueOf(arg.rawlen()); return valueOf(arg.rawlen());
} }
} }
// "rawset", // (table, index, value) -> table // "rawset", // (table, index, value) -> table
static final class rawset extends TableLibFunction { static final class rawset extends TableLibFunction {
public LuaValue call(LuaValue table) { public LuaValue call(LuaValue table) {
return argerror(2,"value expected"); return argerror(2,"value expected");
} }
public LuaValue call(LuaValue table, LuaValue index) { public LuaValue call(LuaValue table, LuaValue index) {
return argerror(3,"value expected"); return argerror(3,"value expected");
} }
public LuaValue call(LuaValue table, LuaValue index, LuaValue value) { public LuaValue call(LuaValue table, LuaValue index, LuaValue value) {
LuaTable t = table.checktable(); LuaTable t = table.checktable();
if (!index.isvalidkey()) argerror(2, "table index is nil"); if (!index.isvalidkey()) argerror(2, "table index is nil");
t.rawset(index, value); t.rawset(index, value);
return t; return t;
} }
} }
// "select", // (f, ...) -> value1, ... // "select", // (f, ...) -> value1, ...
static final class select extends VarArgFunction { static final class select extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
int n = args.narg()-1; int n = args.narg()-1;
if ( args.arg1().equals(valueOf("#")) ) if ( args.arg1().equals(valueOf("#")) )
return valueOf(n); return valueOf(n);
int i = args.checkint(1); int i = args.checkint(1);
if ( i == 0 || i < -n ) if ( i == 0 || i < -n )
argerror(1,"index out of range"); argerror(1,"index out of range");
return args.subargs(i<0? n+i+2: i+1); return args.subargs(i<0? n+i+2: i+1);
} }
} }
// "setmetatable", // (table, metatable) -> table // "setmetatable", // (table, metatable) -> table
static final class setmetatable extends TableLibFunction { static final class setmetatable extends TableLibFunction {
public LuaValue call(LuaValue table) { public LuaValue call(LuaValue table) {
return argerror(2,"nil or table expected"); return argerror(2,"nil or table expected");
} }
public LuaValue call(LuaValue table, LuaValue metatable) { public LuaValue call(LuaValue table, LuaValue metatable) {
final LuaValue mt0 = table.checktable().getmetatable(); final LuaValue mt0 = table.checktable().getmetatable();
if ( mt0!=null && !mt0.rawget(METATABLE).isnil() ) if ( mt0!=null && !mt0.rawget(METATABLE).isnil() )
error("cannot change a protected metatable"); error("cannot change a protected metatable");
return table.setmetatable(metatable.isnil()? null: metatable.checktable()); return table.setmetatable(metatable.isnil()? null: metatable.checktable());
} }
} }
// "tonumber", // (e [,base]) -> value // "tonumber", // (e [,base]) -> value
static final class tonumber extends LibFunction { static final class tonumber extends LibFunction {
public LuaValue call(LuaValue e) { public LuaValue call(LuaValue e) {
return e.tonumber(); return e.tonumber();
} }
public LuaValue call(LuaValue e, LuaValue base) { public LuaValue call(LuaValue e, LuaValue base) {
if (base.isnil()) if (base.isnil())
return e.tonumber(); return e.tonumber();
final int b = base.checkint(); final int b = base.checkint();
if ( b < 2 || b > 36 ) if ( b < 2 || b > 36 )
argerror(2, "base out of range"); argerror(2, "base out of range");
return e.checkstring().tonumber(b); return e.checkstring().tonumber(b);
} }
} }
// "tostring", // (e) -> value // "tostring", // (e) -> value
static final class tostring extends LibFunction { static final class tostring extends LibFunction {
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
LuaValue h = arg.metatag(TOSTRING); LuaValue h = arg.metatag(TOSTRING);
if ( ! h.isnil() ) if ( ! h.isnil() )
return h.call(arg); return h.call(arg);
LuaValue v = arg.tostring(); LuaValue v = arg.tostring();
if ( ! v.isnil() ) if ( ! v.isnil() )
return v; return v;
return valueOf(arg.tojstring()); return valueOf(arg.tojstring());
} }
} }
// "type", // (v) -> value // "type", // (v) -> value
static final class type extends LibFunction { static final class type extends LibFunction {
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
return valueOf(arg.typename()); return valueOf(arg.typename());
} }
} }
// "xpcall", // (f, err) -> result1, ... // "xpcall", // (f, err) -> result1, ...
final class xpcall extends VarArgFunction { final class xpcall extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
final LuaThread t = globals.running; final LuaThread t = globals.running;
final LuaValue preverror = t.errorfunc; final LuaValue preverror = t.errorfunc;
t.errorfunc = args.checkvalue(2); t.errorfunc = args.checkvalue(2);
try { try {
if (globals != null && globals.debuglib != null) if (globals != null && globals.debuglib != null)
globals.debuglib.onCall(this); globals.debuglib.onCall(this);
try { try {
return varargsOf(TRUE, args.arg1().invoke(args.subargs(3))); return varargsOf(TRUE, args.arg1().invoke(args.subargs(3)));
} catch ( LuaError le ) { } catch ( LuaError le ) {
final LuaValue m = le.getMessageObject(); final LuaValue m = le.getMessageObject();
return varargsOf(FALSE, m!=null? m: NIL); return varargsOf(FALSE, m!=null? m: NIL);
} catch ( Exception e ) { } catch ( Exception e ) {
final String m = e.getMessage(); final String m = e.getMessage();
return varargsOf(FALSE, valueOf(m!=null? m: e.toString())); return varargsOf(FALSE, valueOf(m!=null? m: e.toString()));
} finally { } finally {
if (globals != null && globals.debuglib != null) if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn(); globals.debuglib.onReturn();
} }
} finally { } finally {
t.errorfunc = preverror; t.errorfunc = preverror;
} }
} }
} }
// "pairs" (t) -> iter-func, t, nil // "pairs" (t) -> iter-func, t, nil
static final class pairs extends VarArgFunction { static final class pairs extends VarArgFunction {
final next next; final next next;
pairs(next next) { pairs(next next) {
this.next = next; this.next = next;
} }
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
return varargsOf( next, args.checktable(1), NIL ); return varargsOf( next, args.checktable(1), NIL );
} }
} }
// // "ipairs", // (t) -> iter-func, t, 0 // // "ipairs", // (t) -> iter-func, t, 0
static final class ipairs extends VarArgFunction { static final class ipairs extends VarArgFunction {
inext inext = new inext(); inext inext = new inext();
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
return varargsOf( inext, args.checktable(1), ZERO ); return varargsOf( inext, args.checktable(1), ZERO );
} }
} }
// "next" ( table, [index] ) -> next-index, next-value // "next" ( table, [index] ) -> next-index, next-value
static final class next extends VarArgFunction { static final class next extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
return args.checktable(1).next(args.arg(2)); return args.checktable(1).next(args.arg(2));
} }
} }
// "inext" ( table, [int-index] ) -> next-index, next-value // "inext" ( table, [int-index] ) -> next-index, next-value
static final class inext extends VarArgFunction { static final class inext extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
return args.checktable(1).inext(args.arg(2)); return args.checktable(1).inext(args.arg(2));
} }
} }
/** /**
* Load from a named file, returning the chunk or nil,error of can't load * Load from a named file, returning the chunk or nil,error of can't load
* @param env * @param env
* @param mode * @param mode
* @return Varargs containing chunk, or NIL,error-text on error * @return Varargs containing chunk, or NIL,error-text on error
*/ */
public Varargs loadFile(String filename, String mode, LuaValue env) { public Varargs loadFile(String filename, String mode, LuaValue env) {
InputStream is = globals.finder.findResource(filename); InputStream is = globals.finder.findResource(filename);
if ( is == null ) if ( is == null )
return varargsOf(NIL, valueOf("cannot open "+filename+": No such file or directory")); return varargsOf(NIL, valueOf("cannot open "+filename+": No such file or directory"));
try { try {
return loadStream(is, "@"+filename, mode, env); return loadStream(is, "@"+filename, mode, env);
} finally { } finally {
try { try {
is.close(); is.close();
} catch ( Exception e ) { } catch ( Exception e ) {
e.printStackTrace(); e.printStackTrace();
} }
} }
} }
public Varargs loadStream(InputStream is, String chunkname, String mode, LuaValue env) { public Varargs loadStream(InputStream is, String chunkname, String mode, LuaValue env) {
try { try {
if ( is == null ) if ( is == null )
return varargsOf(NIL, valueOf("not found: "+chunkname)); return varargsOf(NIL, valueOf("not found: "+chunkname));
return globals.load(is, chunkname, mode, env); return globals.load(is, chunkname, mode, env);
} catch (Exception e) { } catch (Exception e) {
return varargsOf(NIL, valueOf(e.getMessage())); return varargsOf(NIL, valueOf(e.getMessage()));
} }
} }
private static class StringInputStream extends InputStream { private static class StringInputStream extends InputStream {
final LuaValue func; final LuaValue func;
byte[] bytes; byte[] bytes;
int offset, remaining = 0; int offset, remaining = 0;
StringInputStream(LuaValue func) { StringInputStream(LuaValue func) {
this.func = func; this.func = func;
} }
public int read() throws IOException { public int read() throws IOException {
if ( remaining < 0 ) if ( remaining < 0 )
return -1; return -1;
if ( remaining == 0 ) { if ( remaining == 0 ) {
LuaValue s = func.call(); LuaValue s = func.call();
if ( s.isnil() ) if ( s.isnil() )
return remaining = -1; return remaining = -1;
LuaString ls = s.strvalue(); LuaString ls = s.strvalue();
bytes = ls.m_bytes; bytes = ls.m_bytes;
offset = ls.m_offset; offset = ls.m_offset;
remaining = ls.m_length; remaining = ls.m_length;
if (remaining <= 0) if (remaining <= 0)
return -1; return -1;
} }
--remaining; --remaining;
return 0xFF&bytes[offset++]; return 0xFF&bytes[offset++];
} }
} }
} }