diff --git a/src/core/org/luaj/vm2/LuaValue.java b/src/core/org/luaj/vm2/LuaValue.java index 13037a5c..2f55d711 100644 --- a/src/core/org/luaj/vm2/LuaValue.java +++ b/src/core/org/luaj/vm2/LuaValue.java @@ -164,7 +164,7 @@ public class LuaValue extends Varargs { public static LuaValue error(String message) { throw new LuaError(message); } public static void assert_(boolean b,String msg) { if(!b) throw new LuaError(msg); } protected LuaValue argerror(String expected) { throw new LuaError("bad argument: "+expected+" expected, got "+typename()); } - public static void argerror(int iarg,String msg) { throw new LuaError("bad argument #"+iarg+": "+msg); } + public static LuaValue argerror(int iarg,String msg) { throw new LuaError("bad argument #"+iarg+": "+msg); } protected LuaValue typerror(String expected) { throw new LuaError(expected+" expected, got "+typename()); } protected LuaValue unimplemented(String fun) { throw new LuaError("'"+fun+"' not implemented for "+typename()); } protected LuaValue callerror() { throw new LuaError("attempt to call "+typename()); } diff --git a/src/core/org/luaj/vm2/Varargs.java b/src/core/org/luaj/vm2/Varargs.java index 41d2ebe1..9647b7f8 100644 --- a/src/core/org/luaj/vm2/Varargs.java +++ b/src/core/org/luaj/vm2/Varargs.java @@ -109,7 +109,7 @@ public abstract class Varargs { public LuaThread checkthread(int i) { return arg(i).checkthread(); } public Object checkuserdata(int i) { return arg(i).checkuserdata(); } public Object checkuserdata(int i,Class c) { return arg(i).checkuserdata(c); } - public LuaValue checkvalue(int i) { return i<=narg()? arg(i): LuaValue.error("value expected"); } + public LuaValue checkvalue(int i) { return i<=narg()? arg(i): LuaValue.argerror(i,"value expected"); } public LuaValue checknotnil(int i) { return arg(i).checknotnil(); } /** @deprecated - use checkjstring() instead */ diff --git a/src/core/org/luaj/vm2/lib/BaseLib.java b/src/core/org/luaj/vm2/lib/BaseLib.java index 29a2b7a2..6f177b66 100644 --- a/src/core/org/luaj/vm2/lib/BaseLib.java +++ b/src/core/org/luaj/vm2/lib/BaseLib.java @@ -66,6 +66,39 @@ public class BaseLib extends OneArgFunction implements ResourceFinder { private LuaValue next; private LuaValue inext; + private static final String[] LIB1_KEYS = { + "getfenv", // ( [f] ) -> env + "getmetatable", // ( object ) -> table + }; + private static final String[] LIB2_KEYS = { + "collectgarbage", // ( opt [,arg] ) -> value + "error", // ( message [,level] ) -> ERR + "setfenv", // (f, table) -> void + }; + private static final String[] LIBV_KEYS = { + "assert", // ( v [,message] ) -> v, message | ERR + "dofile", // ( filename ) -> result1, ... + "load", // ( func [,chunkname] ) -> chunk | nil, msg + "loadfile", // ( [filename] ) -> chunk | nil, msg + "loadstring", // ( string [,chunkname] ) -> chunk | nil, msg + "pcall", // (f, arg1, ...) -> status, result1, ... + "xpcall", // (f, err) -> result1, ... + "print", // (...) -> void + "select", // (f, ...) -> value1, ... + "unpack", // (list [,i [,j]]) -> result1, ... + "type", // (v) -> value + "rawequal", // (v1, v2) -> boolean + "rawget", // (table, index) -> value + "rawset", // (table, index, value) -> table + "setmetatable", // (table, metatable) -> table + "tostring", // (e) -> value + "tonumber", // (e [,base]) -> value + "pairs", // "pairs" (t) -> iter-func, t, nil + "ipairs", // "ipairs", // (t) -> iter-func, t, 0 + "next", // "next" ( table, [index] ) -> next-index, next-value + "__inext", // "inext" ( table, [int-index] ) -> next-index, next-value + }; + /** * Construct a base libarary instance. */ @@ -76,47 +109,17 @@ public class BaseLib extends OneArgFunction implements ResourceFinder { public LuaValue call(LuaValue arg) { env.set( "_G", env ); env.set( "_VERSION", VERSION ); - bind( env, BaseLib1.class, new String[] { - "getfenv", // ( [f] ) -> env - "getmetatable", // ( object ) -> table - } ); - bind( env, BaseLib2.class, new String[] { - "collectgarbage", // ( opt [,arg] ) -> value - "error", // ( message [,level] ) -> ERR - "rawequal", // (v1, v2) -> boolean - "setfenv", // (f, table) -> void - } ); - bind( env, BaseLibV.class, new String[] { - "assert", // ( v [,message] ) -> v, message | ERR - "dofile", // ( filename ) -> result1, ... - "load", // ( func [,chunkname] ) -> chunk | nil, msg - "loadfile", // ( [filename] ) -> chunk | nil, msg - "loadstring", // ( string [,chunkname] ) -> chunk | nil, msg - "pcall", // (f, arg1, ...) -> status, result1, ... - "xpcall", // (f, err) -> result1, ... - "print", // (...) -> void - "select", // (f, ...) -> value1, ... - "unpack", // (list [,i [,j]]) -> result1, ... - "type", // (v) -> value - "rawget", // (table, index) -> value - "rawset", // (table, index, value) -> table - "setmetatable", // (table, metatable) -> table - "tostring", // (e) -> value - "tonumber", // (e [,base]) -> value - "pairs", // "pairs" (t) -> iter-func, t, nil - "ipairs", // "ipairs", // (t) -> iter-func, t, 0 - "next", // "next" ( table, [index] ) -> next-index, next-value - "__inext", // "inext" ( table, [int-index] ) -> next-index, next-value - } ); + bind( env, BaseLib1.class, LIB1_KEYS ); + bind( env, BaseLib2.class, LIB2_KEYS ); + bind( env, BaseLibV.class, LIBV_KEYS ); // remember next, and inext for use in pairs and ipairs next = env.get("next"); inext = env.get("__inext"); - // inject base lib - ((BaseLibV) env.get("print")).baselib = this; - ((BaseLibV) env.get("pairs")).baselib = this; - ((BaseLibV) env.get("ipairs")).baselib = this; + // inject base lib int vararg instances + for ( int i=0; i ERR throw new LuaError( arg1.isnil()? null: arg1.tojstring(), arg2.optint(1) ); - case 2: // "rawequal", // (v1, v2) -> boolean - return valueOf(arg1 == arg2); - case 3: { // "setfenv", // (f, table) -> void + case 2: { // "setfenv", // (f, table) -> void LuaTable t = arg2.checktable(); LuaValue f = getfenvobj(arg1); f.setfenv(t); @@ -205,45 +206,35 @@ public class BaseLib extends OneArgFunction implements ResourceFinder { return args; case 1: // "dofile", // ( filename ) -> result1, ... { - LuaValue chunk; - try { - String filename = args.checkjstring(1); - chunk = loadFile(filename).arg1(); - } catch ( IOException e ) { - return error(e.getMessage()); - } - return chunk.invoke(); + Varargs v = args.isnil(1)? + BaseLib.loadStream( baselib.STDIN, "=stdin" ): + BaseLib.loadFile( args.checkjstring(1) ); + return v.isnil(1)? error(v.tojstring(2)): v.arg1().invoke(); } case 2: // "load", // ( func [,chunkname] ) -> chunk | nil, msg - try { - LuaValue func = args.checkfunction(1); - String chunkname = args.optjstring(2, "function"); - return LoadState.load(new StringInputStream(func), chunkname, LuaThread.getGlobals()); - } catch ( Exception e ) { - return varargsOf(NIL, valueOf(e.getMessage())); - } + { + LuaValue func = args.checkfunction(1); + String chunkname = args.optjstring(2, "function"); + return BaseLib.loadStream(new StringInputStream(func), chunkname); + } case 3: // "loadfile", // ( [filename] ) -> chunk | nil, msg { - try { - String filename = args.checkjstring(1); - return loadFile(filename); - } catch ( Exception e ) { - return varargsOf(NIL, valueOf(e.getMessage())); - } + return args.isnil(1)? + BaseLib.loadStream( baselib.STDIN, "=stdin" ): + BaseLib.loadFile( args.checkjstring(1) ); } case 4: // "loadstring", // ( string [,chunkname] ) -> chunk | nil, msg - try { - LuaString script = args.checkstring(1); - String chunkname = args.optjstring(2, "string"); - return LoadState.load(script.toInputStream(),chunkname,LuaThread.getGlobals()); - } catch ( Exception e ) { - return varargsOf(NIL, valueOf(e.getMessage())); - } + { + LuaString script = args.checkstring(1); + String chunkname = args.optjstring(2, "string"); + return BaseLib.loadStream(script.toInputStream(),chunkname); + } case 5: // "pcall", // (f, arg1, ...) -> status, result1, ... { + LuaValue func = args.checkvalue(1); LuaThread.onCall(this); try { - return pcall(args.arg1(),args.subargs(2),null); + return pcall(func,args.subargs(2),null); } finally { LuaThread.onReturn(); } @@ -297,23 +288,25 @@ public class BaseLib extends OneArgFunction implements ResourceFinder { } case 10: // "type", // (v) -> value return valueOf(args.checkvalue(1).typename()); - case 11: // "rawget", // (table, index) -> value + case 11: // "rawequal", // (v1, v2) -> boolean + return valueOf(args.checkvalue(1) == args.checkvalue(2)); + case 12: // "rawget", // (table, index) -> value return args.checktable(1).rawget(args.checkvalue(2)); - case 12: { // "rawset", // (table, index, value) -> table + case 13: { // "rawset", // (table, index, value) -> table LuaTable t = args.checktable(1); t.rawset(args.checknotnil(2), args.checkvalue(3)); return t; } - case 13: { // "setmetatable", // (table, metatable) -> table + case 14: { // "setmetatable", // (table, metatable) -> table final LuaValue t = args.arg1(); final LuaValue mt = args.checkvalue(2); return t.setmetatable(mt.isnil()? null: mt.checktable()); } - case 14: { // "tostring", // (e) -> value + case 15: { // "tostring", // (e) -> value LuaValue arg = args.checkvalue(1); return arg.type() == LuaValue.TSTRING? arg: valueOf(arg.tojstring()); } - case 15: { // "tonumber", // (e [,base]) -> value + case 16: { // "tonumber", // (e [,base]) -> value LuaValue arg1 = args.checkvalue(1); final int base = args.optint(2,10); if (base == 10) { /* standard conversion */ @@ -325,14 +318,14 @@ public class BaseLib extends OneArgFunction implements ResourceFinder { return str!=null? str.tonumber(base): NIL; } } - case 16: // "pairs" (t) -> iter-func, t, nil + case 17: // "pairs" (t) -> iter-func, t, nil return varargsOf( baselib.next, args.checktable(1) ); - case 17: // "ipairs", // (t) -> iter-func, t, 0 + case 18: // "ipairs", // (t) -> iter-func, t, 0 return varargsOf( baselib.inext, args.checktable(1), ZERO ); - case 18: // "next" ( table, [index] ) -> next-index, next-value - return args.arg1().next(args.arg(2)); - case 19: // "inext" ( table, [int-index] ) -> next-index, next-value - return args.arg1().inext(args.arg(2)); + case 19: // "next" ( table, [index] ) -> next-index, next-value + return args.checktable(1).next(args.arg(2)); + case 20: // "inext" ( table, [int-index] ) -> next-index, next-value + return args.checktable(1).inext(args.arg(2)); } return NONE; } @@ -357,17 +350,34 @@ public class BaseLib extends OneArgFunction implements ResourceFinder { } } - public static Varargs loadFile(String filename) throws IOException { + /** + * Load from a named file, returning the chunk or nil,error of can't load + * @return Varargs containing chunk, or NIL,error-text on error + */ + public static Varargs loadFile(String filename) { InputStream is = FINDER.findResource(filename); if ( is == null ) - return varargsOf(NIL, valueOf("not found: "+filename)); + return varargsOf(NIL, valueOf("cannot open "+filename)); try { - return LoadState.load(is, filename, LuaThread.getGlobals()); + return loadStream(is, "@"+filename); } finally { - is.close(); + try { + is.close(); + } catch ( Exception e ) { + e.printStackTrace(); + } } } + public static Varargs loadStream(InputStream is, String chunkname) { + try { + if ( is == null ) + return varargsOf(NIL, valueOf("not found: "+chunkname)); + return LoadState.load(is, chunkname, LuaThread.getGlobals()); + } catch (IOException e) { + return varargsOf(NIL, valueOf(e.getMessage())); + } + } private static class StringInputStream extends InputStream { diff --git a/src/core/org/luaj/vm2/lib/PackageLib.java b/src/core/org/luaj/vm2/lib/PackageLib.java index 98808dcf..4784cce1 100644 --- a/src/core/org/luaj/vm2/lib/PackageLib.java +++ b/src/core/org/luaj/vm2/lib/PackageLib.java @@ -366,13 +366,7 @@ public class PackageLib extends OneArgFunction { } // try loading the file - Varargs v; - try { - v = BaseLib.loadFile(filename); - } catch ( IOException ioe ) { - v = varargsOf(NIL, valueOf(ioe.getMessage())); - - } + Varargs v = BaseLib.loadFile(filename); if ( v.arg1().isfunction() ) return v.arg1(); diff --git a/test/junit/org/luaj/vm2/ErrorsTest.java b/test/junit/org/luaj/vm2/ErrorsTest.java index c0fbb344..c23e661a 100644 --- a/test/junit/org/luaj/vm2/ErrorsTest.java +++ b/test/junit/org/luaj/vm2/ErrorsTest.java @@ -21,7 +21,11 @@ ******************************************************************************/ package org.luaj.vm2; +import java.io.IOException; +import java.io.InputStream; + import org.luaj.vm2.compiler.LuaC; +import org.luaj.vm2.lib.BaseLib; /** @@ -42,7 +46,14 @@ public class ErrorsTest extends ScriptDrivenTest { super.setUp(); } - public void testBaseLibArgs() { runTest("baselibargs"); } + public void testBaseLibArgs() { + BaseLib.instance.STDIN = new InputStream() { + public int read() throws IOException { + return -1; + } + }; + runTest("baselibargs"); + } public void testCoroutineLibArgs() { runTest("coroutinelibargs"); } public void testIoLibArgs() { runTest("iolibargs"); } public void testMathLibArgs() { runTest("mathlibargs"); } diff --git a/test/junit/org/luaj/vm2/ScriptDrivenTest.java b/test/junit/org/luaj/vm2/ScriptDrivenTest.java index 432024a4..cda05103 100644 --- a/test/junit/org/luaj/vm2/ScriptDrivenTest.java +++ b/test/junit/org/luaj/vm2/ScriptDrivenTest.java @@ -68,13 +68,13 @@ public class ScriptDrivenTest extends TestCase { protected void setUp() throws Exception { super.setUp(); + initGlobals(); } // */ protected void runTest(String testName) { try { // override print() - initGlobals(); final ByteArrayOutputStream output = new ByteArrayOutputStream(); final PrintStream oldps = BaseLib.instance.STDOUT; final PrintStream ps = new PrintStream( output ); diff --git a/test/lua/errors/baselibargs.lua b/test/lua/errors/baselibargs.lua index 02885a90..a684d02f 100644 --- a/test/lua/errors/baselibargs.lua +++ b/test/lua/errors/baselibargs.lua @@ -19,17 +19,17 @@ checkallerrors('collectgarbage',{{aboolean, atable, afunction, athread}},'string banner('dofile') checkallpass('dofile', {}) checkallpass('dofile', {{'test/lua/errors/args.lua'}}) -checkallerrors('dofile', {{'args.lua'}}, 'cannot open args.lua') +checkallerrors('dofile', {{'foo.bar'}}, 'cannot open foo.bar') checkallerrors('dofile', {nonstring}, 'bad argument') -- error banner('error') -checkallerrors('error', {{'message'},{nil,0,1,2}}, 'message') -checkallerrors('error', {{123},{nil,1,2}}, 123) +checkallerrors('error', {{'message'},{nil,0,1,2,n=4}}, 'message') +checkallerrors('error', {{123},{nil,1,2,n=3}}, 123) -- getfenv banner('getfenv') -checkallpass('getfenv', {{nil,print,function()end,0,1,2}}) +checkallpass('getfenv', {{nil,print,function()end,0,1,2,n=5}}) checkallerrors('getfenv', {{true,{},'abc'}}, 'bad argument') -- getmetatable @@ -44,8 +44,8 @@ checkallerrors('ipairs', {notatable}, 'bad argument') -- load banner('load') -checkallpass('load', {somefunction,{nil,astring}}) -checkallerrors('load', {notafunction,{nil,astring,anumber}}, 'bad argument') +checkallpass('load', {somefunction,{nil,astring,n=2}}) +checkallerrors('load', {notafunction,{nil,astring,anumber,n=3}}, 'bad argument') checkallerrors('load', {somefunction,{afunction,atable}}, 'bad argument') -- loadfile @@ -62,13 +62,13 @@ checkallpass('loadstring', {{'return'}}) checkallpass('loadstring', {{'return'},{'mychunk'}}) checkallpass('loadstring', {{'return a ... b'},{'mychunk'}},true) checkallerrors('loadstring', {{'return a ... b'},{'mychunk'}},'hello') -checkallerrors('loadstring', {notastring,{nil,astring,anumber}}, 'bad argument') +checkallerrors('loadstring', {notastring,{nil,astring,anumber,n=3}}, 'bad argument') checkallerrors('loadstring', {{'return'},{afunction,atable}}, 'bad argument') -- next banner('next') checkallpass('next', {sometable,somekey}) -checkallerrors('next', {notatable,{nil,1}}, 'bad argument') +checkallerrors('next', {notatable,{nil,1,n=2}}, 'bad argument') checkallerrors('next', {sometable,nonkey}, 'invalid key') -- pairs @@ -84,7 +84,7 @@ checkallerrors('pcall',{},'bad argument') -- print banner('print') checkallpass('print', {}) -checkallpass('print', {{nil,astring,anumber,aboolean}}) +checkallpass('print', {{nil,astring,anumber,aboolean,n=4}}) -- rawequal banner('rawequal') @@ -135,9 +135,9 @@ checkallerrors('setmetatable',{sometable,notatable},'bad argument') -- tonumber banner('tonumber') -checkallpass('tonumber',{somenumber,{nil,2,10,36}}) -checkallpass('tonumber',{notanil,{nil,10}}) -checkallerrors('tonumber',{{nil,afunction,atable},{2,9,11,36}},'bad argument') +checkallpass('tonumber',{somenumber,{nil,2,10,36,n=4}}) +checkallpass('tonumber',{notanil,{nil,10,n=2}}) +checkallerrors('tonumber',{{nil,afunction,atable,n=3},{2,9,11,36}},'bad argument') checkallerrors('tonumber',{somenumber,{1,37,atable,afunction,aboolean}},'bad argument') -- tostring