| /* |
| * 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 java.io.*; |
| |
| import com.sun.jdi.*; |
| |
| import sun.jvm.hotspot.memory.SystemDictionary; |
| import sun.jvm.hotspot.oops.Instance; |
| import sun.jvm.hotspot.oops.InstanceKlass; |
| import sun.jvm.hotspot.oops.ArrayKlass; |
| import sun.jvm.hotspot.oops.JVMDIClassStatus; |
| import sun.jvm.hotspot.oops.Klass; |
| import sun.jvm.hotspot.oops.ObjArray; |
| import sun.jvm.hotspot.oops.Oop; |
| import sun.jvm.hotspot.oops.Symbol; |
| import sun.jvm.hotspot.oops.DefaultHeapVisitor; |
| import sun.jvm.hotspot.utilities.Assert; |
| |
| import java.util.*; |
| import java.lang.ref.SoftReference; |
| |
| public abstract class ReferenceTypeImpl extends TypeImpl |
| implements ReferenceType { |
| protected Klass saKlass; // This can be an InstanceKlass or an ArrayKlass |
| protected Symbol typeNameSymbol; // This is used in vm.classesByName to speedup search |
| private int modifiers = -1; |
| private String signature = null; |
| private SoftReference sdeRef = null; |
| private SoftReference fieldsCache; |
| private SoftReference allFieldsCache; |
| private SoftReference methodsCache; |
| private SoftReference allMethodsCache; |
| private SoftReference nestedTypesCache; |
| private SoftReference methodInvokesCache; |
| |
| /* to mark when no info available */ |
| static final SDE NO_SDE_INFO_MARK = new SDE(); |
| |
| protected ReferenceTypeImpl(VirtualMachine aVm, sun.jvm.hotspot.oops.Klass klass) { |
| super(aVm); |
| saKlass = klass; |
| typeNameSymbol = saKlass.getName(); |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(typeNameSymbol != null, "null type name for a Klass"); |
| } |
| } |
| |
| Symbol typeNameAsSymbol() { |
| return typeNameSymbol; |
| } |
| |
| Method getMethodMirror(sun.jvm.hotspot.oops.Method ref) { |
| // SA creates new Method objects when they are referenced which means |
| // that the incoming object might not be the same object as on our |
| // even though it is the same method. So do an address compare by |
| // calling equals rather than just reference compare. |
| Iterator it = methods().iterator(); |
| while (it.hasNext()) { |
| MethodImpl method = (MethodImpl)it.next(); |
| if (ref.equals(method.ref())) { |
| return method; |
| } |
| } |
| if (ref.getMethodHolder().equals(SystemDictionary.getMethodHandleKlass())) { |
| // invoke methods are generated as needed, so make mirrors as needed |
| List mis = null; |
| if (methodInvokesCache == null) { |
| mis = new ArrayList(); |
| methodInvokesCache = new SoftReference(mis); |
| } else { |
| mis = (List)methodInvokesCache.get(); |
| } |
| it = mis.iterator(); |
| while (it.hasNext()) { |
| MethodImpl method = (MethodImpl)it.next(); |
| if (ref.equals(method.ref())) { |
| return method; |
| } |
| } |
| |
| MethodImpl method = MethodImpl.createMethodImpl(vm, this, ref); |
| mis.add(method); |
| return method; |
| } |
| throw new IllegalArgumentException("Invalid method id: " + ref); |
| } |
| |
| public boolean equals(Object obj) { |
| if ((obj != null) && (obj instanceof ReferenceTypeImpl)) { |
| ReferenceTypeImpl other = (ReferenceTypeImpl)obj; |
| return (ref().equals(other.ref())) && |
| (vm.equals(other.virtualMachine())); |
| } else { |
| return false; |
| } |
| } |
| |
| public int hashCode() { |
| return saKlass.hashCode(); |
| } |
| |
| public int compareTo(ReferenceType refType) { |
| /* |
| * Note that it is critical that compareTo() == 0 |
| * implies that equals() == true. Otherwise, TreeSet |
| * will collapse classes. |
| * |
| * (Classes of the same name loaded by different class loaders |
| * or in different VMs must not return 0). |
| */ |
| ReferenceTypeImpl other = (ReferenceTypeImpl)refType; |
| int comp = name().compareTo(other.name()); |
| if (comp == 0) { |
| Klass rf1 = ref(); |
| Klass rf2 = other.ref(); |
| // optimize for typical case: refs equal and VMs equal |
| if (rf1.equals(rf2)) { |
| // sequenceNumbers are always positive |
| comp = vm.sequenceNumber - |
| ((VirtualMachineImpl)(other.virtualMachine())).sequenceNumber; |
| } else { |
| comp = rf1.getAddress().minus(rf2.getAddress()) < 0? -1 : 1; |
| } |
| } |
| return comp; |
| } |
| |
| public String signature() { |
| if (signature == null) { |
| signature = saKlass.signature(); |
| } |
| return signature; |
| } |
| |
| // refer to JvmtiEnv::GetClassSignature. |
| // null is returned for array klasses. |
| public String genericSignature() { |
| if (saKlass instanceof ArrayKlass) { |
| return null; |
| } else { |
| Symbol genSig = ((InstanceKlass)saKlass).getGenericSignature(); |
| return (genSig != null)? genSig.asString() : null; |
| } |
| } |
| |
| public ClassLoaderReference classLoader() { |
| Instance xx = (Instance)(((InstanceKlass)saKlass).getClassLoader()); |
| return (ClassLoaderReferenceImpl)vm.classLoaderMirror(xx); |
| } |
| |
| public boolean isPublic() { |
| return((modifiers() & VMModifiers.PUBLIC) != 0); |
| } |
| |
| public boolean isProtected() { |
| return((modifiers() & VMModifiers.PROTECTED) != 0); |
| } |
| |
| public boolean isPrivate() { |
| return((modifiers() & VMModifiers.PRIVATE) != 0); |
| } |
| |
| public boolean isPackagePrivate() { |
| return !isPublic() && !isPrivate() && !isProtected(); |
| } |
| |
| public boolean isAbstract() { |
| return((modifiers() & VMModifiers.ABSTRACT) != 0); |
| } |
| |
| public boolean isFinal() { |
| return((modifiers() & VMModifiers.FINAL) != 0); |
| } |
| |
| public boolean isStatic() { |
| return((modifiers() & VMModifiers.STATIC) != 0); |
| } |
| |
| public boolean isPrepared() { |
| return (saKlass.getClassStatus() & JVMDIClassStatus.PREPARED) != 0; |
| } |
| |
| final void checkPrepared() throws ClassNotPreparedException { |
| if (! isPrepared()) { |
| throw new ClassNotPreparedException(); |
| } |
| } |
| |
| public boolean isVerified() { |
| return (saKlass.getClassStatus() & JVMDIClassStatus.VERIFIED) != 0; |
| } |
| |
| public boolean isInitialized() { |
| return (saKlass.getClassStatus() & JVMDIClassStatus.INITIALIZED) != 0; |
| } |
| |
| public boolean failedToInitialize() { |
| return (saKlass.getClassStatus() & JVMDIClassStatus.ERROR) != 0; |
| } |
| |
| private boolean isThrowableBacktraceField(sun.jvm.hotspot.oops.Field fld) { |
| // refer to JvmtiEnv::GetClassFields in jvmtiEnv.cpp. |
| // We want to filter out java.lang.Throwable.backtrace (see 4446677). |
| // It contains some Method*s that aren't quite real Objects. |
| if (fld.getFieldHolder().getName().equals(vm.javaLangThrowable()) && |
| fld.getID().getName().equals("backtrace")) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| public final List fields() throws ClassNotPreparedException { |
| List fields = (fieldsCache != null)? (List) fieldsCache.get() : null; |
| if (fields == null) { |
| checkPrepared(); |
| if (saKlass instanceof ArrayKlass) { |
| fields = new ArrayList(0); |
| } else { |
| // Get a list of the sa Field types |
| List saFields = ((InstanceKlass)saKlass).getImmediateFields(); |
| |
| // Create a list of our Field types |
| int len = saFields.size(); |
| fields = new ArrayList(len); |
| for (int ii = 0; ii < len; ii++) { |
| sun.jvm.hotspot.oops.Field curField = (sun.jvm.hotspot.oops.Field)saFields.get(ii); |
| if (! isThrowableBacktraceField(curField)) { |
| fields.add(new FieldImpl(vm, this, curField)); |
| } |
| } |
| } |
| fields = Collections.unmodifiableList(fields); |
| fieldsCache = new SoftReference(fields); |
| } |
| return fields; |
| } |
| |
| public final List allFields() throws ClassNotPreparedException { |
| List allFields = (allFieldsCache != null)? (List) allFieldsCache.get() : null; |
| if (allFields == null) { |
| checkPrepared(); |
| if (saKlass instanceof ArrayKlass) { |
| // is 'length' a field of array klasses? To maintain |
| // consistency with JVMDI-JDI we return 0 size. |
| allFields = new ArrayList(0); |
| } else { |
| List saFields; |
| |
| // Get a list of the sa Field types |
| saFields = ((InstanceKlass)saKlass).getAllFields(); |
| |
| // Create a list of our Field types |
| int len = saFields.size(); |
| allFields = new ArrayList(len); |
| for (int ii = 0; ii < len; ii++) { |
| sun.jvm.hotspot.oops.Field curField = (sun.jvm.hotspot.oops.Field)saFields.get(ii); |
| if (! isThrowableBacktraceField(curField)) { |
| allFields.add(new FieldImpl(vm, vm.referenceType(curField.getFieldHolder()), curField)); |
| } |
| } |
| } |
| allFields = Collections.unmodifiableList(allFields); |
| allFieldsCache = new SoftReference(allFields); |
| } |
| return allFields; |
| } |
| |
| abstract List inheritedTypes(); |
| |
| void addVisibleFields(List visibleList, Map visibleTable, List ambiguousNames) { |
| List list = visibleFields(); |
| Iterator iter = list.iterator(); |
| while (iter.hasNext()) { |
| Field field = (Field)iter.next(); |
| String name = field.name(); |
| if (!ambiguousNames.contains(name)) { |
| Field duplicate = (Field)visibleTable.get(name); |
| if (duplicate == null) { |
| visibleList.add(field); |
| visibleTable.put(name, field); |
| } else if (!field.equals(duplicate)) { |
| ambiguousNames.add(name); |
| visibleTable.remove(name); |
| visibleList.remove(duplicate); |
| } else { |
| // identical field from two branches; do nothing |
| } |
| } |
| } |
| } |
| |
| public final List visibleFields() throws ClassNotPreparedException { |
| checkPrepared(); |
| /* |
| * Maintain two different collections of visible fields. The |
| * list maintains a reasonable order for return. The |
| * hash map provides an efficient way to lookup visible fields |
| * by name, important for finding hidden or ambiguous fields. |
| */ |
| List visibleList = new ArrayList(); |
| Map visibleTable = new HashMap(); |
| |
| /* Track fields removed from above collection due to ambiguity */ |
| List ambiguousNames = new ArrayList(); |
| |
| /* Add inherited, visible fields */ |
| List types = inheritedTypes(); |
| Iterator iter = types.iterator(); |
| while (iter.hasNext()) { |
| /* |
| * TO DO: Be defensive and check for cyclic interface inheritance |
| */ |
| ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next(); |
| type.addVisibleFields(visibleList, visibleTable, ambiguousNames); |
| } |
| |
| /* |
| * Insert fields from this type, removing any inherited fields they |
| * hide. |
| */ |
| List retList = new ArrayList(fields()); |
| iter = retList.iterator(); |
| while (iter.hasNext()) { |
| Field field = (Field)iter.next(); |
| Field hidden = (Field)visibleTable.get(field.name()); |
| if (hidden != null) { |
| visibleList.remove(hidden); |
| } |
| } |
| retList.addAll(visibleList); |
| return retList; |
| } |
| |
| public final Field fieldByName(String fieldName) throws ClassNotPreparedException { |
| java.util.List searchList; |
| Field f; |
| |
| // visibleFields calls checkPrepared |
| searchList = visibleFields(); |
| |
| for (int i=0; i<searchList.size(); i++) { |
| f = (Field)searchList.get(i); |
| |
| if (f.name().equals(fieldName)) { |
| return f; |
| } |
| } |
| //throw new NoSuchFieldException("Field '" + fieldName + "' not found in " + name()); |
| return null; |
| } |
| |
| public final List methods() throws ClassNotPreparedException { |
| List methods = (methodsCache != null)? (List) methodsCache.get() : null; |
| if (methods == null) { |
| checkPrepared(); |
| if (saKlass instanceof ArrayKlass) { |
| methods = new ArrayList(0); |
| } else { |
| List saMethods; |
| // Get a list of the SA Method types |
| saMethods = ((InstanceKlass)saKlass).getImmediateMethods(); |
| |
| // Create a list of our MethodImpl types |
| int len = saMethods.size(); |
| methods = new ArrayList(len); |
| for (int ii = 0; ii < len; ii++) { |
| methods.add(MethodImpl.createMethodImpl(vm, this, (sun.jvm.hotspot.oops.Method)saMethods.get(ii))); |
| } |
| } |
| methods = Collections.unmodifiableList(methods); |
| methodsCache = new SoftReference(methods); |
| } |
| return methods; |
| } |
| |
| abstract List getAllMethods(); |
| public final List allMethods() throws ClassNotPreparedException { |
| List allMethods = (allMethodsCache != null)? (List) allMethodsCache.get() : null; |
| if (allMethods == null) { |
| checkPrepared(); |
| allMethods = Collections.unmodifiableList(getAllMethods()); |
| allMethodsCache = new SoftReference(allMethods); |
| } |
| return allMethods; |
| } |
| |
| /* |
| * Utility method used by subclasses to build lists of visible |
| * methods. |
| */ |
| void addToMethodMap(Map methodMap, List methodList) { |
| Iterator iter = methodList.iterator(); |
| while (iter.hasNext()) { |
| Method method = (Method)iter.next(); |
| methodMap.put(method.name().concat(method.signature()), method); |
| } |
| } |
| |
| abstract void addVisibleMethods(Map methodMap); |
| public final List visibleMethods() throws ClassNotPreparedException { |
| checkPrepared(); |
| /* |
| * Build a collection of all visible methods. The hash |
| * map allows us to do this efficiently by keying on the |
| * concatenation of name and signature. |
| */ |
| //System.out.println("jj: RTI: Calling addVisibleMethods for:" + this); |
| Map map = new HashMap(); |
| addVisibleMethods(map); |
| |
| /* |
| * ... but the hash map destroys order. Methods should be |
| * returned in a sensible order, as they are in allMethods(). |
| * So, start over with allMethods() and use the hash map |
| * to filter that ordered collection. |
| */ |
| //System.out.println("jj: RTI: Calling allMethods for:" + this); |
| |
| List list = new ArrayList(allMethods()); |
| //System.out.println("jj: allMethods = " + jjstr(list)); |
| //System.out.println("jj: map = " + map.toString()); |
| //System.out.println("jj: map = " + jjstr(map.values())); |
| list.retainAll(map.values()); |
| //System.out.println("jj: map = " + jjstr(list)); |
| //System.exit(0); |
| return list; |
| } |
| |
| static Object prev; |
| |
| static public String jjstr(Collection cc) { |
| StringBuffer buf = new StringBuffer(); |
| buf.append("["); |
| Iterator i = cc.iterator(); |
| boolean hasNext = i.hasNext(); |
| while (hasNext) { |
| Object o = i.next(); |
| if (prev == null) { |
| prev = o; |
| } else { |
| System.out.println("prev == curr?" + prev.equals(o)); |
| System.out.println("prev == curr?" + (prev == o)); |
| } |
| buf.append( o + "@" + o.hashCode()); |
| //buf.append( ((Object)o).toString()); |
| hasNext = i.hasNext(); |
| if (hasNext) |
| buf.append(", "); |
| } |
| |
| buf.append("]"); |
| return buf.toString(); |
| } |
| |
| public final List methodsByName(String name) throws ClassNotPreparedException { |
| // visibleMethods calls checkPrepared |
| List methods = visibleMethods(); |
| ArrayList retList = new ArrayList(methods.size()); |
| Iterator iter = methods.iterator(); |
| while (iter.hasNext()) { |
| Method candidate = (Method)iter.next(); |
| if (candidate.name().equals(name)) { |
| retList.add(candidate); |
| } |
| } |
| retList.trimToSize(); |
| return retList; |
| } |
| |
| public final List methodsByName(String name, String signature) throws ClassNotPreparedException { |
| // visibleMethods calls checkPrepared |
| List methods = visibleMethods(); |
| ArrayList retList = new ArrayList(methods.size()); |
| Iterator iter = methods.iterator(); |
| while (iter.hasNext()) { |
| Method candidate = (Method)iter.next(); |
| if (candidate.name().equals(name) && |
| candidate.signature().equals(signature)) { |
| retList.add(candidate); |
| } |
| } |
| retList.trimToSize(); |
| return retList; |
| } |
| |
| |
| List getInterfaces() { |
| List myInterfaces; |
| if (saKlass instanceof ArrayKlass) { |
| // Actually, JLS says arrays implement Cloneable and Serializable |
| // But, JVMDI-JDI just returns 0 interfaces for arrays. We follow |
| // the same for consistency. |
| myInterfaces = new ArrayList(0); |
| } else { |
| // Get a list of the sa InstanceKlass types |
| List saInterfaces = ((InstanceKlass)saKlass).getDirectImplementedInterfaces(); |
| |
| // Create a list of our InterfaceTypes |
| int len = saInterfaces.size(); |
| myInterfaces = new ArrayList(len); |
| for (int ii = 0; ii < len; ii++) { |
| myInterfaces.add(new InterfaceTypeImpl(vm, (InstanceKlass)saInterfaces.get(ii))); |
| } |
| } |
| return myInterfaces; |
| } |
| |
| public final List nestedTypes() { |
| List nestedTypes = (nestedTypesCache != null)? (List) nestedTypesCache.get() : null; |
| if (nestedTypes == null) { |
| if (saKlass instanceof ArrayKlass) { |
| nestedTypes = new ArrayList(0); |
| } else { |
| ClassLoaderReference cl = classLoader(); |
| List classes = null; |
| if (cl != null) { |
| classes = cl.visibleClasses(); |
| } else { |
| classes = vm.bootstrapClasses(); |
| } |
| nestedTypes = new ArrayList(); |
| Iterator iter = classes.iterator(); |
| while (iter.hasNext()) { |
| ReferenceTypeImpl refType = (ReferenceTypeImpl)iter.next(); |
| Symbol candidateName = refType.ref().getName(); |
| if (((InstanceKlass)saKlass).isInnerOrLocalClassName(candidateName)) { |
| nestedTypes.add(refType); |
| } |
| } |
| } |
| nestedTypes = Collections.unmodifiableList(nestedTypes); |
| nestedTypesCache = new SoftReference(nestedTypes); |
| } |
| return nestedTypes; |
| } |
| |
| public Value getValue(Field sig) { |
| List list = new ArrayList(1); |
| list.add(sig); |
| Map map = getValues(list); |
| return(Value)map.get(sig); |
| } |
| |
| /** |
| * Returns a map of field values |
| */ |
| public Map getValues(List theFields) { |
| //validateMirrors(); |
| int size = theFields.size(); |
| Map map = new HashMap(size); |
| for (int ii=0; ii<size; ii++) { |
| FieldImpl fieldImpl = (FieldImpl)theFields.get(ii); |
| |
| validateFieldAccess(fieldImpl); |
| // Do more validation specific to ReferenceType field getting |
| if (!fieldImpl.isStatic()) { |
| throw new IllegalArgumentException( |
| "Attempt to use non-static field with ReferenceType: " + |
| fieldImpl.name()); |
| } |
| map.put(fieldImpl, fieldImpl.getValue()); |
| } |
| return map; |
| } |
| |
| void validateFieldAccess(Field field) { |
| /* |
| * Field must be in this object's class, a superclass, or |
| * implemented interface |
| */ |
| ReferenceTypeImpl declType = (ReferenceTypeImpl)field.declaringType(); |
| if (!declType.isAssignableFrom(this)) { |
| throw new IllegalArgumentException("Invalid field"); |
| } |
| } |
| |
| public ClassObjectReference classObject() { |
| return vm.classObjectMirror(ref().getJavaMirror()); |
| } |
| |
| SDE.Stratum stratum(String stratumID) { |
| SDE sde = sourceDebugExtensionInfo(); |
| if (!sde.isValid()) { |
| sde = NO_SDE_INFO_MARK; |
| } |
| return sde.stratum(stratumID); |
| } |
| |
| public String sourceName() throws AbsentInformationException { |
| return (String)(sourceNames(vm.getDefaultStratum()).get(0)); |
| } |
| |
| public List sourceNames(String stratumID) |
| throws AbsentInformationException { |
| SDE.Stratum stratum = stratum(stratumID); |
| if (stratum.isJava()) { |
| List result = new ArrayList(1); |
| result.add(baseSourceName()); |
| return result; |
| } |
| return stratum.sourceNames(this); |
| } |
| |
| public List sourcePaths(String stratumID) |
| throws AbsentInformationException { |
| SDE.Stratum stratum = stratum(stratumID); |
| if (stratum.isJava()) { |
| List result = new ArrayList(1); |
| result.add(baseSourceDir() + baseSourceName()); |
| return result; |
| } |
| return stratum.sourcePaths(this); |
| } |
| |
| String baseSourceName() throws AbsentInformationException { |
| if (saKlass instanceof ArrayKlass) { |
| throw new AbsentInformationException(); |
| } |
| Symbol sym = ((InstanceKlass)saKlass).getSourceFileName(); |
| if (sym != null) { |
| return sym.asString(); |
| } else { |
| throw new AbsentInformationException(); |
| } |
| } |
| |
| String baseSourcePath() throws AbsentInformationException { |
| return baseSourceDir() + baseSourceName(); |
| } |
| |
| String baseSourceDir() { |
| String typeName = name(); |
| StringBuffer sb = new StringBuffer(typeName.length() + 10); |
| int index = 0; |
| int nextIndex; |
| |
| while ((nextIndex = typeName.indexOf('.', index)) > 0) { |
| sb.append(typeName.substring(index, nextIndex)); |
| sb.append(java.io.File.separatorChar); |
| index = nextIndex + 1; |
| } |
| return sb.toString(); |
| } |
| |
| public String sourceDebugExtension() |
| throws AbsentInformationException { |
| if (!vm.canGetSourceDebugExtension()) { |
| throw new UnsupportedOperationException(); |
| } |
| SDE sde = sourceDebugExtensionInfo(); |
| if (sde == NO_SDE_INFO_MARK) { |
| throw new AbsentInformationException(); |
| } |
| return sde.sourceDebugExtension; |
| } |
| |
| private SDE sourceDebugExtensionInfo() { |
| if (!vm.canGetSourceDebugExtension()) { |
| return NO_SDE_INFO_MARK; |
| } |
| SDE sde = null; |
| sde = (sdeRef == null) ? null : (SDE)sdeRef.get(); |
| if (sde == null) { |
| String extension = null; |
| if (saKlass instanceof InstanceKlass) { |
| extension = ((InstanceKlass)saKlass).getSourceDebugExtension(); |
| } |
| if (extension == null) { |
| sde = NO_SDE_INFO_MARK; |
| } else { |
| sde = new SDE(extension); |
| } |
| sdeRef = new SoftReference(sde); |
| } |
| return sde; |
| } |
| |
| public List availableStrata() { |
| SDE sde = sourceDebugExtensionInfo(); |
| if (sde.isValid()) { |
| return sde.availableStrata(); |
| } else { |
| List strata = new ArrayList(); |
| strata.add(SDE.BASE_STRATUM_NAME); |
| return strata; |
| } |
| } |
| |
| /** |
| * Always returns non-null stratumID |
| */ |
| public String defaultStratum() { |
| SDE sdei = sourceDebugExtensionInfo(); |
| if (sdei.isValid()) { |
| return sdei.defaultStratumId; |
| } else { |
| return SDE.BASE_STRATUM_NAME; |
| } |
| } |
| |
| public final int modifiers() { |
| if (modifiers == -1) { |
| modifiers = getModifiers(); |
| } |
| return modifiers; |
| } |
| |
| // new method since 1.6. |
| // Real body will be supplied later. |
| public List instances(long maxInstances) { |
| if (!vm.canGetInstanceInfo()) { |
| throw new UnsupportedOperationException( |
| "target does not support getting instances"); |
| } |
| |
| if (maxInstances < 0) { |
| throw new IllegalArgumentException("maxInstances is less than zero: " |
| + maxInstances); |
| } |
| |
| final List objects = new ArrayList(0); |
| if (isAbstract() || (this instanceof InterfaceType)) { |
| return objects; |
| } |
| |
| final Klass givenKls = this.ref(); |
| final long max = maxInstances; |
| vm.saObjectHeap().iterate(new DefaultHeapVisitor() { |
| private long instCount=0; |
| public boolean doObj(Oop oop) { |
| if (givenKls.equals(oop.getKlass())) { |
| objects.add(vm.objectMirror(oop)); |
| instCount++; |
| } |
| if (max > 0 && instCount >= max) { |
| return true; |
| } |
| return false; |
| } |
| }); |
| return objects; |
| } |
| |
| int getModifiers() { |
| return (int) saKlass.getClassModifiers(); |
| } |
| |
| public List allLineLocations() |
| throws AbsentInformationException { |
| return allLineLocations(vm.getDefaultStratum(), null); |
| } |
| |
| public List allLineLocations(String stratumID, String sourceName) |
| throws AbsentInformationException { |
| checkPrepared(); |
| boolean someAbsent = false; // A method that should have info, didn't |
| SDE.Stratum stratum = stratum(stratumID); |
| List list = new ArrayList(); // location list |
| |
| for (Iterator iter = methods().iterator(); iter.hasNext(); ) { |
| MethodImpl method = (MethodImpl)iter.next(); |
| try { |
| list.addAll( |
| method.allLineLocations(stratum.id(), sourceName)); |
| } catch(AbsentInformationException exc) { |
| someAbsent = true; |
| } |
| } |
| |
| // If we retrieved no line info, and at least one of the methods |
| // should have had some (as determined by an |
| // AbsentInformationException being thrown) then we rethrow |
| // the AbsentInformationException. |
| if (someAbsent && list.size() == 0) { |
| throw new AbsentInformationException(); |
| } |
| return list; |
| } |
| |
| public List locationsOfLine(int lineNumber) |
| throws AbsentInformationException { |
| return locationsOfLine(vm.getDefaultStratum(), |
| null, |
| lineNumber); |
| } |
| |
| public List locationsOfLine(String stratumID, |
| String sourceName, |
| int lineNumber) |
| throws AbsentInformationException { |
| checkPrepared(); |
| // A method that should have info, didn't |
| boolean someAbsent = false; |
| // A method that should have info, did |
| boolean somePresent = false; |
| List methods = methods(); |
| SDE.Stratum stratum = stratum(stratumID); |
| |
| List list = new ArrayList(); |
| |
| Iterator iter = methods.iterator(); |
| while(iter.hasNext()) { |
| MethodImpl method = (MethodImpl)iter.next(); |
| // eliminate native and abstract to eliminate |
| // false positives |
| if (!method.isAbstract() && |
| !method.isNative()) { |
| try { |
| list.addAll( |
| method.locationsOfLine(stratum.id(), |
| sourceName, |
| lineNumber)); |
| somePresent = true; |
| } catch(AbsentInformationException exc) { |
| someAbsent = true; |
| } |
| } |
| } |
| if (someAbsent && !somePresent) { |
| throw new AbsentInformationException(); |
| } |
| return list; |
| } |
| |
| Klass ref() { |
| return saKlass; |
| } |
| |
| |
| /* |
| * Return true if an instance of this type |
| * can be assigned to a variable of the given type |
| */ |
| abstract boolean isAssignableTo(ReferenceType type); |
| |
| boolean isAssignableFrom(ReferenceType type) { |
| return ((ReferenceTypeImpl)type).isAssignableTo(this); |
| } |
| |
| boolean isAssignableFrom(ObjectReference object) { |
| return object == null || |
| isAssignableFrom(object.referenceType()); |
| } |
| |
| int indexOf(Method method) { |
| // Make sure they're all here - the obsolete method |
| // won't be found and so will have index -1 |
| return methods().indexOf(method); |
| } |
| |
| int indexOf(Field field) { |
| // Make sure they're all here |
| return fields().indexOf(field); |
| } |
| |
| private static boolean isPrimitiveArray(String signature) { |
| int i = signature.lastIndexOf('['); |
| /* |
| * TO DO: Centralize JNI signature knowledge. |
| * |
| * Ref: |
| * jdk1.4/doc/guide/jpda/jdi/com/sun/jdi/doc-files/signature.html |
| */ |
| boolean isPA; |
| if (i < 0) { |
| isPA = false; |
| } else { |
| char c = signature.charAt(i + 1); |
| isPA = (c != 'L'); |
| } |
| return isPA; |
| } |
| |
| Type findType(String signature) throws ClassNotLoadedException { |
| Type type; |
| if (signature.length() == 1) { |
| /* OTI FIX: Must be a primitive type or the void type */ |
| char sig = signature.charAt(0); |
| if (sig == 'V') { |
| type = vm.theVoidType(); |
| } else { |
| type = vm.primitiveTypeMirror(sig); |
| } |
| } else { |
| // Must be a reference type. |
| ClassLoaderReferenceImpl loader = |
| (ClassLoaderReferenceImpl)classLoader(); |
| if ((loader == null) || |
| (isPrimitiveArray(signature)) //Work around 4450091 |
| ) { |
| // Caller wants type of boot class field |
| type = vm.findBootType(signature); |
| } else { |
| // Caller wants type of non-boot class field |
| type = loader.findType(signature); |
| } |
| } |
| return type; |
| } |
| |
| String loaderString() { |
| if (classLoader() != null) { |
| return "loaded by " + classLoader().toString(); |
| } else { |
| return "loaded by bootstrap loader"; |
| } |
| } |
| |
| long uniqueID() { |
| return vm.getAddressValue(ref().getJavaMirror()); |
| } |
| |
| // new method since 1.6 |
| public int majorVersion() { |
| if (!vm.canGetClassFileVersion()) { |
| throw new UnsupportedOperationException("Cannot get class file version"); |
| } |
| return (int)((InstanceKlass)saKlass).majorVersion(); |
| } |
| |
| // new method since 1.6 |
| public int minorVersion() { |
| if (!vm.canGetClassFileVersion()) { |
| throw new UnsupportedOperationException("Cannot get class file version"); |
| } |
| return (int)((InstanceKlass)saKlass).minorVersion(); |
| } |
| |
| // new method since 1.6 |
| public int constantPoolCount() { |
| if (!vm.canGetConstantPool()) { |
| throw new UnsupportedOperationException("Cannot get constant pool"); |
| } |
| if (saKlass instanceof ArrayKlass) { |
| return 0; |
| } else { |
| return (int)((InstanceKlass)saKlass).getConstants().getLength(); |
| } |
| } |
| |
| // new method since 1.6 |
| public byte[] constantPool() { |
| if (!vm.canGetConstantPool()) { |
| throw new UnsupportedOperationException("Cannot get constant pool"); |
| } |
| if (this instanceof ArrayType || this instanceof PrimitiveType) { |
| byte bytes[] = new byte[0]; |
| return bytes; |
| } else { |
| ByteArrayOutputStream bs = new ByteArrayOutputStream(); |
| try { |
| ((InstanceKlass)saKlass).getConstants().writeBytes(bs); |
| } catch (IOException ex) { |
| ex.printStackTrace(); |
| byte bytes[] = new byte[0]; |
| return bytes; |
| } |
| return bs.toByteArray(); |
| } |
| } |
| } |