/* * Copyright (c) 2002, 2011, 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 private List typesBySignature; // List - 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); } // 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 is not the same that of // 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/**/ 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/**/ objectsByType(ReferenceType type) { return objectsByType(type, true); } // returns objects of type exactly equal to given type private List/**/ 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/**/ 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/**/ 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(); } } }