Add optional debug library.

This commit is contained in:
James Roseborough
2009-03-24 14:27:59 +00:00
parent 48b839b142
commit 0f40feb625
4 changed files with 397 additions and 21 deletions

View File

@@ -0,0 +1,372 @@
/*******************************************************************************
* 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.LClosure;
import org.luaj.vm.LFunction;
import org.luaj.vm.LInteger;
import org.luaj.vm.LNil;
import org.luaj.vm.LString;
import org.luaj.vm.LTable;
import org.luaj.vm.LValue;
import org.luaj.vm.LocVars;
import org.luaj.vm.LuaState;
public class DebugLib extends LFunction {
private static final String[] NAMES = {
"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 GETHOOKCOUNT = 4;
private static final int GETHOOKMASK = 5;
private static final int GETINFO = 6;
private static final int GETLOCAL = 7;
private static final int GETMETATABLE = 8;
private static final int GETREGISTRY = 9;
private static final int GETUPVALUE = 10;
private static final int SETFENV = 11;
private static final int SETHOOK = 12;
private static final int SETLOCAL = 13;
private static final int SETMETATABLE = 14;
private static final int SETUPVALUE = 15;
private static final int TRACEBACK = 16;
public static void install( LTable globals ) {
LTable debug = new LTable();
for (int i = 0; i < NAMES.length; i++)
debug.put(NAMES[i], new DebugLib(i + 1));
globals.put("debug", debug);
PackageLib.setIsLoaded("debug", debug);
}
private final int id;
private DebugLib( int id ) {
this.id = id;
}
public String toString() {
return NAMES[id]+"()";
}
public boolean luaStackCall( LuaState vm ) {
switch ( id ) {
case INSTALL:
install(vm._G);
break;
case DEBUG:
debug(vm);
break;
case GETFENV:
getfenv(vm);
break;
case GETHOOK:
gethook(vm);
break;
case GETHOOKCOUNT:
gethookcount(vm);
break;
case GETHOOKMASK:
gethookmask(vm);
break;
case GETINFO:
getinfo(vm);
break;
case GETLOCAL:
getlocal(vm);
break;
case GETMETATABLE:
getmetatable(vm);
break;
case GETREGISTRY:
getregistry(vm);
break;
case GETUPVALUE:
getupvalue(vm);
break;
case SETFENV:
setfenv(vm);
break;
case SETHOOK:
sethook(vm);
break;
case SETLOCAL:
setlocal(vm);
break;
case SETMETATABLE:
setmetatable(vm);
break;
case SETUPVALUE:
setupvalue(vm);
break;
case TRACEBACK:
traceback(vm);
break;
default:
LuaState.vmerror( "bad package id" );
}
return false;
}
private void debug(LuaState vm) {
// TODO Auto-generated method stub
vm.resettop();
}
private void gethook(LuaState vm) {
// TODO Auto-generated method stub
vm.resettop();
}
private void gethookcount(LuaState vm) {
// TODO Auto-generated method stub
vm.resettop();
vm.pushinteger(0);
}
private void gethookmask(LuaState vm) {
// TODO Auto-generated method stub
vm.resettop();
vm.pushinteger(0);
}
private void sethook(LuaState vm) {
// TODO Auto-generated method stub
vm.resettop();
}
private void getfenv(LuaState vm) {
LValue object = vm.topointer(2);
LValue env = object.luaGetEnv(null);
vm.pushlvalue(env!=null? env: LNil.NIL);
}
private void setfenv(LuaState vm) {
LValue object = vm.topointer(2);
LTable table = vm.checktable(3);
object.luaSetEnv(table);
}
private void getinfo(LuaState vm) {
LuaState threadVm = vm;
CallInfo ci = null;
LFunction func = null;
LClosure closure = null;
String what = "";
if ( vm.gettop() >= 4 ) {
threadVm = vm.checkthread(2).vm;
vm.remove(2);
}
if ( vm.gettop() >= 3 ) {
what = vm.tostring(3);
}
if ( vm.isnumber(2) ) {
ci = this.getcallinfo(vm, threadVm, vm.tointeger(2));
closure = ci.closure;
} else {
func = vm.checkfunction(2);
if ( func instanceof LClosure )
closure = (LClosure) func;
}
vm.resettop();
LTable info = new LTable();
vm.pushlvalue(info);
for (int i = 0, n = what.length(); i < n; i++) {
switch (what.charAt(i)) {
case 'S': {
info.put("source", (closure!=null? closure.p.source: new LString("?")));
info.put("linedefined", (closure!=null? closure.p.linedefined: 0));
info.put("lastlinedefined", (closure!=null? closure.p.lastlinedefined: 0));
info.put("what", new LString(what));
break;
}
case 'l': {
info.put( "currentline", (ci!=null? ci.pc: 0) );
break;
}
case 'u': {
info.put("nups", (closure!=null? closure.p.nups: 0));
info.put("what", new LString(what));
break;
}
case 'n': {
// TODO: name
info.put("name", new LString("?"));
info.put("namewhat", new LString("?"));
break;
}
case 'f': {
vm.pushlvalue( func!=null? func: LNil.NIL );
break;
}
case 'L': {
LTable lines = new LTable();
vm.pushlvalue(lines);
if ( closure != null )
for ( int j=0, k=1; j<closure.p.lineinfo.length; j++, k++ )
lines.put(k, LInteger.valueOf(closure.p.lineinfo[j]));
break;
}
}
}
}
private void getlocal(LuaState vm) {
LuaState threadVm = vm;
if ( vm.gettop() >= 4 ) {
threadVm = vm.checkthread(2).vm;
vm.remove(2);
}
int level = vm.checkint(2);
int local = vm.checkint(3);
CallInfo ci = getcallinfo(vm, threadVm, level);
LValue value = LNil.NIL;
LValue name = LNil.NIL;
if ( local >= 0 && local < ci.top-ci.base ) {
value = threadVm.stack[ ci.base + local ];
LocVars[] vars = ci.closure.p.locvars;
if ( vars != null && local >= 0 && local < vars.length )
name = vars[local].varname;
}
vm.resettop();
vm.pushlvalue( name );
vm.pushlvalue( value );
}
private void setlocal(LuaState vm) {
LuaState threadVm = vm;
if ( vm.gettop() >= 4 ) {
threadVm = vm.checkthread(2).vm;
vm.remove(2);
}
int level = vm.checkint(2);
int local = vm.checkint(3);
LValue value = vm.topointer(4);
CallInfo ci = getcallinfo(vm, threadVm, level);
LValue name = LNil.NIL;
if ( local >= 0 && local < ci.top-ci.base ) {
threadVm.stack[ ci.base + local ] = value;
LocVars[] vars = ci.closure.p.locvars;
if ( vars != null && local >= 0 && local < vars.length )
name = vars[local].varname;
}
vm.resettop();
vm.pushlvalue( name );
}
private CallInfo getcallinfo(LuaState vm, LuaState threadVm, int level) {
if ( level <= 0 || level > threadVm.cc )
vm.error("level out of range");
int cc = threadVm.cc-(level-1);
return threadVm.calls[cc];
}
private void getmetatable(LuaState vm) {
LValue object = vm.topointer(2);
vm.resettop();
vm.pushlvalue( object.luaGetMetatable() );
}
private void setmetatable(LuaState vm) {
LValue object = vm.topointer(2);
LValue table = vm.totable(3);
object.luaSetMetatable(table);
vm.resettop();
vm.pushlvalue( object );
}
private void getregistry(LuaState vm) {
vm.resettop();
vm.pushlvalue( new LTable() );
}
private void getupvalue(LuaState vm) {
LFunction func = vm.checkfunction(2);
int up = vm.checkint(3);
vm.resettop();
if ( func instanceof LClosure ) {
LClosure c = (LClosure) func;
if ( c.upVals != null && up > 0 && up < c.upVals.length ) {
vm.pushlvalue(c.upVals[up].getValue());
return;
}
}
vm.pushnil();
}
private void setupvalue(LuaState vm) {
LFunction func = vm.checkfunction(2);
int up = vm.checkint(3);
LValue value = vm.topointer(4);
vm.resettop();
if ( func instanceof LClosure ) {
LClosure c = (LClosure) func;
if ( c.upVals != null && up > 0 && up < c.upVals.length ) {
c.upVals[up].setValue(value);
if ( c.p.upvalues != null && up < c.p.upvalues.length )
vm.pushlvalue( c.p.upvalues[up] );
else
vm.pushstring( "."+up+"" );
return;
}
}
vm.pushnil();
}
private void traceback(LuaState vm) {
LuaState threadVm = vm;
int level = 1;
String message = "";
StringBuffer sb = new StringBuffer();
if ( vm.gettop() >= 4 ) {
threadVm = vm.checkthread(2).vm;
vm.remove(2);
}
if ( vm.gettop() >= 3 )
level = vm.optint(3,1);
if ( vm.gettop() >= 2 )
message = vm.tostring(2)+"\n";
message += threadVm.getStackTrace(level);
vm.resettop();
vm.pushstring(sb.toString());
}
}

View File

@@ -42,13 +42,13 @@ public class LThread extends LValue implements Runnable {
private int status = STATUS_SUSPENDED;
LuaState threadVm;
public final LuaState vm;
private Thread thread;
static LThread running;
public LThread(LFunction c, LTable env) {
threadVm = new LuaState(env);
threadVm.pushlvalue(c);
vm = new LuaState(env);
vm.pushlvalue(c);
}
public int luaGetType() {
@@ -61,7 +61,7 @@ public class LThread extends LValue implements Runnable {
// Set the environment if a thread, or closure, and return 1, otherwise return 0
public boolean luaSetEnv(LTable t) {
threadVm._G = t;
vm._G = t;
return true;
}
@@ -76,7 +76,7 @@ public class LThread extends LValue implements Runnable {
public void run() {
synchronized ( this ) {
try {
threadVm.execute();
vm.execute();
} finally {
status = STATUS_DEAD;
this.notify();
@@ -87,7 +87,7 @@ public class LThread extends LValue implements Runnable {
public boolean yield() {
synchronized ( this ) {
if ( status != STATUS_RUNNING )
threadVm.error(this+" not running");
vm.error(this+" not running");
status = STATUS_SUSPENDED;
if ( USE_JAVA_THREADS ) {
this.notify();
@@ -96,7 +96,7 @@ public class LThread extends LValue implements Runnable {
status = STATUS_RUNNING;
} catch ( InterruptedException e ) {
status = STATUS_DEAD;
threadVm.error(this+" "+e);
vm.error(this+" "+e);
}
}
return false;
@@ -128,12 +128,12 @@ public class LThread extends LValue implements Runnable {
status = STATUS_RUNNING;
// copy args in
if (threadVm.cc < 0) {
vm.xmove(threadVm, nargs);
threadVm.prepStackCall();
if (this.vm.cc < 0) {
vm.xmove(this.vm, nargs);
this.vm.prepStackCall();
} else {
threadVm.resettop();
vm.xmove(threadVm, nargs);
this.vm.resettop();
vm.xmove(this.vm, nargs);
}
// execute in the other thread
@@ -149,19 +149,19 @@ public class LThread extends LValue implements Runnable {
this.wait();
} else {
// run this vm until it yields
while (threadVm.cc >= 0 && status == STATUS_RUNNING)
threadVm.exec();
while (this.vm.cc >= 0 && status == STATUS_RUNNING)
this.vm.exec();
}
// copy return values from yielding stack state
vm.resettop();
if ( threadVm.cc >= 0 ) {
if ( this.vm.cc >= 0 ) {
vm.pushboolean(status != STATUS_DEAD);
threadVm.xmove(vm, threadVm.gettop() - 1);
this.vm.xmove(vm, this.vm.gettop() - 1);
} else {
vm.pushboolean(true);
threadVm.base = 0;
threadVm.xmove(vm, threadVm.gettop());
this.vm.base = 0;
this.vm.xmove(vm, this.vm.gettop());
}
} catch ( Throwable t ) {

View File

@@ -78,7 +78,7 @@ public class LuaErrorException extends RuntimeException {
return message;
if ( vm == null ) {
if ( LThread.running != null )
vm = LThread.running.threadVm;
vm = LThread.running.vm;
else
vm = LuaState.mainState;
}

View File

@@ -1062,8 +1062,12 @@ public class LuaState extends Lua {
}
public String getStackTrace() {
StringBuffer buffer = new StringBuffer("Stack Trace:\n");
for (int i = cc; i >= 0; i--) {
return "Stack Trace:\n"+getStackTrace(0);
}
public String getStackTrace(int level) {
StringBuffer buffer = new StringBuffer();
for (int i = cc-level; i >= 0; i--) {
buffer.append(" ");
buffer.append(getFileLine(i));
buffer.append("\n");