From 1ebbabc9d636c83682ce9c5c99b03de79dd07eb6 Mon Sep 17 00:00:00 2001 From: Shu Lei Date: Tue, 30 Oct 2007 01:37:26 +0000 Subject: [PATCH] 1. improved stack state (correct scoping) 2. fixed the problem with debugger not pausing on first line --- src/main/java/lua/StackState.java | 42 +- src/main/java/lua/debug/DebugStackState.java | 793 ++++++++++-------- src/main/java/lua/debug/DebugUtils.java | 6 + .../java/lua/debug/j2se/StandardLuaJVM.java | 3 +- 4 files changed, 467 insertions(+), 377 deletions(-) diff --git a/src/main/java/lua/StackState.java b/src/main/java/lua/StackState.java index 5374b1e7..0f1450ba 100644 --- a/src/main/java/lua/StackState.java +++ b/src/main/java/lua/StackState.java @@ -6,6 +6,7 @@ import java.util.Stack; import lua.io.Closure; import lua.io.LoadState; +import lua.io.LocVars; import lua.io.Proto; import lua.io.UpVal; import lua.value.LBoolean; @@ -340,11 +341,21 @@ public class StackState extends Lua implements VM { // loop until a return instruction is processed, // or the vm yields while (true) { - debugAssert( ci == calls[cc] ); - - if (TRACE) + debugAssert( ci == calls[cc] ); + + // sync up top + ci.top = top; + + if (TRACE) { 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 debugHooks( ci.pc ); @@ -367,8 +378,7 @@ public class StackState extends Lua implements VM { case StackState.OP_LOADBOOL: { b = StackState.GETARG_B(i); c = StackState.GETARG_C(i); - this.stack[base + a] = (b != 0 ? LBoolean.TRUE - : LBoolean.FALSE); + this.stack[base + a] = (b != 0 ? LBoolean.TRUE : LBoolean.FALSE); if (c != 0) ci.pc++; /* skip next instruction (if C) */ continue; @@ -535,7 +545,7 @@ public class StackState extends Lua implements VM { // restore base base = ci.base; - + continue; } @@ -555,11 +565,20 @@ public class StackState extends Lua implements VM { this.top = base + b; this.nresults = ci.nresults; --cc; - + // make or set up the call - if (this.stack[base].luaStackCall(this)) - return; - + try { + 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 // and number of args > 0. If call completed but // c == 0, leave top to point to end of results @@ -704,7 +723,6 @@ public class StackState extends Lua implements VM { continue; } } - ci.top = top; } } diff --git a/src/main/java/lua/debug/DebugStackState.java b/src/main/java/lua/debug/DebugStackState.java index 6e325bd0..513e46d8 100644 --- a/src/main/java/lua/debug/DebugStackState.java +++ b/src/main/java/lua/debug/DebugStackState.java @@ -1,24 +1,24 @@ /******************************************************************************* -* Copyright (c) 2007 LuaJ. All rights reserved. -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* 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 -* THE SOFTWARE. -******************************************************************************/ + * Copyright (c) 2007 LuaJ. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * 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 + * THE SOFTWARE. + ******************************************************************************/ package lua.debug; import java.io.IOException; @@ -51,331 +51,365 @@ import lua.value.LValue; public class DebugStackState extends StackState implements DebugRequestListener { - // stepping constants and stepping state - 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_RETURN = 3; + // stepping constants and stepping state + 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_RETURN = 3; + protected int stepping = STEP_NONE; protected boolean shouldPauseForStepping = false; protected int steppingFrame = -1; - - protected Hashtable breakpoints = new Hashtable(); + protected Hashtable breakpoints = new Hashtable(); protected boolean exiting = false; protected boolean suspended = false; + protected boolean isStarted = false; protected int lastline = -1; protected String lastSource; - protected DebugSupport debugSupport; + protected DebugSupport debugSupport; protected VMException lastException; - - 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" ); + public DebugStackState() { } - - 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 ) { + 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 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. - } catch ( VMException e ) { - // let VM exceptions be processed - // the same as the base class to minimize differences - // between the debug and non-debug behavior - throw e; - } catch ( Exception e ) { - lastException = new VMException(e); - debugSupport.notifyDebugEvent(new DebugEventError(e.getMessage())); - suspend(); - } - } - - - // debug hooks - public void debugHooks( int pc ) { - if ( exiting ) { - throw new AbortException("aborted by debug client"); - } - - if(DebugUtils.IS_DEBUG) - DebugUtils.println("entered debugHook on pc=" + pc + "..."); - - synchronized ( this ) { - CallInfo currentCallInfo = calls[cc]; - Proto currentProto = currentCallInfo.closure.p; - - // if we are not stepping, we 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; + } catch (VMException e) { + // let VM exceptions be processed + // the same as the base class to minimize differences + // between the debug and non-debug behavior + throw e; + } catch (Exception e) { + lastException = new VMException(e); + debugSupport.notifyDebugEvent(new DebugEventError(e.getMessage())); + suspend(); + } + } + + // debug hooks + public void debugHooks(int pc) { + if (exiting) { + throw new AbortException("aborted by debug client"); + } + + if (DebugUtils.IS_DEBUG) + DebugUtils.println("entered debugHook on pc=" + pc + "..."); + + synchronized (this) { + while (!isStarted) { + try { + this.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } } - - if(DebugUtils.IS_DEBUG) - DebugUtils.println("debugHook - executing line: " + line); - + + CallInfo currentCallInfo = calls[cc]; + 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 opCode = StackState.GET_OPCODE(i); - if (isStepping() && - opCode == StackState.OP_RETURN && cc == 0) { - cancelStepping(); + if (isStepping() && opCode == StackState.OP_RETURN && cc == 0) { + cancelStepping(); } else if (shouldPauseForStepping) { - shouldPauseForStepping = false; - suspendOnStepping(); - } else if ( stepping == STEP_INTO ) { - if (lastline != line){ - suspendOnStepping(); - } else if (opCode == StackState.OP_CALL) { - shouldPauseForStepping = true; - } - } else if (stepping == STEP_OVER) { - if ((lastline != line && steppingFrame == cc) || (steppingFrame > cc)) { - suspendOnStepping(); - } + shouldPauseForStepping = false; + suspendOnStepping(); + } else if (stepping == STEP_INTO) { + if (lastline != line) { + suspendOnStepping(); + } else if (opCode == StackState.OP_CALL) { + shouldPauseForStepping = true; + } + } else if (stepping == STEP_OVER) { + if ((lastline != line && steppingFrame == cc) + || (steppingFrame > cc)) { + suspendOnStepping(); + } } else if (stepping == STEP_RETURN) { - if ((opCode == StackState.OP_RETURN && cc == this.steppingFrame) || - (opCode == StackState.OP_TAILCALL && cc == this.steppingFrame)){ - shouldPauseForStepping = true; - } + if ((opCode == StackState.OP_RETURN && cc == this.steppingFrame) + || (opCode == StackState.OP_TAILCALL && cc == this.steppingFrame)) { + shouldPauseForStepping = true; + } } - + // check for a break point if we aren't suspended already - if ( !suspended && lastline != line) { + if (!suspended && lastline != line) { 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); - if (breakpoints.containsKey(breakpointKey)){ - if(DebugUtils.IS_DEBUG) - 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(); - } - } - } - } + debugSupport.notifyDebugEvent(new DebugEventBreakpoint( + fileName, line)); + suspended = true; + } + } - private boolean isStepping() { - return stepping != STEP_NONE; - } + // save line in case next op is a step + lastline = line; + lastSource = source; - private void cancelStepping() { - debugSupport.notifyDebugEvent(new DebugEvent(DebugEventType.resumedOnSteppingEnd)); - stepping = STEP_NONE; - steppingFrame = -1; - shouldPauseForStepping = false; - } + // wait for a state change + while (suspended && !exiting) { + try { + this.wait(); + if (DebugUtils.IS_DEBUG) + DebugUtils.println("resuming execution..."); - private void suspendOnStepping() { - debugSupport.notifyDebugEvent(new DebugEvent(DebugEventType.suspendedOnStepping)); - suspended = true; - steppingFrame = -1; - } + if (lastException != null) { + throw lastException; + } + } 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 + * * @param pc program counter * @return the line number corresponding to the pc */ private int getLineNumber(CallInfo ci) { int[] lineNumbers = ci.closure.p.lineinfo; 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; } - - // ------------------ commands coming from the debugger ------------------- - - public DebugResponse handleRequest(DebugRequest request) { - if (this.debugSupport == null) { - throw new IllegalStateException("DebugStackState is not equiped with DebugSupport."); - } - - if (DebugUtils.IS_DEBUG) - DebugUtils.println("DebugStackState is handling request: " + request.toString()); - - DebugRequestType requestType = request.getType(); + + // ------------------ commands coming from the debugger ------------------- + + public DebugResponse handleRequest(DebugRequest request) { + if (this.debugSupport == null) { + throw new IllegalStateException( + "DebugStackState is not equiped with DebugSupport."); + } + + if (DebugUtils.IS_DEBUG) + DebugUtils.println("DebugStackState is handling request: " + + request.toString()); + + DebugRequestType requestType = request.getType(); if (DebugRequestType.start == requestType) { DebugEvent event = new DebugEvent(DebugEventType.started); - debugSupport.notifyDebugEvent(event); + debugSupport.notifyDebugEvent(event); + setStarted(); return DebugResponseSimple.SUCCESS; } else if (DebugRequestType.exit == requestType) { stop(); return DebugResponseSimple.SUCCESS; - } else if (DebugRequestType.suspend == requestType) { - suspend(); + } else if (DebugRequestType.suspend == requestType) { + suspend(); 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); return DebugResponseSimple.SUCCESS; - } else if (DebugRequestType.lineBreakpointSet == requestType) { - DebugRequestLineBreakpointToggle setBreakpointRequest - = (DebugRequestLineBreakpointToggle)request; - setBreakpoint(setBreakpointRequest.getSource(), setBreakpointRequest.getLineNumber()); + } else if (DebugRequestType.resume == requestType) { + resume(); + DebugEvent event = new DebugEvent(DebugEventType.resumedByClient); + debugSupport.notifyDebugEvent(event); return DebugResponseSimple.SUCCESS; - } else if (DebugRequestType.lineBreakpointClear == requestType) { - DebugRequestLineBreakpointToggle clearBreakpointRequest - = (DebugRequestLineBreakpointToggle)request; - clearBreakpoint(clearBreakpointRequest.getSource(), clearBreakpointRequest.getLineNumber()); + } else if (DebugRequestType.lineBreakpointSet == requestType) { + DebugRequestLineBreakpointToggle setBreakpointRequest = (DebugRequestLineBreakpointToggle) request; + setBreakpoint(setBreakpointRequest.getSource(), + setBreakpointRequest.getLineNumber()); 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()); - } else if (DebugRequestType.stack == requestType) { + } else if (DebugRequestType.stack == requestType) { DebugRequestStack stackRequest = (DebugRequestStack) request; int index = stackRequest.getIndex(); return new DebugResponseStack(getStack(index)); - } else if (DebugRequestType.stepInto == requestType) { - DebugEvent event = new DebugEvent(DebugEventType.resumedOnSteppingInto); + } else if (DebugRequestType.stepInto == requestType) { + DebugEvent event = new DebugEvent( + DebugEventType.resumedOnSteppingInto); debugSupport.notifyDebugEvent(event); - stepInto(); + stepInto(); return DebugResponseSimple.SUCCESS; - } else if (DebugRequestType.stepOver == requestType) { - DebugEvent event = new DebugEvent(DebugEventType.resumedOnSteppingOver); + } else if (DebugRequestType.stepOver == requestType) { + DebugEvent event = new DebugEvent( + DebugEventType.resumedOnSteppingOver); debugSupport.notifyDebugEvent(event); stepOver(); return DebugResponseSimple.SUCCESS; - } else if (DebugRequestType.stepReturn == requestType) { - DebugEvent event = new DebugEvent(DebugEventType.resumedOnSteppingReturn); + } else if (DebugRequestType.stepReturn == requestType) { + DebugEvent event = new DebugEvent( + DebugEventType.resumedOnSteppingReturn); debugSupport.notifyDebugEvent(event); stepReturn(); 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 */ - public void suspend() { - synchronized ( this ) { - suspended = true; - stepping = STEP_NONE; - lastline = -1; - this.notify(); - } - } - - /** - * resume the execution - */ - public void resume() { - synchronized ( this ) { - suspended = false; + public void suspend() { + synchronized (this) { + suspended = true; 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() { - if (this.debugSupport == null) { - throw new IllegalStateException("DebugStackState is not equiped with DebugSupport."); - } - + if (this.debugSupport == null) { + throw new IllegalStateException( + "DebugStackState is not equiped with DebugSupport."); + } + DebugEvent event = new DebugEvent(DebugEventType.terminated); debugSupport.notifyDebugEvent(event); new Timer().schedule(new TimerTask() { - public void run () { - debugSupport.stop(); - debugSupport = null; - } + public void run() { + debugSupport.stop(); + debugSupport = null; + } }, 500); exit(); } - - /** + + /** * terminate the execution */ - public void exit() { - synchronized ( this ) { - exiting = true; - this.notify(); - } - } - + public void exit() { + synchronized (this) { + exiting = true; + this.notify(); + } + } + /** * 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) { - if (DebugUtils.IS_DEBUG) { - DebugUtils.print("source: " + source + " line:" + lineNumber); - DebugUtils.println("adding breakpoint " + constructBreakpointKey(source, lineNumber)); - } - - synchronized ( this ) { - String normalizedFileName = DebugUtils.getSourceFileName(source); - breakpoints.put(constructBreakpointKey(normalizedFileName, lineNumber), Boolean.TRUE ); - } - } - + public void setBreakpoint(String source, int lineNumber) { + String fileName = DebugUtils.getSourceFileName(source); + String breakpointKey = constructBreakpointKey(fileName, lineNumber); + + if (DebugUtils.IS_DEBUG) + DebugUtils.println("adding breakpoint " + breakpointKey); + + breakpoints.put(breakpointKey, Boolean.TRUE); + } + protected String constructBreakpointKey(String source, int lineNumber) { return source + ":" + lineNumber; } @@ -383,139 +417,172 @@ public class DebugStackState extends StackState implements DebugRequestListener /** * clear breakpoint at line lineNumber of source source */ - public void clearBreakpoint(String source, int lineNumber) { - if(DebugUtils.IS_DEBUG) - DebugUtils.println("removing breakpoint " + constructBreakpointKey(source, 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 void clearBreakpoint(String source, int lineNumber) { + String fileName = DebugUtils.getSourceFileName(source); + String breakpointKey = constructBreakpointKey(fileName, lineNumber); - 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) { - //TODO: this is an error, handle it differently - return new Variable[0]; + throw new RuntimeException("invalid stack index"); + } + + 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]; - 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; - LocVars[] localVariables = prototype.locvars; - Vector variables = new Vector(); - int localVariableCount = 0; - Hashtable variablesSeen = new Hashtable(); - for (int i = 0; localVariables != null && i < localVariables.length && i <= top; i++) { - String varName = localVariables[i].varname.toString(); + int base = callInfo.base; + int top = callInfo.top < callInfo.base ? callInfo.base+1 : callInfo.top; + + if (DebugUtils.IS_DEBUG) { + DebugUtils.println("Stack Frame: " + index + " [" + base + "," + top + "], # of localvars: " + prototype.locvars.length + ", pc=" + callInfo.pc); + 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) { - DebugUtils.print("\tVariable: " + varName); - DebugUtils.print("\tValue: " + stack[callInfo.base + i]); + DebugUtils.print("\tVariable: " + varName); + DebugUtils.print("\tValue: " + stack[i]); } if (!variablesSeen.contains(varName) && !LexState.isReservedKeyword(varName)) { variablesSeen.put(varName, varName); - LValue value = stack[callInfo.base + i]; + LValue value = stack[i]; if (value != null) { int type = value.luaGetType(); if (DebugUtils.IS_DEBUG) - DebugUtils.print("\tType: " + Lua.TYPE_NAMES[type]); + DebugUtils.print("\tType: " + Lua.TYPE_NAMES[type]); if (type == Lua.LUA_TTABLE) { - if (DebugUtils.IS_DEBUG) - DebugUtils.println(" (selected)"); + if (DebugUtils.IS_DEBUG) + DebugUtils.print(" (selected)"); variables.addElement( - new TableVariable(localVariableCount++, + new TableVariable(selectedVariableCount++, varName, type, (LTable) value)); - } else if (type != Lua.LUA_TFUNCTION && - type != LUA_TTHREAD) { - if (DebugUtils.IS_DEBUG) - DebugUtils.println(" (selected)"); + } else if (type == LUA_TTHREAD) { + // coroutines + } else if (type != LUA_TFUNCTION) { + if (DebugUtils.IS_DEBUG) + DebugUtils.print(" (selected)"); variables.addElement( - new Variable(localVariableCount++, + new Variable(selectedVariableCount++, varName, type, 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 */ - public void stepOver() { - synchronized ( this ) { - if (DebugUtils.IS_DEBUG) - DebugUtils.println("stepOver on cc=" + cc + "..."); + public void stepOver() { + synchronized (this) { + if (DebugUtils.IS_DEBUG) + 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 */ - public void stepInto() { - synchronized ( this ) { + public void stepInto() { + synchronized (this) { suspended = false; - stepping = STEP_INTO; - this.notify(); - } - } - + stepping = STEP_INTO; + this.notify(); + } + } + /** * return from the method call */ - public void stepReturn() { - synchronized ( this ) { - if (DebugUtils.IS_DEBUG) - DebugUtils.println("stepReturn on cc=" + cc + "..."); - + public void stepReturn() { + synchronized (this) { + if (DebugUtils.IS_DEBUG) + DebugUtils.println("stepReturn on cc=" + cc + "..."); + suspended = false; - stepping = STEP_RETURN; - steppingFrame = cc; - this.notify(); - } - } + stepping = STEP_RETURN; + steppingFrame = cc; + this.notify(); + } + } } diff --git a/src/main/java/lua/debug/DebugUtils.java b/src/main/java/lua/debug/DebugUtils.java index a1f9950f..65cf2fa4 100644 --- a/src/main/java/lua/debug/DebugUtils.java +++ b/src/main/java/lua/debug/DebugUtils.java @@ -48,6 +48,12 @@ public class DebugUtils { if (!LoadState.SOURCE_BINARY_STRING.equals(sourceStr)) { sourceStr = sourceStr.replace('\\', '/'); } + + int index = sourceStr.lastIndexOf('/'); + if (index != -1) { + sourceStr = sourceStr.substring(index + 1); + } + return sourceStr; } } diff --git a/src/main/java/lua/debug/j2se/StandardLuaJVM.java b/src/main/java/lua/debug/j2se/StandardLuaJVM.java index 7587da2e..d9c3fbf2 100644 --- a/src/main/java/lua/debug/j2se/StandardLuaJVM.java +++ b/src/main/java/lua/debug/j2se/StandardLuaJVM.java @@ -210,8 +210,7 @@ public class StandardLuaJVM { DebugUtils.println("start debugging..."); DebugSupport debugSupport = new DebugSupportImpl(getRequestPort(), getEventPort()); - getDebugState().setDebugSupport(debugSupport); - getDebugState().suspend(); + getDebugState().setDebugSupport(debugSupport); // create closure and execute final Closure c = new Closure(state, p);