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