1 /*
   2  * Copyright (c) 1998, 2017, 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.NAMES_DONT_MATCH :
 399                 throw new NoClassDefFoundError(
 400                     "class names do not match");
 401             default:
 402                 throw exc.toJDIException();
 403             }
 404         }
 405 
 406         // Delete any record of the breakpoints
 407         List<BreakpointRequest> toDelete = new ArrayList<>();
 408         EventRequestManager erm = eventRequestManager();
 409         it = erm.breakpointRequests().iterator();
 410         while (it.hasNext()) {
 411             BreakpointRequest req = (BreakpointRequest)it.next();
 412             if (classToBytes.containsKey(req.location().declaringType())) {
 413                 toDelete.add(req);
 414             }
 415         }
 416         erm.deleteEventRequests(toDelete);
 417 
 418         // Invalidate any information cached for the classes just redefined.
 419         it = classToBytes.keySet().iterator();
 420         while (it.hasNext()) {
 421             ReferenceTypeImpl rti = (ReferenceTypeImpl)it.next();
 422             rti.noticeRedefineClass();
 423         }
 424     }
 425 
 426     public List<ThreadReference> allThreads() {
 427         validateVM();
 428         return state.allThreads();
 429     }
 430 
 431     public List<ThreadGroupReference> topLevelThreadGroups() {
 432         validateVM();
 433         return state.topLevelThreadGroups();
 434     }
 435 
 436     /*
 437      * Sends a command to the back end which is defined to do an
 438      * implicit vm-wide resume. The VM can no longer be considered
 439      * suspended, so certain cached data must be invalidated.
 440      */
 441     PacketStream sendResumingCommand(CommandSender sender) {
 442         return state.thawCommand(sender);
 443     }
 444 
 445     /*
 446      * The VM has been suspended. Additional caching can be done
 447      * as long as there are no pending resumes.
 448      */
 449     void notifySuspend() {
 450         state.freeze();
 451     }
 452 
 453     public void suspend() {
 454         validateVM();
 455         try {
 456             JDWP.VirtualMachine.Suspend.process(vm);
 457         } catch (JDWPException exc) {
 458             throw exc.toJDIException();
 459         }
 460         notifySuspend();
 461     }
 462 
 463     public void resume() {
 464         validateVM();
 465         CommandSender sender =
 466             new CommandSender() {
 467                 public PacketStream send() {
 468                     return JDWP.VirtualMachine.Resume.enqueueCommand(vm);
 469                 }
 470         };
 471         try {
 472             PacketStream stream = state.thawCommand(sender);
 473             JDWP.VirtualMachine.Resume.waitForReply(vm, stream);
 474         } catch (VMDisconnectedException exc) {
 475             /*
 476              * If the debugger makes a VMDeathRequest with SUSPEND_ALL,
 477              * then when it does an EventSet.resume after getting the
 478              * VMDeathEvent, the normal flow of events is that the
 479              * BE shuts down, but the waitForReply comes back ok.  In this
 480              * case, the run loop in TargetVM that is waiting for a packet
 481              * gets an EOF because the socket closes. It generates a
 482              * VMDisconnectedEvent and everyone is happy.
 483              * However, sometimes, the BE gets shutdown before this
 484              * waitForReply completes.  In this case, TargetVM.waitForReply
 485              * gets awakened with no reply and so gens a VMDisconnectedException
 486              * which is not what we want.  It might be possible to fix this
 487              * in the BE, but it is ok to just ignore the VMDisconnectedException
 488              * here.  This will allow the VMDisconnectedEvent to be generated
 489              * correctly.  And, if the debugger should happen to make another
 490              * request, it will get a VMDisconnectedException at that time.
 491              */
 492         } catch (JDWPException exc) {
 493             switch (exc.errorCode()) {
 494                 case JDWP.Error.VM_DEAD:
 495                     return;
 496                 default:
 497                     throw exc.toJDIException();
 498             }
 499         }
 500     }
 501 
 502     public EventQueue eventQueue() {
 503         /*
 504          * No VM validation here. We allow access to the event queue
 505          * after disconnection, so that there is access to the terminating
 506          * events.
 507          */
 508         return eventQueue;
 509     }
 510 
 511     public EventRequestManager eventRequestManager() {
 512         validateVM();
 513         return eventRequestManager;
 514     }
 515 
 516     EventRequestManagerImpl eventRequestManagerImpl() {
 517         return eventRequestManager;
 518     }
 519 
 520     public BooleanValue mirrorOf(boolean value) {
 521         validateVM();
 522         return new BooleanValueImpl(this,value);
 523     }
 524 
 525     public ByteValue mirrorOf(byte value) {
 526         validateVM();
 527         return new ByteValueImpl(this,value);
 528     }
 529 
 530     public CharValue mirrorOf(char value) {
 531         validateVM();
 532         return new CharValueImpl(this,value);
 533     }
 534 
 535     public ShortValue mirrorOf(short value) {
 536         validateVM();
 537         return new ShortValueImpl(this,value);
 538     }
 539 
 540     public IntegerValue mirrorOf(int value) {
 541         validateVM();
 542         return new IntegerValueImpl(this,value);
 543     }
 544 
 545     public LongValue mirrorOf(long value) {
 546         validateVM();
 547         return new LongValueImpl(this,value);
 548     }
 549 
 550     public FloatValue mirrorOf(float value) {
 551         validateVM();
 552         return new FloatValueImpl(this,value);
 553     }
 554 
 555     public DoubleValue mirrorOf(double value) {
 556         validateVM();
 557         return new DoubleValueImpl(this,value);
 558     }
 559 
 560     public StringReference mirrorOf(String value) {
 561         validateVM();
 562         try {
 563             return JDWP.VirtualMachine.CreateString.
 564                 process(vm, value).stringObject;
 565         } catch (JDWPException exc) {
 566             throw exc.toJDIException();
 567         }
 568     }
 569 
 570     public VoidValue mirrorOfVoid() {
 571         if (voidVal == null) {
 572             voidVal = new VoidValueImpl(this);
 573         }
 574         return voidVal;
 575     }
 576 
 577     public long[] instanceCounts(List<? extends ReferenceType> classes) {
 578         if (!canGetInstanceInfo()) {
 579             throw new UnsupportedOperationException(
 580                 "target does not support getting instances");
 581         }
 582         long[] retValue ;
 583         ReferenceTypeImpl[] rtArray = new ReferenceTypeImpl[classes.size()];
 584         int ii = 0;
 585         for (ReferenceType rti: classes) {
 586             validateMirror(rti);
 587             rtArray[ii++] = (ReferenceTypeImpl)rti;
 588         }
 589         try {
 590             retValue = JDWP.VirtualMachine.InstanceCounts.
 591                                 process(vm, rtArray).counts;
 592         } catch (JDWPException exc) {
 593             throw exc.toJDIException();
 594         }
 595 
 596         return retValue;
 597     }
 598 
 599     public void dispose() {
 600         validateVM();
 601         shutdown = true;
 602         try {
 603             JDWP.VirtualMachine.Dispose.process(vm);
 604         } catch (JDWPException exc) {
 605             throw exc.toJDIException();
 606         }
 607         target.stopListening();
 608     }
 609 
 610     public void exit(int exitCode) {
 611         validateVM();
 612         shutdown = true;
 613         try {
 614             JDWP.VirtualMachine.Exit.process(vm, exitCode);
 615         } catch (JDWPException exc) {
 616             throw exc.toJDIException();
 617         }
 618         target.stopListening();
 619     }
 620 
 621     public Process process() {
 622         validateVM();
 623         return process;
 624     }
 625 
 626     private JDWP.VirtualMachine.Version versionInfo() {
 627        try {
 628            if (versionInfo == null) {
 629                // Need not be synchronized since it is static information
 630                versionInfo = JDWP.VirtualMachine.Version.process(vm);
 631            }
 632            return versionInfo;
 633        } catch (JDWPException exc) {
 634            throw exc.toJDIException();
 635        }
 636     }
 637 
 638     public String description() {
 639         validateVM();
 640 
 641         return MessageFormat.format(vmManager.getString("version_format"),
 642                                     "" + vmManager.majorInterfaceVersion(),
 643                                     "" + vmManager.minorInterfaceVersion(),
 644                                      versionInfo().description);
 645     }
 646 
 647     public String version() {
 648         validateVM();
 649         return versionInfo().vmVersion;
 650     }
 651 
 652     public String name() {
 653         validateVM();
 654         return versionInfo().vmName;
 655     }
 656 
 657     public boolean canWatchFieldModification() {
 658         validateVM();
 659         return capabilities().canWatchFieldModification;
 660     }
 661 
 662     public boolean canWatchFieldAccess() {
 663         validateVM();
 664         return capabilities().canWatchFieldAccess;
 665     }
 666 
 667     public boolean canGetBytecodes() {
 668         validateVM();
 669         return capabilities().canGetBytecodes;
 670     }
 671 
 672     public boolean canGetSyntheticAttribute() {
 673         validateVM();
 674         return capabilities().canGetSyntheticAttribute;
 675     }
 676 
 677     public boolean canGetOwnedMonitorInfo() {
 678         validateVM();
 679         return capabilities().canGetOwnedMonitorInfo;
 680     }
 681 
 682     public boolean canGetCurrentContendedMonitor() {
 683         validateVM();
 684         return capabilities().canGetCurrentContendedMonitor;
 685     }
 686 
 687     public boolean canGetMonitorInfo() {
 688         validateVM();
 689         return capabilities().canGetMonitorInfo;
 690     }
 691 
 692     private boolean hasNewCapabilities() {
 693         return versionInfo().jdwpMajor > 1 ||
 694             versionInfo().jdwpMinor >= 4;
 695     }
 696 
 697     boolean canGet1_5LanguageFeatures() {
 698         return versionInfo().jdwpMajor > 1 ||
 699             versionInfo().jdwpMinor >= 5;
 700     }
 701 
 702     public boolean canUseInstanceFilters() {
 703         validateVM();
 704         return hasNewCapabilities() &&
 705             capabilitiesNew().canUseInstanceFilters;
 706     }
 707 
 708     public boolean canRedefineClasses() {
 709         validateVM();
 710         return hasNewCapabilities() &&
 711             capabilitiesNew().canRedefineClasses;
 712     }
 713 
 714     public boolean canAddMethod() {
 715         validateVM();
 716         return hasNewCapabilities() &&
 717             capabilitiesNew().canAddMethod;
 718     }
 719 
 720     public boolean canUnrestrictedlyRedefineClasses() {
 721         validateVM();
 722         return hasNewCapabilities() &&
 723             capabilitiesNew().canUnrestrictedlyRedefineClasses;
 724     }
 725 
 726     public boolean canPopFrames() {
 727         validateVM();
 728         return hasNewCapabilities() &&
 729             capabilitiesNew().canPopFrames;
 730     }
 731 
 732     public boolean canGetMethodReturnValues() {
 733         return versionInfo().jdwpMajor > 1 ||
 734             versionInfo().jdwpMinor >= 6;
 735     }
 736 
 737     public boolean canGetInstanceInfo() {
 738         if (versionInfo().jdwpMajor > 1 ||
 739             versionInfo().jdwpMinor >= 6) {
 740             validateVM();
 741             return hasNewCapabilities() &&
 742                 capabilitiesNew().canGetInstanceInfo;
 743         } else {
 744             return false;
 745         }
 746     }
 747 
 748     public boolean canUseSourceNameFilters() {
 749         return versionInfo().jdwpMajor > 1 ||
 750             versionInfo().jdwpMinor >= 6;
 751     }
 752 
 753     public boolean canForceEarlyReturn() {
 754         validateVM();
 755         return hasNewCapabilities() &&
 756             capabilitiesNew().canForceEarlyReturn;
 757     }
 758 
 759     public boolean canBeModified() {
 760         return true;
 761     }
 762 
 763     public boolean canGetSourceDebugExtension() {
 764         validateVM();
 765         return hasNewCapabilities() &&
 766             capabilitiesNew().canGetSourceDebugExtension;
 767     }
 768 
 769     public boolean canGetClassFileVersion() {
 770         return versionInfo().jdwpMajor > 1 ||
 771             versionInfo().jdwpMinor >= 6;
 772     }
 773 
 774     public boolean canGetConstantPool() {
 775         validateVM();
 776         return hasNewCapabilities() &&
 777             capabilitiesNew().canGetConstantPool;
 778     }
 779 
 780     public boolean canRequestVMDeathEvent() {
 781         validateVM();
 782         return hasNewCapabilities() &&
 783             capabilitiesNew().canRequestVMDeathEvent;
 784     }
 785 
 786     public boolean canRequestMonitorEvents() {
 787         validateVM();
 788         return hasNewCapabilities() &&
 789             capabilitiesNew().canRequestMonitorEvents;
 790     }
 791 
 792     public boolean canGetMonitorFrameInfo() {
 793         validateVM();
 794         return hasNewCapabilities() &&
 795             capabilitiesNew().canGetMonitorFrameInfo;
 796     }
 797 
 798     public boolean canGetModuleInfo() {
 799         validateVM();
 800         return versionInfo().jdwpMajor >= 9;
 801     }
 802 
 803     public void setDebugTraceMode(int traceFlags) {
 804         validateVM();
 805         this.traceFlags = traceFlags;
 806         this.traceReceives = (traceFlags & TRACE_RECEIVES) != 0;
 807     }
 808 
 809     void printTrace(String string) {
 810         System.err.println("[JDI: " + string + "]");
 811     }
 812 
 813     void printReceiveTrace(int depth, String string) {
 814         StringBuilder sb = new StringBuilder("Receiving:");
 815         for (int i = depth; i > 0; --i) {
 816             sb.append("    ");
 817         }
 818         sb.append(string);
 819         printTrace(sb.toString());
 820     }
 821 
 822     private synchronized ReferenceTypeImpl addReferenceType(long id,
 823                                                             int tag,
 824                                                             String signature) {
 825         if (typesByID == null) {
 826             initReferenceTypes();
 827         }
 828         ReferenceTypeImpl type = null;
 829         switch(tag) {
 830             case JDWP.TypeTag.CLASS:
 831                 type = new ClassTypeImpl(vm, id);
 832                 break;
 833             case JDWP.TypeTag.INTERFACE:
 834                 type = new InterfaceTypeImpl(vm, id);
 835                 break;
 836             case JDWP.TypeTag.ARRAY:
 837                 type = new ArrayTypeImpl(vm, id);
 838                 break;
 839             default:
 840                 throw new InternalException("Invalid reference type tag");
 841         }
 842 
 843         /*
 844          * If a signature was specified, make sure to set it ASAP, to
 845          * prevent any needless JDWP command to retrieve it. (for example,
 846          * typesBySignature.add needs the signature, to maintain proper
 847          * ordering.
 848          */
 849         if (signature != null) {
 850             type.setSignature(signature);
 851         }
 852 
 853         typesByID.put(id, type);
 854         typesBySignature.add(type);
 855 
 856         if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
 857            vm.printTrace("Caching new ReferenceType, sig=" + signature +
 858                          ", id=" + id);
 859         }
 860 
 861         return type;
 862     }
 863 
 864     synchronized void removeReferenceType(String signature) {
 865         if (typesByID == null) {
 866             return;
 867         }
 868         /*
 869          * There can be multiple classes with the same name. Since
 870          * we can't differentiate here, we first remove all
 871          * matching classes from our cache...
 872          */
 873         Iterator<ReferenceType> iter = typesBySignature.iterator();
 874         int matches = 0;
 875         while (iter.hasNext()) {
 876             ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
 877             int comp = signature.compareTo(type.signature());
 878             if (comp == 0) {
 879                 matches++;
 880                 iter.remove();
 881                 typesByID.remove(type.ref());
 882                 if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
 883                    vm.printTrace("Uncaching ReferenceType, sig=" + signature +
 884                                  ", id=" + type.ref());
 885                 }
 886                 // fix for 4359077, don't break out. list is no longer sorted
 887                 // in the order we think
 888             }
 889         }
 890 
 891         /*
 892          * ...and if there was more than one, re-retrieve the classes
 893          * with that name
 894          */
 895         if (matches > 1) {
 896             retrieveClassesBySignature(signature);
 897         }
 898     }
 899 
 900     private synchronized List<ReferenceType> findReferenceTypes(String signature) {
 901         if (typesByID == null) {
 902             return new ArrayList<>(0);
 903         }
 904         Iterator<ReferenceType> iter = typesBySignature.iterator();
 905         List<ReferenceType> list = new ArrayList<>();
 906         while (iter.hasNext()) {
 907             ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
 908             int comp = signature.compareTo(type.signature());
 909             if (comp == 0) {
 910                 list.add(type);
 911                 // fix for 4359077, don't break out. list is no longer sorted
 912                 // in the order we think
 913             }
 914         }
 915         return list;
 916     }
 917 
 918     private void initReferenceTypes() {
 919         typesByID = new HashMap<>(300);
 920         typesBySignature = new TreeSet<>();
 921     }
 922 
 923     ReferenceTypeImpl referenceType(long ref, byte tag) {
 924         return referenceType(ref, tag, null);
 925     }
 926 
 927     ClassTypeImpl classType(long ref) {
 928         return (ClassTypeImpl)referenceType(ref, JDWP.TypeTag.CLASS, null);
 929     }
 930 
 931     InterfaceTypeImpl interfaceType(long ref) {
 932         return (InterfaceTypeImpl)referenceType(ref, JDWP.TypeTag.INTERFACE, null);
 933     }
 934 
 935     ArrayTypeImpl arrayType(long ref) {
 936         return (ArrayTypeImpl)referenceType(ref, JDWP.TypeTag.ARRAY, null);
 937     }
 938 
 939     ReferenceTypeImpl referenceType(long id, int tag, String signature) {
 940         if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
 941             StringBuilder sb = new StringBuilder();
 942             sb.append("Looking up ");
 943             if (tag == JDWP.TypeTag.CLASS) {
 944                 sb.append("Class");
 945             } else if (tag == JDWP.TypeTag.INTERFACE) {
 946                 sb.append("Interface");
 947             } else if (tag == JDWP.TypeTag.ARRAY) {
 948                 sb.append("ArrayType");
 949             } else {
 950                 sb.append("UNKNOWN TAG: ").append(tag);
 951             }
 952             if (signature != null) {
 953                 sb.append(", signature='").append(signature).append('\'');
 954             }
 955             sb.append(", id=").append(id);
 956             vm.printTrace(sb.toString());
 957         }
 958         if (id == 0) {
 959             return null;
 960         } else {
 961             ReferenceTypeImpl retType = null;
 962             synchronized (this) {
 963                 if (typesByID != null) {
 964                     retType = (ReferenceTypeImpl)typesByID.get(id);
 965                 }
 966                 if (retType == null) {
 967                     retType = addReferenceType(id, tag, signature);
 968                 }
 969             }
 970             return retType;
 971         }
 972     }
 973 
 974     private JDWP.VirtualMachine.Capabilities capabilities() {
 975         if (capabilities == null) {
 976             try {
 977                 capabilities = JDWP.VirtualMachine
 978                                  .Capabilities.process(vm);
 979             } catch (JDWPException exc) {
 980                 throw exc.toJDIException();
 981             }
 982         }
 983         return capabilities;
 984     }
 985 
 986     private JDWP.VirtualMachine.CapabilitiesNew capabilitiesNew() {
 987         if (capabilitiesNew == null) {
 988             try {
 989                 capabilitiesNew = JDWP.VirtualMachine
 990                                  .CapabilitiesNew.process(vm);
 991             } catch (JDWPException exc) {
 992                 throw exc.toJDIException();
 993             }
 994         }
 995         return capabilitiesNew;
 996     }
 997 
 998     private synchronized ModuleReference addModule(long id) {
 999         if (modulesByID == null) {
1000             modulesByID = new HashMap<>(77);
1001         }
1002         ModuleReference module = new ModuleReferenceImpl(vm, id);
1003         modulesByID.put(id, module);
1004         return module;
1005     }
1006 
1007     ModuleReference getModule(long id) {
1008         if (id == 0) {
1009             return null;
1010         } else {
1011             ModuleReference module = null;
1012             synchronized (this) {
1013                 if (modulesByID != null) {
1014                     module = modulesByID.get(id);
1015                 }
1016                 if (module == null) {
1017                     module = addModule(id);
1018                 }
1019             }
1020             return module;
1021         }
1022     }
1023 
1024     private synchronized List<ModuleReference> retrieveAllModules() {
1025         ModuleReferenceImpl[] reqModules;
1026         try {
1027             reqModules = JDWP.VirtualMachine.AllModules.process(vm).modules;
1028         } catch (JDWPException exc) {
1029             throw exc.toJDIException();
1030         }
1031         ArrayList<ModuleReference> modules = new ArrayList<>();
1032         for (int i = 0; i < reqModules.length; i++) {
1033             long moduleRef = reqModules[i].ref();
1034             ModuleReference module = getModule(moduleRef);
1035             modules.add(module);
1036         }
1037         return modules;
1038     }
1039 
1040     private List<ReferenceType> retrieveClassesBySignature(String signature) {
1041         if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
1042             vm.printTrace("Retrieving matching ReferenceTypes, sig=" + signature);
1043         }
1044         JDWP.VirtualMachine.ClassesBySignature.ClassInfo[] cinfos;
1045         try {
1046             cinfos = JDWP.VirtualMachine.ClassesBySignature.
1047                                       process(vm, signature).classes;
1048         } catch (JDWPException exc) {
1049             throw exc.toJDIException();
1050         }
1051 
1052         int count = cinfos.length;
1053         List<ReferenceType> list = new ArrayList<>(count);
1054 
1055         // Hold lock during processing to improve performance
1056         synchronized (this) {
1057             for (int i = 0; i < count; i++) {
1058                 JDWP.VirtualMachine.ClassesBySignature.ClassInfo ci =
1059                                                                cinfos[i];
1060                 ReferenceTypeImpl type = referenceType(ci.typeID,
1061                                                        ci.refTypeTag,
1062                                                        signature);
1063                 type.setStatus(ci.status);
1064                 list.add(type);
1065             }
1066         }
1067         return list;
1068     }
1069 
1070     private void retrieveAllClasses1_4() {
1071         JDWP.VirtualMachine.AllClasses.ClassInfo[] cinfos;
1072         try {
1073             cinfos = JDWP.VirtualMachine.AllClasses.process(vm).classes;
1074         } catch (JDWPException exc) {
1075             throw exc.toJDIException();
1076         }
1077 
1078         // Hold lock during processing to improve performance
1079         // and to have safe check/set of retrievedAllTypes
1080         synchronized (this) {
1081             if (!retrievedAllTypes) {
1082                 // Number of classes
1083                 int count = cinfos.length;
1084                 for (int i = 0; i < count; i++) {
1085                     JDWP.VirtualMachine.AllClasses.ClassInfo ci = cinfos[i];
1086                     ReferenceTypeImpl type = referenceType(ci.typeID,
1087                                                            ci.refTypeTag,
1088                                                            ci.signature);
1089                     type.setStatus(ci.status);
1090                 }
1091                 retrievedAllTypes = true;
1092             }
1093         }
1094     }
1095 
1096     private void retrieveAllClasses() {
1097         if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
1098             vm.printTrace("Retrieving all ReferenceTypes");
1099         }
1100 
1101         if (!vm.canGet1_5LanguageFeatures()) {
1102             retrieveAllClasses1_4();
1103             return;
1104         }
1105 
1106         /*
1107          * To save time (assuming the caller will be
1108          * using then) we will get the generic sigs too.
1109          */
1110         JDWP.VirtualMachine.AllClassesWithGeneric.ClassInfo[] cinfos;
1111         try {
1112             cinfos = JDWP.VirtualMachine.AllClassesWithGeneric.process(vm).classes;
1113         } catch (JDWPException exc) {
1114             throw exc.toJDIException();
1115         }
1116 
1117         // Hold lock during processing to improve performance
1118         // and to have safe check/set of retrievedAllTypes
1119         synchronized (this) {
1120             if (!retrievedAllTypes) {
1121                 // Number of classes
1122                 int count = cinfos.length;
1123                 for (int i = 0; i < count; i++) {
1124                     JDWP.VirtualMachine.AllClassesWithGeneric.ClassInfo ci =
1125                                                                cinfos[i];
1126                     ReferenceTypeImpl type = referenceType(ci.typeID,
1127                                                            ci.refTypeTag,
1128                                                            ci.signature);
1129                     type.setGenericSignature(ci.genericSignature);
1130                     type.setStatus(ci.status);
1131                 }
1132                 retrievedAllTypes = true;
1133             }
1134         }
1135     }
1136 
1137     void sendToTarget(Packet packet) {
1138         target.send(packet);
1139     }
1140 
1141     void waitForTargetReply(Packet packet) {
1142         target.waitForReply(packet);
1143         /*
1144          * If any object disposes have been batched up, send them now.
1145          */
1146         processBatchedDisposes();
1147     }
1148 
1149     Type findBootType(String signature) throws ClassNotLoadedException {
1150         List<ReferenceType> types = retrieveClassesBySignature(signature);
1151         Iterator<ReferenceType> iter = types.iterator();
1152         while (iter.hasNext()) {
1153             ReferenceType type = iter.next();
1154             if (type.classLoader() == null) {
1155                 return type;
1156             }
1157         }
1158         JNITypeParser parser = new JNITypeParser(signature);
1159         throw new ClassNotLoadedException(parser.typeName(),
1160                                          "Type " + parser.typeName() + " not loaded");
1161     }
1162 
1163     BooleanType theBooleanType() {
1164         if (theBooleanType == null) {
1165             synchronized(this) {
1166                 if (theBooleanType == null) {
1167                     theBooleanType = new BooleanTypeImpl(this);
1168                 }
1169             }
1170         }
1171         return theBooleanType;
1172     }
1173 
1174     ByteType theByteType() {
1175         if (theByteType == null) {
1176             synchronized(this) {
1177                 if (theByteType == null) {
1178                     theByteType = new ByteTypeImpl(this);
1179                 }
1180             }
1181         }
1182         return theByteType;
1183     }
1184 
1185     CharType theCharType() {
1186         if (theCharType == null) {
1187             synchronized(this) {
1188                 if (theCharType == null) {
1189                     theCharType = new CharTypeImpl(this);
1190                 }
1191             }
1192         }
1193         return theCharType;
1194     }
1195 
1196     ShortType theShortType() {
1197         if (theShortType == null) {
1198             synchronized(this) {
1199                 if (theShortType == null) {
1200                     theShortType = new ShortTypeImpl(this);
1201                 }
1202             }
1203         }
1204         return theShortType;
1205     }
1206 
1207     IntegerType theIntegerType() {
1208         if (theIntegerType == null) {
1209             synchronized(this) {
1210                 if (theIntegerType == null) {
1211                     theIntegerType = new IntegerTypeImpl(this);
1212                 }
1213             }
1214         }
1215         return theIntegerType;
1216     }
1217 
1218     LongType theLongType() {
1219         if (theLongType == null) {
1220             synchronized(this) {
1221                 if (theLongType == null) {
1222                     theLongType = new LongTypeImpl(this);
1223                 }
1224             }
1225         }
1226         return theLongType;
1227     }
1228 
1229     FloatType theFloatType() {
1230         if (theFloatType == null) {
1231             synchronized(this) {
1232                 if (theFloatType == null) {
1233                     theFloatType = new FloatTypeImpl(this);
1234                 }
1235             }
1236         }
1237         return theFloatType;
1238     }
1239 
1240     DoubleType theDoubleType() {
1241         if (theDoubleType == null) {
1242             synchronized(this) {
1243                 if (theDoubleType == null) {
1244                     theDoubleType = new DoubleTypeImpl(this);
1245                 }
1246             }
1247         }
1248         return theDoubleType;
1249     }
1250 
1251     VoidType theVoidType() {
1252         if (theVoidType == null) {
1253             synchronized(this) {
1254                 if (theVoidType == null) {
1255                     theVoidType = new VoidTypeImpl(this);
1256                 }
1257             }
1258         }
1259         return theVoidType;
1260     }
1261 
1262     PrimitiveType primitiveTypeMirror(byte tag) {
1263         switch (tag) {
1264             case JDWP.Tag.BOOLEAN:
1265                 return theBooleanType();
1266             case JDWP.Tag.BYTE:
1267                 return theByteType();
1268             case JDWP.Tag.CHAR:
1269                 return theCharType();
1270             case JDWP.Tag.SHORT:
1271                 return theShortType();
1272             case JDWP.Tag.INT:
1273                 return theIntegerType();
1274             case JDWP.Tag.LONG:
1275                 return theLongType();
1276             case JDWP.Tag.FLOAT:
1277                 return theFloatType();
1278             case JDWP.Tag.DOUBLE:
1279                 return theDoubleType();
1280             default:
1281                 throw new IllegalArgumentException("Unrecognized primitive tag " + tag);
1282         }
1283     }
1284 
1285     private void processBatchedDisposes() {
1286         if (shutdown) {
1287             return;
1288         }
1289 
1290         JDWP.VirtualMachine.DisposeObjects.Request[] requests = null;
1291         synchronized(batchedDisposeRequests) {
1292             int size = batchedDisposeRequests.size();
1293             if (size >= DISPOSE_THRESHOLD) {
1294                 if ((traceFlags & TRACE_OBJREFS) != 0) {
1295                     printTrace("Dispose threashold reached. Will dispose "
1296                                + size + " object references...");
1297                 }
1298                 requests = new JDWP.VirtualMachine.DisposeObjects.Request[size];
1299                 for (int i = 0; i < requests.length; i++) {
1300                     SoftObjectReference ref = batchedDisposeRequests.get(i);
1301                     if ((traceFlags & TRACE_OBJREFS) != 0) {
1302                         printTrace("Disposing object " + ref.key().longValue() +
1303                                    " (ref count = " + ref.count() + ")");
1304                     }
1305 
1306                     // This is kludgy. We temporarily re-create an object
1307                     // reference so that we can correctly pass its id to the
1308                     // JDWP command.
1309                     requests[i] =
1310                         new JDWP.VirtualMachine.DisposeObjects.Request(
1311                             new ObjectReferenceImpl(this, ref.key().longValue()),
1312                             ref.count());
1313                 }
1314                 batchedDisposeRequests.clear();
1315             }
1316         }
1317         if (requests != null) {
1318             try {
1319                 JDWP.VirtualMachine.DisposeObjects.process(vm, requests);
1320             } catch (JDWPException exc) {
1321                 throw exc.toJDIException();
1322             }
1323         }
1324     }
1325 
1326     private void batchForDispose(SoftObjectReference ref) {
1327         if ((traceFlags & TRACE_OBJREFS) != 0) {
1328             printTrace("Batching object " + ref.key().longValue() +
1329                        " for dispose (ref count = " + ref.count() + ")");
1330         }
1331         batchedDisposeRequests.add(ref);
1332     }
1333 
1334     private void processQueue() {
1335         Reference<?> ref;
1336         //if ((traceFlags & TRACE_OBJREFS) != 0) {
1337         //    printTrace("Checking for softly reachable objects");
1338         //}
1339         while ((ref = referenceQueue.poll()) != null) {
1340             SoftObjectReference softRef = (SoftObjectReference)ref;
1341             removeObjectMirror(softRef);
1342             batchForDispose(softRef);
1343         }
1344     }
1345 
1346     synchronized ObjectReferenceImpl objectMirror(long id, int tag) {
1347 
1348         // Handle any queue elements that are not strongly reachable
1349         processQueue();
1350 
1351         if (id == 0) {
1352             return null;
1353         }
1354         ObjectReferenceImpl object = null;
1355         Long key = id;
1356 
1357         /*
1358          * Attempt to retrieve an existing object reference
1359          */
1360         SoftObjectReference ref = objectsByID.get(key);
1361         if (ref != null) {
1362             object = ref.object();
1363         }
1364 
1365         /*
1366          * If the object wasn't in the table, or it's soft reference was
1367          * cleared, create a new instance.
1368          */
1369         if (object == null) {
1370             switch (tag) {
1371                 case JDWP.Tag.OBJECT:
1372                     object = new ObjectReferenceImpl(vm, id);
1373                     break;
1374                 case JDWP.Tag.STRING:
1375                     object = new StringReferenceImpl(vm, id);
1376                     break;
1377                 case JDWP.Tag.ARRAY:
1378                     object = new ArrayReferenceImpl(vm, id);
1379                     break;
1380                 case JDWP.Tag.THREAD:
1381                     ThreadReferenceImpl thread =
1382                         new ThreadReferenceImpl(vm, id);
1383                     thread.addListener(this);
1384                     object = thread;
1385                     break;
1386                 case JDWP.Tag.THREAD_GROUP:
1387                     object = new ThreadGroupReferenceImpl(vm, id);
1388                     break;
1389                 case JDWP.Tag.CLASS_LOADER:
1390                     object = new ClassLoaderReferenceImpl(vm, id);
1391                     break;
1392                 case JDWP.Tag.CLASS_OBJECT:
1393                     object = new ClassObjectReferenceImpl(vm, id);
1394                     break;
1395                 default:
1396                     throw new IllegalArgumentException("Invalid object tag: " + tag);
1397             }
1398             ref = new SoftObjectReference(key, object, referenceQueue);
1399 
1400             /*
1401              * If there was no previous entry in the table, we add one here
1402              * If the previous entry was cleared, we replace it here.
1403              */
1404             objectsByID.put(key, ref);
1405             if ((traceFlags & TRACE_OBJREFS) != 0) {
1406                 printTrace("Creating new " +
1407                            object.getClass().getName() + " (id = " + id + ")");
1408             }
1409         } else {
1410             ref.incrementCount();
1411         }
1412 
1413         return object;
1414     }
1415 
1416     synchronized void removeObjectMirror(ObjectReferenceImpl object) {
1417         // Handle any queue elements that are not strongly reachable
1418         processQueue();
1419 
1420         SoftObjectReference ref = objectsByID.remove(object.ref());
1421         if (ref != null) {
1422             batchForDispose(ref);
1423         } else {
1424             /*
1425              * If there's a live ObjectReference about, it better be part
1426              * of the cache.
1427              */
1428             throw new InternalException("ObjectReference " + object.ref() +
1429                                         " not found in object cache");
1430         }
1431     }
1432 
1433     synchronized void removeObjectMirror(SoftObjectReference ref) {
1434         /*
1435          * This will remove the soft reference if it has not been
1436          * replaced in the cache.
1437          */
1438         objectsByID.remove(ref.key());
1439     }
1440 
1441     ObjectReferenceImpl objectMirror(long id) {
1442         return objectMirror(id, JDWP.Tag.OBJECT);
1443     }
1444 
1445     StringReferenceImpl stringMirror(long id) {
1446         return (StringReferenceImpl)objectMirror(id, JDWP.Tag.STRING);
1447     }
1448 
1449     ArrayReferenceImpl arrayMirror(long id) {
1450        return (ArrayReferenceImpl)objectMirror(id, JDWP.Tag.ARRAY);
1451     }
1452 
1453     ThreadReferenceImpl threadMirror(long id) {
1454         return (ThreadReferenceImpl)objectMirror(id, JDWP.Tag.THREAD);
1455     }
1456 
1457     ThreadGroupReferenceImpl threadGroupMirror(long id) {
1458         return (ThreadGroupReferenceImpl)objectMirror(id,
1459                                                       JDWP.Tag.THREAD_GROUP);
1460     }
1461 
1462     ClassLoaderReferenceImpl classLoaderMirror(long id) {
1463         return (ClassLoaderReferenceImpl)objectMirror(id,
1464                                                       JDWP.Tag.CLASS_LOADER);
1465     }
1466 
1467     ClassObjectReferenceImpl classObjectMirror(long id) {
1468         return (ClassObjectReferenceImpl)objectMirror(id,
1469                                                       JDWP.Tag.CLASS_OBJECT);
1470     }
1471 
1472     ModuleReferenceImpl moduleMirror(long id) {
1473         return (ModuleReferenceImpl)getModule(id);
1474     }
1475 
1476     /*
1477      * Implementation of PathSearchingVirtualMachine
1478      */
1479     private JDWP.VirtualMachine.ClassPaths getClasspath() {
1480         if (pathInfo == null) {
1481             try {
1482                 pathInfo = JDWP.VirtualMachine.ClassPaths.process(vm);
1483             } catch (JDWPException exc) {
1484                 throw exc.toJDIException();
1485             }
1486         }
1487         return pathInfo;
1488     }
1489 
1490    public List<String> classPath() {
1491        return Arrays.asList(getClasspath().classpaths);
1492    }
1493 
1494    public List<String> bootClassPath() {
1495        return Collections.emptyList();
1496    }
1497 
1498    public String baseDirectory() {
1499        return getClasspath().baseDir;
1500    }
1501 
1502     public void setDefaultStratum(String stratum) {
1503         defaultStratum = stratum;
1504         if (stratum == null) {
1505             stratum = "";
1506         }
1507         try {
1508             JDWP.VirtualMachine.SetDefaultStratum.process(vm,
1509                                                           stratum);
1510         } catch (JDWPException exc) {
1511             throw exc.toJDIException();
1512         }
1513     }
1514 
1515     public String getDefaultStratum() {
1516         return defaultStratum;
1517     }
1518 
1519     ThreadGroup threadGroupForJDI() {
1520         return threadGroupForJDI;
1521     }
1522 
1523    static private class SoftObjectReference extends SoftReference<ObjectReferenceImpl> {
1524        int count;
1525        Long key;
1526 
1527        SoftObjectReference(Long key, ObjectReferenceImpl mirror,
1528                            ReferenceQueue<ObjectReferenceImpl> queue) {
1529            super(mirror, queue);
1530            this.count = 1;
1531            this.key = key;
1532        }
1533 
1534        int count() {
1535            return count;
1536        }
1537 
1538        void incrementCount() {
1539            count++;
1540        }
1541 
1542        Long key() {
1543            return key;
1544        }
1545 
1546        ObjectReferenceImpl object() {
1547            return get();
1548        }
1549    }
1550 }