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

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

There is a newer version: 3.0.1
Show newest version
/*******************************************************************************
* Copyright (c) 2009 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.IOException;
import java.io.InputStream;
import java.io.PrintStream;

import org.luaj.vm2.LoadState;
import org.luaj.vm2.LuaError;
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;

/** 
 * Base library implementation, targeted for JME platforms.
 * 
 * BaseLib instances are typically used as the initial globals table 
 * when creating a new uniqued runtime context.  
 * 
 * Since JME has no file system by default, dofile and loadfile use the 
 * FINDER instance to find resource files.  The default loader chain
 * in PackageLib will use these as well.  
 * 
 * For an implementation that looks in the current directory on JSE, 
 * use org.luaj.lib.j2se.BaseLib instead.
 *  
 * @see org.luaj.vm2.lib.jse.JseBaseLib
 */
public class BaseLib extends OneArgFunction implements ResourceFinder {
	public static final String VERSION = "Luaj 2.0";
	
	public static BaseLib instance;
	
	public InputStream STDIN  = null;
	public PrintStream STDOUT = System.out;
	public PrintStream STDERR = System.err;

	/** 
	 * Singleton file opener for this Java ClassLoader realm.
	 * 
	 * Unless set or changed elsewhere, will be set by the BaseLib that is created.
	 */
	public static ResourceFinder FINDER;
	
	private LuaValue next;
	private LuaValue inext;
	
	private static final String[] LIB2_KEYS = {
		"collectgarbage", // ( opt [,arg] ) -> value
		"error", // ( message [,level] ) -> ERR
		"setfenv", // (f, table) -> void
	};
	private static final String[] LIBV_KEYS = {
		"assert", // ( v [,message] ) -> v, message | ERR
		"dofile", // ( filename ) -> result1, ...
		"getfenv", // ( [f] ) -> env
		"getmetatable", // ( object ) -> table 
		"load", // ( func [,chunkname] ) -> chunk | nil, msg
		"loadfile", // ( [filename] ) -> chunk | nil, msg
		"loadstring", // ( string [,chunkname] ) -> chunk | nil, msg
		"pcall", // (f, arg1, ...) -> status, result1, ...
		"xpcall", // (f, err) -> result1, ...
		"print", // (...) -> void
		"select", // (f, ...) -> value1, ...
		"unpack", // (list [,i [,j]]) -> result1, ...
		"type",  // (v) -> value
		"rawequal", // (v1, v2) -> boolean
		"rawget", // (table, index) -> value
		"rawset", // (table, index, value) -> table
		"setmetatable", // (table, metatable) -> table
		"tostring", // (e) -> value
		"tonumber", // (e [,base]) -> value
		"pairs", // "pairs" (t) -> iter-func, t, nil
		"ipairs", // "ipairs", // (t) -> iter-func, t, 0
		"next", // "next"  ( table, [index] ) -> next-index, next-value
		"__inext", // "inext" ( table, [int-index] ) -> next-index, next-value
	};
	
	/**
	 * Construct a base libarary instance.
	 */
	public BaseLib() {
		instance = this;
	}
	
