/******************************************************************************* * Copyright (c) 2010-2011 Luaj.org. All rights reserved. * <p/> * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * <p/> * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * <p/> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ package org.luaj.vm2.lib; //TODO 与3.0不一样 import com.taobao.luaview.util.LogUtil; import org.luaj.vm2.Globals; import org.luaj.vm2.LuaFunction; import org.luaj.vm2.LuaString; import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaValue; import org.luaj.vm2.Varargs; import java.io.InputStream; /** * Subclass of {@link LibFunction} which implements the lua standard package and module * library functions. * <p/> * <h3>Lua Environment Variables</h3> * The following variables are available to lua scrips when this library has been loaded: * <ul> * <li><code>"package.loaded"</code> Lua table of loaded modules. * <li><code>"package.path"</code> Search path for lua scripts. * <li><code>"package.preload"</code> Lua table of uninitialized preload functions. * <li><code>"package.searchers"</code> Lua table of functions that search for object to load. * </ul> * <p/> * <h3>Java Environment Variables</h3> * These Java environment variables affect the library behavior: * <ul> * <li><code>"luaj.package.path"</code> Initial value for <code>"package.path"</code>. Default value is <code>"?.lua"</code> * </ul> * <p/> * <h3>Loading</h3> * Typically, this library is included as part of a call to either * {@link JsePlatform#standardGlobals()} or {@link JmePlatform#standardGlobals()} * <pre> {@code * Globals globals = JsePlatform.standardGlobals(); * System.out.println( globals.get("require").call"foo") ); * } </pre> * <p/> * To instantiate and use it directly, * link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as: * <pre> {@code * Globals globals = new Globals(); * globals.load(new JseBaseLib()); * globals.load(new PackageLib()); * System.out.println( globals.get("require").call("foo") ); * } </pre> * <h3>Limitations</h3> * This library 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} documentation. * <p/> * * @see LibFunction * @see BaseLib * @see JseBaseLib * @see JsePlatform * @see JmePlatform * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.3">Lua 5.2 Package Lib Reference</a> */ public class PackageLib extends TwoArgFunction { /** * The default value to use for package.path. This can be set with the system property * <code>"luaj.package.path"</code>, and is <code>"?.lua"</code> by default. */ public static String DEFAULT_LUA_PATH; static { try { DEFAULT_LUA_PATH = System.getProperty("luaj.package.path"); } catch (Exception e) { LogUtil.i(e.toString()); // System.out.println(e.toString()); } if (DEFAULT_LUA_PATH == null) DEFAULT_LUA_PATH = "?.lua"; } 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 _SEARCHPATH = valueOf("searchpath"); private static final LuaString _SEARCHERS = valueOf("searchers"); /** * The globals that were used to load this library. */ Globals globals; /** * The table for this package. */ LuaTable package_; /** * 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 String FILE_SEP = System.getProperty("file.separator"); public PackageLib() { } public LuaValue call(LuaValue modname, LuaValue env) { globals = env.checkglobals(); globals.set("require", new require()); package_ = new LuaTable(); package_.set(_LOADED, new LuaTable()); package_.set(_PRELOAD, new LuaTable()); package_.set(_PATH, LuaValue.valueOf(DEFAULT_LUA_PATH)); package_.set(_LOADLIB, new loadlib()); package_.set(_SEARCHPATH, new searchpath()); LuaTable 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); package_.get(_LOADED).set("package", package_); env.set("package", package_); globals.package_ = this; return env; } /** * Allow packages to mark themselves as loaded */ public void setIsLoaded(String name, LuaTable value) { package_.get(_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, LuaValue.valueOf(newLuaPath)); } public String tojstring() { return "package"; } // ======================== Package loading ============================= /** * require (modname) * <p/> * Loads the given module. The function starts by looking into the package.loaded table * to determine whether modname is already loaded. If it is, then require returns the value * stored at package.loaded[modname]. Otherwise, it tries to find a loader for the module. * <p/> * To find a loader, require is guided by the package.searchers sequence. * By changing this sequence, we can change how require looks for a module. * The following explanation is based on the default configuration for package.searchers. * <p/> * First require queries package.preload[modname]. If it has a value, this value * (which should be a function) is the loader. Otherwise require searches for a Lua loader using * the path stored in package.path. If that also fails, it searches for a Java loader using * the classpath, using the public default constructor, and casting the instance to LuaFunction. * <p/> * Once a loader is found, require calls the loader with two arguments: modname and an extra value * dependent on how it got the loader. If the loader came from a file, this extra value is the file name. * If the loader is a Java instance of LuaFunction, this extra value is the environment. * If the loader returns any non-nil value, require assigns the returned value to package.loaded[modname]. * If the loader does not return a non-nil value and has not assigned any value to package.loaded[modname], * then require assigns true to this entry. * In any case, require returns the final value of package.loaded[modname]. * <p/> * If there is any error loading or running the module, or if it cannot find any loader for the module, * then require raises an error. */ public class require extends OneArgFunction { public LuaValue call(LuaValue arg) { LuaString name = arg.checkstring(); LuaValue loaded = package_.get(_LOADED); LuaValue result = loaded.get(name); if (result.toboolean()) { if (result == _SENTINEL) error("loop or previous error loading module '" + name + "'"); return result; }/* else if (globals != null && (result = globals.lazyLoad(name.checkjstring())) != null){//TODO add by song, 如果是加载自定义的内容则使用globals加载 loaded.set(name, _SENTINEL); return result; }*/ /* else must load it; iterate over available loaders */ LuaTable tbl = package_.get(_SEARCHERS).checktable(); StringBuffer sb = new StringBuffer(); Varargs loader = null; for (int i = 1; true; i++) { LuaValue searcher = tbl.get(i); if (searcher.isnil()) { error("module '" + name + "' not found: " + name + sb); } /* call loader with module name as argument */ loader = searcher.invoke(name); if (loader.isfunction(1)) break; if (loader.isstring(1)) sb.append(loader.tojstring(1)); } // load the module using the loader loaded.set(name, _SENTINEL); try { result = loader.arg1().call(name, loader.arg(2)); if (!result.isnil()) loaded.set(name, result); else if ((result = loaded.get(name)) == _SENTINEL) loaded.set(name, result = LuaValue.TRUE); return result; } catch (Exception e){ LogUtil.e("[PackageLib]", name, "load failed!", e); return NIL; } } } 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 = package_.get(_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 LuaValue path = package_.get(_PATH); if (!path.isstring()) return valueOf("package.path is not a string"); // get the searchpath function. Varargs v = package_.get(_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 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.getLuaResourceFinder().findResource(filename);//modify by song 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())); } } 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(); if (v.isfunction()) ((LuaFunction) v).initupvalue1(globals); return varargsOf(v, globals); } catch (ClassNotFoundException cnfe) { return valueOf("\n\tno class '" + classname + "'"); } catch (Exception e) { return valueOf("\n\tjava load failed on '" + classname + "', " + e); } } } /** * Convert lua filename to valid class name */ public static final String toClassname(String filename) { int n = filename.length(); int j = n; if (filename.endsWith(".lua")) j -= 4; for (int k = 0; k < j; k++) { char c = filename.charAt(k); if ((!isClassnamePart(c)) || (c == '/') || (c == '\\')) { StringBuffer sb = new StringBuffer(j); for (int i = 0; i < j; i++) { c = filename.charAt(i); sb.append( (isClassnamePart(c)) ? c : ((c == '/') || (c == '\\')) ? '.' : '_'); } return sb.toString(); } } return n == j ? filename : filename.substring(0, j); } private static final boolean isClassnamePart(char c) { if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) return true; switch (c) { case '.': case '$': case '_': return true; default: return false; } } }