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