diff --git a/src/core/org/luaj/vm2/Globals.java b/src/core/org/luaj/vm2/Globals.java index 1383fb07..e9305993 100644 --- a/src/core/org/luaj/vm2/Globals.java +++ b/src/core/org/luaj/vm2/Globals.java @@ -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; diff --git a/src/core/org/luaj/vm2/LuaValue.java b/src/core/org/luaj/vm2/LuaValue.java index 83cce20d..376a97e7 100644 --- a/src/core/org/luaj/vm2/LuaValue.java +++ b/src/core/org/luaj/vm2/LuaValue.java @@ -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. *

diff --git a/src/core/org/luaj/vm2/lib/PackageLib.java b/src/core/org/luaj/vm2/lib/PackageLib.java index 8593f4b8..a9cd9331 100644 --- a/src/core/org/luaj/vm2/lib/PackageLib.java +++ b/src/core/org/luaj/vm2/lib/PackageLib.java @@ -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: *

 {@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") );
  * } 
- * 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. *

* 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 ); + } } }