blob: d3a2ea9d291978b87744f19f6ec757f3ab5b5667 [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 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();
}
}
}