	public LuaValue call(LuaValue arg) {
		env.set( "_G", env );
		env.set( "_VERSION", VERSION );
		bind( env, BaseLib2.class, LIB2_KEYS );
		bind( env, BaseLibV.class, LIBV_KEYS ); 
		
		// remember next, and inext for use in pairs and ipairs
		next = env.get("next");
		inext = env.get("__inext");
		
		// inject base lib int vararg instances
		for ( int i=0; i value
				String s = arg1.checkjstring();
				int result = 0;
				if ( "collect".equals(s) ) {
					System.gc();
					return ZERO;
				} else if ( "count".equals(s) ) {
					Runtime rt = Runtime.getRuntime();
					long used = rt.totalMemory() - rt.freeMemory();
					return valueOf(used/1024.);
				} else if ( "step".equals(s) ) {
					System.gc();
					return LuaValue.TRUE;
				} else {
					this.argerror(1, "gc op");
				}
				return NIL;
			case 1: // "error", // ( message [,level] ) -> ERR
				throw new LuaError( arg1.isnil()? null: arg1.tojstring(), arg2.optint(1) );
			case 2: { // "setfenv", // (f, table) -> void
				LuaTable t = arg2.checktable();
				LuaValue f = getfenvobj(arg1);
				if ( ! f.isfunction() && ! f.isclosure() )
					error("'setfenv' cannot change environment of given object");
			    f.setfenv(t);
			    return f.isthread()? NONE: f;
			}
			}
			return NIL;
		}
	}
	
	private static LuaValue getfenvobj(LuaValue arg) {
		if ( arg.isfunction() )
			return arg;
		int level = arg.optint(1);
	    arg.argcheck(level>=0, 1, "level must be non-negative");
		if ( level == 0 )
			return LuaThread.getRunning();
		LuaValue f = LuaThread.getCallstackFunction(level);
	    arg.argcheck(f != null, 1, "invalid level");
	    return f;
	}

	public static final class BaseLibV extends VarArgFunction {
		public BaseLib baselib;
		public Varargs invoke(Varargs args) {
			switch ( opcode ) {
			case 0: // "assert", // ( v [,message] ) -> v, message | ERR
				if ( !args.arg1().toboolean() ) 
					error( args.narg()>1? args.optjstring(2,"assertion failed!"): "assertion failed!" );
				return args;
			case 1: // "dofile", // ( filename ) -> result1, ...
			{
				Varargs v = args.isnil(1)? 
						BaseLib.loadStream( baselib.STDIN, "=stdin" ):
						BaseLib.loadFile( args.checkjstring(1) );
				return v.isnil(1)? error(v.tojstring(2)): v.arg1().invoke();
			}
			case 2: // "getfenv", // ( [f] ) -> env
			{
				LuaValue f = getfenvobj(args.arg1());
			    LuaValue e = f.getfenv();
				return e!=null? e: NIL;
			}
			case 3: // "getmetatable", // ( object ) -> table
			{
				LuaValue mt = args.checkvalue(1).getmetatable();
				return mt!=null? mt.rawget(METATABLE).optvalue(mt): NIL;
			}
			case 4: // "load", // ( func [,chunkname] ) -> chunk | nil, msg
			{
				LuaValue func = args.checkfunction(1);
				String chunkname = args.optjstring(2, "function");
				return BaseLib.loadStream(new StringInputStream(func), chunkname);
			}
			case 5: // "loadfile", // ( [filename] ) -> chunk | nil, msg
			{
				return args.isnil(1)? 
					BaseLib.loadStream( baselib.STDIN, "stdin" ):
					BaseLib.loadFile( args.checkjstring(1) );
			}
			case 6: // "loadstring", // ( string [,chunkname] ) -> chunk | nil, msg
			{
				LuaString script = args.checkstring(1);
				String chunkname = args.optjstring(2, "string");
				return BaseLib.loadStream(script.toInputStream(),chunkname);
			}
			case 7: // "pcall", // (f, arg1, ...) -> status, result1, ...
			{
				LuaValue func = args.checkvalue(1);
				LuaThread.onCall(this);
				try {
					return pcall(func,args.subargs(2),null);
				} finally {
					LuaThread.onReturn();
				}
			}
			case 8: // "xpcall", // (f, err) -> result1, ...				
			{
				LuaThread.onCall(this);
				try {
					return pcall(args.arg1(),NONE,args.checkvalue(2));
				} finally {
					LuaThread.onReturn();
				}
			}
			case 9: // "print", // (...) -> void
			{
				LuaValue tostring = LuaThread.getGlobals().get("tostring"); 
				for ( int i=1, n=args.narg(); i<=n; i++ ) {
					if ( i>1 ) baselib.STDOUT.write( '\t' );
					LuaString s = tostring.call( args.arg(i) ).strvalue();
					int z = s.indexOf((byte)0, 0);
					baselib.STDOUT.write( s.m_bytes, s.m_offset, z>=0? z: s.m_length );
				}
				baselib.STDOUT.println();
				return NONE;
			}
			case 10: // "select", // (f, ...) -> value1, ...
			{
				int n = args.narg()-1; 				
				if ( args.arg1().equals(valueOf("#")) )
					return valueOf(n);
				int i = args.checkint(1);
				if ( i == 0 || i < -n )
					argerror(1,"index out of range");
				return args.subargs(i<0? n+i+2: i+1);
			}
			case 11: // "unpack", // (list [,i [,j]]) -> result1, ...
			{
				int na = args.narg();
				LuaTable t = args.checktable(1);
				int n = t.length();
				int i = na>=2? args.checkint(2): 1;
				int j = na>=3? args.checkint(3): n;
				n = j-i+1;
				if ( n<0 ) return NONE;
				if ( n==1 ) return t.get(i);
				if ( n==2 ) return varargsOf(t.get(i),t.get(j));
				LuaValue[] v = new LuaValue[n];
				for ( int k=0; k value
				return valueOf(args.checkvalue(1).typename());
			case 13: // "rawequal", // (v1, v2) -> boolean
				return valueOf(args.checkvalue(1) == args.checkvalue(2));
			case 14: // "rawget", // (table, index) -> value
				return args.checktable(1).rawget(args.checkvalue(2));
			case 15: { // "rawset", // (table, index, value) -> table
				LuaTable t = args.checktable(1);
				t.rawset(args.checknotnil(2), args.checkvalue(3));
				return t;
			}
			case 16: { // "setmetatable", // (table, metatable) -> table
				final LuaValue t = args.arg1();
				final LuaValue mt0 = t.getmetatable();
				if ( mt0!=null && !mt0.rawget(METATABLE).isnil() )
					error("cannot change a protected metatable");
				final LuaValue mt = args.checkvalue(2);
				return t.setmetatable(mt.isnil()? null: mt.checktable());
			}
			case 17: { // "tostring", // (e) -> value
				LuaValue arg = args.checkvalue(1);
				LuaValue h = arg.metatag(TOSTRING);
				if ( ! h.isnil() ) 
					return h.call(arg);
				LuaValue v = arg.tostring();
				if ( ! v.isnil() ) 
					return v;
				return valueOf(arg.tojstring());
			}
			case 18: { // "tonumber", // (e [,base]) -> value
				LuaValue arg1 = args.checkvalue(1);
				final int base = args.optint(2,10);
				if (base == 10) {  /* standard conversion */
					return arg1.tonumber();
				} else {
					if ( base < 2 || base > 36 )
						argerror(2, "base out of range");
					return arg1.checkstring().tonumber(base);
				}
			}
			case 19: // "pairs" (t) -> iter-func, t, nil
				return varargsOf( baselib.next, args.checktable(1), NIL );
			case 20: // "ipairs", // (t) -> iter-func, t, 0
				return varargsOf( baselib.inext, args.checktable(1), ZERO );
			case 21: // "next"  ( table, [index] ) -> next-index, next-value
				return args.checktable(1).next(args.arg(2));
			case 22: // "inext" ( table, [int-index] ) -> next-index, next-value
				return args.checktable(1).inext(args.arg(2));
			}
			return NONE;
		}
	}

	public static Varargs pcall(LuaValue func, Varargs args, LuaValue errfunc) {
		try {
			LuaThread thread = LuaThread.getRunning();
			LuaValue olderr = thread.err;
			try {
				thread.err = errfunc;
				return varargsOf(LuaValue.TRUE, func.invoke(args));
			} finally {
				thread.err = olderr;
			}
		} catch ( LuaError le ) {
			String m = le.getMessage();
			return varargsOf(FALSE, m!=null? valueOf(m): NIL);
		} catch ( Exception e ) {
			String m = e.getMessage();
			return varargsOf(FALSE, valueOf(m!=null? m: e.toString()));
		}
	}
	
	/** 
	 * Load from a named file, returning the chunk or nil,error of can't load
	 * @return Varargs containing chunk, or NIL,error-text on error
	 */
	public static Varargs loadFile(String filename) {
		InputStream is = FINDER.findResource(filename);
		if ( is == null )
			return varargsOf(NIL, valueOf("cannot open "+filename+": No such file or directory"));
		try {
			return loadStream(is, "@"+filename);
		} finally {
			try {
				is.close();
			} catch ( Exception e ) {
				e.printStackTrace();
			}
		}
	}

	public static Varargs loadStream(InputStream is, String chunkname) {
		try {
			if ( is == null )
				return varargsOf(NIL, valueOf("not found: "+chunkname));
			return LoadState.load(is, chunkname, LuaThread.getGlobals());
		} catch (Exception e) {
			return varargsOf(NIL, valueOf(e.getMessage()));
		}
	}
	
	
	private static class StringInputStream extends InputStream {
		LuaValue func;
		byte[] bytes; 
		int offset;
		StringInputStream(LuaValue func) {
			this.func = func;
		}
		public int read() throws IOException {
			if ( func == null ) return -1;
			if ( bytes == null ) {
				LuaValue s = func.call();
				if ( s.isnil() ) {
					func = null;
					bytes = null;
					return -1;
				}
				bytes = s.tojstring().getBytes();
				offset = 0;
			}
			if ( offset >= bytes.length )
				return -1;
			return bytes[offset++];
			
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy