1 /*
   2  * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.jdi;
  27 
  28 import com.sun.jdi.*;
  29 import com.sun.jdi.connect.spi.Connection;
  30 import com.sun.jdi.request.EventRequestManager;
  31 import com.sun.jdi.request.EventRequest;
  32 import com.sun.jdi.request.BreakpointRequest;
  33 import com.sun.jdi.event.EventQueue;
  34 
  35 import java.util.*;
  36 import java.text.MessageFormat;
  37 import java.lang.ref.ReferenceQueue;
  38 import java.lang.ref.Reference;
  39 import java.lang.ref.SoftReference;
  40 import java.lang.ref.WeakReference;
  41 
  42 class VirtualMachineImpl extends MirrorImpl
  43              implements PathSearchingVirtualMachine, ThreadListener {
  44     // VM Level exported variables, these
  45     // are unique to a given vm
  46     public final int sizeofFieldRef;
  47     public final int sizeofMethodRef;
  48     public final int sizeofObjectRef;
  49     public final int sizeofClassRef;
  50     public final int sizeofFrameRef;
  51 
  52     final int sequenceNumber;
  53 
  54     private final TargetVM target;
  55     private final EventQueueImpl eventQueue;
  56     private final EventRequestManagerImpl internalEventRequestManager;
  57     private final EventRequestManagerImpl eventRequestManager;
  58     final VirtualMachineManagerImpl vmManager;
  59     private final ThreadGroup threadGroupForJDI;
  60 
  61     // Allow direct access to this field so that that tracing code slows down
  62     // JDI as little as possible when not enabled.
  63     int traceFlags = TRACE_NONE;
  64 
  65     static int TRACE_RAW_SENDS     = 0x01000000;
  66     static int TRACE_RAW_RECEIVES  = 0x02000000;
  67 
  68     boolean traceReceives = false;   // pre-compute because of frequency
  69 
  70     // ReferenceType access - updated with class prepare and unload events
  71     // Protected by "synchronized(this)". "retrievedAllTypes" may be
  72     // tested unsynchronized (since once true, it stays true), but must
  73     // be set synchronously
  74     private Map<Long, ReferenceType> typesByID;
  75     private TreeSet<ReferenceType> typesBySignature;
  76     private boolean retrievedAllTypes = false;
  77 
  78     // For other languages support
  79     private String defaultStratum = null;
  80 
  81     // ObjectReference cache
  82     // "objectsByID" protected by "synchronized(this)".
  83     private final Map<Long, SoftObjectReference> objectsByID = new HashMap<Long, SoftObjectReference>();
  84     private final ReferenceQueue<ObjectReferenceImpl> referenceQueue = new ReferenceQueue<ObjectReferenceImpl>();
  85     static private final int DISPOSE_THRESHOLD = 50;
  86     private final List<SoftObjectReference> batchedDisposeRequests =
  87             Collections.synchronizedList(new ArrayList<SoftObjectReference>(DISPOSE_THRESHOLD + 10));
  88 
  89     // These are cached once for the life of the VM
  90     private JDWP.VirtualMachine.Version versionInfo;
  91     private JDWP.VirtualMachine.ClassPaths pathInfo;
  92     private JDWP.VirtualMachine.Capabilities capabilities = null;
  93     private JDWP.VirtualMachine.CapabilitiesNew capabilitiesNew = null;
  94 
  95     // Per-vm singletons for primitive types and for void.
  96     // singleton-ness protected by "synchronized(this)".
  97     private BooleanType theBooleanType;
  98     private ByteType    theByteType;
  99     private CharType    theCharType;
 100     private ShortType   theShortType;
 101     private IntegerType theIntegerType;
 102     private LongType    theLongType;
 103     private FloatType   theFloatType;
 104     private DoubleType  theDoubleType;
 105 
 106     private VoidType    theVoidType;
 107 
 108     private VoidValue voidVal;
 109 
 110     // Launched debuggee process
 111     private Process process;
 112 
 113     // coordinates state changes and corresponding listener notifications
 114     private VMState state = new VMState(this);
 115 
 116     private Object initMonitor = new Object();
 117     private boolean initComplete = false;
 118     private boolean shutdown = false;
 119 
 120     private void notifyInitCompletion() {
 121         synchronized(initMonitor) {
 122             initComplete = true;
 123             initMonitor.notifyAll();
 124         }
 125     }
 126 
 127     void waitInitCompletion() {
 128         synchronized(initMonitor) {
 129             while (!initComplete) {
 130                 try {
 131                     initMonitor.wait();
 132                 } catch (InterruptedException e) {
 133                     // ignore
 134                 }
 135             }
 136         }
 137     }
 138 
 139     VMState state() {
 140         return state;
 141     }
 142 
 143     /*
 144      * ThreadListener implementation
 145      */
 146     public boolean threadResumable(ThreadAction action) {
 147         /*
 148          * If any thread is resumed, the VM is considered not suspended.
 149          * Just one thread is being resumed so pass it to thaw.
 150          */
 151         state.thaw(action.thread());
 152         return true;
 153     }
 154 
 155     VirtualMachineImpl(VirtualMachineManager manager,
 156                        Connection connection, Process process,
 157                        int sequenceNumber) {
 158         super(null);  // Can't use super(this)
 159         vm = this;
 160 
 161         this.vmManager = (VirtualMachineManagerImpl)manager;
 162         this.process = process;
 163         this.sequenceNumber = sequenceNumber;
 164 
 165         /* Create ThreadGroup to be used by all threads servicing
 166          * this VM.
 167          */
 168         threadGroupForJDI = new ThreadGroup(vmManager.mainGroupForJDI(),
 169                                             "JDI [" +
 170                                             this.hashCode() + "]");
 171 
 172         /*
 173          * Set up a thread to communicate with the target VM over
 174          * the specified transport.
 175          */
 176         target = new TargetVM(this, connection);
 177 
 178         /*
 179          * Set up a thread to handle events processed internally
 180          * the JDI implementation.
 181          */
 182         EventQueueImpl internalEventQueue = new EventQueueImpl(this, target);
 183         new InternalEventHandler(this, internalEventQueue);
 184         /*
 185          * Initialize client access to event setting and handling
 186          */
 187         eventQueue = new EventQueueImpl(this, target);
 188         eventRequestManager = new EventRequestManagerImpl(this);
 189 
 190         target.start();
 191 
 192         /*
 193          * Many ids are variably sized, depending on target VM.
 194          * Find out the sizes right away.
 195          */
 196         JDWP.VirtualMachine.IDSizes idSizes;
 197         try {
 198             idSizes = JDWP.VirtualMachine.IDSizes.process(vm);
 199         } catch (JDWPException exc) {
 200             throw exc.toJDIException();
 201         }
 202         sizeofFieldRef  = idSizes.fieldIDSize;
 203         sizeofMethodRef = idSizes.methodIDSize;
 204         sizeofObjectRef = idSizes.objectIDSize;
 205         sizeofClassRef = idSizes.referenceTypeIDSize;
 206         sizeofFrameRef  = idSizes.frameIDSize;
 207 
 208         /**
 209          * Set up requests needed by internal event handler.
 210          * Make sure they are distinguished by creating them with
 211          * an internal event request manager.
 212          *
 213          * Warning: create events only with SUSPEND_NONE policy.
 214          * In the current implementation other policies will not
 215          * be handled correctly when the event comes in. (notfiySuspend()
 216          * will not be properly called, and if the event is combined
 217          * with external events in the same set, suspend policy is not
 218          * correctly determined for the internal vs. external event sets)
 219          */
 220         internalEventRequestManager = new EventRequestManagerImpl(this);
 221         EventRequest er = internalEventRequestManager.createClassPrepareRequest();
 222         er.setSuspendPolicy(EventRequest.SUSPEND_NONE);
 223         er.enable();
 224         er = internalEventRequestManager.createClassUnloadRequest();
 225         er.setSuspendPolicy(EventRequest.SUSPEND_NONE);
 226         er.enable();
 227 
 228         /*
 229          * Tell other threads, notably TargetVM, that initialization
 230          * is complete.
 231          */
 232         notifyInitCompletion();
 233     }
 234 
 235     EventRequestManagerImpl getInternalEventRequestManager() {
 236         return internalEventRequestManager;
 237     }
 238 
 239     void validateVM() {
 240         /*
 241          * We no longer need to do this.  The spec now says
 242          * that a VMDisconnected _may_ be thrown in these
 243          * cases, not that it _will_ be thrown.
 244          * So, to simplify things we will just let the
 245          * caller's of this method proceed with their business.
 246          * If the debuggee is disconnected, either because it
 247          * crashed or finished or something, or because the
 248          * debugger called exit() or dispose(), then if
 249          * we end up trying to communicate with the debuggee,
 250          * code in TargetVM will throw a VMDisconnectedException.
 251          * This means that if we can satisfy a request without
 252          * talking to the debuggee, (eg, with cached data) then
 253          * VMDisconnectedException will _not_ be thrown.
 254          * if (shutdown) {
 255          *    throw new VMDisconnectedException();
 256          * }
 257          */
 258     }
 259 
 260     public boolean equals(Object obj) {
 261         return this == obj;
 262     }
 263 
 264     public int hashCode() {
 265         return System.identityHashCode(this);
 266     }
 267 
 268     public List<ReferenceType> classesByName(String className) {
 269         validateVM();
 270         String signature = JNITypeParser.typeNameToSignature(className);
 271         List<ReferenceType> list;
 272         if (retrievedAllTypes) {
 273            list = findReferenceTypes(signature);
 274         } else {
 275            list = retrieveClassesBySignature(signature);
 276         }
 277         return Collections.unmodifiableList(list);
 278     }
 279 
 280     public List<ReferenceType> allClasses() {
 281         validateVM();
 282 
 283         if (!retrievedAllTypes) {
 284             retrieveAllClasses();
 285         }
 286         ArrayList<ReferenceType> a;
 287         synchronized (this) {
 288             a = new ArrayList<ReferenceType>(typesBySignature);
 289         }
 290         return Collections.unmodifiableList(a);
 291     }
 292 
 293     public void
 294         redefineClasses(Map<? extends ReferenceType,byte[]> classToBytes)
 295     {
 296         int cnt = classToBytes.size();
 297         JDWP.VirtualMachine.RedefineClasses.ClassDef[] defs =
 298             new JDWP.VirtualMachine.RedefineClasses.ClassDef[cnt];
 299         validateVM();
 300         if (!canRedefineClasses()) {
 301             throw new UnsupportedOperationException();
 302         }
 303         Iterator<?> it = classToBytes.entrySet().iterator();
 304         for (int i = 0; it.hasNext(); i++) {
 305             Map.Entry<?,?> entry = (Map.Entry)it.next();
 306             ReferenceTypeImpl refType = (ReferenceTypeImpl)entry.getKey();
 307             validateMirror(refType);
 308             defs[i] = new JDWP.VirtualMachine.RedefineClasses
 309                        .ClassDef(refType, (byte[])entry.getValue());
 310         }
 311 
 312         // flush caches and disable caching until the next suspend
 313         vm.state().thaw();
 314 
 315         try {
 316             JDWP.VirtualMachine.RedefineClasses.
 317                 process(vm, defs);
 318         } catch (JDWPException exc) {
 319             switch (exc.errorCode()) {
 320             case JDWP.Error.INVALID_CLASS_FORMAT :
 321                 throw new ClassFormatError(
 322                         "class not in class file format");
 323             case JDWP.Error.CIRCULAR_CLASS_DEFINITION :
 324                 throw new ClassCircularityError(
 325        "circularity has been detected while initializing a class");
 326             case JDWP.Error.FAILS_VERIFICATION :
 327                 throw new VerifyError(
 328    "verifier detected internal inconsistency or security problem");
 329             case JDWP.Error.UNSUPPORTED_VERSION :
 330                 throw new UnsupportedClassVersionError(
 331                     "version numbers of class are not supported");
 332             case JDWP.Error.ADD_METHOD_NOT_IMPLEMENTED:
 333                 throw new UnsupportedOperationException(
 334                               "add method not implemented");
 335             case JDWP.Error.SCHEMA_CHANGE_NOT_IMPLEMENTED :
 336                 throw new UnsupportedOperationException(
 337                               "schema change not implemented");
 338             case JDWP.Error.HIERARCHY_CHANGE_NOT_IMPLEMENTED:
 339                 throw new UnsupportedOperationException(
 340                               "hierarchy change not implemented");
 341             case JDWP.Error.DELETE_METHOD_NOT_IMPLEMENTED :
 342                 throw new UnsupportedOperationException(
 343                               "delete method not implemented");
 344             case JDWP.Error.CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED:
 345                 throw new UnsupportedOperationException(
 346                        "changes to class modifiers not implemented");
 347             case JDWP.Error.METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED :
 348                 throw new UnsupportedOperationException(
 349                        "changes to method modifiers not implemented");
 350             case JDWP.Error.NAMES_DONT_MATCH :
 351                 throw new NoClassDefFoundError(
 352                               "class names do not match");
 353             default:
 354                 throw exc.toJDIException();
 355             }
 356         }
 357 
 358         // Delete any record of the breakpoints
 359         List<BreakpointRequest> toDelete = new ArrayList<BreakpointRequest>();
 360         EventRequestManager erm = eventRequestManager();
 361         it = erm.breakpointRequests().iterator();
 362         while (it.hasNext()) {
 363             BreakpointRequest req = (BreakpointRequest)it.next();
 364             if (classToBytes.containsKey(req.location().declaringType())) {
 365                 toDelete.add(req);
 366             }
 367         }
 368         erm.deleteEventRequests(toDelete);
 369 
 370         // Invalidate any information cached for the classes just redefined.
 371         it = classToBytes.keySet().iterator();
 372         while (it.hasNext()) {
 373             ReferenceTypeImpl rti = (ReferenceTypeImpl)it.next();
 374             rti.noticeRedefineClass();
 375         }
 376     }
 377 
 378     public List<ThreadReference> allThreads() {
 379         validateVM();
 380         return state.allThreads();
 381     }
 382 
 383     public List<ThreadGroupReference> topLevelThreadGroups() {
 384         validateVM();
 385         return state.topLevelThreadGroups();
 386     }
 387 
 388     /*
 389      * Sends a command to the back end which is defined to do an
 390      * implicit vm-wide resume. The VM can no longer be considered
 391      * suspended, so certain cached data must be invalidated.
 392      */
 393     PacketStream sendResumingCommand(CommandSender sender) {
 394         return state.thawCommand(sender);
 395     }
 396 
 397     /*
 398      * The VM has been suspended. Additional caching can be done
 399      * as long as there are no pending resumes.
 400      */
 401     void notifySuspend() {
 402         state.freeze();
 403     }
 404 
 405     public void suspend() {
 406         validateVM();
 407         try {
 408             JDWP.VirtualMachine.Suspend.process(vm);
 409         } catch (JDWPException exc) {
 410             throw exc.toJDIException();
 411         }
 412         notifySuspend();
 413     }
 414 
 415     public void resume() {
 416         validateVM();
 417         CommandSender sender =
 418             new CommandSender() {
 419                 public PacketStream send() {
 420                     return JDWP.VirtualMachine.Resume.enqueueCommand(vm);
 421                 }
 422         };
 423         try {
 424             PacketStream stream = state.thawCommand(sender);
 425             JDWP.VirtualMachine.Resume.waitForReply(vm, stream);
 426         } catch (VMDisconnectedException exc) {
 427             /*
 428              * If the debugger makes a VMDeathRequest with SUSPEND_ALL,
 429              * then when it does an EventSet.resume after getting the
 430              * VMDeathEvent, the normal flow of events is that the
 431              * BE shuts down, but the waitForReply comes back ok.  In this
 432              * case, the run loop in TargetVM that is waiting for a packet
 433              * gets an EOF because the socket closes. It generates a
 434              * VMDisconnectedEvent and everyone is happy.
 435              * However, sometimes, the BE gets shutdown before this
 436              * waitForReply completes.  In this case, TargetVM.waitForReply
 437              * gets awakened with no reply and so gens a VMDisconnectedException
 438              * which is not what we want.  It might be possible to fix this
 439              * in the BE, but it is ok to just ignore the VMDisconnectedException
 440              * here.  This will allow the VMDisconnectedEvent to be generated
 441              * correctly.  And, if the debugger should happen to make another
 442              * request, it will get a VMDisconnectedException at that time.
 443              */
 444         } catch (JDWPException exc) {
 445             switch (exc.errorCode()) {
 446                 case JDWP.Error.VM_DEAD:
 447                     return;
 448                 default:
 449                     throw exc.toJDIException();
 450             }
 451         }
 452     }
 453 
 454     public EventQueue eventQueue() {
 455         /*
 456          * No VM validation here. We allow access to the event queue
 457          * after disconnection, so that there is access to the terminating
 458          * events.
 459          */
 460         return eventQueue;
 461     }
 462 
 463     public EventRequestManager eventRequestManager() {
 464         validateVM();
 465         return eventRequestManager;
 466     }
 467 
 468     EventRequestManagerImpl eventRequestManagerImpl() {
 469         return eventRequestManager;
 470     }
 471 
 472     public BooleanValue mirrorOf(boolean value) {
 473         validateVM();
 474         return new BooleanValueImpl(this,value);
 475     }
 476 
 477     public ByteValue mirrorOf(byte value) {
 478         validateVM();
 479         return new ByteValueImpl(this,value);
 480     }
 481 
 482     public CharValue mirrorOf(char value) {
 483         validateVM();
 484         return new CharValueImpl(this,value);
 485     }
 486 
 487     public ShortValue mirrorOf(short value) {
 488         validateVM();
 489         return new ShortValueImpl(this,value);
 490     }
 491 
 492     public IntegerValue mirrorOf(int value) {
 493         validateVM();
 494         return new IntegerValueImpl(this,value);
 495     }
 496 
 497     public LongValue mirrorOf(long value) {
 498         validateVM();
 499         return new LongValueImpl(this,value);
 500     }
 501 
 502     public FloatValue mirrorOf(float value) {
 503         validateVM();
 504         return new FloatValueImpl(this,value);
 505     }
 506 
 507     public DoubleValue mirrorOf(double value) {
 508         validateVM();
 509         return new DoubleValueImpl(this,value);
 510     }
 511 
 512     public StringReference mirrorOf(String value) {
 513         validateVM();
 514         try {
 515             return (StringReference)JDWP.VirtualMachine.CreateString.
 516                              process(vm, value).stringObject;
 517         } catch (JDWPException exc) {
 518             throw exc.toJDIException();
 519         }
 520     }
 521 
 522     public VoidValue mirrorOfVoid() {
 523         if (voidVal == null) {
 524             voidVal = new VoidValueImpl(this);
 525         }
 526         return voidVal;
 527     }
 528 
 529     public long[] instanceCounts(List<? extends ReferenceType> classes) {
 530         if (!canGetInstanceInfo()) {
 531             throw new UnsupportedOperationException(
 532                 "target does not support getting instances");
 533         }
 534         long[] retValue ;
 535         ReferenceTypeImpl[] rtArray = new ReferenceTypeImpl[classes.size()];
 536         int ii = 0;
 537         for (ReferenceType rti: classes) {
 538             validateMirror(rti);
 539             rtArray[ii++] = (ReferenceTypeImpl)rti;
 540         }
 541         try {
 542             retValue = JDWP.VirtualMachine.InstanceCounts.
 543                                 process(vm, rtArray).counts;
 544         } catch (JDWPException exc) {
 545             throw exc.toJDIException();
 546         }
 547 
 548         return retValue;
 549     }
 550 
 551     public void dispose() {
 552         validateVM();
 553         shutdown = true;
 554         try {
 555             JDWP.VirtualMachine.Dispose.process(vm);
 556         } catch (JDWPException exc) {
 557             throw exc.toJDIException();
 558         }
 559         target.stopListening();
 560     }
 561 
 562     public void exit(int exitCode) {
 563         validateVM();
 564         shutdown = true;
 565         try {
 566             JDWP.VirtualMachine.Exit.process(vm, exitCode);
 567         } catch (JDWPException exc) {
 568             throw exc.toJDIException();
 569         }
 570         target.stopListening();
 571     }
 572 
 573     public Process process() {
 574         validateVM();
 575         return process;
 576     }
 577 
 578     private JDWP.VirtualMachine.Version versionInfo() {
 579        try {
 580            if (versionInfo == null) {
 581                // Need not be synchronized since it is static information
 582                versionInfo = JDWP.VirtualMachine.Version.process(vm);
 583            }
 584            return versionInfo;
 585        } catch (JDWPException exc) {
 586            throw exc.toJDIException();
 587        }
 588    }
 589     public String description() {
 590         validateVM();
 591 
 592         return MessageFormat.format(vmManager.getString("version_format"),
 593                                     "" + vmManager.majorInterfaceVersion(),
 594                                     "" + vmManager.minorInterfaceVersion(),
 595                                      versionInfo().description);
 596     }
 597 
 598     public String version() {
 599         validateVM();
 600         return versionInfo().vmVersion;
 601     }
 602 
 603     public String name() {
 604         validateVM();
 605         return versionInfo().vmName;
 606     }
 607 
 608     public boolean canWatchFieldModification() {
 609         validateVM();
 610         return capabilities().canWatchFieldModification;
 611     }
 612     public boolean canWatchFieldAccess() {
 613         validateVM();
 614         return capabilities().canWatchFieldAccess;
 615     }
 616     public boolean canGetBytecodes() {
 617         validateVM();
 618         return capabilities().canGetBytecodes;
 619     }
 620     public boolean canGetSyntheticAttribute() {
 621         validateVM();
 622         return capabilities().canGetSyntheticAttribute;
 623     }
 624     public boolean canGetOwnedMonitorInfo() {
 625         validateVM();
 626         return capabilities().canGetOwnedMonitorInfo;
 627     }
 628     public boolean canGetCurrentContendedMonitor() {
 629         validateVM();
 630         return capabilities().canGetCurrentContendedMonitor;
 631     }
 632     public boolean canGetMonitorInfo() {
 633         validateVM();
 634         return capabilities().canGetMonitorInfo;
 635     }
 636 
 637     private boolean hasNewCapabilities() {
 638         return versionInfo().jdwpMajor > 1 ||
 639             versionInfo().jdwpMinor >= 4;
 640     }
 641 
 642     boolean canGet1_5LanguageFeatures() {
 643         return versionInfo().jdwpMajor > 1 ||
 644             versionInfo().jdwpMinor >= 5;
 645     }
 646 
 647     public boolean canUseInstanceFilters() {
 648         validateVM();
 649         return hasNewCapabilities() &&
 650             capabilitiesNew().canUseInstanceFilters;
 651     }
 652     public boolean canRedefineClasses() {
 653         validateVM();
 654         return hasNewCapabilities() &&
 655             capabilitiesNew().canRedefineClasses;
 656     }
 657     public boolean canAddMethod() {
 658         validateVM();
 659         return hasNewCapabilities() &&
 660             capabilitiesNew().canAddMethod;
 661     }
 662     public boolean canUnrestrictedlyRedefineClasses() {
 663         validateVM();
 664         return hasNewCapabilities() &&
 665             capabilitiesNew().canUnrestrictedlyRedefineClasses;
 666     }
 667     public boolean canPopFrames() {
 668         validateVM();
 669         return hasNewCapabilities() &&
 670             capabilitiesNew().canPopFrames;
 671     }
 672     public boolean canGetMethodReturnValues() {
 673         return versionInfo().jdwpMajor > 1 ||
 674             versionInfo().jdwpMinor >= 6;
 675     }
 676     public boolean canGetInstanceInfo() {
 677         if (versionInfo().jdwpMajor < 1 ||
 678             versionInfo().jdwpMinor < 6) {
 679             return false;
 680         }
 681         validateVM();
 682         return hasNewCapabilities() &&
 683             capabilitiesNew().canGetInstanceInfo;
 684     }
 685     public boolean canUseSourceNameFilters() {
 686         if (versionInfo().jdwpMajor < 1 ||
 687             versionInfo().jdwpMinor < 6) {
 688             return false;
 689         }
 690         return true;
 691     }
 692     public boolean canForceEarlyReturn() {
 693         validateVM();
 694         return hasNewCapabilities() &&
 695             capabilitiesNew().canForceEarlyReturn;
 696     }
 697     public boolean canBeModified() {
 698         return true;
 699     }
 700     public boolean canGetSourceDebugExtension() {
 701         validateVM();
 702         return hasNewCapabilities() &&
 703             capabilitiesNew().canGetSourceDebugExtension;
 704     }
 705     public boolean canGetClassFileVersion() {
 706         if ( versionInfo().jdwpMajor < 1 &&
 707              versionInfo().jdwpMinor  < 6) {
 708             return false;
 709         } else {
 710             return true;
 711         }
 712     }
 713     public boolean canGetConstantPool() {
 714         validateVM();
 715         return hasNewCapabilities() &&
 716             capabilitiesNew().canGetConstantPool;
 717     }
 718     public boolean canRequestVMDeathEvent() {
 719         validateVM();
 720         return hasNewCapabilities() &&
 721             capabilitiesNew().canRequestVMDeathEvent;
 722     }
 723     public boolean canRequestMonitorEvents() {
 724         validateVM();
 725         return hasNewCapabilities() &&
 726             capabilitiesNew().canRequestMonitorEvents;
 727     }
 728     public boolean canGetMonitorFrameInfo() {
 729         validateVM();
 730         return hasNewCapabilities() &&
 731             capabilitiesNew().canGetMonitorFrameInfo;
 732     }
 733 
 734     public void setDebugTraceMode(int traceFlags) {
 735         validateVM();
 736         this.traceFlags = traceFlags;
 737         this.traceReceives = (traceFlags & TRACE_RECEIVES) != 0;
 738     }
 739 
 740     void printTrace(String string) {
 741         System.err.println("[JDI: " + string + "]");
 742     }
 743 
 744     void printReceiveTrace(int depth, String string) {
 745         StringBuilder sb = new StringBuilder("Receiving:");
 746         for (int i = depth; i > 0; --i) {
 747             sb.append("    ");
 748         }
 749         sb.append(string);
 750         printTrace(sb.toString());
 751     }
 752 
 753     private synchronized ReferenceTypeImpl addReferenceType(long id,
 754                                                        int tag,
 755                                                        String signature) {
 756         if (typesByID == null) {
 757             initReferenceTypes();
 758         }
 759         ReferenceTypeImpl type = null;
 760         switch(tag) {
 761             case JDWP.TypeTag.CLASS:
 762                 type = new ClassTypeImpl(vm, id);
 763                 break;
 764             case JDWP.TypeTag.INTERFACE:
 765                 type = new InterfaceTypeImpl(vm, id);
 766                 break;
 767             case JDWP.TypeTag.ARRAY:
 768                 type = new ArrayTypeImpl(vm, id);
 769                 break;
 770             default:
 771                 throw new InternalException("Invalid reference type tag");
 772         }
 773 
 774         /*
 775          * If a signature was specified, make sure to set it ASAP, to
 776          * prevent any needless JDWP command to retrieve it. (for example,
 777          * typesBySignature.add needs the signature, to maintain proper
 778          * ordering.
 779          */
 780         if (signature != null) {
 781             type.setSignature(signature);
 782         }
 783 
 784         typesByID.put(new Long(id), type);
 785         typesBySignature.add(type);
 786 
 787         if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
 788            vm.printTrace("Caching new ReferenceType, sig=" + signature +
 789                          ", id=" + id);
 790         }
 791 
 792         return type;
 793     }
 794 
 795     synchronized void removeReferenceType(String signature) {
 796         if (typesByID == null) {
 797             return;
 798         }
 799         /*
 800          * There can be multiple classes with the same name. Since
 801          * we can't differentiate here, we first remove all
 802          * matching classes from our cache...
 803          */
 804         Iterator<ReferenceType> iter = typesBySignature.iterator();
 805         int matches = 0;
 806         while (iter.hasNext()) {
 807             ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
 808             int comp = signature.compareTo(type.signature());
 809             if (comp == 0) {
 810                 matches++;
 811                 iter.remove();
 812                 typesByID.remove(new Long(type.ref()));
 813                 if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
 814                    vm.printTrace("Uncaching ReferenceType, sig=" + signature +
 815                                  ", id=" + type.ref());
 816                 }
 817 /* fix for 4359077 , don't break out. list is no longer sorted
 818         in the order we think
 819  */
 820             }
 821         }
 822 
 823         /*
 824          * ...and if there was more than one, re-retrieve the classes
 825          * with that name
 826          */
 827         if (matches > 1) {
 828             retrieveClassesBySignature(signature);
 829         }
 830     }
 831 
 832     private synchronized List<ReferenceType> findReferenceTypes(String signature) {
 833         if (typesByID == null) {
 834             return new ArrayList<ReferenceType>(0);
 835         }
 836         Iterator<ReferenceType> iter = typesBySignature.iterator();
 837         List<ReferenceType> list = new ArrayList<ReferenceType>();
 838         while (iter.hasNext()) {
 839             ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
 840             int comp = signature.compareTo(type.signature());
 841             if (comp == 0) {
 842                 list.add(type);
 843 /* fix for 4359077 , don't break out. list is no longer sorted
 844         in the order we think
 845  */
 846             }
 847         }
 848         return list;
 849     }
 850 
 851     private void initReferenceTypes() {
 852         typesByID = new HashMap<Long, ReferenceType>(300);
 853         typesBySignature = new TreeSet<ReferenceType>();
 854     }
 855 
 856     ReferenceTypeImpl referenceType(long ref, byte tag) {
 857         return referenceType(ref, tag, null);
 858     }
 859 
 860     ClassTypeImpl classType(long ref) {
 861         return (ClassTypeImpl)referenceType(ref, JDWP.TypeTag.CLASS, null);
 862     }
 863 
 864     InterfaceTypeImpl interfaceType(long ref) {
 865         return (InterfaceTypeImpl)referenceType(ref, JDWP.TypeTag.INTERFACE, null);
 866     }
 867 
 868     ArrayTypeImpl arrayType(long ref) {
 869         return (ArrayTypeImpl)referenceType(ref, JDWP.TypeTag.ARRAY, null);
 870     }
 871 
 872     ReferenceTypeImpl referenceType(long id, int tag,
 873                                                  String signature) {
 874         if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
 875             StringBuilder sb = new StringBuilder();
 876             sb.append("Looking up ");
 877             if (tag == JDWP.TypeTag.CLASS) {
 878                 sb.append("Class");
 879             } else if (tag == JDWP.TypeTag.INTERFACE) {
 880                 sb.append("Interface");
 881             } else if (tag == JDWP.TypeTag.ARRAY) {
 882                 sb.append("ArrayType");
 883             } else {
 884                 sb.append("UNKNOWN TAG: " + tag);
 885             }
 886             if (signature != null) {
 887                 sb.append(", signature='" + signature + "'");
 888             }
 889             sb.append(", id=" + id);
 890             vm.printTrace(sb.toString());
 891         }
 892         if (id == 0) {
 893             return null;
 894         } else {
 895             ReferenceTypeImpl retType = null;
 896             synchronized (this) {
 897                 if (typesByID != null) {
 898                     retType = (ReferenceTypeImpl)typesByID.get(new Long(id));
 899                 }
 900                 if (retType == null) {
 901                     retType = addReferenceType(id, tag, signature);
 902                 }
 903             }
 904             return retType;
 905         }
 906     }
 907 
 908     private JDWP.VirtualMachine.Capabilities capabilities() {
 909         if (capabilities == null) {
 910             try {
 911                 capabilities = JDWP.VirtualMachine
 912                                  .Capabilities.process(vm);
 913             } catch (JDWPException exc) {
 914                 throw exc.toJDIException();
 915             }
 916         }
 917         return capabilities;
 918     }
 919 
 920     private JDWP.VirtualMachine.CapabilitiesNew capabilitiesNew() {
 921         if (capabilitiesNew == null) {
 922             try {
 923                 capabilitiesNew = JDWP.VirtualMachine
 924                                  .CapabilitiesNew.process(vm);
 925             } catch (JDWPException exc) {
 926                 throw exc.toJDIException();
 927             }
 928         }
 929         return capabilitiesNew;
 930     }
 931 
 932     private List<ReferenceType> retrieveClassesBySignature(String signature) {
 933         if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
 934             vm.printTrace("Retrieving matching ReferenceTypes, sig=" + signature);
 935         }
 936         JDWP.VirtualMachine.ClassesBySignature.ClassInfo[] cinfos;
 937         try {
 938             cinfos = JDWP.VirtualMachine.ClassesBySignature.
 939                                       process(vm, signature).classes;
 940         } catch (JDWPException exc) {
 941             throw exc.toJDIException();
 942         }
 943 
 944         int count = cinfos.length;
 945         List<ReferenceType> list = new ArrayList<ReferenceType>(count);
 946 
 947         // Hold lock during processing to improve performance
 948         synchronized (this) {
 949             for (int i = 0; i < count; i++) {
 950                 JDWP.VirtualMachine.ClassesBySignature.ClassInfo ci =
 951                                                                cinfos[i];
 952                 ReferenceTypeImpl type = referenceType(ci.typeID,
 953                                                        ci.refTypeTag,
 954                                                        signature);
 955                 type.setStatus(ci.status);
 956                 list.add(type);
 957             }
 958         }
 959         return list;
 960     }
 961 
 962     private void retrieveAllClasses1_4() {
 963         JDWP.VirtualMachine.AllClasses.ClassInfo[] cinfos;
 964         try {
 965             cinfos = JDWP.VirtualMachine.AllClasses.process(vm).classes;
 966         } catch (JDWPException exc) {
 967             throw exc.toJDIException();
 968         }
 969 
 970         // Hold lock during processing to improve performance
 971         // and to have safe check/set of retrievedAllTypes
 972         synchronized (this) {
 973             if (!retrievedAllTypes) {
 974                 // Number of classes
 975                 int count = cinfos.length;
 976                 for (int i=0; i<count; i++) {
 977                     JDWP.VirtualMachine.AllClasses.ClassInfo ci =
 978                                                                cinfos[i];
 979                     ReferenceTypeImpl type = referenceType(ci.typeID,
 980                                                            ci.refTypeTag,
 981                                                            ci.signature);
 982                     type.setStatus(ci.status);
 983                 }
 984                 retrievedAllTypes = true;
 985             }
 986         }
 987     }
 988 
 989     private void retrieveAllClasses() {
 990         if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
 991             vm.printTrace("Retrieving all ReferenceTypes");
 992         }
 993 
 994         if (!vm.canGet1_5LanguageFeatures()) {
 995             retrieveAllClasses1_4();
 996             return;
 997         }
 998 
 999         /*
1000          * To save time (assuming the caller will be
1001          * using then) we will get the generic sigs too.
1002          */
1003 
1004         JDWP.VirtualMachine.AllClassesWithGeneric.ClassInfo[] cinfos;
1005         try {
1006             cinfos = JDWP.VirtualMachine.AllClassesWithGeneric.process(vm).classes;
1007         } catch (JDWPException exc) {
1008             throw exc.toJDIException();
1009         }
1010 
1011         // Hold lock during processing to improve performance
1012         // and to have safe check/set of retrievedAllTypes
1013         synchronized (this) {
1014             if (!retrievedAllTypes) {
1015                 // Number of classes
1016                 int count = cinfos.length;
1017                 for (int i=0; i<count; i++) {
1018                     JDWP.VirtualMachine.AllClassesWithGeneric.ClassInfo ci =
1019                                                                cinfos[i];
1020                     ReferenceTypeImpl type = referenceType(ci.typeID,
1021                                                            ci.refTypeTag,
1022                                                            ci.signature);
1023                     type.setGenericSignature(ci.genericSignature);
1024                     type.setStatus(ci.status);
1025                 }
1026                 retrievedAllTypes = true;
1027             }
1028         }
1029     }
1030 
1031     void sendToTarget(Packet packet) {
1032         target.send(packet);
1033     }
1034 
1035     void waitForTargetReply(Packet packet) {
1036         target.waitForReply(packet);
1037         /*
1038          * If any object disposes have been batched up, send them now.
1039          */
1040         processBatchedDisposes();
1041     }
1042 
1043     Type findBootType(String signature) throws ClassNotLoadedException {
1044         List<ReferenceType> types = allClasses();
1045         Iterator<ReferenceType> iter = types.iterator();
1046         while (iter.hasNext()) {
1047             ReferenceType type = iter.next();
1048             if ((type.classLoader() == null) &&
1049                 (type.signature().equals(signature))) {
1050                 return type;
1051             }
1052         }
1053         JNITypeParser parser = new JNITypeParser(signature);
1054         throw new ClassNotLoadedException(parser.typeName(),
1055                                          "Type " + parser.typeName() + " not loaded");
1056     }
1057 
1058     BooleanType theBooleanType() {
1059         if (theBooleanType == null) {
1060             synchronized(this) {
1061                 if (theBooleanType == null) {
1062                     theBooleanType = new BooleanTypeImpl(this);
1063                 }
1064             }
1065         }
1066         return theBooleanType;
1067     }
1068 
1069     ByteType theByteType() {
1070         if (theByteType == null) {
1071             synchronized(this) {
1072                 if (theByteType == null) {
1073                     theByteType = new ByteTypeImpl(this);
1074                 }
1075             }
1076         }
1077         return theByteType;
1078     }
1079 
1080     CharType theCharType() {
1081         if (theCharType == null) {
1082             synchronized(this) {
1083                 if (theCharType == null) {
1084                     theCharType = new CharTypeImpl(this);
1085                 }
1086             }
1087         }
1088         return theCharType;
1089     }
1090 
1091     ShortType theShortType() {
1092         if (theShortType == null) {
1093             synchronized(this) {
1094                 if (theShortType == null) {
1095                     theShortType = new ShortTypeImpl(this);
1096                 }
1097             }
1098         }
1099         return theShortType;
1100     }
1101 
1102     IntegerType theIntegerType() {
1103         if (theIntegerType == null) {
1104             synchronized(this) {
1105                 if (theIntegerType == null) {
1106                     theIntegerType = new IntegerTypeImpl(this);
1107                 }
1108             }
1109         }
1110         return theIntegerType;
1111     }
1112 
1113     LongType theLongType() {
1114         if (theLongType == null) {
1115             synchronized(this) {
1116                 if (theLongType == null) {
1117                     theLongType = new LongTypeImpl(this);
1118                 }
1119             }
1120         }
1121         return theLongType;
1122     }
1123 
1124     FloatType theFloatType() {
1125         if (theFloatType == null) {
1126             synchronized(this) {
1127                 if (theFloatType == null) {
1128                     theFloatType = new FloatTypeImpl(this);
1129                 }
1130             }
1131         }
1132         return theFloatType;
1133     }
1134 
1135     DoubleType theDoubleType() {
1136         if (theDoubleType == null) {
1137             synchronized(this) {
1138                 if (theDoubleType == null) {
1139                     theDoubleType = new DoubleTypeImpl(this);
1140                 }
1141             }
1142         }
1143         return theDoubleType;
1144     }
1145 
1146     VoidType theVoidType() {
1147         if (theVoidType == null) {
1148             synchronized(this) {
1149                 if (theVoidType == null) {
1150                     theVoidType = new VoidTypeImpl(this);
1151                 }
1152             }
1153         }
1154         return theVoidType;
1155     }
1156 
1157     PrimitiveType primitiveTypeMirror(byte tag) {
1158         switch (tag) {
1159             case JDWP.Tag.BOOLEAN:
1160                 return theBooleanType();
1161             case JDWP.Tag.BYTE:
1162                 return theByteType();
1163             case JDWP.Tag.CHAR:
1164                 return theCharType();
1165             case JDWP.Tag.SHORT:
1166                 return theShortType();
1167             case JDWP.Tag.INT:
1168                 return theIntegerType();
1169             case JDWP.Tag.LONG:
1170                 return theLongType();
1171             case JDWP.Tag.FLOAT:
1172                 return theFloatType();
1173             case JDWP.Tag.DOUBLE:
1174                 return theDoubleType();
1175             default:
1176                 throw new IllegalArgumentException("Unrecognized primitive tag " + tag);
1177         }
1178     }
1179 
1180     private void processBatchedDisposes() {
1181         if (shutdown) {
1182             return;
1183         }
1184 
1185         JDWP.VirtualMachine.DisposeObjects.Request[] requests = null;
1186         synchronized(batchedDisposeRequests) {
1187             int size = batchedDisposeRequests.size();
1188             if (size >= DISPOSE_THRESHOLD) {
1189                 if ((traceFlags & TRACE_OBJREFS) != 0) {
1190                     printTrace("Dispose threashold reached. Will dispose "
1191                                + size + " object references...");
1192                 }
1193                 requests = new JDWP.VirtualMachine.DisposeObjects.Request[size];
1194                 for (int i = 0; i < requests.length; i++) {
1195                     SoftObjectReference ref = batchedDisposeRequests.get(i);
1196                     if ((traceFlags & TRACE_OBJREFS) != 0) {
1197                         printTrace("Disposing object " + ref.key().longValue() +
1198                                    " (ref count = " + ref.count() + ")");
1199                     }
1200 
1201                     // This is kludgy. We temporarily re-create an object
1202                     // reference so that we can correctly pass its id to the
1203                     // JDWP command.
1204                     requests[i] =
1205                         new JDWP.VirtualMachine.DisposeObjects.Request(
1206                             new ObjectReferenceImpl(this, ref.key().longValue()),
1207                             ref.count());
1208                 }
1209                 batchedDisposeRequests.clear();
1210             }
1211         }
1212         if (requests != null) {
1213             try {
1214                 JDWP.VirtualMachine.DisposeObjects.process(vm, requests);
1215             } catch (JDWPException exc) {
1216                 throw exc.toJDIException();
1217             }
1218         }
1219     }
1220 
1221     private void batchForDispose(SoftObjectReference ref) {
1222         if ((traceFlags & TRACE_OBJREFS) != 0) {
1223             printTrace("Batching object " + ref.key().longValue() +
1224                        " for dispose (ref count = " + ref.count() + ")");
1225         }
1226         batchedDisposeRequests.add(ref);
1227     }
1228 
1229     private void processQueue() {
1230         Reference<?> ref;
1231         //if ((traceFlags & TRACE_OBJREFS) != 0) {
1232         //    printTrace("Checking for softly reachable objects");
1233         //}
1234         while ((ref = referenceQueue.poll()) != null) {
1235             SoftObjectReference softRef = (SoftObjectReference)ref;
1236             removeObjectMirror(softRef);
1237             batchForDispose(softRef);
1238         }
1239     }
1240 
1241     synchronized ObjectReferenceImpl objectMirror(long id, int tag) {
1242 
1243         // Handle any queue elements that are not strongly reachable
1244         processQueue();
1245 
1246         if (id == 0) {
1247             return null;
1248         }
1249         ObjectReferenceImpl object = null;
1250         Long key = new Long(id);
1251 
1252         /*
1253          * Attempt to retrieve an existing object object reference
1254          */
1255         SoftObjectReference ref = objectsByID.get(key);
1256         if (ref != null) {
1257             object = ref.object();
1258         }
1259 
1260         /*
1261          * If the object wasn't in the table, or it's soft reference was
1262          * cleared, create a new instance.
1263          */
1264         if (object == null) {
1265             switch (tag) {
1266                 case JDWP.Tag.OBJECT:
1267                     object = new ObjectReferenceImpl(vm, id);
1268                     break;
1269                 case JDWP.Tag.STRING:
1270                     object = new StringReferenceImpl(vm, id);
1271                     break;
1272                 case JDWP.Tag.ARRAY:
1273                     object = new ArrayReferenceImpl(vm, id);
1274                     break;
1275                 case JDWP.Tag.THREAD:
1276                     ThreadReferenceImpl thread =
1277                         new ThreadReferenceImpl(vm, id);
1278                     thread.addListener(this);
1279                     object = thread;
1280                     break;
1281                 case JDWP.Tag.THREAD_GROUP:
1282                     object = new ThreadGroupReferenceImpl(vm, id);
1283                     break;
1284                 case JDWP.Tag.CLASS_LOADER:
1285                     object = new ClassLoaderReferenceImpl(vm, id);
1286                     break;
1287                 case JDWP.Tag.CLASS_OBJECT:
1288                     object = new ClassObjectReferenceImpl(vm, id);
1289                     break;
1290                 default:
1291                     throw new IllegalArgumentException("Invalid object tag: " + tag);
1292             }
1293             ref = new SoftObjectReference(key, object, referenceQueue);
1294 
1295             /*
1296              * If there was no previous entry in the table, we add one here
1297              * If the previous entry was cleared, we replace it here.
1298              */
1299             objectsByID.put(key, ref);
1300             if ((traceFlags & TRACE_OBJREFS) != 0) {
1301                 printTrace("Creating new " +
1302                            object.getClass().getName() + " (id = " + id + ")");
1303             }
1304         } else {
1305             ref.incrementCount();
1306         }
1307 
1308         return object;
1309     }
1310 
1311     synchronized void removeObjectMirror(ObjectReferenceImpl object) {
1312 
1313         // Handle any queue elements that are not strongly reachable
1314         processQueue();
1315 
1316         SoftObjectReference ref = objectsByID.remove(new Long(object.ref()));
1317         if (ref != null) {
1318             batchForDispose(ref);
1319         } else {
1320             /*
1321              * If there's a live ObjectReference about, it better be part
1322              * of the cache.
1323              */
1324             throw new InternalException("ObjectReference " + object.ref() +
1325                                         " not found in object cache");
1326         }
1327     }
1328 
1329     synchronized void removeObjectMirror(SoftObjectReference ref) {
1330         /*
1331          * This will remove the soft reference if it has not been
1332          * replaced in the cache.
1333          */
1334         objectsByID.remove(ref.key());
1335     }
1336 
1337     ObjectReferenceImpl objectMirror(long id) {
1338         return objectMirror(id, JDWP.Tag.OBJECT);
1339     }
1340 
1341     StringReferenceImpl stringMirror(long id) {
1342         return (StringReferenceImpl)objectMirror(id, JDWP.Tag.STRING);
1343     }
1344 
1345     ArrayReferenceImpl arrayMirror(long id) {
1346        return (ArrayReferenceImpl)objectMirror(id, JDWP.Tag.ARRAY);
1347     }
1348 
1349     ThreadReferenceImpl threadMirror(long id) {
1350         return (ThreadReferenceImpl)objectMirror(id, JDWP.Tag.THREAD);
1351     }
1352 
1353     ThreadGroupReferenceImpl threadGroupMirror(long id) {
1354         return (ThreadGroupReferenceImpl)objectMirror(id,
1355                                                       JDWP.Tag.THREAD_GROUP);
1356     }
1357 
1358     ClassLoaderReferenceImpl classLoaderMirror(long id) {
1359         return (ClassLoaderReferenceImpl)objectMirror(id,
1360                                                       JDWP.Tag.CLASS_LOADER);
1361     }
1362 
1363     ClassObjectReferenceImpl classObjectMirror(long id) {
1364         return (ClassObjectReferenceImpl)objectMirror(id,
1365                                                       JDWP.Tag.CLASS_OBJECT);
1366     }
1367 
1368     /*
1369      * Implementation of PathSearchingVirtualMachine
1370      */
1371     private JDWP.VirtualMachine.ClassPaths getClasspath() {
1372         if (pathInfo == null) {
1373             try {
1374                 pathInfo = JDWP.VirtualMachine.ClassPaths.process(vm);
1375             } catch (JDWPException exc) {
1376                 throw exc.toJDIException();
1377             }
1378         }
1379         return pathInfo;
1380     }
1381 
1382    public List<String> classPath() {
1383        return Arrays.asList(getClasspath().classpaths);
1384    }
1385 
1386    public List<String> bootClassPath() {
1387        return Arrays.asList(getClasspath().bootclasspaths);
1388    }
1389 
1390    public String baseDirectory() {
1391        return getClasspath().baseDir;
1392    }
1393 
1394     public void setDefaultStratum(String stratum) {
1395         defaultStratum = stratum;
1396         if (stratum == null) {
1397             stratum = "";
1398         }
1399         try {
1400             JDWP.VirtualMachine.SetDefaultStratum.process(vm,
1401                                                           stratum);
1402         } catch (JDWPException exc) {
1403             throw exc.toJDIException();
1404         }
1405     }
1406 
1407     public String getDefaultStratum() {
1408         return defaultStratum;
1409     }
1410 
1411     ThreadGroup threadGroupForJDI() {
1412         return threadGroupForJDI;
1413     }
1414 
1415    static private class SoftObjectReference extends SoftReference<ObjectReferenceImpl> {
1416        int count;
1417        Long key;
1418 
1419        SoftObjectReference(Long key, ObjectReferenceImpl mirror,
1420                            ReferenceQueue<ObjectReferenceImpl> queue) {
1421            super(mirror, queue);
1422            this.count = 1;
1423            this.key = key;
1424        }
1425 
1426        int count() {
1427            return count;
1428        }
1429 
1430        void incrementCount() {
1431            count++;
1432        }
1433 
1434        Long key() {
1435            return key;
1436        }
1437 
1438        ObjectReferenceImpl object() {
1439            return get();
1440        }
1441    }
1442 }