diff --git a/src/core/org/luaj/lib/BaseLib.java b/src/core/org/luaj/lib/BaseLib.java index 7c87600e..f6e589fd 100644 --- a/src/core/org/luaj/lib/BaseLib.java +++ b/src/core/org/luaj/lib/BaseLib.java @@ -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; i2? 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) { diff --git a/src/core/org/luaj/vm/LClosure.java b/src/core/org/luaj/vm/LClosure.java index 3bd7baa5..15fa6b60 100644 --- a/src/core/org/luaj/vm/LClosure.java +++ b/src/core/org/luaj/vm/LClosure.java @@ -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; + } + } diff --git a/src/core/org/luaj/vm/LThread.java b/src/core/org/luaj/vm/LThread.java index d1a21329..db11f047 100644 --- a/src/core/org/luaj/vm/LThread.java +++ b/src/core/org/luaj/vm/LThread.java @@ -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]; } diff --git a/src/core/org/luaj/vm/LValue.java b/src/core/org/luaj/vm/LValue.java index 593a6934..1922dca0 100644 --- a/src/core/org/luaj/vm/LValue.java +++ b/src/core/org/luaj/vm/LValue.java @@ -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; + } + } diff --git a/src/core/org/luaj/vm/LuaState.java b/src/core/org/luaj/vm/LuaState.java index b2ec26a1..45dd0264 100644 --- a/src/core/org/luaj/vm/LuaState.java +++ b/src/core/org/luaj/vm/LuaState.java @@ -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); } diff --git a/src/sample/org/luaj/sample/LuaRunner.java b/src/sample/org/luaj/sample/LuaRunner.java index ceaefc92..363fbe05 100644 --- a/src/sample/org/luaj/sample/LuaRunner.java +++ b/src/sample/org/luaj/sample/LuaRunner.java @@ -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 ); diff --git a/src/test/java/org/luaj/vm/LuaJTest.java b/src/test/java/org/luaj/vm/LuaJTest.java index 8fbe079e..f656c0eb 100644 --- a/src/test/java/org/luaj/vm/LuaJTest.java +++ b/src/test/java/org/luaj/vm/LuaJTest.java @@ -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 ); diff --git a/src/test/res/baselib.lua b/src/test/res/baselib.lua new file mode 100644 index 00000000..dd172697 --- /dev/null +++ b/src/test/res/baselib.lua @@ -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 ) +--]] diff --git a/src/test/res/baselib.luac b/src/test/res/baselib.luac new file mode 100644 index 00000000..198e2f37 Binary files /dev/null and b/src/test/res/baselib.luac differ diff --git a/src/test/res/setfenv.lua b/src/test/res/setfenv.lua new file mode 100644 index 00000000..ba5b48bb --- /dev/null +++ b/src/test/res/setfenv.lua @@ -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 ) diff --git a/src/test/res/setfenv.luac b/src/test/res/setfenv.luac new file mode 100644 index 00000000..06c06558 Binary files /dev/null and b/src/test/res/setfenv.luac differ