Add unit tests for part of base library plus fixes to [gs]etfenv, [gs]etmetatable

This commit is contained in:
James Roseborough
2007-11-16 00:41:43 +00:00
parent 31abaacec1
commit eef469c715
11 changed files with 223 additions and 65 deletions

View File

@@ -47,6 +47,7 @@ public class BaseLib extends LFunction {
"tonumber",
"rawget",
"rawset",
"getfenv",
"setfenv",
"select",
"collectgarbage",
@@ -72,15 +73,16 @@ public class BaseLib extends LFunction {
private static final int TONUMBER = 11;
private static final int RAWGET = 12;
private static final int RAWSET = 13;
private static final int SETFENV = 14;
private static final int SELECT = 15;
private static final int COLLECTGARBAGE = 16;
private static final int DOFILE = 17;
private static final int LOADSTRING = 18;
private static final int LOAD = 19;
private static final int TOSTRING = 20;
private static final int UNPACK = 21;
private static final int NEXT = 22;
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 NEXT = 23;
public static void install(LTable globals) {
for ( int i=1; i<NAMES.length; i++ )
@@ -130,20 +132,26 @@ public class BaseLib extends LFunction {
break;
}
case GETMETATABLE: {
if ( 0 == vm.getmetatable(2) )
if ( 0 == vm.getmetatable(2) ) {
vm.settop(0);
else {
vm.pushnil();
} else {
vm.insert(1);
vm.settop(1);
}
break;
}
case SETMETATABLE: {
if ( ! vm.istable(2) )
vm.error("bad argument #1 to '?' (table expected, got "+vm.typename(2)+")");
vm.setmetatable(2);
vm.remove(1);
break;
}
case TYPE: {
// TODO: generalize, compute location insofar as possible
if ( vm.gettop() < 2 )
vm.error("bad argument #1 to '?' (value expected)");
LValue v = vm.topointer(2);
vm.settop(0);
vm.pushlstring( v.luaGetTypeName() );
@@ -163,7 +171,7 @@ public class BaseLib extends LFunction {
break;
}
case ERROR: {
vm.error(vm.tostring(2), vm.gettop()>2? vm.tointeger(3): 1);
vm.error(vm.tostring(2), vm.gettop()>=3? vm.tointeger(3): 1);
break;
}
case ASSERT: {
@@ -211,9 +219,38 @@ public class BaseLib extends LFunction {
vm.error( "expected table" );
}
} break;
case SETFENV:
setfenv( (LuaState) vm );
case GETFENV: {
if ( vm.gettop() <= 1 ) {
vm.pushlvalue(vm._G);
} else {
if ( ! vm.isfunction(2) ) {
int i = (vm.isnil(2)? 1: vm.tointeger(2));
vm.pushlvalue( vm.getStackFrame(i).closure );
}
vm.getfenv(-1);
}
vm.insert(1);
vm.settop(1);
break;
}
case SETFENV: {
LTable t = vm.totable(-1);
if ( vm.setfenv(2) != 0 ) {
vm.remove(1);
break;
}
int i = vm.tointeger(2);
if ( i == 0 ) {
vm._G = t;
vm.settop(0);
} else {
LClosure c = vm.getStackFrame(i-1).closure;
c.luaSetEnv(t);
vm.settop(0);
vm.pushlvalue(c);
}
break;
}
case SELECT:
select( vm );
break;
@@ -269,43 +306,6 @@ public class BaseLib extends LFunction {
}
}
private void setfenv( LuaState state ) {
LValue f = state.topointer(2);
LValue newenv = state.topointer(3);
LClosure c = null;
// Lua reference manual says that first argument, f, can be a "Lua
// function" or an integer. Lots of things extend LFunction, but only
// instances of Closure are "Lua functions".
if ( f instanceof LClosure ) {
c = (LClosure) f;
} else {
int callStackDepth = f.toJavaInt();
if ( callStackDepth > 0 ) {
CallInfo frame = state.getStackFrame( callStackDepth );
if ( frame != null ) {
c = frame.closure;
}
} else {
// This is supposed to set the environment of the current
// "thread". But, we have not implemented coroutines yet.
throw new LuaErrorException( "not implemented" );
}
}
if ( c != null ) {
if ( newenv instanceof LTable ) {
c.env = (LTable) newenv;
}
state.settop(0);
state.pushlvalue( c );
return;
}
state.settop(0);
return;
}
// closes the input stream, provided its not null or System.in
private static void closeSafely(InputStream is) {

View File

@@ -57,4 +57,11 @@ public class LClosure extends LFunction {
vm.prepStackCall();
return true;
}
/** Set the environment if a thread, or closure, and return 1, otherwise return 0 */
public int luaSetEnv(LTable t) {
this.env = t;
return 1;
}
}

View File

@@ -59,6 +59,12 @@ public class LThread extends LValue implements Runnable {
return "thread: "+hashCode();
}
/** Set the environment if a thread, or closure, and return 1, otherwise return 0 */
public int luaSetEnv(LTable t) {
threadVm._G = t;
return 1;
}
public String getStatus() {
return NAMES[status];
}

View File

@@ -297,4 +297,9 @@ 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;
}
}

View File

@@ -958,8 +958,8 @@ public class LuaState extends Lua {
* after filling line number information first when level > 0.
*/
public void error(String message, int level) {
if ( level > 0 )
message = getFileLine(cc + 1 - level) + ": " + message;
if ( level > 1 )
message = getFileLine(cc + 2 - level) + ": " + message;
throw new LuaErrorException( message );
}
@@ -1117,7 +1117,8 @@ public class LuaState extends Lua {
* index.
*/
public void getfenv(int index) {
notImplemented();
LValue f = topointer(index);
pushlvalue( ((LClosure) f).env );
}
/**
@@ -1130,8 +1131,10 @@ public class LuaState extends Lua {
* returns 0. Otherwise it returns 1.
*/
public int setfenv(int index) {
notImplemented();
return 0;
LTable t = totable(-1);
LValue f = topointer(index);
pop(1);
return f.luaSetEnv(t);
}

View File

@@ -24,15 +24,9 @@ package org.luaj.sample;
import java.io.IOException;
import java.io.InputStream;
import org.luaj.lib.CoroutineLib;
import org.luaj.lib.MathLib;
import org.luaj.lib.PackageLib;
import org.luaj.lib.StringLib;
import org.luaj.lib.TableLib;
import org.luaj.lib.j2se.LuajavaLib;
import org.luaj.compiler.LuaC;
import org.luaj.vm.LClosure;
import org.luaj.vm.LPrototype;
import org.luaj.vm.LTable;
import org.luaj.vm.LValue;
import org.luaj.vm.LoadState;
import org.luaj.vm.LuaState;
@@ -56,6 +50,7 @@ public class LuaRunner {
// add standard bindings
state.installStandardLibs();
LuaC.install();
// load the file
InputStream is = LuaRunner.class.getResourceAsStream( script );

View File

@@ -6,13 +6,14 @@ import java.io.OutputStream;
import junit.framework.TestCase;
import org.luaj.compiler.LuaC;
import org.luaj.debug.DebugLuaState;
import org.luaj.lib.BaseLib;
import org.luaj.lib.j2se.LuajavaLib;
public class LuaJTest extends TestCase {
public void testTest1() throws IOException, InterruptedException {
runTest( "test1" );
}
@@ -45,6 +46,10 @@ public class LuaJTest extends TestCase {
runTest( "autoload" );
}
public void testBaseLib() throws IOException, InterruptedException {
runTest( "baselib" );
}
public void testBoolean() throws IOException, InterruptedException {
runTest( "boolean" );
}
@@ -81,6 +86,10 @@ public class LuaJTest extends TestCase {
runTest( "select" );
}
public void testSetfenv() throws IOException, InterruptedException {
runTest( "setfenv" );
}
public void testSetlist() throws IOException, InterruptedException {
runTest( "setlist" );
}
@@ -108,7 +117,7 @@ public class LuaJTest extends TestCase {
public void testUpvalues2() throws IOException, InterruptedException {
runTest( "upvalues2" );
}
//*/
private void runTest( String testName ) throws IOException, InterruptedException {
// new lua state
@@ -119,6 +128,7 @@ public class LuaJTest extends TestCase {
// add luajava
LuajavaLib.install( state._G );
LuaC.install();
// load the file
LPrototype p = loadScriptResource( state, testName );

92
src/test/res/baselib.lua Normal file
View File

@@ -0,0 +1,92 @@
-- unit tests for functions in BaseLib.java
-- assert
print( 'assert(true)', assert(true) )
print( 'pcall(assert,true)', pcall(assert,true) )
print( 'pcall(assert,false)', pcall(assert,false) )
print( 'pcall(assert,nil)', pcall(assert,nil) )
print( 'pcall(assert,true,"msg")', pcall(assert,true,"msg") )
print( 'pcall(assert,false,"msg")', pcall(assert,false,"msg") )
print( 'pcall(assert,nil,"msg")', pcall(assert,nil,"msg") )
print( 'pcall(assert,false,"msg","msg2")', pcall(assert,false,"msg","msg2") )
-- collectgarbage (not supported)
-- dofile (not supported)
-- error
print( 'pcall(error)', pcall(error) )
print( 'pcall(error,"msg")', pcall(error,"msg") )
print( 'pcall(error,"msg",1)', pcall(error,"msg",1) )
print( 'pcall(error,"msg",2)', pcall(error,"msg",2) )
local function le(level)
error("msg",level)
end
function ge(level)
error("msg",level)
end
for i = 0,4 do
print( 'pcall(le,i)', i, pcall(le,i) )
print( 'pcall(ge,i)', i, pcall(ge,i) )
end
-- _G
print( '_G["abc"] (before)', _G["abc"] )
abc='def'
print( '_G["abc"] (after)', _G["abc"] )
-- type
print( 'type(nil)', type(nil) )
print( 'type("a")', type("a") )
print( 'type(1)', type(1) )
print( 'type(1.5)', type(1.5) )
print( 'type(function() end)', type(function() end) )
print( 'type({})', type({}) )
print( 'type(true)', type(true) )
print( 'type(false)', type(false) )
print( 'pcall(type,type)', pcall(type,type) )
print( 'pcall(type)', pcall(type) )
print( '(function() return pcall(type) end)()', (function() return pcall(type) end)() )
local function la() return pcall(type) end
print( 'la()', la() )
function ga() return pcall(type) end
print( 'ga()', ga() )
-- getfenv, setfenv: tested in setfenv.lua
-- getmetatable, setmetatable
ta = { aa1="aaa1", aa2="aaa2" }
tb = { bb1="bbb1", bb2="bbb2" }
print( 'getmetatable(ta)', getmetatable(ta) )
print( 'getmetatable(tb)', getmetatable(tb) )
print( 'setmetatable(ta),{cc1="ccc1"}', type( setmetatable(ta,{cc1="ccc1"}) ) )
print( 'setmetatable(tb),{dd1="ddd1"}', type( setmetatable(tb,{dd1="ddd1"}) ) )
print( 'getmetatable(ta)["cc1"]', getmetatable(ta)["cc1"] )
print( 'getmetatable(tb)["dd1"]', getmetatable(tb)["dd1"] )
print( 'getmetatable(1)', getmetatable(1) )
print( 'pcall(setmetatable,1)', pcall(setmetatable,1) )
print( 'pcall(setmetatable,nil)', pcall(setmetatable,nil) )
print( 'pcall(setmetatable,"ABC")', pcall(setmetatable,"ABC") )
print( 'pcall(setmetatable,function() end)', pcall(setmetatable,function() end) )
-- ipairs
-- load
-- loadfile
-- loadstring
-- next
-- pairs
-- pcall
-- print
-- rawget
-- rawset
-- select
-- tonumber
-- tostring
-- unpack
--[[
print( 'pcall(unpack)', pcall(unpack) );
print( 'unpack({"aa"})', unpack({"aa"}) );
print( 'unpack({"aa","bb"})', unpack({"aa","bb"}) );
print( 'unpack({"aa","bb","cc"})', unpack({"aa","bb","cc"}) );
-- _VERSION
print( '_VERSION', _VERSION )
--]]

BIN
src/test/res/baselib.luac Normal file

Binary file not shown.

40
src/test/res/setfenv.lua Normal file
View File

@@ -0,0 +1,40 @@
-- unit tests for getfenv, setfenv
local function f3(level,value)
if value then
setfenv( level, {setfenv=setfenv, abc=value} )
end
return abc
end
local function f2(...)
local r = f3(...)
return abc,r
end
local function f1(...)
local r,s = f2(...)
return abc,r,s
end
print( 'getfenv,setfenv - before')
print( 'getfenv(f1)["abc"]', getfenv(f1)["abc"] )
print( 'getfenv(f2)["abc"]', getfenv(f2)["abc"] )
print( 'getfenv(f3)["abc"]', getfenv(f3)["abc"] )
print( 'getfenv()["abc"]', getfenv()["abc"] )
print( 'abc,f1()', abc,f1() )
setfenv(f1,{setfenv=setfenv, abc='ghi'})
setfenv(f2,{setfenv=setfenv, abc='jkl'})
setfenv(f3,{setfenv=setfenv, abc='mno'})
print( 'getfenv,setfenv - after')
print( 'getfenv(f1)["abc"]', getfenv(f1)["abc"] )
print( 'getfenv(f2)["abc"]', getfenv(f2)["abc"] )
print( 'getfenv(f3)["abc"]', getfenv(f3)["abc"] )
print( 'getfenv()["abc"]', getfenv()["abc"] )
print( 'abc,f1()', abc,f1() )
print( 'abc,f1(1,"pqr")', abc,f1(1,"pqr") )
print( 'abc,f1(2,"stu")', abc,f1(2,"stu") )
print( 'abc,f1(3,"vwx")', abc,f1(3,"vwx") )
print( 'abc', abc )
local c = coroutine.create( function()
print( 'pcall(f1,0,"abc")', pcall(f1,0,"234") )
end )
print( 'resume', coroutine.resume(c) )
print( 'abc,pcall(f1)', abc,pcall(f1) )
print( 'abc (out)', abc )

BIN
src/test/res/setfenv.luac Normal file

Binary file not shown.