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