All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.luaj.vm2.lib.PackageLib Maven / Gradle / Ivy

There is a newer version: 3.0.1
Show newest version
/*******************************************************************************
* Copyright (c) 2010-2011 Luaj.org. All rights reserved.
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* 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;

import java.io.InputStream;
import java.io.PrintStream;

import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;

/** 
 * Subclass of {@link LibFunction} which implements the lua standard package and module 
 * library functions. 
 * 
 * 

* Typically, this library is included as part of a call to either * {@link JsePlatform#standardGlobals()} or {@link JmePlatform#standardGlobals()} *

* 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);
 * _G.load(new BaseLib());
 * _G.load(new PackageLib());
 * System.out.println( _G.get("require").call(LuaValue.valueOf("hyperbolic")) );
 * } 
* 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, * 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. * @see LibFunction * @see BaseLib * @see JseBaseLib * @see JsePlatform * @see JmePlatform * @see http://www.lua.org/manual/5.1/manual.html#5.3 */ public class PackageLib extends OneArgFunction { public static String DEFAULT_LUA_PATH = "?.lua"; public InputStream STDIN = null; public PrintStream STDOUT = System.out; public LuaTable LOADED; public LuaTable PACKAGE; /** Most recent instance of PackageLib */ public static PackageLib instance; /** Loader that loads from preload table if found there */ public LuaValue preload_loader; /** Loader that loads as a lua script using the LUA_PATH */ public LuaValue lua_loader; /** Loader that loads as a Java class. Class must have public constructor and be a LuaValue */ public LuaValue java_loader; private static final LuaString _M = valueOf("_M"); private static final LuaString _NAME = valueOf("_NAME"); private static final LuaString _PACKAGE = valueOf("_PACKAGE"); private static final LuaString _DOT = valueOf("."); private static final LuaString _LOADERS = valueOf("loaders"); 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 _SEEALL = valueOf("seeall"); private static final LuaString _SENTINEL = valueOf("\u0001"); private static final int OP_MODULE = 0; private static final int OP_REQUIRE = 1; private static final int OP_LOADLIB = 2; private static final int OP_SEEALL = 3; private static final int OP_PRELOAD_LOADER = 4; private static final int OP_LUA_LOADER = 5; private static final int OP_JAVA_LOADER = 6; public PackageLib() { instance = this; } public LuaValue call(LuaValue arg) { env.set("require", new PkgLib1(env,"require",OP_REQUIRE,this)); env.set("module", new PkgLibV(env,"module",OP_MODULE,this)); env.set( "package", PACKAGE=tableOf( new LuaValue[] { _LOADED, LOADED=tableOf(), _PRELOAD, tableOf(), _PATH, valueOf(DEFAULT_LUA_PATH), _LOADLIB, new PkgLibV(env,"loadlib",OP_LOADLIB,this), _SEEALL, new PkgLib1(env,"seeall",OP_SEEALL,this), _LOADERS, listOf(new LuaValue[] { preload_loader = new PkgLibV(env,"preload_loader", OP_PRELOAD_LOADER,this), lua_loader = new PkgLibV(env,"lua_loader", OP_LUA_LOADER,this), java_loader = new PkgLibV(env,"java_loader", OP_JAVA_LOADER,this), }) }) ); LOADED.set("package", PACKAGE); return env; } static final class PkgLib1 extends OneArgFunction { PackageLib lib; public PkgLib1(LuaValue env,String name, int opcode, PackageLib lib) { this.env = env; this.name = name; this.opcode = opcode; this.lib = lib; } public LuaValue call(LuaValue arg) { switch ( opcode ) { case OP_REQUIRE: return lib.require(arg); case OP_SEEALL: { LuaTable t = arg.checktable(); LuaValue m = t.getmetatable(); if ( m == null ) t.setmetatable(m=tableOf()); m.set( INDEX, LuaThread.getGlobals() ); return NONE; } } return NIL; } } static final class PkgLibV extends VarArgFunction { PackageLib lib; public PkgLibV(LuaValue env,String name, int opcode, PackageLib lib) { this.env = env; this.name = name; this.opcode = opcode; this.lib = lib; } public Varargs invoke(Varargs args) { switch ( opcode ) { case OP_MODULE: return lib.module(args); case OP_LOADLIB: return loadlib(args); case OP_PRELOAD_LOADER: { return lib.loader_preload(args); } case OP_LUA_LOADER: { return lib.loader_Lua(args); } case OP_JAVA_LOADER: { return lib.loader_Java(args); } } return NONE; } } /** Allow packages to mark themselves as loaded */ public void setIsLoaded(String name, LuaTable value) { LOADED.set(name, value); } public void setLuaPath( String newLuaPath ) { PACKAGE.set( _PATH, valueOf(newLuaPath) ); } public String tojstring() { return "package"; } // ======================== Module, Package loading ============================= /** * module (name [, ...]) * * Creates a module. If there is a table in package.loaded[name], this table * is the module. Otherwise, if there is a global table t with the given * name, this table is the module. Otherwise creates a new table t and sets * it as the value of the global name and the value of package.loaded[name]. * This function also initializes t._NAME with the given name, t._M with the * module (t itself), and t._PACKAGE with the package name (the full module * name minus last component; see below). Finally, module sets t as the new * environment of the current function and the new value of * package.loaded[name], so that require returns t. * * If name is a compound name (that is, one with components separated by * dots), module creates (or reuses, if they already exist) tables for each * component. For instance, if name is a.b.c, then module stores the module * table in field c of field b of global a. * * This function may receive optional options after the module name, where * each option is a function to be applied over the module. */ public Varargs module(Varargs args) { LuaString modname = args.checkstring(1); int n = args.narg(); LuaValue value = LOADED.get(modname); LuaValue module; if ( ! value.istable() ) { /* not found? */ /* try global variable (and create one if it does not exist) */ LuaValue globals = LuaThread.getGlobals(); module = findtable( globals, modname ); if ( module == null ) error( "name conflict for module '"+modname+"'" ); LOADED.set(modname, module); } else { module = (LuaTable) value; } /* check whether table already has a _NAME field */ LuaValue name = module.get(_NAME); if ( name.isnil() ) { modinit( module, modname ); } // set the environment of the current function LuaFunction f = LuaThread.getCallstackFunction(1); if ( f == null ) error("no calling function"); if ( ! f.isclosure() ) error("'module' not called from a Lua function"); f.setfenv(module); // apply the functions for ( int i=2; i<=n; i++ ) args.arg(i).call( module ); // returns no results return NONE; } /** * * @param table the table at which to start the search * @param fname the name to look up or create, such as "abc.def.ghi" * @return the table for that name, possible a new one, or null if a non-table has that name already. */ private static final LuaValue findtable(LuaValue table, LuaString fname) { int b, e=(-1); do { e = fname.indexOf(_DOT, b=e+1 ); if ( e < 0 ) e = fname.m_length; LuaString key = fname.substring(b, e); LuaValue val = table.rawget(key); if ( val.isnil() ) { /* no such field? */ LuaTable field = new LuaTable(); /* new table for field */ table.set(key, field); table = field; } else if ( ! val.istable() ) { /* field has a non-table value? */ return null; } else { table = val; } } while ( e < fname.m_length ); return table; } private static final void modinit(LuaValue module, LuaString modname) { /* module._M = module */ module.set(_M, module); int e = modname.lastIndexOf(_DOT); module.set(_NAME, modname ); module.set(_PACKAGE, (e<0? EMPTYSTRING: modname.substring(0,e+1)) ); } /** * require (modname) * * 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. * * To find a loader, require is guided by the package.loaders array. By changing this array, * we can change how require looks for a module. The following explanation is based on the * default configuration for package.loaders. * * 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 C loader * using the path stored in package.cpath. If that also fails, it tries an all-in-one loader * (see package.loaders). * * Once a loader is found, require calls the loader with a single argument, modname. * If the loader returns any value, require assigns the returned value to package.loaded[modname]. * If the loader returns no 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]. * * 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(_LOADERS).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 loader_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; } LuaValue loader_Lua( Varargs args ) { String name = args.checkjstring(1); InputStream is = null; // get package path LuaValue pp = PACKAGE.get(_PATH); if ( ! pp.isstring() ) return valueOf("package.path is not a string"); String path = pp.tojstring(); // check the path elements int e = -1; int n = path.length(); StringBuffer sb = null; name = name.replace('.','/'); 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 loading the file Varargs v = BaseLib.loadFile(filename); if ( v.arg1().isfunction() ) return v.arg1(); // report error if ( sb == null ) sb = new StringBuffer(); sb.append( "\n\t'"+filename+"': "+v.arg(2) ); } return valueOf(sb.toString()); } LuaValue loader_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(); v.setfenv(env); return v; } 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='a'&&c<='z') || (c>='A'&&c<='Z') || (c>='0'&&c<='9') ) return true; switch ( c ) { case '.': case '$': case '_': return true; default: return false; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy