KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > drjava > model > debug > JPDADebugger


1 /*BEGIN_COPYRIGHT_BLOCK
2  *
3  * This file is part of DrJava. Download the current version of this project from http://www.drjava.org/
4  * or http://sourceforge.net/projects/drjava/
5  *
6  * DrJava Open Source License
7  *
8  * Copyright (C) 2001-2005 JavaPLT group at Rice University (javaplt@rice.edu). All rights reserved.
9  *
10  * Developed by: Java Programming Languages Team, Rice University, http://www.cs.rice.edu/~javaplt/
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13  * documentation files (the "Software"), to deal with the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
15  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16  *
17  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the
18  * following disclaimers.
19  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
20  * following disclaimers in the documentation and/or other materials provided with the distribution.
21  * - Neither the names of DrJava, the JavaPLT, Rice University, nor the names of its contributors may be used to
22  * endorse or promote products derived from this Software without specific prior written permission.
23  * - Products derived from this software may not be called "DrJava" nor use the term "DrJava" as part of their
24  * names without prior written permission from the JavaPLT group. For permission, write to javaplt@rice.edu.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
27  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28  * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * WITH THE SOFTWARE.
31  *
32  *END_COPYRIGHT_BLOCK*/

33
34 package edu.rice.cs.drjava.model.debug;
35
36 import java.io.*;
37 import java.util.Enumeration JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.LinkedList JavaDoc;
40 import java.util.List JavaDoc;
41 import java.util.Map JavaDoc;
42 import java.util.NoSuchElementException JavaDoc;
43 import java.util.Stack JavaDoc;
44 import java.util.StringTokenizer JavaDoc;
45 import java.util.Vector JavaDoc;
46
47 // DrJava stuff
48
import edu.rice.cs.util.StringOps;
49 import edu.rice.cs.util.UnexpectedException;
50 import edu.rice.cs.util.swing.Utilities;
51 import edu.rice.cs.drjava.model.DefaultGlobalModel;
52 import edu.rice.cs.drjava.model.repl.DefaultInteractionsModel;
53 import edu.rice.cs.drjava.model.repl.DummyInteractionsListener;
54 import edu.rice.cs.drjava.model.repl.InteractionsListener;
55 import edu.rice.cs.drjava.model.GlobalModelListener;
56 import edu.rice.cs.drjava.model.OpenDefinitionsDocument;
57 import edu.rice.cs.util.Log;
58 import edu.rice.cs.util.Lambda;
59
60 import com.sun.jdi.*;
61 import com.sun.jdi.connect.*;
62 import com.sun.jdi.request.*;
63 import com.sun.jdi.event.*;
64 import javax.swing.SwingUtilities JavaDoc;
65
66 /** An integrated debugger which attaches to the Interactions JVM using
67  * Sun's Java Platform Debugger Architecture (JPDA/JDI) interface.
68  *
69  * Every public method in this class throws an llegalStateException if
70  * it is called while the debugger is not active, except for isAvailable,
71  * isReady, and startUp. Public methods also throw a DebugException if
72  * the EventHandlerThread has caught an exception.
73  *
74  * @version $Id: JPDADebugger.java 4076 2007-01-19 23:01:04Z dlsmith $
75  */

76 public class JPDADebugger implements Debugger, DebugModelCallback {
77   
78   /** A log for recording messages in a file. */
79   private static final Log _log = new Log("GlobalModelTest.txt", false);
80   
81 // private static final boolean printMessages = false;
82
// private final PrintStream printStream = System.out;
83
private static final int OBJECT_COLLECTED_TRIES = 5;
84
85   /** Reference to DrJava's model. */
86   private volatile DefaultGlobalModel _model;
87
88   /** VirtualMachine of the interactions JVM. */
89   private volatile VirtualMachine _vm;
90
91   /** Manages all event requests in JDI. */
92   private volatile EventRequestManager _eventManager;
93
94   /** Vector of all current Watches. */
95   private final Vector JavaDoc<DebugWatchData> _watches = new Vector JavaDoc<DebugWatchData>();
96
97   /** Keeps track of any DebugActions whose classes have not yet been loaded, so that EventRequests can be created when the correct
98    * ClassPrepareEvent occurs.
99    */

100   private final PendingRequestManager _pendingRequestManager = new PendingRequestManager(this);
101
102   /** Provides a way for the JPDADebugger to communicate with the view. */
103   final DebugEventNotifier _notifier = new DebugEventNotifier();
104
105   /** The running ThreadReference that we are debugging. */
106   private volatile ThreadReference _runningThread;
107
108   /** Storage for all the threads suspended by this debugger. The "current" thread is the top one on the stack. */
109   private volatile RandomAccessStack _suspendedThreads;
110
111   /** A handle to the interpreterJVM that we need so we can populate the environment. */
112   private volatile ObjectReference _interpreterJVM;
113
114   private volatile InteractionsListener _watchListener;
115
116   /** If not null, this field holds an error caught by the EventHandlerThread. */
117   private volatile Throwable JavaDoc _eventHandlerError;
118
119   /** Builds a new JPDADebugger to debug code in the Interactions JVM, using the JPDA/JDI interfaces.
120    * Does not actually connect to the interpreterJVM until startUp().
121    */

122   public JPDADebugger(DefaultGlobalModel model) {
123     _model = model;
124     _vm = null;
125     _eventManager = null;
126     
127     _suspendedThreads = new RandomAccessStack();
128     _runningThread = null;
129     _interpreterJVM = null;
130     _eventHandlerError = null;
131     
132     // TO DO: Replace this with an InteractionsListener,
133
// since we really can't talk about SingleDisplayModel here!
134
_watchListener = new DummyInteractionsListener() {
135       public void interactionEnded() {
136         try { _updateWatches(); }
137         catch(DebugException de) { _log("couldn't update watches", de); }
138       }
139     };
140   }
141
142   /** Adds a listener to this JPDADebugger.
143    * @param listener a listener that reacts on events generated by the JPDADebugger
144    */

145   public void addListener(DebugListener listener) {
146     _notifier.addListener(listener);
147     _model.getBreakpointManager().addListener(listener);
148   }
149
150   /** Removes a listener to this JPDADebugger.
151    * @param listener listener to remove
152    */

153   public void removeListener(DebugListener listener) {
154     _notifier.removeListener(listener);
155     _model.getBreakpointManager().removeListener(listener);
156   }
157
158   /** Accessor for the _vm field. Called from DocumentDebugAction and this. */
159   VirtualMachine getVM() { return _vm; }
160
161   /** Logs any unexpected behavior that occurs (but which should not cause DrJava to abort).
162    * @param message message to print to the log
163    */

164   private void _log(String JavaDoc message) { _log.log(message); }
165
166   /** Logs any unexpected behavior that occurs (but which should not cause DrJava to abort).
167    * @param message message to print to the log
168    * @param t Exception or Error being logged
169    */

170   private void _log(String JavaDoc message, Throwable JavaDoc t) { _log.log(message, t); }
171
172   /** Returns whether the debugger is available in this copy of DrJava. This method does not indicate whether the
173    * debugger is ready to be used, which is indicated by isReady().
174    */

175   public boolean isAvailable() { return true; }
176
177   /** Returns whether the debugger is currently enabled. */
178   public boolean isReady() { return _vm != null; }
179   
180   /** Ensures that debugger is active. Should be called by every public method in the debugger except for startUp().
181    * Assumes lock is already held.
182    * @throws IllegalStateException if debugger is not active
183    * @throws DebugException if an exception was detected in the EventHandlerThread
184    */

185   private void _ensureReady() throws DebugException {
186     if (! isReady()) throw new IllegalStateException JavaDoc("Debugger is not active.");
187     
188     if (_eventHandlerError != null) {
189       Throwable JavaDoc t = _eventHandlerError;
190       _eventHandlerError = null;
191       throw new DebugException("Error in Debugger Event Handler: " + t);
192     }
193   }
194
195   /** Records that an error occurred in the EventHandlerThread. The next call to _ensureReady() will fail, indicating
196    * that the error occurred. Not private because EventHandlerThread accesses it.
197    * @param t Error occurring in the EventHandlerThread
198    */

199   void eventHandlerError(Throwable JavaDoc t) {
200     _log("Error in EventHandlerThread: " + t);
201     _eventHandlerError = t;
202   }
203
204   /** Attaches the debugger to the Interactions JVM to prepare for debugging. */
205   public synchronized void startUp() throws DebugException {
206     if (! isReady()) {
207       _eventHandlerError = null;
208       // check if all open documents are in sync
209
for (OpenDefinitionsDocument doc: _model.getOpenDefinitionsDocuments()) {
210         doc.checkIfClassFileInSync();
211       }
212
213       try { _attachToVM(); }
214       catch(DebugException e1) { // We sometimes see ConnectExceptions stating that the connection was refused
215
try {
216           try { Thread.sleep(100); } // Give any temporary connection problems a chance to resolve
217
catch (InterruptedException JavaDoc e) { /* ignore */ }
218           _attachToVM();
219           System.out.println("Two attempts required for debugger to attach to slave JVM");
220         }
221         catch(DebugException e2) {
222           try { Thread.sleep(100); } // Give any temporary connection problems a chance to resolve
223
catch (InterruptedException JavaDoc e) { /* ignore */ }
224           _attachToVM();
225           System.out.println("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
ThreadDeathRequest tdr = _eventManager.createThreadDeathRequest();
231       tdr.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
232       tdr.enable();
233
234       // Listen for events from JPDA in a new thread
235
EventHandlerThread eventHandler = new EventHandlerThread(this, _vm);
236       eventHandler.start();
237
238       /* Move the following command to the end of the event queue so that it is done outside outside the readLock
239        * held when this method is called from an InteractionsListener. */

240       SwingUtilities.invokeLater(new Runnable JavaDoc() {
241         public void run() { _model.getInteractionsModel().addListener(_watchListener); }
242       });
243       
244       // re-set breakpoints that have already been set
245
Vector JavaDoc<Breakpoint> oldBreakpoints = new Vector JavaDoc<Breakpoint>(_model.getBreakpointManager().getRegions());
246       _model.getBreakpointManager().clearRegions();
247       for (int i = 0; i < oldBreakpoints.size(); i++) {
248         Breakpoint bp = oldBreakpoints.get(i);
249         setBreakpoint(new Breakpoint(bp.getDocument(), bp.getOffset(), bp.getLineNumber(), bp.isEnabled(), this));
250       }
251     }
252
253     else
254       // Already started
255
throw new IllegalStateException JavaDoc("Debugger has already been started.");
256   }
257
258     
259   /** Handles the details of attaching to the interpreterJVM. Assume lock is already held. */
260   private void _attachToVM() throws DebugException {
261     // Blocks until the interpreter has registered if hasn't already. Blocks all synchronized methods in this class.
262
_model.waitForInterpreter();
263
264     // Get the connector
265
AttachingConnector connector = _getAttachingConnector();
266
267     // Try to connect on our debug port
268
Map JavaDoc<String JavaDoc, Connector.Argument> args = connector.defaultArguments();
269     Connector.Argument port = args.get("port");
270     Connector.Argument host = args.get("hostname");
271     try {
272       int debugPort = _model.getDebugPort();
273       port.setValue("" + debugPort);
274       host.setValue("127.0.0.1"); // necessary if hostname can't be resolved
275
_vm = connector.attach(args);
276       _eventManager = _vm.eventRequestManager();
277     }
278     catch(Exception JavaDoc e) { throw new DebugException("Could not connect to VM: " + e); }
279
280     _interpreterJVM = _getInterpreterJVMRef();
281   }
282
283   /** Returns an attaching connector to use for connecting to the interpreter JVM. Assumes lock is already held. */
284   private AttachingConnector _getAttachingConnector()
285     throws DebugException {
286     VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
287     List JavaDoc<AttachingConnector> connectors = vmm.attachingConnectors();
288     AttachingConnector connector = null;
289     for (AttachingConnector conn: connectors) {
290       if (conn.name().equals("com.sun.jdi.SocketAttach")) connector = conn;
291     }
292     if (connector == null) throw new DebugException("Could not find an AttachingConnector!");
293     return connector;
294   }
295
296   /** Returns an ObjectReference to the singleton instance of the InterpreterJVM class in the virtual machine being
297    * debugged. This is used to mainupulate interpreters at breakpoints. Assumes lock is already held.
298    */

299   private ObjectReference _getInterpreterJVMRef()
300     throws DebugException {
301     String JavaDoc className = "edu.rice.cs.drjava.model.repl.newjvm.InterpreterJVM";
302     List JavaDoc<ReferenceType> referenceTypes = _vm.classesByName(className); // Added parameterization <ReferenceType>. JDK 1.5 will eliminate this warning
303
if (referenceTypes.size() > 0) {
304       ReferenceType rt = referenceTypes.get(0);
305       Field field = rt.fieldByName("ONLY");
306       if (field == null) throw new DebugException("Unable to get ONLY field");
307       return (ObjectReference) rt.getValue(field);
308     }
309     else throw new DebugException("Could not get a reference to interpreterJVM");
310   }
311
312   /** Disconnects the debugger from the Interactions JVM and cleans up any state.
313    * @throws IllegalStateException if debugger is not ready
314    */

315   public synchronized void shutdown() {
316     if (isReady()) {
317       Runnable JavaDoc command = new Runnable JavaDoc() { public void run() { _model.getInteractionsModel().removeListener(_watchListener); } };
318       
319       /* Use SwingUtilities.invokeLater rather than Utilities.invokeLater because we want to defer executing this
320        * code after pending events (that may involve the _watchListener) */

321       SwingUtilities.invokeLater(command);
322       
323       _removeAllDebugInterpreters();
324       
325       try { _vm.dispose(); }
326       catch (VMDisconnectedException vmde) {
327         //VM was shutdown prematurely
328
}
329       finally {
330         _model.getInteractionsModel().setToDefaultInterpreter();
331         _vm = null;
332         _suspendedThreads = new RandomAccessStack();
333         _eventManager = null;
334         _runningThread = null;
335         try { _updateWatches(); }
336         catch (DebugException de) {
337           // Couldn't remove breakpoints/watches
338
_log("Could not remove breakpoints/watches or update watches: " + de);
339         }
340       }
341     }
342   }
343
344
345   /** Returns the current EventRequestManager from JDI, or null if startUp() has not been called. */
346   EventRequestManager getEventRequestManager() { return _eventManager; }
347
348   /** Returns the pending request manager used by the debugger. */
349   PendingRequestManager getPendingRequestManager() { return _pendingRequestManager; }
350
351   /**
352    * Sets the debugger's currently active thread.
353    * This method assumes that the given thread is already suspended.
354    * Returns true if this actually changed the suspended thread
355    * by pushing it onto the stack of suspended threads. Returns
356    * false if this thread was already selected.
357    *
358    * The return value fixes a bug that occurs if the user steps
359    * into a breakpoint.
360    *
361    * @throws IllegalArgumentException if thread is not suspended.
362    */

363   synchronized boolean setCurrentThread(ThreadReference thread) {
364     if (! thread.isSuspended()) {
365       throw new IllegalArgumentException JavaDoc("Thread must be suspended to set as current. Given: " + thread);
366     }
367
368     try {
369       if ((_suspendedThreads.isEmpty() || !_suspendedThreads.contains(thread.uniqueID())) &&
370           (thread.frameCount() > 0)) {
371         _suspendedThreads.push(thread);
372         return true;
373       }
374       else return false;
375     }
376     catch (IncompatibleThreadStateException itse) {
377       // requesting stack frames should be fine, since the thread must be
378
// suspended or frameCount() is not called
379
throw new UnexpectedException(itse);
380     }
381   }
382
383   /** Sets the notion of current thread to the one contained in threadData. The thread must be suspended. (Note: the
384    * intention is for this method to suspend the thread if necessary, but this is not yet implemented. The catch is
385    * that any manually suspended threads won't cooperate with the debug interpreters; the thread must be suspended by
386    * a breakpoint or step.)
387    * @param threadData Thread to set as current
388    * @throws IllegalStateException if debugger is not ready
389    * @throws IllegalArgumentException if threadData is null or not suspended
390    */

391   public synchronized void setCurrentThread(DebugThreadData threadData) throws DebugException {
392     _ensureReady();
393
394     if (threadData == null) {
395       throw new IllegalArgumentException JavaDoc("Cannot set current thread to null.");
396     }
397
398     ThreadReference threadRef = _getThreadFromDebugThreadData(threadData);
399
400     // Special case to avoid overhead of scrollToSource() if we
401
// are selecting the thread we have already selected currently
402

403     // Currently disabled, so we will always scroll to source, even if the
404
// thread is already selected.
405
// if ( _suspendedThreads.size() > 0 &&
406
// _suspendedThreads.peek().uniqueID() == threadRef.uniqueID() ) {
407
// return;
408
// }
409

410     // if we switch to a currently suspended thread, we need to remove
411
// it from the stack and put it on the top
412
if (_suspendedThreads.contains(threadRef.uniqueID())) _suspendedThreads.remove(threadRef.uniqueID());
413
414     if (!threadRef.isSuspended()) {
415       throw new IllegalArgumentException JavaDoc("Given thread must be suspended.");
416 // threadRef.suspend();
417
//
418
// try{
419
// if ( threadRef.frameCount() <= 0 ) {
420
// printMessage(threadRef.name() + " could not be suspended. It had no stackframes.");
421
// _suspendedThreads.push(threadRef);
422
// resume();
423
// return;
424
// }
425
// }
426
// catch(IncompatibleThreadStateException ex) {
427
// throw new UnexpectedException(ex);
428
// }
429
//
430
// //
431
// // Step now so that we can get an interpreter,
432
// // do not notify (hence the false argument)
433
// _stepHelper(StepRequest.STEP_OVER, false);
434
//return;
435
}
436
437     _suspendedThreads.push(threadRef);
438
439     try {
440       if (threadRef.frameCount() <= 0) {
441         printMessage(threadRef.name() + " could not be suspended since it has no stackframes.");
442         resume();
443         return;
444       }
445     }
446     catch (IncompatibleThreadStateException e) {
447       throw new DebugException("Could not suspend thread: " + e);
448     }
449
450     // Activate the debug interpreter for interacting with this thread
451
_switchToInterpreterForThreadReference(threadRef);
452     _switchToSuspendedThread();
453     printMessage("The current thread has changed.");
454   }
455
456   /** Returns the currently selected thread for the debugger. */
457   ThreadReference getCurrentThread() {
458     // Current thread is the top one on the stack
459
return _suspendedThreads.peek();
460   }
461
462   /** Returns the suspended thread at the current index of the stack.
463    * @param i index into the stack of suspended threads
464    */

465   ThreadReference getThreadAt(int i) { return _suspendedThreads.peekAt(i); }
466
467   /** Returns the running thread currently tracked by the debugger. */
468   ThreadReference getCurrentRunningThread() { return _runningThread; }
469
470   /** Returns whether the debugger currently has any suspended threads. */
471   public synchronized boolean hasSuspendedThreads() throws DebugException {
472     if (! isReady()) return false;
473     return _suspendedThreads.size() > 0;
474   }
475
476   /** Returns whether the debugger's current thread is suspended. */
477   public synchronized boolean isCurrentThreadSuspended() throws DebugException {
478     if (! isReady()) return false;
479     return hasSuspendedThreads() && !hasRunningThread();
480   }
481
482   /** Returns whether the thread the debugger is tracking is now running. */
483   public synchronized boolean hasRunningThread() throws DebugException {
484     if (! isReady()) return false;
485     return _runningThread != null;
486   }
487
488   /** Returns a Vector with all the loaded ReferenceTypes for the given class name (empty if the class could not be
489    * found). Makes no attempt to load the class if it is not already loaded.
490    * <p>
491    * If custom class loaders are in use, multiple copies of the class may be loaded, so all are returned.
492    */

493   Vector JavaDoc<ReferenceType> getReferenceTypes(String JavaDoc className) {
494     return getReferenceTypes(className, DebugAction.ANY_LINE);
495   }
496
497   /** Returns a Vector with the loaded ReferenceTypes for the given class name
498    * (empty if the class could not be found). Makes no attempt to load the
499    * class if it is not already loaded. If the lineNumber is not
500    * DebugAction.ANY_LINE, this method ensures that the returned ReferenceTypes
501    * contain the given lineNumber, searching through inner classes if necessary.
502    * If no inner classes contain the line number, an empty Vector is returned.
503    * <p>
504    * If custom class loaders are in use, multiple copies of the class
505    * may be loaded, so all are returned.
506    */

507   synchronized Vector JavaDoc<ReferenceType> getReferenceTypes(String JavaDoc className, int lineNumber) {
508     // Get all classes that match this name
509
List JavaDoc<ReferenceType> classes;
510     
511     try { classes = _vm.classesByName(className); }
512     catch (VMDisconnectedException vmde) {
513       // We're quitting, return empty Vector.
514
return new Vector JavaDoc<ReferenceType>();
515     }
516
517     // Return each valid reference type
518
Vector JavaDoc<ReferenceType> refTypes = new Vector JavaDoc<ReferenceType>();
519     ReferenceType ref;
520     for (int i=0; i < classes.size(); i++) {
521       ref = classes.get(i);
522
523       if (lineNumber != DebugAction.ANY_LINE) {
524         List JavaDoc<Location> lines = new LinkedList JavaDoc<Location>();
525         try {
526           lines = ref.locationsOfLine(lineNumber); // JDK 1.5 will eliminate this type warning
527
}
528         catch (AbsentInformationException aie) {
529           // try looking in inner classes
530
}
531         catch (ClassNotPreparedException cnpe) {
532           // try the next class, maybe loaded by a different classloader
533
continue;
534         }
535         // If lines.size > 0, lineNumber was found in ref
536
if (lines.size() == 0) {
537           // The ReferenceType might be in an inner class, so
538
// look for locationsOfLine for nestedTypes
539
List JavaDoc<ReferenceType> innerRefs = ref.nestedTypes(); // Added parameterization <ReferenceType>. JDK 1.5 will eliminate this type warning
540
ref = null;
541           for (int j = 0; j < innerRefs.size(); j++) {
542             try {
543               ReferenceType currRef = innerRefs.get(j);
544               lines = currRef.locationsOfLine(lineNumber); // JDK 1.5 will eliminate this type warning
545
if (lines.size() > 0) {
546                 ref = currRef;
547                 break;
548               }
549             }
550             catch (AbsentInformationException aie) {
551               // skipping this inner class, look in another
552
}
553             catch (ClassNotPreparedException cnpe) {
554               // skipping this inner class, look in another
555
}
556           }
557         }
558       }
559       if ((ref != null) && ref.isPrepared()) {
560         refTypes.add(ref);
561       }
562     }
563     return refTypes;
564   }
565
566   /** Assumes lock is already held.
567    * @return The thread in the virtual machine with name d.uniqueID()
568    * @throws NoSuchElementException if the thread could not be found
569    */

570   private ThreadReference _getThreadFromDebugThreadData(DebugThreadData d) throws NoSuchElementException JavaDoc {
571     List JavaDoc<ThreadReference> threads = _vm.allThreads();
572     Iterator JavaDoc<ThreadReference> iterator = threads.iterator();
573     while (iterator.hasNext()) {
574       ThreadReference threadRef = iterator.next();
575       if (threadRef.uniqueID() == d.getUniqueID()) {
576         return threadRef;
577       }
578     }
579     // Thread not found
580
throw new NoSuchElementException JavaDoc("Thread " + d.getName() + " not found in virtual machine!");
581   }
582
583   /**
584    * Suspends all the currently running threads in the virtual machine.
585    *
586    * Not currently in use/available, since it is incompatible with
587    * the debug interpreters.
588    *
589   public synchronized void suspendAll() {
590     _ensureReady();
591     List threads = _vm.allThreads();
592     Iterator iterator = threads.iterator();
593     ThreadReference threadRef = null;
594
595     while(iterator.hasNext()) {
596       threadRef = (ThreadReference)iterator.next();
597
598       if ( !threadRef.isSuspended() ) {
599         threadRef.suspend();
600         _suspendedThreads.push(threadRef);
601       }
602     }
603     _runningThread = null;
604   }*/

605
606   /**
607    * Suspends execution of the thread referenced by threadData.
608    *
609    * Not in use/available, since it is currently incompatible with the
610    * debug interpreters. (Can't execute code in a suspended thread unless
611    * it was suspended with a breakpoint/step.)
612    *
613   public synchronized void suspend(DebugThreadData threadData)
614     throws DebugException
615   {
616     _ensureReady();
617     // setCurrentThread suspends if necessary
618     setCurrentThread(threadData);
619     _runningThread = null;
620   }*/

621
622   /** Resumes the thread currently being debugged without removing the debug interpreter or switching to the next
623    * suspended thread. Assumes lock is already held.
624    */

625   private void _resumeFromStep() throws DebugException { _resumeHelper(true); }
626
627   /** Resumes the thread currently being debugged, copying back all variables from the current debug interpreter. */
628   public synchronized void resume() throws DebugException {
629     _ensureReady();
630     _resumeHelper(false);
631   }
632
633   /** Resumes execution of the currently suspended thread. Assumes lock is already held.
634    * @param fromStep Whether to copy back the variables from the current debug interpreter and switch to the next
635    * suspended thread.
636    */

637   private void _resumeHelper(boolean fromStep) throws DebugException {
638     try {
639       ThreadReference thread = _suspendedThreads.pop();
640
641       _log.log("In resumeThread()");
642       _resumeThread(thread, fromStep);
643     }
644     catch (NoSuchElementException JavaDoc e) { throw new DebugException("No thread to resume."); }
645   }
646
647   /** Resumes the given thread, copying back any variables from its associated debug interpreter.
648    * @param threadData Thread to resume
649    */

650   public synchronized void resume(DebugThreadData threadData) throws DebugException {
651     _ensureReady();
652     ThreadReference thread = _suspendedThreads.remove(threadData.getUniqueID());
653     _resumeThread(thread, false);
654   }
655
656   /**
657    * Resumes the given thread, only copying variables from its debug interpreter
658    * if shouldCopyBack is true.
659    * @param thread Thread to resume
660    * @param fromStep Whether to copy back the variables from
661    * the current debug interpreter and switch to the next
662    * suspended thread.
663    * @throws IllegalArgumentException if thread is null
664    */

665   private void _resumeThread(ThreadReference thread, boolean fromStep) throws DebugException {
666     if (thread == null) {
667       throw new IllegalArgumentException JavaDoc("Cannot resume a null thread");
668     }
669
670     int suspendCount = thread.suspendCount();
671     _log.log("Getting suspendCount = " + suspendCount);
672
673
674     _runningThread = thread;
675     if (!fromStep) {
676       // Copy variables back into the thread
677
_copyVariablesFromInterpreter();
678       _updateWatches();
679     }
680     try {
681       _removeCurrentDebugInterpreter(fromStep);
682       currThreadResumed();
683     }
684     catch(DebugException e) { throw new UnexpectedException(e); }
685
686     // Must resume the correct number of times
687
for (int i=suspendCount; i>0; i--) thread.resume();
688
689     // Notify listeners of a resume
690

691     // Switch to next suspended thread, if any
692
if (! fromStep && ! _suspendedThreads.isEmpty()) _switchToSuspendedThread();
693   }
694
695   /**
696    * Steps the currently suspended thread.
697    * @param flag The flag denotes what kind of step to take.
698    * The following are the valid options:
699    * StepRequest.STEP_INTO, StepRequest.STEP_OVER, StepRequest.STEP_OUT
700    */

701   public synchronized void step(int flag) throws DebugException {
702     _ensureReady();
703     _stepHelper(flag, true);
704   }
705
706   /** Performs a step in the currently suspended thread, only generating a step event if shouldNotify if true. Assumes
707    * that lock is already held.
708    * @param flag The type of step to perform (see step())
709    * @param shouldNotify Whether to generate a step event
710    */

711   private void _stepHelper(int flag, boolean shouldNotify) throws DebugException {
712     if (_suspendedThreads.size() <= 0 || _runningThread != null) {
713       throw new IllegalStateException JavaDoc("Cannot step if the current thread is not suspended.");
714     }
715
716     _log.log(this + "is About to peek ...");
717
718     ThreadReference thread = _suspendedThreads.peek();
719     _log.log(this + "is Stepping " + thread.toString());
720
721     // Copy the variables back into the thread from the appropriate interpreter.
722
// We do this before stepping since DrJava will hang if you try to copy back
723
// variables after creating the step request.
724
_runningThread = thread;
725     _copyVariablesFromInterpreter();
726
727     _log.log(this + " is Deleting pending requests ...");
728
729     // If there's already a step request for the current thread, delete
730
// it first
731
List JavaDoc<StepRequest> steps = _eventManager.stepRequests(); // Added parameterization <StepRequest>. JDK 1.5 will eliminate this type warning
732
for (int i = 0; i < steps.size(); i++) {
733       StepRequest step = steps.get(i);
734       if (step.thread().equals(thread)) {
735         _eventManager.deleteEventRequest(step);
736         break;
737       }
738     }
739
740     _log.log(this + " Issued step request");
741     //Step step =
742
new Step(this, StepRequest.STEP_LINE, flag);
743     if (shouldNotify) notifyStepRequested();
744     _log.log(this + " About to resume");
745     _resumeFromStep();
746   }
747
748
749   /**
750    * Adds a watch on the given field or variable.
751    * @param field the name of the field we will watch
752    */

753   public synchronized void addWatch(String JavaDoc field) throws DebugException {
754     // _ensureReady();
755

756     final DebugWatchData w = new DebugWatchData(field);
757     _watches.add(w);
758     _updateWatches();
759     
760     Utilities.invokeLater(new Runnable JavaDoc() { public void run() { _notifier.watchSet(w); } });
761   }
762
763   /**
764    * Removes any watches on the given field or variable.
765    * Has no effect if the given field is not being watched.
766    * @param field the name of the field we will watch
767    */

768   public synchronized void removeWatch(String JavaDoc field) throws DebugException {
769     // _ensureReady();
770

771     for (int i=0; i < _watches.size(); i++) {
772       final DebugWatchData watch = _watches.get(i);
773       if (watch.getName().equals(field)) {
774         _watches.remove(i);
775         Utilities.invokeLater(new Runnable JavaDoc() { public void run() { _notifier.watchRemoved(watch); } });
776       }
777     }
778   }
779
780   /**
781    * Removes the watch at the given index.
782    * @param index Index of the watch to remove
783    */

784   public synchronized void removeWatch(int index) throws DebugException {
785     // _ensureReady();
786

787     if (index < _watches.size()) {
788       final DebugWatchData watch = _watches.get(index);
789       _watches.remove(index);
790       Utilities.invokeLater(new Runnable JavaDoc() { public void run() { _notifier.watchRemoved(watch); } });
791     }
792   }
793
794   /**
795    * Removes all watches on existing fields and variables.
796    */

797   public synchronized void removeAllWatches() throws DebugException {
798     // _ensureReady();
799

800     while (_watches.size() > 0) {
801       removeWatch( _watches.get(0).getName());
802     }
803   }
804
805   /**
806    * Enable or disable the specified breakpoint.
807    * @param breakpoint breakpoint to change
808    */

809   public synchronized void notifyBreakpointChange(Breakpoint breakpoint) {
810     _model.getBreakpointManager().changeRegion(breakpoint, new Lambda<Object JavaDoc, Breakpoint>() {
811       public Object JavaDoc apply(Breakpoint bp) {
812         // change has already been made, just notify all listeners
813
return null;
814       }
815     });
816   }
817   
818   /** Toggles whether a breakpoint is set at the given line in the given document.
819    * @param doc Document in which to set or remove the breakpoint
820    * @param offset Start offset on the line to set the breakpoint
821    * @param lineNum Line on which to set or remove the breakpoint
822    * @param isEnabled {@code true} if this breakpoint should be enabled
823    */

824   public synchronized void toggleBreakpoint(OpenDefinitionsDocument doc, int offset, int lineNum, boolean isEnabled)
825     throws DebugException {
826     
827     Breakpoint breakpoint = _model.getBreakpointManager().getRegionAt(doc, offset);
828     
829     if (breakpoint == null) {
830       if (doc.getLineStartPos(offset) == doc.getLineEndPos(offset)) {
831         Utilities.show("Cannot set a breakpoint on an empty line.");
832       }
833       else {
834         try { setBreakpoint(new Breakpoint (doc, offset, lineNum, isEnabled, this)); }
835         catch(LineNotExecutableException lnee) { Utilities.show(lnee.getMessage()); }
836       }
837     }
838     else _model.getBreakpointManager().removeRegion(breakpoint);
839   }
840
841   /** Sets a breakpoint.
842    * @param breakpoint The new breakpoint to set
843    */

844   public synchronized void setBreakpoint(final Breakpoint breakpoint) throws DebugException {
845     breakpoint.getDocument().checkIfClassFileInSync();
846
847     _model.getBreakpointManager().addRegion(breakpoint);
848   }
849
850  /** Removes a breakpoint. Called from toggleBreakpoint -- even with BPs that are not active.
851   * @param breakpoint The breakpoint to remove.
852   */

853   public synchronized void removeBreakpoint(final Breakpoint breakpoint) throws DebugException {
854     Vector JavaDoc<BreakpointRequest> requests = breakpoint.getRequests();
855     if (requests.size() > 0 && _eventManager != null) {
856       // Remove all event requests for this breakpoint
857
try {
858         for (int i=0; i < requests.size(); i++) {
859           _eventManager.deleteEventRequest(requests.get(i));
860         }
861       }
862       catch (VMMismatchException vme) {
863         // Not associated with this VM; probably from a previous session.
864
// Ignore and make sure it gets removed from the document.
865
_log("VMMismatch when removing breakpoint.", vme);
866       }
867       catch (VMDisconnectedException vmde) {
868         // The VM has already disconnected for some reason
869
// Ignore it and make sure the breakpoint gets removed from the document
870
_log("VMDisconnected when removing breakpoint.", vmde);
871       }
872     }
873
874     // Always remove from pending request, since it's always there
875
_pendingRequestManager.removePendingRequest(breakpoint);
876   }
877
878   /** Called when a breakpoint is reached. The Breakpoint object itself should be stored in the
879    * "debugAction" property on the request.
880    * @param request The BreakPointRequest reached by the debugger
881    */

882   synchronized void reachedBreakpoint(BreakpointRequest request) {
883 // Utilities.showDebug("JPDADebugger.reachedBreakPoint(" + request + ") called");
884
Object JavaDoc property = request.getProperty("debugAction");
885     if ( (property != null) && (property instanceof Breakpoint) ) {
886       final Breakpoint breakpoint = (Breakpoint) property;
887       printMessage("Breakpoint hit in class " + breakpoint.getClassName() + " [line " + breakpoint.getLineNumber() + "]");
888
889       Utilities.invokeLater(new Runnable JavaDoc() { public void run() { _notifier.breakpointReached(breakpoint); } });
890     }
891     else {
892       // A breakpoint we didn't set??
893
_log("Reached a breakpoint without a debugAction property: " + request);
894     }
895   }
896
897   /** Returns all currently watched fields and variables. */
898   public synchronized Vector JavaDoc<DebugWatchData> getWatches() throws DebugException {
899     //_ensureReady();
900
return _watches;
901   }
902
903   /**
904    * Returns a list of all threads being tracked by the debugger.
905    * Does not return any threads known to be dead.
906    */

907   public synchronized Vector JavaDoc<DebugThreadData> getCurrentThreadData() throws DebugException {
908     if (! isReady()) { return new Vector JavaDoc<DebugThreadData>(); }
909     List JavaDoc<ThreadReference> listThreads; // Add parameterization <ThreadReference> to listThreads.
910
try {
911       listThreads = _vm.allThreads(); // JDK 1.5 will eliminate this type warning
912
}
913     catch (VMDisconnectedException vmde) {
914       // We're quitting, just pass back an empty Vector
915
return new Vector JavaDoc<DebugThreadData>();
916     }
917
918     // get an iterator from the list returned by _vm.allThreads()
919
Iterator JavaDoc<ThreadReference> iter = listThreads.iterator(); // Added parameterization <ThreadReference>.
920
Vector JavaDoc<DebugThreadData> threads = new Vector JavaDoc<DebugThreadData>();
921     while (iter.hasNext()) {
922       try {
923         threads.add(new DebugThreadData(iter.next()));
924       }
925       catch (ObjectCollectedException e) {
926         // this thread just died, we don't want to list it anyway
927
}
928     }
929     return threads;
930   }
931
932   /**
933    * Returns a Vector of DebugStackData for the current suspended thread.
934    * @throws DebugException if the current thread is running or there
935    * are no suspended threads
936    * TO DO: Config option for hiding DrJava subset of stack trace
937    */

938   public synchronized Vector JavaDoc<DebugStackData> getCurrentStackFrameData()
939     throws DebugException
940   {
941     if (! isReady()) return new Vector JavaDoc<DebugStackData>();
942
943     if (_runningThread != null || _suspendedThreads.size() <= 0) {
944       throw new DebugException("No suspended thread to obtain stack frames.");
945     }
946
947     try {
948       ThreadReference thread = _suspendedThreads.peek();
949       Iterator JavaDoc<StackFrame> iter = thread.frames().iterator(); // Added <StackFrame> parameterization; warning will go away in JDK 1.5
950
Vector JavaDoc<DebugStackData> frames = new Vector JavaDoc<DebugStackData>();
951       while (iter.hasNext()) {
952         frames.add(new DebugStackData(iter.next()));
953       }
954       return frames;
955     }
956     catch (IncompatibleThreadStateException itse) {
957       _log("Unable to obtain stack frame.", itse);
958       return new Vector JavaDoc<DebugStackData>();
959     }
960     catch (VMDisconnectedException vmde) {
961       _log("VMDisconnected when getting the current stack frame data.", vmde);
962       return new Vector JavaDoc<DebugStackData>();
963     }
964     catch (InvalidStackFrameException isfe) {
965       _log("The stack frame requested is invalid.", isfe);
966       return new Vector JavaDoc<DebugStackData>();
967     }
968   }
969
970   /** Takes the location of event e, opens the document corresponding to its class
971    * and centers the definition pane's view on the appropriate line number
972    * @param e LocatableEvent containing location to display
973    */

974   synchronized void scrollToSource(LocatableEvent e) {
975     Location location = e.location();
976
977     // First see if doc is stored
978
EventRequest request = e.request();
979     Object JavaDoc docProp = request.getProperty("document");
980     if ((docProp != null) && (docProp instanceof OpenDefinitionsDocument)) {
981       openAndScroll((OpenDefinitionsDocument) docProp, location, true);
982     }
983     else scrollToSource(location);
984   }
985
986   /** Scroll to the location specified by location */
987   synchronized void scrollToSource(Location location) {
988     scrollToSource(location, true);
989   }
990
991   /** Scroll to the location specified by location */
992   synchronized void scrollToSource(Location location, boolean shouldHighlight) {
993     OpenDefinitionsDocument doc = null;
994
995     // No stored doc, look on the source root set (later, also the sourcepath)
996
ReferenceType rt = location.declaringType();
997     String JavaDoc fileName;
998     try { fileName = getPackageDir(rt.name()) + rt.sourceName(); }
999     catch (AbsentInformationException aie) {
1000      // Don't know real source name:
1001
// assume source name is same as file name
1002
String JavaDoc className = rt.name();
1003      String JavaDoc ps = System.getProperty("file.separator");
1004      // replace periods with the System's file separator
1005
className = StringOps.replace(className, ".", ps);
1006
1007      // crop off the $ if there is one and anything after it
1008
int indexOfDollar = className.indexOf('$');
1009      if (indexOfDollar > -1) {
1010        className = className.substring(0, indexOfDollar);
1011      }
1012
1013      fileName = className + ".java";
1014    }
1015
1016    // Check source root set (open files)
1017
File f = _model.getSourceFile(fileName);
1018// File[] sourceRoots = _model.getSourceRootSet();
1019
// Vector<File> roots = new Vector<File>();
1020
// for (int i=0; i < sourceRoots.length; i++) {
1021
// roots.add(sourceRoots[i]);
1022
// }
1023
// File f = _model.getSourceFileFromPaths(filename, roots);
1024
// if (f == null) {
1025
// Vector<File> sourcepath =
1026
// DrJava.getConfig().getSetting(OptionConstants.DEBUG_SOURCEPATH);
1027
// f = _model.getSourceFileFromPaths(filename, sourcepath);
1028
// }
1029

1030    if (f != null) {
1031      // Get a document for this file, forcing it to open
1032
try { doc = _model.getDocumentForFile(f); }
1033      catch (IOException ioe) {
1034        // No doc, so don't notify listener
1035
}
1036    }
1037
1038    openAndScroll(doc, location, shouldHighlight);
1039  }
1040
1041  /**
1042   * Scrolls to the source location specified by the the debug stack data.
1043   * @param stackData Stack data containing location to display
1044   * @throws DebugException if current thread is not suspended
1045   */

1046  public synchronized void scrollToSource(DebugStackData stackData) throws DebugException {
1047    _ensureReady();
1048    if (_runningThread != null) {
1049      throw new DebugException("Cannot scroll to source unless thread is suspended.");
1050    }
1051
1052    ThreadReference threadRef = _suspendedThreads.peek();
1053    Iterator JavaDoc<StackFrame> i;
1054
1055    try {
1056      if (threadRef.frameCount() <= 0 ) {
1057        printMessage("Could not scroll to source. The current thread had no stack frames.");
1058        return;
1059      }
1060      i = threadRef.frames().iterator(); // JDK 1.5 will eliminate this warning
1061
}
1062    catch (IncompatibleThreadStateException e) {
1063      throw new DebugException("Unable to find stack frames: " + e);
1064    }
1065
1066    while (i.hasNext()) {
1067      StackFrame frame = i.next();
1068
1069      if (frame.location().lineNumber() == stackData.getLine() &&
1070          stackData.getMethod().equals(frame.location().declaringType().name() + "." +
1071                                       frame.location().method().name()))
1072      {
1073        scrollToSource(frame.location(), false);
1074      }
1075    }
1076  }
1077
1078  /** Scrolls to the source of the given breakpoint.
1079   * @param bp the breakpoint
1080   */

1081  public synchronized void scrollToSource(Breakpoint bp) {
1082    openAndScroll(bp.getDocument(), bp.getLineNumber(), bp.getClassName(), false);
1083  }
1084
1085  /**
1086   * Gets the Breakpoint object at the specified line in the given class.
1087   * If the given data do not correspond to an actual breakpoint, null is returned.
1088   * @param line the line number of the breakpoint
1089   * @param className the name of the class the breakpoint's in
1090   * @return the Breakpoint corresponding to the line and className, or null if
1091   * there is no such breakpoint.
1092   */

1093  public synchronized Breakpoint getBreakpoint(int line, String JavaDoc className) {
1094    for (int i = 0; i < _model.getBreakpointManager().getRegions().size(); i++) {
1095      Breakpoint bp = _model.getBreakpointManager().getRegions().get(i);
1096      if ((bp.getLineNumber() == line) && (bp.getClassName().equals(className))) {
1097        return bp;
1098      }
1099    }
1100    // bp not found in the list of breakpoints
1101
return null;
1102  }
1103
1104  /**
1105   * Opens a document and scrolls to the appropriate location. If
1106   * doc is null, a message is printed indicating the source file
1107   * could not be found.
1108   * @param doc Document to open
1109   * @param location Location to display
1110   */

1111  synchronized void openAndScroll(OpenDefinitionsDocument doc, Location location, boolean shouldHighlight) {
1112    openAndScroll(doc, location.lineNumber(), location.declaringType().name(), shouldHighlight);
1113  }
1114
1115  /** Opens a document and scrolls to the appropriate location. If doc is null, a message is printed indicating the
1116   * source file could not be found.
1117   * @param doc Document to open
1118   * @param line the line number to display
1119   * @param className the name of the appropriate class
1120   */

1121  synchronized void openAndScroll(final OpenDefinitionsDocument doc, final int line, String JavaDoc className, final boolean shouldHighlight) {
1122    // Open and scroll if doc was found
1123
if (doc != null) {
1124      doc.checkIfClassFileInSync();
1125      // change UI if in sync in MainFrame listener
1126

1127      Utilities.invokeLater(new Runnable JavaDoc() { public void run() { _notifier.threadLocationUpdated(doc, line, shouldHighlight); } });
1128    }
1129    else printMessage(" (Source for " + className + " not found.)");
1130  }
1131
1132  /**
1133   * Returns the relative directory (from the source root) that the source
1134   * file with this qualifed name will be in, given its package.
1135   * Returns the empty string for classes without packages.
1136   *
1137   * TO DO: Move this to a static utility class
1138   * @param className The fully qualified class name
1139   */

1140  String JavaDoc getPackageDir(String JavaDoc className) {
1141    // Only keep up to the last dot
1142
int lastDotIndex = className.lastIndexOf(".");
1143    if (lastDotIndex == -1) {
1144      // No dots, so no package
1145
return "";
1146    }
1147    else {
1148      String JavaDoc packageName = className.substring(0, lastDotIndex);
1149      // replace periods with the System's file separator
1150
String JavaDoc ps = System.getProperty("file.separator");
1151      packageName = StringOps.replace(packageName, ".", ps);
1152      return packageName + ps;
1153    }
1154  }
1155
1156  /**
1157   * Prints a message in the Interactions Pane.
1158   * @param message Message to display
1159   */

1160  synchronized void printMessage(String JavaDoc message) {
1161    _model.printDebugMessage(message);
1162  }
1163
1164  /**
1165   * Returns whether the given className corresponds to a class
1166   * that is anonymous or has an anonymous enclosing class.
1167   * @param rt the ReferenceType to check
1168   * @return whether the class is anonymous
1169   */

1170  private boolean hasAnonymous(ReferenceType rt) {
1171    String JavaDoc className = rt.name();
1172    StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(className, "$");
1173    while (st.hasMoreElements()) {
1174      String JavaDoc currToken = st.nextToken();
1175      try {
1176        Integer JavaDoc anonymousNum = Integer.valueOf(currToken);
1177        return true;
1178      }
1179      catch(NumberFormatException JavaDoc nfe) {
1180        // flow through to false if token cannot be parsed into an int
1181
}
1182    }
1183    return false;
1184  }
1185
1186  private boolean _getWatchFromInterpreter(DebugWatchData currWatch) {
1187    String JavaDoc currName = currWatch.getName();
1188    // get the value and type from the interactions model
1189
String JavaDoc value = _model.getInteractionsModel().getVariableToString(currName);
1190    if (value != null) {
1191      String JavaDoc type = _model.getInteractionsModel().getVariableClassName(currName);
1192      currWatch.setValue(value);
1193      currWatch.setType(type);
1194      return true;
1195    }
1196    else {
1197      return false;
1198    }
1199  }
1200
1201  /**
1202   * Hides all of the values of the watches and their types. Called
1203   * when there is no debug information.
1204   */

1205  private synchronized void _hideWatches() {
1206    for (int i = 0; i < _watches.size(); i++) {
1207      DebugWatchData currWatch = _watches.get(i);
1208      currWatch.hideValueAndType();
1209    }
1210  }
1211
1212  /** Updates the stored value of each watched field and variable. */
1213  private synchronized void _updateWatches() throws DebugException {
1214    if (! isReady()) return;
1215      
1216    if (_suspendedThreads.size() <= 0) {
1217      // Not suspended, get values in interpreter
1218
for (int i = 0; i < _watches.size(); i++) {
1219        DebugWatchData currWatch = _watches.get(i);
1220        if (!_getWatchFromInterpreter(currWatch)) currWatch.hideValueAndType();
1221      }
1222      return;
1223// for (int i = 0; i < _watches.size(); i++) {
1224
// DebugWatchData currWatch = _watches.get(i);
1225
// currWatch.hideValueAndType();
1226
// }
1227
// return;
1228
}
1229
1230    try {
1231      StackFrame currFrame;
1232      List JavaDoc<StackFrame> frames;
1233      ThreadReference thread = _suspendedThreads.peek();
1234      if (thread.frameCount() <= 0 ) {
1235        printMessage("Could not update watch values. The current thread had no stack frames.");
1236        return;
1237      }
1238      frames = thread.frames(); // JDK 1.5 will eliminate this warning
1239
currFrame = frames.get(0);
1240      Location location = currFrame.location();
1241
1242      ReferenceType rt = location.declaringType();
1243      ObjectReference obj = currFrame.thisObject();
1244      // note: obj is null if we're in a static context
1245

1246      // Get the name to determine how many $'s there are
1247
String JavaDoc rtName = rt.name();
1248      int numDollars = 0;
1249      int dollarIndex = rtName.indexOf("$", 0);
1250      while (dollarIndex != -1) {
1251        numDollars++;
1252        dollarIndex = rtName.indexOf("$", dollarIndex+1);
1253      }
1254
1255      for (int i = 0; i < _watches.size(); i++) {
1256        DebugWatchData currWatch = _watches.get(i);
1257        String JavaDoc currName = currWatch.getName();
1258        if (_getWatchFromInterpreter(currWatch)) {
1259          continue;
1260        }
1261// // check for "this"
1262
// if (currName.equals("this")) {
1263
// if (obj != null) {
1264
// currWatch.setValue(_getValue(obj));
1265
// currWatch.setType(String.valueOf(obj.type()));
1266
// }
1267
// else {
1268
// // "this" is not defined in a static context
1269
// currWatch.setNoValue();
1270
// currWatch.setNoType();
1271
// }
1272
// continue;
1273
// }
1274

1275// // Look for a variable with this name
1276
// LocalVariable localVar = null;
1277
// try {
1278
// frames = thread.frames();
1279
// currFrame = (StackFrame) frames.get(0);
1280
// localVar = currFrame.visibleVariableByName(currName);
1281
// }
1282
// catch (AbsentInformationException aie) {
1283
// // Not compiled with debug flag.... ignore
1284
// }
1285
// catch (InvalidStackFrameException isfe) {
1286
// currWatch.setNoValue();
1287
// currWatch.setNoType();
1288
// _log("Failed to get local var from stack frame", isfe);
1289
// continue;
1290
// }
1291
//
1292
// if (localVar != null) {
1293
// // currWatch.setValue(_getValue(currFrame.getValue(localVar)));
1294
// try {
1295
// Value v = _getValueOfLocalVariable(localVar, thread);
1296
// if (v == null) {
1297
// currWatch.setValue(_getValue(null));
1298
// try {
1299
// currWatch.setType(localVar.type().name());
1300
// }
1301
// catch (ClassNotLoadedException cnle) {
1302
// List classes = _vm.classesByName(localVar.typeName());
1303
// if (!classes.isEmpty()) {
1304
// currWatch.setType(((Type)classes.get(0)).name());
1305
// }
1306
// else {
1307
// currWatch.setTypeNotLoaded();
1308
// }
1309
// }
1310
// }
1311
// else {
1312
// currWatch.setValue(_getValue(v));
1313
// currWatch.setType(v.type().name());
1314
// }
1315
// }
1316
// catch (Exception ex) {
1317
// _log("Exception when getting the value of a local variable", ex);
1318
// currWatch.setNoValue();
1319
// currWatch.setNoType();
1320
// }
1321
// }
1322
// if the variable being watched is not a local variable,
1323
// check if it's a field
1324
ReferenceType outerRt = rt;
1325        ObjectReference outer = obj; // (null if static context)
1326
Field field = outerRt.fieldByName(currName);
1327
1328        if (obj != null) {
1329          // We're not in a static context
1330

1331          // If we don't find it in this class, loop through any enclosing
1332
// classes. Start at this$N, where N is the number of dollar signs in
1333
// the reference type's name, minus one.
1334
int outerIndex = numDollars - 1;
1335          if (hasAnonymous(outerRt)) {
1336            // We don't know the appropriate this$N to look for so we have to
1337
// search for a field that begins with this$.
1338
List JavaDoc<Field> fields = outerRt.allFields(); // This type warning will go away in JDK 1.5
1339
Iterator JavaDoc<Field> iter = fields.iterator();
1340            while (iter.hasNext()) {
1341              Field f = iter.next();
1342              String JavaDoc name = f.name();
1343              if (name.startsWith("this$")) {
1344                int lastIndex = name.lastIndexOf("$");
1345                outerIndex = Integer.valueOf(name.substring(lastIndex+1, name.length())).intValue();
1346                break;
1347              }
1348            }
1349          }
1350          Field outerThis = outerRt.fieldByName("this$" + outerIndex);
1351          if (field == null) {
1352            // Try concatenating "val$" to the beginning of the field in
1353
// case it's a final local variable of the outer class
1354
field = outerRt.fieldByName("val$" + currName);
1355          }
1356
1357          while ((field == null) && (outerThis != null)) {
1358            outer = (ObjectReference) outer.getValue(outerThis);
1359            if (outer == null) {
1360              // We're probably in the constructor and this$N has
1361
// not yet been initialized. We can't do anything, so just
1362
// break display no value.
1363
break;
1364            }
1365            outerRt = outer.referenceType();
1366            field = outerRt.fieldByName(currName);
1367
1368            if (field == null) {
1369              // Try concatenating "val$" to the beginning of the field in
1370
// case it's a final local variable of the outer class
1371
field = outerRt.fieldByName("val$" + currName);
1372
1373              if (field == null) {
1374                // Enter the loop again with the next outer enclosing class
1375
outerIndex--;
1376                outerThis = outerRt.fieldByName("this$" + outerIndex);
1377              }
1378            }
1379          }
1380        }
1381        else {
1382          // We're in a static context
1383

1384          // If we don't find it in this class, loop through any enclosing
1385
// classes. Do this by loading any outer classes by invoking the
1386
// method on the class loader that loaded this class and passing
1387
// it the class name with the last class removed each time.
1388
String JavaDoc rtClassName = outerRt.name();
1389          int index = rtClassName.lastIndexOf("$");
1390          while ((field == null) && (index != -1)) {
1391            rtClassName = rtClassName.substring(0, index);
1392            List JavaDoc<ReferenceType> l = _vm.classesByName(rtClassName); // JDK 1.5 will eliminate this warning
1393
if (l.isEmpty()) {
1394              // field is null, we will end up setting
1395
// the value to no value
1396
break;
1397            }
1398            outerRt = l.get(0);
1399            field = outerRt.fieldByName(currName);
1400
1401            if (field == null) {
1402              // Enter the loop again with the next outer enclosing class
1403
index = rtClassName.lastIndexOf("$");
1404            }
1405          }
1406        }
1407
1408        // Try to set the value and type of the field.
1409
// If the field is not static and we are in a static context
1410
// (outer==null), we have to setNoValue.
1411
if ((field != null) &&
1412            (field.isStatic() || (outer != null))) {
1413          Value v = (field.isStatic()) ?
1414            outerRt.getValue(field) :
1415            outer.getValue(field);
1416          currWatch.setValue(_getValue(v));
1417          try {
1418            currWatch.setType(field.type().name());
1419          }
1420          catch (ClassNotLoadedException cnle) {
1421            List JavaDoc<ReferenceType> classes = _vm.classesByName(field.typeName()); // JDK 1.5 will eliminate this warning
1422
if (!classes.isEmpty()) {
1423              currWatch.setType(classes.get(0).name());
1424            }
1425            else {
1426              currWatch.setTypeNotLoaded();
1427            }
1428          }
1429        }
1430        else {
1431          currWatch.setNoValue();
1432          currWatch.setNoType();
1433        }
1434
1435      }
1436    }
1437    catch (IncompatibleThreadStateException itse) {
1438      _log("Exception updating watches.", itse);
1439    }
1440    catch (InvalidStackFrameException isfe) {
1441      _log("Exception updating watches.", isfe);
1442    }
1443    catch (VMDisconnectedException vmde) {
1444      _log("Exception updating watches.", vmde);
1445      shutdown();
1446    }
1447  }
1448
1449  /**
1450   * Returns a string representation of the given Value from JDI.
1451   * @param value the Value of interest
1452   * @return the String representation of the Value
1453   */

1454  private String JavaDoc _getValue(Value value) throws DebugException {
1455    // Most types work as they are; for the rest, for now, only care about getting
1456
// accurate toString for Objects
1457
if (value == null) {
1458      return "null";
1459    }
1460
1461    if (!(value instanceof ObjectReference)) {
1462      return value.toString();
1463    }
1464    ObjectReference object = (ObjectReference) value;
1465    ReferenceType rt = object.referenceType();
1466    ThreadReference thread = _suspendedThreads.peek();
1467    List JavaDoc<Method> toStrings = rt.methodsByName("toString"); // JDK 1.5 will eliminate this warning
1468
if (toStrings.size() == 0) {
1469      // not sure how an Object can't have a toString method, but it happens
1470
return value.toString();
1471    }
1472    // Assume that there's only one method named toString
1473
Method method = toStrings.get(0);
1474    try {
1475      Value stringValue =
1476        object.invokeMethod(thread, method, new LinkedList JavaDoc<Value>(), ObjectReference.INVOKE_SINGLE_THREADED);
1477      if (stringValue == null) return "null";
1478      return stringValue.toString();
1479    }
1480    catch (InvalidTypeException ite) {
1481      // shouldn't happen, not passing any arguments to toString()
1482
throw new UnexpectedException(ite);
1483    }
1484    catch (ClassNotLoadedException cnle) {
1485      // once again, no arguments
1486
throw new UnexpectedException(cnle);
1487    }
1488    catch (IncompatibleThreadStateException itse) {
1489      throw new DebugException("Cannot determine value from thread: " + itse);
1490    }
1491    catch (InvocationException ie) {
1492      throw new DebugException("Could not invoke toString: " + ie);
1493    }
1494  }
1495
1496  /** @return the appropriate Method to call in the InterpreterJVM in order to define a variable of the type val. */
1497  private Method _getDefineVariableMethod(ReferenceType interpreterRef, Value val) throws DebugException {
1498    List JavaDoc<Method> methods;
1499    String JavaDoc signature_beginning = "(Ljava/lang/String;";
1500    String JavaDoc signature_end = ")V";
1501    String JavaDoc signature_mid;
1502    String JavaDoc signature;
1503
1504    if ((val == null) || ( val instanceof ObjectReference )) {
1505      signature_mid = "Ljava/lang/Object;Ljava/lang/Class;";
1506    }
1507    else if ( val instanceof BooleanValue ) {
1508      signature_mid = "Z";
1509    }
1510    else if ( val instanceof ByteValue ) {
1511      signature_mid = "B";
1512    }
1513    else if ( val instanceof CharValue ) {
1514      signature_mid = "C";
1515    }
1516    else if ( val instanceof DoubleValue ) {
1517      signature_mid = "D";
1518    }
1519    else if ( val instanceof FloatValue ) {
1520      signature_mid = "F";
1521    }
1522    else if ( val instanceof IntegerValue ) {
1523      signature_mid = "I";
1524    }
1525    else if ( val instanceof LongValue ) {
1526      signature_mid = "J";
1527    }
1528    else if ( val instanceof ShortValue ) {
1529      signature_mid = "S";
1530    }
1531    else{
1532      throw new IllegalArgumentException JavaDoc("Tried to define a variable which is\n" +
1533                                         "not an Object or a primitive type:\n" +
1534                                         val);
1535    }
1536
1537    signature = signature_beginning + signature_mid + signature_end;
1538    methods = interpreterRef.methodsByName("defineVariable", signature); // JDK 1.5 will eliminate this warning
1539
if (methods.size() <= 0) {
1540      throw new DebugException("Could not find defineVariable method.");
1541    }
1542
1543    // Make sure we have a concrete method
1544
Method tempMethod = methods.get(0);
1545    for (int i = 1; i < methods.size() && tempMethod.isAbstract(); i++) {
1546      tempMethod = methods.get(i);
1547    }
1548    if (tempMethod.isAbstract()) {
1549      throw new DebugException("Could not find concrete defineVariable method.");
1550    }
1551
1552    return tempMethod;
1553  }
1554
1555  /** Assumes that this method is only called immedeately after suspending a thread. */
1556  private ObjectReference _getDebugInterpreter() throws InvalidTypeException, ClassNotLoadedException,
1557    IncompatibleThreadStateException, InvocationException, DebugException {
1558    
1559    ThreadReference threadRef = _suspendedThreads.peek();
1560    String JavaDoc interpreterName = _getUniqueThreadName(threadRef);
1561    return _getDebugInterpreter(interpreterName, threadRef);
1562  }
1563
1564  /** Gets the debug interpreter with the given name using the given suspended thread to invoke methods.
1565   * @param interpreterName Name of the interpreter in the InterpreterJVM
1566   * @param threadRef Suspended thread to use for invoking methods
1567   * @throws IllegalStateException if threadRef is not suspended
1568   */

1569  private ObjectReference _getDebugInterpreter(String JavaDoc interpreterName, ThreadReference threadRef) throws
1570    InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException,
1571    DebugException {
1572    
1573    if (!threadRef.isSuspended()) {
1574      throw new IllegalStateException JavaDoc("threadRef must be suspended to get a debug interpreter.");
1575    }
1576
1577    // Get the method to return the interpreter
1578
Method m = _getMethod(_interpreterJVM.referenceType(), "getJavaInterpreter");
1579
1580    // invokeMethod would throw an ObjectCollectedException if the StringReference
1581
// declared by _vm.mirrorOf(name) had been garbage collected before
1582
// invokeMethod could execute. We now just disable collection until after the
1583
// method is invoked.
1584

1585    int tries = 0;
1586    StringReference sr = null;
1587    while (tries < OBJECT_COLLECTED_TRIES) {
1588      try{
1589        LinkedList JavaDoc<StringReference> args = new LinkedList JavaDoc<StringReference>(); //Added parameterization <StringReference>.
1590
sr = _vm.mirrorOf(interpreterName);
1591        sr.disableCollection();
1592        args.add(sr); // make the String a JDI Value
1593
_log.log("Invoking " + m.toString() + " on " + args.toString());
1594        _log.log("Thread is " + threadRef.toString() + " <suspended = " + threadRef.isSuspended() + ">");
1595
1596        ObjectReference tmpInterpreter =
1597          (ObjectReference) _interpreterJVM.invokeMethod(threadRef, m, args, ObjectReference.INVOKE_SINGLE_THREADED);
1598
1599        _log.log("Returning...");
1600        return tmpInterpreter;
1601      }
1602      catch (ObjectCollectedException e) { tries++; }
1603      finally { sr.enableCollection(); }
1604    }
1605    throw new DebugException("The debugInterpreter: " + interpreterName + " could not be obtained from interpreterJVM");
1606  }
1607
1608  /**
1609   * Notifies the debugger that an assignment has been made in
1610   * the given debug interpreter.
1611   *
1612   * Not currently used.
1613   *
1614   * @param name the name of the interpreter
1615   *
1616  public void notifyDebugInterpreterAssignment(String name) {
1617    //System.out.println("notifyDebugInterpreterAssignment(" + name + ")");
1618  }*/

1619
1620  /**
1621   * Copy the current selected thread's visible variables (those in scope) into
1622   * an interpreter's environment and then switch the Interactions window's
1623   * interpreter to that interpreter.
1624   */

1625  private void _dumpVariablesIntoInterpreterAndSwitch() throws DebugException, AbsentInformationException {
1626    _log.log(this + " invoked dumpVariablesIntoInterpreterAndSwitch");
1627    try {
1628      ThreadReference suspendedThreadRef = _suspendedThreads.peek();
1629      StackFrame frame = suspendedThreadRef.frame(0);
1630      Location l = frame.location();
1631      ReferenceType rt = l.declaringType();
1632      String JavaDoc className = rt.name();
1633
1634      // Name the new interpreter based on this thread
1635
String JavaDoc interpreterName = _getUniqueThreadName(suspendedThreadRef);
1636      _model.getInteractionsModel().addDebugInterpreter(interpreterName, className);
1637      ObjectReference debugInterpreter = _getDebugInterpreter();
1638      _log.log(this + " executing: frame = suspendedThreadRef.frame(0);");
1639      frame = suspendedThreadRef.frame(0);
1640
1641      List JavaDoc<LocalVariable> vars = frame.visibleVariables(); // JDK 1.5 will eliminate this warning
1642
Iterator JavaDoc<LocalVariable> varsIterator = vars.iterator();
1643
1644      _log.log(this + " got visibleVariables");
1645
1646      // Define each variable
1647
while(varsIterator.hasNext()) {
1648        LocalVariable localVar = varsIterator.next();
1649        _log.log(this + " defined local variable: " + localVar);
1650        // Have to update the frame each time
1651
frame = suspendedThreadRef.frame(0);
1652        Value val = frame.getValue(localVar);
1653        Type type;
1654        if (val != null) {
1655          type = val.type();
1656        }
1657        else {
1658          try {
1659            type = localVar.type();
1660          }
1661          catch(ClassNotLoadedException e) {
1662            List JavaDoc<ReferenceType> classes = _vm.classesByName(localVar.typeName()); //JDK 1.5 will eliminate this warning
1663
if (!classes.isEmpty()) {
1664              type = classes.get(0);
1665            }
1666            else {
1667              type = null;
1668            }
1669          }
1670        }
1671        _defineVariable(suspendedThreadRef, debugInterpreter,
1672                        localVar.name(), val, type);
1673      }
1674
1675      // Update the frame
1676
frame = suspendedThreadRef.frame(0);
1677
1678      // Define "this"
1679
Value thisVal = frame.thisObject();
1680      if (thisVal != null) {
1681        _defineVariable(suspendedThreadRef, debugInterpreter,
1682                        "this", thisVal, thisVal.type());
1683        //_setThisInInterpreter(suspendedThreadRef, debugInterpreter, thisVal);
1684
}
1685
1686      // Set the new interpreter and prompt
1687
String JavaDoc prompt = _getPromptString(suspendedThreadRef);
1688      _log.log(this + " is setting active interpreter");
1689      _model.getInteractionsModel().setActiveInterpreter(interpreterName, prompt);
1690    }
1691    catch(InvalidTypeException exc) {
1692      throw new DebugException(exc.toString());
1693    }
1694    catch(IncompatibleThreadStateException e2) {
1695      throw new DebugException(e2.toString());
1696    }
1697    catch(ClassNotLoadedException e3) {
1698      throw new DebugException(e3.toString());
1699    }
1700    catch(InvocationException e4) {
1701      throw new DebugException(e4.toString());
1702    }
1703  }
1704
1705  /**
1706   * @return the prompt to display in the itneractions console
1707   * based upon the ThreadReference threadRef, which is being debugged.
1708   */

1709  private String JavaDoc _getPromptString(ThreadReference threadRef) {
1710    return "[" + threadRef.name() + "] > ";
1711  }
1712
1713  /**
1714   * Defines a variable with the given name to the given value, using
1715   * a thread reference and JavaInterpreter.
1716   * If type == null, we assume that the type of this variable
1717   * has not been loaded so we will set it to Object in DynamicJavaAdapter.
1718   * @param suspendedThreadRef Thread ref being debugged
1719   * @param debugInterpreter ObjectReference to the JavaInterpreter to contain
1720   * the variable
1721   * @param name Name of the variable
1722   * @param val Value of the variable
1723   */

1724  private void _defineVariable(ThreadReference suspendedThreadRef,
1725                               ObjectReference debugInterpreter,
1726                               String JavaDoc name, Value val, Type type)
1727    throws InvalidTypeException, IncompatibleThreadStateException,
1728      ClassNotLoadedException, InvocationException, DebugException
1729  {
1730    ReferenceType rtDebugInterpreter = debugInterpreter.referenceType();
1731    Method method2Call = _getDefineVariableMethod(rtDebugInterpreter, val);
1732
1733    // invokeMethod would throw an ObjectCollectedException if the StringReference
1734
// declared by _vm.mirrorOf(name) had been garbage collected before
1735
// invokeMethod could execute. We now just disable collection until after the
1736
// method is invoked.
1737

1738    int tries = 0;
1739    StringReference sr = null;
1740    while (tries < OBJECT_COLLECTED_TRIES) {
1741      try {
1742        //Added parameterization <Value>.
1743
List JavaDoc<Value> args = new LinkedList JavaDoc<Value>();
1744        /* Mirror is the common supertype of StringReference, Value, and ReferenceType. Changed from Mirror to Value
1745         * because invokeMethod requires a List of Value type. It does not need to be a Mirror because neither sr nor
1746         * val can be a ReferenceType */

1747        sr = _vm.mirrorOf(name);
1748        sr.disableCollection();
1749        args.add(sr);
1750        args.add(val);
1751        if (type == null) args.add(null);
1752        else if (type instanceof ReferenceType) {
1753          args.add(((ReferenceType)type).classObject());
1754        }
1755
1756        /* System.out.println("Calling " + method2Call.toString() + "with " + args.get(0).toString()); */
1757        debugInterpreter.invokeMethod(suspendedThreadRef, method2Call, args, ObjectReference.INVOKE_SINGLE_THREADED);
1758        return;
1759      }
1760      catch (ObjectCollectedException oce) {
1761        tries++;
1762      }
1763      finally {
1764        sr.enableCollection();
1765      }
1766    }
1767    throw new DebugException("The variable: " + name +
1768                             " could not be defined in the debug interpreter");
1769  }
1770
1771  /** Notifies all listeners that the current thread has been suspended. */
1772  synchronized void currThreadSuspended() {
1773    try {
1774      try {
1775      // copy the variables in scope into an interpreter
1776
// and switch the current interpreter to that interpreter
1777
_dumpVariablesIntoInterpreterAndSwitch();
1778        _switchToSuspendedThread();
1779      }
1780      catch(AbsentInformationException aie) {
1781        // an AbsentInformationException can be thrown if the user does not
1782
// compile the classes to be debugged with the -g flag
1783
printMessage("No debug information available for this class.\nMake sure to compile classes to be debugged with the -g flag.");
1784        _hideWatches();
1785        // don't updateWatches in _switchToSuspendedThread since it will display the default
1786
// interpreter's watch information.
1787
_switchToSuspendedThread(false);
1788      }
1789    }
1790    catch(DebugException de) {
1791      throw new UnexpectedException(de);
1792    }
1793  }
1794
1795  /** Calls the real switchToSuspendedThread, telling it to updateWatches. This is what is usually called. */
1796  private void _switchToSuspendedThread() throws DebugException { _switchToSuspendedThread(true); }
1797
1798  /** Performs the bookkeeping to switch to the suspened thread on the top of the _suspendedThreads stack.
1799   * @param updateWatches this is false if the current file does not have debug information. This prevents the default
1800   * interpreter's watch values from being shown.
1801   */

1802  private void _switchToSuspendedThread(boolean updateWatches) throws DebugException {
1803    _log.log(this + " executing _switchToSuspendedThread()");
1804    _runningThread = null;
1805    if (updateWatches) _updateWatches();
1806    final ThreadReference currThread = _suspendedThreads.peek();
1807    _notifier.currThreadSuspended();
1808    // Anytime a thread is suspended, it becomes the current thread.
1809
// This makes sure the debug panel will correctly put the
1810
// current thread in bold.
1811
_notifier.currThreadSet(new DebugThreadData(currThread));
1812
1813    try {
1814      if (currThread.frameCount() > 0) {
1815        scrollToSource(currThread.frame(0).location());
1816      }
1817    }
1818    catch (IncompatibleThreadStateException itse) {
1819      throw new UnexpectedException(itse);
1820    }
1821  }
1822
1823  /**
1824   * Returns a unique name for the given thread.
1825   */

1826  private String JavaDoc _getUniqueThreadName(ThreadReference thread) {
1827    return Long.toString(thread.uniqueID());
1828  }
1829
1830  /**
1831   * @return the Method corresponding to DynamicJavaAdapter.getVariable()
1832   */

1833  private Method _getGetVariableMethod(ReferenceType rtInterpreter) {
1834    return _getMethod(rtInterpreter, "getVariable");
1835  }
1836
1837  /**
1838   * Returns the concrete method with the given name on the reference type.
1839   * @param rt ReferenceType containing the method
1840   * @param name Name of the method
1841   * @throws NoSuchElementException if no concrete method could be found
1842   */

1843  private Method _getMethod(ReferenceType rt, String JavaDoc name) {
1844    List JavaDoc<Method> methods = rt.methodsByName(name); // JDK 1.5 will eliminate this warning
1845
Iterator JavaDoc<Method> methodsIterator = methods.iterator();
1846
1847    // iterate over all the methods in the list and return the first non-abstract one
1848
while( methodsIterator.hasNext() ) {
1849      Method m = methodsIterator.next();
1850      if ( !m.isAbstract() ) {
1851        return m;
1852      }
1853    }
1854
1855    throw new NoSuchElementException JavaDoc("No non-abstract method called " + name + " found in " + rt.name());
1856  }
1857
1858  /**
1859   * Converts a primitive wrapper object (eg. Integer) to its corresponding
1860   * primitive value (eg. int) by invoking the appropriate method in the
1861   * given thread.
1862   * @param threadRef Thread in which to invoke the method
1863   * @param localVar Variable to convert
1864   * @param v Value of localVar
1865   * @return Converted primitive, or v if it was a reference type
1866   */

1867  private Value _convertToActualType(ThreadReference threadRef, LocalVariable localVar,
1868                                     Value v)
1869    throws InvalidTypeException, ClassNotLoadedException,
1870    IncompatibleThreadStateException, InvocationException
1871  {
1872    String JavaDoc typeSignature;
1873    try {
1874      typeSignature = localVar.type().signature();
1875    }
1876    catch (ClassNotLoadedException cnle) {
1877      return v;
1878    }
1879    Method m;
1880    ObjectReference ref = (ObjectReference)v;
1881    ReferenceType rt = ref.referenceType();
1882
1883    if ( typeSignature.equals("Z") ) {
1884      m = _getMethod(rt, "booleanValue");
1885    }
1886    else if ( typeSignature.equals("B") ) {
1887      m = _getMethod(rt, "byteValue");
1888    }
1889    else if ( typeSignature.equals("C") ) {
1890      m = _getMethod(rt, "charValue");
1891    }
1892    else if ( typeSignature.equals("S") ) {
1893      m = _getMethod(rt, "shortValue");
1894    }
1895    else if ( typeSignature.equals("I") ) {
1896      m = _getMethod(rt, "intValue");
1897    }
1898    else if ( typeSignature.equals("J") ) {
1899      m = _getMethod(rt, "longValue");
1900    }
1901    else if ( typeSignature.equals("F") ) {
1902      m = _getMethod(rt, "floatValue");
1903    }
1904    else if ( typeSignature.equals("D") ) {
1905      m = _getMethod(rt, "doubleValue");
1906    }
1907    else{
1908      return v;
1909    }
1910
1911    return ref.invokeMethod(threadRef, m, new LinkedList JavaDoc<Value>(), ObjectReference.INVOKE_SINGLE_THREADED);
1912  }
1913
1914// private ClassObjectReference _getClassForName(String name, ThreadReference thread, ClassLoaderReference clr)
1915
// throws InvalidTypeException, ClassNotLoadedException, AbsentInformationException,
1916
// IncompatibleThreadStateException, InvocationException, DebugException
1917
// {
1918
// Value v = null;
1919
//
1920
// // invokeMethod would throw an ObjectCollectedException if the StringReference
1921
// // declared by _vm.mirrorOf(name) had been garbage collected before
1922
// // invokeMethod could execute. This happened infrequently so by trying this
1923
// // multiple times, the chance of failure each time should be acceptably low.
1924
// int tries = 0;
1925
// while (tries < MAXINVOKETRIES) {
1926
// try {
1927
// ReferenceType rt = clr.referenceType();
1928
// Method method2Call = _getMethod(rt, "loadClass");
1929
// List args = new LinkedList();
1930
// args.add(_vm.mirrorOf(name));
1931
// args.add(_vm.mirrorOf(true));
1932
// v = clr.invokeMethod(thread, method2Call, args,
1933
// ObjectReference.INVOKE_SINGLE_THREADED);
1934
// break;
1935
// }
1936
// catch (ObjectCollectedException oce) {
1937
// _log.log("Got ObjectCollectedException");
1938
// tries++;
1939
// }
1940
// }
1941
// if (v != null) {
1942
// //v = _convertToActualType(thread, var, v);
1943
// return (ClassObjectReference)v;
1944
// }
1945
//
1946
// return null;
1947
// }
1948

1949  private Value _getValueOfLocalVariable(LocalVariable var, ThreadReference thread)
1950    throws InvalidTypeException, ClassNotLoadedException,
1951      IncompatibleThreadStateException, InvocationException, DebugException
1952  {
1953    ObjectReference interpreter = _getDebugInterpreter(_getUniqueThreadName(thread), thread);
1954    ReferenceType rtInterpreter = interpreter.referenceType();
1955    Method method2Call = _getGetVariableMethod(rtInterpreter);
1956
1957    // invokeMethod would throw an ObjectCollectedException if the StringReference
1958
// declared by _vm.mirrorOf(name) had been garbage collected before
1959
// invokeMethod could execute. We now just disable collection until after the
1960
// method is invoked.
1961

1962    int tries = 0;
1963    StringReference sr = null;
1964    String JavaDoc varName = var.name();
1965    while (tries < OBJECT_COLLECTED_TRIES) {
1966      try {
1967        List JavaDoc<Value> args = new LinkedList JavaDoc<Value>(); //Added parameterization <Value>
1968
sr = _vm.mirrorOf(varName);
1969        sr.disableCollection();
1970        args.add(sr);
1971        Value v = interpreter.invokeMethod(thread, method2Call, args, ObjectReference.INVOKE_SINGLE_THREADED);
1972        if (v != null) v = _convertToActualType(thread, var, v);
1973
1974        return v;
1975      }
1976      catch (ObjectCollectedException oce) { tries++; }
1977      finally { sr.enableCollection(); }
1978    }
1979    throw new DebugException("The value of variable: " + varName +
1980                             " could not be obtained from the debug interpreter");
1981
1982  }
1983
1984  /** Copies the variables in the current interpreter back into the Threaf it refers to. */
1985  private void _copyBack(ThreadReference threadRef) throws IncompatibleThreadStateException, AbsentInformationException,
1986      InvocationException, DebugException {
1987    _log.log("Getting debug interpreter");
1988    _log.log("Getting variables");
1989    StackFrame frame = threadRef.frame(0);
1990    List JavaDoc<LocalVariable> vars = frame.visibleVariables(); // Added <LocalVariable> type argument; warning will go away in JDK 1.5
1991
Iterator JavaDoc<LocalVariable> varsIterator = vars.iterator();
1992
1993    // Get each variable from the stack frame
1994
while(varsIterator.hasNext()) {
1995      _log.log("Iterating through vars");
1996      LocalVariable localVar = varsIterator.next();
1997
1998      try {
1999        Value v = _getValueOfLocalVariable(localVar, threadRef);
2000        frame = threadRef.frame(0);
2001        frame.setValue(localVar, v);
2002      }
2003      catch (ClassNotLoadedException cnle) {
2004        printMessage("Could not update the value of '" + localVar.name() + "' (class not loaded)");
2005      }
2006      catch (InvalidTypeException ite) {
2007        printMessage("Could not update the value of '" + localVar.name() + "' (invalid type exception)");
2008      }
2009    }
2010  }
2011
2012  /** Assumes lock is already held. */
2013  private void _copyVariablesFromInterpreter() throws DebugException {
2014    try {
2015      // copy variables values out of interpreter's environment and
2016
// into the relevant stack frame
2017
_log.log("In _copyBack()");
2018      _copyBack(_runningThread);
2019      _log.log("Out of _copyBack()");
2020    }
2021    catch(AbsentInformationException e2) {
2022      //throw new DebugException(e2.toString());
2023
// Silently fail for now to ignore the AbsentInformationException that
2024
// we should have noticed when first suspending on this line (see currThreadSuspended).
2025
}
2026    catch(IncompatibleThreadStateException e) {
2027      throw new DebugException(e.toString());
2028    }
2029    catch(InvocationException e4) {
2030      throw new DebugException(e4.toString());
2031    }
2032  }
2033
2034  /** Removes all of the debug interpreters as part of shutting down. Assumes lock is already held. */
2035  private void _removeAllDebugInterpreters() {
2036    DefaultInteractionsModel interactionsModel = _model.getInteractionsModel();
2037    String JavaDoc oldInterpreterName;
2038    if (_runningThread != null) {
2039      oldInterpreterName = _getUniqueThreadName(_runningThread);
2040      interactionsModel.removeInterpreter(oldInterpreterName);
2041    }
2042    while (!_suspendedThreads.isEmpty()) {
2043      ThreadReference threadRef = _suspendedThreads.pop();
2044      oldInterpreterName = _getUniqueThreadName(threadRef);
2045      interactionsModel.removeInterpreter(oldInterpreterName);
2046    }
2047  }
2048
2049  /**
2050   * This method is called to remove the current debug interpreter upon resuming
2051   * the current thread.
2052   * @param fromStep If true, switch to the default interpreter since we don't want
2053   * to switch to the next debug interpreter and display its watch data. We would like
2054   * to just not have an active interpreter and put up an hourglass over the
2055   * interactions pane, but the interpreterJVM must have an active interpreter.
2056   */

2057  private void _removeCurrentDebugInterpreter(boolean fromStep) {
2058    DefaultInteractionsModel interactionsModel =
2059      _model.getInteractionsModel();
2060    // switch to next interpreter on the stack
2061
if (fromStep || _suspendedThreads.isEmpty()) {
2062      interactionsModel.setToDefaultInterpreter();
2063    }
2064    else {
2065      ThreadReference threadRef = _suspendedThreads.peek();
2066      _switchToInterpreterForThreadReference(threadRef);
2067    }
2068    String JavaDoc oldInterpreterName = _getUniqueThreadName(_runningThread);
2069    interactionsModel.removeInterpreter(oldInterpreterName);
2070  }
2071
2072  /** Notifies all listeners that the current thread has been resumed.
2073   * Precondition: Assumes that the current thread hasn't yet been resumed
2074   */

2075  synchronized void currThreadResumed() throws DebugException {
2076    _log.log(this + " is executing currThreadResumed()");
2077    Utilities.invokeLater(new Runnable JavaDoc() { public void run() { _notifier.currThreadResumed(); } });
2078  }
2079
2080  /** Switches the current interpreter to the one corresponding to threadRef.
2081   * @param threadRef The ThreadRefernce corresponding to the interpreter to switch to
2082   */

2083  private void _switchToInterpreterForThreadReference(ThreadReference threadRef) {
2084    String JavaDoc threadName = _getUniqueThreadName(threadRef);
2085    String JavaDoc prompt = _getPromptString(threadRef);
2086    _model.getInteractionsModel().setActiveInterpreter(threadName, prompt);
2087  }
2088
2089  synchronized void threadStarted() {
2090    Utilities.invokeLater(new Runnable JavaDoc() { public void run() { _notifier.threadStarted(); } });
2091  }
2092
2093  /** Notifies all listeners that the current thread has died. updateThreads is set to true if the threads and stack
2094   * tables need to be updated, false if there are no suspended threads
2095   */

2096  synchronized void currThreadDied() throws DebugException {
2097    printMessage("The current thread has finished.");
2098    _runningThread = null;
2099
2100    _updateWatches();
2101
2102    if (_suspendedThreads.size() > 0) {
2103      ThreadReference thread = _suspendedThreads.peek();
2104      _switchToInterpreterForThreadReference(thread);
2105
2106      try {
2107        if (thread.frameCount() <= 0) {
2108          printMessage("Could not scroll to source for " + thread.name() + ". It has no stackframes.");
2109        }
2110        else scrollToSource(thread.frame(0).location());
2111      }
2112      catch(IncompatibleThreadStateException e) { throw new UnexpectedException(e); }
2113
2114      // updates watches and makes buttons in UI active, does this because
2115
// there are suspended threads on the stack
2116
_switchToSuspendedThread();
2117    }
2118    Utilities.invokeLater(new Runnable JavaDoc() { public void run() { _notifier.currThreadDied(); } });
2119  }
2120
2121  void nonCurrThreadDied() {
2122    Utilities.invokeLater(new Runnable JavaDoc() { public void run() { _notifier.nonCurrThreadDied(); } });
2123  }
2124
2125  /** Notifies all listeners that the debugger has shut down. updateThreads is set to true if the threads and stack
2126   * tables need to be updated, false if there are no suspended threads
2127   */

2128  void notifyDebuggerShutdown() {
2129    Utilities.invokeLater(new Runnable JavaDoc() { public void run() { _notifier.debuggerShutdown(); } });
2130  }
2131
2132  /** Notifies all listeners that the debugger has started. */
2133  void notifyDebuggerStarted() {
2134    Utilities.invokeLater(new Runnable JavaDoc() { public void run() { _notifier.debuggerStarted(); } });
2135  }
2136
2137  /** Notifies all listeners that a step has been requested. */
2138  void notifyStepRequested() {
2139    Utilities.invokeLater(new Runnable JavaDoc() { public void run() { _notifier.stepRequested(); } });
2140  }
2141
2142  /** A thread-safe stack from which you can remove any element, not just the top of the stack. All synchronization is
2143   * performed on the wrapped vector.
2144   * TODO: make a generic Collection extending/replacing Stack.
2145   */

2146  private static class RandomAccessStack extends Stack JavaDoc<ThreadReference> {
2147
2148    public ThreadReference peekAt(int i) { return get(i); }
2149      
2150    public ThreadReference remove(long id) throws NoSuchElementException JavaDoc {
2151      synchronized(this) {
2152        for (int i = 0; i < size(); i++) {
2153          if (get(i).uniqueID() == id) {
2154            ThreadReference t = get(i);
2155            remove(i);
2156            return t;
2157          }
2158        }
2159      }
2160
2161      throw new NoSuchElementException JavaDoc("Thread " + id + " not found in debugger suspended threads stack!");
2162    }
2163
2164    public synchronized boolean contains(long id) {
2165      for(int i = 0; i < size(); i++) {
2166        if (get(i).uniqueID() == id) return true;
2167      }
2168      return false;
2169    }
2170
2171    public boolean isEmpty() { return empty(); }
2172  }
2173
2174// /** This class tries to filter out system threads. It is currently unused. */
2175
// class SystemThreadsFilter{
2176
// private HashMap<String,Boolean> _filterThese;
2177
//
2178
// public SystemThreadsFilter(List threads) {
2179
// _filterThese = new HashMap<String,Boolean>();
2180
// Iterator iterator = threads.iterator();
2181
// String temp = null;
2182
//
2183
// while(iterator.hasNext()) {
2184
// temp = ((ThreadReference)iterator.next()).name();
2185
// _filterThese.put(temp, Boolean.TRUE);
2186
// }
2187
// }
2188
//
2189
// public List filter(List list) {
2190
// LinkedList retList = new LinkedList();
2191
// String temp = null;
2192
// ThreadReference tempThreadRef = null;
2193
// Iterator iterator = list.iterator();
2194
//
2195
// while(iterator.hasNext()) {
2196
// tempThreadRef = (ThreadReference)iterator.next();
2197
// temp = tempThreadRef.name();
2198
// if ( _filterThese.get(temp) == null ) {
2199
// retList.add(tempThreadRef);
2200
// }
2201
// }
2202
//
2203
// return retList;
2204
// }
2205
// }
2206
}
2207
Popular Tags