Home | History | Annotate | Download | only in jdi
      1 /*
      2  * Copyright (c) 1998, 2015, 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         StringBuffer sb = new StringBuffer("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             StringBuffer sb = new StringBuffer();
    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 = retrieveClassesBySignature(signature);
   1045         Iterator<ReferenceType> iter = types.iterator();
   1046         while (iter.hasNext()) {
   1047             ReferenceType type = iter.next();
   1048             if (type.classLoader() == null) {
   1049                 return type;
   1050             }
   1051         }
   1052         JNITypeParser parser = new JNITypeParser(signature);
   1053         throw new ClassNotLoadedException(parser.typeName(),
   1054                                          "Type " + parser.typeName() + " not loaded");
   1055     }
   1056 
   1057     BooleanType theBooleanType() {
   1058         if (theBooleanType == null) {
   1059             synchronized(this) {
   1060                 if (theBooleanType == null) {
   1061                     theBooleanType = new BooleanTypeImpl(this);
   1062                 }
   1063             }
   1064         }
   1065         return theBooleanType;
   1066     }
   1067 
   1068     ByteType theByteType() {
   1069         if (theByteType == null) {
   1070             synchronized(this) {
   1071                 if (theByteType == null) {
   1072                     theByteType = new ByteTypeImpl(this);
   1073                 }
   1074             }
   1075         }
   1076         return theByteType;
   1077     }
   1078 
   1079     CharType theCharType() {
   1080         if (theCharType == null) {
   1081             synchronized(this) {
   1082                 if (theCharType == null) {
   1083                     theCharType = new CharTypeImpl(this);
   1084                 }
   1085             }
   1086         }
   1087         return theCharType;
   1088     }
   1089 
   1090     ShortType theShortType() {
   1091         if (theShortType == null) {
   1092             synchronized(this) {
   1093                 if (theShortType == null) {
   1094                     theShortType = new ShortTypeImpl(this);
   1095                 }
   1096             }
   1097         }
   1098         return theShortType;
   1099     }
   1100 
   1101     IntegerType theIntegerType() {
   1102         if (theIntegerType == null) {
   1103             synchronized(this) {
   1104                 if (theIntegerType == null) {
   1105                     theIntegerType = new IntegerTypeImpl(this);
   1106                 }
   1107             }
   1108         }
   1109         return theIntegerType;
   1110     }
   1111 
   1112     LongType theLongType() {
   1113         if (theLongType == null) {
   1114             synchronized(this) {
   1115                 if (theLongType == null) {
   1116                     theLongType = new LongTypeImpl(this);
   1117                 }
   1118             }
   1119         }
   1120         return theLongType;
   1121     }
   1122 
   1123     FloatType theFloatType() {
   1124         if (theFloatType == null) {
   1125             synchronized(this) {
   1126                 if (theFloatType == null) {
   1127                     theFloatType = new FloatTypeImpl(this);
   1128                 }
   1129             }
   1130         }
   1131         return theFloatType;
   1132     }
   1133 
   1134     DoubleType theDoubleType() {
   1135         if (theDoubleType == null) {
   1136             synchronized(this) {
   1137                 if (theDoubleType == null) {
   1138                     theDoubleType = new DoubleTypeImpl(this);
   1139                 }
   1140             }
   1141         }
   1142         return theDoubleType;
   1143     }
   1144 
   1145     VoidType theVoidType() {
   1146         if (theVoidType == null) {
   1147             synchronized(this) {
   1148                 if (theVoidType == null) {
   1149                     theVoidType = new VoidTypeImpl(this);
   1150                 }
   1151             }
   1152         }
   1153         return theVoidType;
   1154     }
   1155 
   1156     PrimitiveType primitiveTypeMirror(byte tag) {
   1157         switch (tag) {
   1158             case JDWP.Tag.BOOLEAN:
   1159                 return theBooleanType();
   1160             case JDWP.Tag.BYTE:
   1161                 return theByteType();
   1162             case JDWP.Tag.CHAR:
   1163                 return theCharType();
   1164             case JDWP.Tag.SHORT:
   1165                 return theShortType();
   1166             case JDWP.Tag.INT:
   1167                 return theIntegerType();
   1168             case JDWP.Tag.LONG:
   1169                 return theLongType();
   1170             case JDWP.Tag.FLOAT:
   1171                 return theFloatType();
   1172             case JDWP.Tag.DOUBLE:
   1173                 return theDoubleType();
   1174             default:
   1175                 throw new IllegalArgumentException("Unrecognized primitive tag " + tag);
   1176         }
   1177     }
   1178 
   1179     private void processBatchedDisposes() {
   1180         if (shutdown) {
   1181             return;
   1182         }
   1183 
   1184         JDWP.VirtualMachine.DisposeObjects.Request[] requests = null;
   1185         synchronized(batchedDisposeRequests) {
   1186             int size = batchedDisposeRequests.size();
   1187             if (size >= DISPOSE_THRESHOLD) {
   1188                 if ((traceFlags & TRACE_OBJREFS) != 0) {
   1189                     printTrace("Dispose threashold reached. Will dispose "
   1190                                + size + " object references...");
   1191                 }
   1192                 requests = new JDWP.VirtualMachine.DisposeObjects.Request[size];
   1193                 for (int i = 0; i < requests.length; i++) {
   1194                     SoftObjectReference ref = batchedDisposeRequests.get(i);
   1195                     if ((traceFlags & TRACE_OBJREFS) != 0) {
   1196                         printTrace("Disposing object " + ref.key().longValue() +
   1197                                    " (ref count = " + ref.count() + ")");
   1198                     }
   1199 
   1200                     // This is kludgy. We temporarily re-create an object
   1201                     // reference so that we can correctly pass its id to the
   1202                     // JDWP command.
   1203                     requests[i] =
   1204                         new JDWP.VirtualMachine.DisposeObjects.Request(
   1205                             new ObjectReferenceImpl(this, ref.key().longValue()),
   1206                             ref.count());
   1207                 }
   1208                 batchedDisposeRequests.clear();
   1209             }
   1210         }
   1211         if (requests != null) {
   1212             try {
   1213                 JDWP.VirtualMachine.DisposeObjects.process(vm, requests);
   1214             } catch (JDWPException exc) {
   1215                 throw exc.toJDIException();
   1216             }
   1217         }
   1218     }
   1219 
   1220     private void batchForDispose(SoftObjectReference ref) {
   1221         if ((traceFlags & TRACE_OBJREFS) != 0) {
   1222             printTrace("Batching object " + ref.key().longValue() +
   1223                        " for dispose (ref count = " + ref.count() + ")");
   1224         }
   1225         batchedDisposeRequests.add(ref);
   1226     }
   1227 
   1228     private void processQueue() {
   1229         Reference<?> ref;
   1230         //if ((traceFlags & TRACE_OBJREFS) != 0) {
   1231         //    printTrace("Checking for softly reachable objects");
   1232         //}
   1233         while ((ref = referenceQueue.poll()) != null) {
   1234             SoftObjectReference softRef = (SoftObjectReference)ref;
   1235             removeObjectMirror(softRef);
   1236             batchForDispose(softRef);
   1237         }
   1238     }
   1239 
   1240     synchronized ObjectReferenceImpl objectMirror(long id, int tag) {
   1241 
   1242         // Handle any queue elements that are not strongly reachable
   1243         processQueue();
   1244 
   1245         if (id == 0) {
   1246             return null;
   1247         }
   1248         ObjectReferenceImpl object = null;
   1249         Long key = new Long(id);
   1250 
   1251         /*
   1252          * Attempt to retrieve an existing object object reference
   1253          */
   1254         SoftObjectReference ref = objectsByID.get(key);
   1255         if (ref != null) {
   1256             object = ref.object();
   1257         }
   1258 
   1259         /*
   1260          * If the object wasn't in the table, or it's soft reference was
   1261          * cleared, create a new instance.
   1262          */
   1263         if (object == null) {
   1264             switch (tag) {
   1265                 case JDWP.Tag.OBJECT:
   1266                     object = new ObjectReferenceImpl(vm, id);
   1267                     break;
   1268                 case JDWP.Tag.STRING:
   1269                     object = new StringReferenceImpl(vm, id);
   1270                     break;
   1271                 case JDWP.Tag.ARRAY:
   1272                     object = new ArrayReferenceImpl(vm, id);
   1273                     break;
   1274                 case JDWP.Tag.THREAD:
   1275                     ThreadReferenceImpl thread =
   1276                         new ThreadReferenceImpl(vm, id);
   1277                     thread.addListener(this);
   1278                     object = thread;
   1279                     break;
   1280                 case JDWP.Tag.THREAD_GROUP:
   1281                     object = new ThreadGroupReferenceImpl(vm, id);
   1282                     break;
   1283                 case JDWP.Tag.CLASS_LOADER:
   1284                     object = new ClassLoaderReferenceImpl(vm, id);
   1285                     break;
   1286                 case JDWP.Tag.CLASS_OBJECT:
   1287                     object = new ClassObjectReferenceImpl(vm, id);
   1288                     break;
   1289                 default:
   1290                     throw new IllegalArgumentException("Invalid object tag: " + tag);
   1291             }
   1292             ref = new SoftObjectReference(key, object, referenceQueue);
   1293 
   1294             /*
   1295              * If there was no previous entry in the table, we add one here
   1296              * If the previous entry was cleared, we replace it here.
   1297              */
   1298             objectsByID.put(key, ref);
   1299             if ((traceFlags & TRACE_OBJREFS) != 0) {
   1300                 printTrace("Creating new " +
   1301                            object.getClass().getName() + " (id = " + id + ")");
   1302             }
   1303         } else {
   1304             ref.incrementCount();
   1305         }
   1306 
   1307         return object;
   1308     }
   1309 
   1310     synchronized void removeObjectMirror(ObjectReferenceImpl object) {
   1311 
   1312         // Handle any queue elements that are not strongly reachable
   1313         processQueue();
   1314 
   1315         SoftObjectReference ref = objectsByID.remove(new Long(object.ref()));
   1316         if (ref != null) {
   1317             batchForDispose(ref);
   1318         } else {
   1319             /*
   1320              * If there's a live ObjectReference about, it better be part
   1321              * of the cache.
   1322              */
   1323             throw new InternalException("ObjectReference " + object.ref() +
   1324                                         " not found in object cache");
   1325         }
   1326     }
   1327 
   1328     synchronized void removeObjectMirror(SoftObjectReference ref) {
   1329         /*
   1330          * This will remove the soft reference if it has not been
   1331          * replaced in the cache.
   1332          */
   1333         objectsByID.remove(ref.key());
   1334     }
   1335 
   1336     ObjectReferenceImpl objectMirror(long id) {
   1337         return objectMirror(id, JDWP.Tag.OBJECT);
   1338     }
   1339 
   1340     StringReferenceImpl stringMirror(long id) {
   1341         return (StringReferenceImpl)objectMirror(id, JDWP.Tag.STRING);
   1342     }
   1343 
   1344     ArrayReferenceImpl arrayMirror(long id) {
   1345        return (ArrayReferenceImpl)objectMirror(id, JDWP.Tag.ARRAY);
   1346     }
   1347 
   1348     ThreadReferenceImpl threadMirror(long id) {
   1349         return (ThreadReferenceImpl)objectMirror(id, JDWP.Tag.THREAD);
   1350     }
   1351 
   1352     ThreadGroupReferenceImpl threadGroupMirror(long id) {
   1353         return (ThreadGroupReferenceImpl)objectMirror(id,
   1354                                                       JDWP.Tag.THREAD_GROUP);
   1355     }
   1356 
   1357     ClassLoaderReferenceImpl classLoaderMirror(long id) {
   1358         return (ClassLoaderReferenceImpl)objectMirror(id,
   1359                                                       JDWP.Tag.CLASS_LOADER);
   1360     }
   1361 
   1362     ClassObjectReferenceImpl classObjectMirror(long id) {
   1363         return (ClassObjectReferenceImpl)objectMirror(id,
   1364                                                       JDWP.Tag.CLASS_OBJECT);
   1365     }
   1366 
   1367     /*
   1368      * Implementation of PathSearchingVirtualMachine
   1369      */
   1370     private JDWP.VirtualMachine.ClassPaths getClasspath() {
   1371         if (pathInfo == null) {
   1372             try {
   1373                 pathInfo = JDWP.VirtualMachine.ClassPaths.process(vm);
   1374             } catch (JDWPException exc) {
   1375                 throw exc.toJDIException();
   1376             }
   1377         }
   1378         return pathInfo;
   1379     }
   1380 
   1381    public List<String> classPath() {
   1382        return Arrays.asList(getClasspath().classpaths);
   1383    }
   1384 
   1385    public List<String> bootClassPath() {
   1386        return Arrays.asList(getClasspath().bootclasspaths);
   1387    }
   1388 
   1389    public String baseDirectory() {
   1390        return getClasspath().baseDir;
   1391    }
   1392 
   1393     public void setDefaultStratum(String stratum) {
   1394         defaultStratum = stratum;
   1395         if (stratum == null) {
   1396             stratum = "";
   1397         }
   1398         try {
   1399             JDWP.VirtualMachine.SetDefaultStratum.process(vm,
   1400                                                           stratum);
   1401         } catch (JDWPException exc) {
   1402             throw exc.toJDIException();
   1403         }
   1404     }
   1405 
   1406     public String getDefaultStratum() {
   1407         return defaultStratum;
   1408     }
   1409 
   1410     ThreadGroup threadGroupForJDI() {
   1411         return threadGroupForJDI;
   1412     }
   1413 
   1414    static private class SoftObjectReference extends SoftReference<ObjectReferenceImpl> {
   1415        int count;
   1416        Long key;
   1417 
   1418        SoftObjectReference(Long key, ObjectReferenceImpl mirror,
   1419                            ReferenceQueue<ObjectReferenceImpl> queue) {
   1420            super(mirror, queue);
   1421            this.count = 1;
   1422            this.key = key;
   1423        }
   1424 
   1425        int count() {
   1426            return count;
   1427        }
   1428 
   1429        void incrementCount() {
   1430            count++;
   1431        }
   1432 
   1433        Long key() {
   1434            return key;
   1435        }
   1436 
   1437        ObjectReferenceImpl object() {
   1438            return get();
   1439        }
   1440    }
   1441 }
   1442