Improved local variable and upvalue reporting for functions.
This commit is contained in:
@@ -60,4 +60,8 @@ public class UpVal {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isClosed() {
|
||||||
|
return state == null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import java.io.IOException;
|
|||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import org.luaj.compiler.LexState;
|
|
||||||
import org.luaj.debug.event.DebugEventBreakpoint;
|
import org.luaj.debug.event.DebugEventBreakpoint;
|
||||||
import org.luaj.debug.event.DebugEventError;
|
import org.luaj.debug.event.DebugEventError;
|
||||||
import org.luaj.debug.net.DebugNetSupportBase;
|
import org.luaj.debug.net.DebugNetSupportBase;
|
||||||
@@ -39,6 +38,7 @@ import org.luaj.vm.CallInfo;
|
|||||||
import org.luaj.vm.DebugNetSupport;
|
import org.luaj.vm.DebugNetSupport;
|
||||||
import org.luaj.vm.LClosure;
|
import org.luaj.vm.LClosure;
|
||||||
import org.luaj.vm.LPrototype;
|
import org.luaj.vm.LPrototype;
|
||||||
|
import org.luaj.vm.LString;
|
||||||
import org.luaj.vm.LTable;
|
import org.luaj.vm.LTable;
|
||||||
import org.luaj.vm.LValue;
|
import org.luaj.vm.LValue;
|
||||||
import org.luaj.vm.LocVars;
|
import org.luaj.vm.LocVars;
|
||||||
@@ -154,7 +154,7 @@ import org.luaj.vm.Platform;
|
|||||||
*/
|
*/
|
||||||
public class DebugLuaState extends LuaState implements DebugRequestListener {
|
public class DebugLuaState extends LuaState implements DebugRequestListener {
|
||||||
private static final boolean TRACE = (null != System.getProperty("TRACE"));
|
private static final boolean TRACE = (null != System.getProperty("TRACE"));
|
||||||
|
|
||||||
// stepping constants and stepping state
|
// stepping constants and stepping state
|
||||||
protected static final int STEP_NONE = 0;
|
protected static final int STEP_NONE = 0;
|
||||||
protected static final int STEP_OVER = 1;
|
protected static final int STEP_OVER = 1;
|
||||||
@@ -173,6 +173,9 @@ public class DebugLuaState extends LuaState implements DebugRequestListener {
|
|||||||
protected DebugNetSupportBase debugSupport;
|
protected DebugNetSupportBase debugSupport;
|
||||||
protected LuaErrorException lastError;
|
protected LuaErrorException lastError;
|
||||||
|
|
||||||
|
final String uparrow = "^";
|
||||||
|
final String rtarrow = "~";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance of DebugLuaState.
|
* Creates an instance of DebugLuaState.
|
||||||
*
|
*
|
||||||
@@ -285,7 +288,7 @@ public class DebugLuaState extends LuaState implements DebugRequestListener {
|
|||||||
throw new AbortException("aborted by debug client");
|
throw new AbortException("aborted by debug client");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
//*
|
||||||
if (TRACE) {
|
if (TRACE) {
|
||||||
System.out.println("entered debugHook on pc=" + pc + "...Line: " + getFileLine(cc));
|
System.out.println("entered debugHook on pc=" + pc + "...Line: " + getFileLine(cc));
|
||||||
for (int j = 0; j <= cc; j++) {
|
for (int j = 0; j <= cc; j++) {
|
||||||
@@ -293,7 +296,7 @@ public class DebugLuaState extends LuaState implements DebugRequestListener {
|
|||||||
dumpStack(j);
|
dumpStack(j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
//*/
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
while (bSuspendOnStart) {
|
while (bSuspendOnStart) {
|
||||||
try {
|
try {
|
||||||
@@ -620,22 +623,44 @@ public class DebugLuaState extends LuaState implements DebugRequestListener {
|
|||||||
* @return the visible local variables on the given stack frame.
|
* @return the visible local variables on the given stack frame.
|
||||||
*/
|
*/
|
||||||
public Variable[] getStack(int index) {
|
public Variable[] getStack(int index) {
|
||||||
if (index < 0 || index >= calls.length) {
|
if (index < 0 || index > cc) {
|
||||||
throw new RuntimeException("invalid stack index");
|
throw new RuntimeException("invalid stack index");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CallInfo callInfo = calls[index];
|
||||||
|
LClosure closure = callInfo.closure;
|
||||||
|
LPrototype prototype = closure.p;
|
||||||
|
LocVars[] localVariables = prototype.locvars;
|
||||||
|
|
||||||
Vector variables = new Vector();
|
Vector variables = new Vector();
|
||||||
Hashtable variablesSeen = new Hashtable();
|
|
||||||
LPrototype p = calls[index].closure.p;
|
int pc = getCurrentPc(callInfo);
|
||||||
for (int i = index; i >= 0; i--) {
|
|
||||||
if (i == index || isInScope(p, calls[i])) {
|
// add upvalues
|
||||||
addVariables(variables, variablesSeen, i);
|
for ( int i=0; i<prototype.nups; i++ ) {
|
||||||
|
if ( closure.upVals[i] != null ) {
|
||||||
|
LString[] ups = prototype.upvalues;
|
||||||
|
String upstate = (closure.upVals[i].isClosed()? rtarrow: uparrow);
|
||||||
|
String name = (ups!=null && ups.length>i? String.valueOf(ups[i]): "?");
|
||||||
|
LValue value = closure.upVals[i].getValue();
|
||||||
|
addVariable( variables, upstate+name, value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add locals
|
||||||
|
for (int i = 0; i < localVariables.length; i++) {
|
||||||
|
if (isActiveVariable(pc, localVariables[i])) {
|
||||||
|
String name = localVariables[i].varname.toJavaString();
|
||||||
|
LValue value = stack[callInfo.base+i];
|
||||||
|
addVariable(variables, name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// convert to array
|
||||||
Variable[] result = new Variable[variables.size()];
|
Variable[] result = new Variable[variables.size()];
|
||||||
for (int i = 0; i < variables.size(); i++) {
|
variables.copyInto(result);
|
||||||
result[i] = (Variable) variables.elementAt(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -690,7 +715,9 @@ public class DebugLuaState extends LuaState implements DebugRequestListener {
|
|||||||
CallInfo callInfo = calls[index];
|
CallInfo callInfo = calls[index];
|
||||||
LPrototype prototype = callInfo.closure.p;
|
LPrototype prototype = callInfo.closure.p;
|
||||||
LocVars[] localVariables = prototype.locvars;
|
LocVars[] localVariables = prototype.locvars;
|
||||||
System.out.println("Stack Frame: " + index + " [" + base + "," + top + "], # of localvars: " + localVariables.length + ", pc=" + callInfo.pc);
|
System.out.println("Stack Frame: " + index + " [" + base + "," + top + "]," +
|
||||||
|
" # of localvars: " + localVariables.length + ", pc=" + callInfo.pc +
|
||||||
|
" # upvals: " + prototype.nups );
|
||||||
|
|
||||||
int pc = getCurrentPc(callInfo);
|
int pc = getCurrentPc(callInfo);
|
||||||
for (int i = 0; i < localVariables.length; i++) {
|
for (int i = 0; i < localVariables.length; i++) {
|
||||||
@@ -707,32 +734,7 @@ public class DebugLuaState extends LuaState implements DebugRequestListener {
|
|||||||
System.out.println("stack[" + i + "]=" + stack[i]);
|
System.out.println("stack[" + i + "]=" + stack[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the name of the Nth variable in scope of the call info.
|
|
||||||
* @param callInfo Call info
|
|
||||||
* @param index Index of the variable
|
|
||||||
* @return the name of the Nth variable in scope of the call info. If the
|
|
||||||
* variable for the given index is not found, null is returned.
|
|
||||||
*/
|
|
||||||
private String getVariable(CallInfo callInfo, int index) {
|
|
||||||
int count = -1;
|
|
||||||
LocVars[] localVariables = callInfo.closure.p.locvars;
|
|
||||||
int pc = getCurrentPc(callInfo);
|
|
||||||
for (int i = 0; i < localVariables.length; i++) {
|
|
||||||
if (!isActiveVariable(pc, localVariables[i])) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
count++;
|
|
||||||
if (count == index) {
|
|
||||||
return localVariables[i].varname.toJavaString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a variable is in scope.
|
* Check if a variable is in scope.
|
||||||
* @param pc -- Current program counter.
|
* @param pc -- Current program counter.
|
||||||
@@ -745,71 +747,43 @@ public class DebugLuaState extends LuaState implements DebugRequestListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the active variables for the given call frame to the list of variables.
|
* Adds an active variable for the given call frame to the list of variables.
|
||||||
* @param variables -- the list of active variables.
|
* @param variables -- the list of active variables.
|
||||||
* @param variablesSeen -- variables already seen so far
|
* @param varName -- the name of the variable
|
||||||
* @param index -- index of the call frame
|
* @param index -- the value of the variable
|
||||||
*/
|
*/
|
||||||
private void addVariables(Vector variables, Hashtable variablesSeen, int index) {
|
private void addVariable(Vector variables, String varName, LValue value) {
|
||||||
CallInfo callInfo = calls[index];
|
|
||||||
int base = callInfo.base;
|
|
||||||
int top = callInfo.top < callInfo.base ? callInfo.base+1 : callInfo.top;
|
|
||||||
|
|
||||||
if (TRACE) {
|
int selectedVariableCount = variables.size();
|
||||||
dumpStack(index);
|
if (TRACE) {
|
||||||
}
|
System.out.print("\tVariable: " + varName);
|
||||||
|
System.out.print("\tValue: " + value);
|
||||||
int selectedVariableCount = 0;
|
System.out.print("\tN: "+selectedVariableCount);
|
||||||
for (int i = base; i < top; i++) {
|
}
|
||||||
String varName = getVariable(callInfo, i-base);
|
|
||||||
if (varName == null) {
|
|
||||||
// we don't care about the temporary variables and constants
|
|
||||||
// on the stack
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(TRACE) {
|
|
||||||
System.out.print("\tVariable: " + varName);
|
|
||||||
System.out.print("\tValue: " + stack[i]);
|
|
||||||
}
|
|
||||||
if (!variablesSeen.contains(varName) &&
|
|
||||||
!LexState.isReservedKeyword(varName)) {
|
|
||||||
variablesSeen.put(varName, varName);
|
|
||||||
LValue value = stack[i];
|
|
||||||
if (value != null) {
|
|
||||||
int type = value.luaGetType();
|
|
||||||
if (TRACE)
|
|
||||||
System.out.print("\tType: " + Lua.TYPE_NAMES[type]);
|
|
||||||
if (type == Lua.LUA_TTABLE) {
|
|
||||||
if (TRACE)
|
|
||||||
System.out.print(" (selected)");
|
|
||||||
variables.addElement(
|
|
||||||
new TableVariable(selectedVariableCount++,
|
|
||||||
varName,
|
|
||||||
type,
|
|
||||||
(LTable) value));
|
|
||||||
} else if (type == LUA_TTHREAD) {
|
|
||||||
// coroutines
|
|
||||||
} else if (type != LUA_TFUNCTION) {
|
|
||||||
if (TRACE)
|
|
||||||
System.out.print(" (selected)");
|
|
||||||
variables.addElement(
|
|
||||||
new Variable(selectedVariableCount++,
|
|
||||||
varName,
|
|
||||||
type,
|
|
||||||
value.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TRACE)
|
|
||||||
System.out.print("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if (value != null) {
|
||||||
* step over to next line
|
int type = value.luaGetType();
|
||||||
*/
|
if (TRACE)
|
||||||
|
System.out.print("\tType: " + value.luaGetTypeName());
|
||||||
|
if (type == Lua.LUA_TTABLE) {
|
||||||
|
variables.addElement(new TableVariable( selectedVariableCount, varName, type, (LTable) value));
|
||||||
|
} else if (type == LUA_TNUMBER || type == LUA_TBOOLEAN || type == LUA_TNIL) {
|
||||||
|
variables.addElement(new Variable(selectedVariableCount, varName, type, value.toString()));
|
||||||
|
} else if (type == LUA_TSTRING) {
|
||||||
|
variables.addElement(new Variable(selectedVariableCount, varName, type, "'"+value.toString()+"'"));
|
||||||
|
} else { // thread, userdata, function
|
||||||
|
variables.addElement(new Variable(selectedVariableCount, varName, type, "<"+value.luaGetTypeName().toJavaString()+">"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TRACE)
|
||||||
|
System.out.print("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* step over to next line
|
||||||
|
*/
|
||||||
public synchronized void stepOver() {
|
public synchronized void stepOver() {
|
||||||
suspended = false;
|
suspended = false;
|
||||||
stepping = STEP_OVER;
|
stepping = STEP_OVER;
|
||||||
@@ -818,8 +792,8 @@ public class DebugLuaState extends LuaState implements DebugRequestListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* step to the next statement
|
* step to the next statement
|
||||||
*/
|
*/
|
||||||
public synchronized void stepInto() {
|
public synchronized void stepInto() {
|
||||||
suspended = false;
|
suspended = false;
|
||||||
stepping = STEP_INTO;
|
stepping = STEP_INTO;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package org.luaj.debug;
|
package org.luaj.debug;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -38,19 +39,79 @@ import org.luaj.vm.Platform;
|
|||||||
|
|
||||||
public class DebugStackStateTest extends TestCase {
|
public class DebugStackStateTest extends TestCase {
|
||||||
|
|
||||||
public void testDebugStackState() throws InterruptedException, IOException {
|
protected void setUp() throws Exception {
|
||||||
String script = "src/test/res/test6.lua";
|
|
||||||
|
|
||||||
// set up the vm
|
|
||||||
System.setProperty(Platform.PROPERTY_LUAJ_DEBUG, "true");
|
System.setProperty(Platform.PROPERTY_LUAJ_DEBUG, "true");
|
||||||
Platform.setInstance(new TestPlatform() {
|
Platform.setInstance(new TestPlatform() {
|
||||||
public DebugNetSupport getDebugSupport() throws IOException {
|
public DebugNetSupport getDebugSupport() throws IOException {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
LuaC.install();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void testStackStateUpvalues() throws Exception {
|
||||||
|
tryScript(
|
||||||
|
"print( 'aaa' ); local a = 3;\n"+
|
||||||
|
"print( 'bbb' ); local f = function()\n"+
|
||||||
|
" print('in f'); local b = 4; a=33\n"+
|
||||||
|
" print( 'bbb' ); local g = function()\n"+
|
||||||
|
" print('in g'); local c = 6; b=444; a=333\n" +
|
||||||
|
" return c, b, a\n"+
|
||||||
|
" end; print( 'calling g' ); g(); return b, a\n"+
|
||||||
|
"end\n"+
|
||||||
|
"print('calling f'); f()\n"+
|
||||||
|
"print( 'returned from f' ); local d = 6\n"+
|
||||||
|
"return 123, a, b, c, d\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testStackStateLocals() throws Exception {
|
||||||
|
tryScript(
|
||||||
|
"print('hello, world')\n"+
|
||||||
|
"print('aaa'); local a = 3; print('aaa')\n"+
|
||||||
|
"print('bbb'); local b = 4; print('bbb')\n"+
|
||||||
|
"print('ccc'); local c = 5; print('ccc')\n"+
|
||||||
|
"print('ddd'); local d = 6; print('ddd')\n"+
|
||||||
|
"print( a,b,c,d )\n"+
|
||||||
|
"return 123\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryScript(String script) throws Exception {
|
||||||
|
|
||||||
|
// set up closure
|
||||||
|
final DebugLuaState vm = (DebugLuaState) Platform.newLuaState();
|
||||||
|
final InputStream is = new ByteArrayInputStream(script.getBytes());
|
||||||
|
final LPrototype p = LoadState.undump(vm, is, "script");
|
||||||
|
final LClosure c = p.newClosure( vm._G );
|
||||||
|
|
||||||
|
// suspend the vm right away
|
||||||
|
vm.suspend();
|
||||||
|
|
||||||
|
// start the call processing in its own thread
|
||||||
|
Thread t = new Thread() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
vm.doCall( c, new LValue[0] );
|
||||||
|
} catch ( Throwable e ) {
|
||||||
|
System.out.println(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
t.start();
|
||||||
|
|
||||||
|
for ( int i=0; i<20; i++ ) {
|
||||||
|
vm.stepInto();
|
||||||
|
printStackState(vm);
|
||||||
|
Thread.sleep(100);
|
||||||
|
}
|
||||||
|
vm.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void testDebugStackState() throws InterruptedException, IOException {
|
||||||
|
String script = "src/test/res/test6.lua";
|
||||||
|
|
||||||
final DebugLuaState state = (DebugLuaState) Platform.newLuaState();
|
final DebugLuaState state = (DebugLuaState) Platform.newLuaState();
|
||||||
LuaC.install();
|
|
||||||
InputStream is = new FileInputStream( script );
|
InputStream is = new FileInputStream( script );
|
||||||
LPrototype p = LoadState.undump(state, is, script);
|
LPrototype p = LoadState.undump(state, is, script);
|
||||||
|
|
||||||
@@ -88,4 +149,23 @@ public class DebugStackStateTest extends TestCase {
|
|||||||
Thread.sleep(500);
|
Thread.sleep(500);
|
||||||
System.out.println("--- callgraph="+state.getCallgraph() );
|
System.out.println("--- callgraph="+state.getCallgraph() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void printStackState(DebugLuaState vm) {
|
||||||
|
int n = vm.getCallgraph().length;
|
||||||
|
System.out.println("stacks: "+n);
|
||||||
|
for ( int j=0; j<n; j++ ) {
|
||||||
|
try {
|
||||||
|
Variable[] v = vm.getStack(j);
|
||||||
|
for ( int i=0; i<v.length; i++ )
|
||||||
|
System.out.println("v["+j+","+i+"]= index="+v[i].index+" "+v[i].name+"="+v[i].value+" type="+v[i].type);
|
||||||
|
} catch ( Throwable t ) {
|
||||||
|
System.out.println(t.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user