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 }