1/* 2 * Copyright (c) 1998, 2016, 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 26package com.sun.tools.jdi; 27 28import com.sun.jdi.*; 29import com.sun.jdi.ModuleReference; 30import com.sun.jdi.connect.spi.Connection; 31import com.sun.jdi.request.EventRequestManager; 32import com.sun.jdi.request.EventRequest; 33import com.sun.jdi.request.BreakpointRequest; 34import com.sun.jdi.event.EventQueue; 35 36import java.util.*; 37import java.text.MessageFormat; 38import java.lang.ref.ReferenceQueue; 39import java.lang.ref.Reference; 40import java.lang.ref.SoftReference; 41import java.lang.ref.WeakReference; 42 43class 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, ModuleReference> 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<ModuleReference> allModules() { 274 validateVM(); 275 List<ModuleReference> 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 601 public String description() { 602 validateVM(); 603 604 return MessageFormat.format(vmManager.getString("version_format"), 605 "" + vmManager.majorInterfaceVersion(), 606 "" + vmManager.minorInterfaceVersion(), 607 versionInfo().description); 608 } 609 610 public String version() { 611 validateVM(); 612 return versionInfo().vmVersion; 613 } 614 615 public String name() { 616 validateVM(); 617 return versionInfo().vmName; 618 } 619 620 public boolean canWatchFieldModification() { 621 validateVM(); 622 return capabilities().canWatchFieldModification; 623 } 624 public boolean canWatchFieldAccess() { 625 validateVM(); 626 return capabilities().canWatchFieldAccess; 627 } 628 public boolean canGetBytecodes() { 629 validateVM(); 630 return capabilities().canGetBytecodes; 631 } 632 public boolean canGetSyntheticAttribute() { 633 validateVM(); 634 return capabilities().canGetSyntheticAttribute; 635 } 636 public boolean canGetOwnedMonitorInfo() { 637 validateVM(); 638 return capabilities().canGetOwnedMonitorInfo; 639 } 640 public boolean canGetCurrentContendedMonitor() { 641 validateVM(); 642 return capabilities().canGetCurrentContendedMonitor; 643 } 644 public boolean canGetMonitorInfo() { 645 validateVM(); 646 return capabilities().canGetMonitorInfo; 647 } 648 649 private boolean hasNewCapabilities() { 650 return versionInfo().jdwpMajor > 1 || 651 versionInfo().jdwpMinor >= 4; 652 } 653 654 boolean canGet1_5LanguageFeatures() { 655 return versionInfo().jdwpMajor > 1 || 656 versionInfo().jdwpMinor >= 5; 657 } 658 659 public boolean canUseInstanceFilters() { 660 validateVM(); 661 return hasNewCapabilities() && 662 capabilitiesNew().canUseInstanceFilters; 663 } 664 public boolean canRedefineClasses() { 665 validateVM(); 666 return hasNewCapabilities() && 667 capabilitiesNew().canRedefineClasses; 668 } 669 public boolean canAddMethod() { 670 validateVM(); 671 return hasNewCapabilities() && 672 capabilitiesNew().canAddMethod; 673 } 674 public boolean canUnrestrictedlyRedefineClasses() { 675 validateVM(); 676 return hasNewCapabilities() && 677 capabilitiesNew().canUnrestrictedlyRedefineClasses; 678 } 679 public boolean canPopFrames() { 680 validateVM(); 681 return hasNewCapabilities() && 682 capabilitiesNew().canPopFrames; 683 } 684 public boolean canGetMethodReturnValues() { 685 return versionInfo().jdwpMajor > 1 || 686 versionInfo().jdwpMinor >= 6; 687 } 688 public boolean canGetInstanceInfo() { 689 if (versionInfo().jdwpMajor > 1 || 690 versionInfo().jdwpMinor >= 6) { 691 validateVM(); 692 return hasNewCapabilities() && 693 capabilitiesNew().canGetInstanceInfo; 694 } else { 695 return false; 696 } 697 } 698 public boolean canUseSourceNameFilters() { 699 return versionInfo().jdwpMajor > 1 || 700 versionInfo().jdwpMinor >= 6; 701 } 702 public boolean canForceEarlyReturn() { 703 validateVM(); 704 return hasNewCapabilities() && 705 capabilitiesNew().canForceEarlyReturn; 706 } 707 public boolean canBeModified() { 708 return true; 709 } 710 public boolean canGetSourceDebugExtension() { 711 validateVM(); 712 return hasNewCapabilities() && 713 capabilitiesNew().canGetSourceDebugExtension; 714 } 715 public boolean canGetClassFileVersion() { 716 return versionInfo().jdwpMajor > 1 || 717 versionInfo().jdwpMinor >= 6; 718 } 719 public boolean canGetConstantPool() { 720 validateVM(); 721 return hasNewCapabilities() && 722 capabilitiesNew().canGetConstantPool; 723 } 724 public boolean canRequestVMDeathEvent() { 725 validateVM(); 726 return hasNewCapabilities() && 727 capabilitiesNew().canRequestVMDeathEvent; 728 } 729 public boolean canRequestMonitorEvents() { 730 validateVM(); 731 return hasNewCapabilities() && 732 capabilitiesNew().canRequestMonitorEvents; 733 } 734 public boolean canGetMonitorFrameInfo() { 735 validateVM(); 736 return hasNewCapabilities() && 737 capabilitiesNew().canGetMonitorFrameInfo; 738 } 739 public boolean canGetModuleInfo() { 740 validateVM(); 741 return versionInfo().jdwpMajor >= 9; 742 } 743 744 public void setDebugTraceMode(int traceFlags) { 745 validateVM(); 746 this.traceFlags = traceFlags; 747 this.traceReceives = (traceFlags & TRACE_RECEIVES) != 0; 748 } 749 750 void printTrace(String string) { 751 System.err.println("[JDI: " + string + "]"); 752 } 753 754 void printReceiveTrace(int depth, String string) { 755 StringBuilder sb = new StringBuilder("Receiving:"); 756 for (int i = depth; i > 0; --i) { 757 sb.append(" "); 758 } 759 sb.append(string); 760 printTrace(sb.toString()); 761 } 762 763 private synchronized ReferenceTypeImpl addReferenceType(long id, 764 int tag, 765 String signature) { 766 if (typesByID == null) { 767 initReferenceTypes(); 768 } 769 ReferenceTypeImpl type = null; 770 switch(tag) { 771 case JDWP.TypeTag.CLASS: 772 type = new ClassTypeImpl(vm, id); 773 break; 774 case JDWP.TypeTag.INTERFACE: 775 type = new InterfaceTypeImpl(vm, id); 776 break; 777 case JDWP.TypeTag.ARRAY: 778 type = new ArrayTypeImpl(vm, id); 779 break; 780 default: 781 throw new InternalException("Invalid reference type tag"); 782 } 783 784 /* 785 * If a signature was specified, make sure to set it ASAP, to 786 * prevent any needless JDWP command to retrieve it. (for example, 787 * typesBySignature.add needs the signature, to maintain proper 788 * ordering. 789 */ 790 if (signature != null) { 791 type.setSignature(signature); 792 } 793 794 typesByID.put(id, type); 795 typesBySignature.add(type); 796 797 if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { 798 vm.printTrace("Caching new ReferenceType, sig=" + signature + 799 ", id=" + id); 800 } 801 802 return type; 803 } 804 805 synchronized void removeReferenceType(String signature) { 806 if (typesByID == null) { 807 return; 808 } 809 /* 810 * There can be multiple classes with the same name. Since 811 * we can't differentiate here, we first remove all 812 * matching classes from our cache... 813 */ 814 Iterator<ReferenceType> iter = typesBySignature.iterator(); 815 int matches = 0; 816 while (iter.hasNext()) { 817 ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next(); 818 int comp = signature.compareTo(type.signature()); 819 if (comp == 0) { 820 matches++; 821 iter.remove(); 822 typesByID.remove(type.ref()); 823 if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { 824 vm.printTrace("Uncaching ReferenceType, sig=" + signature + 825 ", id=" + type.ref()); 826 } 827/* fix for 4359077 , don't break out. list is no longer sorted 828 in the order we think 829 */ 830 } 831 } 832 833 /* 834 * ...and if there was more than one, re-retrieve the classes 835 * with that name 836 */ 837 if (matches > 1) { 838 retrieveClassesBySignature(signature); 839 } 840 } 841 842 private synchronized List<ReferenceType> findReferenceTypes(String signature) { 843 if (typesByID == null) { 844 return new ArrayList<ReferenceType>(0); 845 } 846 Iterator<ReferenceType> iter = typesBySignature.iterator(); 847 List<ReferenceType> list = new ArrayList<ReferenceType>(); 848 while (iter.hasNext()) { 849 ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next(); 850 int comp = signature.compareTo(type.signature()); 851 if (comp == 0) { 852 list.add(type); 853/* fix for 4359077 , don't break out. list is no longer sorted 854 in the order we think 855 */ 856 } 857 } 858 return list; 859 } 860 861 private void initReferenceTypes() { 862 typesByID = new HashMap<Long, ReferenceType>(300); 863 typesBySignature = new TreeSet<ReferenceType>(); 864 } 865 866 ReferenceTypeImpl referenceType(long ref, byte tag) { 867 return referenceType(ref, tag, null); 868 } 869 870 ClassTypeImpl classType(long ref) { 871 return (ClassTypeImpl)referenceType(ref, JDWP.TypeTag.CLASS, null); 872 } 873 874 InterfaceTypeImpl interfaceType(long ref) { 875 return (InterfaceTypeImpl)referenceType(ref, JDWP.TypeTag.INTERFACE, null); 876 } 877 878 ArrayTypeImpl arrayType(long ref) { 879 return (ArrayTypeImpl)referenceType(ref, JDWP.TypeTag.ARRAY, null); 880 } 881 882 ReferenceTypeImpl referenceType(long id, int tag, 883 String signature) { 884 if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { 885 StringBuilder sb = new StringBuilder(); 886 sb.append("Looking up "); 887 if (tag == JDWP.TypeTag.CLASS) { 888 sb.append("Class"); 889 } else if (tag == JDWP.TypeTag.INTERFACE) { 890 sb.append("Interface"); 891 } else if (tag == JDWP.TypeTag.ARRAY) { 892 sb.append("ArrayType"); 893 } else { 894 sb.append("UNKNOWN TAG: ").append(tag); 895 } 896 if (signature != null) { 897 sb.append(", signature='").append(signature).append('\''); 898 } 899 sb.append(", id=").append(id); 900 vm.printTrace(sb.toString()); 901 } 902 if (id == 0) { 903 return null; 904 } else { 905 ReferenceTypeImpl retType = null; 906 synchronized (this) { 907 if (typesByID != null) { 908 retType = (ReferenceTypeImpl)typesByID.get(id); 909 } 910 if (retType == null) { 911 retType = addReferenceType(id, tag, signature); 912 } 913 } 914 return retType; 915 } 916 } 917 918 private JDWP.VirtualMachine.Capabilities capabilities() { 919 if (capabilities == null) { 920 try { 921 capabilities = JDWP.VirtualMachine 922 .Capabilities.process(vm); 923 } catch (JDWPException exc) { 924 throw exc.toJDIException(); 925 } 926 } 927 return capabilities; 928 } 929 930 private JDWP.VirtualMachine.CapabilitiesNew capabilitiesNew() { 931 if (capabilitiesNew == null) { 932 try { 933 capabilitiesNew = JDWP.VirtualMachine 934 .CapabilitiesNew.process(vm); 935 } catch (JDWPException exc) { 936 throw exc.toJDIException(); 937 } 938 } 939 return capabilitiesNew; 940 } 941 942 private synchronized ModuleReference addModule(long id) { 943 if (modulesByID == null) { 944 modulesByID = new HashMap<Long, ModuleReference>(77); 945 } 946 ModuleReference module = new ModuleReferenceImpl(vm, id); 947 modulesByID.put(id, module); 948 return module; 949 } 950 951 ModuleReference getModule(long id) { 952 if (id == 0) { 953 return null; 954 } else { 955 ModuleReference module = null; 956 synchronized (this) { 957 if (modulesByID != null) { 958 module = modulesByID.get(id); 959 } 960 if (module == null) { 961 module = addModule(id); 962 } 963 } 964 return module; 965 } 966 } 967 968 private synchronized List<ModuleReference> retrieveAllModules() { 969 ModuleReferenceImpl[] reqModules; 970 try { 971 reqModules = JDWP.VirtualMachine.AllModules.process(vm).modules; 972 } catch (JDWPException exc) { 973 throw exc.toJDIException(); 974 } 975 ArrayList<ModuleReference> modules = new ArrayList<>(); 976 for (int i = 0; i < reqModules.length; i++) { 977 long moduleRef = reqModules[i].ref(); 978 ModuleReference module = getModule(moduleRef); 979 modules.add(module); 980 } 981 return modules; 982 } 983 984 private List<ReferenceType> retrieveClassesBySignature(String signature) { 985 if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { 986 vm.printTrace("Retrieving matching ReferenceTypes, sig=" + signature); 987 } 988 JDWP.VirtualMachine.ClassesBySignature.ClassInfo[] cinfos; 989 try { 990 cinfos = JDWP.VirtualMachine.ClassesBySignature. 991 process(vm, signature).classes; 992 } catch (JDWPException exc) { 993 throw exc.toJDIException(); 994 } 995 996 int count = cinfos.length; 997 List<ReferenceType> list = new ArrayList<ReferenceType>(count); 998 999 // Hold lock during processing to improve performance 1000 synchronized (this) { 1001 for (int i = 0; i < count; i++) { 1002 JDWP.VirtualMachine.ClassesBySignature.ClassInfo ci = 1003 cinfos[i]; 1004 ReferenceTypeImpl type = referenceType(ci.typeID, 1005 ci.refTypeTag, 1006 signature); 1007 type.setStatus(ci.status); 1008 list.add(type); 1009 } 1010 } 1011 return list; 1012 } 1013 1014 private void retrieveAllClasses1_4() { 1015 JDWP.VirtualMachine.AllClasses.ClassInfo[] cinfos; 1016 try { 1017 cinfos = JDWP.VirtualMachine.AllClasses.process(vm).classes; 1018 } catch (JDWPException exc) { 1019 throw exc.toJDIException(); 1020 } 1021 1022 // Hold lock during processing to improve performance 1023 // and to have safe check/set of retrievedAllTypes 1024 synchronized (this) { 1025 if (!retrievedAllTypes) { 1026 // Number of classes 1027 int count = cinfos.length; 1028 for (int i=0; i<count; i++) { 1029 JDWP.VirtualMachine.AllClasses.ClassInfo ci = 1030 cinfos[i]; 1031 ReferenceTypeImpl type = referenceType(ci.typeID, 1032 ci.refTypeTag, 1033 ci.signature); 1034 type.setStatus(ci.status); 1035 } 1036 retrievedAllTypes = true; 1037 } 1038 } 1039 } 1040 1041 private void retrieveAllClasses() { 1042 if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { 1043 vm.printTrace("Retrieving all ReferenceTypes"); 1044 } 1045 1046 if (!vm.canGet1_5LanguageFeatures()) { 1047 retrieveAllClasses1_4(); 1048 return; 1049 } 1050 1051 /* 1052 * To save time (assuming the caller will be 1053 * using then) we will get the generic sigs too. 1054 */ 1055 1056 JDWP.VirtualMachine.AllClassesWithGeneric.ClassInfo[] cinfos; 1057 try { 1058 cinfos = JDWP.VirtualMachine.AllClassesWithGeneric.process(vm).classes; 1059 } catch (JDWPException exc) { 1060 throw exc.toJDIException(); 1061 } 1062 1063 // Hold lock during processing to improve performance 1064 // and to have safe check/set of retrievedAllTypes 1065 synchronized (this) { 1066 if (!retrievedAllTypes) { 1067 // Number of classes 1068 int count = cinfos.length; 1069 for (int i=0; i<count; i++) { 1070 JDWP.VirtualMachine.AllClassesWithGeneric.ClassInfo ci = 1071 cinfos[i]; 1072 ReferenceTypeImpl type = referenceType(ci.typeID, 1073 ci.refTypeTag, 1074 ci.signature); 1075 type.setGenericSignature(ci.genericSignature); 1076 type.setStatus(ci.status); 1077 } 1078 retrievedAllTypes = true; 1079 } 1080 } 1081 } 1082 1083 void sendToTarget(Packet packet) { 1084 target.send(packet); 1085 } 1086 1087 void waitForTargetReply(Packet packet) { 1088 target.waitForReply(packet); 1089 /* 1090 * If any object disposes have been batched up, send them now. 1091 */ 1092 processBatchedDisposes(); 1093 } 1094 1095 Type findBootType(String signature) throws ClassNotLoadedException { 1096 List<ReferenceType> types = retrieveClassesBySignature(signature); 1097 Iterator<ReferenceType> iter = types.iterator(); 1098 while (iter.hasNext()) { 1099 ReferenceType type = iter.next(); 1100 if (type.classLoader() == null) { 1101 return type; 1102 } 1103 } 1104 JNITypeParser parser = new JNITypeParser(signature); 1105 throw new ClassNotLoadedException(parser.typeName(), 1106 "Type " + parser.typeName() + " not loaded"); 1107 } 1108 1109 BooleanType theBooleanType() { 1110 if (theBooleanType == null) { 1111 synchronized(this) { 1112 if (theBooleanType == null) { 1113 theBooleanType = new BooleanTypeImpl(this); 1114 } 1115 } 1116 } 1117 return theBooleanType; 1118 } 1119 1120 ByteType theByteType() { 1121 if (theByteType == null) { 1122 synchronized(this) { 1123 if (theByteType == null) { 1124 theByteType = new ByteTypeImpl(this); 1125 } 1126 } 1127 } 1128 return theByteType; 1129 } 1130 1131 CharType theCharType() { 1132 if (theCharType == null) { 1133 synchronized(this) { 1134 if (theCharType == null) { 1135 theCharType = new CharTypeImpl(this); 1136 } 1137 } 1138 } 1139 return theCharType; 1140 } 1141 1142 ShortType theShortType() { 1143 if (theShortType == null) { 1144 synchronized(this) { 1145 if (theShortType == null) { 1146 theShortType = new ShortTypeImpl(this); 1147 } 1148 } 1149 } 1150 return theShortType; 1151 } 1152 1153 IntegerType theIntegerType() { 1154 if (theIntegerType == null) { 1155 synchronized(this) { 1156 if (theIntegerType == null) { 1157 theIntegerType = new IntegerTypeImpl(this); 1158 } 1159 } 1160 } 1161 return theIntegerType; 1162 } 1163 1164 LongType theLongType() { 1165 if (theLongType == null) { 1166 synchronized(this) { 1167 if (theLongType == null) { 1168 theLongType = new LongTypeImpl(this); 1169 } 1170 } 1171 } 1172 return theLongType; 1173 } 1174 1175 FloatType theFloatType() { 1176 if (theFloatType == null) { 1177 synchronized(this) { 1178 if (theFloatType == null) { 1179 theFloatType = new FloatTypeImpl(this); 1180 } 1181 } 1182 } 1183 return theFloatType; 1184 } 1185 1186 DoubleType theDoubleType() { 1187 if (theDoubleType == null) { 1188 synchronized(this) { 1189 if (theDoubleType == null) { 1190 theDoubleType = new DoubleTypeImpl(this); 1191 } 1192 } 1193 } 1194 return theDoubleType; 1195 } 1196 1197 VoidType theVoidType() { 1198 if (theVoidType == null) { 1199 synchronized(this) { 1200 if (theVoidType == null) { 1201 theVoidType = new VoidTypeImpl(this); 1202 } 1203 } 1204 } 1205 return theVoidType; 1206 } 1207 1208 PrimitiveType primitiveTypeMirror(byte tag) { 1209 switch (tag) { 1210 case JDWP.Tag.BOOLEAN: 1211 return theBooleanType(); 1212 case JDWP.Tag.BYTE: 1213 return theByteType(); 1214 case JDWP.Tag.CHAR: 1215 return theCharType(); 1216 case JDWP.Tag.SHORT: 1217 return theShortType(); 1218 case JDWP.Tag.INT: 1219 return theIntegerType(); 1220 case JDWP.Tag.LONG: 1221 return theLongType(); 1222 case JDWP.Tag.FLOAT: 1223 return theFloatType(); 1224 case JDWP.Tag.DOUBLE: 1225 return theDoubleType(); 1226 default: 1227 throw new IllegalArgumentException("Unrecognized primitive tag " + tag); 1228 } 1229 } 1230 1231 private void processBatchedDisposes() { 1232 if (shutdown) { 1233 return; 1234 } 1235 1236 JDWP.VirtualMachine.DisposeObjects.Request[] requests = null; 1237 synchronized(batchedDisposeRequests) { 1238 int size = batchedDisposeRequests.size(); 1239 if (size >= DISPOSE_THRESHOLD) { 1240 if ((traceFlags & TRACE_OBJREFS) != 0) { 1241 printTrace("Dispose threashold reached. Will dispose " 1242 + size + " object references..."); 1243 } 1244 requests = new JDWP.VirtualMachine.DisposeObjects.Request[size]; 1245 for (int i = 0; i < requests.length; i++) { 1246 SoftObjectReference ref = batchedDisposeRequests.get(i); 1247 if ((traceFlags & TRACE_OBJREFS) != 0) { 1248 printTrace("Disposing object " + ref.key().longValue() + 1249 " (ref count = " + ref.count() + ")"); 1250 } 1251 1252 // This is kludgy. We temporarily re-create an object 1253 // reference so that we can correctly pass its id to the 1254 // JDWP command. 1255 requests[i] = 1256 new JDWP.VirtualMachine.DisposeObjects.Request( 1257 new ObjectReferenceImpl(this, ref.key().longValue()), 1258 ref.count()); 1259 } 1260 batchedDisposeRequests.clear(); 1261 } 1262 } 1263 if (requests != null) { 1264 try { 1265 JDWP.VirtualMachine.DisposeObjects.process(vm, requests); 1266 } catch (JDWPException exc) { 1267 throw exc.toJDIException(); 1268 } 1269 } 1270 } 1271 1272 private void batchForDispose(SoftObjectReference ref) { 1273 if ((traceFlags & TRACE_OBJREFS) != 0) { 1274 printTrace("Batching object " + ref.key().longValue() + 1275 " for dispose (ref count = " + ref.count() + ")"); 1276 } 1277 batchedDisposeRequests.add(ref); 1278 } 1279 1280 private void processQueue() { 1281 Reference<?> ref; 1282 //if ((traceFlags & TRACE_OBJREFS) != 0) { 1283 // printTrace("Checking for softly reachable objects"); 1284 //} 1285 while ((ref = referenceQueue.poll()) != null) { 1286 SoftObjectReference softRef = (SoftObjectReference)ref; 1287 removeObjectMirror(softRef); 1288 batchForDispose(softRef); 1289 } 1290 } 1291 1292 synchronized ObjectReferenceImpl objectMirror(long id, int tag) { 1293 1294 // Handle any queue elements that are not strongly reachable 1295 processQueue(); 1296 1297 if (id == 0) { 1298 return null; 1299 } 1300 ObjectReferenceImpl object = null; 1301 Long key = id; 1302 1303 /* 1304 * Attempt to retrieve an existing object reference 1305 */ 1306 SoftObjectReference ref = objectsByID.get(key); 1307 if (ref != null) { 1308 object = ref.object(); 1309 } 1310 1311 /* 1312 * If the object wasn't in the table, or it's soft reference was 1313 * cleared, create a new instance. 1314 */ 1315 if (object == null) { 1316 switch (tag) { 1317 case JDWP.Tag.OBJECT: 1318 object = new ObjectReferenceImpl(vm, id); 1319 break; 1320 case JDWP.Tag.STRING: 1321 object = new StringReferenceImpl(vm, id); 1322 break; 1323 case JDWP.Tag.ARRAY: 1324 object = new ArrayReferenceImpl(vm, id); 1325 break; 1326 case JDWP.Tag.THREAD: 1327 ThreadReferenceImpl thread = 1328 new ThreadReferenceImpl(vm, id); 1329 thread.addListener(this); 1330 object = thread; 1331 break; 1332 case JDWP.Tag.THREAD_GROUP: 1333 object = new ThreadGroupReferenceImpl(vm, id); 1334 break; 1335 case JDWP.Tag.CLASS_LOADER: 1336 object = new ClassLoaderReferenceImpl(vm, id); 1337 break; 1338 case JDWP.Tag.CLASS_OBJECT: 1339 object = new ClassObjectReferenceImpl(vm, id); 1340 break; 1341 default: 1342 throw new IllegalArgumentException("Invalid object tag: " + tag); 1343 } 1344 ref = new SoftObjectReference(key, object, referenceQueue); 1345 1346 /* 1347 * If there was no previous entry in the table, we add one here 1348 * If the previous entry was cleared, we replace it here. 1349 */ 1350 objectsByID.put(key, ref); 1351 if ((traceFlags & TRACE_OBJREFS) != 0) { 1352 printTrace("Creating new " + 1353 object.getClass().getName() + " (id = " + id + ")"); 1354 } 1355 } else { 1356 ref.incrementCount(); 1357 } 1358 1359 return object; 1360 } 1361 1362 synchronized void removeObjectMirror(ObjectReferenceImpl object) { 1363 1364 // Handle any queue elements that are not strongly reachable 1365 processQueue(); 1366 1367 SoftObjectReference ref = objectsByID.remove(object.ref()); 1368 if (ref != null) { 1369 batchForDispose(ref); 1370 } else { 1371 /* 1372 * If there's a live ObjectReference about, it better be part 1373 * of the cache. 1374 */ 1375 throw new InternalException("ObjectReference " + object.ref() + 1376 " not found in object cache"); 1377 } 1378 } 1379 1380 synchronized void removeObjectMirror(SoftObjectReference ref) { 1381 /* 1382 * This will remove the soft reference if it has not been 1383 * replaced in the cache. 1384 */ 1385 objectsByID.remove(ref.key()); 1386 } 1387 1388 ObjectReferenceImpl objectMirror(long id) { 1389 return objectMirror(id, JDWP.Tag.OBJECT); 1390 } 1391 1392 StringReferenceImpl stringMirror(long id) { 1393 return (StringReferenceImpl)objectMirror(id, JDWP.Tag.STRING); 1394 } 1395 1396 ArrayReferenceImpl arrayMirror(long id) { 1397 return (ArrayReferenceImpl)objectMirror(id, JDWP.Tag.ARRAY); 1398 } 1399 1400 ThreadReferenceImpl threadMirror(long id) { 1401 return (ThreadReferenceImpl)objectMirror(id, JDWP.Tag.THREAD); 1402 } 1403 1404 ThreadGroupReferenceImpl threadGroupMirror(long id) { 1405 return (ThreadGroupReferenceImpl)objectMirror(id, 1406 JDWP.Tag.THREAD_GROUP); 1407 } 1408 1409 ClassLoaderReferenceImpl classLoaderMirror(long id) { 1410 return (ClassLoaderReferenceImpl)objectMirror(id, 1411 JDWP.Tag.CLASS_LOADER); 1412 } 1413 1414 ClassObjectReferenceImpl classObjectMirror(long id) { 1415 return (ClassObjectReferenceImpl)objectMirror(id, 1416 JDWP.Tag.CLASS_OBJECT); 1417 } 1418 1419 ModuleReferenceImpl moduleMirror(long id) { 1420 return (ModuleReferenceImpl)getModule(id); 1421 } 1422 1423 /* 1424 * Implementation of PathSearchingVirtualMachine 1425 */ 1426 private JDWP.VirtualMachine.ClassPaths getClasspath() { 1427 if (pathInfo == null) { 1428 try { 1429 pathInfo = JDWP.VirtualMachine.ClassPaths.process(vm); 1430 } catch (JDWPException exc) { 1431 throw exc.toJDIException(); 1432 } 1433 } 1434 return pathInfo; 1435 } 1436 1437 public List<String> classPath() { 1438 return Arrays.asList(getClasspath().classpaths); 1439 } 1440 1441 public List<String> bootClassPath() { 1442 return Collections.emptyList(); 1443 } 1444 1445 public String baseDirectory() { 1446 return getClasspath().baseDir; 1447 } 1448 1449 public void setDefaultStratum(String stratum) { 1450 defaultStratum = stratum; 1451 if (stratum == null) { 1452 stratum = ""; 1453 } 1454 try { 1455 JDWP.VirtualMachine.SetDefaultStratum.process(vm, 1456 stratum); 1457 } catch (JDWPException exc) { 1458 throw exc.toJDIException(); 1459 } 1460 } 1461 1462 public String getDefaultStratum() { 1463 return defaultStratum; 1464 } 1465 1466 ThreadGroup threadGroupForJDI() { 1467 return threadGroupForJDI; 1468 } 1469 1470 static private class SoftObjectReference extends SoftReference<ObjectReferenceImpl> { 1471 int count; 1472 Long key; 1473 1474 SoftObjectReference(Long key, ObjectReferenceImpl mirror, 1475 ReferenceQueue<ObjectReferenceImpl> queue) { 1476 super(mirror, queue); 1477 this.count = 1; 1478 this.key = key; 1479 } 1480 1481 int count() { 1482 return count; 1483 } 1484 1485 void incrementCount() { 1486 count++; 1487 } 1488 1489 Long key() { 1490 return key; 1491 } 1492 1493 ObjectReferenceImpl object() { 1494 return get(); 1495 } 1496 } 1497} 1498