package lua.debug; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import lua.CallInfo; import lua.StackState; import lua.io.LocVars; import lua.value.LValue; public class DebugStackState extends StackState implements DebugRequestListener { public Map breakpoints = new HashMap(); private boolean exiting = false; private boolean suspended = false; private boolean stepping = false; private int lastline = -1; public DebugStackState() { StackState.debugHooksEnabled = true; } // debug hooks public void debugHooks( int pc ) { if ( exiting ) throw new java.lang.RuntimeException("exiting"); if ( ! suspended ) return; synchronized ( this ) { // vm is in suspended state, although it might also // be stepping to the next instruction at the same time. // in either case we need the current line number int[] li = calls[cc].closure.p.lineinfo; int line = (li!=null && li.length>pc? li[pc]: -1); if ( stepping ) { if ( lastline == line ) return; stepping = false; } // save line in case next op is a step lastline = line; // wait for a state change while ( suspended && (!exiting) && (!stepping) ) { try { this.wait(); } catch ( InterruptedException ie ) { ie.printStackTrace(); } } } } // ------------------ commands coming from the debugger ------------------- public enum RequestType { suspend, resume, exit, set, clear, callgraph, stack, step, variable, } public String handleRequest(String request) { StringTokenizer st = new StringTokenizer( request ); String req = st.nextToken(); RequestType rt = RequestType.valueOf(req); switch ( rt ) { case suspend: suspend(); return "true"; case resume: resume(); return "true"; case exit: exit(); return "true"; case set: set( Integer.parseInt(st.nextToken()) ); return "true"; case clear: clear( Integer.parseInt(st.nextToken()) ); return "true"; case callgraph: return callgraph(); case stack: return stack(); case step: step(); return "true"; case variable: String N = st.nextToken(); String M = st.nextToken(); return variable( Integer.parseInt(N), Integer.parseInt(M) ); } throw new java.lang.IllegalArgumentException( "unkown request type: "+req ); } /** * suspend the execution */ public void suspend() { synchronized ( this ) { suspended = true; stepping = false; lastline = -1; this.notify(); } } /** * resume the execution */ public void resume() { synchronized ( this ) { suspended = false; 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 */ public void set( int N ) { synchronized ( this ) { breakpoints.put( N, Boolean.TRUE ); } } /** * clear breakpoint at line N */ public void clear( int N ) { synchronized ( this ) { breakpoints.remove( N ); } } /** * return the current call graph (i.e. stack frames from * old to new, include information about file, method, etc.) */ public String callgraph() { int n = cc; if ( n < 0 || n >= calls.length ) return ""; StringBuffer sb = new StringBuffer(); for ( int i=0; i= calls.length || (ci=calls[cc]) == null ) return ""; LocVars[] lv = ci.closure.p.locvars; int n = (lv != null? lv.length: 0); StringBuffer sb = new StringBuffer(); for ( int i=0; i= calls.length || (ci=calls[M]) == null ) return ""; return String.valueOf( super.stack[ci.base] ); } }