| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package com.sun.tools.jdi;  | 
 | 
 | 
 | 
import com.sun.jdi.*;  | 
 | 
import com.sun.jdi.connect.spi.Connection;  | 
 | 
import com.sun.jdi.request.EventRequestManager;  | 
 | 
import com.sun.jdi.request.EventRequest;  | 
 | 
import com.sun.jdi.request.BreakpointRequest;  | 
 | 
import com.sun.jdi.event.EventQueue;  | 
 | 
 | 
 | 
import java.util.*;  | 
 | 
import java.text.MessageFormat;  | 
 | 
import java.lang.ref.ReferenceQueue;  | 
 | 
import java.lang.ref.Reference;  | 
 | 
import java.lang.ref.SoftReference;  | 
 | 
import java.lang.ref.WeakReference;  | 
 | 
 | 
 | 
class VirtualMachineImpl extends MirrorImpl  | 
 | 
             implements PathSearchingVirtualMachine, ThreadListener { | 
 | 
    // VM Level exported variables, these  | 
 | 
      | 
 | 
    public final int sizeofFieldRef;  | 
 | 
    public final int sizeofMethodRef;  | 
 | 
    public final int sizeofObjectRef;  | 
 | 
    public final int sizeofClassRef;  | 
 | 
    public final int sizeofFrameRef;  | 
 | 
 | 
 | 
    final int sequenceNumber;  | 
 | 
 | 
 | 
    private final TargetVM target;  | 
 | 
    private final EventQueueImpl eventQueue;  | 
 | 
    private final EventRequestManagerImpl internalEventRequestManager;  | 
 | 
    private final EventRequestManagerImpl eventRequestManager;  | 
 | 
    final VirtualMachineManagerImpl vmManager;  | 
 | 
    private final ThreadGroup threadGroupForJDI;  | 
 | 
 | 
 | 
    // Allow direct access to this field so that that tracing code slows down  | 
 | 
      | 
 | 
    int traceFlags = TRACE_NONE;  | 
 | 
 | 
 | 
    static int TRACE_RAW_SENDS     = 0x01000000;  | 
 | 
    static int TRACE_RAW_RECEIVES  = 0x02000000;  | 
 | 
 | 
 | 
    boolean traceReceives = false;     | 
 | 
 | 
 | 
    // ReferenceType access - updated with class prepare and unload events  | 
 | 
    // Protected by "synchronized(this)". "retrievedAllTypes" may be  | 
 | 
    // tested unsynchronized (since once true, it stays true), but must  | 
 | 
      | 
 | 
    private Map<Long, ReferenceType> typesByID;  | 
 | 
    private TreeSet<ReferenceType> typesBySignature;  | 
 | 
    private boolean retrievedAllTypes = false;  | 
 | 
 | 
 | 
      | 
 | 
    private String defaultStratum = null;  | 
 | 
 | 
 | 
    // ObjectReference cache  | 
 | 
      | 
 | 
    private final Map<Long, SoftObjectReference> objectsByID = new HashMap<Long, SoftObjectReference>();  | 
 | 
    private final ReferenceQueue<ObjectReferenceImpl> referenceQueue = new ReferenceQueue<ObjectReferenceImpl>();  | 
 | 
    static private final int DISPOSE_THRESHOLD = 50;  | 
 | 
    private final List<SoftObjectReference> batchedDisposeRequests =  | 
 | 
            Collections.synchronizedList(new ArrayList<SoftObjectReference>(DISPOSE_THRESHOLD + 10));  | 
 | 
 | 
 | 
      | 
 | 
    private JDWP.VirtualMachine.Version versionInfo;  | 
 | 
    private JDWP.VirtualMachine.ClassPaths pathInfo;  | 
 | 
    private JDWP.VirtualMachine.Capabilities capabilities = null;  | 
 | 
    private JDWP.VirtualMachine.CapabilitiesNew capabilitiesNew = null;  | 
 | 
 | 
 | 
    // Per-vm singletons for primitive types and for void.  | 
 | 
      | 
 | 
    private BooleanType theBooleanType;  | 
 | 
    private ByteType    theByteType;  | 
 | 
    private CharType    theCharType;  | 
 | 
    private ShortType   theShortType;  | 
 | 
    private IntegerType theIntegerType;  | 
 | 
    private LongType    theLongType;  | 
 | 
    private FloatType   theFloatType;  | 
 | 
    private DoubleType  theDoubleType;  | 
 | 
 | 
 | 
    private VoidType    theVoidType;  | 
 | 
 | 
 | 
    private VoidValue voidVal;  | 
 | 
 | 
 | 
      | 
 | 
    private Process process;  | 
 | 
 | 
 | 
      | 
 | 
    private VMState state = new VMState(this);  | 
 | 
 | 
 | 
    private Object initMonitor = new Object();  | 
 | 
    private boolean initComplete = false;  | 
 | 
    private boolean shutdown = false;  | 
 | 
 | 
 | 
    private void notifyInitCompletion() { | 
 | 
        synchronized(initMonitor) { | 
 | 
            initComplete = true;  | 
 | 
            initMonitor.notifyAll();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    void waitInitCompletion() { | 
 | 
        synchronized(initMonitor) { | 
 | 
            while (!initComplete) { | 
 | 
                try { | 
 | 
                    initMonitor.wait();  | 
 | 
                } catch (InterruptedException e) { | 
 | 
                    // ignore  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    VMState state() { | 
 | 
        return state;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public boolean threadResumable(ThreadAction action) { | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        state.thaw(action.thread());  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
    VirtualMachineImpl(VirtualMachineManager manager,  | 
 | 
                       Connection connection, Process process,  | 
 | 
                       int sequenceNumber) { | 
 | 
        super(null);    | 
 | 
        vm = this;  | 
 | 
 | 
 | 
        this.vmManager = (VirtualMachineManagerImpl)manager;  | 
 | 
        this.process = process;  | 
 | 
        this.sequenceNumber = sequenceNumber;  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
         */  | 
 | 
        threadGroupForJDI = new ThreadGroup(vmManager.mainGroupForJDI(),  | 
 | 
                                            "JDI [" +  | 
 | 
                                            this.hashCode() + "]");  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        target = new TargetVM(this, connection);  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        EventQueueImpl internalEventQueue = new EventQueueImpl(this, target);  | 
 | 
        new InternalEventHandler(this, internalEventQueue);  | 
 | 
          | 
 | 
 | 
 | 
         */  | 
 | 
        eventQueue = new EventQueueImpl(this, target);  | 
 | 
        eventRequestManager = new EventRequestManagerImpl(this);  | 
 | 
 | 
 | 
        target.start();  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        JDWP.VirtualMachine.IDSizes idSizes;  | 
 | 
        try { | 
 | 
            idSizes = JDWP.VirtualMachine.IDSizes.process(vm);  | 
 | 
        } catch (JDWPException exc) { | 
 | 
            throw exc.toJDIException();  | 
 | 
        }  | 
 | 
        sizeofFieldRef  = idSizes.fieldIDSize;  | 
 | 
        sizeofMethodRef = idSizes.methodIDSize;  | 
 | 
        sizeofObjectRef = idSizes.objectIDSize;  | 
 | 
        sizeofClassRef = idSizes.referenceTypeIDSize;  | 
 | 
        sizeofFrameRef  = idSizes.frameIDSize;  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        internalEventRequestManager = new EventRequestManagerImpl(this);  | 
 | 
        EventRequest er = internalEventRequestManager.createClassPrepareRequest();  | 
 | 
        er.setSuspendPolicy(EventRequest.SUSPEND_NONE);  | 
 | 
        er.enable();  | 
 | 
        er = internalEventRequestManager.createClassUnloadRequest();  | 
 | 
        er.setSuspendPolicy(EventRequest.SUSPEND_NONE);  | 
 | 
        er.enable();  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        notifyInitCompletion();  | 
 | 
    }  | 
 | 
 | 
 | 
    EventRequestManagerImpl getInternalEventRequestManager() { | 
 | 
        return internalEventRequestManager;  | 
 | 
    }  | 
 | 
 | 
 | 
    void validateVM() { | 
 | 
        /*  | 
 | 
         * We no longer need to do this.  The spec now says  | 
 | 
         * that a VMDisconnected _may_ be thrown in these  | 
 | 
         * cases, not that it _will_ be thrown.  | 
 | 
         * So, to simplify things we will just let the  | 
 | 
         * caller's of this method proceed with their business.  | 
 | 
         * If the debuggee is disconnected, either because it  | 
 | 
         * crashed or finished or something, or because the  | 
 | 
         * debugger called exit() or dispose(), then if  | 
 | 
         * we end up trying to communicate with the debuggee,  | 
 | 
         * code in TargetVM will throw a VMDisconnectedException.  | 
 | 
         * This means that if we can satisfy a request without  | 
 | 
         * talking to the debuggee, (eg, with cached data) then  | 
 | 
         * VMDisconnectedException will _not_ be thrown.  | 
 | 
         * if (shutdown) { | 
 | 
         *    throw new VMDisconnectedException();  | 
 | 
         * }  | 
 | 
         */  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean equals(Object obj) { | 
 | 
        return this == obj;  | 
 | 
    }  | 
 | 
 | 
 | 
    public int hashCode() { | 
 | 
        return System.identityHashCode(this);  | 
 | 
    }  | 
 | 
 | 
 | 
    public List<ReferenceType> classesByName(String className) { | 
 | 
        validateVM();  | 
 | 
        String signature = JNITypeParser.typeNameToSignature(className);  | 
 | 
        List<ReferenceType> list;  | 
 | 
        if (retrievedAllTypes) { | 
 | 
           list = findReferenceTypes(signature);  | 
 | 
        } else { | 
 | 
           list = retrieveClassesBySignature(signature);  | 
 | 
        }  | 
 | 
        return Collections.unmodifiableList(list);  | 
 | 
    }  | 
 | 
 | 
 | 
    public List<ReferenceType> allClasses() { | 
 | 
        validateVM();  | 
 | 
 | 
 | 
        if (!retrievedAllTypes) { | 
 | 
            retrieveAllClasses();  | 
 | 
        }  | 
 | 
        ArrayList<ReferenceType> a;  | 
 | 
        synchronized (this) { | 
 | 
            a = new ArrayList<ReferenceType>(typesBySignature);  | 
 | 
        }  | 
 | 
        return Collections.unmodifiableList(a);  | 
 | 
    }  | 
 | 
 | 
 | 
    public void  | 
 | 
        redefineClasses(Map<? extends ReferenceType,byte[]> classToBytes)  | 
 | 
    { | 
 | 
        int cnt = classToBytes.size();  | 
 | 
        JDWP.VirtualMachine.RedefineClasses.ClassDef[] defs =  | 
 | 
            new JDWP.VirtualMachine.RedefineClasses.ClassDef[cnt];  | 
 | 
        validateVM();  | 
 | 
        if (!canRedefineClasses()) { | 
 | 
            throw new UnsupportedOperationException();  | 
 | 
        }  | 
 | 
        Iterator<?> it = classToBytes.entrySet().iterator();  | 
 | 
        for (int i = 0; it.hasNext(); i++) { | 
 | 
            Map.Entry<?,?> entry = (Map.Entry)it.next();  | 
 | 
            ReferenceTypeImpl refType = (ReferenceTypeImpl)entry.getKey();  | 
 | 
            validateMirror(refType);  | 
 | 
            defs[i] = new JDWP.VirtualMachine.RedefineClasses  | 
 | 
                       .ClassDef(refType, (byte[])entry.getValue());  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        vm.state().thaw();  | 
 | 
 | 
 | 
        try { | 
 | 
            JDWP.VirtualMachine.RedefineClasses.  | 
 | 
                process(vm, defs);  | 
 | 
        } catch (JDWPException exc) { | 
 | 
            switch (exc.errorCode()) { | 
 | 
            case JDWP.Error.INVALID_CLASS_FORMAT :  | 
 | 
                throw new ClassFormatError(  | 
 | 
                        "class not in class file format");  | 
 | 
            case JDWP.Error.CIRCULAR_CLASS_DEFINITION :  | 
 | 
                throw new ClassCircularityError(  | 
 | 
       "circularity has been detected while initializing a class");  | 
 | 
            case JDWP.Error.FAILS_VERIFICATION :  | 
 | 
                throw new VerifyError(  | 
 | 
   "verifier detected internal inconsistency or security problem");  | 
 | 
            case JDWP.Error.UNSUPPORTED_VERSION :  | 
 | 
                throw new UnsupportedClassVersionError(  | 
 | 
                    "version numbers of class are not supported");  | 
 | 
            case JDWP.Error.ADD_METHOD_NOT_IMPLEMENTED:  | 
 | 
                throw new UnsupportedOperationException(  | 
 | 
                              "add method not implemented");  | 
 | 
            case JDWP.Error.SCHEMA_CHANGE_NOT_IMPLEMENTED :  | 
 | 
                throw new UnsupportedOperationException(  | 
 | 
                              "schema change not implemented");  | 
 | 
            case JDWP.Error.HIERARCHY_CHANGE_NOT_IMPLEMENTED:  | 
 | 
                throw new UnsupportedOperationException(  | 
 | 
                              "hierarchy change not implemented");  | 
 | 
            case JDWP.Error.DELETE_METHOD_NOT_IMPLEMENTED :  | 
 | 
                throw new UnsupportedOperationException(  | 
 | 
                              "delete method not implemented");  | 
 | 
            case JDWP.Error.CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED:  | 
 | 
                throw new UnsupportedOperationException(  | 
 | 
                       "changes to class modifiers not implemented");  | 
 | 
            case JDWP.Error.METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED :  | 
 | 
                throw new UnsupportedOperationException(  | 
 | 
                       "changes to method modifiers not implemented");  | 
 | 
            case JDWP.Error.NAMES_DONT_MATCH :  | 
 | 
                throw new NoClassDefFoundError(  | 
 | 
                              "class names do not match");  | 
 | 
            default:  | 
 | 
                throw exc.toJDIException();  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        List<BreakpointRequest> toDelete = new ArrayList<BreakpointRequest>();  | 
 | 
        EventRequestManager erm = eventRequestManager();  | 
 | 
        it = erm.breakpointRequests().iterator();  | 
 | 
        while (it.hasNext()) { | 
 | 
            BreakpointRequest req = (BreakpointRequest)it.next();  | 
 | 
            if (classToBytes.containsKey(req.location().declaringType())) { | 
 | 
                toDelete.add(req);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        erm.deleteEventRequests(toDelete);  | 
 | 
 | 
 | 
          | 
 | 
        it = classToBytes.keySet().iterator();  | 
 | 
        while (it.hasNext()) { | 
 | 
            ReferenceTypeImpl rti = (ReferenceTypeImpl)it.next();  | 
 | 
            rti.noticeRedefineClass();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public List<ThreadReference> allThreads() { | 
 | 
        validateVM();  | 
 | 
        return state.allThreads();  | 
 | 
    }  | 
 | 
 | 
 | 
    public List<ThreadGroupReference> topLevelThreadGroups() { | 
 | 
        validateVM();  | 
 | 
        return state.topLevelThreadGroups();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    PacketStream sendResumingCommand(CommandSender sender) { | 
 | 
        return state.thawCommand(sender);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    void notifySuspend() { | 
 | 
        state.freeze();  | 
 | 
    }  | 
 | 
 | 
 | 
    public void suspend() { | 
 | 
        validateVM();  | 
 | 
        try { | 
 | 
            JDWP.VirtualMachine.Suspend.process(vm);  | 
 | 
        } catch (JDWPException exc) { | 
 | 
            throw exc.toJDIException();  | 
 | 
        }  | 
 | 
        notifySuspend();  | 
 | 
    }  | 
 | 
 | 
 | 
    public void resume() { | 
 | 
        validateVM();  | 
 | 
        CommandSender sender =  | 
 | 
            new CommandSender() { | 
 | 
                public PacketStream send() { | 
 | 
                    return JDWP.VirtualMachine.Resume.enqueueCommand(vm);  | 
 | 
                }  | 
 | 
        };  | 
 | 
        try { | 
 | 
            PacketStream stream = state.thawCommand(sender);  | 
 | 
            JDWP.VirtualMachine.Resume.waitForReply(vm, stream);  | 
 | 
        } catch (VMDisconnectedException exc) { | 
 | 
            /*  | 
 | 
             * If the debugger makes a VMDeathRequest with SUSPEND_ALL,  | 
 | 
             * then when it does an EventSet.resume after getting the  | 
 | 
             * VMDeathEvent, the normal flow of events is that the  | 
 | 
             * BE shuts down, but the waitForReply comes back ok.  In this  | 
 | 
             * case, the run loop in TargetVM that is waiting for a packet  | 
 | 
             * gets an EOF because the socket closes. It generates a  | 
 | 
             * VMDisconnectedEvent and everyone is happy.  | 
 | 
             * However, sometimes, the BE gets shutdown before this  | 
 | 
             * waitForReply completes.  In this case, TargetVM.waitForReply  | 
 | 
             * gets awakened with no reply and so gens a VMDisconnectedException  | 
 | 
             * which is not what we want.  It might be possible to fix this  | 
 | 
             * in the BE, but it is ok to just ignore the VMDisconnectedException  | 
 | 
             * here.  This will allow the VMDisconnectedEvent to be generated  | 
 | 
             * correctly.  And, if the debugger should happen to make another  | 
 | 
             * request, it will get a VMDisconnectedException at that time.  | 
 | 
             */  | 
 | 
        } catch (JDWPException exc) { | 
 | 
            switch (exc.errorCode()) { | 
 | 
                case JDWP.Error.VM_DEAD:  | 
 | 
                    return;  | 
 | 
                default:  | 
 | 
                    throw exc.toJDIException();  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public EventQueue eventQueue() { | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        return eventQueue;  | 
 | 
    }  | 
 | 
 | 
 | 
    public EventRequestManager eventRequestManager() { | 
 | 
        validateVM();  | 
 | 
        return eventRequestManager;  | 
 | 
    }  | 
 | 
 | 
 | 
    EventRequestManagerImpl eventRequestManagerImpl() { | 
 | 
        return eventRequestManager;  | 
 | 
    }  | 
 | 
 | 
 | 
    public BooleanValue mirrorOf(boolean value) { | 
 | 
        validateVM();  | 
 | 
        return new BooleanValueImpl(this,value);  | 
 | 
    }  | 
 | 
 | 
 | 
    public ByteValue mirrorOf(byte value) { | 
 | 
        validateVM();  | 
 | 
        return new ByteValueImpl(this,value);  | 
 | 
    }  | 
 | 
 | 
 | 
    public CharValue mirrorOf(char value) { | 
 | 
        validateVM();  | 
 | 
        return new CharValueImpl(this,value);  | 
 | 
    }  | 
 | 
 | 
 | 
    public ShortValue mirrorOf(short value) { | 
 | 
        validateVM();  | 
 | 
        return new ShortValueImpl(this,value);  | 
 | 
    }  | 
 | 
 | 
 | 
    public IntegerValue mirrorOf(int value) { | 
 | 
        validateVM();  | 
 | 
        return new IntegerValueImpl(this,value);  | 
 | 
    }  | 
 | 
 | 
 | 
    public LongValue mirrorOf(long value) { | 
 | 
        validateVM();  | 
 | 
        return new LongValueImpl(this,value);  | 
 | 
    }  | 
 | 
 | 
 | 
    public FloatValue mirrorOf(float value) { | 
 | 
        validateVM();  | 
 | 
        return new FloatValueImpl(this,value);  | 
 | 
    }  | 
 | 
 | 
 | 
    public DoubleValue mirrorOf(double value) { | 
 | 
        validateVM();  | 
 | 
        return new DoubleValueImpl(this,value);  | 
 | 
    }  | 
 | 
 | 
 | 
    public StringReference mirrorOf(String value) { | 
 | 
        validateVM();  | 
 | 
        try { | 
 | 
            return (StringReference)JDWP.VirtualMachine.CreateString.  | 
 | 
                             process(vm, value).stringObject;  | 
 | 
        } catch (JDWPException exc) { | 
 | 
            throw exc.toJDIException();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public VoidValue mirrorOfVoid() { | 
 | 
        if (voidVal == null) { | 
 | 
            voidVal = new VoidValueImpl(this);  | 
 | 
        }  | 
 | 
        return voidVal;  | 
 | 
    }  | 
 | 
 | 
 | 
    public long[] instanceCounts(List<? extends ReferenceType> classes) { | 
 | 
        if (!canGetInstanceInfo()) { | 
 | 
            throw new UnsupportedOperationException(  | 
 | 
                "target does not support getting instances");  | 
 | 
        }  | 
 | 
        long[] retValue ;  | 
 | 
        ReferenceTypeImpl[] rtArray = new ReferenceTypeImpl[classes.size()];  | 
 | 
        int ii = 0;  | 
 | 
        for (ReferenceType rti: classes) { | 
 | 
            validateMirror(rti);  | 
 | 
            rtArray[ii++] = (ReferenceTypeImpl)rti;  | 
 | 
        }  | 
 | 
        try { | 
 | 
            retValue = JDWP.VirtualMachine.InstanceCounts.  | 
 | 
                                process(vm, rtArray).counts;  | 
 | 
        } catch (JDWPException exc) { | 
 | 
            throw exc.toJDIException();  | 
 | 
        }  | 
 | 
 | 
 | 
        return retValue;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void dispose() { | 
 | 
        validateVM();  | 
 | 
        shutdown = true;  | 
 | 
        try { | 
 | 
            JDWP.VirtualMachine.Dispose.process(vm);  | 
 | 
        } catch (JDWPException exc) { | 
 | 
            throw exc.toJDIException();  | 
 | 
        }  | 
 | 
        target.stopListening();  | 
 | 
    }  | 
 | 
 | 
 | 
    public void exit(int exitCode) { | 
 | 
        validateVM();  | 
 | 
        shutdown = true;  | 
 | 
        try { | 
 | 
            JDWP.VirtualMachine.Exit.process(vm, exitCode);  | 
 | 
        } catch (JDWPException exc) { | 
 | 
            throw exc.toJDIException();  | 
 | 
        }  | 
 | 
        target.stopListening();  | 
 | 
    }  | 
 | 
 | 
 | 
    public Process process() { | 
 | 
        validateVM();  | 
 | 
        return process;  | 
 | 
    }  | 
 | 
 | 
 | 
    private JDWP.VirtualMachine.Version versionInfo() { | 
 | 
       try { | 
 | 
           if (versionInfo == null) { | 
 | 
                 | 
 | 
               versionInfo = JDWP.VirtualMachine.Version.process(vm);  | 
 | 
           }  | 
 | 
           return versionInfo;  | 
 | 
       } catch (JDWPException exc) { | 
 | 
           throw exc.toJDIException();  | 
 | 
       }  | 
 | 
   }  | 
 | 
    public String description() { | 
 | 
        validateVM();  | 
 | 
 | 
 | 
        return MessageFormat.format(vmManager.getString("version_format"), | 
 | 
                                    "" + vmManager.majorInterfaceVersion(),  | 
 | 
                                    "" + vmManager.minorInterfaceVersion(),  | 
 | 
                                     versionInfo().description);  | 
 | 
    }  | 
 | 
 | 
 | 
    public String version() { | 
 | 
        validateVM();  | 
 | 
        return versionInfo().vmVersion;  | 
 | 
    }  | 
 | 
 | 
 | 
    public String name() { | 
 | 
        validateVM();  | 
 | 
        return versionInfo().vmName;  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean canWatchFieldModification() { | 
 | 
        validateVM();  | 
 | 
        return capabilities().canWatchFieldModification;  | 
 | 
    }  | 
 | 
    public boolean canWatchFieldAccess() { | 
 | 
        validateVM();  | 
 | 
        return capabilities().canWatchFieldAccess;  | 
 | 
    }  | 
 | 
    public boolean canGetBytecodes() { | 
 | 
        validateVM();  | 
 | 
        return capabilities().canGetBytecodes;  | 
 | 
    }  | 
 | 
    public boolean canGetSyntheticAttribute() { | 
 | 
        validateVM();  | 
 | 
        return capabilities().canGetSyntheticAttribute;  | 
 | 
    }  | 
 | 
    public boolean canGetOwnedMonitorInfo() { | 
 | 
        validateVM();  | 
 | 
        return capabilities().canGetOwnedMonitorInfo;  | 
 | 
    }  | 
 | 
    public boolean canGetCurrentContendedMonitor() { | 
 | 
        validateVM();  | 
 | 
        return capabilities().canGetCurrentContendedMonitor;  | 
 | 
    }  | 
 | 
    public boolean canGetMonitorInfo() { | 
 | 
        validateVM();  | 
 | 
        return capabilities().canGetMonitorInfo;  | 
 | 
    }  | 
 | 
 | 
 | 
    private boolean hasNewCapabilities() { | 
 | 
        return versionInfo().jdwpMajor > 1 ||  | 
 | 
            versionInfo().jdwpMinor >= 4;  | 
 | 
    }  | 
 | 
 | 
 | 
    boolean canGet1_5LanguageFeatures() { | 
 | 
        return versionInfo().jdwpMajor > 1 ||  | 
 | 
            versionInfo().jdwpMinor >= 5;  | 
 | 
    }  | 
 | 
 | 
 | 
    public boolean canUseInstanceFilters() { | 
 | 
        validateVM();  | 
 | 
        return hasNewCapabilities() &&  | 
 | 
            capabilitiesNew().canUseInstanceFilters;  | 
 | 
    }  | 
 | 
    public boolean canRedefineClasses() { | 
 | 
        validateVM();  | 
 | 
        return hasNewCapabilities() &&  | 
 | 
            capabilitiesNew().canRedefineClasses;  | 
 | 
    }  | 
 | 
    public boolean canAddMethod() { | 
 | 
        validateVM();  | 
 | 
        return hasNewCapabilities() &&  | 
 | 
            capabilitiesNew().canAddMethod;  | 
 | 
    }  | 
 | 
    public boolean canUnrestrictedlyRedefineClasses() { | 
 | 
        validateVM();  | 
 | 
        return hasNewCapabilities() &&  | 
 | 
            capabilitiesNew().canUnrestrictedlyRedefineClasses;  | 
 | 
    }  | 
 | 
    public boolean canPopFrames() { | 
 | 
        validateVM();  | 
 | 
        return hasNewCapabilities() &&  | 
 | 
            capabilitiesNew().canPopFrames;  | 
 | 
    }  | 
 | 
    public boolean canGetMethodReturnValues() { | 
 | 
        return versionInfo().jdwpMajor > 1 ||  | 
 | 
            versionInfo().jdwpMinor >= 6;  | 
 | 
    }  | 
 | 
    public boolean canGetInstanceInfo() { | 
 | 
        if (versionInfo().jdwpMajor > 1 ||  | 
 | 
            versionInfo().jdwpMinor >= 6) { | 
 | 
            validateVM();  | 
 | 
            return hasNewCapabilities() &&  | 
 | 
                capabilitiesNew().canGetInstanceInfo;  | 
 | 
        } else { | 
 | 
            return false;  | 
 | 
        }  | 
 | 
    }  | 
 | 
    public boolean canUseSourceNameFilters() { | 
 | 
        return versionInfo().jdwpMajor > 1 ||  | 
 | 
            versionInfo().jdwpMinor >= 6;  | 
 | 
    }  | 
 | 
    public boolean canForceEarlyReturn() { | 
 | 
        validateVM();  | 
 | 
        return hasNewCapabilities() &&  | 
 | 
            capabilitiesNew().canForceEarlyReturn;  | 
 | 
    }  | 
 | 
    public boolean canBeModified() { | 
 | 
        return true;  | 
 | 
    }  | 
 | 
    public boolean canGetSourceDebugExtension() { | 
 | 
        validateVM();  | 
 | 
        return hasNewCapabilities() &&  | 
 | 
            capabilitiesNew().canGetSourceDebugExtension;  | 
 | 
    }  | 
 | 
    public boolean canGetClassFileVersion() { | 
 | 
        return versionInfo().jdwpMajor > 1 ||  | 
 | 
            versionInfo().jdwpMinor >= 6;  | 
 | 
    }  | 
 | 
    public boolean canGetConstantPool() { | 
 | 
        validateVM();  | 
 | 
        return hasNewCapabilities() &&  | 
 | 
            capabilitiesNew().canGetConstantPool;  | 
 | 
    }  | 
 | 
    public boolean canRequestVMDeathEvent() { | 
 | 
        validateVM();  | 
 | 
        return hasNewCapabilities() &&  | 
 | 
            capabilitiesNew().canRequestVMDeathEvent;  | 
 | 
    }  | 
 | 
    public boolean canRequestMonitorEvents() { | 
 | 
        validateVM();  | 
 | 
        return hasNewCapabilities() &&  | 
 | 
            capabilitiesNew().canRequestMonitorEvents;  | 
 | 
    }  | 
 | 
    public boolean canGetMonitorFrameInfo() { | 
 | 
        validateVM();  | 
 | 
        return hasNewCapabilities() &&  | 
 | 
            capabilitiesNew().canGetMonitorFrameInfo;  | 
 | 
    }  | 
 | 
 | 
 | 
    public void setDebugTraceMode(int traceFlags) { | 
 | 
        validateVM();  | 
 | 
        this.traceFlags = traceFlags;  | 
 | 
        this.traceReceives = (traceFlags & TRACE_RECEIVES) != 0;  | 
 | 
    }  | 
 | 
 | 
 | 
    void printTrace(String string) { | 
 | 
        System.err.println("[JDI: " + string + "]"); | 
 | 
    }  | 
 | 
 | 
 | 
    void printReceiveTrace(int depth, String string) { | 
 | 
        StringBuffer sb = new StringBuffer("Receiving:"); | 
 | 
        for (int i = depth; i > 0; --i) { | 
 | 
            sb.append("    "); | 
 | 
        }  | 
 | 
        sb.append(string);  | 
 | 
        printTrace(sb.toString());  | 
 | 
    }  | 
 | 
 | 
 | 
    private synchronized ReferenceTypeImpl addReferenceType(long id,  | 
 | 
                                                       int tag,  | 
 | 
                                                       String signature) { | 
 | 
        if (typesByID == null) { | 
 | 
            initReferenceTypes();  | 
 | 
        }  | 
 | 
        ReferenceTypeImpl type = null;  | 
 | 
        switch(tag) { | 
 | 
            case JDWP.TypeTag.CLASS:  | 
 | 
                type = new ClassTypeImpl(vm, id);  | 
 | 
                break;  | 
 | 
            case JDWP.TypeTag.INTERFACE:  | 
 | 
                type = new InterfaceTypeImpl(vm, id);  | 
 | 
                break;  | 
 | 
            case JDWP.TypeTag.ARRAY:  | 
 | 
                type = new ArrayTypeImpl(vm, id);  | 
 | 
                break;  | 
 | 
            default:  | 
 | 
                throw new InternalException("Invalid reference type tag"); | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        if (signature != null) { | 
 | 
            type.setSignature(signature);  | 
 | 
        }  | 
 | 
 | 
 | 
        typesByID.put(new Long(id), type);  | 
 | 
        typesBySignature.add(type);  | 
 | 
 | 
 | 
        if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { | 
 | 
           vm.printTrace("Caching new ReferenceType, sig=" + signature + | 
 | 
                         ", id=" + id);  | 
 | 
        }  | 
 | 
 | 
 | 
        return type;  | 
 | 
    }  | 
 | 
 | 
 | 
    synchronized void removeReferenceType(String signature) { | 
 | 
        if (typesByID == null) { | 
 | 
            return;  | 
 | 
        }  | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        Iterator<ReferenceType> iter = typesBySignature.iterator();  | 
 | 
        int matches = 0;  | 
 | 
        while (iter.hasNext()) { | 
 | 
            ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();  | 
 | 
            int comp = signature.compareTo(type.signature());  | 
 | 
            if (comp == 0) { | 
 | 
                matches++;  | 
 | 
                iter.remove();  | 
 | 
                typesByID.remove(new Long(type.ref()));  | 
 | 
                if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { | 
 | 
                   vm.printTrace("Uncaching ReferenceType, sig=" + signature + | 
 | 
                                 ", id=" + type.ref());  | 
 | 
                }  | 
 | 
/* fix for 4359077 , don't break out. list is no longer sorted  | 
 | 
        in the order we think  | 
 | 
 */  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        if (matches > 1) { | 
 | 
            retrieveClassesBySignature(signature);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private synchronized List<ReferenceType> findReferenceTypes(String signature) { | 
 | 
        if (typesByID == null) { | 
 | 
            return new ArrayList<ReferenceType>(0);  | 
 | 
        }  | 
 | 
        Iterator<ReferenceType> iter = typesBySignature.iterator();  | 
 | 
        List<ReferenceType> list = new ArrayList<ReferenceType>();  | 
 | 
        while (iter.hasNext()) { | 
 | 
            ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();  | 
 | 
            int comp = signature.compareTo(type.signature());  | 
 | 
            if (comp == 0) { | 
 | 
                list.add(type);  | 
 | 
/* fix for 4359077 , don't break out. list is no longer sorted  | 
 | 
        in the order we think  | 
 | 
 */  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return list;  | 
 | 
    }  | 
 | 
 | 
 | 
    private void initReferenceTypes() { | 
 | 
        typesByID = new HashMap<Long, ReferenceType>(300);  | 
 | 
        typesBySignature = new TreeSet<ReferenceType>();  | 
 | 
    }  | 
 | 
 | 
 | 
    ReferenceTypeImpl referenceType(long ref, byte tag) { | 
 | 
        return referenceType(ref, tag, null);  | 
 | 
    }  | 
 | 
 | 
 | 
    ClassTypeImpl classType(long ref) { | 
 | 
        return (ClassTypeImpl)referenceType(ref, JDWP.TypeTag.CLASS, null);  | 
 | 
    }  | 
 | 
 | 
 | 
    InterfaceTypeImpl interfaceType(long ref) { | 
 | 
        return (InterfaceTypeImpl)referenceType(ref, JDWP.TypeTag.INTERFACE, null);  | 
 | 
    }  | 
 | 
 | 
 | 
    ArrayTypeImpl arrayType(long ref) { | 
 | 
        return (ArrayTypeImpl)referenceType(ref, JDWP.TypeTag.ARRAY, null);  | 
 | 
    }  | 
 | 
 | 
 | 
    ReferenceTypeImpl referenceType(long id, int tag,  | 
 | 
                                                 String signature) { | 
 | 
        if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { | 
 | 
            StringBuffer sb = new StringBuffer();  | 
 | 
            sb.append("Looking up "); | 
 | 
            if (tag == JDWP.TypeTag.CLASS) { | 
 | 
                sb.append("Class"); | 
 | 
            } else if (tag == JDWP.TypeTag.INTERFACE) { | 
 | 
                sb.append("Interface"); | 
 | 
            } else if (tag == JDWP.TypeTag.ARRAY) { | 
 | 
                sb.append("ArrayType"); | 
 | 
            } else { | 
 | 
                sb.append("UNKNOWN TAG: " + tag); | 
 | 
            }  | 
 | 
            if (signature != null) { | 
 | 
                sb.append(", signature='" + signature + "'"); | 
 | 
            }  | 
 | 
            sb.append(", id=" + id); | 
 | 
            vm.printTrace(sb.toString());  | 
 | 
        }  | 
 | 
        if (id == 0) { | 
 | 
            return null;  | 
 | 
        } else { | 
 | 
            ReferenceTypeImpl retType = null;  | 
 | 
            synchronized (this) { | 
 | 
                if (typesByID != null) { | 
 | 
                    retType = (ReferenceTypeImpl)typesByID.get(new Long(id));  | 
 | 
                }  | 
 | 
                if (retType == null) { | 
 | 
                    retType = addReferenceType(id, tag, signature);  | 
 | 
                }  | 
 | 
            }  | 
 | 
            return retType;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private JDWP.VirtualMachine.Capabilities capabilities() { | 
 | 
        if (capabilities == null) { | 
 | 
            try { | 
 | 
                capabilities = JDWP.VirtualMachine  | 
 | 
                                 .Capabilities.process(vm);  | 
 | 
            } catch (JDWPException exc) { | 
 | 
                throw exc.toJDIException();  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return capabilities;  | 
 | 
    }  | 
 | 
 | 
 | 
    private JDWP.VirtualMachine.CapabilitiesNew capabilitiesNew() { | 
 | 
        if (capabilitiesNew == null) { | 
 | 
            try { | 
 | 
                capabilitiesNew = JDWP.VirtualMachine  | 
 | 
                                 .CapabilitiesNew.process(vm);  | 
 | 
            } catch (JDWPException exc) { | 
 | 
                throw exc.toJDIException();  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return capabilitiesNew;  | 
 | 
    }  | 
 | 
 | 
 | 
    private List<ReferenceType> retrieveClassesBySignature(String signature) { | 
 | 
        if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { | 
 | 
            vm.printTrace("Retrieving matching ReferenceTypes, sig=" + signature); | 
 | 
        }  | 
 | 
        JDWP.VirtualMachine.ClassesBySignature.ClassInfo[] cinfos;  | 
 | 
        try { | 
 | 
            cinfos = JDWP.VirtualMachine.ClassesBySignature.  | 
 | 
                                      process(vm, signature).classes;  | 
 | 
        } catch (JDWPException exc) { | 
 | 
            throw exc.toJDIException();  | 
 | 
        }  | 
 | 
 | 
 | 
        int count = cinfos.length;  | 
 | 
        List<ReferenceType> list = new ArrayList<ReferenceType>(count);  | 
 | 
 | 
 | 
          | 
 | 
        synchronized (this) { | 
 | 
            for (int i = 0; i < count; i++) { | 
 | 
                JDWP.VirtualMachine.ClassesBySignature.ClassInfo ci =  | 
 | 
                                                               cinfos[i];  | 
 | 
                ReferenceTypeImpl type = referenceType(ci.typeID,  | 
 | 
                                                       ci.refTypeTag,  | 
 | 
                                                       signature);  | 
 | 
                type.setStatus(ci.status);  | 
 | 
                list.add(type);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return list;  | 
 | 
    }  | 
 | 
 | 
 | 
    private void retrieveAllClasses1_4() { | 
 | 
        JDWP.VirtualMachine.AllClasses.ClassInfo[] cinfos;  | 
 | 
        try { | 
 | 
            cinfos = JDWP.VirtualMachine.AllClasses.process(vm).classes;  | 
 | 
        } catch (JDWPException exc) { | 
 | 
            throw exc.toJDIException();  | 
 | 
        }  | 
 | 
 | 
 | 
        // Hold lock during processing to improve performance  | 
 | 
          | 
 | 
        synchronized (this) { | 
 | 
            if (!retrievedAllTypes) { | 
 | 
                  | 
 | 
                int count = cinfos.length;  | 
 | 
                for (int i=0; i<count; i++) { | 
 | 
                    JDWP.VirtualMachine.AllClasses.ClassInfo ci =  | 
 | 
                                                               cinfos[i];  | 
 | 
                    ReferenceTypeImpl type = referenceType(ci.typeID,  | 
 | 
                                                           ci.refTypeTag,  | 
 | 
                                                           ci.signature);  | 
 | 
                    type.setStatus(ci.status);  | 
 | 
                }  | 
 | 
                retrievedAllTypes = true;  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void retrieveAllClasses() { | 
 | 
        if ((vm.traceFlags & VirtualMachine.TRACE_REFTYPES) != 0) { | 
 | 
            vm.printTrace("Retrieving all ReferenceTypes"); | 
 | 
        }  | 
 | 
 | 
 | 
        if (!vm.canGet1_5LanguageFeatures()) { | 
 | 
            retrieveAllClasses1_4();  | 
 | 
            return;  | 
 | 
        }  | 
 | 
 | 
 | 
        /*  | 
 | 
         * To save time (assuming the caller will be  | 
 | 
         * using then) we will get the generic sigs too.  | 
 | 
         */  | 
 | 
 | 
 | 
        JDWP.VirtualMachine.AllClassesWithGeneric.ClassInfo[] cinfos;  | 
 | 
        try { | 
 | 
            cinfos = JDWP.VirtualMachine.AllClassesWithGeneric.process(vm).classes;  | 
 | 
        } catch (JDWPException exc) { | 
 | 
            throw exc.toJDIException();  | 
 | 
        }  | 
 | 
 | 
 | 
        // Hold lock during processing to improve performance  | 
 | 
          | 
 | 
        synchronized (this) { | 
 | 
            if (!retrievedAllTypes) { | 
 | 
                  | 
 | 
                int count = cinfos.length;  | 
 | 
                for (int i=0; i<count; i++) { | 
 | 
                    JDWP.VirtualMachine.AllClassesWithGeneric.ClassInfo ci =  | 
 | 
                                                               cinfos[i];  | 
 | 
                    ReferenceTypeImpl type = referenceType(ci.typeID,  | 
 | 
                                                           ci.refTypeTag,  | 
 | 
                                                           ci.signature);  | 
 | 
                    type.setGenericSignature(ci.genericSignature);  | 
 | 
                    type.setStatus(ci.status);  | 
 | 
                }  | 
 | 
                retrievedAllTypes = true;  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    void sendToTarget(Packet packet) { | 
 | 
        target.send(packet);  | 
 | 
    }  | 
 | 
 | 
 | 
    void waitForTargetReply(Packet packet) { | 
 | 
        target.waitForReply(packet);  | 
 | 
          | 
 | 
 | 
 | 
         */  | 
 | 
        processBatchedDisposes();  | 
 | 
    }  | 
 | 
 | 
 | 
    Type findBootType(String signature) throws ClassNotLoadedException { | 
 | 
        List<ReferenceType> types = retrieveClassesBySignature(signature);  | 
 | 
        Iterator<ReferenceType> iter = types.iterator();  | 
 | 
        while (iter.hasNext()) { | 
 | 
            ReferenceType type = iter.next();  | 
 | 
            if (type.classLoader() == null) { | 
 | 
                return type;  | 
 | 
            }  | 
 | 
        }  | 
 | 
        JNITypeParser parser = new JNITypeParser(signature);  | 
 | 
        throw new ClassNotLoadedException(parser.typeName(),  | 
 | 
                                         "Type " + parser.typeName() + " not loaded");  | 
 | 
    }  | 
 | 
 | 
 | 
    BooleanType theBooleanType() { | 
 | 
        if (theBooleanType == null) { | 
 | 
            synchronized(this) { | 
 | 
                if (theBooleanType == null) { | 
 | 
                    theBooleanType = new BooleanTypeImpl(this);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return theBooleanType;  | 
 | 
    }  | 
 | 
 | 
 | 
    ByteType theByteType() { | 
 | 
        if (theByteType == null) { | 
 | 
            synchronized(this) { | 
 | 
                if (theByteType == null) { | 
 | 
                    theByteType = new ByteTypeImpl(this);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return theByteType;  | 
 | 
    }  | 
 | 
 | 
 | 
    CharType theCharType() { | 
 | 
        if (theCharType == null) { | 
 | 
            synchronized(this) { | 
 | 
                if (theCharType == null) { | 
 | 
                    theCharType = new CharTypeImpl(this);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return theCharType;  | 
 | 
    }  | 
 | 
 | 
 | 
    ShortType theShortType() { | 
 | 
        if (theShortType == null) { | 
 | 
            synchronized(this) { | 
 | 
                if (theShortType == null) { | 
 | 
                    theShortType = new ShortTypeImpl(this);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return theShortType;  | 
 | 
    }  | 
 | 
 | 
 | 
    IntegerType theIntegerType() { | 
 | 
        if (theIntegerType == null) { | 
 | 
            synchronized(this) { | 
 | 
                if (theIntegerType == null) { | 
 | 
                    theIntegerType = new IntegerTypeImpl(this);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return theIntegerType;  | 
 | 
    }  | 
 | 
 | 
 | 
    LongType theLongType() { | 
 | 
        if (theLongType == null) { | 
 | 
            synchronized(this) { | 
 | 
                if (theLongType == null) { | 
 | 
                    theLongType = new LongTypeImpl(this);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return theLongType;  | 
 | 
    }  | 
 | 
 | 
 | 
    FloatType theFloatType() { | 
 | 
        if (theFloatType == null) { | 
 | 
            synchronized(this) { | 
 | 
                if (theFloatType == null) { | 
 | 
                    theFloatType = new FloatTypeImpl(this);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return theFloatType;  | 
 | 
    }  | 
 | 
 | 
 | 
    DoubleType theDoubleType() { | 
 | 
        if (theDoubleType == null) { | 
 | 
            synchronized(this) { | 
 | 
                if (theDoubleType == null) { | 
 | 
                    theDoubleType = new DoubleTypeImpl(this);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return theDoubleType;  | 
 | 
    }  | 
 | 
 | 
 | 
    VoidType theVoidType() { | 
 | 
        if (theVoidType == null) { | 
 | 
            synchronized(this) { | 
 | 
                if (theVoidType == null) { | 
 | 
                    theVoidType = new VoidTypeImpl(this);  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return theVoidType;  | 
 | 
    }  | 
 | 
 | 
 | 
    PrimitiveType primitiveTypeMirror(byte tag) { | 
 | 
        switch (tag) { | 
 | 
            case JDWP.Tag.BOOLEAN:  | 
 | 
                return theBooleanType();  | 
 | 
            case JDWP.Tag.BYTE:  | 
 | 
                return theByteType();  | 
 | 
            case JDWP.Tag.CHAR:  | 
 | 
                return theCharType();  | 
 | 
            case JDWP.Tag.SHORT:  | 
 | 
                return theShortType();  | 
 | 
            case JDWP.Tag.INT:  | 
 | 
                return theIntegerType();  | 
 | 
            case JDWP.Tag.LONG:  | 
 | 
                return theLongType();  | 
 | 
            case JDWP.Tag.FLOAT:  | 
 | 
                return theFloatType();  | 
 | 
            case JDWP.Tag.DOUBLE:  | 
 | 
                return theDoubleType();  | 
 | 
            default:  | 
 | 
                throw new IllegalArgumentException("Unrecognized primitive tag " + tag); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void processBatchedDisposes() { | 
 | 
        if (shutdown) { | 
 | 
            return;  | 
 | 
        }  | 
 | 
 | 
 | 
        JDWP.VirtualMachine.DisposeObjects.Request[] requests = null;  | 
 | 
        synchronized(batchedDisposeRequests) { | 
 | 
            int size = batchedDisposeRequests.size();  | 
 | 
            if (size >= DISPOSE_THRESHOLD) { | 
 | 
                if ((traceFlags & TRACE_OBJREFS) != 0) { | 
 | 
                    printTrace("Dispose threashold reached. Will dispose " | 
 | 
                               + size + " object references...");  | 
 | 
                }  | 
 | 
                requests = new JDWP.VirtualMachine.DisposeObjects.Request[size];  | 
 | 
                for (int i = 0; i < requests.length; i++) { | 
 | 
                    SoftObjectReference ref = batchedDisposeRequests.get(i);  | 
 | 
                    if ((traceFlags & TRACE_OBJREFS) != 0) { | 
 | 
                        printTrace("Disposing object " + ref.key().longValue() + | 
 | 
                                   " (ref count = " + ref.count() + ")");  | 
 | 
                    }  | 
 | 
 | 
 | 
                    // This is kludgy. We temporarily re-create an object  | 
 | 
                    // reference so that we can correctly pass its id to the  | 
 | 
                      | 
 | 
                    requests[i] =  | 
 | 
                        new JDWP.VirtualMachine.DisposeObjects.Request(  | 
 | 
                            new ObjectReferenceImpl(this, ref.key().longValue()),  | 
 | 
                            ref.count());  | 
 | 
                }  | 
 | 
                batchedDisposeRequests.clear();  | 
 | 
            }  | 
 | 
        }  | 
 | 
        if (requests != null) { | 
 | 
            try { | 
 | 
                JDWP.VirtualMachine.DisposeObjects.process(vm, requests);  | 
 | 
            } catch (JDWPException exc) { | 
 | 
                throw exc.toJDIException();  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void batchForDispose(SoftObjectReference ref) { | 
 | 
        if ((traceFlags & TRACE_OBJREFS) != 0) { | 
 | 
            printTrace("Batching object " + ref.key().longValue() + | 
 | 
                       " for dispose (ref count = " + ref.count() + ")");  | 
 | 
        }  | 
 | 
        batchedDisposeRequests.add(ref);  | 
 | 
    }  | 
 | 
 | 
 | 
    private void processQueue() { | 
 | 
        Reference<?> ref;  | 
 | 
        //if ((traceFlags & TRACE_OBJREFS) != 0) { | 
 | 
        //    printTrace("Checking for softly reachable objects"); | 
 | 
          | 
 | 
        while ((ref = referenceQueue.poll()) != null) { | 
 | 
            SoftObjectReference softRef = (SoftObjectReference)ref;  | 
 | 
            removeObjectMirror(softRef);  | 
 | 
            batchForDispose(softRef);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    synchronized ObjectReferenceImpl objectMirror(long id, int tag) { | 
 | 
 | 
 | 
          | 
 | 
        processQueue();  | 
 | 
 | 
 | 
        if (id == 0) { | 
 | 
            return null;  | 
 | 
        }  | 
 | 
        ObjectReferenceImpl object = null;  | 
 | 
        Long key = new Long(id);  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
         */  | 
 | 
        SoftObjectReference ref = objectsByID.get(key);  | 
 | 
        if (ref != null) { | 
 | 
            object = ref.object();  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        if (object == null) { | 
 | 
            switch (tag) { | 
 | 
                case JDWP.Tag.OBJECT:  | 
 | 
                    object = new ObjectReferenceImpl(vm, id);  | 
 | 
                    break;  | 
 | 
                case JDWP.Tag.STRING:  | 
 | 
                    object = new StringReferenceImpl(vm, id);  | 
 | 
                    break;  | 
 | 
                case JDWP.Tag.ARRAY:  | 
 | 
                    object = new ArrayReferenceImpl(vm, id);  | 
 | 
                    break;  | 
 | 
                case JDWP.Tag.THREAD:  | 
 | 
                    ThreadReferenceImpl thread =  | 
 | 
                        new ThreadReferenceImpl(vm, id);  | 
 | 
                    thread.addListener(this);  | 
 | 
                    object = thread;  | 
 | 
                    break;  | 
 | 
                case JDWP.Tag.THREAD_GROUP:  | 
 | 
                    object = new ThreadGroupReferenceImpl(vm, id);  | 
 | 
                    break;  | 
 | 
                case JDWP.Tag.CLASS_LOADER:  | 
 | 
                    object = new ClassLoaderReferenceImpl(vm, id);  | 
 | 
                    break;  | 
 | 
                case JDWP.Tag.CLASS_OBJECT:  | 
 | 
                    object = new ClassObjectReferenceImpl(vm, id);  | 
 | 
                    break;  | 
 | 
                default:  | 
 | 
                    throw new IllegalArgumentException("Invalid object tag: " + tag); | 
 | 
            }  | 
 | 
            ref = new SoftObjectReference(key, object, referenceQueue);  | 
 | 
 | 
 | 
              | 
 | 
 | 
 | 
 | 
 | 
             */  | 
 | 
            objectsByID.put(key, ref);  | 
 | 
            if ((traceFlags & TRACE_OBJREFS) != 0) { | 
 | 
                printTrace("Creating new " + | 
 | 
                           object.getClass().getName() + " (id = " + id + ")");  | 
 | 
            }  | 
 | 
        } else { | 
 | 
            ref.incrementCount();  | 
 | 
        }  | 
 | 
 | 
 | 
        return object;  | 
 | 
    }  | 
 | 
 | 
 | 
    synchronized void removeObjectMirror(ObjectReferenceImpl object) { | 
 | 
 | 
 | 
          | 
 | 
        processQueue();  | 
 | 
 | 
 | 
        SoftObjectReference ref = objectsByID.remove(new Long(object.ref()));  | 
 | 
        if (ref != null) { | 
 | 
            batchForDispose(ref);  | 
 | 
        } else { | 
 | 
              | 
 | 
 | 
 | 
 | 
 | 
             */  | 
 | 
            throw new InternalException("ObjectReference " + object.ref() + | 
 | 
                                        " not found in object cache");  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    synchronized void removeObjectMirror(SoftObjectReference ref) { | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
         */  | 
 | 
        objectsByID.remove(ref.key());  | 
 | 
    }  | 
 | 
 | 
 | 
    ObjectReferenceImpl objectMirror(long id) { | 
 | 
        return objectMirror(id, JDWP.Tag.OBJECT);  | 
 | 
    }  | 
 | 
 | 
 | 
    StringReferenceImpl stringMirror(long id) { | 
 | 
        return (StringReferenceImpl)objectMirror(id, JDWP.Tag.STRING);  | 
 | 
    }  | 
 | 
 | 
 | 
    ArrayReferenceImpl arrayMirror(long id) { | 
 | 
       return (ArrayReferenceImpl)objectMirror(id, JDWP.Tag.ARRAY);  | 
 | 
    }  | 
 | 
 | 
 | 
    ThreadReferenceImpl threadMirror(long id) { | 
 | 
        return (ThreadReferenceImpl)objectMirror(id, JDWP.Tag.THREAD);  | 
 | 
    }  | 
 | 
 | 
 | 
    ThreadGroupReferenceImpl threadGroupMirror(long id) { | 
 | 
        return (ThreadGroupReferenceImpl)objectMirror(id,  | 
 | 
                                                      JDWP.Tag.THREAD_GROUP);  | 
 | 
    }  | 
 | 
 | 
 | 
    ClassLoaderReferenceImpl classLoaderMirror(long id) { | 
 | 
        return (ClassLoaderReferenceImpl)objectMirror(id,  | 
 | 
                                                      JDWP.Tag.CLASS_LOADER);  | 
 | 
    }  | 
 | 
 | 
 | 
    ClassObjectReferenceImpl classObjectMirror(long id) { | 
 | 
        return (ClassObjectReferenceImpl)objectMirror(id,  | 
 | 
                                                      JDWP.Tag.CLASS_OBJECT);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private JDWP.VirtualMachine.ClassPaths getClasspath() { | 
 | 
        if (pathInfo == null) { | 
 | 
            try { | 
 | 
                pathInfo = JDWP.VirtualMachine.ClassPaths.process(vm);  | 
 | 
            } catch (JDWPException exc) { | 
 | 
                throw exc.toJDIException();  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return pathInfo;  | 
 | 
    }  | 
 | 
 | 
 | 
   public List<String> classPath() { | 
 | 
       return Arrays.asList(getClasspath().classpaths);  | 
 | 
   }  | 
 | 
 | 
 | 
   public List<String> bootClassPath() { | 
 | 
       return Arrays.asList(getClasspath().bootclasspaths);  | 
 | 
   }  | 
 | 
 | 
 | 
   public String baseDirectory() { | 
 | 
       return getClasspath().baseDir;  | 
 | 
   }  | 
 | 
 | 
 | 
    public void setDefaultStratum(String stratum) { | 
 | 
        defaultStratum = stratum;  | 
 | 
        if (stratum == null) { | 
 | 
            stratum = "";  | 
 | 
        }  | 
 | 
        try { | 
 | 
            JDWP.VirtualMachine.SetDefaultStratum.process(vm,  | 
 | 
                                                          stratum);  | 
 | 
        } catch (JDWPException exc) { | 
 | 
            throw exc.toJDIException();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public String getDefaultStratum() { | 
 | 
        return defaultStratum;  | 
 | 
    }  | 
 | 
 | 
 | 
    ThreadGroup threadGroupForJDI() { | 
 | 
        return threadGroupForJDI;  | 
 | 
    }  | 
 | 
 | 
 | 
   static private class SoftObjectReference extends SoftReference<ObjectReferenceImpl> { | 
 | 
       int count;  | 
 | 
       Long key;  | 
 | 
 | 
 | 
       SoftObjectReference(Long key, ObjectReferenceImpl mirror,  | 
 | 
                           ReferenceQueue<ObjectReferenceImpl> queue) { | 
 | 
           super(mirror, queue);  | 
 | 
           this.count = 1;  | 
 | 
           this.key = key;  | 
 | 
       }  | 
 | 
 | 
 | 
       int count() { | 
 | 
           return count;  | 
 | 
       }  | 
 | 
 | 
 | 
       void incrementCount() { | 
 | 
           count++;  | 
 | 
       }  | 
 | 
 | 
 | 
       Long key() { | 
 | 
           return key;  | 
 | 
       }  | 
 | 
 | 
 | 
       ObjectReferenceImpl object() { | 
 | 
           return get();  | 
 | 
       }  | 
 | 
   }  | 
 | 
}  |