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 DEBUG = 1;
|
||||||
private static final int GETFENV = 2;
|
private static final int GETFENV = 2;
|
||||||
private static final int GETHOOK = 3;
|
private static final int GETHOOK = 3;
|
||||||
private static final int GETHOOKCOUNT = 4;
|
private static final int GETINFO = 4;
|
||||||
private static final int GETHOOKMASK = 5;
|
private static final int GETLOCAL = 5;
|
||||||
private static final int GETINFO = 6;
|
private static final int GETMETATABLE = 6;
|
||||||
private static final int GETLOCAL = 7;
|
private static final int GETREGISTRY = 7;
|
||||||
private static final int GETMETATABLE = 8;
|
private static final int GETUPVALUE = 8;
|
||||||
private static final int GETREGISTRY = 9;
|
private static final int SETFENV = 9;
|
||||||
private static final int GETUPVALUE = 10;
|
private static final int SETHOOK = 10;
|
||||||
private static final int SETFENV = 11;
|
private static final int SETLOCAL = 11;
|
||||||
private static final int SETHOOK = 12;
|
private static final int SETMETATABLE = 12;
|
||||||
private static final int SETLOCAL = 13;
|
private static final int SETUPVALUE = 13;
|
||||||
private static final int SETMETATABLE = 14;
|
private static final int TRACEBACK = 14;
|
||||||
private static final int SETUPVALUE = 15;
|
|
||||||
private static final int TRACEBACK = 16;
|
|
||||||
|
|
||||||
public static void install( LTable globals ) {
|
public static void install( LuaState vm ) {
|
||||||
LTable debug = new LTable();
|
LTable debug = new LTable();
|
||||||
for (int i = 0; i < NAMES.length; i++)
|
for (int i = 0; i < NAMES.length; i++)
|
||||||
debug.put(NAMES[i], new DebugLib(i + 1));
|
debug.put(NAMES[i], new DebugLib(i + 1));
|
||||||
globals.put("debug", debug);
|
vm._G.put("debug", debug);
|
||||||
PackageLib.setIsLoaded("debug", debug);
|
PackageLib.setIsLoaded("debug", debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +89,7 @@ public class DebugLib extends LFunction {
|
|||||||
public boolean luaStackCall( LuaState vm ) {
|
public boolean luaStackCall( LuaState vm ) {
|
||||||
switch ( id ) {
|
switch ( id ) {
|
||||||
case INSTALL:
|
case INSTALL:
|
||||||
install(vm._G);
|
install(vm);
|
||||||
break;
|
break;
|
||||||
case DEBUG:
|
case DEBUG:
|
||||||
debug(vm);
|
debug(vm);
|
||||||
@@ -102,12 +100,6 @@ public class DebugLib extends LFunction {
|
|||||||
case GETHOOK:
|
case GETHOOK:
|
||||||
gethook(vm);
|
gethook(vm);
|
||||||
break;
|
break;
|
||||||
case GETHOOKCOUNT:
|
|
||||||
gethookcount(vm);
|
|
||||||
break;
|
|
||||||
case GETHOOKMASK:
|
|
||||||
gethookmask(vm);
|
|
||||||
break;
|
|
||||||
case GETINFO:
|
case GETINFO:
|
||||||
getinfo(vm);
|
getinfo(vm);
|
||||||
break;
|
break;
|
||||||
@@ -148,29 +140,37 @@ public class DebugLib extends LFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void debug(LuaState vm) {
|
private void debug(LuaState vm) {
|
||||||
// TODO Auto-generated method stub
|
// TODO: interactive console impl
|
||||||
vm.resettop();
|
vm.resettop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void gethook(LuaState vm) {
|
private void gethook(LuaState vm) {
|
||||||
// TODO Auto-generated method stub
|
LuaState threadVm = vm;
|
||||||
|
if ( vm.gettop() >= 2 )
|
||||||
|
threadVm = vm.checkthread(2).vm;
|
||||||
vm.resettop();
|
vm.resettop();
|
||||||
}
|
vm.pushlvalue(threadVm.gethook());
|
||||||
|
vm.pushinteger(threadVm.gethookmask());
|
||||||
private void gethookcount(LuaState vm) {
|
vm.pushinteger(threadVm.gethookcount());
|
||||||
// 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) {
|
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();
|
vm.resettop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +213,8 @@ public class DebugLib extends LFunction {
|
|||||||
for (int i = 0, n = what.length(); i < n; i++) {
|
for (int i = 0, n = what.length(); i < n; i++) {
|
||||||
switch (what.charAt(i)) {
|
switch (what.charAt(i)) {
|
||||||
case 'S': {
|
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("linedefined", (closure!=null? closure.p.linedefined: 0));
|
||||||
info.put("lastlinedefined", (closure!=null? closure.p.lastlinedefined: 0));
|
info.put("lastlinedefined", (closure!=null? closure.p.lastlinedefined: 0));
|
||||||
info.put("what", new LString(what));
|
info.put("what", new LString(what));
|
||||||
@@ -235,12 +236,13 @@ public class DebugLib extends LFunction {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'f': {
|
case 'f': {
|
||||||
vm.pushlvalue( func!=null? func: LNil.NIL );
|
if ( func != null )
|
||||||
|
info.put( "func", func );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'L': {
|
case 'L': {
|
||||||
LTable lines = new LTable();
|
LTable lines = new LTable();
|
||||||
vm.pushlvalue(lines);
|
info.put("activelines", lines);
|
||||||
if ( closure != null )
|
if ( closure != null )
|
||||||
for ( int j=0, k=1; j<closure.p.lineinfo.length; j++, k++ )
|
for ( int j=0, k=1; j<closure.p.lineinfo.length; j++, k++ )
|
||||||
lines.put(k, LInteger.valueOf(closure.p.lineinfo[j]));
|
lines.put(k, LInteger.valueOf(closure.p.lineinfo[j]));
|
||||||
|
|||||||
@@ -356,6 +356,5 @@ public class Lua {
|
|||||||
"thread",
|
"thread",
|
||||||
"value",
|
"value",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ import org.luaj.lib.TableLib;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class LuaState extends Lua {
|
public class LuaState extends Lua {
|
||||||
|
|
||||||
/* thread status; 0 is OK */
|
/* thread status; 0 is OK */
|
||||||
public static final int LUA_YIELD = 1;
|
public static final int LUA_YIELD = 1;
|
||||||
public static final int LUA_ERRRUN = 2;
|
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 LUA_MINCALLS = 10;
|
||||||
private static final int MAXTAGLOOP = 100;
|
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 base = 0;
|
||||||
public int top = 0;
|
public int top = 0;
|
||||||
protected int nresults = -1;
|
protected int nresults = -1;
|
||||||
@@ -92,12 +103,16 @@ public class LuaState extends Lua {
|
|||||||
static LuaState mainState;
|
static LuaState mainState;
|
||||||
public LTable _G;
|
public LTable _G;
|
||||||
|
|
||||||
// main debug hook, overridden by DebugStackState
|
// debug hooks - these MUST NOT be initialized,
|
||||||
protected void debugHooks(int pc) {
|
// so that a later obfuscation step can decide to remove them.
|
||||||
}
|
private boolean hooksenabled;
|
||||||
protected void debugAssert(boolean b) {
|
private int hookmask;
|
||||||
}
|
private int hookcount;
|
||||||
|
private LFunction hookfunc;
|
||||||
|
private int hookincr;
|
||||||
|
private int hookline;
|
||||||
|
|
||||||
|
protected void debugAssert(boolean b) {}
|
||||||
|
|
||||||
// ------------------- constructors ---------------------
|
// ------------------- constructors ---------------------
|
||||||
/**
|
/**
|
||||||
@@ -525,7 +540,9 @@ public class LuaState extends Lua {
|
|||||||
ci.top = top;
|
ci.top = top;
|
||||||
|
|
||||||
// allow debug hooks a chance to operate
|
// allow debug hooks a chance to operate
|
||||||
debugHooks( ci.pc );
|
if ( hooksenabled ) {
|
||||||
|
debugBytecodeHooks( ci.pc );
|
||||||
|
}
|
||||||
|
|
||||||
// advance program counter
|
// advance program counter
|
||||||
i = code[ci.pc++];
|
i = code[ci.pc++];
|
||||||
@@ -695,6 +712,11 @@ public class LuaState extends Lua {
|
|||||||
// number of return values we need
|
// number of return values we need
|
||||||
c = LuaState.GETARG_C(i);
|
c = LuaState.GETARG_C(i);
|
||||||
|
|
||||||
|
// call hook
|
||||||
|
if ( hooksenabled ) {
|
||||||
|
debugCallHooks( ci.pc-1 );
|
||||||
|
}
|
||||||
|
|
||||||
// make or set up the call
|
// make or set up the call
|
||||||
this.nresults = c - 1;
|
this.nresults = c - 1;
|
||||||
if (this.stack[base].luaStackCall(this))
|
if (this.stack[base].luaStackCall(this))
|
||||||
@@ -713,6 +735,12 @@ public class LuaState extends Lua {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case LuaState.OP_TAILCALL: {
|
case LuaState.OP_TAILCALL: {
|
||||||
|
// return hook
|
||||||
|
if ( hooksenabled ) {
|
||||||
|
debugTailReturnHooks( ci.pc-1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// close up values
|
||||||
closeUpVals(base);
|
closeUpVals(base);
|
||||||
|
|
||||||
// copy down the frame before calling!
|
// copy down the frame before calling!
|
||||||
@@ -755,6 +783,12 @@ public class LuaState extends Lua {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case LuaState.OP_RETURN: {
|
case LuaState.OP_RETURN: {
|
||||||
|
// return hook
|
||||||
|
if ( hooksenabled ) {
|
||||||
|
debugReturnHooks( ci.pc-1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// close up values
|
||||||
closeUpVals( base );
|
closeUpVals( base );
|
||||||
|
|
||||||
// number of return vals to return
|
// number of return vals to return
|
||||||
@@ -2326,4 +2360,119 @@ public class LuaState extends Lua {
|
|||||||
top = oldtop;
|
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