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;
}
}
public boolean isClosed() {
return state == null;
}
}

View File

@@ -25,7 +25,6 @@ import java.io.IOException;
import java.util.Hashtable;
import java.util.Vector;
import org.luaj.compiler.LexState;
import org.luaj.debug.event.DebugEventBreakpoint;
import org.luaj.debug.event.DebugEventError;
import org.luaj.debug.net.DebugNetSupportBase;
@@ -39,6 +38,7 @@ import org.luaj.vm.CallInfo;
import org.luaj.vm.DebugNetSupport;
import org.luaj.vm.LClosure;
import org.luaj.vm.LPrototype;
import org.luaj.vm.LString;
import org.luaj.vm.LTable;
import org.luaj.vm.LValue;
import org.luaj.vm.LocVars;
@@ -173,6 +173,9 @@ public class DebugLuaState extends LuaState implements DebugRequestListener {
protected DebugNetSupportBase debugSupport;
protected LuaErrorException lastError;
final String uparrow = "^";
final String rtarrow = "~";
/**
* Creates an instance of DebugLuaState.
*
@@ -285,7 +288,7 @@ public class DebugLuaState extends LuaState implements DebugRequestListener {
throw new AbortException("aborted by debug client");
}
/*
//*
if (TRACE) {
System.out.println("entered debugHook on pc=" + pc + "...Line: " + getFileLine(cc));
for (int j = 0; j <= cc; j++) {
@@ -293,7 +296,7 @@ public class DebugLuaState extends LuaState implements DebugRequestListener {
dumpStack(j);
}
}
*/
//*/
synchronized (this) {
while (bSuspendOnStart) {
try {
@@ -620,22 +623,44 @@ public class DebugLuaState extends LuaState implements DebugRequestListener {
* @return the visible local variables on the given stack frame.
*/
public Variable[] getStack(int index) {
if (index < 0 || index >= calls.length) {
if (index < 0 || index > cc) {
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();
Hashtable variablesSeen = new Hashtable();
LPrototype p = calls[index].closure.p;
for (int i = index; i >= 0; i--) {
if (i == index || isInScope(p, calls[i])) {
addVariables(variables, variablesSeen, i);
int pc = getCurrentPc(callInfo);
// add upvalues
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()];
for (int i = 0; i < variables.size(); i++) {
result[i] = (Variable) variables.elementAt(i);
}
variables.copyInto(result);
return result;
}
@@ -690,7 +715,9 @@ public class DebugLuaState extends LuaState implements DebugRequestListener {
CallInfo callInfo = calls[index];
LPrototype prototype = callInfo.closure.p;
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);
for (int i = 0; i < localVariables.length; i++) {
@@ -708,31 +735,6 @@ public class DebugLuaState extends LuaState implements DebugRequestListener {
}
}
/**
* 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.
* @param pc -- Current program counter.
@@ -745,67 +747,39 @@ 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 variablesSeen -- variables already seen so far
* @param index -- index of the call frame
* @param varName -- the name of the variable
* @param index -- the value of the variable
*/
private void addVariables(Vector variables, Hashtable variablesSeen, int index) {
CallInfo callInfo = calls[index];
int base = callInfo.base;
int top = callInfo.top < callInfo.base ? callInfo.base+1 : callInfo.top;
private void addVariable(Vector variables, String varName, LValue value) {
int selectedVariableCount = variables.size();
if (TRACE) {
dumpStack(index);
}
int selectedVariableCount = 0;
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]);
System.out.print("\tValue: " + value);
System.out.print("\tN: "+selectedVariableCount);
}
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]);
System.out.print("\tType: " + value.luaGetTypeName());
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()));
}
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

View File

@@ -21,6 +21,7 @@
******************************************************************************/
package org.luaj.debug;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -38,19 +39,79 @@ import org.luaj.vm.Platform;
public class DebugStackStateTest extends TestCase {
public void testDebugStackState() throws InterruptedException, IOException {
String script = "src/test/res/test6.lua";
// set up the vm
protected void setUp() throws Exception {
System.setProperty(Platform.PROPERTY_LUAJ_DEBUG, "true");
Platform.setInstance(new TestPlatform() {
public DebugNetSupport getDebugSupport() throws IOException {
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();
LuaC.install();
InputStream is = new FileInputStream( script );
LPrototype p = LoadState.undump(state, is, script);
@@ -88,4 +149,23 @@ public class DebugStackStateTest extends TestCase {
Thread.sleep(500);
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;
}
}
}
}