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