0N/A/*
2362N/A * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.tools.jdi;
0N/A
0N/Aimport com.sun.jdi.*;
0N/Aimport com.sun.jdi.connect.spi.Connection;
0N/Aimport com.sun.jdi.request.EventRequestManager;
0N/Aimport com.sun.jdi.request.EventRequest;
0N/Aimport com.sun.jdi.request.BreakpointRequest;
0N/Aimport com.sun.jdi.event.EventQueue;
0N/A
0N/Aimport java.util.*;
0N/Aimport java.text.MessageFormat;
0N/Aimport java.lang.ref.ReferenceQueue;
0N/Aimport java.lang.ref.Reference;
0N/Aimport java.lang.ref.SoftReference;
0N/Aimport java.lang.ref.WeakReference;
0N/A
0N/Aclass VirtualMachineImpl extends MirrorImpl
0N/A implements PathSearchingVirtualMachine, ThreadListener {
0N/A // VM Level exported variables, these
0N/A // are unique to a given vm
0N/A public final int sizeofFieldRef;
0N/A public final int sizeofMethodRef;
0N/A public final int sizeofObjectRef;
0N/A public final int sizeofClassRef;
0N/A public final int sizeofFrameRef;
0N/A
0N/A final int sequenceNumber;
0N/A
0N/A private final TargetVM target;
0N/A private final EventQueueImpl eventQueue;
0N/A private final EventRequestManagerImpl internalEventRequestManager;
0N/A private final EventRequestManagerImpl eventRequestManager;
0N/A final VirtualMachineManagerImpl vmManager;
0N/A private final ThreadGroup threadGroupForJDI;
0N/A
0N/A // Allow direct access to this field so that that tracing code slows down
0N/A // JDI as little as possible when not enabled.
0N/A int traceFlags = TRACE_NONE;
0N/A
0N/A static int TRACE_RAW_SENDS = 0x01000000;
0N/A static int TRACE_RAW_RECEIVES = 0x02000000;
0N/A
0N/A boolean traceReceives = false; // pre-compute because of frequency
0N/A
0N/A // ReferenceType access - updated with class prepare and unload events
0N/A // Protected by "synchronized(this)". "retrievedAllTypes" may be
0N/A // tested unsynchronized (since once true, it stays true), but must
0N/A // be set synchronously
0N/A private Map<Long, ReferenceType> typesByID;
0N/A private TreeSet<ReferenceType> typesBySignature;
0N/A private boolean retrievedAllTypes = false;
0N/A
0N/A // For other languages support
0N/A private String defaultStratum = null;
0N/A
0N/A // ObjectReference cache
0N/A // "objectsByID" protected by "synchronized(this)".
0N/A private final Map<Long, SoftObjectReference> objectsByID = new HashMap<Long, SoftObjectReference>();
0N/A private final ReferenceQueue<ObjectReferenceImpl> referenceQueue = new ReferenceQueue<ObjectReferenceImpl>();
0N/A static private final int DISPOSE_THRESHOLD = 50;
0N/A private final List<SoftObjectReference> batchedDisposeRequests =
0N/A Collections.synchronizedList(new ArrayList<SoftObjectReference>(DISPOSE_THRESHOLD + 10));
0N/A
0N/A // These are cached once for the life of the VM
0N/A private JDWP.VirtualMachine.Version versionInfo;
0N/A private JDWP.VirtualMachine.ClassPaths pathInfo;
0N/A private JDWP.VirtualMachine.Capabilities capabilities = null;
0N/A private JDWP.VirtualMachine.CapabilitiesNew capabilitiesNew = null;
0N/A
0N/A // Per-vm singletons for primitive types and for void.
0N/A // singleton-ness protected by "synchronized(this)".
0N/A private BooleanType theBooleanType;
0N/A private ByteType theByteType;
0N/A private CharType theCharType;
0N/A private ShortType theShortType;
0N/A private IntegerType theIntegerType;
0N/A private LongType theLongType;
0N/A private FloatType theFloatType;
0N/A private DoubleType theDoubleType;
0N/A
0N/A private VoidType theVoidType;
0N/A
0N/A private VoidValue voidVal;
0N/A
0N/A // Launched debuggee process
0N/A private Process process;
0N/A
0N/A // coordinates state changes and corresponding listener notifications
0N/A private VMState state = new VMState(this);
0N/A
0N/A private Object initMonitor = new Object();
0N/A private boolean initComplete = false;
0N/A private boolean shutdown = false;
0N/A
0N/A private void notifyInitCompletion() {
0N/A synchronized(initMonitor) {
0N/A initComplete = true;
0N/A initMonitor.notifyAll();
0N/A }
0N/A }
0N/A
0N/A void waitInitCompletion() {
0N/A synchronized(initMonitor) {
0N/A while (!initComplete) {
0N/A try {
0N/A initMonitor.wait();
0N/A } catch (InterruptedException e) {
0N/A // ignore
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A VMState state() {
0N/A return state;
0N/A }
0N/A
0N/A /*
0N/A * ThreadListener implementation
0N/A */
0N/A public boolean threadResumable(ThreadAction action) {
0N/A /*
0N/A * If any thread is resumed, the VM is considered not suspended.
409N/A * Just one thread is being resumed so pass it to thaw.
0N/A */
409N/A state.thaw(action.thread());
0N/A return true;
0N/A }
0N/A
0N/A VirtualMachineImpl(VirtualMachineManager manager,
0N/A Connection connection, Process process,
0N/A int sequenceNumber) {
0N/A super(null); // Can't use super(this)
0N/A vm = this;
0N/A
0N/A this.vmManager = (VirtualMachineManagerImpl)manager;
0N/A this.process = process;
0N/A this.sequenceNumber = sequenceNumber;
0N/A
0N/A /* Create ThreadGroup to be used by all threads servicing
0N/A * this VM.
0N/A */
0N/A threadGroupForJDI = new ThreadGroup(vmManager.mainGroupForJDI(),
0N/A "JDI [" +
0N/A this.hashCode() + "]");
0N/A
0N/A /*
0N/A * Set up a thread to communicate with the target VM over
0N/A * the specified transport.
0N/A */
0N/A target = new TargetVM(this, connection);
0N/A
0N/A /*
0N/A * Set up a thread to handle events processed internally
0N/A * the JDI implementation.
0N/A */
0N/A EventQueueImpl internalEventQueue = new EventQueueImpl(this, target);
0N/A new InternalEventHandler(this, internalEventQueue);
0N/A /*
0N/A * Initialize client access to event setting and handling
0N/A */
0N/A eventQueue = new EventQueueImpl(this, target);
0N/A eventRequestManager = new EventRequestManagerImpl(this);
0N/A
0N/A target.start();
0N/A
0N/A /*
0N/A * Many ids are variably sized, depending on target VM.
0N/A * Find out the sizes right away.
0N/A */
0N/A JDWP.VirtualMachine.IDSizes idSizes;
0N/A try {
0N/A idSizes = JDWP.VirtualMachine.IDSizes.process(vm);
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A sizeofFieldRef = idSizes.fieldIDSize;
0N/A sizeofMethodRef = idSizes.methodIDSize;
0N/A sizeofObjectRef = idSizes.objectIDSize;
0N/A sizeofClassRef = idSizes.referenceTypeIDSize;
0N/A sizeofFrameRef = idSizes.frameIDSize;
0N/A
0N/A /**
0N/A * Set up requests needed by internal event handler.
0N/A * Make sure they are distinguished by creating them with
0N/A * an internal event request manager.
0N/A *
0N/A * Warning: create events only with SUSPEND_NONE policy.
0N/A * In the current implementation other policies will not
0N/A * be handled correctly when the event comes in. (notfiySuspend()
0N/A * will not be properly called, and if the event is combined
0N/A * with external events in the same set, suspend policy is not
0N/A * correctly determined for the internal vs. external event sets)
0N/A */
0N/A internalEventRequestManager = new EventRequestManagerImpl(this);
0N/A EventRequest er = internalEventRequestManager.createClassPrepareRequest();
0N/A er.setSuspendPolicy(EventRequest.SUSPEND_NONE);
0N/A er.enable();
0N/A er = internalEventRequestManager.createClassUnloadRequest();
0N/A er.setSuspendPolicy(EventRequest.SUSPEND_NONE);
0N/A er.enable();
0N/A
0N/A /*
0N/A * Tell other threads, notably TargetVM, that initialization
0N/A * is complete.
0N/A */
0N/A notifyInitCompletion();
0N/A }
0N/A
0N/A EventRequestManagerImpl getInternalEventRequestManager() {
0N/A return internalEventRequestManager;
0N/A }
0N/A
0N/A void validateVM() {
0N/A /*
0N/A * We no longer need to do this. The spec now says
0N/A * that a VMDisconnected _may_ be thrown in these
0N/A * cases, not that it _will_ be thrown.
0N/A * So, to simplify things we will just let the
0N/A * caller's of this method proceed with their business.
0N/A * If the debuggee is disconnected, either because it
0N/A * crashed or finished or something, or because the
0N/A * debugger called exit() or dispose(), then if
0N/A * we end up trying to communicate with the debuggee,
0N/A * code in TargetVM will throw a VMDisconnectedException.
0N/A * This means that if we can satisfy a request without
0N/A * talking to the debuggee, (eg, with cached data) then
0N/A * VMDisconnectedException will _not_ be thrown.
0N/A * if (shutdown) {
0N/A * throw new VMDisconnectedException();
0N/A * }
0N/A */
0N/A }
0N/A
0N/A public boolean equals(Object obj) {
0N/A return this == obj;
0N/A }
0N/A
0N/A public int hashCode() {
0N/A return System.identityHashCode(this);
0N/A }
0N/A
0N/A public List<ReferenceType> classesByName(String className) {
0N/A validateVM();
0N/A String signature = JNITypeParser.typeNameToSignature(className);
0N/A List<ReferenceType> list;
0N/A if (retrievedAllTypes) {
0N/A list = findReferenceTypes(signature);
0N/A } else {
0N/A list = retrieveClassesBySignature(signature);
0N/A }
0N/A return Collections.unmodifiableList(list);
0N/A }
0N/A
0N/A public List<ReferenceType> allClasses() {
0N/A validateVM();
0N/A
0N/A if (!retrievedAllTypes) {
0N/A retrieveAllClasses();
0N/A }
0N/A ArrayList<ReferenceType> a;
0N/A synchronized (this) {
0N/A a = new ArrayList<ReferenceType>(typesBySignature);
0N/A }
0N/A return Collections.unmodifiableList(a);
0N/A }
0N/A
0N/A public void
0N/A redefineClasses(Map<? extends ReferenceType,byte[]> classToBytes)
0N/A {
0N/A int cnt = classToBytes.size();
0N/A JDWP.VirtualMachine.RedefineClasses.ClassDef[] defs =
0N/A new JDWP.VirtualMachine.RedefineClasses.ClassDef[cnt];
0N/A validateVM();
0N/A if (!canRedefineClasses()) {
0N/A throw new UnsupportedOperationException();
0N/A }
0N/A Iterator it = classToBytes.entrySet().iterator();
0N/A for (int i = 0; it.hasNext(); i++) {
0N/A Map.Entry entry = (Map.Entry)it.next();
0N/A ReferenceTypeImpl refType = (ReferenceTypeImpl)entry.getKey();
0N/A validateMirror(refType);
0N/A defs[i] = new JDWP.VirtualMachine.RedefineClasses
0N/A .ClassDef(refType, (byte[])entry.getValue());
0N/A }
0N/A
0N/A // flush caches and disable caching until the next suspend
0N/A vm.state().thaw();
0N/A
0N/A try {
0N/A JDWP.VirtualMachine.RedefineClasses.
0N/A process(vm, defs);
0N/A } catch (JDWPException exc) {
0N/A switch (exc.errorCode()) {
0N/A case JDWP.Error.INVALID_CLASS_FORMAT :
0N/A throw new ClassFormatError(
0N/A "class not in class file format");
0N/A case JDWP.Error.CIRCULAR_CLASS_DEFINITION :
0N/A throw new ClassCircularityError(
0N/A "circularity has been detected while initializing a class");
0N/A case JDWP.Error.FAILS_VERIFICATION :
0N/A throw new VerifyError(
0N/A "verifier detected internal inconsistency or security problem");
0N/A case JDWP.Error.UNSUPPORTED_VERSION :
0N/A throw new UnsupportedClassVersionError(
0N/A "version numbers of class are not supported");
0N/A case JDWP.Error.ADD_METHOD_NOT_IMPLEMENTED:
0N/A throw new UnsupportedOperationException(
0N/A "add method not implemented");
0N/A case JDWP.Error.SCHEMA_CHANGE_NOT_IMPLEMENTED :
0N/A throw new UnsupportedOperationException(
0N/A "schema change not implemented");
0N/A case JDWP.Error.HIERARCHY_CHANGE_NOT_IMPLEMENTED:
0N/A throw new UnsupportedOperationException(
0N/A "hierarchy change not implemented");
0N/A case JDWP.Error.DELETE_METHOD_NOT_IMPLEMENTED :
0N/A throw new UnsupportedOperationException(
0N/A "delete method not implemented");
0N/A case JDWP.Error.CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED:
0N/A throw new UnsupportedOperationException(
0N/A "changes to class modifiers not implemented");
0N/A case JDWP.Error.METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED :
0N/A throw new UnsupportedOperationException(
0N/A "changes to method modifiers not implemented");
0N/A case JDWP.Error.NAMES_DONT_MATCH :
0N/A throw new NoClassDefFoundError(
0N/A "class names do not match");
0N/A default:
0N/A throw exc.toJDIException();
0N/A }
0N/A }
0N/A
0N/A // Delete any record of the breakpoints
0N/A List<BreakpointRequest> toDelete = new ArrayList<BreakpointRequest>();
0N/A EventRequestManager erm = eventRequestManager();
0N/A it = erm.breakpointRequests().iterator();
0N/A while (it.hasNext()) {
0N/A BreakpointRequest req = (BreakpointRequest)it.next();
0N/A if (classToBytes.containsKey(req.location().declaringType())) {
0N/A toDelete.add(req);
0N/A }
0N/A }
0N/A erm.deleteEventRequests(toDelete);
0N/A
0N/A // Invalidate any information cached for the classes just redefined.
0N/A it = classToBytes.keySet().iterator();
0N/A while (it.hasNext()) {
0N/A ReferenceTypeImpl rti = (ReferenceTypeImpl)it.next();
0N/A rti.noticeRedefineClass();
0N/A }
0N/A }
0N/A
0N/A public List<ThreadReference> allThreads() {
0N/A validateVM();
0N/A return state.allThreads();
0N/A }
0N/A
0N/A public List<ThreadGroupReference> topLevelThreadGroups() {
0N/A validateVM();
0N/A return state.topLevelThreadGroups();
0N/A }
0N/A
0N/A /*
0N/A * Sends a command to the back end which is defined to do an
0N/A * implicit vm-wide resume. The VM can no longer be considered
0N/A * suspended, so certain cached data must be invalidated.
0N/A */
0N/A PacketStream sendResumingCommand(CommandSender sender) {
0N/A return state.thawCommand(sender);
0N/A }
0N/A
0N/A /*
0N/A * The VM has been suspended. Additional caching can be done
0N/A * as long as there are no pending resumes.
0N/A */
0N/A void notifySuspend() {
0N/A state.freeze();
0N/A }
0N/A
0N/A public void suspend() {
0N/A validateVM();
0N/A try {
0N/A JDWP.VirtualMachine.Suspend.process(vm);
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A notifySuspend();
0N/A }
0N/A
0N/A public void resume() {
0N/A validateVM();
0N/A CommandSender sender =
0N/A new CommandSender() {
0N/A public PacketStream send() {
0N/A return JDWP.VirtualMachine.Resume.enqueueCommand(vm);
0N/A }
0N/A };
0N/A try {
0N/A PacketStream stream = state.thawCommand(sender);
0N/A JDWP.VirtualMachine.Resume.waitForReply(vm, stream);
0N/A } catch (VMDisconnectedException exc) {
0N/A /*
0N/A * If the debugger makes a VMDeathRequest with SUSPEND_ALL,
0N/A * then when it does an EventSet.resume after getting the
0N/A * VMDeathEvent, the normal flow of events is that the
0N/A * BE shuts down, but the waitForReply comes back ok. In this
0N/A * case, the run loop in TargetVM that is waiting for a packet
0N/A * gets an EOF because the socket closes. It generates a
0N/A * VMDisconnectedEvent and everyone is happy.
0N/A * However, sometimes, the BE gets shutdown before this
0N/A * waitForReply completes. In this case, TargetVM.waitForReply
0N/A * gets awakened with no reply and so gens a VMDisconnectedException
0N/A * which is not what we want. It might be possible to fix this
0N/A * in the BE, but it is ok to just ignore the VMDisconnectedException
0N/A * here. This will allow the VMDisconnectedEvent to be generated
0N/A * correctly. And, if the debugger should happen to make another
0N/A * request, it will get a VMDisconnectedException at that time.
0N/A */
0N/A } catch (JDWPException exc) {
0N/A switch (exc.errorCode()) {
0N/A case JDWP.Error.VM_DEAD:
0N/A return;
0N/A default:
0N/A throw exc.toJDIException();
0N/A }
0N/A }
0N/A }
0N/A
0N/A public EventQueue eventQueue() {
0N/A /*
0N/A * No VM validation here. We allow access to the event queue
0N/A * after disconnection, so that there is access to the terminating
0N/A * events.
0N/A */
0N/A return eventQueue;
0N/A }
0N/A
0N/A public EventRequestManager eventRequestManager() {
0N/A validateVM();
0N/A return eventRequestManager;
0N/A }
0N/A
0N/A EventRequestManagerImpl eventRequestManagerImpl() {
0N/A return eventRequestManager;
0N/A }
0N/A
0N/A public BooleanValue mirrorOf(boolean value) {
0N/A validateVM();
0N/A return new BooleanValueImpl(this,value);
0N/A }
0N/A
0N/A public ByteValue mirrorOf(byte value) {
0N/A validateVM();
0N/A return new ByteValueImpl(this,value);
0N/A }
0N/A
0N/A public CharValue mirrorOf(char value) {
0N/A validateVM();
0N/A return new CharValueImpl(this,value);
0N/A }
0N/A
0N/A public ShortValue mirrorOf(short value) {
0N/A validateVM();
0N/A return new ShortValueImpl(this,value);
0N/A }
0N/A
0N/A public IntegerValue mirrorOf(int value) {
0N/A validateVM();
0N/A return new IntegerValueImpl(this,value);
0N/A }
0N/A
0N/A public LongValue mirrorOf(long value) {
0N/A validateVM();
0N/A return new LongValueImpl(this,value);
0N/A }
0N/A
0N/A public FloatValue mirrorOf(float value) {
0N/A validateVM();
0N/A return new FloatValueImpl(this,value);
0N/A }
0N/A
0N/A public DoubleValue mirrorOf(double value) {
0N/A validateVM();
0N/A return new DoubleValueImpl(this,value);
0N/A }
0N/A
0N/A public StringReference mirrorOf(String value) {
0N/A validateVM();
0N/A try {
0N/A return (StringReference)JDWP.VirtualMachine.CreateString.
0N/A process(vm, value).stringObject;
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A }
0N/A
0N/A public VoidValue mirrorOfVoid() {
0N/A if (voidVal == null) {
0N/A voidVal = new VoidValueImpl(this);
0N/A }
0N/A return voidVal;
0N/A }
0N/A
0N/A public long[] instanceCounts(List<? extends ReferenceType> classes) {
0N/A if (!canGetInstanceInfo()) {
0N/A throw new UnsupportedOperationException(
0N/A "target does not support getting instances");
0N/A }
0N/A long[] retValue ;
0N/A ReferenceTypeImpl[] rtArray = new ReferenceTypeImpl[classes.size()];
0N/A int ii = 0;
0N/A for (ReferenceType rti: classes) {
0N/A validateMirror(rti);
0N/A rtArray[ii++] = (ReferenceTypeImpl)rti;
0N/A }
0N/A try {
0N/A retValue = JDWP.VirtualMachine.InstanceCounts.
0N/A process(vm, rtArray).counts;
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A
0N/A return retValue;
0N/A }
0N/A
0N/A public void dispose() {
0N/A validateVM();
0N/A shutdown = true;
0N/A try {
0N/A JDWP.VirtualMachine.Dispose.process(vm);
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A target.stopListening();
0N/A }
0N/A
0N/A public void exit(int exitCode) {
0N/A validateVM();
0N/A shutdown = true;
0N/A try {
0N/A JDWP.VirtualMachine.Exit.process(vm, exitCode);
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A target.stopListening();
0N/A }
0N/A
0N/A public Process process() {
0N/A validateVM();
0N/A return process;
0N/A }
0N/A
0N/A private JDWP.VirtualMachine.Version versionInfo() {
0N/A try {
0N/A if (versionInfo == null) {
0N/A // Need not be synchronized since it is static information
0N/A versionInfo = JDWP.VirtualMachine.Version.process(vm);
0N/A }
0N/A return versionInfo;
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A }
0N/A public String description() {
0N/A validateVM();
0N/A
0N/A return MessageFormat.format(vmManager.getString("version_format"),
0N/A "" + vmManager.majorInterfaceVersion(),
0N/A "" + vmManager.minorInterfaceVersion(),
0N/A versionInfo().description);
0N/A }
0N/A
0N/A public String version() {
0N/A validateVM();
0N/A return versionInfo().vmVersion;
0N/A }
0N/A
0N/A public String name() {
0N/A validateVM();
0N/A return versionInfo().vmName;
0N/A }
0N/A
0N/A public boolean canWatchFieldModification() {
0N/A validateVM();
0N/A return capabilities().canWatchFieldModification;
0N/A }
0N/A public boolean canWatchFieldAccess() {
0N/A validateVM();
0N/A return capabilities().canWatchFieldAccess;
0N/A }
0N/A public boolean canGetBytecodes() {
0N/A validateVM();
0N/A return capabilities().canGetBytecodes;
0N/A }
0N/A public boolean canGetSyntheticAttribute() {
0N/A validateVM();
0N/A return capabilities().canGetSyntheticAttribute;
0N/A }
0N/A public boolean canGetOwnedMonitorInfo() {
0N/A validateVM();
0N/A return capabilities().canGetOwnedMonitorInfo;
0N/A }
0N/A public boolean canGetCurrentContendedMonitor() {
0N/A validateVM();
0N/A return capabilities().canGetCurrentContendedMonitor;
0N/A }
0N/A public boolean canGetMonitorInfo() {
0N/A validateVM();
0N/A return capabilities().canGetMonitorInfo;
0N/A }
0N/A
0N/A private boolean hasNewCapabilities() {
0N/A return versionInfo().jdwpMajor > 1 ||
0N/A versionInfo().jdwpMinor >= 4;
0N/A }
0N/A
0N/A boolean canGet1_5LanguageFeatures() {
0N/A return versionInfo().jdwpMajor > 1 ||
0N/A versionInfo().jdwpMinor >= 5;
0N/A }
0N/A
0N/A public boolean canUseInstanceFilters() {
0N/A validateVM();
0N/A return hasNewCapabilities() &&
0N/A capabilitiesNew().canUseInstanceFilters;
0N/A }
0N/A public boolean canRedefineClasses() {
0N/A validateVM();
0N/A return hasNewCapabilities() &&
0N/A capabilitiesNew().canRedefineClasses;
0N/A }
0N/A public boolean canAddMethod() {
0N/A validateVM();
0N/A return hasNewCapabilities() &&
0N/A capabilitiesNew().canAddMethod;
0N/A }
0N/A public boolean canUnrestrictedlyRedefineClasses() {
0N/A validateVM();
0N/A return hasNewCapabilities() &&
0N/A capabilitiesNew().canUnrestrictedlyRedefineClasses;
0N/A }
0N/A public boolean canPopFrames() {
0N/A validateVM();
0N/A return hasNewCapabilities() &&
0N/A capabilitiesNew().canPopFrames;
0N/A }
0N/A public boolean canGetMethodReturnValues() {
0N/A return versionInfo().jdwpMajor > 1 ||
0N/A versionInfo().jdwpMinor >= 6;
0N/A }
0N/A public boolean canGetInstanceInfo() {
0N/A if (versionInfo().jdwpMajor < 1 ||
0N/A versionInfo().jdwpMinor < 6) {
0N/A return false;
0N/A }
0N/A validateVM();
0N/A return hasNewCapabilities() &&
0N/A capabilitiesNew().canGetInstanceInfo;
0N/A }
0N/A public boolean canUseSourceNameFilters() {
0N/A if (versionInfo().jdwpMajor < 1 ||
0N/A versionInfo().jdwpMinor < 6) {
0N/A return false;
0N/A }
0N/A return true;
0N/A }
0N/A public boolean canForceEarlyReturn() {
0N/A validateVM();
0N/A return hasNewCapabilities() &&
0N/A capabilitiesNew().canForceEarlyReturn;
0N/A }
0N/A public boolean canBeModified() {
0N/A return true;
0N/A }
0N/A public boolean canGetSourceDebugExtension() {
0N/A validateVM();
0N/A return hasNewCapabilities() &&
0N/A capabilitiesNew().canGetSourceDebugExtension;
0N/A }
0N/A public boolean canGetClassFileVersion() {
0N/A if ( versionInfo().jdwpMajor < 1 &&
0N/A versionInfo().jdwpMinor < 6) {
0N/A return false;
0N/A } else {
0N/A return true;
0N/A }
0N/A }
0N/A public boolean canGetConstantPool() {
0N/A validateVM();
0N/A return hasNewCapabilities() &&
0N/A capabilitiesNew().canGetConstantPool;
0N/A }
0N/A public boolean canRequestVMDeathEvent() {
0N/A validateVM();
0N/A return hasNewCapabilities() &&
0N/A capabilitiesNew().canRequestVMDeathEvent;
0N/A }
0N/A public boolean canRequestMonitorEvents() {
0N/A validateVM();
0N/A return hasNewCapabilities() &&
0N/A capabilitiesNew().canRequestMonitorEvents;
0N/A }
0N/A public boolean canGetMonitorFrameInfo() {
0N/A validateVM();
0N/A return hasNewCapabilities() &&
0N/A capabilitiesNew().canGetMonitorFrameInfo;
0N/A }
0N/A
0N/A public void setDebugTraceMode(int traceFlags) {
0N/A validateVM();
0N/A this.traceFlags = traceFlags;
0N/A this.traceReceives = (traceFlags & TRACE_RECEIVES) != 0;
0N/A }
0N/A
0N/A void printTrace(String string) {
0N/A System.err.println("[JDI: " + string + "]");
0N/A }
0N/A
0N/A void printReceiveTrace(int depth, String string) {
0N/A StringBuffer sb = new StringBuffer("Receiving:");
0N/A for (int i = depth; i > 0; --i) {
0N/A sb.append(" ");
0N/A }
0N/A sb.append(string);
0N/A printTrace(sb.toString());
0N/A }
0N/A
0N/A private synchronized ReferenceTypeImpl addReferenceType(long id,
0N/A int tag,
0N/A String signature) {
0N/A if (typesByID == null) {
0N/A initReferenceTypes();
0N/A }
0N/A ReferenceTypeImpl type = null;
0N/A switch(tag) {
0N/A case JDWP.TypeTag.CLASS:
0N/A type = new ClassTypeImpl(vm, id);
0N/A break;
0N/A case JDWP.TypeTag.INTERFACE:
0N/A type = new InterfaceTypeImpl(vm, id);
0N/A break;
0N/A case JDWP.TypeTag.ARRAY:
0N/A type = new ArrayTypeImpl(vm, id);
0N/A break;
0N/A default:
0N/A throw new InternalException("Invalid reference type tag");
0N/A }
0N/A
0N/A /*
0N/A * If a signature was specified, make sure to set it ASAP, to
0N/A * prevent any needless JDWP command to retrieve it. (for example,
0N/A * typesBySignature.add needs the signature, to maintain proper
0N/A * ordering.
0N/A */
0N/A if (signature != null) {
0N/A type.setSignature(signature);
0N/A }
0N/A
0N/A typesByID.put(new Long(id), type);
0N/A typesBySignature.add(type);
0N/A
0N/A if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
0N/A vm.printTrace("Caching new ReferenceType, sig=" + signature +
0N/A ", id=" + id);
0N/A }
0N/A
0N/A return type;
0N/A }
0N/A
0N/A synchronized void removeReferenceType(String signature) {
0N/A if (typesByID == null) {
0N/A return;
0N/A }
0N/A /*
0N/A * There can be multiple classes with the same name. Since
0N/A * we can't differentiate here, we first remove all
0N/A * matching classes from our cache...
0N/A */
0N/A Iterator iter = typesBySignature.iterator();
0N/A int matches = 0;
0N/A while (iter.hasNext()) {
0N/A ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
0N/A int comp = signature.compareTo(type.signature());
0N/A if (comp == 0) {
0N/A matches++;
0N/A iter.remove();
0N/A typesByID.remove(new Long(type.ref()));
0N/A if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
0N/A vm.printTrace("Uncaching ReferenceType, sig=" + signature +
0N/A ", id=" + type.ref());
0N/A }
0N/A/* fix for 4359077 , don't break out. list is no longer sorted
0N/A in the order we think
0N/A */
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * ...and if there was more than one, re-retrieve the classes
0N/A * with that name
0N/A */
0N/A if (matches > 1) {
0N/A retrieveClassesBySignature(signature);
0N/A }
0N/A }
0N/A
0N/A private synchronized List<ReferenceType> findReferenceTypes(String signature) {
0N/A if (typesByID == null) {
0N/A return new ArrayList<ReferenceType>(0);
0N/A }
0N/A Iterator iter = typesBySignature.iterator();
0N/A List<ReferenceType> list = new ArrayList<ReferenceType>();
0N/A while (iter.hasNext()) {
0N/A ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
0N/A int comp = signature.compareTo(type.signature());
0N/A if (comp == 0) {
0N/A list.add(type);
0N/A/* fix for 4359077 , don't break out. list is no longer sorted
0N/A in the order we think
0N/A */
0N/A }
0N/A }
0N/A return list;
0N/A }
0N/A
0N/A private void initReferenceTypes() {
0N/A typesByID = new HashMap<Long, ReferenceType>(300);
0N/A typesBySignature = new TreeSet<ReferenceType>();
0N/A }
0N/A
0N/A ReferenceTypeImpl referenceType(long ref, byte tag) {
0N/A return referenceType(ref, tag, null);
0N/A }
0N/A
0N/A ClassTypeImpl classType(long ref) {
0N/A return (ClassTypeImpl)referenceType(ref, JDWP.TypeTag.CLASS, null);
0N/A }
0N/A
0N/A InterfaceTypeImpl interfaceType(long ref) {
0N/A return (InterfaceTypeImpl)referenceType(ref, JDWP.TypeTag.INTERFACE, null);
0N/A }
0N/A
0N/A ArrayTypeImpl arrayType(long ref) {
0N/A return (ArrayTypeImpl)referenceType(ref, JDWP.TypeTag.ARRAY, null);
0N/A }
0N/A
0N/A ReferenceTypeImpl referenceType(long id, int tag,
0N/A String signature) {
0N/A if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
0N/A StringBuffer sb = new StringBuffer();
0N/A sb.append("Looking up ");
0N/A if (tag == JDWP.TypeTag.CLASS) {
0N/A sb.append("Class");
0N/A } else if (tag == JDWP.TypeTag.INTERFACE) {
0N/A sb.append("Interface");
0N/A } else if (tag == JDWP.TypeTag.ARRAY) {
0N/A sb.append("ArrayType");
0N/A } else {
0N/A sb.append("UNKNOWN TAG: " + tag);
0N/A }
0N/A if (signature != null) {
0N/A sb.append(", signature='" + signature + "'");
0N/A }
0N/A sb.append(", id=" + id);
0N/A vm.printTrace(sb.toString());
0N/A }
0N/A if (id == 0) {
0N/A return null;
0N/A } else {
0N/A ReferenceTypeImpl retType = null;
0N/A synchronized (this) {
0N/A if (typesByID != null) {
0N/A retType = (ReferenceTypeImpl)typesByID.get(new Long(id));
0N/A }
0N/A if (retType == null) {
0N/A retType = addReferenceType(id, tag, signature);
0N/A }
0N/A }
0N/A return retType;
0N/A }
0N/A }
0N/A
0N/A private JDWP.VirtualMachine.Capabilities capabilities() {
0N/A if (capabilities == null) {
0N/A try {
0N/A capabilities = JDWP.VirtualMachine
0N/A .Capabilities.process(vm);
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A }
0N/A return capabilities;
0N/A }
0N/A
0N/A private JDWP.VirtualMachine.CapabilitiesNew capabilitiesNew() {
0N/A if (capabilitiesNew == null) {
0N/A try {
0N/A capabilitiesNew = JDWP.VirtualMachine
0N/A .CapabilitiesNew.process(vm);
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A }
0N/A return capabilitiesNew;
0N/A }
0N/A
0N/A private List<ReferenceType> retrieveClassesBySignature(String signature) {
0N/A if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
0N/A vm.printTrace("Retrieving matching ReferenceTypes, sig=" + signature);
0N/A }
0N/A JDWP.VirtualMachine.ClassesBySignature.ClassInfo[] cinfos;
0N/A try {
0N/A cinfos = JDWP.VirtualMachine.ClassesBySignature.
0N/A process(vm, signature).classes;
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A
0N/A int count = cinfos.length;
0N/A List<ReferenceType> list = new ArrayList<ReferenceType>(count);
0N/A
0N/A // Hold lock during processing to improve performance
0N/A synchronized (this) {
0N/A for (int i = 0; i < count; i++) {
0N/A JDWP.VirtualMachine.ClassesBySignature.ClassInfo ci =
0N/A cinfos[i];
0N/A ReferenceTypeImpl type = referenceType(ci.typeID,
0N/A ci.refTypeTag,
0N/A signature);
0N/A type.setStatus(ci.status);
0N/A list.add(type);
0N/A }
0N/A }
0N/A return list;
0N/A }
0N/A
0N/A private void retrieveAllClasses1_4() {
0N/A JDWP.VirtualMachine.AllClasses.ClassInfo[] cinfos;
0N/A try {
0N/A cinfos = JDWP.VirtualMachine.AllClasses.process(vm).classes;
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A
0N/A // Hold lock during processing to improve performance
0N/A // and to have safe check/set of retrievedAllTypes
0N/A synchronized (this) {
0N/A if (!retrievedAllTypes) {
0N/A // Number of classes
0N/A int count = cinfos.length;
0N/A for (int i=0; i<count; i++) {
0N/A JDWP.VirtualMachine.AllClasses.ClassInfo ci =
0N/A cinfos[i];
0N/A ReferenceTypeImpl type = referenceType(ci.typeID,
0N/A ci.refTypeTag,
0N/A ci.signature);
0N/A type.setStatus(ci.status);
0N/A }
0N/A retrievedAllTypes = true;
0N/A }
0N/A }
0N/A }
0N/A
0N/A private void retrieveAllClasses() {
0N/A if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) {
0N/A vm.printTrace("Retrieving all ReferenceTypes");
0N/A }
0N/A
0N/A if (!vm.canGet1_5LanguageFeatures()) {
0N/A retrieveAllClasses1_4();
0N/A return;
0N/A }
0N/A
0N/A /*
0N/A * To save time (assuming the caller will be
0N/A * using then) we will get the generic sigs too.
0N/A */
0N/A
0N/A JDWP.VirtualMachine.AllClassesWithGeneric.ClassInfo[] cinfos;
0N/A try {
0N/A cinfos = JDWP.VirtualMachine.AllClassesWithGeneric.process(vm).classes;
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A
0N/A // Hold lock during processing to improve performance
0N/A // and to have safe check/set of retrievedAllTypes
0N/A synchronized (this) {
0N/A if (!retrievedAllTypes) {
0N/A // Number of classes
0N/A int count = cinfos.length;
0N/A for (int i=0; i<count; i++) {
0N/A JDWP.VirtualMachine.AllClassesWithGeneric.ClassInfo ci =
0N/A cinfos[i];
0N/A ReferenceTypeImpl type = referenceType(ci.typeID,
0N/A ci.refTypeTag,
0N/A ci.signature);
0N/A type.setGenericSignature(ci.genericSignature);
0N/A type.setStatus(ci.status);
0N/A }
0N/A retrievedAllTypes = true;
0N/A }
0N/A }
0N/A }
0N/A
0N/A void sendToTarget(Packet packet) {
0N/A target.send(packet);
0N/A }
0N/A
0N/A void waitForTargetReply(Packet packet) {
0N/A target.waitForReply(packet);
0N/A /*
0N/A * If any object disposes have been batched up, send them now.
0N/A */
0N/A processBatchedDisposes();
0N/A }
0N/A
0N/A Type findBootType(String signature) throws ClassNotLoadedException {
0N/A List types = allClasses();
0N/A Iterator iter = types.iterator();
0N/A while (iter.hasNext()) {
0N/A ReferenceType type = (ReferenceType)iter.next();
0N/A if ((type.classLoader() == null) &&
0N/A (type.signature().equals(signature))) {
0N/A return type;
0N/A }
0N/A }
0N/A JNITypeParser parser = new JNITypeParser(signature);
0N/A throw new ClassNotLoadedException(parser.typeName(),
0N/A "Type " + parser.typeName() + " not loaded");
0N/A }
0N/A
0N/A BooleanType theBooleanType() {
0N/A if (theBooleanType == null) {
0N/A synchronized(this) {
0N/A if (theBooleanType == null) {
0N/A theBooleanType = new BooleanTypeImpl(this);
0N/A }
0N/A }
0N/A }
0N/A return theBooleanType;
0N/A }
0N/A
0N/A ByteType theByteType() {
0N/A if (theByteType == null) {
0N/A synchronized(this) {
0N/A if (theByteType == null) {
0N/A theByteType = new ByteTypeImpl(this);
0N/A }
0N/A }
0N/A }
0N/A return theByteType;
0N/A }
0N/A
0N/A CharType theCharType() {
0N/A if (theCharType == null) {
0N/A synchronized(this) {
0N/A if (theCharType == null) {
0N/A theCharType = new CharTypeImpl(this);
0N/A }
0N/A }
0N/A }
0N/A return theCharType;
0N/A }
0N/A
0N/A ShortType theShortType() {
0N/A if (theShortType == null) {
0N/A synchronized(this) {
0N/A if (theShortType == null) {
0N/A theShortType = new ShortTypeImpl(this);
0N/A }
0N/A }
0N/A }
0N/A return theShortType;
0N/A }
0N/A
0N/A IntegerType theIntegerType() {
0N/A if (theIntegerType == null) {
0N/A synchronized(this) {
0N/A if (theIntegerType == null) {
0N/A theIntegerType = new IntegerTypeImpl(this);
0N/A }
0N/A }
0N/A }
0N/A return theIntegerType;
0N/A }
0N/A
0N/A LongType theLongType() {
0N/A if (theLongType == null) {
0N/A synchronized(this) {
0N/A if (theLongType == null) {
0N/A theLongType = new LongTypeImpl(this);
0N/A }
0N/A }
0N/A }
0N/A return theLongType;
0N/A }
0N/A
0N/A FloatType theFloatType() {
0N/A if (theFloatType == null) {
0N/A synchronized(this) {
0N/A if (theFloatType == null) {
0N/A theFloatType = new FloatTypeImpl(this);
0N/A }
0N/A }
0N/A }
0N/A return theFloatType;
0N/A }
0N/A
0N/A DoubleType theDoubleType() {
0N/A if (theDoubleType == null) {
0N/A synchronized(this) {
0N/A if (theDoubleType == null) {
0N/A theDoubleType = new DoubleTypeImpl(this);
0N/A }
0N/A }
0N/A }
0N/A return theDoubleType;
0N/A }
0N/A
0N/A VoidType theVoidType() {
0N/A if (theVoidType == null) {
0N/A synchronized(this) {
0N/A if (theVoidType == null) {
0N/A theVoidType = new VoidTypeImpl(this);
0N/A }
0N/A }
0N/A }
0N/A return theVoidType;
0N/A }
0N/A
0N/A PrimitiveType primitiveTypeMirror(byte tag) {
0N/A switch (tag) {
0N/A case JDWP.Tag.BOOLEAN:
0N/A return theBooleanType();
0N/A case JDWP.Tag.BYTE:
0N/A return theByteType();
0N/A case JDWP.Tag.CHAR:
0N/A return theCharType();
0N/A case JDWP.Tag.SHORT:
0N/A return theShortType();
0N/A case JDWP.Tag.INT:
0N/A return theIntegerType();
0N/A case JDWP.Tag.LONG:
0N/A return theLongType();
0N/A case JDWP.Tag.FLOAT:
0N/A return theFloatType();
0N/A case JDWP.Tag.DOUBLE:
0N/A return theDoubleType();
0N/A default:
0N/A throw new IllegalArgumentException("Unrecognized primitive tag " + tag);
0N/A }
0N/A }
0N/A
0N/A private void processBatchedDisposes() {
0N/A if (shutdown) {
0N/A return;
0N/A }
0N/A
0N/A JDWP.VirtualMachine.DisposeObjects.Request[] requests = null;
0N/A synchronized(batchedDisposeRequests) {
0N/A int size = batchedDisposeRequests.size();
0N/A if (size >= DISPOSE_THRESHOLD) {
0N/A if ((traceFlags & TRACE_OBJREFS) != 0) {
0N/A printTrace("Dispose threashold reached. Will dispose "
0N/A + size + " object references...");
0N/A }
0N/A requests = new JDWP.VirtualMachine.DisposeObjects.Request[size];
0N/A for (int i = 0; i < requests.length; i++) {
28N/A SoftObjectReference ref = batchedDisposeRequests.get(i);
0N/A if ((traceFlags & TRACE_OBJREFS) != 0) {
0N/A printTrace("Disposing object " + ref.key().longValue() +
0N/A " (ref count = " + ref.count() + ")");
0N/A }
0N/A
0N/A // This is kludgy. We temporarily re-create an object
0N/A // reference so that we can correctly pass its id to the
0N/A // JDWP command.
0N/A requests[i] =
0N/A new JDWP.VirtualMachine.DisposeObjects.Request(
0N/A new ObjectReferenceImpl(this, ref.key().longValue()),
0N/A ref.count());
0N/A }
0N/A batchedDisposeRequests.clear();
0N/A }
0N/A }
0N/A if (requests != null) {
0N/A try {
0N/A JDWP.VirtualMachine.DisposeObjects.process(vm, requests);
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A }
0N/A }
0N/A
0N/A private void batchForDispose(SoftObjectReference ref) {
0N/A if ((traceFlags & TRACE_OBJREFS) != 0) {
0N/A printTrace("Batching object " + ref.key().longValue() +
0N/A " for dispose (ref count = " + ref.count() + ")");
0N/A }
0N/A batchedDisposeRequests.add(ref);
0N/A }
0N/A
0N/A private void processQueue() {
0N/A Reference ref;
0N/A //if ((traceFlags & TRACE_OBJREFS) != 0) {
0N/A // printTrace("Checking for softly reachable objects");
0N/A //}
0N/A while ((ref = referenceQueue.poll()) != null) {
0N/A SoftObjectReference softRef = (SoftObjectReference)ref;
0N/A removeObjectMirror(softRef);
0N/A batchForDispose(softRef);
0N/A }
0N/A }
0N/A
0N/A synchronized ObjectReferenceImpl objectMirror(long id, int tag) {
0N/A
0N/A // Handle any queue elements that are not strongly reachable
0N/A processQueue();
0N/A
0N/A if (id == 0) {
0N/A return null;
0N/A }
0N/A ObjectReferenceImpl object = null;
0N/A Long key = new Long(id);
0N/A
0N/A /*
0N/A * Attempt to retrieve an existing object object reference
0N/A */
0N/A SoftObjectReference ref = objectsByID.get(key);
0N/A if (ref != null) {
0N/A object = ref.object();
0N/A }
0N/A
0N/A /*
0N/A * If the object wasn't in the table, or it's soft reference was
0N/A * cleared, create a new instance.
0N/A */
0N/A if (object == null) {
0N/A switch (tag) {
0N/A case JDWP.Tag.OBJECT:
0N/A object = new ObjectReferenceImpl(vm, id);
0N/A break;
0N/A case JDWP.Tag.STRING:
0N/A object = new StringReferenceImpl(vm, id);
0N/A break;
0N/A case JDWP.Tag.ARRAY:
0N/A object = new ArrayReferenceImpl(vm, id);
0N/A break;
0N/A case JDWP.Tag.THREAD:
0N/A ThreadReferenceImpl thread =
0N/A new ThreadReferenceImpl(vm, id);
0N/A thread.addListener(this);
0N/A object = thread;
0N/A break;
0N/A case JDWP.Tag.THREAD_GROUP:
0N/A object = new ThreadGroupReferenceImpl(vm, id);
0N/A break;
0N/A case JDWP.Tag.CLASS_LOADER:
0N/A object = new ClassLoaderReferenceImpl(vm, id);
0N/A break;
0N/A case JDWP.Tag.CLASS_OBJECT:
0N/A object = new ClassObjectReferenceImpl(vm, id);
0N/A break;
0N/A default:
0N/A throw new IllegalArgumentException("Invalid object tag: " + tag);
0N/A }
0N/A ref = new SoftObjectReference(key, object, referenceQueue);
0N/A
0N/A /*
0N/A * If there was no previous entry in the table, we add one here
0N/A * If the previous entry was cleared, we replace it here.
0N/A */
0N/A objectsByID.put(key, ref);
0N/A if ((traceFlags & TRACE_OBJREFS) != 0) {
0N/A printTrace("Creating new " +
0N/A object.getClass().getName() + " (id = " + id + ")");
0N/A }
0N/A } else {
0N/A ref.incrementCount();
0N/A }
0N/A
0N/A return object;
0N/A }
0N/A
0N/A synchronized void removeObjectMirror(ObjectReferenceImpl object) {
0N/A
0N/A // Handle any queue elements that are not strongly reachable
0N/A processQueue();
0N/A
0N/A SoftObjectReference ref = objectsByID.remove(new Long(object.ref()));
0N/A if (ref != null) {
0N/A batchForDispose(ref);
0N/A } else {
0N/A /*
0N/A * If there's a live ObjectReference about, it better be part
0N/A * of the cache.
0N/A */
0N/A throw new InternalException("ObjectReference " + object.ref() +
0N/A " not found in object cache");
0N/A }
0N/A }
0N/A
0N/A synchronized void removeObjectMirror(SoftObjectReference ref) {
0N/A /*
0N/A * This will remove the soft reference if it has not been
0N/A * replaced in the cache.
0N/A */
0N/A objectsByID.remove(ref.key());
0N/A }
0N/A
0N/A ObjectReferenceImpl objectMirror(long id) {
0N/A return objectMirror(id, JDWP.Tag.OBJECT);
0N/A }
0N/A
0N/A StringReferenceImpl stringMirror(long id) {
0N/A return (StringReferenceImpl)objectMirror(id, JDWP.Tag.STRING);
0N/A }
0N/A
0N/A ArrayReferenceImpl arrayMirror(long id) {
0N/A return (ArrayReferenceImpl)objectMirror(id, JDWP.Tag.ARRAY);
0N/A }
0N/A
0N/A ThreadReferenceImpl threadMirror(long id) {
0N/A return (ThreadReferenceImpl)objectMirror(id, JDWP.Tag.THREAD);
0N/A }
0N/A
0N/A ThreadGroupReferenceImpl threadGroupMirror(long id) {
0N/A return (ThreadGroupReferenceImpl)objectMirror(id,
0N/A JDWP.Tag.THREAD_GROUP);
0N/A }
0N/A
0N/A ClassLoaderReferenceImpl classLoaderMirror(long id) {
0N/A return (ClassLoaderReferenceImpl)objectMirror(id,
0N/A JDWP.Tag.CLASS_LOADER);
0N/A }
0N/A
0N/A ClassObjectReferenceImpl classObjectMirror(long id) {
0N/A return (ClassObjectReferenceImpl)objectMirror(id,
0N/A JDWP.Tag.CLASS_OBJECT);
0N/A }
0N/A
0N/A /*
0N/A * Implementation of PathSearchingVirtualMachine
0N/A */
0N/A private JDWP.VirtualMachine.ClassPaths getClasspath() {
0N/A if (pathInfo == null) {
0N/A try {
0N/A pathInfo = JDWP.VirtualMachine.ClassPaths.process(vm);
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A }
0N/A return pathInfo;
0N/A }
0N/A
0N/A public List<String> classPath() {
0N/A return Arrays.asList(getClasspath().classpaths);
0N/A }
0N/A
0N/A public List<String> bootClassPath() {
0N/A return Arrays.asList(getClasspath().bootclasspaths);
0N/A }
0N/A
0N/A public String baseDirectory() {
0N/A return getClasspath().baseDir;
0N/A }
0N/A
0N/A public void setDefaultStratum(String stratum) {
0N/A defaultStratum = stratum;
0N/A if (stratum == null) {
0N/A stratum = "";
0N/A }
0N/A try {
0N/A JDWP.VirtualMachine.SetDefaultStratum.process(vm,
0N/A stratum);
0N/A } catch (JDWPException exc) {
0N/A throw exc.toJDIException();
0N/A }
0N/A }
0N/A
0N/A public String getDefaultStratum() {
0N/A return defaultStratum;
0N/A }
0N/A
0N/A ThreadGroup threadGroupForJDI() {
0N/A return threadGroupForJDI;
0N/A }
0N/A
0N/A static private class SoftObjectReference extends SoftReference<ObjectReferenceImpl> {
0N/A int count;
0N/A Long key;
0N/A
0N/A SoftObjectReference(Long key, ObjectReferenceImpl mirror,
0N/A ReferenceQueue<ObjectReferenceImpl> queue) {
0N/A super(mirror, queue);
0N/A this.count = 1;
0N/A this.key = key;
0N/A }
0N/A
0N/A int count() {
0N/A return count;
0N/A }
0N/A
0N/A void incrementCount() {
0N/A count++;
0N/A }
0N/A
0N/A Long key() {
0N/A return key;
0N/A }
0N/A
0N/A ObjectReferenceImpl object() {
28N/A return get();
0N/A }
0N/A }
0N/A}