package rhogenwizard.debugger.backend; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.Vector; public class DebugProtocol { private DebugServer debugServer; private IDebugCallback debugCallback; private DebugState state; private String filePosition = ""; private int linePosition = 0; private String classPosition = ""; private String methodPosition = ""; private boolean watchProcessing = false; private DebugVariableType waitForEOL; private Thread waitingThread; private Vector<DebugVariable> watchList = null; private DebugVariableType lastWatchEOL; private boolean wasWatchEOL = false; private DebugEvaluation evaluationResult = null; public DebugProtocol (DebugServer server, IDebugCallback callback) { this.debugServer = server; this.debugCallback = callback; this.state = DebugState.NOTCONNECTED; } public DebugState getState() { return this.state; } public String getCurrentFile() { return this.filePosition; } public int getCurrentLine() { return this.linePosition; } public String getCurrentClass() { return this.classPosition; } public String getCurrentMethod() { return this.methodPosition; } protected void processCommand(String cmd) { boolean bp=false, stInto=false, stOver=false, stRet=false; if(cmd.endsWith("\n")) cmd = cmd.substring(0, cmd.length()-1); if (cmd.compareTo("CONNECT")==0) { this.state = DebugState.CONNECTED; debugServer.send("CONNECTED"); debugCallback.connected(); } else if (cmd.compareTo("RESUMED")==0) { this.state = DebugState.RUNNING; debugCallback.resumed(); } else if (cmd.compareTo("QUIT")==0) { this.state = DebugState.EXITED; debugCallback.exited(); } else if ( (bp=cmd.startsWith("BP:")) || (stInto=cmd.startsWith("STEP:")) || (stOver=cmd.startsWith("STOVER:")) || (stRet=cmd.startsWith("STRET:")) || cmd.startsWith("SUSP:")) { this.state = bp ? DebugState.BREAKPOINT : (stInto ? DebugState.STOPPED_INTO : (stOver ? DebugState.STOPPED_OVER : (stRet ? DebugState.STOPPED_RETURN : DebugState.SUSPENDED))); String[] brp = cmd.split(":"); this.filePosition = brp[1].replace('|', ':').replace('\\', '/'); this.linePosition = Integer.parseInt(brp[2]); this.classPosition = brp.length > 3 ? brp[3].replace('#', ':') : ""; this.methodPosition = brp.length > 4 ? brp[4] : ""; debugCallback.stopped(this.state, this.filePosition, this.linePosition, this.classPosition, this.methodPosition); } else if (cmd.startsWith("EVL:")) { boolean valid = cmd.charAt(4)=='0'; String var = cmd.substring(6); String val = ""; int val_idx = var.indexOf(':'); if (val_idx>=0) { val = var.substring(val_idx+1); try { var = URLDecoder.decode(var.substring(0,val_idx), "UTF-8"); } catch (UnsupportedEncodingException e) { var = var.substring(0,val_idx); } } if (this.watchProcessing) evaluationPrivate(valid, var, val); else debugCallback.evaluation(valid, var, val); } else if (cmd.startsWith("V:")) { DebugVariableType vt = DebugVariableType.variableTypeById(cmd.charAt(2)); String var = cmd.substring(4); String val = ""; int val_idx = var.indexOf(':'); if (val_idx>=0) { val = var.substring(val_idx+1); var = var.substring(0,val_idx); } if (this.watchProcessing) watchPrivate(vt, var, val); else debugCallback.watch(vt, var, val); } else if (cmd.startsWith("VSTART:")) { DebugVariableType type = DebugVariableType.variableTypeById(cmd.charAt(7)); if (this.watchProcessing) watchBOLPrivate(type); else debugCallback.watchBOL(type); } else if (cmd.startsWith("VEND:")) { DebugVariableType type = DebugVariableType.variableTypeById(cmd.charAt(5)); if (this.watchProcessing) watchEOLPrivate(type); else debugCallback.watchEOL(type); } else { debugCallback.unknown(cmd); } } public void stepOver() throws DebugServerException { checkDebugState(); this.state = DebugState.RESUMING; debugServer.send("STEPOVER"); } public void stepInto() throws DebugServerException { checkDebugState(); this.state = DebugState.RESUMING; debugServer.send("STEPINTO"); } public void stepReturn() throws DebugServerException { checkDebugState(); this.state = DebugState.RESUMING; debugServer.send("STEPRET"); } public void resume() throws DebugServerException { checkDebugState(); this.state = DebugState.RESUMING; debugServer.send("CONT"); } public void addBreakpoint(String file, int line) { debugServer.send("BP:"+file+":"+line); } public void removeBreakpoint(String file, int line) { debugServer.send("RM:"+file+":"+line); } public void removeAllBreakpoints() { debugServer.send("RMALL"); } public void skipBreakpoints(boolean skip) { debugServer.send(skip?"DISABLE":"ENABLE"); } public void evaluate(String expression) throws DebugServerException { checkDebugState(); evaluatePrivate(expression); } private void evaluatePrivate(String expression) { try { expression = URLEncoder.encode(expression, "UTF-8"); } catch (UnsupportedEncodingException e) {} debugServer.send("EVL:"+expression); } public void getVariables(DebugVariableType[] types) throws DebugServerException { checkDebugState(); for (DebugVariableType t: types) { if ((t==DebugVariableType.GLOBAL) || (t==DebugVariableType.LOCAL) || (this.classPosition.length() > 0)) getVariablesPrivate(t); } } private void getVariablesPrivate(DebugVariableType type) { switch (type) { case GLOBAL: debugServer.send("GVARS"); break; case CLASS: debugServer.send("CVARS"); break; case INSTANCE: debugServer.send("IVARS"); break; default: debugServer.send("LVARS"); } } public Vector<DebugVariable> getWatchList(DebugVariableType[] types) { Vector<DebugVariable> result = null; if (DebugState.paused(this.state) && (types.length > 0)) { boolean hasSomethingToWatch = false; for (DebugVariableType t: types) if ((t==DebugVariableType.GLOBAL) || (t==DebugVariableType.LOCAL) || (this.classPosition.length() > 0)) { hasSomethingToWatch = true; this.waitForEOL = t; } if (hasSomethingToWatch) { this.watchProcessing = true; this.watchList = new Vector<DebugVariable>(); this.waitingThread = Thread.currentThread(); this.wasWatchEOL = false; for (DebugVariableType t: types) if ((t==DebugVariableType.GLOBAL) || (t==DebugVariableType.LOCAL) || (this.classPosition.length() > 0)) getVariablesPrivate(t); do { try { Thread.sleep(1000); } catch (InterruptedException e) { } } while (!(this.wasWatchEOL && (this.lastWatchEOL==this.waitForEOL))); this.watchProcessing = false; result = this.watchList; this.watchList = null; } } return result; } public DebugEvaluation instantEvaluate(String expression) { DebugEvaluation result = null; if (DebugState.paused(this.state)) { this.watchProcessing = true; this.evaluationResult = null; this.waitingThread = Thread.currentThread(); evaluatePrivate(expression); do { try { Thread.sleep(1000); } catch (InterruptedException e) { } } while (this.evaluationResult==null); this.watchProcessing = false; result = this.evaluationResult; this.evaluationResult = null; } return result; } private void watchBOLPrivate(DebugVariableType type) { // nothing to do } private void watchEOLPrivate(DebugVariableType type) { this.wasWatchEOL = true; this.lastWatchEOL = type; if (this.waitForEOL==type) this.waitingThread.interrupt(); } private void watchPrivate(DebugVariableType type, String variable, String value) { this.watchList.add(new DebugVariable(type, variable, value)); } private void evaluationPrivate(boolean valid, String code, String value) { this.evaluationResult = new DebugEvaluation(valid, code, value); this.waitingThread.interrupt(); } public void suspend() throws DebugServerException { checkDebugState(); debugServer.send("SUSP"); } public void terminate() throws DebugServerException { checkDebugState(); debugServer.send("KILL"); } private void checkDebugState() throws DebugServerException { if (this.watchProcessing) throw new DebugServerException("Can't interrupt the watch list processing"); } public boolean isProcessing() { return this.watchProcessing; } }