diff --git a/src/core/org/luaj/lib/BaseLib.java b/src/core/org/luaj/lib/BaseLib.java index fac1b769..9189dc1c 100644 --- a/src/core/org/luaj/lib/BaseLib.java +++ b/src/core/org/luaj/lib/BaseLib.java @@ -10,6 +10,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; +import org.luaj.vm.CallInfo; import org.luaj.vm.LClosure; import org.luaj.vm.LFunction; import org.luaj.vm.LInteger; @@ -43,6 +44,7 @@ public class BaseLib extends LFunction { "assert", "loadfile", "tonumber", + "rawequal", "rawget", "rawset", "getfenv", @@ -71,20 +73,21 @@ public class BaseLib extends LFunction { private static final int ASSERT = 9; private static final int LOADFILE = 10; private static final int TONUMBER = 11; - private static final int RAWGET = 12; - private static final int RAWSET = 13; - private static final int GETFENV = 14; - private static final int SETFENV = 15; - private static final int SELECT = 16; - private static final int COLLECTGARBAGE = 17; - private static final int DOFILE = 18; - private static final int LOADSTRING = 19; - private static final int LOAD = 20; - private static final int TOSTRING = 21; - private static final int UNPACK = 22; - private static final int XPCALL = 23; - private static final int NEXT = 24; - private static final int INEXT = 25; + private static final int RAWEQUAL = 12; + private static final int RAWGET = 13; + private static final int RAWSET = 14; + private static final int GETFENV = 15; + private static final int SETFENV = 16; + private static final int SELECT = 17; + private static final int COLLECTGARBAGE = 18; + private static final int DOFILE = 19; + private static final int LOADSTRING = 20; + private static final int LOAD = 21; + private static final int TOSTRING = 22; + private static final int UNPACK = 23; + private static final int XPCALL = 24; + private static final int NEXT = 25; + private static final int INEXT = 26; private static LFunction next; private static LFunction inext; @@ -165,15 +168,16 @@ public class BaseLib extends LFunction { break; } case SETMETATABLE: { - vm.checktable(2); - vm.setmetatable(2); - vm.remove(1); - vm.settop(1); + LTable t = vm.checktable(2); + LValue v = vm.checkany(3); + vm.argcheck(v.isTable() || v.isNil(), 3, "table or nil expected"); + t.luaSetMetatable(v); + vm.resettop(); + vm.pushlvalue(t); break; } case TYPE: { - vm.checkany(2); - LValue v = vm.topointer(2); + LValue v = vm.checkany(2); vm.resettop(); vm.pushlstring( v.luaGetTypeName() ); break; @@ -193,8 +197,7 @@ public class BaseLib extends LFunction { break; } case XPCALL: { - vm.checkany(3); - LValue errfun = vm.topointer(3); + LValue errfun = vm.checkany(3); vm.settop(2); int s = vm.pcall( 0, Lua.LUA_MULTRET, 0 ); if ( s == 0 ) { // success, results are on stack above the xpcall @@ -247,56 +250,45 @@ public class BaseLib extends LFunction { } break; } + case RAWEQUAL: { + LValue a = vm.checkany(2); + LValue b = vm.checkany(3); + vm.resettop(); + vm.pushboolean(a == b); + break; + } case RAWGET: { - vm.checkany(3); LTable t = vm.checktable(2); - LValue k = vm.topointer(3); + LValue k = vm.checkany(3); vm.resettop(); vm.pushlvalue( t.get( k ) ); - } break; + break; + } case RAWSET: { - vm.checkany(3); - vm.checkany(4); LTable t = vm.checktable(2); - LValue k = vm.topointer(3); - LValue v = vm.topointer(4); + LValue k = vm.checkany(3); + LValue v = vm.checkany(4); t.put( k, v ); vm.resettop(); vm.pushlvalue(t); - } break; + break; + } case GETFENV: { - if ( vm.isfunction(2) ) { - vm.getfenv(-1); - } else { - int i = vm.optint(2,1); - if ( i <= 0 ) - vm.pushlvalue(vm._G); - else if ( i-1 <= vm.cc ) - vm.pushlvalue( vm.getStackFrame(i-1).closure.env ); - else - vm.pushnil(); - } - vm.insert(1); - vm.settop(1); + LValue f = getfunc(vm, true); + vm.resettop(); + vm.pushlvalue(f.luaGetEnv(vm._G)); break; } case SETFENV: { - LTable t = vm.checktable(-1); - LFunction f = vm.checkfunction(2); - if ( vm.setfenv(2) != 0 ) { - vm.remove(1); - break; - } - int i = vm.tointeger(2); - if ( i == 0 ) { + LTable t = vm.checktable(3); + LValue f = getfunc(vm, false); + if ( vm.isnumber(2) && vm.tointeger(2) == 0 ) { vm._G = t; - vm.resettop(); - } else { - LClosure c = vm.getStackFrame(i-1).closure; - c.luaSetEnv(t); - vm.resettop(); - vm.pushlvalue(c); + } else if ( (!(f instanceof LClosure)) || ! f.luaSetEnv(t) ) { + vm.error( "'setfenv' cannot change environment of given object" ); } + vm.resettop(); + vm.pushlvalue(f); break; } case SELECT: { @@ -307,7 +299,7 @@ public class BaseLib extends LFunction { if ( index < 0 ) index += n-1; if ( index <= 0 ) - vm.error( "bad argument #1 to '?' (index out of range)" ); + vm.typerror( 2, "index out of range" ); if ( index >= n ) vm.resettop(); else { @@ -317,6 +309,8 @@ public class BaseLib extends LFunction { } else if ( vm.checkstring(2).equals( "#" ) ) { vm.resettop(); vm.pushnumber( n - 2 ); + } else { + vm.typerror(2,"expected number or '#'"); } break; } @@ -346,8 +340,7 @@ public class BaseLib extends LFunction { load(vm); break; case TOSTRING: { - vm.checkany(2); - LValue v = vm.topointer(2); + LValue v = vm.checkany(2); vm.resettop(); vm.pushlvalue( v.luaAsString() ); break; @@ -374,6 +367,21 @@ public class BaseLib extends LFunction { return false; } + private static LValue getfunc (LuaState vm, boolean opt) { + if ( vm.isfunction(2) ) + return vm.tojavafunction(2); + else { + int level = opt? vm.optint(2, 1): vm.checkint(2); + vm.argcheck(level >= 0, 2, "level must be non-negative"); + vm.argcheck(level-1 <= vm.cc, 2, "invalid level"); + CallInfo ci = vm.getStackFrame(level-1); + if ( ci == null || ci.closure == null ) + return LNil.NIL; + return ci.closure; + } + } + + public static void redirectOutput( OutputStream newStdOut ) { STDOUT = new PrintStream( newStdOut ); } diff --git a/src/core/org/luaj/vm/LClosure.java b/src/core/org/luaj/vm/LClosure.java index e93ee0e7..3390d35e 100644 --- a/src/core/org/luaj/vm/LClosure.java +++ b/src/core/org/luaj/vm/LClosure.java @@ -49,9 +49,9 @@ public class LClosure extends LFunction { } /** Set the environment if a thread, or closure, and return 1, otherwise return 0 */ - public int luaSetEnv(LTable t) { + public boolean luaSetEnv(LTable t) { this.env = t; - return 1; + return true; } /** Get the enviroment for this closure */ diff --git a/src/core/org/luaj/vm/LFunction.java b/src/core/org/luaj/vm/LFunction.java index 0ae88ad4..3b202d8f 100644 --- a/src/core/org/luaj/vm/LFunction.java +++ b/src/core/org/luaj/vm/LFunction.java @@ -37,18 +37,6 @@ public class LFunction extends LValue { return Lua.LUA_TFUNCTION; } - /** - * Get the environment of the object if it is a closure, or d if not a closure. - * - * @param d global environment - * @return environment for this object, or d if it is not an LClosure - * @see LClosure - * @see LTable - */ - public LTable luaGetEnv(LTable d) { - return d; - } - /** * Set up a Java invocation, and leave the results on the stack * starting at base. The default implementation for LFunction diff --git a/src/core/org/luaj/vm/LThread.java b/src/core/org/luaj/vm/LThread.java index 95feddac..4b56cbbf 100644 --- a/src/core/org/luaj/vm/LThread.java +++ b/src/core/org/luaj/vm/LThread.java @@ -60,9 +60,9 @@ public class LThread extends LValue implements Runnable { } /** Set the environment if a thread, or closure, and return 1, otherwise return 0 */ - public int luaSetEnv(LTable t) { + public boolean luaSetEnv(LTable t) { threadVm._G = t; - return 1; + return true; } public String getStatus() { diff --git a/src/core/org/luaj/vm/LValue.java b/src/core/org/luaj/vm/LValue.java index 7d9b88fd..652e7974 100644 --- a/src/core/org/luaj/vm/LValue.java +++ b/src/core/org/luaj/vm/LValue.java @@ -292,10 +292,18 @@ public class LValue { return null; } - /** Set the environment if a thread, or closure, and return 1, otherwise return 0 */ - public int luaSetEnv(LTable t) { - return 0; + /** Set the environment if a thread, or closure, and return true, otherwise return false */ + public boolean luaSetEnv(LTable t) { + return false; } + + /** Get the environment of the object if it is a closure, or d if not a closure. + * @param d global environment to return if this is not a closure + */ + public LTable luaGetEnv(LTable d) { + return d; + } + /** Convert to a number if possible, or nil otherwise */ public LValue luaToNumber() { diff --git a/src/core/org/luaj/vm/LuaState.java b/src/core/org/luaj/vm/LuaState.java index c03f099d..6c9b5e94 100644 --- a/src/core/org/luaj/vm/LuaState.java +++ b/src/core/org/luaj/vm/LuaState.java @@ -1248,9 +1248,9 @@ public class LuaState extends Lua { * Pops a table from the stack and sets it as the new environment for the * value at the given index. If the value at the given index is neither a * function nor a thread nor a userdata, lua_setfenv - * returns 0. Otherwise it returns 1. + * returns false. Otherwise it returns true. */ - public int setfenv(int index) { + public boolean setfenv(int index) { LTable t = totable(-1); LValue f = topointer(index); pop(1); @@ -2661,11 +2661,13 @@ public class LuaState extends Lua { * Checks whether the function has an argument of any type (including nil) * at position narg. * @param narg the argument number + * @return the value at the index * @throws LuaErrorException if there is no argument at position narg */ - public void checkany(int narg) { + public LValue checkany(int narg) { if ( gettop() < narg ) argerror(narg, "value expected"); + return topointer(narg); } /** diff --git a/src/test/errors/baselibargs.lua b/src/test/errors/baselibargs.lua index 1ea1b8e2..155e321d 100644 --- a/src/test/errors/baselibargs.lua +++ b/src/test/errors/baselibargs.lua @@ -71,7 +71,7 @@ checkallerrors('pairs', {notatable}, 'bad argument #1') -- pcall banner('pcall') -checkallpass('pcall', {notanil,anylua}) +checkallpass('pcall', {notanil,anylua}, true) checkallerrors('pcall',{},'bad argument #1') -- print @@ -109,9 +109,12 @@ checkallerrors('select', {notanumber}, 'bad argument #1') -- setfenv banner('setfenv') +local g = _G checkallpass('setfenv', {{function()end},sometable}) -checkallerrors('setfenv', {{1.23, '1.33'},{getfenv()}}, 'cannot change environment of given object') -checkallerrors('setfenv', {{atable,athread,aboolean,astring},sometable}, 'bad argument #1') +checkallerrors('setfenv', {{-1, '-2'},{g}}, 'level must be non-negative') +checkallerrors('setfenv', {{10, '11'},{g}}, 'invalid level') +checkallerrors('setfenv', {{rawset},{g}}, 'cannot change environment of given object') +checkallerrors('setfenv', {{atable,athread,aboolean,astring},{g}}, 'bad argument #1') checkallerrors('setfenv', {notafunction}, 'bad argument #2') checkallerrors('setfenv', {anylua}, 'bad argument #2') checkallerrors('setfenv', {{function()end},notatable}, 'bad argument #2') @@ -119,7 +122,7 @@ checkallerrors('setfenv', {{function()end},notatable}, 'bad argument #2') -- setmetatable banner('setmetatable') checkallpass('setmetatable', {sometable,sometable}) -checkallpass('setmetatable', {sometable,{nil,atable},{'anchor'}}) +checkallpass('setmetatable', {sometable,{}}) checkallerrors('setmetatable',{notatable,sometable},'bad argument #1') checkallerrors('setmetatable',{sometable,notatable},'bad argument #2')