Convert package lib to new style.
This commit is contained in:
@@ -27,7 +27,10 @@ import java.io.PrintStream;
|
||||
import org.luaj.vm2.LoadState.LuaCompiler;
|
||||
import org.luaj.vm2.lib.BaseLib;
|
||||
import org.luaj.vm2.lib.DebugLib;
|
||||
import org.luaj.vm2.lib.PackageLib;
|
||||
import org.luaj.vm2.lib.ResourceFinder;
|
||||
import org.luaj.vm2.lib.jme.JmePlatform;
|
||||
import org.luaj.vm2.lib.jse.JsePlatform;
|
||||
|
||||
/**
|
||||
* Global environment used by luaj.
|
||||
@@ -70,6 +73,8 @@ public class Globals extends LuaTable {
|
||||
public LuaThread running_thread = new LuaThread(this);
|
||||
|
||||
public DebugLib debuglib;
|
||||
|
||||
public PackageLib package_;
|
||||
|
||||
public Globals checkglobals() {
|
||||
return this;
|
||||
|
||||
@@ -1462,6 +1462,13 @@ public class LuaValue extends Varargs {
|
||||
*/
|
||||
public LuaValue call(LuaValue arg) { return callmt().call(this,arg); }
|
||||
|
||||
/** Convenience function which calls a luavalue with a single, string argument.
|
||||
* @param arg String argument to the function. This will be converted to a LuaString.
|
||||
* @return return value of the invocation.
|
||||
* @see #call(LuaValue)
|
||||
*/
|
||||
public LuaValue call(String arg) { return call(valueOf(arg)); }
|
||||
|
||||
/** Call {@link this} with 2 arguments, including metatag processing,
|
||||
* and return only the first return value.
|
||||
* <p>
|
||||
|
||||
@@ -40,20 +40,19 @@ import org.luaj.vm2.Varargs;
|
||||
* To instantiate and use it directly,
|
||||
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
|
||||
* <pre> {@code
|
||||
* LuaTable _G = new LuaTable();
|
||||
* LuaThread.setGlobals(_G);
|
||||
* Globals _G = new Globals();
|
||||
* _G.load(new BaseLib());
|
||||
* _G.load(new PackageLib());
|
||||
* System.out.println( _G.get("require").call(LuaValue.valueOf("hyperbolic")) );
|
||||
* System.out.println( _G.package_.require.call"foo") );
|
||||
* } </pre>
|
||||
* In practice, the first 4 lines of the above are minimal requirements to get
|
||||
* and initialize a globals table capable of basic reqire, print, and other functions,
|
||||
* In practice, the first 3 lines of the above are minimal requirements to get
|
||||
* and initialize a globals table capable of basic require, print, and other functions,
|
||||
* so it is much more convenient to use the {@link JsePlatform} and {@link JmePlatform}
|
||||
* utility classes instead.
|
||||
* <p>
|
||||
* This has been implemented to match as closely as possible the behavior in the corresponding library in C.
|
||||
* However, the default filesystem search semantics are different and delegated to the bas library
|
||||
* as outlined in the {@link BaseLib} and {@link JseBaseLib} documetnation.
|
||||
* as outlined in the {@link BaseLib} and {@link JseBaseLib} documentation.
|
||||
* @see LibFunction
|
||||
* @see BaseLib
|
||||
* @see JseBaseLib
|
||||
@@ -63,123 +62,82 @@ import org.luaj.vm2.Varargs;
|
||||
*/
|
||||
public class PackageLib extends OneArgFunction {
|
||||
|
||||
public static String DEFAULT_LUA_PATH = "?.lua";
|
||||
/** The default value to use for package.path. This can be set with the system property
|
||||
* "luaj.package.path", and is "?.lua" by default. */
|
||||
public static String DEFAULT_LUA_PATH = System.getProperty("luaj.package.path", "?.lua");
|
||||
|
||||
/** The globals that were used to load this library. */
|
||||
Globals globals;
|
||||
|
||||
public LuaTable LOADED;
|
||||
public LuaTable PACKAGE;
|
||||
|
||||
/** Loader that loads from preload table if found there */
|
||||
public LuaValue preload_searcher;
|
||||
/** The table used by require to check for loaded modules, and exposed initially as package.loaded. */
|
||||
public LuaTable loaded;
|
||||
|
||||
/** The table used by the {@link preload_loader}, and exposed initially as package.preload. */
|
||||
public LuaTable preload;
|
||||
|
||||
/** Loader that loads as a lua script using the LUA_PATH */
|
||||
public LuaValue lua_searcher;
|
||||
/** The value in use as the package path, and set as the initial value of package.path. */
|
||||
public LuaString path;
|
||||
|
||||
/** Loader that loads as a Java class. Class must have public constructor and be a LuaValue */
|
||||
public LuaValue java_searcher;
|
||||
/** The loadlib function used by the package library. */
|
||||
public loadlib loadlib;
|
||||
|
||||
/** The searchpath function used by the package library. */
|
||||
public searchpath searchpath;
|
||||
|
||||
/** The initial searchers list, and the list exposed initially as package.searchers */
|
||||
public LuaTable searchers;
|
||||
|
||||
private static final LuaString _LOADED = valueOf("loaded");
|
||||
private static final LuaString _LOADLIB = valueOf("loadlib");
|
||||
private static final LuaString _PRELOAD = valueOf("preload");
|
||||
private static final LuaString _PATH = valueOf("path");
|
||||
private static final LuaString _SEARCHERS = valueOf("searchers");
|
||||
private static final LuaString _SEARCHPATH = valueOf("searchpath");
|
||||
/** Loader that loads from {@link preload} table if found there */
|
||||
public preload_searcher preload_searcher;
|
||||
|
||||
/** Loader that loads as a lua script using the lua path currently in {@link path} */
|
||||
public lua_searcher lua_searcher;
|
||||
|
||||
/** Loader that loads as a Java class. Class must have public constructor and be a LuaValue. */
|
||||
public java_searcher java_searcher;
|
||||
|
||||
private static final LuaString _SENTINEL = valueOf("\u0001");
|
||||
|
||||
private static final int OP_REQUIRE = 0;
|
||||
private static final int OP_LOADLIB = 1;
|
||||
private static final int OP_SEARCHPATH = 2;
|
||||
private static final int OP_PRELOAD_SEARCHER = 3;
|
||||
private static final int OP_LUA_SEARCHER = 4;
|
||||
private static final int OP_JAVA_SEARCHER = 5;
|
||||
|
||||
private static final String FILE_SEP = System.getProperty("file.separator");
|
||||
|
||||
public PackageLib() {}
|
||||
|
||||
public LuaValue call(LuaValue env) {
|
||||
globals = env.checkglobals();
|
||||
env.set("require", new PkgLib1("require",OP_REQUIRE,this));
|
||||
env.set( "package", PACKAGE=tableOf( new LuaValue[] {
|
||||
_LOADED, LOADED=tableOf(),
|
||||
_PRELOAD, tableOf(),
|
||||
_PATH, valueOf(DEFAULT_LUA_PATH),
|
||||
_LOADLIB, new PkgLibV("loadlib",OP_LOADLIB,this),
|
||||
_SEARCHPATH, new PkgLibV("searchpath",OP_SEARCHPATH,this),
|
||||
_SEARCHERS, listOf(new LuaValue[] {
|
||||
preload_searcher = new PkgLibV("preload_searcher",OP_PRELOAD_SEARCHER, this),
|
||||
lua_searcher = new PkgLibV("lua_searcher",OP_LUA_SEARCHER, this),
|
||||
java_searcher = new PkgLibV("java_searcher",OP_JAVA_SEARCHER, this),
|
||||
}) }) );
|
||||
LOADED.set("package", PACKAGE);
|
||||
env.set("require", new require());
|
||||
LuaTable package_ = new LuaTable();
|
||||
package_.set("loaded", loaded = new LuaTable());
|
||||
package_.set("preload", preload = new LuaTable());
|
||||
package_.set("path", path = LuaValue.valueOf(DEFAULT_LUA_PATH));
|
||||
package_.set("loadlib", loadlib = new loadlib());
|
||||
package_.set("searchpath", searchpath = new searchpath());
|
||||
searchers = new LuaTable();
|
||||
searchers.set(1, preload_searcher = new preload_searcher());
|
||||
searchers.set(2, lua_searcher = new lua_searcher());
|
||||
searchers.set(3, java_searcher = new java_searcher());
|
||||
package_.set("searchers", searchers);
|
||||
loaded.set("package", package_);
|
||||
env.set("package", package_);
|
||||
globals.package_ = this;
|
||||
return env;
|
||||
}
|
||||
|
||||
static final class PkgLib1 extends OneArgFunction {
|
||||
PackageLib lib;
|
||||
public PkgLib1(String name, int opcode, PackageLib lib) {
|
||||
this.name = name;
|
||||
this.opcode = opcode;
|
||||
this.lib = lib;
|
||||
}
|
||||
public LuaValue call(LuaValue arg) {
|
||||
switch ( opcode ) {
|
||||
case OP_REQUIRE:
|
||||
return lib.require(arg);
|
||||
}
|
||||
return NIL;
|
||||
}
|
||||
}
|
||||
|
||||
static final class PkgLibV extends VarArgFunction {
|
||||
PackageLib lib;
|
||||
public PkgLibV(String name,int opcode, PackageLib lib) {
|
||||
this.name = name;
|
||||
this.opcode = opcode;
|
||||
this.lib = lib;
|
||||
}
|
||||
public Varargs invoke(Varargs args) {
|
||||
switch ( opcode ) {
|
||||
case OP_LOADLIB: {
|
||||
return loadlib(args);
|
||||
}
|
||||
case OP_PRELOAD_SEARCHER: {
|
||||
return lib.searcher_preload(args);
|
||||
}
|
||||
case OP_LUA_SEARCHER: {
|
||||
return lib.searcher_Lua(args);
|
||||
}
|
||||
case OP_JAVA_SEARCHER: {
|
||||
return lib.searcher_Java(args);
|
||||
}
|
||||
case OP_SEARCHPATH: {
|
||||
String name = args.checkjstring(1);
|
||||
String path = args.checkjstring(2);
|
||||
String sep = args.optjstring(3, ".");
|
||||
String rep = args.optjstring(4, FILE_SEP);
|
||||
return lib.searchpath(name, path, sep, rep);
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/** Allow packages to mark themselves as loaded */
|
||||
public void setIsLoaded(String name, LuaTable value) {
|
||||
LOADED.set(name, value);
|
||||
loaded.set(name, value);
|
||||
}
|
||||
|
||||
|
||||
/** Set the lua path used by this library instance to a new value.
|
||||
* Merely sets the value of {@link path} to be used in subsequent searches. */
|
||||
public void setLuaPath( String newLuaPath ) {
|
||||
PACKAGE.set( _PATH, valueOf(newLuaPath) );
|
||||
path = LuaValue.valueOf(newLuaPath);
|
||||
}
|
||||
|
||||
public String tojstring() {
|
||||
return "package";
|
||||
}
|
||||
|
||||
|
||||
// ======================== Package loading =============================
|
||||
|
||||
/**
|
||||
@@ -208,135 +166,148 @@ public class PackageLib extends OneArgFunction {
|
||||
* 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 LuaValue require( LuaValue arg ) {
|
||||
LuaString name = arg.checkstring();
|
||||
LuaValue loaded = LOADED.get(name);
|
||||
if ( loaded.toboolean() ) {
|
||||
if ( loaded == _SENTINEL )
|
||||
error("loop or previous error loading module '"+name+"'");
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/* else must load it; iterate over available loaders */
|
||||
LuaTable tbl = PACKAGE.get(_SEARCHERS).checktable();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
LuaValue chunk = null;
|
||||
for ( int i=1; true; i++ ) {
|
||||
LuaValue loader = tbl.get(i);
|
||||
if ( loader.isnil() ) {
|
||||
error( "module '"+name+"' not found: "+name+sb );
|
||||
}
|
||||
|
||||
/* call loader with module name as argument */
|
||||
chunk = loader.call(name);
|
||||
if ( chunk.isfunction() )
|
||||
break;
|
||||
if ( chunk.isstring() )
|
||||
sb.append( chunk.tojstring() );
|
||||
}
|
||||
|
||||
// load the module using the loader
|
||||
LOADED.set(name, _SENTINEL);
|
||||
LuaValue result = chunk.call(name);
|
||||
if ( ! result.isnil() )
|
||||
LOADED.set( name, result );
|
||||
else if ( (result = LOADED.get(name)) == _SENTINEL )
|
||||
LOADED.set( name, result = LuaValue.TRUE );
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Varargs loadlib( Varargs args ) {
|
||||
args.checkstring(1);
|
||||
return varargsOf(NIL, valueOf("dynamic libraries not enabled"), valueOf("absent"));
|
||||
}
|
||||
|
||||
LuaValue searcher_preload( Varargs args ) {
|
||||
LuaString name = args.checkstring(1);
|
||||
LuaValue preload = PACKAGE.get(_PRELOAD).checktable();
|
||||
LuaValue val = preload.get(name);
|
||||
return val.isnil()?
|
||||
valueOf("\n\tno field package.preload['"+name+"']"):
|
||||
val;
|
||||
}
|
||||
|
||||
Varargs searcher_Lua( Varargs args ) {
|
||||
LuaString name = args.checkstring(1);
|
||||
InputStream is = null;
|
||||
|
||||
// get package path
|
||||
LuaValue path = PACKAGE.get(_PATH);
|
||||
if ( ! path.isstring() )
|
||||
return valueOf("package.path is not a string");
|
||||
|
||||
// get the searchpath function.
|
||||
LuaValue searchpath = PACKAGE.get(_SEARCHPATH);
|
||||
Varargs v = searchpath.invoke(varargsOf(name, path));
|
||||
|
||||
// Didd we get a result?
|
||||
if (!v.isstring(1))
|
||||
return v.arg(2).tostring();
|
||||
LuaString filename = v.arg1().strvalue();
|
||||
|
||||
// Try to load the file.
|
||||
v = globals.loadFile(filename.tojstring());
|
||||
if ( v.arg1().isfunction() )
|
||||
return LuaValue.varargsOf(v.arg1(), filename);
|
||||
|
||||
// report error
|
||||
return varargsOf(NIL, valueOf("'"+filename+"': "+v.arg(2).tojstring()));
|
||||
}
|
||||
|
||||
public Varargs searchpath(String name, String path, String sep, String rep) {
|
||||
|
||||
// check the path elements
|
||||
int e = -1;
|
||||
int n = path.length();
|
||||
StringBuffer sb = null;
|
||||
name = name.replace(sep.charAt(0), rep.charAt(0));
|
||||
while ( e < n ) {
|
||||
|
||||
// find next template
|
||||
int b = e+1;
|
||||
e = path.indexOf(';',b);
|
||||
if ( e < 0 )
|
||||
e = path.length();
|
||||
String template = path.substring(b,e);
|
||||
|
||||
// create filename
|
||||
int q = template.indexOf('?');
|
||||
String filename = template;
|
||||
if ( q >= 0 ) {
|
||||
filename = template.substring(0,q) + name + template.substring(q+1);
|
||||
public class require extends OneArgFunction {
|
||||
public LuaValue call( LuaValue arg ) {
|
||||
LuaString name = arg.checkstring();
|
||||
LuaValue result = loaded.get(name);
|
||||
if ( result.toboolean() ) {
|
||||
if ( result == _SENTINEL )
|
||||
error("loop or previous error loading module '"+name+"'");
|
||||
return result;
|
||||
}
|
||||
|
||||
// try opening the file
|
||||
InputStream is = globals.FINDER.findResource(filename);
|
||||
if (is != null) {
|
||||
try { is.close(); } catch ( java.io.IOException ioe ) {}
|
||||
return valueOf(filename);
|
||||
|
||||
/* else must load it; iterate over available loaders */
|
||||
LuaTable tbl = PackageLib.this.searchers.checktable();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
LuaValue chunk = null;
|
||||
for ( int i=1; true; i++ ) {
|
||||
LuaValue loader = tbl.get(i);
|
||||
if ( loader.isnil() ) {
|
||||
error( "module '"+name+"' not found: "+name+sb );
|
||||
}
|
||||
|
||||
/* call loader with module name as argument */
|
||||
chunk = loader.call(name);
|
||||
if ( chunk.isfunction() )
|
||||
break;
|
||||
if ( chunk.isstring() )
|
||||
sb.append( chunk.tojstring() );
|
||||
}
|
||||
|
||||
// load the module using the loader
|
||||
loaded.set(name, _SENTINEL);
|
||||
result = chunk.call(name);
|
||||
if ( ! result.isnil() )
|
||||
loaded.set( name, result );
|
||||
else if ( (result = PackageLib.this.loaded.get(name)) == _SENTINEL )
|
||||
loaded.set( name, result = LuaValue.TRUE );
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static class loadlib extends VarArgFunction {
|
||||
public Varargs loadlib( Varargs args ) {
|
||||
args.checkstring(1);
|
||||
return varargsOf(NIL, valueOf("dynamic libraries not enabled"), valueOf("absent"));
|
||||
}
|
||||
}
|
||||
|
||||
public class preload_searcher extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
LuaString name = args.checkstring(1);
|
||||
LuaValue val = preload.get(name);
|
||||
return val.isnil()?
|
||||
valueOf("\n\tno field package.preload['"+name+"']"):
|
||||
val;
|
||||
}
|
||||
}
|
||||
|
||||
public class lua_searcher extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
LuaString name = args.checkstring(1);
|
||||
InputStream is = null;
|
||||
|
||||
// get package path
|
||||
if ( ! path.isstring() )
|
||||
return valueOf("package.path is not a string");
|
||||
|
||||
// get the searchpath function.
|
||||
Varargs v = searchpath.invoke(varargsOf(name, path));
|
||||
|
||||
// Did we get a result?
|
||||
if (!v.isstring(1))
|
||||
return v.arg(2).tostring();
|
||||
LuaString filename = v.arg1().strvalue();
|
||||
|
||||
// Try to load the file.
|
||||
v = globals.loadFile(filename.tojstring());
|
||||
if ( v.arg1().isfunction() )
|
||||
return LuaValue.varargsOf(v.arg1(), filename);
|
||||
|
||||
// report error
|
||||
if ( sb == null )
|
||||
sb = new StringBuffer();
|
||||
sb.append( "\n\t"+filename );
|
||||
return varargsOf(NIL, valueOf("'"+filename+"': "+v.arg(2).tojstring()));
|
||||
}
|
||||
}
|
||||
|
||||
public class searchpath extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
String name = args.checkjstring(1);
|
||||
String path = args.checkjstring(2);
|
||||
String sep = args.optjstring(3, ".");
|
||||
String rep = args.optjstring(4, FILE_SEP);
|
||||
|
||||
// check the path elements
|
||||
int e = -1;
|
||||
int n = path.length();
|
||||
StringBuffer sb = null;
|
||||
name = name.replace(sep.charAt(0), rep.charAt(0));
|
||||
while ( e < n ) {
|
||||
|
||||
// find next template
|
||||
int b = e+1;
|
||||
e = path.indexOf(';',b);
|
||||
if ( e < 0 )
|
||||
e = path.length();
|
||||
String template = path.substring(b,e);
|
||||
|
||||
// create filename
|
||||
int q = template.indexOf('?');
|
||||
String filename = template;
|
||||
if ( q >= 0 ) {
|
||||
filename = template.substring(0,q) + name + template.substring(q+1);
|
||||
}
|
||||
|
||||
// try opening the file
|
||||
InputStream is = globals.FINDER.findResource(filename);
|
||||
if (is != null) {
|
||||
try { is.close(); } catch ( java.io.IOException ioe ) {}
|
||||
return valueOf(filename);
|
||||
}
|
||||
|
||||
// report error
|
||||
if ( sb == null )
|
||||
sb = new StringBuffer();
|
||||
sb.append( "\n\t"+filename );
|
||||
}
|
||||
return varargsOf(NIL, valueOf(sb.toString()));
|
||||
}
|
||||
return varargsOf(NIL, valueOf(sb.toString()));
|
||||
}
|
||||
|
||||
LuaValue searcher_Java( Varargs args ) {
|
||||
String name = args.checkjstring(1);
|
||||
String classname = toClassname( name );
|
||||
Class c = null;
|
||||
LuaValue v = null;
|
||||
try {
|
||||
c = Class.forName(classname);
|
||||
v = (LuaValue) c.newInstance();
|
||||
return v;
|
||||
} catch ( ClassNotFoundException cnfe ) {
|
||||
return valueOf("\n\tno class '"+classname+"'" );
|
||||
} catch ( Exception e ) {
|
||||
return valueOf("\n\tjava load failed on '"+classname+"', "+e );
|
||||
public class java_searcher extends VarArgFunction {
|
||||
public Varargs invoke(Varargs args) {
|
||||
String name = args.checkjstring(1);
|
||||
String classname = toClassname( name );
|
||||
Class c = null;
|
||||
LuaValue v = null;
|
||||
try {
|
||||
c = Class.forName(classname);
|
||||
v = (LuaValue) c.newInstance();
|
||||
return v;
|
||||
} catch ( ClassNotFoundException cnfe ) {
|
||||
return valueOf("\n\tno class '"+classname+"'" );
|
||||
} catch ( Exception e ) {
|
||||
return valueOf("\n\tjava load failed on '"+classname+"', "+e );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user