Add optional debug library.
This commit is contained in:
372
src/core/org/luaj/lib/DebugLib.java
Normal file
372
src/core/org/luaj/lib/DebugLib.java
Normal 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());
|
||||
}
|
||||
}
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user