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
|
// return true if loaded, false if error put onto the stack
|
||||||
private static boolean loadis(LuaState vm, InputStream is, String chunkname ) {
|
static boolean loadis(LuaState vm, InputStream is, String chunkname ) {
|
||||||
try {
|
try {
|
||||||
vm.resettop();
|
vm.resettop();
|
||||||
if ( 0 != vm.load(is, chunkname) ) {
|
if ( 0 != vm.load(is, chunkname) ) {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ package org.luaj.lib;
|
|||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
import org.luaj.vm.CallInfo;
|
import org.luaj.vm.CallInfo;
|
||||||
import org.luaj.vm.LBoolean;
|
import org.luaj.vm.LBoolean;
|
||||||
@@ -33,10 +34,13 @@ import org.luaj.vm.LTable;
|
|||||||
import org.luaj.vm.LValue;
|
import org.luaj.vm.LValue;
|
||||||
import org.luaj.vm.Lua;
|
import org.luaj.vm.Lua;
|
||||||
import org.luaj.vm.LuaState;
|
import org.luaj.vm.LuaState;
|
||||||
|
import org.luaj.vm.Platform;
|
||||||
|
|
||||||
|
|
||||||
public class PackageLib extends LFunction {
|
public class PackageLib extends LFunction {
|
||||||
|
|
||||||
|
public static final String DEFAULT_LUA_PATH = "?.luac;?.lua";
|
||||||
|
|
||||||
public static InputStream STDIN = null;
|
public static InputStream STDIN = null;
|
||||||
public static PrintStream STDOUT = System.out;
|
public static PrintStream STDOUT = System.out;
|
||||||
public static LTable LOADED = new LTable();
|
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 _DOT = new LString(".");
|
||||||
private static final LString _EMPTY = new LString("");
|
private static final LString _EMPTY = new LString("");
|
||||||
private static final LString __INDEX = new LString("__index");
|
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 = {
|
private static final String[] NAMES = {
|
||||||
"package",
|
"package",
|
||||||
@@ -54,6 +63,9 @@ public class PackageLib extends LFunction {
|
|||||||
"require",
|
"require",
|
||||||
"loadlib",
|
"loadlib",
|
||||||
"seeall",
|
"seeall",
|
||||||
|
"preload_loader",
|
||||||
|
"lua_loader",
|
||||||
|
"java_loader",
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final int INSTALL = 0;
|
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 REQUIRE = 2;
|
||||||
private static final int LOADLIB = 3;
|
private static final int LOADLIB = 3;
|
||||||
private static final int SEEALL = 4;
|
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 ) {
|
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) );
|
globals.put( NAMES[i], new PackageLib(i) );
|
||||||
LTable pckg = new LTable();
|
pckg = new LTable();
|
||||||
for ( int i=LOADLIB; i<NAMES.length; i++ )
|
for ( int i=LOADLIB; i<=SEEALL; i++ )
|
||||||
pckg.put( NAMES[i], new PackageLib(i) );
|
pckg.put( NAMES[i], new PackageLib(i) );
|
||||||
globals.put( "package", pckg );
|
|
||||||
pckg.put( "loaded", LOADED );
|
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;
|
private final int id;
|
||||||
@@ -108,6 +131,18 @@ public class PackageLib extends LFunction {
|
|||||||
vm.resettop();
|
vm.resettop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PRELOAD_LOADER: {
|
||||||
|
loader_preload(vm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LUA_LOADER: {
|
||||||
|
loader_Lua(vm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case JAVA_LOADER: {
|
||||||
|
loader_Java(vm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
luaUnsupportedOperation();
|
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
|
* 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.
|
* the module, then require signals an error.
|
||||||
*/
|
*/
|
||||||
public static void require( LuaState vm ) {
|
public void require( LuaState vm ) {
|
||||||
LString modname = vm.tolstring(2);
|
LString name = vm.tolstring(2);
|
||||||
if ( LOADED.containsKey(modname) ) {
|
LValue loaded = LOADED.get(name);
|
||||||
|
if ( loaded.toJavaBoolean() ) {
|
||||||
|
if ( loaded == _SENTINEL )
|
||||||
|
vm.error("loop or previous error loading module '"+name+"'");
|
||||||
vm.resettop();
|
vm.resettop();
|
||||||
vm.pushlvalue( LOADED.get(modname) );
|
vm.pushlvalue( loaded );
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else {
|
vm.settop(0);
|
||||||
String s = modname.toJavaString();
|
|
||||||
if ( ! BaseLib.loadfile(vm, s+".luac") && ! BaseLib.loadfile(vm, s+".lua") )
|
/* else must load it; iterate over available loaders */
|
||||||
vm.error( "not found: "+s );
|
pckg.luaGetTable(vm, pckg, _LOADERS);
|
||||||
else if ( 0 == vm.pcall(0, 1, 0) ) {
|
if ( ! vm.istable(1) )
|
||||||
LValue result = vm.topointer( -1 );
|
vm.error( "'package.loaders' must be a table" );
|
||||||
if ( result != LNil.NIL )
|
Vector v = new Vector();
|
||||||
LOADED.put(modname, result);
|
for ( int i=1; true; i++ ) {
|
||||||
else if ( ! LOADED.containsKey(modname) )
|
vm.rawgeti(1, i);
|
||||||
LOADED.put(modname, result = LBoolean.TRUE);
|
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.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 ) {
|
public static void loadlib( LuaState vm ) {
|
||||||
vm.error( "loadlib not implemented" );
|
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 class LClosure extends LFunction {
|
||||||
public LTable env;
|
|
||||||
public LPrototype p;
|
public LPrototype p;
|
||||||
public UpVal[] upVals;
|
public UpVal[] upVals;
|
||||||
|
public LTable env;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated construct with environment instead
|
* @deprecated construct with environment instead
|
||||||
@@ -63,5 +64,4 @@ public class LClosure extends LFunction {
|
|||||||
this.env = t;
|
this.env = t;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,10 +40,10 @@ public class LThread extends LValue implements Runnable {
|
|||||||
|
|
||||||
private int status = STATUS_SUSPENDED;
|
private int status = STATUS_SUSPENDED;
|
||||||
|
|
||||||
private LuaState threadVm;
|
LuaState threadVm;
|
||||||
private Thread thread;
|
Thread thread;
|
||||||
|
|
||||||
private static LThread running;
|
static LThread running;
|
||||||
|
|
||||||
|
|
||||||
public LThread(LClosure c) {
|
public LThread(LClosure c) {
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ public class LuaState extends Lua {
|
|||||||
protected Stack upvals = new Stack();
|
protected Stack upvals = new Stack();
|
||||||
protected LFunction panic;
|
protected LFunction panic;
|
||||||
|
|
||||||
|
public static LTable DEFAULT_GLOBALS;
|
||||||
public LTable _G;
|
public LTable _G;
|
||||||
|
|
||||||
// main debug hook, overridden by DebugStackState
|
// 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()}
|
* @deprecated As of version 0.10, replaced by {@link #newState()}
|
||||||
*/
|
*/
|
||||||
public LuaState() {
|
public LuaState() {
|
||||||
_G = new LTable();
|
_G = DEFAULT_GLOBALS = new LTable();
|
||||||
_G.put("_G", _G);
|
_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;
|
_G = globals;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1364,11 +1370,9 @@ public class LuaState extends Lua {
|
|||||||
* href="#2.8">§2.8</a>).
|
* href="#2.8">§2.8</a>).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void getfield(int index, String k) {
|
public void getfield(int index, LString k) {
|
||||||
LTable t = totable(index);
|
LTable t = totable(index);
|
||||||
// TODO: what if this triggers metatable ops
|
t.luaGetTable(this, t, k);
|
||||||
// pushlvalue( t.luaGetTable(this, t, new LString(k)) );
|
|
||||||
t.luaGetTable(this, t, new LString(k));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2039,10 +2043,10 @@ public class LuaState extends Lua {
|
|||||||
* trigger a metamethod for the "newindex" event (see <a
|
* trigger a metamethod for the "newindex" event (see <a
|
||||||
* href="#2.8">§2.8</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);
|
LTable t = totable(index);
|
||||||
LValue v = poplvalue();
|
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();
|
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
|
// load the file
|
||||||
InputStream is = LuaRunner.class.getResourceAsStream( script );
|
InputStream is = LuaRunner.class.getResourceAsStream( script );
|
||||||
|
if ( is == null )
|
||||||
|
throw new java.io.FileNotFoundException( "not found: "+script );
|
||||||
LPrototype p = LoadState.undump(state, is, script);
|
LPrototype p = LoadState.undump(state, is, script);
|
||||||
|
|
||||||
// create closure and execute
|
// create closure and execute
|
||||||
|
|||||||
@@ -90,6 +90,10 @@ public class LuaJTest extends TestCase {
|
|||||||
runTest( "pcalls" );
|
runTest( "pcalls" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testRequire() throws IOException, InterruptedException {
|
||||||
|
runTest( "require" );
|
||||||
|
}
|
||||||
|
|
||||||
public void testSelect() throws IOException, InterruptedException {
|
public void testSelect() throws IOException, InterruptedException {
|
||||||
runTest( "select" );
|
runTest( "select" );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
-- unit tests for require() function
|
-- unit tests for require() function
|
||||||
local ids = {}
|
local ids = {}
|
||||||
|
local ti = table.insert
|
||||||
local function id(obj)
|
local function id(obj)
|
||||||
if not obj or type(obj) == 'number' or type(obj) == 'string' then
|
if not obj or type(obj) == 'number' or type(obj) == 'string' then
|
||||||
return obj
|
return obj
|
||||||
@@ -8,12 +9,13 @@ local function id(obj)
|
|||||||
if v then
|
if v then
|
||||||
return v
|
return v
|
||||||
end
|
end
|
||||||
table.insert(ids,obj)
|
ti(ids,obj)
|
||||||
ids[obj] = type(obj)..'.'..tostring(#ids)
|
ids[obj] = type(obj)..'.'..tostring(#ids)
|
||||||
return ids[obj]
|
return ids[obj]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- tests on require
|
-- tests on require
|
||||||
|
package.path='?.lua;src/test/res/?.lua'
|
||||||
function f( name )
|
function f( name )
|
||||||
print( module( 'testmod', package.seeall ) )
|
print( module( 'testmod', package.seeall ) )
|
||||||
print( 'before', id(sample), id(bogus), id(_G[name]) );
|
print( 'before', id(sample), id(bogus), id(_G[name]) );
|
||||||
@@ -33,7 +35,7 @@ f('bogus')
|
|||||||
print( 'main', id(sample), id(bogus), id(custom) );
|
print( 'main', id(sample), id(bogus), id(custom) );
|
||||||
|
|
||||||
-- custom loader chain
|
-- custom loader chain
|
||||||
for i=1,4 do
|
for i=1,3 do
|
||||||
print( i,id(package.loaders[i]) )
|
print( i,id(package.loaders[i]) )
|
||||||
end
|
end
|
||||||
function loader1( ... )
|
function loader1( ... )
|
||||||
|
|||||||
Reference in New Issue
Block a user