2007-10-03 17:39:37 +00:00
|
|
|
/*******************************************************************************
|
|
|
|
|
* 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.
|
|
|
|
|
******************************************************************************/
|
2007-08-11 04:53:35 +00:00
|
|
|
package lua.debug;
|
|
|
|
|
|
2007-10-03 17:39:37 +00:00
|
|
|
import java.util.ArrayList;
|
2007-08-11 04:53:35 +00:00
|
|
|
import java.util.HashMap;
|
2007-10-03 17:39:37 +00:00
|
|
|
import java.util.HashSet;
|
|
|
|
|
import java.util.List;
|
2007-08-11 04:53:35 +00:00
|
|
|
import java.util.Map;
|
2007-10-03 17:39:37 +00:00
|
|
|
import java.util.Set;
|
2007-08-11 04:53:35 +00:00
|
|
|
|
|
|
|
|
import lua.CallInfo;
|
2007-10-04 21:04:38 +00:00
|
|
|
import lua.Lua;
|
2007-08-11 04:53:35 +00:00
|
|
|
import lua.StackState;
|
2007-10-03 17:39:37 +00:00
|
|
|
import lua.addon.compile.LexState;
|
2007-08-11 04:53:35 +00:00
|
|
|
import lua.io.LocVars;
|
2007-09-18 22:55:22 +00:00
|
|
|
import lua.io.Proto;
|
2007-10-03 17:39:37 +00:00
|
|
|
import lua.value.LTable;
|
|
|
|
|
import lua.value.LValue;
|
2007-08-11 04:53:35 +00:00
|
|
|
|
|
|
|
|
public class DebugStackState extends StackState implements DebugRequestListener {
|
2007-09-18 22:55:22 +00:00
|
|
|
|
2007-10-03 17:39:37 +00:00
|
|
|
private static final boolean DEBUG = false;
|
|
|
|
|
|
2007-10-10 00:26:23 +00:00
|
|
|
protected Map breakpoints = new HashMap();
|
2007-10-03 17:39:37 +00:00
|
|
|
protected boolean exiting = false;
|
|
|
|
|
protected boolean suspended = false;
|
|
|
|
|
protected boolean stepping = false;
|
|
|
|
|
protected int lastline = -1;
|
2007-10-10 00:26:23 +00:00
|
|
|
protected List debugEventListeners = new ArrayList();
|
2007-08-11 04:53:35 +00:00
|
|
|
|
|
|
|
|
public DebugStackState() {
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-06 18:48:37 +00:00
|
|
|
protected void debugAssert(boolean b) {
|
|
|
|
|
if ( ! b )
|
|
|
|
|
error( "assert failure" );
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-03 17:39:37 +00:00
|
|
|
public void addDebugEventListener(DebugEventListener listener) {
|
|
|
|
|
if (!debugEventListeners.contains(listener)) {
|
|
|
|
|
debugEventListeners.add(listener);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void removeDebugEventListener(DebugEventListener listener) {
|
|
|
|
|
if (debugEventListeners.contains(listener)) {
|
|
|
|
|
debugEventListeners.remove(listener);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void notifyDebugEventListeners(DebugEvent event) {
|
2007-10-10 00:26:23 +00:00
|
|
|
for (int i = 0; debugEventListeners != null && i < debugEventListeners.size(); i++) {
|
|
|
|
|
DebugEventListener listener = (DebugEventListener)debugEventListeners.get(i);
|
2007-10-03 17:39:37 +00:00
|
|
|
listener.notifyDebugEvent(event);
|
2007-10-10 00:26:23 +00:00
|
|
|
}
|
2007-10-03 17:39:37 +00:00
|
|
|
}
|
|
|
|
|
|
2007-09-18 22:55:22 +00:00
|
|
|
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();
|
2007-10-03 17:39:37 +00:00
|
|
|
if ( p.lineinfo != null && p.lineinfo.length > call.pc )
|
|
|
|
|
line = String.valueOf( p.lineinfo[call.pc] );
|
2007-09-18 22:55:22 +00:00
|
|
|
// TODO: reverse lookup on function name ????
|
|
|
|
|
func = call.closure.luaAsString().toJavaString();
|
|
|
|
|
}
|
|
|
|
|
return source+":"+line+"("+func+")";
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-21 00:34:14 +00:00
|
|
|
|
2007-10-03 17:39:37 +00:00
|
|
|
// override and fill in line number info
|
2007-10-04 23:51:39 +00:00
|
|
|
public void error(String message) {
|
|
|
|
|
super.error( getFileLine(cc)+": "+message );
|
2007-09-18 22:55:22 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-03 17:39:37 +00:00
|
|
|
private void printLuaTrace() {
|
|
|
|
|
System.out.println( "Lua location: "+getFileLine(cc) );
|
|
|
|
|
for ( int cindex=cc-1; cindex>=0; cindex-- )
|
|
|
|
|
System.out.println( "\tin "+getFileLine( cindex ) );
|
2007-09-21 00:34:14 +00:00
|
|
|
}
|
|
|
|
|
|
2007-09-18 22:55:22 +00:00
|
|
|
// intercept exceptions and fill in line numbers
|
|
|
|
|
public void exec() {
|
|
|
|
|
try {
|
|
|
|
|
super.exec();
|
2007-10-03 17:39:37 +00:00
|
|
|
} catch (AbortException e) {
|
|
|
|
|
// ignored. Client aborts the debugging session.
|
|
|
|
|
} catch ( Exception t ) {
|
|
|
|
|
t.printStackTrace();
|
|
|
|
|
printLuaTrace();
|
2007-09-18 22:55:22 +00:00
|
|
|
System.out.flush();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-08-11 04:53:35 +00:00
|
|
|
// debug hooks
|
|
|
|
|
public void debugHooks( int pc ) {
|
2007-10-03 17:39:37 +00:00
|
|
|
DebugUtils.println("entered debugHook...");
|
|
|
|
|
|
2007-08-11 04:53:35 +00:00
|
|
|
if ( exiting )
|
2007-10-03 17:39:37 +00:00
|
|
|
throw new AbortException("exiting");
|
2007-08-11 05:29:56 +00:00
|
|
|
|
2007-08-11 04:53:35 +00:00
|
|
|
synchronized ( this ) {
|
2007-10-03 17:39:37 +00:00
|
|
|
|
2007-08-11 05:29:56 +00:00
|
|
|
// anytime the line doesn't change we keep going
|
2007-10-03 17:39:37 +00:00
|
|
|
int line = getLineNumber(calls[cc]);
|
|
|
|
|
DebugUtils.println("debugHook - executing line: " + line);
|
|
|
|
|
if ( !stepping && lastline == line ) {
|
2007-08-11 05:29:56 +00:00
|
|
|
return;
|
2007-10-03 17:39:37 +00:00
|
|
|
}
|
2007-08-11 04:53:35 +00:00
|
|
|
|
|
|
|
|
// save line in case next op is a step
|
|
|
|
|
lastline = line;
|
2007-10-03 17:39:37 +00:00
|
|
|
|
|
|
|
|
if ( stepping ) {
|
|
|
|
|
DebugUtils.println("suspended by stepping at pc=" + pc);
|
|
|
|
|
notifyDebugEventListeners(new DebugEventStepping());
|
|
|
|
|
suspended = true;
|
|
|
|
|
} else if ( !suspended ) {
|
|
|
|
|
// check for a break point if we aren't suspended already
|
|
|
|
|
Proto p = calls[cc].closure.p;
|
|
|
|
|
String source = DebugUtils.getSourceFileName(p.source);
|
|
|
|
|
if ( breakpoints.containsKey(constructBreakpointKey(source, line))){
|
|
|
|
|
DebugUtils.println("hitting breakpoint " + constructBreakpointKey(source, line));
|
|
|
|
|
notifyDebugEventListeners(
|
|
|
|
|
new DebugEventBreakpoint(source, line));
|
|
|
|
|
suspended = true;
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2007-08-11 05:29:56 +00:00
|
|
|
}
|
|
|
|
|
|
2007-08-11 04:53:35 +00:00
|
|
|
// wait for a state change
|
2007-10-03 17:39:37 +00:00
|
|
|
while (suspended && !exiting ) {
|
2007-08-11 04:53:35 +00:00
|
|
|
try {
|
|
|
|
|
this.wait();
|
2007-10-03 17:39:37 +00:00
|
|
|
DebugUtils.println("resuming execution...");
|
2007-08-11 04:53:35 +00:00
|
|
|
} catch ( InterruptedException ie ) {
|
|
|
|
|
ie.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-10-03 17:39:37 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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);
|
|
|
|
|
return line;
|
2007-08-11 04:53:35 +00:00
|
|
|
}
|
2007-10-03 17:39:37 +00:00
|
|
|
|
|
|
|
|
// ------------------ commands coming from the debugger -------------------
|
2007-08-11 04:53:35 +00:00
|
|
|
|
2007-10-03 17:39:37 +00:00
|
|
|
public DebugResponse handleRequest(DebugRequest request) {
|
|
|
|
|
DebugUtils.println("DebugStackState is handling request: " + request.toString());
|
2007-10-10 00:26:23 +00:00
|
|
|
DebugRequestType requestType = request.getType();
|
|
|
|
|
if (DebugRequestType.suspend == requestType) {
|
|
|
|
|
suspend();
|
2007-10-03 17:39:37 +00:00
|
|
|
return DebugResponseSimple.SUCCESS;
|
2007-10-10 00:26:23 +00:00
|
|
|
} else if (DebugRequestType.resume == requestType) {
|
2007-10-03 17:39:37 +00:00
|
|
|
resume();
|
|
|
|
|
return DebugResponseSimple.SUCCESS;
|
2007-10-10 00:26:23 +00:00
|
|
|
} else if (DebugRequestType.exit == requestType) {
|
2007-10-03 17:39:37 +00:00
|
|
|
exit();
|
|
|
|
|
return DebugResponseSimple.SUCCESS;
|
2007-10-10 00:26:23 +00:00
|
|
|
} else if (DebugRequestType.lineBreakpointSet == requestType) {
|
2007-10-03 17:39:37 +00:00
|
|
|
DebugRequestLineBreakpointToggle setBreakpointRequest
|
|
|
|
|
= (DebugRequestLineBreakpointToggle)request;
|
|
|
|
|
setBreakpoint(setBreakpointRequest.getSource(), setBreakpointRequest.getLineNumber());
|
|
|
|
|
return DebugResponseSimple.SUCCESS;
|
2007-10-10 00:26:23 +00:00
|
|
|
} else if (DebugRequestType.lineBreakpointClear == requestType) {
|
2007-10-03 17:39:37 +00:00
|
|
|
DebugRequestLineBreakpointToggle clearBreakpointRequest
|
|
|
|
|
= (DebugRequestLineBreakpointToggle)request;
|
|
|
|
|
clearBreakpoint(clearBreakpointRequest.getSource(), clearBreakpointRequest.getLineNumber());
|
|
|
|
|
return DebugResponseSimple.SUCCESS;
|
2007-10-10 00:26:23 +00:00
|
|
|
} else if (DebugRequestType.callgraph == requestType) {
|
2007-10-03 17:39:37 +00:00
|
|
|
return new DebugResponseCallgraph(getCallgraph());
|
2007-10-10 00:26:23 +00:00
|
|
|
} else if (DebugRequestType.stack == requestType) {
|
2007-10-03 17:39:37 +00:00
|
|
|
DebugRequestStack stackRequest = (DebugRequestStack) request;
|
|
|
|
|
int index = stackRequest.getIndex();
|
|
|
|
|
return new DebugResponseStack(getStack(index));
|
2007-10-10 00:26:23 +00:00
|
|
|
} else if (DebugRequestType.step == requestType) {
|
2007-10-03 17:39:37 +00:00
|
|
|
step();
|
|
|
|
|
return DebugResponseSimple.SUCCESS;
|
|
|
|
|
}
|
2007-10-10 00:26:23 +00:00
|
|
|
|
|
|
|
|
throw new java.lang.IllegalArgumentException( "unkown request type: "+ request.getType());
|
2007-08-11 04:53:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* suspend the execution
|
|
|
|
|
*/
|
|
|
|
|
public void suspend() {
|
|
|
|
|
synchronized ( this ) {
|
|
|
|
|
suspended = true;
|
|
|
|
|
stepping = false;
|
|
|
|
|
lastline = -1;
|
|
|
|
|
this.notify();
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-10-03 17:39:37 +00:00
|
|
|
|
2007-08-11 04:53:35 +00:00
|
|
|
/**
|
|
|
|
|
* resume the execution
|
|
|
|
|
*/
|
|
|
|
|
public void resume() {
|
|
|
|
|
synchronized ( this ) {
|
|
|
|
|
suspended = false;
|
2007-10-03 17:39:37 +00:00
|
|
|
stepping = false;
|
2007-08-11 04:53:35 +00:00
|
|
|
this.notify();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* terminate the execution
|
|
|
|
|
*/
|
|
|
|
|
public void exit() {
|
|
|
|
|
synchronized ( this ) {
|
|
|
|
|
exiting = true;
|
|
|
|
|
this.notify();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* set breakpoint at line N
|
|
|
|
|
* @param N the line to set the breakpoint at
|
|
|
|
|
*/
|
2007-10-03 17:39:37 +00:00
|
|
|
public void setBreakpoint(String source, int lineNumber) {
|
|
|
|
|
DebugUtils.println("adding breakpoint " + constructBreakpointKey(source, lineNumber));
|
2007-08-11 04:53:35 +00:00
|
|
|
synchronized ( this ) {
|
2007-10-03 17:39:37 +00:00
|
|
|
breakpoints.put(constructBreakpointKey(source, lineNumber), Boolean.TRUE );
|
2007-08-11 04:53:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-03 17:39:37 +00:00
|
|
|
protected String constructBreakpointKey(String source, int lineNumber) {
|
|
|
|
|
return source + ":" + lineNumber;
|
|
|
|
|
}
|
|
|
|
|
|
2007-08-11 04:53:35 +00:00
|
|
|
/**
|
2007-10-03 17:39:37 +00:00
|
|
|
* clear breakpoint at line lineNumber of source source
|
2007-08-11 04:53:35 +00:00
|
|
|
*/
|
2007-10-03 17:39:37 +00:00
|
|
|
public void clearBreakpoint(String source, int lineNumber) {
|
|
|
|
|
DebugUtils.println("removing breakpoint " + constructBreakpointKey(source, lineNumber));
|
2007-08-11 04:53:35 +00:00
|
|
|
synchronized ( this ) {
|
2007-10-03 17:39:37 +00:00
|
|
|
breakpoints.remove(constructBreakpointKey(source, lineNumber));
|
2007-08-11 04:53:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* return the current call graph (i.e. stack frames from
|
|
|
|
|
* old to new, include information about file, method, etc.)
|
|
|
|
|
*/
|
2007-10-03 17:39:37 +00:00
|
|
|
public StackFrame[] getCallgraph() {
|
2007-08-11 04:53:35 +00:00
|
|
|
int n = cc;
|
2007-10-03 17:39:37 +00:00
|
|
|
|
2007-08-11 04:53:35 +00:00
|
|
|
if ( n < 0 || n >= calls.length )
|
2007-10-03 17:39:37 +00:00
|
|
|
return new StackFrame[0];
|
|
|
|
|
|
|
|
|
|
StackFrame[] frames = new StackFrame[n+1];
|
|
|
|
|
for ( int i = 0; i <= n; i++ ) {
|
2007-08-11 04:53:35 +00:00
|
|
|
CallInfo ci = calls[i];
|
2007-10-03 17:39:37 +00:00
|
|
|
frames[i] = new StackFrame(ci, getLineNumber(ci));
|
2007-08-11 04:53:35 +00:00
|
|
|
}
|
2007-10-03 17:39:37 +00:00
|
|
|
return frames;
|
2007-08-11 04:53:35 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-03 17:39:37 +00:00
|
|
|
public Variable[] getStack(int index) {
|
|
|
|
|
if (index < 0 || index >= calls.length) {
|
|
|
|
|
//TODO: this is an error, handle it differently
|
|
|
|
|
return new Variable[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CallInfo callInfo = calls[index];
|
|
|
|
|
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;
|
2007-10-10 00:26:23 +00:00
|
|
|
List variables = new ArrayList();
|
2007-10-03 17:39:37 +00:00
|
|
|
int localVariableCount = 0;
|
2007-10-10 00:26:23 +00:00
|
|
|
Set variablesSeen = new HashSet();
|
2007-10-03 17:39:37 +00:00
|
|
|
for (int i = 0; localVariables != null && i < localVariables.length && i <= top; i++) {
|
|
|
|
|
String varName = localVariables[i].varname.toString();
|
|
|
|
|
DebugUtils.print("\tVariable: " + varName);
|
|
|
|
|
DebugUtils.print("\tValue: " + stack[callInfo.base + i]);
|
|
|
|
|
if (!variablesSeen.contains(varName) &&
|
|
|
|
|
!LexState.isReservedKeyword(varName)) {
|
|
|
|
|
variablesSeen.add(varName);
|
|
|
|
|
LValue value = stack[callInfo.base + i];
|
|
|
|
|
if (value != null) {
|
2007-10-04 21:04:38 +00:00
|
|
|
int type = value.luaGetType();
|
2007-10-03 17:39:37 +00:00
|
|
|
DebugUtils.print("\tType: " + type);
|
2007-10-04 21:04:38 +00:00
|
|
|
if (type == Lua.LUA_TTABLE) {
|
2007-10-03 17:39:37 +00:00
|
|
|
DebugUtils.println(" (selected)");
|
|
|
|
|
variables.add(
|
|
|
|
|
new TableVariable(localVariableCount++,
|
|
|
|
|
varName,
|
|
|
|
|
type,
|
|
|
|
|
(LTable) value));
|
2007-10-04 21:04:38 +00:00
|
|
|
} else if (type != Lua.LUA_TFUNCTION &&
|
|
|
|
|
type != LUA_TTHREAD) {
|
2007-10-03 17:39:37 +00:00
|
|
|
DebugUtils.println(" (selected)");
|
|
|
|
|
variables.add(
|
|
|
|
|
new Variable(localVariableCount++,
|
|
|
|
|
varName,
|
|
|
|
|
type,
|
|
|
|
|
value.toString()));
|
|
|
|
|
} else {
|
|
|
|
|
DebugUtils.println("");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
DebugUtils.println("");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
DebugUtils.println("");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return (Variable[])variables.toArray(new Variable[0]);
|
2007-08-11 04:53:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* single step forward (go to next statement)
|
|
|
|
|
*/
|
|
|
|
|
public void step() {
|
|
|
|
|
synchronized ( this ) {
|
2007-10-03 17:39:37 +00:00
|
|
|
suspended = false;
|
2007-08-11 04:53:35 +00:00
|
|
|
stepping = true;
|
|
|
|
|
this.notify();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|