blob: dc99ffd76be9eccb0c426395446e64a2982ba45f [file] [log] [blame]
/*
* Copyright (c) 2002, 2012, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.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 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.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:" + kk);
}
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() {
return java.text.MessageFormat.format(java.util.ResourceBundle.
getBundle("com.sun.tools.jdi.resources.jdi").getString("version_format"),
"" + vmmgr.majorInterfaceVersion(),
"" + vmmgr.minorInterfaceVersion(),
name());
}
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 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();
}
}
}