1. improved stack state (correct scoping)

2. fixed the problem with debugger not pausing on first line
This commit is contained in:
Shu Lei
2007-10-30 01:37:26 +00:00
parent 3aeb6fc290
commit 1ebbabc9d6
4 changed files with 467 additions and 377 deletions

View File

@@ -6,6 +6,7 @@ import java.util.Stack;
import lua.io.Closure; import lua.io.Closure;
import lua.io.LoadState; import lua.io.LoadState;
import lua.io.LocVars;
import lua.io.Proto; import lua.io.Proto;
import lua.io.UpVal; import lua.io.UpVal;
import lua.value.LBoolean; import lua.value.LBoolean;
@@ -340,11 +341,21 @@ public class StackState extends Lua implements VM {
// loop until a return instruction is processed, // loop until a return instruction is processed,
// or the vm yields // or the vm yields
while (true) { while (true) {
debugAssert( ci == calls[cc] ); debugAssert( ci == calls[cc] );
if (TRACE) // sync up top
ci.top = top;
if (TRACE) {
Print.printState(this, base, top, base+p.maxstacksize, cl, ci.pc); Print.printState(this, base, top, base+p.maxstacksize, cl, ci.pc);
for (int j = 0; j <= cc; j++) {
System.out.println("calls[" + j + "]: " + calls[j].base + "," + calls[j].top);
LocVars[] localVars = calls[j].closure.p.locvars;
for (int t = 0; t < localVars.length; t++)
System.out.println("localVars[" + t + "]: " + localVars[t].varname);
}
}
// allow debug hooks a chance to operate // allow debug hooks a chance to operate
debugHooks( ci.pc ); debugHooks( ci.pc );
@@ -367,8 +378,7 @@ public class StackState extends Lua implements VM {
case StackState.OP_LOADBOOL: { case StackState.OP_LOADBOOL: {
b = StackState.GETARG_B(i); b = StackState.GETARG_B(i);
c = StackState.GETARG_C(i); c = StackState.GETARG_C(i);
this.stack[base + a] = (b != 0 ? LBoolean.TRUE this.stack[base + a] = (b != 0 ? LBoolean.TRUE : LBoolean.FALSE);
: LBoolean.FALSE);
if (c != 0) if (c != 0)
ci.pc++; /* skip next instruction (if C) */ ci.pc++; /* skip next instruction (if C) */
continue; continue;
@@ -535,7 +545,7 @@ public class StackState extends Lua implements VM {
// restore base // restore base
base = ci.base; base = ci.base;
continue; continue;
} }
@@ -555,11 +565,20 @@ public class StackState extends Lua implements VM {
this.top = base + b; this.top = base + b;
this.nresults = ci.nresults; this.nresults = ci.nresults;
--cc; --cc;
// make or set up the call // make or set up the call
if (this.stack[base].luaStackCall(this)) try {
return; if (this.stack[base].luaStackCall(this)) {
return;
}
} catch (RuntimeException e) {
// in case of error, we need to restore cc so that
// the debug can get the correct location where the error
// occured.
cc++;
throw e;
}
// adjustTop only for case when call was completed // adjustTop only for case when call was completed
// and number of args > 0. If call completed but // and number of args > 0. If call completed but
// c == 0, leave top to point to end of results // c == 0, leave top to point to end of results
@@ -704,7 +723,6 @@ public class StackState extends Lua implements VM {
continue; continue;
} }
} }
ci.top = top;
} }
} }

View File

