Implement require() using package.loaders, etc. add preload_loader, lua_loader, and java_loader.
This commit is contained in:
@@ -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) ) {
|
||||
|
||||
@@ -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) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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">§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">§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;
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" );
|
||||
}
|
||||
|
||||
@@ -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( ... )
|
||||
|
||||
Reference in New Issue
Block a user