Implement require() using package.loaders, etc. add preload_loader, lua_loader, and java_loader.

This commit is contained in:
James Roseborough
2007-12-05 01:46:59 +00:00
parent 3c05880012
commit 7db4d54d50
8 changed files with 215 additions and 39 deletions

View File

@@ -397,8 +397,8 @@ public class BaseLib extends LFunction {
}
}
// return true if laoded, false if error put onto the stack
private static boolean loadis(LuaState vm, InputStream is, String chunkname ) {
// return true if loaded, false if error put onto the stack
static boolean loadis(LuaState vm, InputStream is, String chunkname ) {
try {
vm.resettop();
if ( 0 != vm.load(is, chunkname) ) {

View File

@@ -23,6 +23,7 @@ package org.luaj.lib;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Vector;
import org.luaj.vm.CallInfo;
import org.luaj.vm.LBoolean;
@@ -33,10 +34,13 @@ import org.luaj.vm.LTable;
import org.luaj.vm.LValue;
import org.luaj.vm.Lua;
import org.luaj.vm.LuaState;
import org.luaj.vm.Platform;
public class PackageLib extends LFunction {
public static final String DEFAULT_LUA_PATH = "?.luac;?.lua";
public static InputStream STDIN = null;
public static PrintStream STDOUT = System.out;
public static LTable LOADED = new LTable();
@@ -47,6 +51,11 @@ public class PackageLib extends LFunction {
private static final LString _DOT = new LString(".");
private static final LString _EMPTY = new LString("");
private static final LString __INDEX = new LString("__index");
private static final LString _LOADERS = new LString("loaders");
private static final LString _PRELOAD = new LString("preload");
private static final LString _PATH = new LString("path");
private static final LValue _SENTINEL = _EMPTY;
private static final LString _LUA_PATH = new LString(DEFAULT_LUA_PATH);
private static final String[] NAMES = {
"package",
@@ -54,6 +63,9 @@ public class PackageLib extends LFunction {
"require",
"loadlib",
"seeall",
"preload_loader",
"lua_loader",
"java_loader",
};
private static final int INSTALL = 0;
@@ -61,16 +73,27 @@ public class PackageLib extends LFunction {
private static final int REQUIRE = 2;
private static final int LOADLIB = 3;
private static final int SEEALL = 4;
private static final int PRELOAD_LOADER = 5;
private static final int LUA_LOADER = 6;
private static final int JAVA_LOADER = 7;
// all functions in package share this environment
private static LTable pckg;
public static void install( LTable globals ) {
for ( int i=1; i<LOADLIB; i++ )
for ( int i=1; i<=REQUIRE; i++ )
globals.put( NAMES[i], new PackageLib(i) );
LTable pckg = new LTable();
for ( int i=LOADLIB; i<NAMES.length; i++ )
pckg = new LTable();
for ( int i=LOADLIB; i<=SEEALL; i++ )
pckg.put( NAMES[i], new PackageLib(i) );
globals.put( "package", pckg );
pckg.put( "loaded", LOADED );
pckg.put( _PRELOAD, new LTable() );
LTable loaders = new LTable(3,0);
for ( int i=PRELOAD_LOADER; i<=JAVA_LOADER; i++ )
loaders.luaInsertPos(0, new PackageLib(i) );
pckg.put( "loaders", loaders );
pckg.put( _PATH, _LUA_PATH );
globals.put( "package", pckg );
}
private final int id;
@@ -108,6 +131,18 @@ public class PackageLib extends LFunction {
vm.resettop();
break;
}
case PRELOAD_LOADER: {
loader_preload(vm);
break;
}
case LUA_LOADER: {
loader_Lua(vm);
break;
}
case JAVA_LOADER: {
loader_Java(vm);
break;
}
default:
luaUnsupportedOperation();
}
@@ -236,29 +271,141 @@ public class PackageLib extends LFunction {
* If there is any error loading or running the module, or if it cannot find any loader for
* the module, then require signals an error.
*/
public static void require( LuaState vm ) {
LString modname = vm.tolstring(2);
if ( LOADED.containsKey(modname) ) {
public void require( LuaState vm ) {
LString name = vm.tolstring(2);
LValue loaded = LOADED.get(name);
if ( loaded.toJavaBoolean() ) {
if ( loaded == _SENTINEL )
vm.error("loop or previous error loading module '"+name+"'");
vm.resettop();
vm.pushlvalue( LOADED.get(modname) );
vm.pushlvalue( loaded );
return;
}
else {
String s = modname.toJavaString();
if ( ! BaseLib.loadfile(vm, s+".luac") && ! BaseLib.loadfile(vm, s+".lua") )
vm.error( "not found: "+s );
else if ( 0 == vm.pcall(0, 1, 0) ) {
LValue result = vm.topointer( -1 );
if ( result != LNil.NIL )
LOADED.put(modname, result);
else if ( ! LOADED.containsKey(modname) )
LOADED.put(modname, result = LBoolean.TRUE);
vm.settop(0);
/* else must load it; iterate over available loaders */
pckg.luaGetTable(vm, pckg, _LOADERS);
if ( ! vm.istable(1) )
vm.error( "'package.loaders' must be a table" );
Vector v = new Vector();
for ( int i=1; true; i++ ) {
vm.rawgeti(1, i);
if ( vm.isnil(-1) ) {
vm.error( "module '"+name+"' not found: "+v );
}
/* call loader with module name as argument */
vm.pushlstring(name);
vm.call(1, 1);
if ( vm.isfunction(-1) )
break; /* module loaded successfully */
if ( vm.isstring(-1) ) /* loader returned error message? */
v.addElement(vm.tolstring(-1)); /* accumulate it */
vm.pop(1);
}
// load the module using the loader
LOADED.luaSetTable(vm, LOADED, name, _SENTINEL);
vm.pushlstring( name ); /* pass name as argument to module */
vm.call( 1, 1 ); /* run loaded module */
if ( ! vm.isnil(-1) ) /* non-nil return? */
LOADED.luaSetTable(vm, LOADED, name, vm.topointer(-1) ); /* _LOADED[name] = returned value */
vm.resettop();
vm.pushlvalue( result );
}
LOADED.luaGetTable(vm, LOADED, name);
if ( vm.topointer(-1) == _SENTINEL ) { /* module did not set a value? */
LOADED.luaSetTable(vm, LOADED, name, LBoolean.TRUE ); /* _LOADED[name] = true */
vm.resettop();
vm.pushboolean(true);
}
}
public static void loadlib( LuaState vm ) {
vm.error( "loadlib not implemented" );
}
private void loader_preload( LuaState vm ) {
LString name = vm.tolstring(2);
pckg.luaGetTable(vm, pckg, _PRELOAD);
if ( ! vm.istable(-1) )
vm.error("package.preload '"+name+"' must be a table");
LTable preload = vm.totable(-1);
preload.luaGetTable(vm, preload, name);
if ( vm.isnil(-1) )
vm.pushstring("\n\tno field package.preload['"+name+"']");
vm.insert(1);
vm.settop(1);
}
private void loader_Lua( LuaState vm ) {
String name = vm.tostring(2);
InputStream is = findfile( vm, name, _PATH );
if ( is != null ) {
String filename = vm.tostring(-1);
if ( ! BaseLib.loadis(vm, is, filename) )
loaderror( vm, filename );
}
vm.insert(1);
vm.settop(1);
}
private void loader_Java( LuaState vm ) {
String name = vm.tostring(2);
Class c = null;
LValue v = null;
try {
c = Class.forName(name);
v = (LValue) c.newInstance();
vm.pushlvalue( v );
} catch ( ClassNotFoundException cnfe ) {
vm.pushstring("\n\tno class '"+name+"'" );
} catch ( Exception e ) {
vm.pushstring("\n\tjava load failed on '"+name+"', "+e );
}
vm.insert(1);
vm.settop(1);
}
private InputStream findfile(LuaState vm, String name, LString pname) {
Platform p = Platform.getInstance();
pckg.luaGetTable(vm, pckg, pname);
if ( ! vm.isstring(-1) )
vm.error("package."+pname+" must be a string");
String path = vm.tostring(-1);
int te = -1;
int n = path.length();
StringBuffer sb = null;
while ( te < n ) {
// find next template
int tb = te+1;
te = path.indexOf(';',tb);
if ( te < 0 )
te = path.length();
String template = path.substring(tb,te);
// create filename
int ques = template.indexOf('?');
String filename = (ques<0? template: template.substring(0,ques)+name+template.substring(ques+1));
// try opening the file
InputStream is = p.openFile(filename);
if ( is != null ) {
vm.pushstring(filename);
return is;
}
// report error
if ( sb == null )
sb = new StringBuffer();
sb.append( "\n\tno file '"+filename+"'");
}
// not found, push error on stack and return
vm.pushstring(sb.toString());
return null;
}
private static void loaderror(LuaState vm, String filename) {
vm.error( "error loading module '"+vm.tostring(1)+"' from file '"+filename+"':\n\t"+vm.tostring(-1) );
}
}

View File

@@ -24,9 +24,10 @@ package org.luaj.vm;
public class LClosure extends LFunction {
public LTable env;
public LPrototype p;
public UpVal[] upVals;
public LTable env;
/**
* @deprecated construct with environment instead
@@ -63,5 +64,4 @@ public class LClosure extends LFunction {
this.env = t;
return 1;
}
}

View File

@@ -40,10 +40,10 @@ public class LThread extends LValue implements Runnable {
private int status = STATUS_SUSPENDED;
private LuaState threadVm;
private Thread thread;
LuaState threadVm;
Thread thread;
private static LThread running;
static LThread running;
public LThread(LClosure c) {

View File

@@ -96,6 +96,7 @@ public class LuaState extends Lua {
protected Stack upvals = new Stack();
protected LFunction panic;
public static LTable DEFAULT_GLOBALS;
public LTable _G;
// main debug hook, overridden by DebugStackState
@@ -120,11 +121,16 @@ public class LuaState extends Lua {
* @deprecated As of version 0.10, replaced by {@link #newState()}
*/
public LuaState() {
_G = new LTable();
_G = DEFAULT_GLOBALS = new LTable();
_G.put("_G", _G);
}
public LuaState(LTable globals) {
/**
* Create a LuaState with a specific global environment. Used by LThread.
*
* @param globals the LTable to use as globals for this LuaState
*/
LuaState(LTable globals) {
_G = globals;
}
@@ -1364,11 +1370,9 @@ public class LuaState extends Lua {
* href="#2.8">&sect;2.8</a>).
*
*/
public void getfield(int index, String k) {
public void getfield(int index, LString k) {
LTable t = totable(index);
// TODO: what if this triggers metatable ops
// pushlvalue( t.luaGetTable(this, t, new LString(k)) );
t.luaGetTable(this, t, new LString(k));
t.luaGetTable(this, t, k);
}
/**
@@ -2039,10 +2043,10 @@ public class LuaState extends Lua {
* trigger a metamethod for the "newindex" event (see <a
* href="#2.8">&sect;2.8</a>).
*/
public void setfield(int index, String k) {
public void setfield(int index, LString k) {
LTable t = totable(index);
LValue v = poplvalue();
t.luaSetTable(this, t, new LString(k), v);
t.luaSetTable(this, t, k, v);
}
/**
@@ -2570,4 +2574,21 @@ public class LuaState extends Lua {
return topointer(index).toJavaBoxedLong();
}
/**
* Get the current environment.
*
* @return LTable the current environment
*/
// public LTable curr_env() {
// LuaState vm = (LThread.running!=null?
// LThread.running.threadVm:
// this);
// for ( int i=vm.cc; i>=0; i-- ) {
// LClosure c = vm.calls[cc].closure;
// if ( c != null )
// return c.env;
// }
// return _G;
// }
}

View File

@@ -54,6 +54,8 @@ public class LuaRunner {
// load the file
InputStream is = LuaRunner.class.getResourceAsStream( script );
if ( is == null )
throw new java.io.FileNotFoundException( "not found: "+script );
LPrototype p = LoadState.undump(state, is, script);
// create closure and execute

View File

@@ -90,6 +90,10 @@ public class LuaJTest extends TestCase {
runTest( "pcalls" );
}
public void testRequire() throws IOException, InterruptedException {
runTest( "require" );
}
public void testSelect() throws IOException, InterruptedException {
runTest( "select" );
}

View File

@@ -1,5 +1,6 @@
-- unit tests for require() function
local ids = {}
local ti = table.insert
local function id(obj)
if not obj or type(obj) == 'number' or type(obj) == 'string' then
return obj
@@ -8,12 +9,13 @@ local function id(obj)
if v then
return v
end
table.insert(ids,obj)
ti(ids,obj)
ids[obj] = type(obj)..'.'..tostring(#ids)
return ids[obj]
end
-- tests on require
package.path='?.lua;src/test/res/?.lua'
function f( name )
print( module( 'testmod', package.seeall ) )
print( 'before', id(sample), id(bogus), id(_G[name]) );
@@ -33,7 +35,7 @@ f('bogus')
print( 'main', id(sample), id(bogus), id(custom) );
-- custom loader chain
for i=1,4 do
for i=1,3 do
print( i,id(package.loaders[i]) )
end
function loader1( ... )