Home | History | Annotate | Download | only in jdi
      1 /*
      2  * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 package com.sun.tools.jdi;
     27 
     28 import com.sun.jdi.*;
     29 
     30 import java.util.*;
     31 import java.lang.ref.SoftReference;
     32 
     33 public abstract class ReferenceTypeImpl extends TypeImpl
     34 implements ReferenceType {
     35     protected long ref;
     36     private String signature = null;
     37     private String genericSignature = null;
     38     private boolean genericSignatureGotten = false;
     39     private String baseSourceName = null;
     40     private String baseSourceDir = null;
     41     private String baseSourcePath = null;
     42     protected int modifiers = -1;
     43     private SoftReference<List<Field>> fieldsRef = null;
     44     private SoftReference<List<Method>> methodsRef = null;
     45     private SoftReference<SDE> sdeRef = null;
     46 
     47     private boolean isClassLoaderCached = false;
     48     private ClassLoaderReference classLoader = null;
     49     private ClassObjectReference classObject = null;
     50 
     51     private int status = 0;
     52     private boolean isPrepared = false;
     53 
     54 
     55     private boolean versionNumberGotten = false;
     56     private int majorVersion;
     57     private int minorVersion;
     58 
     59     private boolean constantPoolInfoGotten = false;
     60     private int constanPoolCount;
     61     private byte[] constantPoolBytes;
     62     private SoftReference<byte[]> constantPoolBytesRef = null;
     63 
     64     /* to mark a SourceFile request that returned a genuine JDWP.Error.ABSENT_INFORMATION */
     65     private static final String ABSENT_BASE_SOURCE_NAME = "**ABSENT_BASE_SOURCE_NAME**";
     66 
     67     /* to mark when no info available */
     68     static final SDE NO_SDE_INFO_MARK = new SDE();
     69 
     70     // bits set when initialization was attempted (succeeded or failed)
     71     private static final int INITIALIZED_OR_FAILED =
     72         JDWP.ClassStatus.INITIALIZED | JDWP.ClassStatus.ERROR;
     73 
     74 
     75     protected ReferenceTypeImpl(VirtualMachine aVm, long aRef) {
     76         super(aVm);
     77         ref = aRef;
     78         genericSignatureGotten = false;
     79     }
     80 
     81     void noticeRedefineClass() {
     82         //Invalidate information previously fetched and cached.
     83         //These will be refreshed later on demand.
     84         baseSourceName = null;
     85         baseSourcePath = null;
     86         modifiers = -1;
     87         fieldsRef = null;
     88         methodsRef = null;
     89         sdeRef = null;
     90         versionNumberGotten = false;
     91         constantPoolInfoGotten = false;
     92     }
     93 
     94     Method getMethodMirror(long ref) {
     95         if (ref == 0) {
     96             // obsolete method
     97             return new ObsoleteMethodImpl(vm, this);
     98         }
     99         // Fetch all methods for the class, check performance impact
    100         // Needs no synchronization now, since methods() returns
    101         // unmodifiable local data
    102         Iterator<Method> it = methods().iterator();
    103         while (it.hasNext()) {
    104             MethodImpl method = (MethodImpl)it.next();
    105             if (method.ref() == ref) {
    106                 return method;
    107             }
    108         }
    109         throw new IllegalArgumentException("Invalid method id: " + ref);
    110     }
    111 
    112     Field getFieldMirror(long ref) {
    113         // Fetch all fields for the class, check performance impact
    114         // Needs no synchronization now, since fields() returns
    115         // unmodifiable local data
    116         Iterator<Field>it = fields().iterator();
    117         while (it.hasNext()) {
    118             FieldImpl field = (FieldImpl)it.next();
    119             if (field.ref() == ref) {
    120                 return field;
    121             }
    122         }
    123         throw new IllegalArgumentException("Invalid field id: " + ref);
    124     }
    125 
    126     public boolean equals(Object obj) {
    127         if ((obj != null) && (obj instanceof ReferenceTypeImpl)) {
    128             ReferenceTypeImpl other = (ReferenceTypeImpl)obj;
    129             return (ref() == other.ref()) &&
    130                 (vm.equals(other.virtualMachine()));
    131         } else {
    132             return false;
    133         }
    134     }
    135 
    136     public int hashCode() {
    137         return(int)ref();
    138     }
    139 
    140     public int compareTo(ReferenceType object) {
    141         /*
    142          * Note that it is critical that compareTo() == 0
    143          * implies that equals() == true. Otherwise, TreeSet
    144          * will collapse classes.
    145          *
    146          * (Classes of the same name loaded by different class loaders
    147          * or in different VMs must not return 0).
    148          */
    149         ReferenceTypeImpl other = (ReferenceTypeImpl)object;
    150         int comp = name().compareTo(other.name());
    151         if (comp == 0) {
    152             long rf1 = ref();
    153             long rf2 = other.ref();
    154             // optimize for typical case: refs equal and VMs equal
    155             if (rf1 == rf2) {
    156                 // sequenceNumbers are always positive
    157                 comp = vm.sequenceNumber -
    158                  ((VirtualMachineImpl)(other.virtualMachine())).sequenceNumber;
    159             } else {
    160                 comp = (rf1 < rf2)? -1 : 1;
    161             }
    162         }
    163         return comp;
    164     }
    165 
    166     public String signature() {
    167         if (signature == null) {
    168             // Does not need synchronization, since worst-case
    169             // static info is fetched twice
    170             if (vm.canGet1_5LanguageFeatures()) {
    171                 /*
    172                  * we might as well get both the signature and the
    173                  * generic signature.
    174                  */
    175                 genericSignature();
    176             } else {
    177                 try {
    178                     signature = JDWP.ReferenceType.Signature.
    179                         process(vm, this).signature;
    180                 } catch (JDWPException exc) {
    181                     throw exc.toJDIException();
    182                 }
    183             }
    184         }
    185         return signature;
    186     }
    187 
    188     public String genericSignature() {
    189         // This gets both the signature and the generic signature
    190         if (vm.canGet1_5LanguageFeatures() && !genericSignatureGotten) {
    191             // Does not need synchronization, since worst-case
    192             // static info is fetched twice
    193             JDWP.ReferenceType.SignatureWithGeneric result;
    194             try {
    195                 result = JDWP.ReferenceType.SignatureWithGeneric.
    196                     process(vm, this);
    197             } catch (JDWPException exc) {
    198                 throw exc.toJDIException();
    199             }
    200             signature = result.signature;
    201             setGenericSignature(result.genericSignature);
    202         }
    203         return genericSignature;
    204     }
    205 
    206     public ClassLoaderReference classLoader() {
    207         if (!isClassLoaderCached) {
    208             // Does not need synchronization, since worst-case
    209             // static info is fetched twice
    210             try {
    211                 classLoader = (ClassLoaderReference)
    212                     JDWP.ReferenceType.ClassLoader.
    213                     process(vm, this).classLoader;
    214                 isClassLoaderCached = true;
    215             } catch (JDWPException exc) {
    216                 throw exc.toJDIException();
    217             }
    218         }
    219         return classLoader;
    220     }
    221 
    222     public boolean isPublic() {
    223         if (modifiers == -1)
    224             getModifiers();
    225 
    226         return((modifiers & VMModifiers.PUBLIC) > 0);
    227     }
    228 
    229     public boolean isProtected() {
    230         if (modifiers == -1)
    231             getModifiers();
    232 
    233         return((modifiers & VMModifiers.PROTECTED) > 0);
    234     }
    235 
    236     public boolean isPrivate() {
    237         if (modifiers == -1)
    238             getModifiers();
    239 
    240         return((modifiers & VMModifiers.PRIVATE) > 0);
    241     }
    242 
    243     public boolean isPackagePrivate() {
    244         return !isPublic() && !isPrivate() && !isProtected();
    245     }
    246 
    247     public boolean isAbstract() {
    248         if (modifiers == -1)
    249             getModifiers();
    250 
    251         return((modifiers & VMModifiers.ABSTRACT) > 0);
    252     }
    253 
    254     public boolean isFinal() {
    255         if (modifiers == -1)
    256             getModifiers();
    257 
    258         return((modifiers & VMModifiers.FINAL) > 0);
    259     }
    260 
    261     public boolean isStatic() {
    262         if (modifiers == -1)
    263             getModifiers();
    264 
    265         return((modifiers & VMModifiers.STATIC) > 0);
    266     }
    267 
    268     public boolean isPrepared() {
    269         // This ref type may have been prepared before we were getting
    270         // events, so get it once.  After that,
    271         // this status flag is updated through the ClassPrepareEvent,
    272         // there is no need for the expense of a JDWP query.
    273         if (status == 0) {
    274             updateStatus();
    275         }
    276         return isPrepared;
    277     }
    278 
    279     public boolean isVerified() {
    280         // Once true, it never resets, so we don't need to update
    281         if ((status & JDWP.ClassStatus.VERIFIED) == 0) {
    282             updateStatus();
    283         }
    284         return (status & JDWP.ClassStatus.VERIFIED) != 0;
    285     }
    286 
    287     public boolean isInitialized() {
    288         // Once initialization succeeds or fails, it never resets,
    289         // so we don't need to update
    290         if ((status & INITIALIZED_OR_FAILED) == 0) {
    291             updateStatus();
    292         }
    293         return (status & JDWP.ClassStatus.INITIALIZED) != 0;
    294     }
    295 
    296     public boolean failedToInitialize() {
    297         // Once initialization succeeds or fails, it never resets,
    298         // so we don't need to update
    299         if ((status & INITIALIZED_OR_FAILED) == 0) {
    300             updateStatus();
    301         }
    302         return (status & JDWP.ClassStatus.ERROR) != 0;
    303     }
    304 
    305     public List<Field> fields() {
    306         List<Field> fields = (fieldsRef == null) ? null : fieldsRef.get();
    307         if (fields == null) {
    308             if (vm.canGet1_5LanguageFeatures()) {
    309                 JDWP.ReferenceType.FieldsWithGeneric.FieldInfo[] jdwpFields;
    310                 try {
    311                     jdwpFields = JDWP.ReferenceType.FieldsWithGeneric.process(vm, this).declared;
    312                 } catch (JDWPException exc) {
    313                     throw exc.toJDIException();
    314                 }
    315                 fields = new ArrayList<Field>(jdwpFields.length);
    316                 for (int i=0; i<jdwpFields.length; i++) {
    317                     JDWP.ReferenceType.FieldsWithGeneric.FieldInfo fi
    318                         = jdwpFields[i];
    319 
    320                     Field field = new FieldImpl(vm, this, fi.fieldID,
    321                                                 fi.name, fi.signature,
    322                                                 fi.genericSignature,
    323                                                 fi.modBits);
    324                     fields.add(field);
    325                 }
    326             } else {
    327                 JDWP.ReferenceType.Fields.FieldInfo[] jdwpFields;
    328                 try {
    329                     jdwpFields = JDWP.ReferenceType.Fields.
    330                         process(vm, this).declared;
    331                 } catch (JDWPException exc) {
    332                     throw exc.toJDIException();
    333                 }
    334                 fields = new ArrayList<Field>(jdwpFields.length);
    335                 for (int i=0; i<jdwpFields.length; i++) {
    336                     JDWP.ReferenceType.Fields.FieldInfo fi = jdwpFields[i];
    337 
    338                     Field field = new FieldImpl(vm, this, fi.fieldID,
    339                                             fi.name, fi.signature,
    340                                             null,
    341                                             fi.modBits);
    342                     fields.add(field);
    343                 }
    344             }
    345 
    346             fields = Collections.unmodifiableList(fields);
    347             fieldsRef = new SoftReference<List<Field>>(fields);
    348         }
    349         return fields;
    350     }
    351 
    352     abstract List<? extends ReferenceType> inheritedTypes();
    353 
    354     void addVisibleFields(List<Field> visibleList, Map<String, Field> visibleTable, List<String> ambiguousNames) {
    355         for (Field field : visibleFields()) {
    356             String name = field.name();
    357             if (!ambiguousNames.contains(name)) {
    358                 Field duplicate = visibleTable.get(name);
    359                 if (duplicate == null) {
    360                     visibleList.add(field);
    361                     visibleTable.put(name, field);
    362                 } else if (!field.equals(duplicate)) {
    363                     ambiguousNames.add(name);
    364                     visibleTable.remove(name);
    365                     visibleList.remove(duplicate);
    366                 } else {
    367                     // identical field from two branches; do nothing
    368                 }
    369             }
    370         }
    371     }
    372 
    373     public List<Field> visibleFields() {
    374         /*
    375          * Maintain two different collections of visible fields. The
    376          * list maintains a reasonable order for return. The
    377          * hash map provides an efficient way to lookup visible fields
    378          * by name, important for finding hidden or ambiguous fields.
    379          */
    380         List<Field> visibleList = new ArrayList<Field>();
    381         Map<String, Field>  visibleTable = new HashMap<String, Field>();
    382 
    383         /* Track fields removed from above collection due to ambiguity */
    384         List<String> ambiguousNames = new ArrayList<String>();
    385 
    386         /* Add inherited, visible fields */
    387         List<? extends ReferenceType> types = inheritedTypes();
    388         Iterator<? extends ReferenceType> iter = types.iterator();
    389         while (iter.hasNext()) {
    390             /*
    391              * TO DO: Be defensive and check for cyclic interface inheritance
    392              */
    393             ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
    394             type.addVisibleFields(visibleList, visibleTable, ambiguousNames);
    395         }
    396 
    397         /*
    398          * Insert fields from this type, removing any inherited fields they
    399          * hide.
    400          */
    401         List<Field> retList = new ArrayList<Field>(fields());
    402         for (Field field : retList) {
    403             Field hidden = visibleTable.get(field.name());
    404             if (hidden != null) {
    405                 visibleList.remove(hidden);
    406             }
    407         }
    408         retList.addAll(visibleList);
    409         return retList;
    410     }
    411 
    412     void addAllFields(List<Field> fieldList, Set<ReferenceType> typeSet) {
    413         /* Continue the recursion only if this type is new */
    414         if (!typeSet.contains(this)) {
    415             typeSet.add((ReferenceType)this);
    416 
    417             /* Add local fields */
    418             fieldList.addAll(fields());
    419 
    420             /* Add inherited fields */
    421             List<? extends ReferenceType> types = inheritedTypes();
    422             Iterator<? extends ReferenceType> iter = types.iterator();
    423             while (iter.hasNext()) {
    424                 ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
    425                 type.addAllFields(fieldList, typeSet);
    426             }
    427         }
    428     }
    429     public List<Field> allFields() {
    430         List<Field> fieldList = new ArrayList<Field>();
    431         Set<ReferenceType> typeSet = new HashSet<ReferenceType>();
    432         addAllFields(fieldList, typeSet);
    433         return fieldList;
    434     }
    435 
    436     public Field fieldByName(String fieldName) {
    437         List<Field> searchList = visibleFields();
    438 
    439         for (int i=0; i<searchList.size(); i++) {
    440             Field f = searchList.get(i);
    441 
    442             if (f.name().equals(fieldName)) {
    443                 return f;
    444             }
    445         }
    446         //throw new NoSuchFieldException("Field '" + fieldName + "' not found in " + name());
    447         return null;
    448     }
    449 
    450     public List<Method> methods() {
    451         List<Method> methods = (methodsRef == null) ? null : methodsRef.get();
    452         if (methods == null) {
    453             if (!vm.canGet1_5LanguageFeatures()) {
    454                 methods = methods1_4();
    455             } else {
    456                 JDWP.ReferenceType.MethodsWithGeneric.MethodInfo[] declared;
    457                 try {
    458                     declared = JDWP.ReferenceType.MethodsWithGeneric.
    459                         process(vm, this).declared;
    460                 } catch (JDWPException exc) {
    461                     throw exc.toJDIException();
    462                 }
    463                 methods = new ArrayList<Method>(declared.length);
    464                 for (int i=0; i<declared.length; i++) {
    465                     JDWP.ReferenceType.MethodsWithGeneric.MethodInfo
    466                         mi = declared[i];
    467 
    468                     Method method = MethodImpl.createMethodImpl(vm, this,
    469                                                          mi.methodID,
    470                                                          mi.name, mi.signature,
    471                                                          mi.genericSignature,
    472                                                          mi.modBits);
    473                     methods.add(method);
    474                 }
    475             }
    476             methods = Collections.unmodifiableList(methods);
    477             methodsRef = new SoftReference<List<Method>>(methods);
    478         }
    479         return methods;
    480     }
    481 
    482     private List<Method> methods1_4() {
    483         List<Method> methods;
    484         JDWP.ReferenceType.Methods.MethodInfo[] declared;
    485         try {
    486             declared = JDWP.ReferenceType.Methods.
    487                 process(vm, this).declared;
    488         } catch (JDWPException exc) {
    489             throw exc.toJDIException();
    490         }
    491         methods = new ArrayList<Method>(declared.length);
    492         for (int i=0; i<declared.length; i++) {
    493             JDWP.ReferenceType.Methods.MethodInfo mi = declared[i];
    494 
    495             Method method = MethodImpl.createMethodImpl(vm, this,
    496                                                         mi.methodID,
    497                                                         mi.name, mi.signature,
    498                                                         null,
    499                                                         mi.modBits);
    500             methods.add(method);
    501         }
    502         return methods;
    503     }
    504 
    505     /*
    506      * Utility method used by subclasses to build lists of visible
    507      * methods.
    508      */
    509     void addToMethodMap(Map<String, Method> methodMap, List<Method> methodList) {
    510         for (Method method : methodList)
    511             methodMap.put(method.name().concat(method.signature()), method);
    512         }
    513 
    514     abstract void addVisibleMethods(Map<String, Method> methodMap, Set<InterfaceType> seenInterfaces);
    515 
    516     public List<Method> visibleMethods() {
    517         /*
    518          * Build a collection of all visible methods. The hash
    519          * map allows us to do this efficiently by keying on the
    520          * concatenation of name and signature.
    521          */
    522         Map<String, Method> map = new HashMap<String, Method>();
    523         addVisibleMethods(map, new HashSet<InterfaceType>());
    524 
    525         /*
    526          * ... but the hash map destroys order. Methods should be
    527          * returned in a sensible order, as they are in allMethods().
    528          * So, start over with allMethods() and use the hash map
    529          * to filter that ordered collection.
    530          */
    531         List<Method> list = allMethods();
    532         list.retainAll(map.values());
    533         return list;
    534     }
    535 
    536     abstract public List<Method> allMethods();
    537 
    538     public List<Method> methodsByName(String name) {
    539         List<Method> methods = visibleMethods();
    540         ArrayList<Method> retList = new ArrayList<Method>(methods.size());
    541         for (Method candidate : methods) {
    542             if (candidate.name().equals(name)) {
    543                 retList.add(candidate);
    544             }
    545         }
    546         retList.trimToSize();
    547         return retList;
    548     }
    549 
    550     public List<Method> methodsByName(String name, String signature) {
    551         List<Method> methods = visibleMethods();
    552         ArrayList<Method> retList = new ArrayList<Method>(methods.size());
    553         for (Method candidate : methods) {
    554             if (candidate.name().equals(name) &&
    555                 candidate.signature().equals(signature)) {
    556                 retList.add(candidate);
    557             }
    558         }
    559         retList.trimToSize();
    560         return retList;
    561     }
    562 
    563     List<InterfaceType> getInterfaces() {
    564         InterfaceTypeImpl[] intfs;
    565         try {
    566             intfs = JDWP.ReferenceType.Interfaces.
    567                                          process(vm, this).interfaces;
    568         } catch (JDWPException exc) {
    569             throw exc.toJDIException();
    570         }
    571         return Arrays.asList((InterfaceType[])intfs);
    572     }
    573 
    574     public List<ReferenceType> nestedTypes() {
    575         List<ReferenceType> all = vm.allClasses();
    576         List<ReferenceType> nested = new ArrayList<ReferenceType>();
    577         String outername = name();
    578         int outerlen = outername.length();
    579         Iterator<ReferenceType> iter = all.iterator();
    580         while (iter.hasNext()) {
    581             ReferenceType refType = iter.next();
    582             String name = refType.name();
    583             int len = name.length();
    584             /* The separator is historically '$' but could also be '#' */
    585             if ( len > outerlen && name.startsWith(outername) ) {
    586                 char c = name.charAt(outerlen);
    587                 if ( c =='$' || c== '#' ) {
    588                     nested.add(refType);
    589                 }
    590             }
    591         }
    592         return nested;
    593     }
    594 
    595     public Value getValue(Field sig) {
    596         List<Field> list = new ArrayList<Field>(1);
    597         list.add(sig);
    598         Map<Field, Value> map = getValues(list);
    599         return map.get(sig);
    600     }
    601 
    602 
    603     void validateFieldAccess(Field field) {
    604         /*
    605          * Field must be in this object's class, a superclass, or
    606          * implemented interface
    607          */
    608         ReferenceTypeImpl declType = (ReferenceTypeImpl)field.declaringType();
    609         if (!declType.isAssignableFrom(this)) {
    610             throw new IllegalArgumentException("Invalid field");
    611         }
    612     }
    613 
    614     void validateFieldSet(Field field) {
    615         validateFieldAccess(field);
    616         if (field.isFinal()) {
    617             throw new IllegalArgumentException("Cannot set value of final field");
    618         }
    619     }
    620 
    621     /**
    622      * Returns a map of field values
    623      */
    624     public Map<Field,Value> getValues(List<? extends Field> theFields) {
    625         validateMirrors(theFields);
    626 
    627         int size = theFields.size();
    628         JDWP.ReferenceType.GetValues.Field[] queryFields =
    629                          new JDWP.ReferenceType.GetValues.Field[size];
    630 
    631         for (int i=0; i<size; i++) {
    632             FieldImpl field = (FieldImpl)theFields.get(i);
    633 
    634             validateFieldAccess(field);
    635 
    636             // Do more validation specific to ReferenceType field getting
    637             if (!field.isStatic()) {
    638                 throw new IllegalArgumentException(
    639                      "Attempt to use non-static field with ReferenceType");
    640             }
    641             queryFields[i] = new JDWP.ReferenceType.GetValues.Field(
    642                                          field.ref());
    643         }
    644 
    645         Map<Field, Value> map = new HashMap<Field, Value>(size);
    646 
    647         ValueImpl[] values;
    648         try {
    649             values = JDWP.ReferenceType.GetValues.
    650                                      process(vm, this, queryFields).values;
    651         } catch (JDWPException exc) {
    652             throw exc.toJDIException();
    653         }
    654 
    655         if (size != values.length) {
    656             throw new InternalException(
    657                          "Wrong number of values returned from target VM");
    658         }
    659         for (int i=0; i<size; i++) {
    660             FieldImpl field = (FieldImpl)theFields.get(i);
    661             map.put(field, values[i]);
    662         }
    663 
    664         return map;
    665     }
    666 
    667     public ClassObjectReference classObject() {
    668         if (classObject == null) {
    669             // Are classObjects unique for an Object, or
    670             // created each time? Is this spec'ed?
    671             synchronized(this) {
    672                 if (classObject == null) {
    673                     try {
    674                         classObject = JDWP.ReferenceType.ClassObject.
    675                             process(vm, this).classObject;
    676                     } catch (JDWPException exc) {
    677                         throw exc.toJDIException();
    678                     }
    679                 }
    680             }
    681         }
    682         return classObject;
    683     }
    684 
    685     SDE.Stratum stratum(String stratumID) {
    686         SDE sde = sourceDebugExtensionInfo();
    687         if (!sde.isValid()) {
    688             sde = NO_SDE_INFO_MARK;
    689         }
    690         return sde.stratum(stratumID);
    691     }
    692 
    693     public String sourceName() throws AbsentInformationException {
    694         return sourceNames(vm.getDefaultStratum()).get(0);
    695     }
    696 
    697     public List<String> sourceNames(String stratumID)
    698                                 throws AbsentInformationException {
    699         SDE.Stratum stratum = stratum(stratumID);
    700         if (stratum.isJava()) {
    701             List<String> result = new ArrayList<String>(1);
    702             result.add(baseSourceName());
    703             return result;
    704         }
    705         return stratum.sourceNames(this);
    706     }
    707 
    708     public List<String> sourcePaths(String stratumID)
    709                                 throws AbsentInformationException {
    710         SDE.Stratum stratum = stratum(stratumID);
    711         if (stratum.isJava()) {
    712             List<String> result = new ArrayList<String>(1);
    713             result.add(baseSourceDir() + baseSourceName());
    714             return result;
    715         }
    716         return stratum.sourcePaths(this);
    717     }
    718 
    719     String baseSourceName() throws AbsentInformationException {
    720         String bsn = baseSourceName;
    721         if (bsn == null) {
    722             // Does not need synchronization, since worst-case
    723             // static info is fetched twice
    724             try {
    725                 bsn = JDWP.ReferenceType.SourceFile.
    726                     process(vm, this).sourceFile;
    727             } catch (JDWPException exc) {
    728                 if (exc.errorCode() == JDWP.Error.ABSENT_INFORMATION) {
    729                     bsn = ABSENT_BASE_SOURCE_NAME;
    730                 } else {
    731                     throw exc.toJDIException();
    732                 }
    733             }
    734             baseSourceName = bsn;
    735         }
    736         if (bsn == ABSENT_BASE_SOURCE_NAME) {
    737             throw new AbsentInformationException();
    738         }
    739         return bsn;
    740     }
    741 
    742     String baseSourcePath() throws AbsentInformationException {
    743         String bsp = baseSourcePath;
    744         if (bsp == null) {
    745             bsp = baseSourceDir() + baseSourceName();
    746             baseSourcePath = bsp;
    747         }
    748         return bsp;
    749     }
    750 
    751     String baseSourceDir() {
    752         if (baseSourceDir == null) {
    753             String typeName = name();
    754             StringBuffer sb = new StringBuffer(typeName.length() + 10);
    755             int index = 0;
    756             int nextIndex;
    757 
    758             while ((nextIndex = typeName.indexOf('.', index)) > 0) {
    759                 sb.append(typeName.substring(index, nextIndex));
    760                 sb.append(java.io.File.separatorChar);
    761                 index = nextIndex + 1;
    762             }
    763             baseSourceDir = sb.toString();
    764         }
    765         return baseSourceDir;
    766     }
    767 
    768     public String sourceDebugExtension()
    769                            throws AbsentInformationException {
    770         if (!vm.canGetSourceDebugExtension()) {
    771             throw new UnsupportedOperationException();
    772         }
    773         SDE sde = sourceDebugExtensionInfo();
    774         if (sde == NO_SDE_INFO_MARK) {
    775             throw new AbsentInformationException();
    776         }
    777         return sde.sourceDebugExtension;
    778     }
    779 
    780     private SDE sourceDebugExtensionInfo() {
    781         if (!vm.canGetSourceDebugExtension()) {
    782             return NO_SDE_INFO_MARK;
    783         }
    784         SDE sde = (sdeRef == null) ?  null : sdeRef.get();
    785         if (sde == null) {
    786             String extension = null;
    787             try {
    788                 extension = JDWP.ReferenceType.SourceDebugExtension.
    789                     process(vm, this).extension;
    790             } catch (JDWPException exc) {
    791                 if (exc.errorCode() != JDWP.Error.ABSENT_INFORMATION) {
    792                     sdeRef = new SoftReference<SDE>(NO_SDE_INFO_MARK);
    793                     throw exc.toJDIException();
    794                 }
    795             }
    796             if (extension == null) {
    797                 sde = NO_SDE_INFO_MARK;
    798             } else {
    799                 sde = new SDE(extension);
    800             }
    801             sdeRef = new SoftReference<SDE>(sde);
    802         }
    803         return sde;
    804     }
    805 
    806     public List<String> availableStrata() {
    807         SDE sde = sourceDebugExtensionInfo();
    808         if (sde.isValid()) {
    809             return sde.availableStrata();
    810         } else {
    811             List<String> strata = new ArrayList<String>();
    812             strata.add(SDE.BASE_STRATUM_NAME);
    813             return strata;
    814         }
    815     }
    816 
    817     /**
    818      * Always returns non-null stratumID
    819      */
    820     public String defaultStratum() {
    821         SDE sdei = sourceDebugExtensionInfo();
    822         if (sdei.isValid()) {
    823             return sdei.defaultStratumId;
    824         } else {
    825             return SDE.BASE_STRATUM_NAME;
    826         }
    827     }
    828 
    829     public int modifiers() {
    830         if (modifiers == -1)
    831             getModifiers();
    832 
    833         return modifiers;
    834     }
    835 
    836     public List<Location> allLineLocations()
    837                             throws AbsentInformationException {
    838         return allLineLocations(vm.getDefaultStratum(), null);
    839     }
    840 
    841     public List<Location> allLineLocations(String stratumID, String sourceName)
    842                             throws AbsentInformationException {
    843         boolean someAbsent = false; // A method that should have info, didn't
    844         SDE.Stratum stratum = stratum(stratumID);
    845         List<Location> list = new ArrayList<Location>();  // location list
    846 
    847         for (Iterator<Method> iter = methods().iterator(); iter.hasNext(); ) {
    848             MethodImpl method = (MethodImpl)iter.next();
    849             try {
    850                 list.addAll(
    851                    method.allLineLocations(stratum, sourceName));
    852             } catch(AbsentInformationException exc) {
    853                 someAbsent = true;
    854             }
    855         }
    856 
    857         // If we retrieved no line info, and at least one of the methods
    858         // should have had some (as determined by an
    859         // AbsentInformationException being thrown) then we rethrow
    860         // the AbsentInformationException.
    861         if (someAbsent && list.size() == 0) {
    862             throw new AbsentInformationException();
    863         }
    864         return list;
    865     }
    866 
    867     public List<Location> locationsOfLine(int lineNumber)
    868                            throws AbsentInformationException {
    869         return locationsOfLine(vm.getDefaultStratum(),
    870                                null,
    871                                lineNumber);
    872     }
    873 
    874     public List<Location> locationsOfLine(String stratumID,
    875                                 String sourceName,
    876                                 int lineNumber)
    877                            throws AbsentInformationException {
    878         // A method that should have info, didn't
    879         boolean someAbsent = false;
    880         // A method that should have info, did
    881         boolean somePresent = false;
    882         List<Method> methods = methods();
    883         SDE.Stratum stratum = stratum(stratumID);
    884 
    885         List<Location> list = new ArrayList<Location>();
    886 
    887         Iterator<Method> iter = methods.iterator();
    888         while(iter.hasNext()) {
    889             MethodImpl method = (MethodImpl)iter.next();
    890             // eliminate native and abstract to eliminate
    891             // false positives
    892             if (!method.isAbstract() &&
    893                 !method.isNative()) {
    894                 try {
    895                     list.addAll(
    896                        method.locationsOfLine(stratum,
    897                                               sourceName,
    898                                               lineNumber));
    899                     somePresent = true;
    900                 } catch(AbsentInformationException exc) {
    901                     someAbsent = true;
    902                 }
    903             }
    904         }
    905         if (someAbsent && !somePresent) {
    906             throw new AbsentInformationException();
    907         }
    908         return list;
    909     }
    910 
    911     public List<ObjectReference> instances(long maxInstances) {
    912         if (!vm.canGetInstanceInfo()) {
    913             throw new UnsupportedOperationException(
    914                 "target does not support getting instances");
    915         }
    916 
    917         if (maxInstances < 0) {
    918             throw new IllegalArgumentException("maxInstances is less than zero: "
    919                                               + maxInstances);
    920         }
    921         int intMax = (maxInstances > Integer.MAX_VALUE)?
    922             Integer.MAX_VALUE: (int)maxInstances;
    923         // JDWP can't currently handle more than this (in mustang)
    924 
    925         try {
    926             return Arrays.asList(
    927                 (ObjectReference[])JDWP.ReferenceType.Instances.
    928                         process(vm, this, intMax).instances);
    929         } catch (JDWPException exc) {
    930             throw exc.toJDIException();
    931         }
    932     }
    933 
    934     private void getClassFileVersion() {
    935         if (!vm.canGetClassFileVersion()) {
    936             throw new UnsupportedOperationException();
    937         }
    938         JDWP.ReferenceType.ClassFileVersion classFileVersion;
    939         if (versionNumberGotten) {
    940             return;
    941         } else {
    942             try {
    943                 classFileVersion = JDWP.ReferenceType.ClassFileVersion.process(vm, this);
    944             } catch (JDWPException exc) {
    945                 if (exc.errorCode() == JDWP.Error.ABSENT_INFORMATION) {
    946                     majorVersion = 0;
    947                     minorVersion = 0;
    948                     versionNumberGotten = true;
    949                     return;
    950                 } else {
    951                     throw exc.toJDIException();
    952                 }
    953             }
    954             majorVersion = classFileVersion.majorVersion;
    955             minorVersion = classFileVersion.minorVersion;
    956             versionNumberGotten = true;
    957         }
    958     }
    959 
    960     public int majorVersion() {
    961         try {
    962             getClassFileVersion();
    963         } catch (RuntimeException exc) {
    964             throw exc;
    965         }
    966         return majorVersion;
    967     }
    968 
    969     public int minorVersion() {
    970         try {
    971             getClassFileVersion();
    972         } catch (RuntimeException exc) {
    973             throw exc;
    974         }
    975         return minorVersion;
    976     }
    977 
    978     private byte[] getConstantPoolInfo() {
    979         JDWP.ReferenceType.ConstantPool jdwpCPool;
    980         if (!vm.canGetConstantPool()) {
    981             throw new UnsupportedOperationException();
    982         }
    983         if (constantPoolInfoGotten) {
    984             if (constantPoolBytesRef == null) {
    985                 return null;
    986             }
    987             byte[] cpbytes = constantPoolBytesRef.get();
    988             if (cpbytes != null) {
    989                 return cpbytes;
    990             }
    991         }
    992 
    993         try {
    994             jdwpCPool = JDWP.ReferenceType.ConstantPool.process(vm, this);
    995         } catch (JDWPException exc) {
    996             if (exc.errorCode() == JDWP.Error.ABSENT_INFORMATION) {
    997                 constanPoolCount = 0;
    998                 constantPoolBytesRef = null;
    999                 constantPoolInfoGotten = true;
   1000                 return null;
   1001             } else {
   1002                 throw exc.toJDIException();
   1003             }
   1004         }
   1005         byte[] cpbytes;
   1006         constanPoolCount = jdwpCPool.count;
   1007         cpbytes = jdwpCPool.bytes;
   1008         constantPoolBytesRef = new SoftReference<byte[]>(cpbytes);
   1009         constantPoolInfoGotten = true;
   1010         return cpbytes;
   1011     }
   1012 
   1013     public int constantPoolCount() {
   1014         try {
   1015             getConstantPoolInfo();
   1016         } catch (RuntimeException exc) {
   1017             throw exc;
   1018         }
   1019         return constanPoolCount;
   1020     }
   1021 
   1022     public byte[] constantPool() {
   1023         byte[] cpbytes;
   1024         try {
   1025             cpbytes = getConstantPoolInfo();
   1026         } catch (RuntimeException exc) {
   1027             throw exc;
   1028         }
   1029         if (cpbytes != null) {
   1030             /*
   1031              * Arrays are always modifiable, so it is a little unsafe
   1032              * to return the cached bytecodes directly; instead, we
   1033              * make a clone at the cost of using more memory.
   1034              */
   1035             return cpbytes.clone();
   1036         } else {
   1037             return null;
   1038         }
   1039     }
   1040 
   1041     // Does not need synchronization, since worst-case
   1042     // static info is fetched twice
   1043     void getModifiers() {
   1044         if (modifiers != -1) {
   1045             return;
   1046         }
   1047         try {
   1048             modifiers = JDWP.ReferenceType.Modifiers.
   1049                                   process(vm, this).modBits;
   1050         } catch (JDWPException exc) {
   1051             throw exc.toJDIException();
   1052         }
   1053     }
   1054 
   1055     void decodeStatus(int status) {
   1056         this.status = status;
   1057         if ((status & JDWP.ClassStatus.PREPARED) != 0) {
   1058             isPrepared = true;
   1059         }
   1060     }
   1061 
   1062     void updateStatus() {
   1063         try {
   1064             decodeStatus(JDWP.ReferenceType.Status.process(vm, this).status);
   1065         } catch (JDWPException exc) {
   1066             throw exc.toJDIException();
   1067         }
   1068     }
   1069 
   1070     void markPrepared() {
   1071         isPrepared = true;
   1072     }
   1073 
   1074     long ref() {
   1075         return ref;
   1076     }
   1077 
   1078     int indexOf(Method method) {
   1079         // Make sure they're all here - the obsolete method
   1080         // won't be found and so will have index -1
   1081         return methods().indexOf(method);
   1082     }
   1083 
   1084     int indexOf(Field field) {
   1085         // Make sure they're all here
   1086         return fields().indexOf(field);
   1087     }
   1088 
   1089     /*
   1090      * Return true if an instance of this type
   1091      * can be assigned to a variable of the given type
   1092      */
   1093     abstract boolean isAssignableTo(ReferenceType type);
   1094 
   1095     boolean isAssignableFrom(ReferenceType type) {
   1096         return ((ReferenceTypeImpl)type).isAssignableTo(this);
   1097     }
   1098 
   1099     boolean isAssignableFrom(ObjectReference object) {
   1100         return object == null ||
   1101                isAssignableFrom(object.referenceType());
   1102     }
   1103 
   1104     void setStatus(int status) {
   1105         decodeStatus(status);
   1106     }
   1107 
   1108     void setSignature(String signature) {
   1109         this.signature = signature;
   1110     }
   1111 
   1112     void setGenericSignature(String signature) {
   1113         if (signature != null && signature.length() == 0) {
   1114             this.genericSignature = null;
   1115         } else{
   1116             this.genericSignature = signature;
   1117         }
   1118         this.genericSignatureGotten = true;
   1119     }
   1120 
   1121     private static boolean isPrimitiveArray(String signature) {
   1122         int i = signature.lastIndexOf('[');
   1123         /*
   1124          * TO DO: Centralize JNI signature knowledge.
   1125          *
   1126          * Ref:
   1127          *  jdk1.4/doc/guide/jpda/jdi/com/sun/jdi/doc-files/signature.html
   1128          */
   1129         boolean isPA;
   1130         if (i < 0) {
   1131             isPA = false;
   1132         } else {
   1133             char c = signature.charAt(i + 1);
   1134             isPA = (c != 'L');
   1135         }
   1136         return isPA;
   1137     }
   1138 
   1139     Type findType(String signature) throws ClassNotLoadedException {
   1140         Type type;
   1141         if (signature.length() == 1) {
   1142             /* OTI FIX: Must be a primitive type or the void type */
   1143             char sig = signature.charAt(0);
   1144             if (sig == 'V') {
   1145                 type = vm.theVoidType();
   1146             } else {
   1147                 type = vm.primitiveTypeMirror((byte)sig);
   1148             }
   1149         } else {
   1150             // Must be a reference type.
   1151             ClassLoaderReferenceImpl loader =
   1152                        (ClassLoaderReferenceImpl)classLoader();
   1153             if ((loader == null) ||
   1154                 (isPrimitiveArray(signature)) //Work around 4450091
   1155                 ) {
   1156                 // Caller wants type of boot class field
   1157                 type = vm.findBootType(signature);
   1158             } else {
   1159                 // Caller wants type of non-boot class field
   1160                 type = loader.findType(signature);
   1161             }
   1162         }
   1163         return type;
   1164     }
   1165 
   1166     String loaderString() {
   1167         if (classLoader() != null) {
   1168             return "loaded by " + classLoader().toString();
   1169         } else {
   1170             return "no class loader";
   1171         }
   1172     }
   1173 
   1174 }
   1175