@@ -1,24 +1,24 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2007 LuaJ. All rights reserved. * Copyright (c) 2007 LuaJ. All rights reserved.
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
******************************************************************************/ ******************************************************************************/
package lua.debug; package lua.debug;
import java.io.IOException; import java.io.IOException;
@@ -51,331 +51,365 @@ import lua.value.LValue;
public class DebugStackState extends StackState implements DebugRequestListener { public class DebugStackState extends StackState implements DebugRequestListener {
// 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_INTO = 2; protected static final int STEP_OVER = 1;
protected static final int STEP_RETURN = 3;
protected static final int STEP_INTO = 2;
protected static final int STEP_RETURN = 3;
protected int stepping = STEP_NONE; protected int stepping = STEP_NONE;
protected boolean shouldPauseForStepping = false; protected boolean shouldPauseForStepping = false;
protected int steppingFrame = -1; protected int steppingFrame = -1;
protected Hashtable breakpoints = new Hashtable();
protected Hashtable breakpoints = new Hashtable();
protected boolean exiting = false; protected boolean exiting = false;
protected boolean suspended = false; protected boolean suspended = false;
protected boolean isStarted = false;
protected int lastline = -1; protected int lastline = -1;
protected String lastSource; protected String lastSource;
protected DebugSupport debugSupport; protected DebugSupport debugSupport;
protected VMException lastException; protected VMException lastException;
public DebugStackState() {} public DebugStackState() {
public void setDebugSupport(DebugSupport debugSupport) throws IOException {
if (debugSupport == null) {
throw new IllegalArgumentException("DebugSupport cannot be null");
}
this.debugSupport = debugSupport;
debugSupport.setDebugStackState(this);
debugSupport.start();
}
protected void debugAssert(boolean b) {
if ( ! b )
error( "assert failure" );
} }
private String getFileLine(int cindex) {
String func = "?";
String line = "?";
String source = "?";
if ( cindex >= 0 ) {
CallInfo call = this.calls[cindex];
Proto p = call.closure.p;
if ( p != null && p.source != null )
source = p.source.toJavaString();
if ( p.lineinfo != null && p.lineinfo.length > call.pc )
line = String.valueOf( p.lineinfo[call.pc] );
// TODO: reverse lookup on function name ????
func = call.closure.toJavaString();
}
return source+":"+line+"("+func+")";
}
// override and fill in line number info
public void error(String message, int level) {
super.error( level<=0? message: getFileLine(cc+1-level)+": "+message );
}
// use line numbers by default public void setDebugSupport(DebugSupport debugSupport) throws IOException {
public void error(String message) { if (debugSupport == null) {
error(message, 1); throw new IllegalArgumentException("DebugSupport cannot be null");
} }
// intercept exceptions and fill in line numbers this.debugSupport = debugSupport;
public void exec() { debugSupport.setDebugStackState(this);
try { debugSupport.start();
super.exec(); }
} catch ( AbortException e ) {
protected void debugAssert(boolean b) {
if (!b)
error("assert failure");
}
private String getFileLine(int cindex) {
String func = "?";
String line = "?";
String source = "?";
if (cindex >= 0) {
CallInfo call = this.calls[cindex];
Proto p = call.closure.p;
if (p != null && p.source != null)
source = p.source.toJavaString();
if (p.lineinfo != null && p.lineinfo.length > call.pc)
line = String.valueOf(p.lineinfo[call.pc]);
// TODO: reverse lookup on function name ????
func = call.closure.toJavaString();
}
return source + ":" + line + "(" + func + ")";
}
// override and fill in line number info
public void error(String message, int level) {
super.error(level <= 0 ? message : getFileLine(cc + 1 - level) + ": "
+ message);
}
// use line numbers by default
public void error(String message) {
error(message, 1);
}
// intercept exceptions and fill in line numbers
public void exec() {
try {
super.exec();
} catch (AbortException e) {
// ignored. Client aborts the debugging session. // ignored. Client aborts the debugging session.
} catch ( VMException e ) { } catch (VMException e) {
// let VM exceptions be processed // let VM exceptions be processed
// the same as the base class to minimize differences // the same as the base class to minimize differences
// between the debug and non-debug behavior // between the debug and non-debug behavior
throw e; throw e;
} catch ( Exception e ) { } catch (Exception e) {
lastException = new VMException(e); lastException = new VMException(e);
debugSupport.notifyDebugEvent(new DebugEventError(e.getMessage())); debugSupport.notifyDebugEvent(new DebugEventError(e.getMessage()));
suspend(); suspend();
} }
} }
// debug hooks
// debug hooks public void debugHooks(int pc) {
public void debugHooks( int pc ) { if (exiting) {
if ( exiting ) { throw new AbortException("aborted by debug client");
throw new AbortException("aborted by debug client"); }
}
if (DebugUtils.IS_DEBUG)
if(DebugUtils.IS_DEBUG) DebugUtils.println("entered debugHook on pc=" + pc + "...");
DebugUtils.println("entered debugHook on pc=" + pc + "...");
synchronized (this) {
synchronized ( this ) { while (!isStarted) {
CallInfo currentCallInfo = calls[cc]; try {
Proto currentProto = currentCallInfo.closure.p; this.wait();
} catch (InterruptedException e) {
// if we are not stepping, we keep going if the line doesn't change e.printStackTrace();
int line = getLineNumber(currentCallInfo); }
String source = DebugUtils.getSourceFileName(currentProto.source);
if ( !isStepping() && lastline == line && source.equals(lastSource) ) {
return;
} }
if(DebugUtils.IS_DEBUG) CallInfo currentCallInfo = calls[cc];
DebugUtils.println("debugHook - executing line: " + line); Proto currentProto = currentCallInfo.closure.p;
// if we are not stepping, we would keep going if the line doesn't
// change
int line = getLineNumber(currentCallInfo);
String source = DebugUtils.getSourceFileName(currentProto.source);
if (!isStepping() && lastline == line && source.equals(lastSource)) {
return;
}
if (DebugUtils.IS_DEBUG)
DebugUtils.println("debugHook - executing line: " + line);
int i = currentProto.code[pc]; int i = currentProto.code[pc];
int opCode = StackState.GET_OPCODE(i); int opCode = StackState.GET_OPCODE(i);
if (isStepping() && if (isStepping() && opCode == StackState.OP_RETURN && cc == 0) {
opCode == StackState.OP_RETURN && cc == 0) { cancelStepping();
cancelStepping();
} else if (shouldPauseForStepping) { } else if (shouldPauseForStepping) {
shouldPauseForStepping = false; shouldPauseForStepping = false;
suspendOnStepping(); suspendOnStepping();
} else if ( stepping == STEP_INTO ) { } else if (stepping == STEP_INTO) {
if (lastline != line){ if (lastline != line) {
suspendOnStepping(); suspendOnStepping();
} else if (opCode == StackState.OP_CALL) { } else if (opCode == StackState.OP_CALL) {
shouldPauseForStepping = true; shouldPauseForStepping = true;
} }
} else if (stepping == STEP_OVER) { } else if (stepping == STEP_OVER) {
if ((lastline != line && steppingFrame == cc) || (steppingFrame > cc)) { if ((lastline != line && steppingFrame == cc)
suspendOnStepping(); || (steppingFrame > cc)) {
} suspendOnStepping();
}
} else if (stepping == STEP_RETURN) { } else if (stepping == STEP_RETURN) {
if ((opCode == StackState.OP_RETURN && cc == this.steppingFrame) || if ((opCode == StackState.OP_RETURN && cc == this.steppingFrame)
(opCode == StackState.OP_TAILCALL && cc == this.steppingFrame)){ || (opCode == StackState.OP_TAILCALL && cc == this.steppingFrame)) {
shouldPauseForStepping = true; shouldPauseForStepping = true;
} }
} }
// check for a break point if we aren't suspended already // check for a break point if we aren't suspended already
if ( !suspended && lastline != line) { if (!suspended && lastline != line) {
if (DebugUtils.IS_DEBUG) if (DebugUtils.IS_DEBUG)
DebugUtils.println("Source: " + currentProto.source); DebugUtils.println("Source: " + currentProto.source);
String fileName = DebugUtils.getSourceFileName(source);
String breakpointKey = constructBreakpointKey(fileName, line);
if (breakpoints.containsKey(breakpointKey)) {
if (DebugUtils.IS_DEBUG)
DebugUtils.println("hitting breakpoint "
+ constructBreakpointKey(fileName, line));
String breakpointKey = constructBreakpointKey(source, line); debugSupport.notifyDebugEvent(new DebugEventBreakpoint(
if (breakpoints.containsKey(breakpointKey)){ fileName, line));
if(DebugUtils.IS_DEBUG) suspended = true;
DebugUtils.println("hitting breakpoint " + constructBreakpointKey(source, line)); }
}
debugSupport.notifyDebugEvent(new DebugEventBreakpoint(source, line));
suspended = true;
}
}
// save line in case next op is a step
lastline = line;
lastSource = source;
// wait for a state change
while (suspended && !exiting ) {
try {
this.wait();
if(DebugUtils.IS_DEBUG)
DebugUtils.println("resuming execution...");
if (lastException != null) {
throw lastException;
}
} catch ( InterruptedException ie ) {
ie.printStackTrace();
}
}
}
}
private boolean isStepping() { // save line in case next op is a step
return stepping != STEP_NONE; lastline = line;
} lastSource = source;
private void cancelStepping() { // wait for a state change
debugSupport.notifyDebugEvent(new DebugEvent(DebugEventType.resumedOnSteppingEnd)); while (suspended && !exiting) {
stepping = STEP_NONE; try {
steppingFrame = -1; this.wait();
shouldPauseForStepping = false; if (DebugUtils.IS_DEBUG)
} DebugUtils.println("resuming execution...");
private void suspendOnStepping() { if (lastException != null) {
debugSupport.notifyDebugEvent(new DebugEvent(DebugEventType.suspendedOnStepping)); throw lastException;
suspended = true; }
steppingFrame = -1; } catch (InterruptedException ie) {
} ie.printStackTrace();
}
}
}
}
private boolean isStepping() {
return stepping != STEP_NONE;
}
private void cancelStepping() {
debugSupport.notifyDebugEvent(new DebugEvent(
DebugEventType.resumedOnSteppingEnd));
stepping = STEP_NONE;
steppingFrame = -1;
shouldPauseForStepping = false;
}
private void suspendOnStepping() {
debugSupport.notifyDebugEvent(new DebugEvent(
DebugEventType.suspendedOnStepping));
suspended = true;
steppingFrame = -1;
}
/** /**
* Get the current line number * Get the current line number
*
* @param pc program counter * @param pc program counter
* @return the line number corresponding to the pc * @return the line number corresponding to the pc
*/ */
private int getLineNumber(CallInfo ci) { private int getLineNumber(CallInfo ci) {
int[] lineNumbers = ci.closure.p.lineinfo; int[] lineNumbers = ci.closure.p.lineinfo;
int pc = ci.pc; int pc = ci.pc;
int line = (lineNumbers != null && lineNumbers.length > pc ? lineNumbers[pc] : -1); int line = (lineNumbers != null && lineNumbers.length > pc ? lineNumbers[pc]
: -1);
return line; return line;
} }
// ------------------ commands coming from the debugger ------------------- // ------------------ commands coming from the debugger -------------------
public DebugResponse handleRequest(DebugRequest request) { public DebugResponse handleRequest(DebugRequest request) {
if (this.debugSupport == null) { if (this.debugSupport == null) {
throw new IllegalStateException("DebugStackState is not equiped with DebugSupport."); throw new IllegalStateException(
} "DebugStackState is not equiped with DebugSupport.");
}
if (DebugUtils.IS_DEBUG)
DebugUtils.println("DebugStackState is handling request: " + request.toString()); if (DebugUtils.IS_DEBUG)
DebugUtils.println("DebugStackState is handling request: "
DebugRequestType requestType = request.getType(); + request.toString());
DebugRequestType requestType = request.getType();
if (DebugRequestType.start == requestType) { if (DebugRequestType.start == requestType) {
DebugEvent event = new DebugEvent(DebugEventType.started); DebugEvent event = new DebugEvent(DebugEventType.started);
debugSupport.notifyDebugEvent(event); debugSupport.notifyDebugEvent(event);
setStarted();
return DebugResponseSimple.SUCCESS; return DebugResponseSimple.SUCCESS;
} else if (DebugRequestType.exit == requestType) { } else if (DebugRequestType.exit == requestType) {
stop(); stop();
return DebugResponseSimple.SUCCESS; return DebugResponseSimple.SUCCESS;
} else if (DebugRequestType.suspend == requestType) { } else if (DebugRequestType.suspend == requestType) {
suspend(); suspend();
DebugEvent event = new DebugEvent(DebugEventType.suspendedByClient); DebugEvent event = new DebugEvent(DebugEventType.suspendedByClient);
debugSupport.notifyDebugEvent(event);
return DebugResponseSimple.SUCCESS;
} else if (DebugRequestType.resume == requestType) {
resume();
DebugEvent event = new DebugEvent(DebugEventType.resumedByClient);
debugSupport.notifyDebugEvent(event); debugSupport.notifyDebugEvent(event);
return DebugResponseSimple.SUCCESS; return DebugResponseSimple.SUCCESS;
} else if (DebugRequestType.lineBreakpointSet == requestType) { } else if (DebugRequestType.resume == requestType) {
DebugRequestLineBreakpointToggle setBreakpointRequest resume();
= (DebugRequestLineBreakpointToggle)request; DebugEvent event = new DebugEvent(DebugEventType.resumedByClient);
setBreakpoint(setBreakpointRequest.getSource(), setBreakpointRequest.getLineNumber()); debugSupport.notifyDebugEvent(event);
return DebugResponseSimple.SUCCESS; return DebugResponseSimple.SUCCESS;
} else if (DebugRequestType.lineBreakpointClear == requestType) { } else if (DebugRequestType.lineBreakpointSet == requestType) {
DebugRequestLineBreakpointToggle clearBreakpointRequest DebugRequestLineBreakpointToggle setBreakpointRequest = (DebugRequestLineBreakpointToggle) request;
= (DebugRequestLineBreakpointToggle)request; setBreakpoint(setBreakpointRequest.getSource(),
clearBreakpoint(clearBreakpointRequest.getSource(), clearBreakpointRequest.getLineNumber()); setBreakpointRequest.getLineNumber());
return DebugResponseSimple.SUCCESS; return DebugResponseSimple.SUCCESS;
} else if (DebugRequestType.callgraph == requestType) { } else if (DebugRequestType.lineBreakpointClear == requestType) {
DebugRequestLineBreakpointToggle clearBreakpointRequest = (DebugRequestLineBreakpointToggle) request;
clearBreakpoint(clearBreakpointRequest.getSource(),
clearBreakpointRequest.getLineNumber());
return DebugResponseSimple.SUCCESS;
} else if (DebugRequestType.callgraph == requestType) {
return new DebugResponseCallgraph(getCallgraph()); return new DebugResponseCallgraph(getCallgraph());
} else if (DebugRequestType.stack == requestType) { } else if (DebugRequestType.stack == requestType) {
DebugRequestStack stackRequest = (DebugRequestStack) request; DebugRequestStack stackRequest = (DebugRequestStack) request;
int index = stackRequest.getIndex(); int index = stackRequest.getIndex();
return new DebugResponseStack(getStack(index)); return new DebugResponseStack(getStack(index));
} else if (DebugRequestType.stepInto == requestType) { } else if (DebugRequestType.stepInto == requestType) {
DebugEvent event = new DebugEvent(DebugEventType.resumedOnSteppingInto); DebugEvent event = new DebugEvent(
DebugEventType.resumedOnSteppingInto);
debugSupport.notifyDebugEvent(event); debugSupport.notifyDebugEvent(event);
stepInto(); stepInto();
return DebugResponseSimple.SUCCESS; return DebugResponseSimple.SUCCESS;
} else if (DebugRequestType.stepOver == requestType) { } else if (DebugRequestType.stepOver == requestType) {
DebugEvent event = new DebugEvent(DebugEventType.resumedOnSteppingOver); DebugEvent event = new DebugEvent(
DebugEventType.resumedOnSteppingOver);
debugSupport.notifyDebugEvent(event); debugSupport.notifyDebugEvent(event);
stepOver(); stepOver();
return DebugResponseSimple.SUCCESS; return DebugResponseSimple.SUCCESS;
} else if (DebugRequestType.stepReturn == requestType) { } else if (DebugRequestType.stepReturn == requestType) {
DebugEvent event = new DebugEvent(DebugEventType.resumedOnSteppingReturn); DebugEvent event = new DebugEvent(
DebugEventType.resumedOnSteppingReturn);
debugSupport.notifyDebugEvent(event); debugSupport.notifyDebugEvent(event);
stepReturn(); stepReturn();
return DebugResponseSimple.SUCCESS; return DebugResponseSimple.SUCCESS;
} }
throw new java.lang.IllegalArgumentException( "unkown request type: "+ request.getType()); throw new java.lang.IllegalArgumentException("unkown request type: "
} + request.getType());
}
/** /**
* suspend the execution * suspend the execution
*/ */
public void suspend() { public void suspend() {
synchronized ( this ) { synchronized (this) {
suspended = true; suspended = true;
stepping = STEP_NONE;
lastline = -1;
this.notify();
}
}
/**
* resume the execution
*/
public void resume() {
synchronized ( this ) {
suspended = false;
stepping = STEP_NONE; stepping = STEP_NONE;
this.notify(); lastline = -1;
} this.notify();
} }
}
protected void setStarted() {
synchronized (this) {
isStarted = true;
this.notify();
}
}
/**
* resume the execution
*/
public void resume() {
synchronized (this) {
suspended = false;
stepping = STEP_NONE;
this.notify();
}
}
public void stop() { public void stop() {
if (this.debugSupport == null) { if (this.debugSupport == null) {
throw new IllegalStateException("DebugStackState is not equiped with DebugSupport."); throw new IllegalStateException(
} "DebugStackState is not equiped with DebugSupport.");
}
DebugEvent event = new DebugEvent(DebugEventType.terminated); DebugEvent event = new DebugEvent(DebugEventType.terminated);
debugSupport.notifyDebugEvent(event); debugSupport.notifyDebugEvent(event);
new Timer().schedule(new TimerTask() { new Timer().schedule(new TimerTask() {
public void run () { public void run() {
debugSupport.stop(); debugSupport.stop();
debugSupport = null; debugSupport = null;
} }
}, 500); }, 500);
exit(); exit();
} }
/** /**
* terminate the execution * terminate the execution
*/ */
public void exit() { public void exit() {
synchronized ( this ) { synchronized (this) {
exiting = true; exiting = true;
this.notify(); this.notify();
} }
} }
/** /**
* set breakpoint at line N * set breakpoint at line N
* @param N the line to set the breakpoint at *
* @param N
* the line to set the breakpoint at
*/ */
public void setBreakpoint(String source, int lineNumber) { public void setBreakpoint(String source, int lineNumber) {
if (DebugUtils.IS_DEBUG) { String fileName = DebugUtils.getSourceFileName(source);
DebugUtils.print("source: " + source + " line:" + lineNumber); String breakpointKey = constructBreakpointKey(fileName, lineNumber);
DebugUtils.println("adding breakpoint " + constructBreakpointKey(source, lineNumber));
} if (DebugUtils.IS_DEBUG)
DebugUtils.println("adding breakpoint " + breakpointKey);
synchronized ( this ) {
String normalizedFileName = DebugUtils.getSourceFileName(source); breakpoints.put(breakpointKey, Boolean.TRUE);
breakpoints.put(constructBreakpointKey(normalizedFileName, lineNumber), Boolean.TRUE ); }
}
}
protected String constructBreakpointKey(String source, int lineNumber) { protected String constructBreakpointKey(String source, int lineNumber) {
return source + ":" + lineNumber; return source + ":" + lineNumber;
} }
@@ -383,139 +417,172 @@ public class DebugStackState extends StackState implements DebugRequestListener
/** /**
* clear breakpoint at line lineNumber of source source * clear breakpoint at line lineNumber of source source
*/ */
public void clearBreakpoint(String source, int lineNumber) { public void clearBreakpoint(String source, int lineNumber) {
if(DebugUtils.IS_DEBUG) String fileName = DebugUtils.getSourceFileName(source);
DebugUtils.println("removing breakpoint " + constructBreakpointKey(source, lineNumber)); String breakpointKey = constructBreakpointKey(fileName, lineNumber);
synchronized ( this ) {
String normalizedFileName = DebugUtils.getSourceFileName(source);
breakpoints.remove(constructBreakpointKey(normalizedFileName, lineNumber));
}
}
/**
* return the current call graph (i.e. stack frames from
* old to new, include information about file, method, etc.)
*/
public StackFrame[] getCallgraph() {
int n = cc;
if ( n < 0 || n >= calls.length )
return new StackFrame[0];
StackFrame[] frames = new StackFrame[n+1];
for ( int i = 0; i <= n; i++ ) {
CallInfo ci = calls[i];
frames[i] = new StackFrame(ci, getLineNumber(ci));
}
return frames;
}
public Variable[] getStack(int index) { if (DebugUtils.IS_DEBUG)
DebugUtils.println("removing breakpoint " + breakpointKey);
breakpoints.remove(breakpointKey);
}
/**
* return the current call graph (i.e. stack frames from old to new, include
* information about file, method, etc.)
*/
public StackFrame[] getCallgraph() {
int n = cc;
if (n < 0 || n >= calls.length)
return new StackFrame[0];
StackFrame[] frames = new StackFrame[n + 1];
for (int i = 0; i <= n; i++) {
CallInfo ci = calls[i];
frames[i] = new StackFrame(ci, getLineNumber(ci));
}
return frames;
}
public Variable[] getStack(int index) {
if (index < 0 || index >= calls.length) { if (index < 0 || index >= calls.length) {
//TODO: this is an error, handle it differently throw new RuntimeException("invalid stack index");
return new Variable[0]; }
Vector variables = new Vector();
Hashtable variablesSeen = new Hashtable();
for (int i = index; i >= 0; i--) {
addVariables(variables, variablesSeen, i);
}
Variable[] result = new Variable[variables.size()];
for (int i = 0; i < variables.size(); i++) {
result[i] = (Variable) variables.elementAt(i);
}
return result;
}
private String getVariable(CallInfo callInfo, int index) {
Proto prototype = callInfo.closure.p;
int count = -1;
LocVars[] localVariables = prototype.locvars;
for (int i = 0; i < localVariables.length; i++) {
if (callInfo.pc < localVariables[i].startpc ||
callInfo.pc > localVariables[i].endpc) {
continue;
} else {
count++;
if (count == index) {
return localVariables[i].varname.toJavaString();
}
}
} }
return null;
}
private void addVariables(Vector variables, Hashtable variablesSeen, int index) {
CallInfo callInfo = calls[index]; CallInfo callInfo = calls[index];
if(DebugUtils.IS_DEBUG)
DebugUtils.println("Stack Frame: " + index + "[" + callInfo.base + "," + callInfo.top + "]");
int top = callInfo.top < callInfo.base ? callInfo.base : callInfo.top;
Proto prototype = callInfo.closure.p; Proto prototype = callInfo.closure.p;
LocVars[] localVariables = prototype.locvars; int base = callInfo.base;
Vector variables = new Vector(); int top = callInfo.top < callInfo.base ? callInfo.base+1 : callInfo.top;
int localVariableCount = 0;
Hashtable variablesSeen = new Hashtable(); if (DebugUtils.IS_DEBUG) {
for (int i = 0; localVariables != null && i < localVariables.length && i <= top; i++) { DebugUtils.println("Stack Frame: " + index + " [" + base + "," + top + "], # of localvars: " + prototype.locvars.length + ", pc=" + callInfo.pc);
String varName = localVariables[i].varname.toString(); for (int i = 0; i < prototype.locvars.length; i++) {
DebugUtils.println("localvars[" + i + "]: " + prototype.locvars[i].varname + "(" + prototype.locvars[i].startpc + "," + prototype.locvars[i].endpc + ")");
}
for (int i = base; i < top; i++){
DebugUtils.println("stack[" + i + "]=" + stack[i]);
}
}
int selectedVariableCount = 0;
for (int i = base; i < top; i++) {
String varName = getVariable(callInfo, i-base);
if (varName == null) {
continue;
}
if(DebugUtils.IS_DEBUG) { if(DebugUtils.IS_DEBUG) {
DebugUtils.print("\tVariable: " + varName); DebugUtils.print("\tVariable: " + varName);
DebugUtils.print("\tValue: " + stack[callInfo.base + i]); DebugUtils.print("\tValue: " + stack[i]);
} }
if (!variablesSeen.contains(varName) && if (!variablesSeen.contains(varName) &&
!LexState.isReservedKeyword(varName)) { !LexState.isReservedKeyword(varName)) {
variablesSeen.put(varName, varName); variablesSeen.put(varName, varName);
LValue value = stack[callInfo.base + i]; LValue value = stack[i];
if (value != null) { if (value != null) {
int type = value.luaGetType(); int type = value.luaGetType();
if (DebugUtils.IS_DEBUG) if (DebugUtils.IS_DEBUG)
DebugUtils.print("\tType: " + Lua.TYPE_NAMES[type]); DebugUtils.print("\tType: " + Lua.TYPE_NAMES[type]);
if (type == Lua.LUA_TTABLE) { if (type == Lua.LUA_TTABLE) {
if (DebugUtils.IS_DEBUG) if (DebugUtils.IS_DEBUG)
DebugUtils.println(" (selected)"); DebugUtils.print(" (selected)");
variables.addElement( variables.addElement(
new TableVariable(localVariableCount++, new TableVariable(selectedVariableCount++,
varName, varName,
type, type,
(LTable) value)); (LTable) value));
} else if (type != Lua.LUA_TFUNCTION && } else if (type == LUA_TTHREAD) {
type != LUA_TTHREAD) { // coroutines
if (DebugUtils.IS_DEBUG) } else if (type != LUA_TFUNCTION) {
DebugUtils.println(" (selected)"); if (DebugUtils.IS_DEBUG)
DebugUtils.print(" (selected)");
variables.addElement( variables.addElement(
new Variable(localVariableCount++, new Variable(selectedVariableCount++,
varName, varName,
type, type,
value.toString())); value.toString()));
} else {
if (DebugUtils.IS_DEBUG)
DebugUtils.println("");
} }
} else {
if (DebugUtils.IS_DEBUG)
DebugUtils.println("");
} }
} else {
if (DebugUtils.IS_DEBUG)
DebugUtils.println("");
} }
if (DebugUtils.IS_DEBUG)
DebugUtils.println("");
} }
}
Variable[] result = new Variable[variables.size()];
for (int i = 0; i < variables.size(); i++) {
result[i] = (Variable) variables.elementAt(i);
}
return result;
}
/** /**
* step over to next line * step over to next line
*/ */
public void stepOver() { public void stepOver() {
synchronized ( this ) { synchronized (this) {
if (DebugUtils.IS_DEBUG) if (DebugUtils.IS_DEBUG)
DebugUtils.println("stepOver on cc=" + cc + "..."); DebugUtils.println("stepOver on cc=" + cc + "...");
suspended = false;
stepping = STEP_OVER;
steppingFrame = cc;
this.notify();
}
}
suspended = false;
stepping = STEP_OVER;
steppingFrame = cc;
this.notify();
}
}
/** /**
* step a single statement * step a single statement
*/ */
public void stepInto() { public void stepInto() {
synchronized ( this ) { synchronized (this) {
suspended = false; suspended = false;
stepping = STEP_INTO; stepping = STEP_INTO;
this.notify(); this.notify();
} }
} }
/** /**
* return from the method call * return from the method call
*/ */
public void stepReturn() { public void stepReturn() {
synchronized ( this ) { synchronized (this) {
if (DebugUtils.IS_DEBUG) if (DebugUtils.IS_DEBUG)
DebugUtils.println("stepReturn on cc=" + cc + "..."); DebugUtils.println("stepReturn on cc=" + cc + "...");
suspended = false; suspended = false;
stepping = STEP_RETURN; stepping = STEP_RETURN;
steppingFrame = cc; steppingFrame = cc;
this.notify(); this.notify();
} }
} }
} }

View File

@@ -48,6 +48,12 @@ public class DebugUtils {
if (!LoadState.SOURCE_BINARY_STRING.equals(sourceStr)) { if (!LoadState.SOURCE_BINARY_STRING.equals(sourceStr)) {
sourceStr = sourceStr.replace('\\', '/'); sourceStr = sourceStr.replace('\\', '/');
} }
int index = sourceStr.lastIndexOf('/');
if (index != -1) {
sourceStr = sourceStr.substring(index + 1);
}
return sourceStr; return sourceStr;
} }
} }

View File

@@ -210,8 +210,7 @@ public class StandardLuaJVM {
DebugUtils.println("start debugging..."); DebugUtils.println("start debugging...");
DebugSupport debugSupport DebugSupport debugSupport
= new DebugSupportImpl(getRequestPort(), getEventPort()); = new DebugSupportImpl(getRequestPort(), getEventPort());
getDebugState().setDebugSupport(debugSupport); getDebugState().setDebugSupport(debugSupport);
getDebugState().suspend();
// create closure and execute // create closure and execute
final Closure c = new Closure(state, p); final Closure c = new Closure(state, p);