Clover coverage report - DrJava Test Coverage (drjava-20120304-r5456)
Coverage timestamp: Sun Mar 4 2012 03:13:23 CST
file stats: LOC: 2,004   Methods: 124
NCLOC: 1,190   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
JPDADebugger.java 0.6% 2.2% 7.3% 2.3%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.drjava.model.debug.jpda;
 38   
 39    import java.awt.EventQueue;
 40    import java.io.*;
 41    import java.util.ArrayList;
 42    import java.util.Arrays;
 43    import java.util.Iterator;
 44    import java.util.LinkedList;
 45    import java.util.TreeMap;
 46    import java.util.List;
 47    import java.util.Map;
 48    import java.util.NoSuchElementException;
 49    import java.util.Stack;
 50    import java.util.Vector;
 51   
 52    // DrJava stuff
 53    import edu.rice.cs.util.UnexpectedException;
 54    import edu.rice.cs.util.swing.Utilities;
 55    import edu.rice.cs.drjava.model.GlobalModel;
 56    import edu.rice.cs.drjava.model.DrJavaFileUtils;
 57    import edu.rice.cs.drjava.model.repl.DefaultInteractionsModel;
 58    import edu.rice.cs.drjava.model.repl.DummyInteractionsListener;
 59    import edu.rice.cs.drjava.model.repl.InteractionsListener;
 60    import edu.rice.cs.drjava.model.repl.newjvm.InterpreterJVM;
 61    import edu.rice.cs.drjava.model.compiler.LanguageLevelStackTraceMapper;
 62    import edu.rice.cs.drjava.model.OpenDefinitionsDocument;
 63    import edu.rice.cs.util.Log;
 64    import edu.rice.cs.plt.lambda.Lambda;
 65    import edu.rice.cs.drjava.model.debug.*;
 66    import edu.rice.cs.drjava.DrJava;
 67    import edu.rice.cs.drjava.config.OptionConstants;
 68    import edu.rice.cs.plt.tuple.Pair;
 69   
 70    import com.sun.jdi.*;
 71    import com.sun.jdi.connect.*;
 72    import com.sun.jdi.request.*;
 73   
 74    import static edu.rice.cs.plt.debug.DebugUtil.error;
 75    import static edu.rice.cs.plt.debug.DebugUtil.debug;
 76   
 77    /** An integrated debugger which attaches to the Interactions JVM using
 78    * Sun's Java Platform Debugger Architecture (JPDA/JDI) interface.
 79    *
 80    * Every public method in this class throws an llegalStateException if
 81    * it is called while the debugger is not active, except for isAvailable,
 82    * isReady, and startUp. Public methods also throw a DebugException if
 83    * the EventHandlerThread has caught an exception.
 84    *
 85    * @version $Id: JPDADebugger.java 5443 2011-08-17 04:58:50Z rcartwright $
 86    */
 87    public class JPDADebugger implements Debugger {
 88   
 89    /** A log for recording messages in a file. */
 90    private static final Log _log = new Log("JPDADebugger.txt", false);
 91   
 92    private static final int OBJECT_COLLECTED_TRIES = 5;
 93   
 94    /** Signature of the InterpreterJVM.addInterpreter method.
 95    * @see edu.rice.cs.drjava.model.repl.newjvm.InterpreterJVM#addInterpreter
 96    * @see #_dumpVariablesIntoInterpreterAndSwitch
 97    */
 98    private static final String ADD_INTERPRETER_SIG =
 99    "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;" +
 100    "[Ljava/lang/Object;[Ljava/lang/String;[Ljava/lang/Class;)V";
 101   
 102    /** Signature of the InterpreterJVM.getVariableValue method.
 103    * @see edu.rice.cs.drjava.model.repl.newjvm.InterpreterJVM#getVariableValue
 104    * @see #_copyVariablesFromInterpreter
 105    */
 106    private static final String GET_VARIABLE_VALUE_SIG = "(Ljava/lang/String;)[Ljava/lang/Object;";
 107   
 108    private static final String NEW_INSTANCE_SIG = "(Ljava/lang/Class;I)Ljava/lang/Object;";
 109   
 110    /** Reference to DrJava's model. */
 111    private volatile GlobalModel _model;
 112   
 113    /** VirtualMachine of the interactions JVM. */
 114    private volatile VirtualMachine _vm;
 115   
 116    /** Manages all event requests in JDI. */
 117    private volatile EventRequestManager _eventManager;
 118   
 119    /** Vector of all current Watches. */
 120    private final ArrayList<DebugWatchData> _watches = new ArrayList<DebugWatchData>();
 121   
 122    /** Keeps track of any DebugActions whose classes have not yet been loaded, so that EventRequests can be created when the correct
 123    * ClassPrepareEvent occurs.
 124    */
 125    private final PendingRequestManager _pendingRequestManager = new PendingRequestManager(this);
 126   
 127    /** Provides a way for the JPDADebugger to communicate with the view. */
 128    final DebugEventNotifier _notifier = new DebugEventNotifier();
 129   
 130    /** The running ThreadReference that we are debugging. */
 131    private volatile ThreadReference _runningThread;
 132   
 133    /** Storage for all the threads suspended by this debugger. The "current" thread is the top one on the stack. */
 134    private volatile RandomAccessStack _suspendedThreads;
 135   
 136    /** A handle to the interpreterJVM that we need so we can populate the environment. */
 137    private volatile ObjectReference _interpreterJVM;
 138   
 139    private volatile InteractionsListener _watchListener;
 140   
 141    /** If not null, this field holds an error caught by the EventHandlerThread. */
 142    private volatile Throwable _eventHandlerError;
 143   
 144    /*Determines whether automatic trace has been enabled*/
 145    private volatile boolean _isAutomaticTraceEnabled = false;
 146   
 147    /** Builds a new JPDADebugger to debug code in the Interactions JVM, using the JPDA/JDI interfaces.
 148    * Does not actually connect to the interpreterJVM until startUp().
 149    */
 150  2198 public JPDADebugger(GlobalModel model) {
 151  2198 _model = model;
 152  2198 _vm = null;
 153  2198 _eventManager = null;
 154   
 155  2198 _suspendedThreads = new RandomAccessStack();
 156  2198 _runningThread = null;
 157  2198 _interpreterJVM = null;
 158  2198 _eventHandlerError = null;
 159   
 160  2198 _watchListener = new DummyInteractionsListener() {
 161  0 public void interactionEnded() { _updateWatches(); }
 162    };
 163    }
 164   
 165    /** Logs any unexpected behavior that occurs (but which should not cause DrJava to abort).
 166    * @param message message to print to the log
 167    */
 168  0 private void _log(String message) { _log.log(message); }
 169   
 170    /** Logs any unexpected behavior that occurs (but which should not cause DrJava to abort).
 171    * @param message message to print to the log
 172    * @param t Exception or Error being logged
 173    */
 174  0 private void _log(String message, Throwable t) { _log.log(message, t); }
 175   
 176   
 177    /** Adds a listener to this JPDADebugger.
 178    * @param listener a listener that reacts on events generated by the JPDADebugger
 179    */
 180  233 public void addListener(DebugListener listener) {
 181  233 _notifier.addListener(listener);
 182  233 _model.getBreakpointManager().addListener(listener);
 183    }
 184   
 185    /** Removes a listener to this JPDADebugger.
 186    * @param listener listener to remove
 187    */
 188  0 public void removeListener(DebugListener listener) {
 189  0 _notifier.removeListener(listener);
 190  0 _model.getBreakpointManager().removeListener(listener);
 191    }
 192   
 193    /** Returns whether the debugger is available in this copy of DrJava. This method does not indicate whether the
 194    * debugger is ready to be used, which is indicated by isReady().
 195    */
 196  2562 public boolean isAvailable() { return true; }
 197   
 198  157 public DebugModelCallback callback() { return new DebugModelCallback() { }; }
 199   
 200    /** Returns whether the debugger is currently enabled. */
 201  71 public boolean isReady() { return _vm != null; }
 202   
 203    /** Attaches the debugger to the Interactions JVM to prepare for debugging. Only runs in event thread. */
 204  0 public /* synchronized */ void startUp() throws DebugException {
 205  0 assert EventQueue.isDispatchThread();
 206  0 if (! isReady()) {
 207  0 _eventHandlerError = null;
 208    // check if all open documents are in sync
 209  0 for (OpenDefinitionsDocument doc: _model.getOpenDefinitionsDocuments()) {
 210  0 doc.checkIfClassFileInSync();
 211    }
 212   
 213  0 try { _attachToVM(); }
 214    catch(DebugException e1) { // We sometimes see ConnectExceptions stating that the connection was refused
 215  0 try {
 216  0 try { Thread.sleep(100); } // Give any temporary connection problems a chance to resolve
 217    catch (InterruptedException e) { /* ignore */ }
 218  0 _attachToVM();
 219  0 error.log("Two attempts required for debugger to attach to slave JVM");
 220    }
 221    catch(DebugException e2) {
 222  0 try { Thread.sleep(500); } // Give any temporary connection problems a chance to resolve
 223    catch (InterruptedException e) { /* ignore */ }
 224  0 _attachToVM();
 225  0 error.log("Three attempts required for debugger to attach to slave JVM");
 226    } // if we throw another exception, three strikes and we're out
 227    }
 228   
 229    // Listen for events when threads die
 230  0 ThreadDeathRequest tdr = _eventManager.createThreadDeathRequest();
 231  0 tdr.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
 232  0 tdr.enable();
 233   
 234    // Listen for events from JPDA in a new thread
 235  0 EventHandlerThread eventHandler = new EventHandlerThread(this, _vm);
 236  0 eventHandler.start();
 237   
 238  0 _model.getInteractionsModel().addListener(_watchListener);
 239   
 240    // re-set breakpoints that have already been set
 241  0 ArrayList<Breakpoint> oldBreakpoints = new ArrayList<Breakpoint>(_model.getBreakpointManager().getRegions());
 242  0 _model.getBreakpointManager().clearRegions(); // oldBreakpoints are removed from the breakpoint manager
 243  0 for (int i = 0; i < oldBreakpoints.size(); i++) {
 244  0 Breakpoint bp = oldBreakpoints.get(i);
 245  0 bp.update();
 246  0 OpenDefinitionsDocument odd = bp.getDocument();
 247  0 setBreakpoint(new JPDABreakpoint(odd, bp.getLineStartOffset(), bp.isEnabled(), this));
 248    }
 249    }
 250   
 251    else
 252    // Already started
 253  0 throw new IllegalStateException("Debugger has already been started.");
 254    }
 255   
 256   
 257    /** Disconnects the debugger from the Interactions JVM and cleans up any state.
 258    * @throws IllegalStateException if debugger is not ready
 259    */
 260  0 public /* synchronized */ void shutdown() {
 261  0 assert EventQueue.isDispatchThread();
 262  0 if (isReady()) {
 263  0 Runnable command = new Runnable() { public void run() { _model.getInteractionsModel().removeListener(_watchListener); } };
 264   
 265    /* Use EventQueue rather than Utilities because we want to defer executing this
 266    * code after pending events (that may involve the _watchListener).
 267    */
 268  0 EventQueue.invokeLater(command);
 269   
 270  0 _removeAllDebugInterpreters();
 271   
 272  0 try { _vm.dispose(); }
 273    catch (VMDisconnectedException vmde) { /* VM was shutdown prematurely */ }
 274    finally {
 275  0 _model.getInteractionsModel().setToDefaultInterpreter();
 276  0 _vm = null;
 277  0 _suspendedThreads = new RandomAccessStack();
 278  0 _eventManager = null;
 279  0 _runningThread = null;
 280  0 _updateWatches();
 281    }
 282    }
 283    }
 284   
 285   
 286    /** Sets the notion of current thread to the one contained in threadData. The thread must be suspended. (Note: the
 287    * intention is for this method to suspend the thread if necessary, but this is not yet implemented. The catch is
 288    * that any manually suspended threads won't cooperate with the debug interpreters; the thread must be suspended by
 289    * a breakpoint or step.)
 290    * @param threadData The Thread to set as current.
 291    * @throws IllegalStateException if debugger is not ready
 292    * @throws IllegalArgumentException if threadData is null or not suspended
 293    */
 294  0 public /* synchronized */ void setCurrentThread(DebugThreadData threadData) throws DebugException {
 295  0 assert EventQueue.isDispatchThread();
 296  0 _ensureReady();
 297   
 298  0 if (threadData == null) {
 299  0 throw new IllegalArgumentException("Cannot set current thread to null.");
 300    }
 301   
 302  0 ThreadReference threadRef = _getThreadFromDebugThreadData(threadData);
 303   
 304    // Special case to avoid overhead of scrollToSource() if we
 305    // are selecting the thread we have already selected currently
 306   
 307    // Currently disabled, so we will always scroll to source, even if the
 308    // thread is already selected.
 309    // if ( _suspendedThreads.size() > 0 &&
 310    // _suspendedThreads.peek().uniqueID() == threadRef.uniqueID() ) {
 311    // return;
 312    // }
 313   
 314    // if we switch to a currently suspended thread, we need to remove
 315    // it from the stack and put it on the top
 316  0 if (_suspendedThreads.contains(threadRef.uniqueID())) _suspendedThreads.remove(threadRef.uniqueID());
 317   
 318  0 if (!threadRef.isSuspended()) {
 319  0 throw new IllegalArgumentException("Given thread must be suspended.");
 320    // threadRef.suspend();
 321    //
 322    // try{
 323    // if ( threadRef.frameCount() <= 0 ) {
 324    // printMessage(threadRef.name() + " could not be suspended. It had no stackframes.");
 325    // _suspendedThreads.push(threadRef);
 326    // resume();
 327    // return;
 328    // }
 329    // }
 330    // catch(IncompatibleThreadStateException ex) {
 331    // throw new UnexpectedException(ex);
 332    // }
 333    //
 334    // //
 335    // // Step now so that we can get an interpreter,
 336    // // do not notify (hence the false argument)
 337    // _stepHelper(StepRequest.STEP_OVER, false);
 338    //return;
 339    }
 340   
 341  0 _suspendedThreads.push(threadRef);
 342   
 343  0 try {
 344  0 if (threadRef.frameCount() <= 0) {
 345  0 printMessage(threadRef.name() + " could not be suspended since it has no stackframes.");
 346  0 resume();
 347  0 return;
 348    }
 349    }
 350    catch (IncompatibleThreadStateException e) {
 351  0 throw new DebugException("Could not suspend thread: " + e);
 352    }
 353   
 354    // Activate the debug interpreter for interacting with this thread
 355  0 _switchToInterpreterForThreadReference(threadRef);
 356  0 _switchToSuspendedThread();
 357  0 printMessage("The current thread has changed.");
 358    }
 359   
 360    /** Returns the currently selected thread for the debugger. */
 361  0 ThreadReference getCurrentThread() {
 362    // Current thread is the top one on the stack
 363  0 return _suspendedThreads.peek();
 364    }
 365   
 366    /** Returns whether the debugger currently has any suspended threads. */
 367  0 public /* synchronized */ boolean hasSuspendedThreads() throws DebugException {
 368  0 assert EventQueue.isDispatchThread();
 369  0 if (! isReady()) return false;
 370  0 return _suspendedThreads.size() > 0;
 371    }
 372   
 373    /** Returns whether the debugger's current thread is suspended. */
 374  0 public /* synchronized */ boolean isCurrentThreadSuspended() throws DebugException {
 375  0 assert EventQueue.isDispatchThread();
 376  0 if (! isReady()) return false;
 377  0 return hasSuspendedThreads() && ! hasRunningThread();
 378    }
 379   
 380    /** Returns whether the thread the debugger is tracking is now running. */
 381  0 public /* synchronized */ boolean hasRunningThread() throws DebugException {
 382  0 assert EventQueue.isDispatchThread();
 383  0 if (! isReady()) return false;
 384  0 return _runningThread != null;
 385    }
 386   
 387    /** Resumes the thread currently being debugged, copying back all variables from the current debug interpreter. */
 388  0 public /* synchronized */ void resume() throws DebugException {
 389  0 assert EventQueue.isDispatchThread();
 390  0 _ensureReady();
 391  0 _resumeHelper(false);
 392    }
 393   
 394    /** Enables or disables automatic trace. */
 395  24 public /*synchronized*/ void setAutomaticTraceEnabled(boolean e) {
 396  24 _isAutomaticTraceEnabled = e;
 397    }
 398   
 399    /*
 400    * Determines whether automatic trace has been enabled on the debugger
 401    */
 402  76 public boolean isAutomaticTraceEnabled() {
 403  76 return _isAutomaticTraceEnabled;
 404    }
 405   
 406    /** Resumes the given thread, copying back any variables from its associated debug interpreter.
 407    * @param threadData Thread to resume
 408    */
 409  0 public /* synchronized */ void resume(DebugThreadData threadData) throws DebugException {
 410  0 assert EventQueue.isDispatchThread();
 411  0 _ensureReady();
 412  0 ThreadReference thread = _suspendedThreads.remove(threadData.getUniqueID());
 413  0 _resumeThread(thread, false);
 414    }
 415   
 416    /** Steps the execution of the currently loaded document. */
 417  0 public /* synchronized */ void step(StepType type) throws DebugException {
 418  0 assert EventQueue.isDispatchThread();
 419  0 _ensureReady();
 420  0 _stepHelper(type, true);
 421    }
 422   
 423    /** Checks whether the argument is a valid variable or field access.
 424    * @param var the name of the field
 425    * @return true if the argument is a valid variable or field access */
 426  0 public static boolean isSimpleVariableOrFieldAccess(String var) {
 427    // only allow these formats:
 428    // f
 429    // f[1]
 430    // f[1][2]
 431    // f[1] [2]
 432    // o.f
 433    // o[1].f
 434    // o[1][2].f
 435    // o[1] [2].f
 436  0 String[] parts = var.split("\\.",-1);
 437  0 String name, indexPart;
 438  0 for(String part: parts) {
 439    // System.out.println("part: "+part);
 440  0 int bracketPos = part.indexOf('[');
 441  0 if (bracketPos >=0) {
 442  0 name = part.substring(0, bracketPos).trim();
 443  0 indexPart = part.substring(bracketPos).trim();
 444    // System.out.println("\tindexPart: "+indexPart);
 445  0 if (!indexPart.startsWith("[") ||
 446  0 !indexPart.endsWith("]")) return false;
 447  0 indexPart = indexPart.substring(1, indexPart.length()-1).trim();
 448    // indexPart now is "1" or "1][2" or "1] [2"
 449  0 String[] indices = indexPart.split("\\]\\s*\\[",-1);
 450  0 if (indices.length==0) return false;
 451  0 for(String indexStr: indices) {
 452  0 indexStr = indexStr.trim();
 453    // System.out.println("\t\tindexStr: "+indexStr);
 454  0 try {
 455  0 Integer index = new Integer(indexStr);
 456    // System.out.println("\t\tindex: "+index);
 457    }
 458  0 catch(NumberFormatException nfe) { return false; }
 459    }
 460    }
 461    else {
 462  0 name = part.trim();
 463    }
 464    // System.out.println("\tname: "+name);
 465  0 if (!isJavaIdentifier(name)) return false;
 466    }
 467  0 return true;
 468    }
 469   
 470    /** @return true if s is a valid Java identifier. */
 471  0 public static boolean isJavaIdentifier(String s) {
 472  0 if (s.length() == 0 || !Character.isJavaIdentifierStart(s.charAt(0))) {
 473  0 return false;
 474    }
 475  0 for (int i=1; i<s.length(); ++i) {
 476  0 if (!Character.isJavaIdentifierPart(s.charAt(i))) {
 477  0 return false;
 478    }
 479    }
 480  0 return true;
 481    }
 482   
 483    /** Adds a watch on the given field or variable.
 484    * @param field the name of the field we will watch
 485    */
 486  0 public /* synchronized */ void addWatch(String field) throws DebugException {
 487    // _ensureReady();
 488  0 assert EventQueue.isDispatchThread();
 489  0 if (!(DrJava.getConfig().getSetting(OptionConstants.DEBUG_EXPRESSIONS_AND_METHODS_IN_WATCHES).booleanValue()) &&
 490    !isSimpleVariableOrFieldAccess(field)) {
 491  0 Utilities.showMessageBox("Expressions and method calls are not allowed.\n"+
 492    "See the 'Debugger' category in the Preferences.",
 493    "Error Adding Watch");
 494  0 return;
 495    }
 496   
 497  0 final DebugWatchData w = new DebugWatchData(field);
 498  0 _watches.add(w);
 499  0 _updateWatches();
 500   
 501    // Utilities.invokeLater(new Runnable() { public void run() {
 502  0 _notifier.watchSet(w);
 503    // } });
 504    }
 505   
 506    /** Removes any watches on the given field or variable.
 507    * Has no effect if the given field is not being watched.
 508    * @param field the name of the field we will watch
 509    */
 510  0 public /* synchronized */ void removeWatch(String field) throws DebugException {
 511    // _ensureReady();
 512  0 assert EventQueue.isDispatchThread();
 513  0 for (int i = 0; i < _watches.size(); i++) {
 514  0 final DebugWatchData watch = _watches.get(i);
 515  0 if (watch.getName().equals(field)) {
 516  0 _watches.remove(i);
 517    // Utilities.invokeLater(new Runnable() { public void run() {
 518  0 _notifier.watchRemoved(watch);
 519    // } });
 520    }
 521    }
 522    }
 523   
 524    /** Removes the watch at the given index.
 525    * @param index Index of the watch to remove
 526    */
 527  0 public /* synchronized */ void removeWatch(int index) throws DebugException {
 528    // _ensureReady();
 529  0 assert EventQueue.isDispatchThread();
 530  0 if (index < _watches.size()) {
 531  0 final DebugWatchData watch = _watches.get(index);
 532  0 _watches.remove(index);
 533    // Utilities.invokeLater(new Runnable() { public void run() {
 534  0 _notifier.watchRemoved(watch);
 535    // } });
 536    }
 537    }
 538   
 539    /** Removes all watches on existing fields and variables.
 540    */
 541  3 public /* synchronized */ void removeAllWatches() throws DebugException {
 542    // _ensureReady();
 543  3 assert EventQueue.isDispatchThread();
 544  3 while (_watches.size() > 0) {
 545  0 removeWatch( _watches.get(0).getName());
 546    }
 547    }
 548   
 549    /** Enable or disable the specified breakpoint.
 550    * @param breakpoint breakpoint to change
 551    */
 552  0 public /* synchronized */ void notifyBreakpointChange(Breakpoint breakpoint) {
 553  0 assert EventQueue.isDispatchThread();
 554  0 _model.getBreakpointManager().changeRegion(breakpoint, new Lambda<Breakpoint, Object>() {
 555  0 public Object value(Breakpoint bp) {
 556    // change has already been made, just notify all listeners
 557  0 return null;
 558    }
 559    });
 560    }
 561   
 562    /** Toggles whether a breakpoint is set at the given line in the given document.
 563    * @param doc Document in which to set or remove the breakpoint
 564    * @param offset Start offset on the line to set the breakpoint
 565    * @param isEnabled {@code true} if this breakpoint should be enabled
 566    * @return true if breakpoint is set
 567    */
 568  0 public boolean toggleBreakpoint(OpenDefinitionsDocument doc, int offset, boolean isEnabled)
 569    throws DebugException {
 570  0 assert EventQueue.isDispatchThread();
 571    // ensure that offset is at line start and falls within the document
 572  0 offset = doc._getLineStartPos(offset);
 573  0 if (offset < 0) return false;
 574   
 575  0 Breakpoint breakpoint = _model.getBreakpointManager().getRegionAt(doc, offset);
 576   
 577  0 if (breakpoint == null) { // no breakpoint on this line
 578  0 if (offset == doc._getLineEndPos(offset)) { // line is empty
 579  0 Utilities.show("Cannot set a breakpoint on an empty line.");
 580  0 return false;
 581    }
 582    else { // set breakpoint
 583  0 try {
 584  0 setBreakpoint(new JPDABreakpoint(doc, offset, isEnabled, this));
 585  0 return true;
 586    }
 587    catch(LineNotExecutableException lne) {
 588  0 Utilities.showMessageBox(lne.getMessage(), "Error Toggling Breakpoint");
 589  0 return false;
 590    }
 591    }
 592    }
 593    else { // breakpoint already set on this line
 594  0 _model.getBreakpointManager().removeRegion(breakpoint);
 595  0 return false;
 596    }
 597    }
 598   
 599   
 600   
 601    /** Sets a breakpoint.
 602    * @param breakpoint The new breakpoint to set
 603    */
 604  0 public /* synchronized */ void setBreakpoint(final Breakpoint breakpoint) throws DebugException {
 605  0 assert EventQueue.isDispatchThread();
 606  0 breakpoint.getDocument().checkIfClassFileInSync();
 607  0 _model.getBreakpointManager().addRegion(breakpoint);
 608    }
 609   
 610    /** Returns the line number of the breakpoint, possibly mapped from LL to Java
 611    * if the breakpoint is set in a language level file. */
 612  0 public int LLBreakpointLineNum(Breakpoint breakpoint){
 613  0 int line = breakpoint.getLineNumber();
 614  0 File f = breakpoint.getFile();
 615   
 616  0 if (DrJavaFileUtils.isLLFile(f)) {
 617  0 f = DrJavaFileUtils.getJavaForLLFile(f);
 618  0 TreeMap<Integer, Integer> tM = getLLSTM().readLLBlock(f);
 619  0 line = tM.get(breakpoint.getLineNumber());
 620    }
 621  0 return line;
 622    }
 623   
 624    /** Return a stack trace element that matches the given location, but Java line numbers
 625    * have been mapped to LL line numbers.
 626    * @param l location with Java line numbers
 627    * @param files open LL files
 628    * @return stack trace element with LL line numbers
 629    */
 630  0 public StackTraceElement getLLStackTraceElement(Location l, List<File> files) {
 631    // map Java line numbers to LL line numbers
 632  0 int lineNum = l.lineNumber();
 633  0 String sourceName = null;
 634  0 try { sourceName = l.sourceName(); }
 635  0 catch(com.sun.jdi.AbsentInformationException aie) { sourceName = null; }
 636  0 StackTraceElement ste =
 637    new StackTraceElement(l.declaringType().name(), l.method().name(), sourceName, l.lineNumber());
 638  0 return getLLSTM().replaceStackTraceElement(ste, files);
 639    }
 640   
 641    /** Return a JDI location that matches the given location, but Java line numbers
 642    * have been mapped to LL line numbers.
 643    * @param l location with Java line numbers
 644    * @param files open LL files
 645    * @return JDI location with LL line numbers
 646    */
 647  0 public Location getLLLocation(Location l, List<File> files) {
 648  0 StackTraceElement ste = getLLStackTraceElement(l, files);
 649  0 return new DelegatingLocation(ste.getFileName(), ste.getLineNumber(), l);
 650    }
 651   
 652    /** Removes a breakpoint. Called from toggleBreakpoint -- even with BPs that are not active.
 653    * @param bp The breakpoint to remove.
 654    */
 655  0 public /* synchronized */ void removeBreakpoint(Breakpoint bp) throws DebugException {
 656  0 assert EventQueue.isDispatchThread();
 657  0 if (!(bp instanceof JPDABreakpoint)) { throw new IllegalArgumentException("Unsupported breakpoint"); }
 658    else {
 659  0 JPDABreakpoint breakpoint = (JPDABreakpoint) bp;
 660  0 Vector<BreakpointRequest> requests = breakpoint.getRequests();
 661  0 if (requests.size() > 0 && _eventManager != null) {
 662    // Remove all event requests for this breakpoint
 663  0 try {
 664  0 for (int i = 0; i < requests.size(); i++) {
 665  0 _eventManager.deleteEventRequest(requests.get(i));
 666    }
 667    }
 668    catch (VMMismatchException vme) {
 669    // Not associated with this VM; probably from a previous session.
 670    // Ignore and make sure it gets removed from the document.
 671  0 _log("VMMismatch when removing breakpoint.", vme);
 672    }
 673    catch (VMDisconnectedException vmde) {
 674    // The VM has already disconnected for some reason
 675    // Ignore it and make sure the breakpoint gets removed from the document
 676  0 _log("VMDisconnected when removing breakpoint.", vmde);
 677    }
 678    }
 679   
 680    // Always remove from pending request, since it's always there
 681  0 _pendingRequestManager.removePendingRequest(breakpoint);
 682    }
 683    }
 684   
 685    /** Returns all currently watched fields and variables. No synchronization required because _watches is final. */
 686  1 public ArrayList<DebugWatchData> getWatches() throws DebugException {
 687    //_ensureReady();
 688  1 return _watches;
 689    }
 690   
 691    /** Returns a list of all threads being tracked by the debugger. Does not return any threads known to be dead. */
 692  0 public /* synchronized */ ArrayList<DebugThreadData> getCurrentThreadData() throws DebugException {
 693  0 assert EventQueue.isDispatchThread();
 694  0 if (! isReady()) { return new ArrayList<DebugThreadData>(); }
 695  0 Iterable<ThreadReference> listThreads;
 696  0 try { listThreads = _vm.allThreads(); }
 697    catch (VMDisconnectedException vmde) {
 698    // We're quitting, just pass back an empty Vector
 699  0 return new ArrayList<DebugThreadData>();
 700    }
 701   
 702  0 ArrayList<DebugThreadData> threads = new ArrayList<DebugThreadData>();
 703  0 for (ThreadReference ref : listThreads) {
 704  0 try { threads.add(new JPDAThreadData(ref)); }
 705    catch (ObjectCollectedException e) {
 706    // this thread just died, we don't want to list it anyway
 707    }
 708    }
 709  0 return threads;
 710    }
 711   
 712    /** Returns a Vector of DebugStackData for the current suspended thread.
 713    * @throws DebugException if the current thread is running or there
 714    * are no suspended threads
 715    * TO DO: Config option for hiding DrJava subset of stack trace
 716    */
 717  0 public ArrayList<DebugStackData> getCurrentStackFrameData() throws DebugException {
 718  0 assert EventQueue.isDispatchThread();
 719  0 if (! isReady()) return new ArrayList<DebugStackData>();
 720   
 721  0 if (_runningThread != null || _suspendedThreads.size() <= 0) {
 722  0 throw new DebugException("No suspended thread to obtain stack frames.");
 723    }
 724   
 725  0 try {
 726  0 ThreadReference thread = _suspendedThreads.peek();
 727  0 ArrayList<DebugStackData> frames = new ArrayList<DebugStackData>();
 728    // get a list of language level files whose line numbers need to be translated
 729  0 final List<File> files = new ArrayList<File>();
 730  0 for (OpenDefinitionsDocument odd: _model.getLLOpenDefinitionsDocuments()) { files.add(odd.getRawFile()); }
 731  0 for (StackFrame f : thread.frames()) {
 732    // map Java line numbers to LL line numbers
 733  0 String method = JPDAStackData.methodName(f);
 734  0 StackTraceElement ste = getLLStackTraceElement(f.location(), files);
 735  0 frames.add(new JPDAStackData(method, ste.getLineNumber()));
 736    }
 737  0 return frames;
 738    }
 739    catch (IncompatibleThreadStateException itse) {
 740  0 error.log("Unable to obtain stack frame.", itse);
 741  0 return new ArrayList<DebugStackData>();
 742    }
 743    catch (VMDisconnectedException vmde) {
 744  0 error.log("VMDisconnected when getting the current stack frame data.", vmde);
 745  0 return new ArrayList<DebugStackData>();
 746    }
 747    catch (InvalidStackFrameException isfe) {
 748  0 error.log("The stack frame requested is invalid.", isfe);
 749  0 return new ArrayList<DebugStackData>();
 750    }
 751    }
 752   
 753    /** Return the adjusted location (identical to input unless the matching document is a LL file) and document
 754    * associated with this location generated by the JVM and hence associated with a conventional Java (not an LL)
 755    * file. A document is preloaded when a debugger step occurs. This method was originally intended to avoid
 756    * the deadlock described in [ 1696060 ] Debugger Infinite Loop. but all debugger actions now occur in the Event
 757    * thread and synchronization has been elided. */
 758  0 public Pair<Location, OpenDefinitionsDocument> preloadDocument(Location location) {
 759  0 assert EventQueue.isDispatchThread();
 760  0 OpenDefinitionsDocument doc = null;
 761  0 Location lll = null; /* Location in source file; adjusted for LL file if necessary. */
 762   
 763  0 String fileName;
 764  0 try {
 765  0 final List<File> files = new ArrayList<File>();
 766  0 for (OpenDefinitionsDocument odd: _model.getLLOpenDefinitionsDocuments()) { files.add(odd.getRawFile()); }
 767  0 lll = getLLLocation(location, files);
 768   
 769   
 770  0 fileName = lll.sourcePath();
 771   
 772    // Check source root set (open files)
 773  0 File f = _model.getSourceFile(fileName);
 774  0 if (f != null) {
 775    // Get a document for this file, forcing it to open
 776  0 try { doc = _model.getDocumentForFile(f); }
 777  0 catch (IOException ioe) { doc = null; }
 778    }
 779    }
 780    catch(AbsentInformationException e) {
 781    // No stored doc, look on the source root set (later, also the sourcepath)
 782  0 final List<File> files = new ArrayList<File>();
 783  0 for(OpenDefinitionsDocument odd: _model.getLLOpenDefinitionsDocuments()) { files.add(odd.getRawFile()); }
 784   
 785  0 ReferenceType rt = location.declaringType();
 786  0 fileName = null;
 787  0 try { fileName = DrJavaFileUtils.getPackageDir(rt.name()) + rt.sourceName(); }
 788    catch (AbsentInformationException aie) {
 789    // Don't know real source name:
 790    // assume source name is same as file name
 791  0 fileName = null;
 792  0 String className = rt.name().replace('.', File.separatorChar);
 793   
 794    // crop off the $ if there is one and anything after it
 795  0 int indexOfDollar = className.indexOf('$');
 796  0 if (indexOfDollar > -1) {
 797  0 className = className.substring(0, indexOfDollar);
 798    }
 799   
 800  0 for(File f: files) {
 801    // TODO: What about Habanero Java extension?
 802  0 for(String ext: DrJavaFileUtils.getSourceFileExtensions()) {
 803  0 if (f.getName().equals(className + ext)) {
 804  0 fileName = f.getName();
 805  0 break;
 806    }
 807    }
 808  0 if (fileName != null) { break; }
 809    }
 810  0 if (fileName == null) {
 811  0 fileName = className + OptionConstants.JAVA_FILE_EXTENSION;
 812    }
 813    }
 814   
 815  0 if (fileName != null) {
 816    // Check source root set (open files)
 817  0 File f = _model.getSourceFile(fileName);
 818  0 if (f != null) {
 819    // Get a document for this file, forcing it to open
 820  0 try { doc = _model.getDocumentForFile(f); }
 821    catch (IOException ioe) {
 822    // No doc, so don't notify listener
 823    }
 824    }
 825    }
 826    }
 827  0 if (lll == null) lll = location;
 828  0 return Pair.make(lll, doc);
 829    }
 830   
 831    /** Scrolls to the source location specified by the the debug stack data.
 832    * @param stackData Stack data containing location to display
 833    * @throws DebugException if current thread is not suspended
 834    */
 835  0 public /* synchronized */ void scrollToSource(DebugStackData stackData) throws DebugException {
 836    // System.out.println("scrollToSource DebugStackData: " + stackData.getLine());
 837  0 assert EventQueue.isDispatchThread();
 838  0 _ensureReady();
 839  0 if (_runningThread != null) {
 840  0 throw new DebugException("Cannot scroll to source unless thread is suspended.");
 841    }
 842   
 843  0 ThreadReference threadRef = _suspendedThreads.peek();
 844  0 Iterator<StackFrame> i;
 845   
 846  0 try {
 847  0 if (threadRef.frameCount() <= 0 ) {
 848  0 printMessage("Could not scroll to source. The current thread had no stack frames.");
 849  0 return;
 850    }
 851  0 i = threadRef.frames().iterator();
 852    }
 853    catch (IncompatibleThreadStateException e) {
 854  0 throw new DebugException("Unable to find stack frames: " + e);
 855    }
 856   
 857  0 final List<File> files = new ArrayList<File>();
 858  0 for(OpenDefinitionsDocument odd: _model.getLLOpenDefinitionsDocuments()) { files.add(odd.getRawFile()); }
 859   
 860    // map Java to LL line numbers using LanguageLevelStackTraceMapper
 861  0 while (i.hasNext()) {
 862  0 StackFrame frame = i.next();
 863   
 864  0 Location lll = getLLLocation(frame.location(), files);
 865   
 866  0 if (lll.lineNumber() == stackData.getLine() &&
 867    stackData.getMethod().equals(frame.location().declaringType().name() + "." +
 868    frame.location().method().name())) {
 869  0 scrollToSource(lll, false);
 870    }
 871    }
 872    }
 873   
 874    /** Scrolls to the source of the given breakpoint.
 875    * @param bp the breakpoint
 876    */
 877  0 public /* synchronized */ void scrollToSource(Breakpoint bp) {
 878  0 scrollToSource(bp, false);
 879    }
 880   
 881    /** Scrolls to the source of the given breakpoint.
 882    * @param bp the breakpoint
 883    */
 884  0 public /* synchronized */ void scrollToSource(Breakpoint bp, boolean shouldHighlight) {
 885    // System.out.println("scrollToSource Breakpoint: " + bp.getLineNumber());
 886  0 openAndScroll(bp.getDocument(), bp.getLineNumber(), bp.getClassName(), shouldHighlight);
 887    }
 888   
 889    /** Gets the Breakpoint object at the specified line in the given class.
 890    * If the given data do not correspond to an actual breakpoint, null is returned.
 891    * @param line The line number of the breakpoint
 892    * @param className The name of the class the breakpoint's in
 893    * @return the Breakpoint corresponding to the line and className, or null if there is no such breakpoint.
 894    */
 895  0 public /* synchronized */ Breakpoint getBreakpoint(int line, String className) {
 896  0 assert EventQueue.isDispatchThread();
 897  0 for (int i = 0; i < _model.getBreakpointManager().getRegions().size(); i++) {
 898  0 Breakpoint bp = _model.getBreakpointManager().getRegions().get(i);
 899  0 if ((LLBreakpointLineNum(bp) == line) && (bp.getClassName().equals(className))) {
 900  0 return bp;
 901    }
 902    }
 903    // bp not found in the list of breakpoints
 904  0 return null;
 905    }
 906   
 907   
 908   
 909   
 910    /** Accessor for the _vm field. Called from DocumentDebugAction and this. */
 911  0 VirtualMachine getVM() { return _vm; }
 912   
 913    /** Returns the current EventRequestManager from JDI, or null if startUp() has not been called. */
 914  0 EventRequestManager getEventRequestManager() { return _eventManager; }
 915   
 916    /** Returns the pending request manager used by the debugger. */
 917  0 PendingRequestManager getPendingRequestManager() { return _pendingRequestManager; }
 918   
 919    /** Returns the suspended thread at the current index of the stack.
 920    * @param i index into the stack of suspended threads
 921    */
 922  0 ThreadReference getThreadAt(int i) { return _suspendedThreads.peekAt(i); }
 923   
 924    /** Returns the running thread currently tracked by the debugger. */
 925  0 ThreadReference getCurrentRunningThread() { return _runningThread; }
 926   
 927   
 928   
 929    /** Ensures that debugger is active. Should be called by every public method in the debugger except for startUp().
 930    * Assumes lock is already held.
 931    * @throws IllegalStateException if debugger is not active
 932    * @throws DebugException if an exception was detected in the EventHandlerThread
 933    */
 934  0 private void _ensureReady() throws DebugException {
 935  0 if (! isReady()) throw new IllegalStateException("Debugger is not active.");
 936   
 937  0 if (_eventHandlerError != null) {
 938  0 Throwable t = _eventHandlerError;
 939  0 _eventHandlerError = null;
 940  0 throw new DebugException("Error in Debugger Event Handler: " + t);
 941    }
 942    }
 943   
 944    /** Records that an error occurred in the EventHandlerThread. The next call to _ensureReady() will fail, indicating
 945    * that the error occurred. Not private because EventHandlerThread accesses it.
 946    * @param t Error occurring in the EventHandlerThread
 947    */
 948  0 void eventHandlerError(Throwable t) {
 949  0 _log("Error in EventHandlerThread: " + t);
 950  0 _eventHandlerError = t;
 951    }
 952   
 953    /** Handles the details of attaching to the interpreterJVM. Only runs in the event thread. */
 954  0 private void _attachToVM() throws DebugException {
 955  0 assert EventQueue.isDispatchThread();
 956    // System.err.println("Debugger attaching to VM");
 957   
 958    // Get the connector
 959  0 AttachingConnector connector = _getAttachingConnector();
 960   
 961    // Try to connect on our debug port
 962  0 Map<String, Connector.Argument> args = connector.defaultArguments();
 963  0 Connector.Argument port = args.get("port");
 964  0 Connector.Argument host = args.get("hostname");
 965  0 try {
 966  0 int debugPort = _model.getDebugPort();
 967  0 port.setValue("" + debugPort);
 968  0 host.setValue("127.0.0.1"); // necessary if hostname can't be resolved
 969  0 _vm = connector.attach(args);
 970  0 _eventManager = _vm.eventRequestManager();
 971    }
 972    catch(Exception e) {
 973    // System.err.println("Could not connect to VM: " + e);
 974  0 throw new DebugException("Could not connect to VM: " + e);
 975    }
 976   
 977  0 _interpreterJVM = (ObjectReference) _getStaticField(_getClass(InterpreterJVM.class.getName()), "ONLY");
 978    // System.err.println("_interpreterm vm is " + _interpreterJVM);
 979    }
 980   
 981    /** Returns an attaching connector to use for connecting to the interpreter JVM. */
 982  0 private AttachingConnector _getAttachingConnector() throws DebugException {
 983  0 VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
 984  0 List<AttachingConnector> connectors = vmm.attachingConnectors();
 985  0 AttachingConnector connector = null;
 986  0 for (AttachingConnector conn: connectors) {
 987  0 if (conn.name().equals("com.sun.jdi.SocketAttach")) connector = conn;
 988    }
 989  0 if (connector == null) throw new DebugException("Could not find an AttachingConnector!");
 990  0 return connector;
 991    }
 992   
 993    /** Sets the debugger's currently active thread. This method assumes that the given thread is already suspended.
 994    * Returns true if this actually changed the suspended thread by pushing it onto the stack of suspended threads.
 995    * Returns false if this thread was already selected. The return value fixes a bug that occurs if the user steps
 996    * into a breakpoint.
 997    * @throws IllegalArgumentException if thread is not suspended.
 998    */
 999  0 boolean setCurrentThread(ThreadReference thread) {
 1000  0 assert EventQueue.isDispatchThread();
 1001  0 if (! thread.isSuspended()) {
 1002  0 throw new IllegalArgumentException("Thread must be suspended to set as current. Given: " + thread);
 1003    }
 1004   
 1005  0 try {
 1006  0 if ((_suspendedThreads.isEmpty() || ! _suspendedThreads.contains(thread.uniqueID())) &&
 1007    (thread.frameCount() > 0)) {
 1008  0 _suspendedThreads.push(thread);
 1009  0 return true;
 1010    }
 1011  0 else return false;
 1012    }
 1013    catch (IncompatibleThreadStateException itse) {
 1014    // requesting stack frames should be fine, since the thread must be
 1015    // suspended or frameCount() is not called
 1016  0 throw new UnexpectedException(itse);
 1017    }
 1018    }
 1019   
 1020    /** Returns a Vector with the loaded ReferenceTypes for the given class name
 1021    * (empty if the class could not be found). Makes no attempt to load the
 1022    * class if it is not already loaded. If the lineNumber is not
 1023    * DebugAction.ANY_LINE, this method ensures that the returned ReferenceTypes
 1024    * contain the given lineNumber, searching through inner classes if necessary.
 1025    * If no inner classes contain the line number, an empty Vector is returned.
 1026    * <p>
 1027    * If custom class loaders are in use, multiple copies of the class
 1028    * may be loaded, so all are returned.
 1029    */
 1030  0 /* synchronized */ Vector<ReferenceType> getReferenceTypes(String className, int lineNumber) {
 1031  0 assert EventQueue.isDispatchThread();
 1032    // Get all classes that match this name
 1033  0 List<ReferenceType> classes;
 1034   
 1035  0 try { classes = _vm.classesByName(className); }
 1036    catch (VMDisconnectedException vmde) {
 1037    // We're quitting, return empty Vector.
 1038  0 return new Vector<ReferenceType>();
 1039    }
 1040   
 1041    // Return each valid reference type
 1042  0 Vector<ReferenceType> refTypes = new Vector<ReferenceType>();
 1043  0 ReferenceType ref;
 1044  0 for (int i = 0; i < classes.size(); i++) {
 1045  0 ref = classes.get(i);
 1046   
 1047  0 if (lineNumber != DebugAction.ANY_LINE) {
 1048  0 List<Location> lines = new LinkedList<Location>();
 1049  0 try {
 1050  0 lines = ref.locationsOfLine(lineNumber);
 1051    }
 1052    catch (AbsentInformationException aie) {
 1053    // try looking in inner classes
 1054    }
 1055    catch (ClassNotPreparedException cnpe) {
 1056    // try the next class, maybe loaded by a different classloader
 1057  0 continue;
 1058    }
 1059    // If lines.size > 0, lineNumber was found in ref
 1060  0 if (lines.size() == 0) {
 1061    // The ReferenceType might be in an inner class, so
 1062    // look for locationsOfLine for nestedTypes
 1063  0 List<ReferenceType> innerRefs = ref.nestedTypes();
 1064  0 ref = null;
 1065  0 for (int j = 0; j < innerRefs.size(); j++) {
 1066  0 try {
 1067  0 ReferenceType currRef = innerRefs.get(j);
 1068  0 lines = currRef.locationsOfLine(lineNumber);
 1069  0 if (lines.size() > 0) {
 1070  0 ref = currRef;
 1071  0 break;
 1072    }
 1073    }
 1074    catch (AbsentInformationException aie) {
 1075    // skipping this inner class, look in another
 1076    }
 1077    catch (ClassNotPreparedException cnpe) {
 1078    // skipping this inner class, look in another
 1079    }
 1080    }
 1081    }
 1082    }
 1083  0 if ((ref != null) && ref.isPrepared()) {
 1084  0 refTypes.add(ref);
 1085    }
 1086    }
 1087  0 return refTypes;
 1088    }
 1089   
 1090    /** Assumes lock is already held.
 1091    * @return The thread in the virtual machine with name d.uniqueID()
 1092    * @throws NoSuchElementException if the thread could not be found
 1093    */
 1094  0 private ThreadReference _getThreadFromDebugThreadData(DebugThreadData d) throws NoSuchElementException {
 1095  0 List<ThreadReference> threads = _vm.allThreads();
 1096  0 Iterator<ThreadReference> iterator = threads.iterator();
 1097  0 while (iterator.hasNext()) {
 1098  0 ThreadReference threadRef = iterator.next();
 1099  0 if (threadRef.uniqueID() == d.getUniqueID()) {
 1100  0 return threadRef;
 1101    }
 1102    }
 1103    // Thread not found
 1104  0 throw new NoSuchElementException("Thread " + d.getName() + " not found in virtual machine!");
 1105    }
 1106   
 1107    /** Suspends all the currently running threads in the virtual machine.
 1108    *
 1109    * Not currently in use/available, since it is incompatible with
 1110    * the debug interpreters.
 1111    *
 1112    public synchronized void suspendAll() {
 1113    _ensureReady();
 1114    List threads = _vm.allThreads();
 1115    Iterator iterator = threads.iterator();
 1116    ThreadReference threadRef = null;
 1117   
 1118    while(iterator.hasNext()) {
 1119    threadRef = (ThreadReference)iterator.next();
 1120   
 1121    if ( !threadRef.isSuspended() ) {
 1122    threadRef.suspend();
 1123    _suspendedThreads.push(threadRef);
 1124    }
 1125    }
 1126    _runningThread = null;
 1127    }*/
 1128   
 1129    /** Suspends execution of the thread referenced by threadData.
 1130    *
 1131    * Not in use/available, since it is currently incompatible with the
 1132    * debug interpreters. (Can't execute code in a suspended thread unless
 1133    * it was suspended with a breakpoint/step.)
 1134    *
 1135    public synchronized void suspend(DebugThreadData threadData)
 1136    throws DebugException
 1137    {
 1138    _ensureReady();
 1139    // setCurrentThread suspends if necessary
 1140    setCurrentThread(threadData);
 1141    _runningThread = null;
 1142    }*/
 1143   
 1144    /** Resumes the thread currently being debugged without removing the debug interpreter or switching to the next
 1145    * suspended thread. Assumes lock is already held.
 1146    */
 1147  0 private void _resumeFromStep() throws DebugException { _resumeHelper(true); }
 1148   
 1149    /** Resumes execution of the currently suspended thread. Assumes lock is already held.
 1150    * @param fromStep Whether to copy back the variables from the current debug interpreter and switch to the next
 1151    * suspended thread.
 1152    */
 1153  0 private void _resumeHelper(boolean fromStep) throws DebugException {
 1154  0 try {
 1155  0 ThreadReference thread = _suspendedThreads.pop();
 1156   
 1157  0 _log.log("In resumeThread()");
 1158  0 _resumeThread(thread, fromStep);
 1159    }
 1160  0 catch (NoSuchElementException e) { throw new DebugException("No thread to resume."); }
 1161    }
 1162   
 1163    /** Resumes the given thread, only copying variables from its debug interpreter if shouldCopyBack is true. Assumes
 1164    * lock on this is already held.
 1165    * @param thread Thread to resume
 1166    * @param fromStep Whether to copy back the variables from
 1167    * the current debug interpreter and switch to the next
 1168    * suspended thread.
 1169    * @throws IllegalArgumentException if thread is null
 1170    */
 1171  0 private void _resumeThread(ThreadReference thread, boolean fromStep) throws DebugException {
 1172  0 if (thread == null) {
 1173  0 throw new IllegalArgumentException("Cannot resume a null thread");
 1174    }
 1175   
 1176  0 int suspendCount = thread.suspendCount();
 1177  0 _log.log("Getting suspendCount = " + suspendCount);
 1178   
 1179   
 1180  0 _runningThread = thread;
 1181  0 if (!fromStep) {
 1182    // Copy variables back into the thread
 1183  0 _copyVariablesFromInterpreter();
 1184  0 _updateWatches();
 1185    }
 1186  0 try {
 1187  0 _removeCurrentDebugInterpreter(fromStep);
 1188  0 _currThreadResumed();
 1189    }
 1190  0 catch(DebugException e) { throw new UnexpectedException(e); }
 1191   
 1192    // Must resume the correct number of times
 1193  0 for (int i = suspendCount; i > 0; i--) thread.resume();
 1194   
 1195    // Notify listeners of a resume
 1196   
 1197    // Switch to next suspended thread, if any
 1198  0 if (! fromStep && ! _suspendedThreads.isEmpty()) _switchToSuspendedThread();
 1199    }
 1200   
 1201    /** Performs a step in the currently suspended thread, only generating a step event if shouldNotify if true. Assumes
 1202    * that lock is already held.
 1203    * @param type The type of step to perform
 1204    * @param shouldNotify Whether to generate a step event
 1205    */
 1206  0 private void _stepHelper(StepType type, boolean shouldNotify) throws DebugException {
 1207  0 if (_suspendedThreads.size() <= 0 || _runningThread != null) {
 1208  0 throw new IllegalStateException("Cannot step if the current thread is not suspended.");
 1209    }
 1210   
 1211  0 _log.log(this + "is About to peek ...");
 1212   
 1213  0 ThreadReference thread = _suspendedThreads.peek();
 1214  0 _log.log(this + "is Stepping " + thread.toString());
 1215   
 1216    // Copy the variables back into the thread from the appropriate interpreter.
 1217    // We do this before stepping since DrJava will hang if you try to copy back
 1218    // variables after creating the step request.
 1219  0 _runningThread = thread;
 1220  0 _copyVariablesFromInterpreter();
 1221   
 1222  0 _log.log(this + " is Deleting pending requests ...");
 1223   
 1224    // If there's already a step request for the current thread, delete
 1225    // it first
 1226  0 List<StepRequest> steps = _eventManager.stepRequests();
 1227  0 for (int i = 0; i < steps.size(); i++) {
 1228  0 StepRequest step = steps.get(i);
 1229  0 if (step.thread().equals(thread)) {
 1230  0 _eventManager.deleteEventRequest(step);
 1231  0 break;
 1232    }
 1233    }
 1234   
 1235  0 _log.log(this + " Issued step request");
 1236  0 int stepFlag = Integer.MIN_VALUE; // should always be changed, but compiler doesn't check that
 1237  0 switch (type) {
 1238  0 case STEP_INTO: stepFlag = StepRequest.STEP_INTO; break;
 1239  0 case STEP_OVER: stepFlag = StepRequest.STEP_OVER; break;
 1240  0 case STEP_OUT: stepFlag = StepRequest.STEP_OUT; break;
 1241    }
 1242  0 new Step(this, StepRequest.STEP_LINE, stepFlag);
 1243  0 if (shouldNotify) notifyStepRequested();
 1244  0 _log.log(this + " About to resume");
 1245  0 _resumeFromStep();
 1246    }
 1247   
 1248    /** Called when a breakpoint is reached. The Breakpoint object itself should be stored in the "debugAction" property
 1249    * on the request.
 1250    * @param request The BreakPointRequest reached by the debugger
 1251    */
 1252  0 void reachedBreakpoint(BreakpointRequest request) {
 1253    // Utilities.showDebug("JPDADebugger.reachedBreakPoint(" + request + ") called");
 1254  0 assert EventQueue.isDispatchThread();
 1255  0 Object property = request.getProperty("debugAction");
 1256  0 if (property != null && (property instanceof JPDABreakpoint)) {
 1257  0 final JPDABreakpoint breakpoint = (JPDABreakpoint) property;
 1258  0 breakpoint.update();
 1259  0 printMessage("Breakpoint hit in class " + breakpoint.getClassName() + " [line " + breakpoint.getLineNumber() + "]");
 1260  0 EventQueue.invokeLater(new Runnable() { public void run() { _notifier.breakpointReached(breakpoint); } });
 1261    }
 1262    else {
 1263    // A breakpoint we didn't set??
 1264  0 error.log("Reached a breakpoint without a debugAction property: " + request);
 1265    }
 1266    }
 1267   
 1268    // Not currently used
 1269    // /** Takes the location of event e, opens the document corresponding to its class and centers the definition pane's
 1270    // * view on the appropriate line number.
 1271    // * @param e LocatableEvent containing location to display
 1272    // */
 1273    // private void scrollToSource(LocatableEvent e) {
 1274    // Location location = e.location();
 1275    //
 1276    // // First see if doc is stored
 1277    // EventRequest request = e.request();
 1278    // Object docProp = request.getProperty("document");
 1279    // if ((docProp != null) && (docProp instanceof OpenDefinitionsDocument)) {
 1280    // openAndScroll((OpenDefinitionsDocument) docProp, location, true);
 1281    // }
 1282    // else scrollToSource(location);
 1283    // }
 1284   
 1285    /** Scroll to the location specified by location Assumes lock on this is already held */
 1286  0 private void scrollToSource(Location location) {
 1287  0 scrollToSource(location, true);
 1288    }
 1289   
 1290    /** Scroll to the location specified by location. */
 1291  0 private void scrollToSource(Location location, boolean shouldHighlight) {
 1292    // try {
 1293    // System.out.println("scrollToSource Location: " + location.lineNumber() + " " + location.sourceName() + " " +
 1294    // location.sourcePath());
 1295    // } catch(AbsentInformationException aie) { }
 1296  0 assert EventQueue.isDispatchThread();
 1297  0 Pair<Location, OpenDefinitionsDocument> locAndDoc = preloadDocument(location); // adjusts location
 1298  0 Location lll = locAndDoc.first(); // Location has been adjusted to LL if necessary
 1299  0 OpenDefinitionsDocument doc = locAndDoc.second();
 1300  0 openAndScroll(doc, lll, shouldHighlight);
 1301    }
 1302   
 1303    /** Opens a document and scrolls to the appropriate location. If doc is null, a message is printed indicating the
 1304    * source file could not be found. Assumes lock on this is already held.
 1305    * @param doc Document to open
 1306    * @param location Location to display
 1307    */
 1308  0 private void openAndScroll(OpenDefinitionsDocument doc, Location location, boolean shouldHighlight) {
 1309    // try {
 1310    // System.out.println("scrollToSource Location: " + location.lineNumber() + " " + location.sourceName() + " " +
 1311    // location.sourcePath() + " " + doc);
 1312    // } catch(AbsentInformationException aie) { }
 1313  0 openAndScroll(doc, location.lineNumber(), location.declaringType().name(), shouldHighlight);
 1314    }
 1315   
 1316    /** Opens a document and scrolls to the appropriate location. If doc is null, a message is printed indicating the
 1317    * source file could not be found. Assumes lock on this is already held.
 1318    * @param doc Document to open
 1319    * @param line the line number to display
 1320    * @param className the name of the appropriate class
 1321    */
 1322  0 private void openAndScroll(final OpenDefinitionsDocument doc, int line, String className,
 1323    final boolean shouldHighlight) {
 1324  0 assert EventQueue.isDispatchThread();
 1325    // Open and scroll if doc was found
 1326  0 if (doc != null) {
 1327  0 doc.checkIfClassFileInSync();
 1328  0 if (DrJavaFileUtils.isLLFile(doc.getRawFile())) {
 1329    // map J
 1330    }
 1331  0 final int llLine = line;
 1332    // change UI if in sync in MainFrame listener
 1333  0 EventQueue.invokeLater(new Runnable() {
 1334  0 public void run() {
 1335  0 _notifier.threadLocationUpdated(doc, llLine, shouldHighlight);
 1336    }
 1337    });
 1338    }
 1339  0 else printMessage(" (Source for " + className + " not found.)");
 1340    }
 1341   
 1342    /** Prints a message in the Interactions Pane. Not synchronized on this on this because no local state is accessed.
 1343    * @param message Message to display
 1344    */
 1345  0 void printMessage(String message) {
 1346  0 _model.printDebugMessage(message);
 1347    }
 1348   
 1349    /** Hides all of the values of the watches and their types. Called when there is no debug information. Assumes lock
 1350    * is already held.
 1351    */
 1352  0 private void _hideWatches() {
 1353  0 for (int i = 0; i < _watches.size(); i++) {
 1354  0 DebugWatchData currWatch = _watches.get(i);
 1355  0 currWatch.hideValueAndType();
 1356    }
 1357    }
 1358   
 1359    /** Updates the stored value of each watched field and variable. Synchronization is necessary because this method is
 1360    * called from unsynchronized listeners. */
 1361  0 private /* synchronized */ void _updateWatches() {
 1362  0 assert EventQueue.isDispatchThread();
 1363  0 if (! isReady()) return;
 1364   
 1365  0 for (DebugWatchData w : _watches) {
 1366  0 String name = w.getName();
 1367  0 String val = "";
 1368  0 String type = "";
 1369    // ArrayList<Integer> arr_index = new ArrayList<Integer>();
 1370    //
 1371    // if(name.indexOf("[") != -1 && name.indexOf("]") != -1) {
 1372    // name = name.substring(0, name.indexOf("["));
 1373    // arr_index.add(Integer.parseInt(w.getName().substring(w.getName().indexOf("[")+1, w.getName().indexOf("]"))));
 1374    // if(w.getName().indexOf("]")<(w.getName().length()-1)) {
 1375    // String iter = w.getName().substring(w.getName().indexOf("]")+1, w.getName().length());
 1376    // while(iter.indexOf("[") != -1 && iter.indexOf("]") != -1) {
 1377    // arr_index.add(Integer.parseInt(iter.substring(iter.indexOf("[")+1, iter.indexOf("]"))));
 1378    // if(iter.indexOf("]")<(iter.length()-1))
 1379    // iter = iter.substring(iter.indexOf("]")+1, iter.length());
 1380    // else
 1381    // iter = "";
 1382    // }
 1383    // }
 1384    // }
 1385    //
 1386    // int [] indices = new int[arr_index.size()];
 1387    // for (int i = 0; i < arr_index.size(); i++) {
 1388    // indices[i] = arr_index.get(i);
 1389    // }
 1390  0 Pair<String,String> pair = _model.getInteractionsModel().getVariableToString(name);
 1391    // System.out.println("pair==null? "+(pair==null));
 1392  0 val = pair.first();
 1393  0 type = pair.second();
 1394   
 1395  0 if (val == null) { w.setNoValue(); }
 1396  0 else { w.setValue(val); }
 1397  0 if (type == null) { w.setNoType(); }
 1398  0 else { w.setType(type); }
 1399    }
 1400    }
 1401   
 1402    /** Copy the current selected thread's visible variables (those in scope) into
 1403    * an interpreter's environment and then switch the Interactions window's
 1404    * interpreter to that interpreter.
 1405    */
 1406  0 private void _dumpVariablesIntoInterpreterAndSwitch() throws DebugException {
 1407  0 _log.log(this + " invoked dumpVariablesIntoInterpreterAndSwitch");
 1408  0 List<ObjectReference> toRelease = new LinkedList<ObjectReference>();
 1409  0 try {
 1410  0 ThreadReference thread = _suspendedThreads.peek();
 1411   
 1412    // Name the new interpreter based on this thread
 1413  0 String interpreterName = _getUniqueThreadName(thread);
 1414  0 ObjectReference mirroredName = _mirrorString(interpreterName, toRelease);
 1415  0 ObjectReference thisVal = thread.frame(0).thisObject();
 1416  0 ClassObjectReference thisClass = thread.frame(0).location().declaringType().classObject();
 1417   
 1418  0 List<ObjectReference> localVars = new LinkedList<ObjectReference>();
 1419  0 List<StringReference> localVarNames = new LinkedList<StringReference>();
 1420  0 List<ClassObjectReference> localVarClasses = new LinkedList<ClassObjectReference>();
 1421  0 try {
 1422    // we don't store the value thread.frame(0) anywhere, because it is invalidated
 1423    // each time we invoke a method in thread (as in _box)
 1424  0 for (LocalVariable v : thread.frame(0).visibleVariables()) {
 1425  0 try {
 1426    // Get the type first, so that if an error occurs, we haven't mutated the lists.
 1427  0 Type t = v.type();
 1428  0 if (t instanceof ReferenceType) {
 1429  0 localVarClasses.add(((ReferenceType) t).classObject());
 1430    }
 1431    else {
 1432    // primitive types are represented by null
 1433  0 localVarClasses.add(null);
 1434    }
 1435  0 localVarNames.add(_mirrorString(v.name(), toRelease));
 1436  0 Value val = thread.frame(0).getValue(v);
 1437  0 if (val == null || val instanceof ObjectReference) { localVars.add((ObjectReference) val); }
 1438  0 else { localVars.add(_box((PrimitiveValue) val, thread, toRelease)); }
 1439    }
 1440    catch (ClassNotLoadedException e) {
 1441    // This is a real possibility, as documented in the ClassNotLoadedException
 1442    // javadocs. We'll just ignore the exception, treating the variable as
 1443    // out-of-scope, since we can't talk about values of its type.
 1444    }
 1445    }
 1446    }
 1447    catch (AbsentInformationException e) { /* ignore -- we just won't include any local variables */ }
 1448  0 ArrayReference mirroredVars = _mirrorArray("java.lang.Object", localVars, thread, toRelease);
 1449  0 ArrayReference mirroredVarNames = _mirrorArray("java.lang.String", localVarNames, thread, toRelease);
 1450  0 ArrayReference mirroredVarClasses = _mirrorArray("java.lang.Class", localVarClasses, thread, toRelease);
 1451   
 1452  0 _invokeMethod(thread, _interpreterJVM, "addInterpreter", ADD_INTERPRETER_SIG,
 1453    mirroredName, thisVal, thisClass, mirroredVars, mirroredVarNames, mirroredVarClasses);
 1454   
 1455    // Set the new interpreter and prompt
 1456  0 String prompt = _getPromptString(thread);
 1457  0 _log.log(this + " is setting active interpreter");
 1458  0 _model.getInteractionsModel().setActiveInterpreter(interpreterName, prompt);
 1459    }
 1460  0 catch (IncompatibleThreadStateException e) { throw new DebugException(e); }
 1461    finally {
 1462  0 for (ObjectReference ref : toRelease) { ref.enableCollection(); }
 1463    }
 1464    }
 1465   
 1466    /** @return the prompt to display in the itneractions console
 1467    * based upon the ThreadReference threadRef, which is being debugged.
 1468    */
 1469  0 private String _getPromptString(ThreadReference threadRef) {
 1470  0 return "[" + threadRef.name() + "] > ";
 1471    }
 1472   
 1473    /** Create a String in the VM and prevent it from being garbage collected. */
 1474  0 private StringReference _mirrorString(String s, List<ObjectReference> toRelease) throws DebugException {
 1475  0 for (int tries = 0; tries < OBJECT_COLLECTED_TRIES; tries++) {
 1476  0 try {
 1477  0 StringReference result = _vm.mirrorOf(s);
 1478  0 result.disableCollection();
 1479  0 if (!result.isCollected()) {
 1480  0 toRelease.add(result);
 1481  0 return result;
 1482    }
 1483    }
 1484    catch (ObjectCollectedException e) { /* try again */ }
 1485    }
 1486  0 throw new DebugException("Ran out of OBJECT_COLLECTED_TRIES");
 1487    }
 1488   
 1489    /** Create an array of the given elements in the VM and prevent it from being garbage collected. */
 1490  0 private ArrayReference _mirrorArray(String elementClass, List<? extends ObjectReference> elts,
 1491    ThreadReference thread, List<ObjectReference> toRelease)
 1492    throws DebugException {
 1493  0 ClassType arrayC = (ClassType) _getClass("java.lang.reflect.Array");
 1494  0 ReferenceType elementC = _getClass(elementClass);
 1495  0 for (int tries = 0; tries < OBJECT_COLLECTED_TRIES; tries++) {
 1496  0 try {
 1497  0 ArrayReference result =
 1498    (ArrayReference) _invokeStaticMethod(thread, arrayC, "newInstance", NEW_INSTANCE_SIG,
 1499    elementC.classObject(), _vm.mirrorOf(elts.size()));
 1500  0 result.disableCollection();
 1501  0 if (!result.isCollected()) {
 1502  0 toRelease.add(result);
 1503  0 try { result.setValues(elts); }
 1504  0 catch (InvalidTypeException e) { throw new DebugException(e); }
 1505  0 catch (ClassNotLoadedException e) { throw new DebugException(e); }
 1506  0 return result;
 1507    }
 1508    }
 1509    catch (ObjectCollectedException e) { /* try again */ }
 1510    }
 1511  0 throw new DebugException("Ran out of OBJECT_COLLECTED_TRIES");
 1512    }
 1513   
 1514    /** Create a boxed object corresponding to the given primitive. */
 1515  0 private ObjectReference _box(PrimitiveValue val, ThreadReference thread,
 1516    List<ObjectReference> toRelease) throws DebugException {
 1517  0 String c = null;
 1518  0 String prim = null;
 1519  0 if (val instanceof BooleanValue) { c = "java.lang.Boolean"; prim = "Z"; }
 1520  0 else if (val instanceof IntegerValue) { c = "java.lang.Integer"; prim = "I"; }
 1521  0 else if (val instanceof DoubleValue) { c = "java.lang.Double"; prim = "D"; }
 1522  0 else if (val instanceof CharValue) { c = "java.lang.Character"; prim = "C"; }
 1523  0 else if (val instanceof ByteValue) { c = "java.lang.Byte"; prim = "B"; }
 1524  0 else if (val instanceof ShortValue) { c = "java.lang.Short"; prim = "S"; }
 1525  0 else if (val instanceof LongValue) { c = "java.lang.Long"; prim = "J"; }
 1526  0 else if (val instanceof FloatValue) { c = "java.lang.Float"; prim = "F"; }
 1527  0 ClassType location = (ClassType) _getClass(c);
 1528  0 for (int tries = 0; tries < OBJECT_COLLECTED_TRIES; tries++) {
 1529  0 try {
 1530  0 ObjectReference result;
 1531  0 try {
 1532  0 String valueOfSig = "(" + prim + ")L" + c.replace('.', '/') + ";";
 1533  0 result = (ObjectReference) _invokeStaticMethod(thread, location, "valueOf",
 1534    valueOfSig, val);
 1535    }
 1536    catch (DebugException e) {
 1537    // valueOf() is not available in all classes in Java 1.4
 1538  0 debug.log("Can't invoke valueOf()", e);
 1539  0 String consSig = "(" + prim + ")V";
 1540  0 result = (ObjectReference) _invokeConstructor(thread, location, consSig, val);
 1541    }
 1542   
 1543  0 result.disableCollection();
 1544  0 if (!result.isCollected()) {
 1545  0 toRelease.add(result);
 1546  0 return result;
 1547    }
 1548    }
 1549    catch (ObjectCollectedException e) { /* try again */ }
 1550    }
 1551  0 throw new DebugException("Ran out of OBJECT_COLLECTED_TRIES");
 1552    }
 1553   
 1554   
 1555    /** Create an unboxed primitive corresponding to the given object.
 1556    * @throws DebugException If the value is not of a type that can be unboxed, or if an error
 1557    * occurs in the unboxing method invocation.
 1558    */
 1559  0 private PrimitiveValue _unbox(ObjectReference val, ThreadReference thread) throws DebugException {
 1560  0 if (val == null) { throw new DebugException("Value can't be unboxed"); }
 1561  0 String type = val.referenceType().name();
 1562  0 String m = null;
 1563  0 String sig = null;
 1564  0 if (type.equals("java.lang.Boolean")) { m = "booleanValue"; sig = "()Z"; }
 1565  0 else if (type.equals("java.lang.Integer")) { m = "intValue"; sig = "()I"; }
 1566  0 else if (type.equals("java.lang.Double")) { m = "doubleValue"; sig = "()D"; }
 1567  0 else if (type.equals("java.lang.Character")) { m = "charValue"; sig = "()C"; }
 1568  0 else if (type.equals("java.lang.Byte")) { m = "byteValue"; sig = "()B"; }
 1569  0 else if (type.equals("java.lang.Short")) { m = "shortValue"; sig = "()S"; }
 1570  0 else if (type.equals("java.lang.Long")) { m = "longValue"; sig = "()J"; }
 1571  0 else if (type.equals("java.lang.Float")) { m = "floatValue"; sig = "()F"; }
 1572   
 1573  0 if (m == null) { throw new DebugException("Value can't be unboxed"); }
 1574  0 else { return (PrimitiveValue) _invokeMethod(thread, val, m, sig); }
 1575    }
 1576   
 1577   
 1578    /** Get a reference type corresponding to the class with the given name.
 1579    * Note there is not necessarily a one-to-one correspondence between classes
 1580    * and names -- every class loader can define a class with a certain name --
 1581    * so we just pick one. Classes defined by the bootstrap class loader have
 1582    * priority over other classes.
 1583    * @throws DebugException If no loaded class has the given name.
 1584    */
 1585  0 private ReferenceType _getClass(String name) throws DebugException {
 1586  0 List<ReferenceType> classes = _vm.classesByName(name);
 1587  0 if (classes.isEmpty()) {
 1588  0 throw new DebugException("Class '" + name + "' is not loaded");
 1589    }
 1590    else {
 1591  0 for (ReferenceType t : classes) {
 1592    // class loader is null iff it comes from the bootstrap loader
 1593  0 if (t.classLoader() == null) { return t; }
 1594    }
 1595  0 return classes.get(0);
 1596    }
 1597    }
 1598   
 1599   
 1600    /** Notifies all listeners that the current thread has been suspended. Synchronization is necessary because it is
 1601    * called from unsynchronized listeners and other classes (in same package).
 1602    */
 1603  0 void currThreadSuspended() {
 1604  0 assert EventQueue.isDispatchThread();
 1605  0 try {
 1606  0 _dumpVariablesIntoInterpreterAndSwitch();
 1607  0 _switchToSuspendedThread();
 1608    }
 1609  0 catch(DebugException de) { throw new UnexpectedException(de); }
 1610    }
 1611   
 1612    /** Notifies all listeners that the current thread has been suspended. Synchronization is unnecessary because it
 1613    * only runs in the Event thread.
 1614    * @param request The BreakPointRequest reached by the debugger
 1615    */
 1616  0 void currThreadSuspended(BreakpointRequest request) {
 1617  0 assert EventQueue.isDispatchThread();
 1618  0 try {
 1619  0 _dumpVariablesIntoInterpreterAndSwitch();
 1620  0 _switchToSuspendedThread(request);
 1621    }
 1622  0 catch(DebugException de) { throw new UnexpectedException(de); }
 1623    }
 1624   
 1625    /** Calls the real switchToSuspendedThread, telling it to updateWatches. This is what is usually called. */
 1626  0 private void _switchToSuspendedThread() throws DebugException { _switchToSuspendedThread(null, true); }
 1627   
 1628    /** Calls the real switchToSuspendedThread, telling it to updateWatches. This is what is usually called. */
 1629  0 private void _switchToSuspendedThread(BreakpointRequest request) throws DebugException {
 1630  0 _switchToSuspendedThread(request, true);
 1631    }
 1632   
 1633    /** Performs the bookkeeping to switch to the suspened thread on the top of the _suspendedThreads stack.
 1634    * @param request The BreakPointRequest reached by the debugger, or null if not a breakpoint
 1635    * @param updateWatches A flag that is false if the current file does not have debug information. This prevents the
 1636    * default interpreter's watch values from being shown.
 1637    */
 1638  0 private void _switchToSuspendedThread(BreakpointRequest request, boolean updateWatches) throws DebugException {
 1639  0 _log.log(this + " executing _switchToSuspendedThread()");
 1640  0 _runningThread = null;
 1641  0 if (updateWatches) _updateWatches();
 1642  0 final ThreadReference currThread = _suspendedThreads.peek();
 1643  0 _notifier.currThreadSuspended();
 1644    // Anytime a thread is suspended, it becomes the current thread.
 1645    // This makes sure the debug panel will correctly put the
 1646    // current thread in bold.
 1647  0 _notifier.currThreadSet(new JPDAThreadData(currThread));
 1648   
 1649  0 boolean usedBreakpointLine = false;
 1650  0 if (request != null) {
 1651    // we have breakpoint information, use it
 1652  0 Object property = request.getProperty("debugAction");
 1653  0 if (property != null && (property instanceof JPDABreakpoint)) {
 1654  0 final JPDABreakpoint breakpoint = (JPDABreakpoint) property;
 1655  0 breakpoint.update();
 1656  0 scrollToSource(breakpoint, true);
 1657  0 usedBreakpointLine = true;
 1658    }
 1659    }
 1660  0 if (! usedBreakpointLine) {
 1661  0 try {
 1662  0 if (currThread.frameCount() > 0) {
 1663  0 final List<File> files = new ArrayList<File>();
 1664  0 for(OpenDefinitionsDocument odd: _model.getLLOpenDefinitionsDocuments()) { files.add(odd.getRawFile()); }
 1665  0 scrollToSource(getLLLocation(currThread.frame(0).location(), files));
 1666    }
 1667    }
 1668    catch (IncompatibleThreadStateException itse) {
 1669  0 throw new UnexpectedException(itse);
 1670    }
 1671    }
 1672    }
 1673   
 1674    /** Returns a unique name for the given thread.
 1675    */
 1676  0 private String _getUniqueThreadName(ThreadReference thread) {
 1677  0 return Long.toString(thread.uniqueID());
 1678    }
 1679   
 1680    /** Assumes lock is already held.
 1681    * @see edu.rice.cs.drjava.model.repl.newjvm.InterpreterJVM#getVariableValue
 1682    * @see #GET_VARIABLE_VALUE_SIG
 1683    * */
 1684  0 private void _copyVariablesFromInterpreter() throws DebugException {
 1685    // copy variables' values out of interpreter's environment and
 1686    // into the relevant stack frame
 1687  0 List<ObjectReference> toRelease = new LinkedList<ObjectReference>();
 1688  0 try {
 1689    // we don't store _runningThread.frame(0) anywhere because it is invalidated
 1690    // every time we invoke a method in the thread (getVariable, for example)
 1691  0 for (LocalVariable var : _runningThread.frame(0).visibleVariables()) {
 1692  0 Value oldVal = _runningThread.frame(0).getValue(var);
 1693  0 StringReference name = _mirrorString(var.name(), toRelease);
 1694  0 ArrayReference wrappedVal =
 1695    (ArrayReference) _invokeMethod(_runningThread, _interpreterJVM, "getVariableValue",
 1696    GET_VARIABLE_VALUE_SIG, name);
 1697  0 if ((wrappedVal != null) && (wrappedVal.length() == 1)) { // if it can't be found (length is 0), just ignore it
 1698  0 try {
 1699  0 Value val = wrappedVal.getValue(0);
 1700  0 if (var.type() instanceof PrimitiveType) {
 1701  0 try { val = _unbox((ObjectReference) val, _runningThread); }
 1702  0 catch (DebugException e) { error.log("Can't unbox variable", e); }
 1703    }
 1704  0 if ((oldVal == null) || (!oldVal.equals(val))) {
 1705  0 try { _runningThread.frame(0).setValue(var, val); }
 1706  0 catch (InvalidTypeException e) { error.log("Can't set variable", e); }
 1707  0 catch (ClassNotLoadedException e) { error.log("Can't set variable", e); }
 1708    }
 1709    }
 1710    catch (ClassNotLoadedException e) { /* just ignore -- val must be null anyway */ }
 1711    }
 1712    }
 1713    }
 1714    catch (AbsentInformationException e) { /* can't see local variables -- just ignore */ }
 1715  0 catch (IncompatibleThreadStateException e) { throw new DebugException(e); }
 1716    finally {
 1717  0 for (ObjectReference ref : toRelease) { ref.enableCollection(); }
 1718    }
 1719    }
 1720   
 1721    /** Removes all of the debug interpreters as part of shutting down. Assumes lock is already held. */
 1722  0 private void _removeAllDebugInterpreters() {
 1723  0 DefaultInteractionsModel interactionsModel = _model.getInteractionsModel();
 1724  0 String oldInterpreterName;
 1725  0 if (_runningThread != null) {
 1726  0 oldInterpreterName = _getUniqueThreadName(_runningThread);
 1727  0 interactionsModel.removeInterpreter(oldInterpreterName);
 1728    }
 1729  0 while (!_suspendedThreads.isEmpty()) {
 1730  0 ThreadReference threadRef = _suspendedThreads.pop();
 1731  0 oldInterpreterName = _getUniqueThreadName(threadRef);
 1732  0 interactionsModel.removeInterpreter(oldInterpreterName);
 1733    }
 1734    }
 1735   
 1736    /** Removes the current debug interpreter upon resuming the current thread. Assumes lock on this is already held.
 1737    * @param fromStep A flat switch specifying a switch to the default interpreter since we don't want to switch to the
 1738    * next debug interpreter and display its watch data. We would like to just not have an active interpreter and put up
 1739    * an hourglass over the interactions pane, but the interpreterJVM must have an active interpreter.
 1740    */
 1741  0 private void _removeCurrentDebugInterpreter(boolean fromStep) {
 1742  0 DefaultInteractionsModel interactionsModel =
 1743    _model.getInteractionsModel();
 1744    // switch to next interpreter on the stack
 1745  0 if (fromStep || _suspendedThreads.isEmpty()) {
 1746  0 interactionsModel.setToDefaultInterpreter();
 1747    }
 1748    else {
 1749  0 ThreadReference threadRef = _suspendedThreads.peek();
 1750  0 _switchToInterpreterForThreadReference(threadRef);
 1751    }
 1752  0 String oldInterpreterName = _getUniqueThreadName(_runningThread);
 1753  0 interactionsModel.removeInterpreter(oldInterpreterName);
 1754    }
 1755   
 1756    /** Notifies all listeners that the current thread has been resumed. Unsynchronized because invokeLater runs
 1757    * asynchronously. Precondition: assumes that the current thread hasn't yet been resumed
 1758    */
 1759  0 private void _currThreadResumed() throws DebugException {
 1760  0 _log.log(this + " is executing _currThreadResumed()");
 1761  0 EventQueue.invokeLater(new Runnable() { public void run() { _notifier.currThreadResumed(); } });
 1762    }
 1763   
 1764    /** Switches the current interpreter to the one corresponding to threadRef. Assumes lock on this is already held.
 1765    * @param threadRef The ThreadRefernce corresponding to the interpreter to switch to
 1766    */
 1767  0 private void _switchToInterpreterForThreadReference(ThreadReference threadRef) {
 1768  0 String threadName = _getUniqueThreadName(threadRef);
 1769  0 String prompt = _getPromptString(threadRef);
 1770  0 _model.getInteractionsModel().setActiveInterpreter(threadName, prompt);
 1771    }
 1772   
 1773  0 void threadStarted() {
 1774  0 EventQueue.invokeLater(new Runnable() { public void run() { _notifier.threadStarted(); } });
 1775    }
 1776   
 1777    /** Notifies all listeners that the current thread has died. updateThreads is set to true if the threads and stack
 1778    * tables need to be updated, false if there are no suspended threads
 1779    */
 1780  0 void currThreadDied() throws DebugException {
 1781  0 assert EventQueue.isDispatchThread();
 1782  0 printMessage("The current thread has finished.");
 1783  0 _runningThread = null;
 1784   
 1785  0 _updateWatches();
 1786   
 1787  0 if (_suspendedThreads.size() > 0) {
 1788  0 ThreadReference thread = _suspendedThreads.peek();
 1789  0 _switchToInterpreterForThreadReference(thread);
 1790   
 1791  0 try {
 1792  0 if (thread.frameCount() <= 0) {
 1793  0 printMessage("Could not scroll to source for " + thread.name() + ". It has no stackframes.");
 1794    }
 1795  0 else scrollToSource(thread.frame(0).location());
 1796    }
 1797  0 catch(IncompatibleThreadStateException e) { throw new UnexpectedException(e); }
 1798   
 1799    // updates watches and makes buttons in UI active, does this because
 1800    // there are suspended threads on the stack
 1801  0 _switchToSuspendedThread();
 1802    }
 1803  0 _notifier.currThreadDied();
 1804    }
 1805   
 1806  0 void nonCurrThreadDied() {
 1807  0 EventQueue.invokeLater(new Runnable() { public void run() { _notifier.nonCurrThreadDied(); } });
 1808    }
 1809   
 1810    /** Notifies all listeners that the debugger has shut down. updateThreads is set to true if the threads and stack
 1811    * tables need to be updated, false if there are no suspended threads
 1812    */
 1813  0 void notifyDebuggerShutdown() {
 1814  0 EventQueue.invokeLater(new Runnable() { public void run() { _notifier.debuggerShutdown(); } });
 1815    }
 1816   
 1817    /** Notifies all listeners that the debugger has started. */
 1818  0 void notifyDebuggerStarted() {
 1819  0 EventQueue.invokeLater(new Runnable() { public void run() { _notifier.debuggerStarted(); } });
 1820    }
 1821   
 1822    /** Notifies all listeners that a step has been requested. */
 1823  0 void notifyStepRequested() {
 1824  0 EventQueue.invokeLater(new Runnable() { public void run() { _notifier.stepRequested(); } });
 1825    }
 1826   
 1827    /** Invoke the given method, and handle any errors that may arise. Note that the result
 1828    * does not have garbage collection disabled; if the result is a reference
 1829    * that will be needed later and that is not referenced elsewhere in the VM, garbage
 1830    * collection for it should be immediately disabled (and there's still the possibility
 1831    * that it was collected in the mean time...)
 1832    */
 1833  0 private static Value _invokeMethod(ThreadReference thread, ObjectReference receiver, String name,
 1834    String signature, Value... args) throws DebugException {
 1835  0 try {
 1836  0 ClassType c = (ClassType) receiver.referenceType();
 1837  0 Method m = c.concreteMethodByName(name, signature);
 1838  0 if (m == null) { throw new DebugException("Cannot find method '" + name + "'"); }
 1839  0 return receiver.invokeMethod(thread, m, Arrays.asList(args),
 1840    ObjectReference.INVOKE_SINGLE_THREADED);
 1841    }
 1842  0 catch (ClassNotPreparedException e) { throw new DebugException(e); }
 1843  0 catch (IllegalArgumentException e) { throw new DebugException(e); }
 1844  0 catch (ClassNotLoadedException e) { throw new DebugException(e); }
 1845  0 catch (IncompatibleThreadStateException e) { throw new DebugException(e); }
 1846  0 catch (InvocationException e) { throw new DebugException(e); }
 1847  0 catch (InvalidTypeException e) { throw new DebugException(e); }
 1848    }
 1849   
 1850    /** Invoke the given static method, and handle any errors that may arise. Note that
 1851    * the result does not have garbage collection disabled; if the result is a reference
 1852    * that will be needed later and that is not referenced elsewhere in the VM, garbage
 1853    * collection for it should be immediately disabled (and there's still the possibility
 1854    * that it was collected in the mean time...)
 1855    * @param signature A method signature descriptor to match.
 1856    * For example: {@code "(Ljava/lang/String;)Ljava/lang/Object;"}.
 1857    */
 1858  0 private static Value _invokeStaticMethod(ThreadReference thread, ClassType location, String name,
 1859    String signature, Value... args) throws DebugException {
 1860  0 try {
 1861  0 Method m = location.concreteMethodByName(name, signature);
 1862  0 if (m == null) { throw new DebugException("Cannot find method '" + name + "'"); }
 1863  0 return location.invokeMethod(thread, m, Arrays.asList(args),
 1864    ClassType.INVOKE_SINGLE_THREADED);
 1865    }
 1866  0 catch (ClassNotPreparedException e) { throw new DebugException(e); }
 1867  0 catch (IllegalArgumentException e) { throw new DebugException(e); }
 1868  0 catch (ClassNotLoadedException e) { throw new DebugException(e); }
 1869  0 catch (IncompatibleThreadStateException e) { throw new DebugException(e); }
 1870  0 catch (InvocationException e) { throw new DebugException(e); }
 1871  0 catch (InvalidTypeException e) { throw new DebugException(e); }
 1872    }
 1873   
 1874    /** Invoke a constructor of the given class, and handle any errors that may arise. Note
 1875    * that the result does not have garbage collection disabled; if the result will be needed
 1876    * later and is not referenced elsewhere in the VM (a likely event, since it was just
 1877    * constructed), garbage collection for it should be immediately disabled (and there's
 1878    * still the possibility that it was collected in the mean time...)
 1879    * @param signature A method signature descriptor matching the corresponding {@code <init>}
 1880    * method. For example: {@code "(Ljava/lang/String;)V"}. The return type
 1881    * will always be void.
 1882    */
 1883  0 private static Value _invokeConstructor(ThreadReference thread, ClassType location,
 1884    String signature, Value... args) throws DebugException {
 1885  0 try {
 1886  0 Method m = location.concreteMethodByName("<init>", signature);
 1887  0 if (m == null) { throw new DebugException("Cannot find requested constructor"); }
 1888  0 return location.newInstance(thread, m, Arrays.asList(args), ClassType.INVOKE_SINGLE_THREADED);
 1889    }
 1890  0 catch (ClassNotPreparedException e) { throw new DebugException(e); }
 1891  0 catch (IllegalArgumentException e) { throw new DebugException(e); }
 1892  0 catch (ClassNotLoadedException e) { throw new DebugException(e); }
 1893  0 catch (IncompatibleThreadStateException e) { throw new DebugException(e); }
 1894  0 catch (InvocationException e) { throw new DebugException(e); }
 1895  0 catch (InvalidTypeException e) { throw new DebugException(e); }
 1896    }
 1897   
 1898    /** Get the value of the given static field, and handle any errors that may arise. Note
 1899    * the result does not have garbage collection enabled; if the result is a reference
 1900    * that will be needed later and that is not referenced elsewhere in the VM (this
 1901    * would require that the value of the field subsequently changes), garbage
 1902    * collection for it should be immediately disabled (and there's still the possibility
 1903    * that it was collected in the mean time...)
 1904    */
 1905  0 private static Value _getStaticField(ReferenceType location, String name) throws DebugException {
 1906  0 try {
 1907  0 Field f = location.fieldByName(name);
 1908  0 if (f == null) { throw new DebugException("Cannot find field '" + name + "'"); }
 1909  0 return location.getValue(f);
 1910    }
 1911  0 catch (ClassNotPreparedException e) { throw new DebugException(e); }
 1912    }
 1913   
 1914    /** A thread-safe stack from which you can remove any element, not just the top of the stack. All synchronization is
 1915    * performed on the wrapped vector.
 1916    * TODO: make a generic Collection extending/replacing Stack.
 1917    */
 1918    private static class RandomAccessStack extends Stack<ThreadReference> {
 1919   
 1920  0 public ThreadReference peekAt(int i) { return get(i); }
 1921   
 1922  0 public ThreadReference remove(long id) throws NoSuchElementException {
 1923  0 synchronized(this) {
 1924  0 for (int i = 0; i < size(); i++) {
 1925  0 if (get(i).uniqueID() == id) {
 1926  0 ThreadReference t = get(i);
 1927  0 remove(i);
 1928  0 return t;
 1929    }
 1930    }
 1931    }
 1932   
 1933  0 throw new NoSuchElementException("Thread " + id + " not found in debugger suspended threads stack!");
 1934    }
 1935   
 1936  0 public boolean contains(long id) {
 1937  0 synchronized(this) {
 1938  0 for (int i = 0; i < size(); i++) {
 1939  0 if (get(i).uniqueID() == id) return true;
 1940    }
 1941  0 return false;
 1942    }
 1943    }
 1944   
 1945  0 public boolean isEmpty() { return empty(); }
 1946    }
 1947   
 1948   
 1949    /** Gets the LanguageLevelStackTraceMapper
 1950    * @return the LanguageLevelStackTraceMapper used by JPDADebugger in the compiler model
 1951    */
 1952  0 public LanguageLevelStackTraceMapper getLLSTM() { return _model.getCompilerModel().getLLSTM(); }
 1953   
 1954    /** A Location that delegates to another location in all cases except for line number,
 1955    * source path and source name. */
 1956    protected static class DelegatingLocation implements Location {
 1957    protected Location _delegee;
 1958    protected String _sourceName;
 1959    protected String _sourcePath;
 1960    protected int _lineNumber;
 1961  0 public DelegatingLocation(String sourceName, int lineNumber, Location delegee) {
 1962  0 _sourceName = sourceName;
 1963  0 try {
 1964  0 _sourcePath = delegee.sourcePath();
 1965  0 int pos = _sourcePath.lastIndexOf(File.separator);
 1966  0 if (pos >= 0)
 1967  0 _sourcePath = _sourcePath.substring(0, pos) + File.separator +_sourceName;
 1968    else
 1969  0 _sourcePath = _sourceName;
 1970    }
 1971    catch(AbsentInformationException e) {
 1972  0 _sourcePath = null;
 1973    }
 1974  0 _lineNumber = lineNumber;
 1975  0 _delegee = delegee;
 1976    }
 1977  0 public long codeIndex() { return _delegee.codeIndex(); }
 1978  0 public ReferenceType declaringType() { return _delegee.declaringType(); }
 1979  0 public boolean equals(Object obj) {
 1980  0 if (!(obj instanceof DelegatingLocation)) return false;
 1981  0 DelegatingLocation other = (DelegatingLocation)obj;
 1982  0 return _sourceName.equals(other._sourceName)
 1983    && (_lineNumber==other._lineNumber)
 1984    && _delegee.equals(other._delegee);
 1985    }
 1986  0 public int hashCode() { return _delegee.hashCode(); }
 1987  0 public int lineNumber() { return _lineNumber; }
 1988  0 public int lineNumber(String stratum) { return _lineNumber; /* Is this right? */ }
 1989  0 public Method method() { return _delegee.method(); }
 1990  0 public String sourceName() { return _sourceName; }
 1991  0 public String sourceName(String stratum) { return _sourceName; /* Is this right? */ }
 1992  0 public String sourcePath() throws AbsentInformationException {
 1993  0 if (_sourcePath != null) return _sourcePath;
 1994  0 else return _delegee.sourcePath();
 1995    }
 1996  0 public String sourcePath(String stratum) throws AbsentInformationException {
 1997  0 if (_sourcePath != null) return _sourcePath;
 1998  0 else return _delegee.sourcePath(); /* Is this right? */
 1999    }
 2000  0 public String toString() { return _delegee.toString(); }
 2001  0 public VirtualMachine virtualMachine() { return _delegee.virtualMachine(); }
 2002  0 public int compareTo(Location o) { return _delegee.compareTo(o); }
 2003    }
 2004    }