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

org.luaj.lib.DebugLib Maven / Gradle / Ivy

There is a newer version: 1.0.5
Show newest version
/*******************************************************************************
* Copyright (c) 2009 LuaJ. 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.lib;


import org.luaj.vm.CallInfo;
import org.luaj.vm.LBoolean;
import org.luaj.vm.LClosure;
import org.luaj.vm.LFunction;
import org.luaj.vm.LInteger;
import org.luaj.vm.LNil;
import org.luaj.vm.LPrototype;
import org.luaj.vm.LString;
import org.luaj.vm.LTable;
import org.luaj.vm.LValue;
import org.luaj.vm.Lua;
import org.luaj.vm.LuaErrorException;
import org.luaj.vm.LuaState;
import org.luaj.vm.UpVal;

public class DebugLib extends LFunction {

	private static final String[] NAMES = {
		"debuglib",
		"debug",
		"getfenv",
		"gethook",
		"getinfo",
		"getlocal",
		"getmetatable",
		"getregistry",
		"getupvalue",
		"setfenv",
		"sethook",
		"setlocal",
		"setmetatable",
		"setupvalue",
		"traceback",
	};
	
	private static final int INSTALL        = 0;
	private static final int DEBUG        	= 1;
	private static final int GETFENV        = 2;
	private static final int GETHOOK        = 3;
	private static final int GETINFO        = 4;
	private static final int GETLOCAL       = 5;
	private static final int GETMETATABLE 	= 6;
	private static final int GETREGISTRY    = 7;
	private static final int GETUPVALUE    	= 8;
	private static final int SETFENV        = 9;
	private static final int SETHOOK        = 10;
	private static final int SETLOCAL 		= 11;
	private static final int SETMETATABLE   = 12;
	private static final int SETUPVALUE    	= 13;
	private static final int TRACEBACK    	= 14;

	/* maximum stack for a Lua function */
	private static final int MAXSTACK = 250;
	
	private static final LString LUA       = new LString("Lua");  
	private static final LString JAVA      = new LString("Java");  
	private static final LString JAVASRC   = new LString("[Java]");  
	private static final LString QMARK     = new LString("?");  
	private static final LString GLOBAL    = new LString("global");  
	private static final LString LOCAL     = new LString("local");  
	private static final LString METHOD    = new LString("method");  
	private static final LString UPVALUE   = new LString("upvalue");  
	private static final LString FIELD     = new LString("field");
	private static final LString NOSTRING  = new LString("");
	
	public static void install( LuaState vm ) {
		LTable debug = new LTable();
		for (int i = 1; i < NAMES.length; i++)
			debug.put(NAMES[i], new DebugLib(i));
		vm._G.put("debug", debug);
		PackageLib.setIsLoaded("debug", debug);
	}

	private final int id;
	
	public DebugLib() {
		this.id = INSTALL;
	}
	
	private DebugLib( int id ) {
		this.id = id;
	}
	
	public String toString() {
		return NAMES[id]+"()";
	}
	
	public int invoke( LuaState vm ) {
		switch ( id ) {
		case INSTALL:
			install(vm);
			return 0;
		case DEBUG: 
			return debug(vm);
		case GETFENV:
			return getfenv(vm);
		case GETHOOK: 
			return gethook(vm);
		case GETINFO: 
			return getinfo(vm);
		case GETLOCAL:
			return getlocal(vm);
		case GETMETATABLE:
			return getmetatable(vm);
		case GETREGISTRY:
			return getregistry(vm);
		case GETUPVALUE:
			return getupvalue(vm);
		case SETFENV:
			return setfenv(vm);
		case SETHOOK:
			return sethook(vm);
		case SETLOCAL:
			return setlocal(vm);
		case SETMETATABLE:
			return setmetatable(vm);
		case SETUPVALUE:
			return setupvalue(vm);
		case TRACEBACK:
			return traceback(vm);
		default:
			LuaState.vmerror( "bad package id" );
			return 0;
		}
	}

	// j2se subclass may wish to override and provide actual console here. 
	// j2me platform has not System.in to provide console.
	protected int debug(LuaState vm) {
		return 0;
	}
	
	protected int gethook(LuaState vm) {
		LuaState threadVm = optthreadvm(vm, 1);
		LValue hook = threadVm.gethook();
		int mask = threadVm.gethookmask();
		int count = threadVm.gethookcount();
		vm.pushlvalue(hook!=null? hook: LNil.NIL);
		vm.pushstring(""
				+((mask&LuaState.LUA_MASKCALL)!=0? "c": "")
				+((mask&LuaState.LUA_MASKRET) !=0? "r": "")
				+((mask&LuaState.LUA_MASKLINE)!=0? "l": ""));
		vm.pushinteger(count);
		return 3;
	}

	protected LuaState optthreadvm(LuaState vm, int index) {
		if ( ! vm.isthread(index) )
			return vm;
		LuaState threadVm = vm.checkthread(index).vm;
		vm.remove(index);
		return threadVm;
	}
	
	protected int sethook(LuaState vm) {
		LuaState threadVm = optthreadvm(vm, 1);
		LFunction func = vm.isnoneornil(1)? null: vm.checkfunction(1);
		String str    =  vm.optstring(2,"");
		int count      = vm.optint(3,0);
		int mask       = 0;
		for ( int i=0; i= 0 )
							lines.put(1, LInteger.valueOf(line));
					}
					break;
				}
				default: break;
			}
		}
		return 1;
	}
	
	protected int getlocal(LuaState vm) {
		LuaState threadVm = optthreadvm(vm, 1);
		int level = vm.checkint(1);
		int local = vm.checkint(2);
		StackInfo si = getstackinfo(threadVm, level, 1)[0];
		CallInfo ci = (si!=null? si.luainfo: null);
		LPrototype p = (ci!=null? ci.closure.p: null);
		LString name = (p!=null? p.getlocalname(local, ci.currentpc()): null);
		if ( name != null ) {
			LValue value = threadVm.stack[ci.base+(local-1)];
			vm.pushlvalue( name );
			vm.pushlvalue( value );
			return 2;
		} else {
			vm.pushnil();
			return 1;
		}
	}

	protected int setlocal(LuaState vm) {
		LuaState threadVm = optthreadvm(vm, 1);
		int level = vm.checkint(1);
		int local = vm.checkint(2);
		LValue value = vm.topointer(3);
		StackInfo si = getstackinfo(threadVm, level, 1)[0];
		CallInfo ci = (si!=null? si.luainfo: null);
		LPrototype p = (ci!=null? ci.closure.p: null);
		LString name = (p!=null? p.getlocalname(local, ci.currentpc()): null);
		if ( name != null ) {
			threadVm.stack[ci.base+(local-1)] = value;
			vm.pushlvalue( name );
		} else {
			vm.pushnil();
		}
		return 1;
	}

	protected int getmetatable(LuaState vm) {
		LValue object = vm.topointer(1);
		LValue mt = object.luaGetMetatable();
		if ( mt != null )
			vm.pushlvalue( object.luaGetMetatable() );
		else
			vm.pushnil();
		return 1;
	}

	protected int setmetatable(LuaState vm) {
		LValue object = vm.topointer(1);
		vm.checkany(2);
		if ( ! vm.isnil(2) )
			object.luaSetMetatable(vm.checktable(2));
		else
			object.luaSetMetatable(null);
		vm.pushboolean(true);
		return 1;
	}

	protected int getregistry(LuaState vm) {
		vm.pushlvalue( new LTable() );
		return 1;
	}

	private static LString findupvalue(LClosure c, int up) {
		if ( c.upVals != null && up > 0 && up <= c.upVals.length ) {
			if ( c.p.upvalues != null && up <= c.p.upvalues.length )
				return c.p.upvalues[up-1];
			else
				return new LString( "."+up+"" );
		}
		return null;
	}

	protected int getupvalue(LuaState vm) {
		LFunction func = vm.checkfunction(1);
		int up = vm.checkint(2);
		vm.resettop();
		if ( func.isClosure() ) {
			LClosure c = (LClosure) func;
			LString name = findupvalue(c, up);
			if ( name != null ) {
				vm.pushlstring(name);
				vm.pushlvalue(c.upVals[up-1].getValue());
				return 2;
			}
		}
		vm.pushnil();
		return 1;
	}

	protected int setupvalue(LuaState vm) {
		LFunction func = vm.checkfunction(1);
		int up = vm.checkint(2);
		LValue value = vm.topointer(3);
		vm.resettop();
		if ( func instanceof LClosure ) {
			LClosure c = (LClosure) func;
			LString name = findupvalue(c, up);
			if ( name != null ) {
				c.upVals[up-1].setValue(value);
				vm.pushlstring(name);
				return 1;
			}
		}
		vm.pushnil();
		return 1;
	}

	protected int traceback(LuaState vm) {
		LuaState threadVm = optthreadvm(vm, 1);
		String message = "stack traceback:\n";
		int level = vm.optint(2,1);
		if ( ! vm.isnoneornil(1) )
			message = vm.checkstring(1)+"\n";
		String tb = DebugLib.traceback(threadVm, level);
		vm.pushstring(message+tb);
		return 1;
	}
	
	// =================== public utilities ====================

	/** 
	 * @param callinfo the CallInfo to inspect
	 * @param up the 1-based index of the local   
	 * @return { name, value } or null if not found.
	 */ 
	public static LValue[] getlocal(LuaState vm, CallInfo ci, int local) {
		LPrototype p = (ci!=null? ci.closure.p: null);
		LString name = (p!=null? p.getlocalname(local, ci.currentpc()): null);
		if ( name != null ) {
			LValue value = vm.stack[ci.base+(local-1)];
			return new LValue[] { name, value };
		} else {
			return null;
		}
	}
	
	/** 
	 * @param c the LClosure to inspect
	 * @param up the 1-based index of the upvalue  
	 * @return { name, value, isclosed } or null if not found.
	 */ 
	public static LValue[] getupvalue(LClosure c, int up) {
		LString name = findupvalue(c, up);
		if ( name != null ) {
			UpVal u = c.upVals[up-1];
			LValue value = u.getValue();
			boolean isclosed = u.isClosed();
			return new LValue[] { name, value, LBoolean.valueOf(isclosed) };
		}
		return null;
	}
	
	/** 
	 * Get a traceback as a string for an arbitrary LuaState 
	 */
	public static String traceback(LuaState vm, int level) {
		StackInfo[] s = getstackinfo(vm, level, 10);
		StringBuffer sb = new StringBuffer();
		for ( int i=0; i=0? getobjname(vm, caller, stackpos): null);
		}
		public int currentline() {
			return luainfo!=null? luainfo.currentline(): -1;
		}
		public String tracename() {
			if ( caller == null )
				return "main chunk";
			if ( func != null )
				return func.toString();
			LString[] kind = getfunckind();
			if ( kind == null )
				return "function ?";
			return "function "+kind[0].toJavaString();
		}
	}

	
	/** 
	 * @param level first level to report
	 * @return array StackInfo with countlevels items, some may be null!
	 */
	private static StackInfo[] getstackinfo(LuaState vm, int level, int countlevels) {
		StackInfo[] si = new StackInfo[countlevels];
		int i = 0;
		LClosure prevclosure = null;
        for (int j=vm.cc; j>=0; --j) {
			
			CallInfo ci = vm.calls[j];
			LFunction f = ci.currentfunc(vm);

			// java, or tailcall? 
			if ( f != null && (! f.isClosure() || f!=prevclosure) ) {
				if ( (level--) <= 0 ) {
					si[i++] = new StackInfo( vm, ci, ci.currentfunca(vm), null, f);
					if ( i >= countlevels )
						return si;
				}
			}
					
			// add the lua closure
			if ( (level--) <= 0 ) {
				if (j>0 && vm.calls[j-1].currentfunc(vm) == ci.closure) {
					CallInfo caller = vm.calls[j-1];
					int callera = caller.currentfunca(vm);
					si[i++] = new StackInfo( vm, caller, callera, ci, ci.closure);
				} else {
					si[i++] = new StackInfo( vm, null, -1, ci, ci.closure);
				}
				if ( i >= countlevels )
					return si;
			}
			prevclosure = ci.closure;
		}
		
		return si;
	}
	
	// look up a function in the stack, if it exists
	private static StackInfo findstackinfo(LuaState vm, LFunction func) {
	    for (int j=vm.cc; j>=0; --j) {
			CallInfo ci = vm.calls[j];
			int instr = ci.closure.p.code[ci.currentpc()];
			if ( Lua.GET_OPCODE(instr) == Lua.OP_CALL ) {
				int a = Lua.GETARG_A(instr);
				if ( func == vm.stack[ci.base + a] )
					return new StackInfo(vm, ci, a, null, func);
				if ( func == ci.closure )
					return new StackInfo(vm, (j>0? vm.calls[j-1]: null), 0, ci, null);
			}
	    }
		return new StackInfo(vm, null, -1, null, func);
	}

	// return LString[] { name, namewhat } if found, null if not
	private static LString[] getobjname(LuaState L, CallInfo ci, int stackpos) {
		LString name;
		if (ci.isLua()) { /* a Lua function? */
			LPrototype p = ci.closure.p;
			int pc = (ci.pc > 0 ? ci.pc - 1 : 0); // currentpc(L, ci);
			int i;// Instruction i;
			name = p.getlocalname(stackpos + 1, pc);
			if (name != null) /* is a local? */
				return new LString[] { name, LOCAL };
			i = symbexec(p, pc, stackpos); /* try symbolic execution */
			lua_assert(pc != -1);
			switch (Lua.GET_OPCODE(i)) {
			case Lua.OP_GETGLOBAL: {
				int g = Lua.GETARG_Bx(i); /* global index */
				// lua_assert(p.k[g].isString());
				return new LString[] { p.k[g].luaAsString(), GLOBAL };
			}
			case Lua.OP_MOVE: {
				int a = Lua.GETARG_A(i);
				int b = Lua.GETARG_B(i); /* move from `b' to `a' */
				if (b < a)
					return getobjname(L, ci, b); /* get name for `b' */
				break;
			}
			case Lua.OP_GETTABLE: {
				int k = Lua.GETARG_C(i); /* key index */
				name = kname(p, k);
				return new LString[] { name, FIELD };
			}
			case Lua.OP_GETUPVAL: {
				int u = Lua.GETARG_B(i); /* upvalue index */
				name = u < p.upvalues.length ? p.upvalues[u] : QMARK;
				return new LString[] { name, UPVALUE };
			}
			case Lua.OP_SELF: {
				int k = Lua.GETARG_C(i); /* key index */
				name = kname(p, k);
				return new LString[] { name, METHOD };
			}
			default:
				break;
			}
		}
		return null; /* no useful name found */
	}

	private static LString kname(LPrototype p, int c) {
		if (Lua.ISK(c) && p.k[Lua.INDEXK(c)].isString())
			return p.k[Lua.INDEXK(c)].luaAsString();
		else
			return QMARK;
	}

	private static boolean checkreg(LPrototype pt,int reg)	{
		return (reg < pt.maxstacksize);
	}

	private static boolean precheck(LPrototype pt) {
		if (!(pt.maxstacksize <= MAXSTACK)) return false;
		lua_assert(pt.numparams + (pt.is_vararg & Lua.VARARG_HASARG) <= pt.maxstacksize);
		lua_assert((pt.is_vararg & Lua.VARARG_NEEDSARG) == 0
				|| (pt.is_vararg & Lua.VARARG_HASARG) != 0);
		if (!(pt.upvalues.length <= pt.nups)) return false;
		if (!(pt.lineinfo.length == pt.code.length || pt.lineinfo.length == 0)) return false;
		if (!(Lua.GET_OPCODE(pt.code[pt.code.length - 1]) == Lua.OP_RETURN)) return false;
		return true;
	}

	private static boolean checkopenop(LPrototype pt,int pc) {
		int i = pt.code[(pc)+1];
		switch (Lua.GET_OPCODE(i)) {
		case Lua.OP_CALL:
		case Lua.OP_TAILCALL:
		case Lua.OP_RETURN:
		case Lua.OP_SETLIST: {
			if (!(Lua.GETARG_B(i) == 0)) return false;
			return true;
		}
		default:
			return false; /* invalid instruction after an open call */
		}
	}
	
	//static int checkArgMode (LPrototype pt, int r, enum OpArgMask mode) {
	private static boolean checkArgMode (LPrototype pt, int r, int mode) {
		switch (mode) {
			case Lua.OpArgN: if (!(r == 0)) return false; break;
			case Lua.OpArgU: break;
			case Lua.OpArgR: checkreg(pt, r); break;
			case Lua.OpArgK:
				if (!(Lua.ISK(r) ? Lua.INDEXK(r) < pt.k.length : r < pt.maxstacksize)) return false;
				break;
			default: break;

		}
		return true;
	}


	// return last instruction, or 0 if error
	private static int symbexec(LPrototype pt, int lastpc, int reg) {
		int pc;
		int last; /* stores position of last instruction that changed `reg' */
		last = pt.code.length - 1; /*
									 * points to final return (a `neutral'
									 * instruction)
									 */
		if (!(precheck(pt))) return 0;
		for (pc = 0; pc < lastpc; pc++) {
			int i = pt.code[pc];
			int op = Lua.GET_OPCODE(i);
			int a = Lua.GETARG_A(i);
			int b = 0;
			int c = 0;
			if (!(op < Lua.NUM_OPCODES)) return 0;
			if (!checkreg(pt, a)) return 0;
			switch (Lua.getOpMode(op)) {
			case Lua.iABC: {
				b = Lua.GETARG_B(i);
				c = Lua.GETARG_C(i);
				if (!(checkArgMode(pt, b, Lua.getBMode(op)))) return 0;
				if (!(checkArgMode(pt, c, Lua.getCMode(op)))) return 0;
				break;
			}
			case Lua.iABx: {
				b = Lua.GETARG_Bx(i);
				if (Lua.getBMode(op) == Lua.OpArgK)
					if (!(b < pt.k.length)) return 0;
				break;
			}
			case Lua.iAsBx: {
				b = Lua.GETARG_sBx(i);
				if (Lua.getBMode(op) == Lua.OpArgR) {
					int dest = pc + 1 + b;
					if (!(0 <= dest && dest < pt.code.length)) return 0;
					if (dest > 0) {
						/* cannot jump to a setlist count */
						int d = pt.code[dest - 1];
						if ((Lua.GET_OPCODE(d) == Lua.OP_SETLIST && Lua.GETARG_C(d) == 0)) return 0;
					}
				}
				break;
			}
			default: break;
			}
			if (Lua.testAMode(op)) {
				if (a == reg)
					last = pc; /* change register `a' */
			}
			if (Lua.testTMode(op)) {
				if (!(pc + 2 < pt.code.length)) return 0; /* check skip */
				if (!(Lua.GET_OPCODE(pt.code[pc + 1]) == Lua.OP_JMP)) return 0;
			}
			switch (op) {
			case Lua.OP_LOADBOOL: {
				if (!(c == 0 || pc + 2 < pt.code.length)) return 0; /* check its jump */
				break;
			}
			case Lua.OP_LOADNIL: {
				if (a <= reg && reg <= b)
					last = pc; /* set registers from `a' to `b' */
				break;
			}
			case Lua.OP_GETUPVAL:
			case Lua.OP_SETUPVAL: {
				if (!(b < pt.nups)) return 0;
				break;
			}
			case Lua.OP_GETGLOBAL:
			case Lua.OP_SETGLOBAL: {
				if (!(pt.k[b].isString())) return 0;
				break;
			}
			case Lua.OP_SELF: {
				if (!checkreg(pt, a + 1)) return 0;
				if (reg == a + 1)
					last = pc;
				break;
			}
			case Lua.OP_CONCAT: {
				if (!(b < c)) return 0; /* at least two operands */
				break;
			}
			case Lua.OP_TFORLOOP: {
				if (!(c >= 1)) return 0; /* at least one result (control variable) */
				if (!checkreg(pt, a + 2 + c)) return 0; /* space for results */
				if (reg >= a + 2)
					last = pc; /* affect all regs above its base */
				break;
			}
			case Lua.OP_FORLOOP:
			case Lua.OP_FORPREP:
				if (!checkreg(pt, a + 3)) return 0;
				/* go through */
			case Lua.OP_JMP: {
				int dest = pc + 1 + b;
				/* not full check and jump is forward and do not skip `lastpc'? */
				if (reg != Lua.NO_REG && pc < dest && dest <= lastpc)
					pc += b; /* do the jump */
				break;
			}
			case Lua.OP_CALL:
			case Lua.OP_TAILCALL: {
				if (b != 0) {
					if (!checkreg(pt, a + b - 1)) return 0;
				}
				c--; /* c = num. returns */
				if (c == Lua.LUA_MULTRET) {
					if (!(checkopenop(pt, pc))) return 0;
				} else if (c != 0)
					if (!checkreg(pt, a + c - 1)) return 0;
				if (reg >= a)
					last = pc; /* affect all registers above base */
				break;
			}
			case Lua.OP_RETURN: {
				b--; /* b = num. returns */
				if (b > 0)
					if (!checkreg(pt, a + b - 1)) return 0;
				break;
			}
			case Lua.OP_SETLIST: {
				if (b > 0)
					if (!checkreg(pt, a + b)) return 0;
				if (c == 0)
					pc++;
				break;
			}
			case Lua.OP_CLOSURE: {
				int nup, j;
				if (!(b < pt.p.length)) return 0;
				nup = pt.p[b].nups;
				if (!(pc + nup < pt.code.length)) return 0;
				for (j = 1; j <= nup; j++) {
					int op1 = Lua.GET_OPCODE(pt.code[pc + j]);
					if (!(op1 == Lua.OP_GETUPVAL || op1 == Lua.OP_MOVE)) return 0;
				}
				if (reg != Lua.NO_REG) /* tracing? */
					pc += nup; /* do not 'execute' these pseudo-instructions */
				break;
			}
			case Lua.OP_VARARG: {
				if (!((pt.is_vararg & Lua.VARARG_ISVARARG) != 0
						&& (pt.is_vararg & Lua.VARARG_NEEDSARG) == 0)) return 0;
				b--;
				if (b == Lua.LUA_MULTRET)
					if (!(checkopenop(pt, pc))) return 0;
				if (!checkreg(pt, a + b - 1)) return 0;
				break;
			}
			default:
				break;
			}
		}
		return pt.code[last];
	}
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy