Improved local variable and upvalue reporting for functions.

This commit is contained in:
James Roseborough
2008-07-25 00:24:38 +00:00
parent 447bc5853a
commit 6c3b98cc82
3 changed files with 166 additions and 108 deletions

View File

@@ -60,4 +60,8 @@ public class UpVal {
return false; return false;
} }
} }
public boolean isClosed() {
return state == null;
}
} }

View File

@@ -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;

View File

@@ -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;
}
}
}
} }