Add debug hook functions to LuaState
This commit is contained in:
@@ -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]));
|
||||
|
||||
@@ -356,6 +356,5 @@ public class Lua {
|
||||
"thread",
|
||||
"value",
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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
40
src/test/res/debuglib.lua
Normal 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") )
|
||||
@@ -1 +1 @@
|
||||
version: 0.92
|
||||
version: 0.93
|
||||
|
||||
Reference in New Issue
Block a user