diff agent/src/share/classes/sun/jvm/hotspot/jdi/VirtualMachineImpl.java @ 0:a61af66fc99e jdk7-b24

Initial load
author duke
date Sat, 01 Dec 2007 00:00:00 +0000
parents
children c18cbe5936b8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/share/classes/sun/jvm/hotspot/jdi/VirtualMachineImpl.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1232 @@
+/*
+ * Copyright 2002-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ */
+
+package sun.jvm.hotspot.jdi;
+
+import com.sun.jdi.*;
+import com.sun.jdi.event.EventQueue;
+import com.sun.jdi.request.EventRequestManager;
+
+import sun.jvm.hotspot.HotSpotAgent;
+import sun.jvm.hotspot.types.TypeDataBase;
+import sun.jvm.hotspot.oops.Klass;
+import sun.jvm.hotspot.oops.InstanceKlass;
+import sun.jvm.hotspot.oops.ArrayKlass;
+import sun.jvm.hotspot.oops.ObjArrayKlass;
+import sun.jvm.hotspot.oops.TypeArrayKlass;
+import sun.jvm.hotspot.oops.Oop;
+import sun.jvm.hotspot.oops.Instance;
+import sun.jvm.hotspot.oops.Array;
+import sun.jvm.hotspot.oops.ObjArray;
+import sun.jvm.hotspot.oops.TypeArray;
+import sun.jvm.hotspot.oops.Symbol;
+import sun.jvm.hotspot.oops.ObjectHeap;
+import sun.jvm.hotspot.oops.DefaultHeapVisitor;
+import sun.jvm.hotspot.oops.JVMDIClassStatus;
+import sun.jvm.hotspot.runtime.VM;
+import sun.jvm.hotspot.runtime.JavaThread;
+import sun.jvm.hotspot.memory.SystemDictionary;
+import sun.jvm.hotspot.memory.SymbolTable;
+import sun.jvm.hotspot.memory.Universe;
+import sun.jvm.hotspot.utilities.Assert;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Iterator;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Observer;
+import java.util.StringTokenizer;
+import java.lang.ref.SoftReference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.Reference;
+
+public class VirtualMachineImpl extends MirrorImpl implements PathSearchingVirtualMachine {
+
+    private HotSpotAgent     saAgent = new HotSpotAgent();
+    private VM               saVM;
+    private Universe         saUniverse;
+    private SystemDictionary saSystemDictionary;
+    private SymbolTable      saSymbolTable;
+    private ObjectHeap       saObjectHeap;
+
+    VM saVM() {
+        return saVM;
+    }
+
+    SystemDictionary saSystemDictionary() {
+        return saSystemDictionary;
+    }
+
+    SymbolTable saSymbolTable() {
+        return saSymbolTable;
+    }
+
+    Universe saUniverse() {
+        return saUniverse;
+    }
+
+    ObjectHeap saObjectHeap() {
+        return saObjectHeap;
+    }
+
+    com.sun.jdi.VirtualMachineManager vmmgr;
+
+    private final ThreadGroup             threadGroupForJDI;
+
+    // Per-vm singletons for primitive types and for void.
+    // singleton-ness protected by "synchronized(this)".
+    private BooleanType theBooleanType;
+    private ByteType    theByteType;
+    private CharType    theCharType;
+    private ShortType   theShortType;
+    private IntegerType theIntegerType;
+    private LongType    theLongType;
+    private FloatType   theFloatType;
+    private DoubleType  theDoubleType;
+
+    private VoidType    theVoidType;
+
+    private VoidValue voidVal;
+    private Map       typesByID;             // Map<Klass, ReferenceTypeImpl>
+    private List      typesBySignature;      // List<ReferenceTypeImpl> - used in signature search
+    private boolean   retrievedAllTypes = false;
+    private List      bootstrapClasses;      // all bootstrap classes
+    private ArrayList allThreads;
+    private ArrayList topLevelGroups;
+    final   int       sequenceNumber;
+
+    // ObjectReference cache
+    // "objectsByID" protected by "synchronized(this)".
+    private final Map            objectsByID = new HashMap();
+    private final ReferenceQueue referenceQueue = new ReferenceQueue();
+
+    // names of some well-known classes to jdi
+    private Symbol javaLangString;
+    private Symbol javaLangThread;
+    private Symbol javaLangThreadGroup;
+    private Symbol javaLangClass;
+    private Symbol javaLangClassLoader;
+
+    // used in ReferenceTypeImpl.isThrowableBacktraceField
+    private Symbol javaLangThrowable;
+
+    // names of classes used in array assignment check
+    // refer to ArrayTypeImpl.isAssignableTo
+    private Symbol javaLangObject;
+    private Symbol javaLangCloneable;
+    private Symbol javaIoSerializable;
+
+    // symbol used in ClassTypeImpl.isEnum check
+    private Symbol javaLangEnum;
+
+    Symbol javaLangObject() {
+        return javaLangObject;
+    }
+
+    Symbol javaLangCloneable() {
+        return javaLangCloneable;
+    }
+
+    Symbol javaIoSerializable() {
+        return javaIoSerializable;
+    }
+
+    Symbol javaLangEnum() {
+        return javaLangEnum;
+    }
+
+    Symbol javaLangThrowable() {
+        return javaLangThrowable;
+    }
+
+    // name of the current default stratum
+    private String defaultStratum;
+
+    // initialize known class name symbols
+    private void initClassNameSymbols() {
+        SymbolTable st = saSymbolTable();
+        javaLangString = st.probe("java/lang/String");
+        javaLangThread = st.probe("java/lang/Thread");
+        javaLangThreadGroup = st.probe("java/lang/ThreadGroup");
+        javaLangClass = st.probe("java/lang/Class");
+        javaLangClassLoader = st.probe("java/lang/ClassLoader");
+        javaLangThrowable = st.probe("java/lang/Throwable");
+        javaLangObject = st.probe("java/lang/Object");
+        javaLangCloneable = st.probe("java/lang/Cloneable");
+        javaIoSerializable = st.probe("java/io/Serializable");
+        javaLangEnum = st.probe("java/lang/Enum");
+    }
+
+    private void init() {
+        saVM = VM.getVM();
+        saUniverse = saVM.getUniverse();
+        saSystemDictionary = saVM.getSystemDictionary();
+        saSymbolTable = saVM.getSymbolTable();
+        saObjectHeap = saVM.getObjectHeap();
+        initClassNameSymbols();
+    }
+
+    static public VirtualMachineImpl createVirtualMachineForCorefile(VirtualMachineManager mgr,
+                                                                     String javaExecutableName,
+                                                                     String coreFileName,
+                                                                     int sequenceNumber)
+        throws Exception {
+        if (Assert.ASSERTS_ENABLED) {
+            Assert.that(coreFileName != null, "SA VirtualMachineImpl: core filename = null is not yet implemented");
+        }
+        if (Assert.ASSERTS_ENABLED) {
+            Assert.that(javaExecutableName != null, "SA VirtualMachineImpl: java executable = null is not yet implemented");
+        }
+
+        VirtualMachineImpl myvm = new VirtualMachineImpl(mgr, sequenceNumber);
+        try {
+            myvm.saAgent.attach(javaExecutableName, coreFileName);
+            myvm.init();
+        } catch (Exception ee) {
+            myvm.saAgent.detach();
+            throw ee;
+        }
+        return myvm;
+    }
+
+    static public VirtualMachineImpl createVirtualMachineForPID(VirtualMachineManager mgr,
+                                                                int pid,
+                                                                int sequenceNumber)
+        throws Exception {
+
+        VirtualMachineImpl myvm = new VirtualMachineImpl(mgr, sequenceNumber);
+        try {
+            myvm.saAgent.attach(pid);
+            myvm.init();
+        } catch (Exception ee) {
+            myvm.saAgent.detach();
+            throw ee;
+        }
+        return myvm;
+    }
+
+    static public VirtualMachineImpl createVirtualMachineForServer(VirtualMachineManager mgr,
+                                                                String server,
+                                                                int sequenceNumber)
+        throws Exception {
+        if (Assert.ASSERTS_ENABLED) {
+            Assert.that(server != null, "SA VirtualMachineImpl: DebugServer = null is not yet implemented");
+        }
+
+        VirtualMachineImpl myvm = new VirtualMachineImpl(mgr, sequenceNumber);
+        try {
+            myvm.saAgent.attach(server);
+            myvm.init();
+        } catch (Exception ee) {
+            myvm.saAgent.detach();
+            throw ee;
+        }
+        return myvm;
+    }
+
+
+    VirtualMachineImpl(VirtualMachineManager mgr, int sequenceNumber)
+        throws Exception {
+        super(null);  // Can't use super(this)
+        vm = this;
+
+        this.sequenceNumber = sequenceNumber;
+        this.vmmgr = mgr;
+
+        /* Create ThreadGroup to be used by all threads servicing
+         * this VM.
+         */
+        threadGroupForJDI = new ThreadGroup("JDI [" +
+                                            this.hashCode() + "]");
+
+        ((com.sun.tools.jdi.VirtualMachineManagerImpl)mgr).addVirtualMachine(this);
+
+        // By default SA agent classes prefer dbx debugger to proc debugger
+        // and Windows process debugger to windbg debugger. SA expects
+        // special properties to be set to choose other debuggers. We will set
+        // those here before attaching to SA agent.
+
+        System.setProperty("sun.jvm.hotspot.debugger.useProcDebugger", "true");
+        System.setProperty("sun.jvm.hotspot.debugger.useWindbgDebugger", "true");
+    }
+
+    // we reflectively use newly spec'ed class because our ALT_BOOTDIR
+    // is 1.4.2 and not 1.5.
+    private static Class vmCannotBeModifiedExceptionClass = null;
+    void throwNotReadOnlyException(String operation) {
+        RuntimeException re = null;
+        if (vmCannotBeModifiedExceptionClass == null) {
+            try {
+                vmCannotBeModifiedExceptionClass = Class.forName("com.sun.jdi.VMCannotBeModifiedException");
+            } catch (ClassNotFoundException cnfe) {
+                vmCannotBeModifiedExceptionClass = UnsupportedOperationException.class;
+            }
+        }
+        try {
+            re = (RuntimeException) vmCannotBeModifiedExceptionClass.newInstance();
+        } catch (Exception exp) {
+            re = new RuntimeException(exp.getMessage());
+        }
+        throw re;
+    }
+
+    public boolean equals(Object obj) {
+        // Oh boy; big recursion troubles if we don't have this!
+        // See MirrorImpl.equals
+        return this == obj;
+    }
+
+    public int hashCode() {
+        // big recursion if we don't have this. See MirrorImpl.hashCode
+        return System.identityHashCode(this);
+    }
+
+    public List classesByName(String className) {
+        String signature = JNITypeParser.typeNameToSignature(className);
+        List list;
+        if (!retrievedAllTypes) {
+            retrieveAllClasses();
+        }
+        list = findReferenceTypes(signature);
+        return Collections.unmodifiableList(list);
+    }
+
+    public List allClasses() {
+        if (!retrievedAllTypes) {
+            retrieveAllClasses();
+        }
+        ArrayList a;
+        synchronized (this) {
+            a = new ArrayList(typesBySignature);
+        }
+        return Collections.unmodifiableList(a);
+    }
+
+    // classes loaded by bootstrap loader
+    List bootstrapClasses() {
+        if (bootstrapClasses == null) {
+            bootstrapClasses = new ArrayList();
+            List all = allClasses();
+            for (Iterator itr = all.iterator(); itr.hasNext();) {
+               ReferenceType type = (ReferenceType) itr.next();
+               if (type.classLoader() == null) {
+                   bootstrapClasses.add(type);
+               }
+            }
+        }
+        return bootstrapClasses;
+    }
+
+    private synchronized List findReferenceTypes(String signature) {
+        if (typesByID == null) {
+            return new ArrayList(0);
+        }
+
+        // we haven't sorted types by signatures. But we can take
+        // advantage of comparing symbols instead of name. In the worst
+        // case, we will be comparing N addresses rather than N strings
+        // where N being total no. of classes in allClasses() list.
+
+        // The signature could be Lx/y/z; or [....
+        // If it is Lx/y/z; the internal type name is x/y/x
+        // for array klasses internal type name is same as
+        // signature
+        String typeName = null;
+        if (signature.charAt(0) == 'L') {
+            typeName = signature.substring(1, signature.length() - 1);
+        } else {
+            typeName = signature;
+        }
+
+        Symbol typeNameSym = saSymbolTable().probe(typeName);
+        // if there is no symbol in VM, then we wouldn't have that type
+        if (typeNameSym == null) {
+            return new ArrayList(0);
+        }
+
+        Iterator iter = typesBySignature.iterator();
+        List list = new ArrayList();
+        while (iter.hasNext()) {
+            // We have cached type name as symbol in reference type
+            ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
+            if (typeNameSym.equals(type.typeNameAsSymbol())) {
+                list.add(type);
+            }
+        }
+        return list;
+    }
+
+    private void retrieveAllClasses() {
+        final List saKlasses = new ArrayList();
+        SystemDictionary.ClassVisitor visitor = new SystemDictionary.ClassVisitor() {
+                public void visit(Klass k) {
+                    for (Klass l = k; l != null; l = l.arrayKlassOrNull()) {
+                        // for non-array classes filter out un-prepared classes
+                        // refer to 'allClasses' in share/back/VirtualMachineImpl.c
+                        if (l instanceof ArrayKlass) {
+                           saKlasses.add(l);
+                        } else {
+                           int status = l.getClassStatus();
+                           if ((status & JVMDIClassStatus.PREPARED) != 0) {
+                               saKlasses.add(l);
+                           }
+                        }
+                    }
+                }
+        };
+
+        // refer to jvmtiGetLoadedClasses.cpp - getLoadedClasses in VM code.
+
+        // classes from SystemDictionary
+        saSystemDictionary.classesDo(visitor);
+
+        // From SystemDictionary we do not get primitive single
+        // dimensional array classes. add primitive single dimensional array
+        // klasses from Universe.
+        saVM.getUniverse().basicTypeClassesDo(visitor);
+
+        // Hold lock during processing to improve performance
+        // and to have safe check/set of retrievedAllTypes
+        synchronized (this) {
+            if (!retrievedAllTypes) {
+                // Number of classes
+                int count = saKlasses.size();
+                for (int ii = 0; ii < count; ii++) {
+                    Klass kk = (Klass)saKlasses.get(ii);
+                    ReferenceTypeImpl type = referenceType(kk);
+                }
+                retrievedAllTypes = true;
+            }
+        }
+    }
+
+    ReferenceTypeImpl referenceType(Klass kk) {
+        ReferenceTypeImpl retType = null;
+        synchronized (this) {
+            if (typesByID != null) {
+                retType = (ReferenceTypeImpl)typesByID.get(kk);
+            }
+            if (retType == null) {
+                retType = addReferenceType(kk);
+            }
+        }
+        return retType;
+    }
+
+    private void initReferenceTypes() {
+        typesByID = new HashMap();
+        typesBySignature = new ArrayList();
+    }
+
+    private synchronized ReferenceTypeImpl addReferenceType(Klass kk) {
+        if (typesByID == null) {
+            initReferenceTypes();
+        }
+        ReferenceTypeImpl newRefType = null;
+        if (kk instanceof ObjArrayKlass || kk instanceof TypeArrayKlass) {
+            newRefType = new ArrayTypeImpl(this, (ArrayKlass)kk);
+        } else if (kk instanceof InstanceKlass) {
+            if (kk.isInterface()) {
+                newRefType = new InterfaceTypeImpl(this, (InstanceKlass)kk);
+            } else {
+                newRefType = new ClassTypeImpl(this, (InstanceKlass)kk);
+            }
+        } else {
+            throw new RuntimeException("should not reach here");
+        }
+
+        typesByID.put(kk, newRefType);
+        typesBySignature.add(newRefType);
+        return newRefType;
+    }
+
+    ThreadGroup threadGroupForJDI() {
+        return threadGroupForJDI;
+    }
+
+    public void redefineClasses(Map classToBytes) {
+        throwNotReadOnlyException("VirtualMachineImpl.redefineClasses()");
+    }
+
+    private List getAllThreads() {
+        if (allThreads == null) {
+            allThreads = new ArrayList(10);  // Might be enough, might not be
+            for (sun.jvm.hotspot.runtime.JavaThread thread =
+                     saVM.getThreads().first(); thread != null;
+                     thread = thread.next()) {
+                // refer to JvmtiEnv::GetAllThreads in jvmtiEnv.cpp.
+                // filter out the hidden-from-external-view threads.
+                if (thread.isHiddenFromExternalView() == false) {
+                    ThreadReferenceImpl myThread = threadMirror(thread);
+                    allThreads.add(myThread);
+                }
+            }
+        }
+        return allThreads;
+    }
+
+    public List allThreads() { //fixme jjh
+        return Collections.unmodifiableList(getAllThreads());
+    }
+
+    public void suspend() {
+        throwNotReadOnlyException("VirtualMachineImpl.suspend()");
+    }
+
+    public void resume() {
+        throwNotReadOnlyException("VirtualMachineImpl.resume()");
+    }
+
+    public List topLevelThreadGroups() { //fixme jjh
+        // The doc for ThreadGroup says that The top-level thread group
+        // is the only thread group whose parent is null.  This means there is
+        // only one top level thread group.  There will be a thread in this
+        // group so we will just find a thread whose threadgroup has no parent
+        // and that will be it.
+
+        if (topLevelGroups == null) {
+            topLevelGroups = new ArrayList(1);
+            Iterator myIt = getAllThreads().iterator();
+            while (myIt.hasNext()) {
+                ThreadReferenceImpl myThread = (ThreadReferenceImpl)myIt.next();
+                ThreadGroupReference myGroup = myThread.threadGroup();
+                ThreadGroupReference myParent = myGroup.parent();
+                if (myGroup.parent() == null) {
+                    topLevelGroups.add(myGroup);
+                    break;
+                }
+            }
+        }
+        return  Collections.unmodifiableList(topLevelGroups);
+    }
+
+    public EventQueue eventQueue() {
+        throwNotReadOnlyException("VirtualMachine.eventQueue()");
+        return null;
+    }
+
+    public EventRequestManager eventRequestManager() {
+        throwNotReadOnlyException("VirtualMachineImpl.eventRequestManager()");
+        return null;
+    }
+
+    public BooleanValue mirrorOf(boolean value) {
+        return new BooleanValueImpl(this,value);
+    }
+
+    public ByteValue mirrorOf(byte value) {
+        return new ByteValueImpl(this,value);
+    }
+
+    public CharValue mirrorOf(char value) {
+        return new CharValueImpl(this,value);
+    }
+
+    public ShortValue mirrorOf(short value) {
+        return new ShortValueImpl(this,value);
+    }
+
+    public IntegerValue mirrorOf(int value) {
+        return new IntegerValueImpl(this,value);
+    }
+
+    public LongValue mirrorOf(long value) {
+        return new LongValueImpl(this,value);
+    }
+
+    public FloatValue mirrorOf(float value) {
+        return new FloatValueImpl(this,value);
+    }
+
+    public DoubleValue mirrorOf(double value) {
+        return new DoubleValueImpl(this,value);
+    }
+
+    public StringReference mirrorOf(String value) {
+        throwNotReadOnlyException("VirtualMachinestop.mirrorOf(String)");
+        return null;
+    }
+
+    public VoidValue mirrorOfVoid() {
+        if (voidVal == null) {
+            voidVal = new VoidValueImpl(this);
+        }
+        return voidVal;
+    }
+
+
+    public Process process() {
+        throwNotReadOnlyException("VirtualMachine.process");
+        return null;
+    }
+
+    // dispose observer for Class re-use. refer to ConnectorImpl.
+    private Observer disposeObserver;
+
+    // ConnectorImpl loaded by a different class loader can not access it.
+    // i.e., runtime package of <ConnectorImpl, L1> is not the same that of
+    // <VirtualMachineImpl, L2> when L1 != L2. So, package private method
+    // can be called reflectively after using setAccessible(true).
+
+    void setDisposeObserver(Observer observer) {
+       disposeObserver = observer;
+    }
+
+    private void notifyDispose() {
+        if (Assert.ASSERTS_ENABLED) {
+            Assert.that(disposeObserver != null, "null VM.dispose observer");
+        }
+        disposeObserver.update(null, null);
+    }
+
+    public void dispose() {
+        saAgent.detach();
+        notifyDispose();
+    }
+
+    public void exit(int exitCode) {
+        throwNotReadOnlyException("VirtualMachine.exit(int)");
+    }
+
+    public boolean canBeModified() {
+        return false;
+    }
+
+    public boolean canWatchFieldModification() {
+        return false;
+    }
+
+    public boolean canWatchFieldAccess() {
+        return false;
+    }
+
+    public boolean canGetBytecodes() {
+        return true;
+    }
+
+    public boolean canGetSyntheticAttribute() {
+        return true;
+    }
+
+    // FIXME: For now, all monitor capabilities are disabled
+    public boolean canGetOwnedMonitorInfo() {
+        return false;
+    }
+
+    public boolean canGetCurrentContendedMonitor() {
+        return false;
+    }
+
+    public boolean canGetMonitorInfo() {
+        return false;
+    }
+
+    // because this SA works only with 1.5 and update releases
+    // this should always succeed unlike JVMDI/JDI.
+    public boolean canGet1_5LanguageFeatures() {
+        return true;
+    }
+
+    public boolean canUseInstanceFilters() {
+        return false;
+    }
+
+    public boolean canRedefineClasses() {
+        return false;
+    }
+
+    public boolean canAddMethod() {
+        return false;
+    }
+
+    public boolean canUnrestrictedlyRedefineClasses() {
+        return false;
+    }
+
+    public boolean canPopFrames() {
+        return false;
+    }
+
+    public boolean canGetSourceDebugExtension() {
+        // We can use InstanceKlass.getSourceDebugExtension only if
+        // ClassFileParser parsed the info. But, ClassFileParser parses
+        // SourceDebugExtension attribute only if corresponding JVMDI/TI
+        // capability is set to true. Currently, vmStructs does not expose
+        // JVMDI/TI capabilities and hence we conservatively assume false.
+        return false;
+    }
+
+    public boolean canRequestVMDeathEvent() {
+        return false;
+    }
+
+    // new method since 1.6
+    public boolean canForceEarlyReturn() {
+        return false;
+    }
+
+    // new method since 1.6
+    public boolean canGetConstantPool() {
+        return true;
+    }
+
+    // new method since 1.6
+    public boolean canGetClassFileVersion() {
+        return true;
+    }
+
+    // new method since 1.6.
+    public boolean canGetMethodReturnValues() {
+        return false;
+    }
+
+    // new method since 1.6
+    // Real body will be supplied later.
+    public boolean canGetInstanceInfo() {
+        return true;
+    }
+
+    // new method since 1.6
+    public boolean canUseSourceNameFilters() {
+        return false;
+    }
+
+    // new method since 1.6.
+    public boolean canRequestMonitorEvents() {
+        return false;
+    }
+
+    // new method since 1.6.
+    public boolean canGetMonitorFrameInfo() {
+        return true;
+    }
+
+    // new method since 1.6
+    // Real body will be supplied later.
+    public long[] instanceCounts(List classes) {
+        if (!canGetInstanceInfo()) {
+            throw new UnsupportedOperationException(
+                      "target does not support getting instances");
+        }
+
+        final long[] retValue = new long[classes.size()] ;
+
+        final Klass [] klassArray = new Klass[classes.size()];
+
+        boolean allAbstractClasses = true;
+        for (int i=0; i < classes.size(); i++) {
+            ReferenceTypeImpl rti = (ReferenceTypeImpl)classes.get(i);
+            klassArray[i] = rti.ref();
+            retValue[i]=0;
+            if (!(rti.isAbstract() || ((ReferenceType)rti instanceof InterfaceType))) {
+                allAbstractClasses = false;
+            }
+        }
+
+        if (allAbstractClasses) {
+            return retValue;
+        }
+        final int size = classes.size();
+        saObjectHeap.iterate(new DefaultHeapVisitor() {
+                public boolean doObj(Oop oop) {
+                    for (int i=0; i < size; i++) {
+                        if (klassArray[i].equals(oop.getKlass())) {
+                            retValue[i]++;
+                            break;
+                        }
+                    }
+                                        return false;
+                }
+            });
+
+        return retValue;
+    }
+
+    private List getPath (String pathName) {
+        String cp = saVM.getSystemProperty(pathName);
+        String pathSep = saVM.getSystemProperty("path.separator");
+        ArrayList al = new ArrayList();
+        StringTokenizer st = new StringTokenizer(cp, pathSep);
+        while (st.hasMoreTokens()) {
+            al.add(st.nextToken());
+        }
+        al.trimToSize();
+        return al;
+    }
+
+    public List classPath() {
+        return getPath("java.class.path");
+    }
+
+    public List bootClassPath() {
+        return getPath("sun.boot.class.path");
+    }
+
+    public String baseDirectory() {
+        return saVM.getSystemProperty("user.dir");
+    }
+
+    public void setDefaultStratum(String stratum) {
+        defaultStratum = stratum;
+    }
+
+    public String getDefaultStratum() {
+        return defaultStratum;
+    }
+
+    public String description() {
+        String[] versionParts = {"" + vmmgr.majorInterfaceVersion(),
+                                 "" + vmmgr.minorInterfaceVersion(),
+                                 name()};
+        return java.text.MessageFormat.format(java.util.ResourceBundle.
+                                              getBundle("com.sun.tools.jdi.resources.jdi").getString("version_format"),
+                                              versionParts);
+    }
+
+    public String version() {
+        return saVM.getSystemProperty("java.version");
+    }
+
+    public String name() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("JVM version ");
+        sb.append(version());
+        sb.append(" (");
+        sb.append(saVM.getSystemProperty("java.vm.name"));
+        sb.append(", ");
+        sb.append(saVM.getSystemProperty("java.vm.info"));
+        sb.append(")");
+        return sb.toString();
+    }
+
+    // from interface Mirror
+    public VirtualMachine virtualMachine() {
+        return this;
+    }
+
+    public String toString() {
+        return name();
+    }
+
+    public void setDebugTraceMode(int traceFlags) {
+        // spec. says output is implementation dependent
+        // and trace mode may be ignored. we ignore it :-)
+    }
+
+    // heap walking API
+
+    // capability check
+    public boolean canWalkHeap() {
+        return true;
+    }
+
+    // return a list of all objects in heap
+    public List/*<ObjectReference>*/ allObjects() {
+        final List objects = new ArrayList(0);
+        saObjectHeap.iterate(
+            new DefaultHeapVisitor() {
+                public boolean doObj(Oop oop) {
+                    objects.add(objectMirror(oop));
+                                        return false;
+                }
+            });
+        return objects;
+    }
+
+    // equivalent to objectsByType(type, true)
+    public List/*<ObjectReference>*/ objectsByType(ReferenceType type) {
+        return objectsByType(type, true);
+    }
+
+    // returns objects of type exactly equal to given type
+    private List/*<ObjectReference>*/ objectsByExactType(ReferenceType type) {
+        final List objects = new ArrayList(0);
+        final Klass givenKls = ((ReferenceTypeImpl)type).ref();
+        saObjectHeap.iterate(new DefaultHeapVisitor() {
+                public boolean doObj(Oop oop) {
+                    if (givenKls.equals(oop.getKlass())) {
+                        objects.add(objectMirror(oop));
+                    }
+                        return false;
+                }
+            });
+        return objects;
+    }
+
+    // returns objects of given type as well as it's subtypes
+    private List/*<ObjectReference>*/ objectsBySubType(ReferenceType type) {
+        final List objects = new ArrayList(0);
+        final ReferenceType givenType = type;
+        saObjectHeap.iterate(new DefaultHeapVisitor() {
+                public boolean doObj(Oop oop) {
+                    ReferenceTypeImpl curType = (ReferenceTypeImpl) referenceType(oop.getKlass());
+                    if (curType.isAssignableTo(givenType)) {
+                        objects.add(objectMirror(oop));
+                    }
+                        return false;
+                }
+            });
+        return objects;
+    }
+
+    // includeSubtypes - do you want to include subclass/subtype instances of given
+    // ReferenceType or do we want objects of exact type only?
+    public List/*<ObjectReference>*/ objectsByType(ReferenceType type, boolean includeSubtypes) {
+        Klass kls = ((ReferenceTypeImpl)type).ref();
+        if (kls instanceof InstanceKlass) {
+            InstanceKlass ik = (InstanceKlass) kls;
+            if (ik.isInterface()) {
+                if (ik.nofImplementors() == 0L) {
+                    return new ArrayList(0);
+                }
+            } else {
+                // if the Klass is final or if there are no subklasses loaded yet
+                if (ik.getAccessFlagsObj().isFinal() || ik.getSubklassKlass() == null) {
+                    includeSubtypes = false;
+                }
+            }
+        } else {
+            // no subtypes for primitive array types
+            ArrayTypeImpl arrayType = (ArrayTypeImpl) type;
+            try {
+                Type componentType = arrayType.componentType();
+                if (componentType instanceof PrimitiveType) {
+                    includeSubtypes = false;
+                }
+            } catch (ClassNotLoadedException cnle) {
+                // ignore. component type not yet loaded
+            }
+        }
+
+        if (includeSubtypes) {
+            return objectsBySubType(type);
+        } else {
+            return objectsByExactType(type);
+        }
+    }
+
+    Type findBootType(String signature) throws ClassNotLoadedException {
+        List types = allClasses();
+        Iterator iter = types.iterator();
+        while (iter.hasNext()) {
+            ReferenceType type = (ReferenceType)iter.next();
+            if ((type.classLoader() == null) &&
+                (type.signature().equals(signature))) {
+                return type;
+            }
+        }
+        JNITypeParser parser = new JNITypeParser(signature);
+        throw new ClassNotLoadedException(parser.typeName(),
+                                         "Type " + parser.typeName() + " not loaded");
+    }
+
+    BooleanType theBooleanType() {
+        if (theBooleanType == null) {
+            synchronized(this) {
+                if (theBooleanType == null) {
+                    theBooleanType = new BooleanTypeImpl(this);
+                }
+            }
+        }
+        return theBooleanType;
+    }
+
+    ByteType theByteType() {
+        if (theByteType == null) {
+            synchronized(this) {
+                if (theByteType == null) {
+                    theByteType = new ByteTypeImpl(this);
+                }
+            }
+        }
+        return theByteType;
+    }
+
+    CharType theCharType() {
+        if (theCharType == null) {
+            synchronized(this) {
+                if (theCharType == null) {
+                    theCharType = new CharTypeImpl(this);
+                }
+            }
+        }
+        return theCharType;
+    }
+
+    ShortType theShortType() {
+        if (theShortType == null) {
+            synchronized(this) {
+                if (theShortType == null) {
+                    theShortType = new ShortTypeImpl(this);
+                }
+            }
+        }
+        return theShortType;
+    }
+
+    IntegerType theIntegerType() {
+        if (theIntegerType == null) {
+            synchronized(this) {
+                if (theIntegerType == null) {
+                    theIntegerType = new IntegerTypeImpl(this);
+                }
+            }
+        }
+        return theIntegerType;
+    }
+
+    LongType theLongType() {
+        if (theLongType == null) {
+            synchronized(this) {
+                if (theLongType == null) {
+                    theLongType = new LongTypeImpl(this);
+                }
+            }
+        }
+        return theLongType;
+    }
+
+    FloatType theFloatType() {
+        if (theFloatType == null) {
+            synchronized(this) {
+                if (theFloatType == null) {
+                    theFloatType = new FloatTypeImpl(this);
+                }
+            }
+        }
+        return theFloatType;
+    }
+
+    DoubleType theDoubleType() {
+        if (theDoubleType == null) {
+            synchronized(this) {
+                if (theDoubleType == null) {
+                    theDoubleType = new DoubleTypeImpl(this);
+                }
+            }
+        }
+        return theDoubleType;
+    }
+
+    VoidType theVoidType() {
+        if (theVoidType == null) {
+            synchronized(this) {
+                if (theVoidType == null) {
+                    theVoidType = new VoidTypeImpl(this);
+                }
+            }
+        }
+        return theVoidType;
+    }
+
+    PrimitiveType primitiveTypeMirror(char tag) {
+        switch (tag) {
+        case 'Z':
+                return theBooleanType();
+        case 'B':
+                return theByteType();
+        case 'C':
+                return theCharType();
+        case 'S':
+                return theShortType();
+        case 'I':
+                return theIntegerType();
+        case 'J':
+                return theLongType();
+        case 'F':
+                return theFloatType();
+        case 'D':
+                return theDoubleType();
+        default:
+                throw new IllegalArgumentException("Unrecognized primitive tag " + tag);
+        }
+    }
+
+    private void processQueue() {
+        Reference ref;
+        while ((ref = referenceQueue.poll()) != null) {
+            SoftObjectReference softRef = (SoftObjectReference)ref;
+            removeObjectMirror(softRef);
+        }
+    }
+
+    // Address value is used as uniqueID by ObjectReferenceImpl
+    long getAddressValue(Oop obj) {
+        return vm.saVM.getDebugger().getAddressValue(obj.getHandle());
+    }
+
+    synchronized ObjectReferenceImpl objectMirror(Oop key) {
+
+        // Handle any queue elements that are not strongly reachable
+        processQueue();
+
+        if (key == null) {
+            return null;
+        }
+        ObjectReferenceImpl object = null;
+
+        /*
+         * Attempt to retrieve an existing object object reference
+         */
+        SoftObjectReference ref = (SoftObjectReference)objectsByID.get(key);
+        if (ref != null) {
+            object = ref.object();
+        }
+
+        /*
+         * If the object wasn't in the table, or it's soft reference was
+         * cleared, create a new instance.
+         */
+        if (object == null) {
+            if (key instanceof Instance) {
+                // look for well-known classes
+                Symbol className = key.getKlass().getName();
+                if (Assert.ASSERTS_ENABLED) {
+                    Assert.that(className != null, "Null class name");
+                }
+                Instance inst = (Instance) key;
+                if (className.equals(javaLangString)) {
+                    object = new StringReferenceImpl(this, inst);
+                } else if (className.equals(javaLangThread)) {
+                    object = new ThreadReferenceImpl(this, inst);
+                } else if (className.equals(javaLangThreadGroup)) {
+                    object = new ThreadGroupReferenceImpl(this, inst);
+                } else if (className.equals(javaLangClass)) {
+                    object = new ClassObjectReferenceImpl(this, inst);
+                } else if (className.equals(javaLangClassLoader)) {
+                    object = new ClassLoaderReferenceImpl(this, inst);
+                } else {
+                    // not a well-known class. But the base class may be
+                    // one of the known classes.
+                    Klass kls = key.getKlass().getSuper();
+                    while (kls != null) {
+                       className = kls.getName();
+                       // java.lang.Class and java.lang.String are final classes
+                       if (className.equals(javaLangThread)) {
+                          object = new ThreadReferenceImpl(this, inst);
+                          break;
+                       } else if(className.equals(javaLangThreadGroup)) {
+                          object = new ThreadGroupReferenceImpl(this, inst);
+                          break;
+                       } else if (className.equals(javaLangClassLoader)) {
+                          object = new ClassLoaderReferenceImpl(this, inst);
+                          break;
+                       }
+                       kls = kls.getSuper();
+                    }
+
+                    if (object == null) {
+                       // create generic object reference
+                       object = new ObjectReferenceImpl(this, inst);
+                    }
+                }
+            } else if (key instanceof TypeArray) {
+                object = new ArrayReferenceImpl(this, (Array) key);
+            } else if (key instanceof ObjArray) {
+                object = new ArrayReferenceImpl(this, (Array) key);
+            } else {
+                throw new RuntimeException("unexpected object type " + key);
+            }
+            ref = new SoftObjectReference(key, object, referenceQueue);
+
+            /*
+             * If there was no previous entry in the table, we add one here
+             * If the previous entry was cleared, we replace it here.
+             */
+            objectsByID.put(key, ref);
+        } else {
+            ref.incrementCount();
+        }
+
+        return object;
+    }
+
+    synchronized void removeObjectMirror(SoftObjectReference ref) {
+        /*
+         * This will remove the soft reference if it has not been
+         * replaced in the cache.
+         */
+        objectsByID.remove(ref.key());
+    }
+
+    StringReferenceImpl stringMirror(Instance id) {
+        return (StringReferenceImpl) objectMirror(id);
+    }
+
+    ArrayReferenceImpl arrayMirror(Array id) {
+       return (ArrayReferenceImpl) objectMirror(id);
+    }
+
+    ThreadReferenceImpl threadMirror(Instance id) {
+        return (ThreadReferenceImpl) objectMirror(id);
+    }
+
+    ThreadReferenceImpl threadMirror(JavaThread jt) {
+        return (ThreadReferenceImpl) objectMirror(jt.getThreadObj());
+    }
+
+    ThreadGroupReferenceImpl threadGroupMirror(Instance id) {
+        return (ThreadGroupReferenceImpl) objectMirror(id);
+    }
+
+    ClassLoaderReferenceImpl classLoaderMirror(Instance id) {
+        return (ClassLoaderReferenceImpl) objectMirror(id);
+    }
+
+    ClassObjectReferenceImpl classObjectMirror(Instance id) {
+        return (ClassObjectReferenceImpl) objectMirror(id);
+    }
+
+    // Use of soft refs and caching stuff here has to be re-examined.
+    //  It might not make sense for JDI - SA.
+    static private class SoftObjectReference extends SoftReference {
+       int count;
+       Object key;
+
+       SoftObjectReference(Object key, ObjectReferenceImpl mirror,
+                           ReferenceQueue queue) {
+           super(mirror, queue);
+           this.count = 1;
+           this.key = key;
+       }
+
+       int count() {
+           return count;
+       }
+
+       void incrementCount() {
+           count++;
+       }
+
+       Object key() {
+           return key;
+       }
+
+       ObjectReferenceImpl object() {
+           return (ObjectReferenceImpl)get();
+       }
+   }
+}