diff --git a/src/core/org/luaj/lib/TableLib.java b/src/core/org/luaj/lib/TableLib.java index b502cd67..13eab744 100644 --- a/src/core/org/luaj/lib/TableLib.java +++ b/src/core/org/luaj/lib/TableLib.java @@ -96,10 +96,10 @@ public class TableLib extends LFunction { */ case CONCAT: { int n = vm.gettop(); - LTable table = vm.totable(2); - LString sep = (n>=3? vm.tolstring(3): null); - int i = (n>=4? vm.tointeger(4): 1); - int j = (n>=5? vm.tointeger(5): table.luaLength()); + LTable table = vm.checktable(2); + LString sep = (n>=3? vm.checklstring(3): null); + int i = (n>=4? vm.checkint(4): 1); + int j = (n>=5? vm.checkint(5): table.luaLength()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); for ( int k=i; k<=j; k++ ) { LValue v = table.get(k); @@ -119,8 +119,8 @@ public class TableLib extends LFunction { case FOREACH: case FOREACHI: { - LTable table = vm.totable(2); - LFunction function = vm.tojavafunction(3); + LTable table = vm.checktable(2); + LFunction function = vm.checkfunction(3); LValue result = table.foreach( vm, function, id==FOREACHI ); vm.resettop(); vm.pushlvalue( result ); @@ -132,7 +132,7 @@ public class TableLib extends LFunction { * Get length of table t. */ case GETN: { - LTable table = vm.totable(2); + LTable table = vm.checktable(2); vm.resettop(); vm.pushinteger(table.luaLength()); break; @@ -146,10 +146,11 @@ public class TableLib extends LFunction { */ case INSERT: { int n = vm.gettop(); - LTable table = vm.totable(2); - int pos = (n>=4? vm.tointeger(3): 0); + LTable table = vm.checktable(2); + int pos = (n>=4? vm.checkint(3): 0); LValue value = vm.topointer(-1); table.luaInsertPos( pos, value ); + vm.resettop(); break; } @@ -159,7 +160,7 @@ public class TableLib extends LFunction { * indices. (To do its job this function does a linear traversal of the whole table.) */ case MAXN: { - LTable table = vm.totable(2); + LTable table = vm.checktable(2); vm.resettop(); vm.pushlvalue( table.luaMaxN() ); break; @@ -173,8 +174,8 @@ public class TableLib extends LFunction { */ case REMOVE: { int n = vm.gettop(); - LTable table = vm.totable(2); - int pos = (n>=3? vm.tointeger(3): 0); + LTable table = vm.checktable(2); + int pos = (n>=3? vm.checkint(3): 0); vm.resettop(); LValue removed = table.luaRemovePos( pos ); if ( removed != LNil.NIL ) @@ -192,8 +193,8 @@ public class TableLib extends LFunction { * The sort algorithm is not stable; that is, elements considered equal by the given order may have their relative positions changed by the sort. */ case SORT: { - LTable table = vm.totable(2); - LValue compare = vm.topointer(3); + LTable table = vm.checktable(2); + LValue compare = vm.checkfunction(3); table.luaSort( vm, compare ); vm.resettop(); break; diff --git a/src/core/org/luaj/vm/LNumber.java b/src/core/org/luaj/vm/LNumber.java index 457064d2..759b0caf 100644 --- a/src/core/org/luaj/vm/LNumber.java +++ b/src/core/org/luaj/vm/LNumber.java @@ -45,6 +45,13 @@ public class LNumber extends LValue { */ public abstract boolean isInteger(); + /** + * In lua all numbers are strings ! + */ + public boolean isString() { + return true; + } + /** Convert to a Byte value */ public Byte toJavaBoxedByte() { return new Byte(toJavaByte()); diff --git a/src/core/org/luaj/vm/LuaState.java b/src/core/org/luaj/vm/LuaState.java index 7644148d..85710455 100644 --- a/src/core/org/luaj/vm/LuaState.java +++ b/src/core/org/luaj/vm/LuaState.java @@ -2614,14 +2614,14 @@ public class LuaState extends Lua { // ================= Error Reporting Functions ================= - /** - * Report an error with an argument. - * - * @param narg - * Stack index of the bad argument - * @param extramsg - * String to include in error message - */ + /** + * Report an error with an argument. + * + * @param narg + * Stack index of the bad argument + * @param extramsg + * String to include in error message + */ public void argerror(int narg, String extramsg) { // TODO: port ldebug.c and provide mode useful error messages. error("bad argument #" + (narg - 1) + " (" + extramsg + ")"); @@ -2651,6 +2651,136 @@ public class LuaState extends Lua { public void typerror(int narg, int typenum) { typerror(narg, TYPE_NAMES[typenum]); } + + + /** + * Checks whether the function has an argument of any type (including nil) + * at position narg. + * @param narg the argument number + * @throws LuaErrorException if there is no argument at position narg + */ + public void checkany(int narg) { + if ( gettop() < narg ) + argerror(narg, "value expected"); + } + + /** + * Checks whether the function argument narg is a function and + * returns this function. + * @see LFunction + * @param narg the argument number + * @throws LuaErrorException if the value is not a function + * @return LFunction value if the argument is a function + */ + public LFunction checkfunction(int narg) { + return (LFunction) checktype(narg, Lua.LUA_TFUNCTION); + } + + /** + * Checks whether the function argument narg is a number and + * returns this number cast to an int. + * @param narg the argument number + * @throws LuaErrorException if the number cannot be converted to an int + * @return int value if the argument is an int or can be converted to one + */ + public int checkint(int narg) { + LValue v = tolnumber(narg); + if ( ! v.isInteger() ) + typerror(narg, Lua.LUA_TNUMBER); + return v.toJavaInt(); + } + + /** + * Checks whether the function argument narg is a number and + * returns this number cast to a LInteger. + * @see LInteger + * @param narg the argument number + * @throws LuaErrorException if the value cannot be converted to an int + * @return LInteger value if the argument is an int or can be converted to one + */ + public LInteger checkinteger(int narg) { + return LInteger.valueOf(checkint(narg)); + } + + /** + * Checks whether the function argument narg is a number and + * returns this number cast to a long. + * @param narg the argument number + * @throws LuaErrorException if the value cannot be converted to a long + * @return long value if the argument is a number or can be converted to long + */ + public long checklong(int narg) { + return checknumber(narg).toJavaLong(); + } + + /** + * Checks whether the function argument narg is a number and + * returns this number. + * @see LNumber + * @param narg the argument number + * @throws LuaErrorException if the value cannot be converted to a number + * @return LNumber value if the argument is a number or can be converted to one + */ + public LNumber checknumber(int narg) { + return (LNumber) checktype(narg, Lua.LUA_TTABLE); + } + + /** + * Checks whether the function argument narg is a string and + * returns this string as a lua string. + * @see LString + * @param narg the argument number + * @throws LuaErrorException if the value cannot be converted to a string + * @return LString value if the argument is a string or can be converted to one + */ + public LString checklstring(int narg) { + LValue v = topointer(narg); + if ( ! v.isString() ) + typerror(narg, Lua.LUA_TSTRING); + return v.luaAsString(); + } + + /** + * Checks whether the function argument narg is a string and + * returns this string as a Java String. + * @param narg the argument number + * @throws LuaErrorException if the value cannot be converted to a string + * @return String value if the argument is a string or can be converted to one + */ + public String checkstring(int narg) { + LValue v = topointer(narg); + if ( ! v.isString() ) + typerror(narg, Lua.LUA_TSTRING); + return v.toJavaString(); + } + + /** + * Checks whether the function argument narg is a table and + * returns this table. + * @see LTable + * @param narg the argument number + * @throws LuaErrorException if the value is not a table + * @return LTable value if the argument is a table + */ + public LTable checktable(int narg) { + return (LTable) checktype(narg, Lua.LUA_TTABLE); + } + + /** + * Checks whether the function argument narg has type + * t and return it as an LValue. + * @param narg the argument number + * @param t the type number to check against + * @return the lua value + * @throws LuaErrorException if the value is not of type t + */ + public LValue checktype(int narg, int t) { + LValue v = topointer(narg); + if ( v.luaGetType() != t ) + typerror(narg, t); + return v; + } + /** * Check that the type of userdata on the stack matches the required type, diff --git a/src/test/errors/args.lua b/src/test/errors/args.lua index a935a8ed..33dc636e 100644 --- a/src/test/errors/args.lua +++ b/src/test/errors/args.lua @@ -52,14 +52,40 @@ local structtypes = { ['userdata']='', } -local function signature(name,arglist) +local function bracket(v) + return "<"..type(v)..">" +end + +local function quote(v) + return "'"..v.."'" +end + +local function ellipses(v) + local s = tostring(v) + return #s <= 8 and s or string.sub(s,8)..'...' +end + +local pretty = { + ['table']=bracket, + ['function']=bracket, + ['thread']=bracket, + ['userdata']=bracket, + ['string']= quote, + ['number']= ellipses, +} + +local function values(list) local t = {} - for i=1,#arglist do - local ai = arglist[i] - local ti = type(ai) - t[i] = structtypes[ti] or tostring(ai) + for i=1,#list do + local ai = list[i] + local fi = pretty[type(ai)] + t[i] = fi and fi(ai) or tostring(ai) end - return name..'('..table.concat(t,',')..')' + return table.concat(t,',') +end + +local function signature(name,arglist) + return name..'('..values(arglist)..')' end local function dup(t) @@ -123,11 +149,12 @@ function checkallpass( name, typesets ) subbanner('checkallpass') for i,v in arglists(typesets) do local sig = signature(name,v) - local s,e = invoke( name, v ) + local r = { invoke( name, v ) } + local s = table.remove( r, 1 ) if s then - print( ok, sig ) + print( ok, sig, values(r) ) else - print( fail, sig, e ) + print( fail, sig, values(r) ) end end end