Add debug hook functions to LuaState

This commit is contained in:
James Roseborough
2009-03-24 22:44:07 +00:00
parent 0f40feb625
commit 643af145d3
5 changed files with 238 additions and 48 deletions

View File

@@ -56,25 +56,23 @@ public class DebugLib extends LFunction {
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;
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;
public static void install( LTable globals ) {
public static void install( LuaState vm ) {
LTable debug = new LTable();
for (int i = 0; i < NAMES.length; i++)
debug.put(NAMES[i], new DebugLib(i + 1));
globals.put("debug", debug);
vm._G.put("debug", debug);
PackageLib.setIsLoaded("debug", debug);
}
@@ -91,7 +89,7 @@ public class DebugLib extends LFunction {
public boolean luaStackCall( LuaState vm ) {
switch ( id ) {
case INSTALL:
install(vm._G);
install(vm);
break;
case DEBUG:
debug(vm);
@@ -102,12 +100,6 @@ public class DebugLib extends LFunction {
case GETHOOK:
gethook(vm);
break;
case GETHOOKCOUNT:
gethookcount(vm);
break;
case GETHOOKMASK:
gethookmask(vm);
break;
case GETINFO:
getinfo(vm);
break;
@@ -148,29 +140,37 @@ public class DebugLib extends LFunction {
}
private void debug(LuaState vm) {
// TODO Auto-generated method stub
// TODO: interactive console impl
vm.resettop();
}
private void gethook(LuaState vm) {
// TODO Auto-generated method stub
LuaState threadVm = vm;
if ( vm.gettop() >= 2 )
threadVm = vm.checkthread(2).vm;
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);
vm.pushlvalue(threadVm.gethook());
vm.pushinteger(threadVm.gethookmask());
vm.pushinteger(threadVm.gethookcount());
}
private void sethook(LuaState vm) {
// TODO Auto-generated method stub
LuaState threadVm = vm;
if ( vm.gettop() >= 4 ) {
threadVm = vm.checkthread(2).vm;
vm.remove(2);
}
LFunction func = vm.checkfunction(2);
LString str = vm.checklstring(3);
int count = vm.optint(4,0);
int mask = 0;
for ( int i=0; i<str.m_length; i++ )
switch ( str.m_bytes[str.m_offset+i] ) {
case 'c': mask |= LuaState.LUA_MASKCALL; break;
case 'l': mask |= LuaState.LUA_MASKLINE; break;
case 'r': mask |= LuaState.LUA_MASKRET; break;
}
threadVm.sethook(func, mask, count);
vm.resettop();
}
@@ -213,7 +213,8 @@ public class DebugLib extends LFunction {
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("source", (closure!=null? closure.p.source: new LString("@?")));
info.put("short_src", (closure!=null? closure.p.source.substring(1, closure.p.source.m_length-1): 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));
@@ -235,12 +236,13 @@ public class DebugLib extends LFunction {
break;
}
case 'f': {
vm.pushlvalue( func!=null? func: LNil.NIL );
if ( func != null )
info.put( "func", func );
break;
}
case 'L': {
LTable lines = new LTable();
vm.pushlvalue(lines);
info.put("activelines", 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]));

View File

@@ -356,6 +356,5 @@ public class Lua {
"thread",
"value",
};
}

View File

@@ -69,6 +69,7 @@ import org.luaj.lib.TableLib;
*
*/
public class LuaState extends Lua {
/* thread status; 0 is OK */
public static final int LUA_YIELD = 1;
public static final int LUA_ERRRUN = 2;
@@ -80,6 +81,16 @@ public class LuaState extends Lua {
private static final int LUA_MINCALLS = 10;
private static final int MAXTAGLOOP = 100;
// hook function values
private static final int LUA_HOOKCALL = 0;
private static final int LUA_HOOKRET = 1;
private static final int LUA_HOOKLINE = 2;
private static final int LUA_HOOKCOUNT = 3;
private static final int LUA_HOOKTAILRET = 4;
public static final int LUA_MASKCALL = (1 << LUA_HOOKCALL);
public static final int LUA_MASKRET = (1 << LUA_HOOKRET);
public static final int LUA_MASKLINE = (1 << LUA_HOOKLINE);
public int base = 0;
public int top = 0;
protected int nresults = -1;
@@ -92,12 +103,16 @@ public class LuaState extends Lua {
static LuaState mainState;
public LTable _G;
// main debug hook, overridden by DebugStackState
protected void debugHooks(int pc) {
}
protected void debugAssert(boolean b) {
}
// debug hooks - these MUST NOT be initialized,
// so that a later obfuscation step can decide to remove them.
private boolean hooksenabled;
private int hookmask;
private int hookcount;
private LFunction hookfunc;
private int hookincr;
private int hookline;
protected void debugAssert(boolean b) {}
// ------------------- constructors ---------------------
/**
@@ -525,7 +540,9 @@ public class LuaState extends Lua {
ci.top = top;
// allow debug hooks a chance to operate
debugHooks( ci.pc );
if ( hooksenabled ) {
debugBytecodeHooks( ci.pc );
}
// advance program counter
i = code[ci.pc++];
@@ -695,6 +712,11 @@ public class LuaState extends Lua {
// number of return values we need
c = LuaState.GETARG_C(i);
// call hook
if ( hooksenabled ) {
debugCallHooks( ci.pc-1 );
}
// make or set up the call
this.nresults = c - 1;
if (this.stack[base].luaStackCall(this))
@@ -713,6 +735,12 @@ public class LuaState extends Lua {
}
case LuaState.OP_TAILCALL: {
// return hook
if ( hooksenabled ) {
debugTailReturnHooks( ci.pc-1 );
}
// close up values
closeUpVals(base);
// copy down the frame before calling!
@@ -755,6 +783,12 @@ public class LuaState extends Lua {
}
case LuaState.OP_RETURN: {
// return hook
if ( hooksenabled ) {
debugReturnHooks( ci.pc-1 );
}
// close up values
closeUpVals( base );
// number of return vals to return
@@ -2326,4 +2360,119 @@ public class LuaState extends Lua {
top = oldtop;
}
}
// ===========================================================================
// Debug hooks.
// These should be obfuscated out when sethook is never called
// cannot be called from the application.
//
/**
* Set the hook function.
*
* @param func LFunction to call on the hook event
* @param mask combination of LuaState.LUA_MASKLINE, LuaState.LUA_MASKCALL, and LuaState.LUA_MASKRET
* @param count 0, or number of bytecodes between count events.
*/
public void sethook( LFunction func, int mask, int count ) {
hooksenabled = (mask != 0);
hookfunc = func;
hookmask = mask;
hookcount = count;
}
/**
* Get the current hook function, if any.
*
* @return LFunction that is set as the current hook function, or null if not set.
*/
public LFunction gethook() {
return hookfunc;
}
/**
* Get the current hook count.
*
* @return current count, which is # of bytecodes between "count" hook calls
*/
public int gethookcount() {
return hookcount;
}
/**
* Get the current hook mask.
*
* @return current mask as a combination of
* LuaState.LUA_MASKLINE, LuaState.LUA_MASKCALL, and LuaState.LUA_MASKRET
*/
public int gethookmask() {
return hookmask;
}
// line number and count hooks
private void debugBytecodeHooks(int pc) {
if ( hookfunc != null && (hookmask & LUA_MASKLINE) != 0 ) {
int line = debugGetLineNumber(calls[cc]);
if ( line != hookline ) {
hookline = line;
debugCallHook(LUA_HOOKLINE, line);
}
if (hookcount != 0) {
if ( --hookincr <= 0 ) {
hookincr = hookcount;
debugCallHook(LUA_HOOKCOUNT, -1);
}
}
}
}
private void debugCallHooks(int pc) {
if ( hookfunc != null && ((hookmask & LUA_MASKCALL) != 0) ) {
debugCallHook(LUA_HOOKCALL, debugGetLineNumber(calls[cc]));
hookline = -1;
}
}
private void debugReturnHooks(int pc) {
if ( hookfunc != null && ((hookmask & LUA_MASKRET) != 0) ) {
debugCallHook(LUA_HOOKRET, debugGetLineNumber(calls[cc]));
hookline = -1;
}
}
private void debugTailReturnHooks(int pc) {
if ( hookfunc != null && ((hookmask & LUA_MASKRET) != 0) ) {
debugCallHook(LUA_HOOKTAILRET, debugGetLineNumber(calls[cc]));
hookline = -1;
}
}
private int debugGetLineNumber(CallInfo ci) {
int[] lineNumbers = ci.closure.p.lineinfo;
int pc = getCurrentPc(ci);
int line = (lineNumbers != null && lineNumbers.length > pc ?
lineNumbers[pc] :
-1);
return line;
}
private void debugCallHook(int mask, int newline) {
int prevmask = hookmask;
try {
hookmask = 0;
this.pushfunction(hookfunc);
switch ( mask ) {
default: this.pushstring("line"); break;
case LUA_HOOKCOUNT: this.pushstring("count"); break;
case LUA_HOOKCALL: this.pushstring("call"); break;
case LUA_HOOKRET: this.pushstring("return"); break;
case LUA_HOOKTAILRET: this.pushstring("tail return"); break;
}
this.pushinteger(newline);
this.pcall(2, 0, 0);
} finally {
hookmask = prevmask;
}
}
}

40
src/test/res/debuglib.lua Normal file
View File

@@ -0,0 +1,40 @@
local print = print
print( 'has debug', debug~=nil )
local printinfo = function(...)
for i,a in ipairs(arg) do
if type(a) == 'table' then
print( ' source: '..tostring(a.source) )
print( ' short_src: '..tostring(a.short_src) )
print( ' what: '..tostring(a.what) )
print( ' currentline: '..tostring(a.currentline) )
print( ' linedefined: '..tostring(a.linedefined) )
print( ' lastlinedefined: '..tostring(a.lastlinedefined) )
else
print( tostring(a) )
end
end
end
function test()
local x = 5
function f()
x = x + 1
return x
end
function g()
x = x - 1
return x
end
print(f())
print(g())
return f, g
end
local e,f,g = pcall( test )
print( 'e,f,g', e, type(f), type(g) )
printinfo( 'debug.getinfo(f,"Sl")', pcall(debug.getinfo, f, "Sl") )
printinfo( 'debug.getinfo(g,"Sl")', pcall(debug.getinfo, g, "Sl") )
printinfo( 'debug.getinfo(test,"Sl")', pcall(debug.getinfo, test, "Sl") )

View File

@@ -1 +1 @@
version: 0.92
version: 0.93