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