1 /*
   2  * Copyright (c) 2002, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot.jdi;
  26 
  27 import java.io.*;
  28 
  29 import com.sun.jdi.*;
  30 
  31 import sun.jvm.hotspot.memory.SystemDictionary;
  32 import sun.jvm.hotspot.oops.Instance;
  33 import sun.jvm.hotspot.oops.InstanceKlass;
  34 import sun.jvm.hotspot.oops.ArrayKlass;
  35 import sun.jvm.hotspot.oops.JVMDIClassStatus;
  36 import sun.jvm.hotspot.oops.Klass;
  37 import sun.jvm.hotspot.oops.ObjArray;
  38 import sun.jvm.hotspot.oops.Oop;
  39 import sun.jvm.hotspot.oops.Symbol;
  40 import sun.jvm.hotspot.oops.DefaultHeapVisitor;
  41 import sun.jvm.hotspot.utilities.Assert;
  42 
  43 import java.util.*;
  44 import java.lang.ref.SoftReference;
  45 
  46 public abstract class ReferenceTypeImpl extends TypeImpl
  47 implements ReferenceType {
  48     protected Klass       saKlass;          // This can be an InstanceKlass or an ArrayKlass
  49     protected Symbol      typeNameSymbol;   // This is used in vm.classesByName to speedup search
  50     private int           modifiers = -1;
  51     private String        signature = null;
  52     private SoftReference sdeRef = null;
  53     private SoftReference fieldsCache;
  54     private SoftReference allFieldsCache;
  55     private SoftReference methodsCache;
  56     private SoftReference allMethodsCache;
  57     private SoftReference nestedTypesCache;
  58     private SoftReference methodInvokesCache;
  59 
  60     /* to mark when no info available */
  61     static final SDE NO_SDE_INFO_MARK = new SDE();
  62 
  63     protected ReferenceTypeImpl(VirtualMachine aVm, sun.jvm.hotspot.oops.Klass klass) {
  64         super(aVm);
  65         saKlass = klass;
  66         typeNameSymbol = saKlass.getName();
  67         if (Assert.ASSERTS_ENABLED) {
  68             Assert.that(typeNameSymbol != null, "null type name for a Klass");
  69         }
  70     }
  71 
  72     Symbol typeNameAsSymbol() {
  73         return typeNameSymbol;
  74     }
  75 
  76     Method getMethodMirror(sun.jvm.hotspot.oops.Method ref) {
  77         // SA creates new Method objects when they are referenced which means
  78         // that the incoming object might not be the same object as on our
  79         // even though it is the same method. So do an address compare by
  80         // calling equals rather than just reference compare.
  81         Iterator it = methods().iterator();
  82         while (it.hasNext()) {
  83             MethodImpl method = (MethodImpl)it.next();
  84             if (ref.equals(method.ref())) {
  85                 return method;
  86             }
  87         }
  88         if (ref.getMethodHolder().equals(SystemDictionary.getMethodHandleKlass())) {
  89           // invoke methods are generated as needed, so make mirrors as needed
  90           List mis = null;
  91           if (methodInvokesCache == null) {
  92             mis = new ArrayList();
  93             methodInvokesCache = new SoftReference(mis);
  94           } else {
  95             mis = (List)methodInvokesCache.get();
  96           }
  97           it = mis.iterator();
  98           while (it.hasNext()) {
  99             MethodImpl method = (MethodImpl)it.next();
 100             if (ref.equals(method.ref())) {
 101               return method;
 102             }
 103           }
 104 
 105           MethodImpl method = MethodImpl.createMethodImpl(vm, this, ref);
 106           mis.add(method);
 107           return method;
 108         }
 109         throw new IllegalArgumentException("Invalid method id: " + ref);
 110     }
 111 
 112     public boolean equals(Object obj) {
 113         if ((obj != null) && (obj instanceof ReferenceTypeImpl)) {
 114             ReferenceTypeImpl other = (ReferenceTypeImpl)obj;
 115             return (ref().equals(other.ref())) &&
 116                 (vm.equals(other.virtualMachine()));
 117         } else {
 118             return false;
 119         }
 120     }
 121 
 122     public int hashCode() {
 123         return saKlass.hashCode();
 124     }
 125 
 126     public int compareTo(ReferenceType refType) {
 127         /*
 128          * Note that it is critical that compareTo() == 0
 129          * implies that equals() == true. Otherwise, TreeSet
 130          * will collapse classes.
 131          *
 132          * (Classes of the same name loaded by different class loaders
 133          * or in different VMs must not return 0).
 134          */
 135         ReferenceTypeImpl other = (ReferenceTypeImpl)refType;
 136         int comp = name().compareTo(other.name());
 137         if (comp == 0) {
 138             Oop rf1 = ref();
 139             Oop rf2 = other.ref();
 140             // optimize for typical case: refs equal and VMs equal
 141             if (rf1.equals(rf2)) {
 142                 // sequenceNumbers are always positive
 143                 comp = vm.sequenceNumber -
 144                  ((VirtualMachineImpl)(other.virtualMachine())).sequenceNumber;
 145             } else {
 146                 comp = rf1.getHandle().minus(rf2.getHandle()) < 0? -1 : 1;
 147             }
 148         }
 149         return comp;
 150     }
 151 
 152     public String signature() {
 153         if (signature == null) {
 154             signature = saKlass.signature();
 155         }
 156         return signature;
 157     }
 158 
 159     // refer to JvmtiEnv::GetClassSignature.
 160     // null is returned for array klasses.
 161     public String genericSignature() {
 162         if (saKlass instanceof ArrayKlass) {
 163             return null;
 164         } else {
 165             Symbol genSig = ((InstanceKlass)saKlass).getGenericSignature();
 166             return (genSig != null)? genSig.asString() : null;
 167         }
 168     }
 169 
 170     public ClassLoaderReference classLoader() {
 171       Instance xx = (Instance)(((InstanceKlass)saKlass).getClassLoader());
 172       return (ClassLoaderReferenceImpl)vm.classLoaderMirror(xx);
 173     }
 174 
 175     public boolean isPublic() {
 176         return((modifiers() & VMModifiers.PUBLIC) != 0);
 177     }
 178 
 179     public boolean isProtected() {
 180         return((modifiers() & VMModifiers.PROTECTED) != 0);
 181     }
 182 
 183     public boolean isPrivate() {
 184         return((modifiers() & VMModifiers.PRIVATE) != 0);
 185     }
 186 
 187     public boolean isPackagePrivate() {
 188         return !isPublic() && !isPrivate() && !isProtected();
 189     }
 190 
 191     public boolean isAbstract() {
 192         return((modifiers() & VMModifiers.ABSTRACT) != 0);
 193     }
 194 
 195     public boolean isFinal() {
 196         return((modifiers() & VMModifiers.FINAL) != 0);
 197     }
 198 
 199     public boolean isStatic() {
 200         return((modifiers() & VMModifiers.STATIC) != 0);
 201     }
 202 
 203     public boolean isPrepared() {
 204         return (saKlass.getClassStatus() & JVMDIClassStatus.PREPARED) != 0;
 205     }
 206 
 207     final void checkPrepared() throws ClassNotPreparedException {
 208         if (! isPrepared()) {
 209             throw new ClassNotPreparedException();
 210         }
 211     }
 212 
 213     public boolean isVerified() {
 214         return (saKlass.getClassStatus() & JVMDIClassStatus.VERIFIED) != 0;
 215     }
 216 
 217     public boolean isInitialized() {
 218         return (saKlass.getClassStatus() & JVMDIClassStatus.INITIALIZED) != 0;
 219     }
 220 
 221     public boolean failedToInitialize() {
 222         return (saKlass.getClassStatus() & JVMDIClassStatus.ERROR) != 0;
 223     }
 224 
 225     private boolean isThrowableBacktraceField(sun.jvm.hotspot.oops.Field fld) {
 226         // refer to JvmtiEnv::GetClassFields in jvmtiEnv.cpp.
 227         // We want to filter out java.lang.Throwable.backtrace (see 4446677).
 228         // It contains some methodOops that aren't quite real Objects.
 229         if (fld.getFieldHolder().getName().equals(vm.javaLangThrowable()) &&
 230             fld.getID().getName().equals("backtrace")) {
 231             return true;
 232         } else {
 233             return false;
 234         }
 235     }
 236 
 237     public final List fields() throws ClassNotPreparedException {
 238         List fields = (fieldsCache != null)? (List) fieldsCache.get() : null;
 239         if (fields == null) {
 240             checkPrepared();
 241             if (saKlass instanceof ArrayKlass) {
 242                 fields = new ArrayList(0);
 243             } else {
 244                 // Get a list of the sa Field types
 245                 List saFields = ((InstanceKlass)saKlass).getImmediateFields();
 246 
 247                 // Create a list of our Field types
 248                 int len = saFields.size();
 249                 fields = new ArrayList(len);
 250                 for (int ii = 0; ii < len; ii++) {
 251                     sun.jvm.hotspot.oops.Field curField = (sun.jvm.hotspot.oops.Field)saFields.get(ii);
 252                     if (! isThrowableBacktraceField(curField)) {
 253                         fields.add(new FieldImpl(vm, this, curField));
 254                     }
 255                 }
 256             }
 257             fields = Collections.unmodifiableList(fields);
 258             fieldsCache = new SoftReference(fields);
 259         }
 260         return fields;
 261     }
 262 
 263     public final List allFields() throws ClassNotPreparedException {
 264         List allFields = (allFieldsCache != null)? (List) allFieldsCache.get() : null;
 265         if (allFields == null) {
 266             checkPrepared();
 267             if (saKlass instanceof ArrayKlass) {
 268                 // is 'length' a field of array klasses? To maintain
 269                 // consistency with JVMDI-JDI we return 0 size.
 270                 allFields = new ArrayList(0);
 271             } else {
 272                 List saFields;
 273 
 274                 // Get a list of the sa Field types
 275                 saFields = ((InstanceKlass)saKlass).getAllFields();
 276 
 277                 // Create a list of our Field types
 278                 int len = saFields.size();
 279                 allFields = new ArrayList(len);
 280                 for (int ii = 0; ii < len; ii++) {
 281                     sun.jvm.hotspot.oops.Field curField = (sun.jvm.hotspot.oops.Field)saFields.get(ii);
 282                     if (! isThrowableBacktraceField(curField)) {
 283                         allFields.add(new FieldImpl(vm, vm.referenceType(curField.getFieldHolder()), curField));
 284                     }
 285                 }
 286             }
 287             allFields = Collections.unmodifiableList(allFields);
 288             allFieldsCache = new SoftReference(allFields);
 289         }
 290         return allFields;
 291     }
 292 
 293     abstract List inheritedTypes();
 294 
 295     void addVisibleFields(List visibleList, Map visibleTable, List ambiguousNames) {
 296         List list = visibleFields();
 297         Iterator iter = list.iterator();
 298         while (iter.hasNext()) {
 299             Field field = (Field)iter.next();
 300             String name = field.name();
 301             if (!ambiguousNames.contains(name)) {
 302                 Field duplicate = (Field)visibleTable.get(name);
 303                 if (duplicate == null) {
 304                     visibleList.add(field);
 305                     visibleTable.put(name, field);
 306                 } else if (!field.equals(duplicate)) {
 307                     ambiguousNames.add(name);
 308                     visibleTable.remove(name);
 309                     visibleList.remove(duplicate);
 310                 } else {
 311                     // identical field from two branches; do nothing
 312                 }
 313             }
 314         }
 315     }
 316 
 317     public final List visibleFields() throws ClassNotPreparedException {
 318         checkPrepared();
 319         /*
 320          * Maintain two different collections of visible fields. The
 321          * list maintains a reasonable order for return. The
 322          * hash map provides an efficient way to lookup visible fields
 323          * by name, important for finding hidden or ambiguous fields.
 324          */
 325         List visibleList = new ArrayList();
 326         Map  visibleTable = new HashMap();
 327 
 328         /* Track fields removed from above collection due to ambiguity */
 329         List ambiguousNames = new ArrayList();
 330 
 331         /* Add inherited, visible fields */
 332         List types = inheritedTypes();
 333         Iterator iter = types.iterator();
 334         while (iter.hasNext()) {
 335             /*
 336              * TO DO: Be defensive and check for cyclic interface inheritance
 337              */
 338             ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
 339             type.addVisibleFields(visibleList, visibleTable, ambiguousNames);
 340         }
 341 
 342         /*
 343          * Insert fields from this type, removing any inherited fields they
 344          * hide.
 345          */
 346         List retList = new ArrayList(fields());
 347         iter = retList.iterator();
 348         while (iter.hasNext()) {
 349             Field field = (Field)iter.next();
 350             Field hidden = (Field)visibleTable.get(field.name());
 351             if (hidden != null) {
 352                 visibleList.remove(hidden);
 353             }
 354         }
 355         retList.addAll(visibleList);
 356         return retList;
 357     }
 358 
 359    public final Field fieldByName(String fieldName) throws ClassNotPreparedException {
 360         java.util.List searchList;
 361         Field f;
 362 
 363         // visibleFields calls checkPrepared
 364         searchList = visibleFields();
 365 
 366         for (int i=0; i<searchList.size(); i++) {
 367             f = (Field)searchList.get(i);
 368 
 369             if (f.name().equals(fieldName)) {
 370                 return f;
 371             }
 372         }
 373         //throw new NoSuchFieldException("Field '" + fieldName + "' not found in " + name());
 374         return null;
 375     }
 376 
 377     public final List methods() throws ClassNotPreparedException {
 378         List methods = (methodsCache != null)? (List) methodsCache.get() : null;
 379         if (methods == null) {
 380             checkPrepared();
 381             if (saKlass instanceof ArrayKlass) {
 382                 methods = new ArrayList(0);
 383             } else {
 384                 List saMethods;
 385                 // Get a list of the SA Method types
 386                 saMethods = ((InstanceKlass)saKlass).getImmediateMethods();
 387 
 388                 // Create a list of our MethodImpl types
 389                 int len = saMethods.size();
 390                 methods = new ArrayList(len);
 391                 for (int ii = 0; ii < len; ii++) {
 392                     methods.add(MethodImpl.createMethodImpl(vm, this, (sun.jvm.hotspot.oops.Method)saMethods.get(ii)));
 393                 }
 394             }
 395             methods = Collections.unmodifiableList(methods);
 396             methodsCache = new SoftReference(methods);
 397         }
 398         return methods;
 399     }
 400 
 401     abstract List getAllMethods();
 402     public final List allMethods() throws ClassNotPreparedException {
 403         List allMethods = (allMethodsCache != null)? (List) allMethodsCache.get() : null;
 404         if (allMethods == null) {
 405             checkPrepared();
 406             allMethods = Collections.unmodifiableList(getAllMethods());
 407             allMethodsCache = new SoftReference(allMethods);
 408         }
 409         return allMethods;
 410     }
 411 
 412     /*
 413      * Utility method used by subclasses to build lists of visible
 414      * methods.
 415      */
 416     void addToMethodMap(Map methodMap, List methodList) {
 417         Iterator iter = methodList.iterator();
 418         while (iter.hasNext()) {
 419             Method method = (Method)iter.next();
 420             methodMap.put(method.name().concat(method.signature()), method);
 421         }
 422     }
 423 
 424     abstract void addVisibleMethods(Map methodMap);
 425     public final List visibleMethods() throws ClassNotPreparedException {
 426         checkPrepared();
 427         /*
 428          * Build a collection of all visible methods. The hash
 429          * map allows us to do this efficiently by keying on the
 430          * concatenation of name and signature.
 431          */
 432         //System.out.println("jj: RTI: Calling addVisibleMethods for:" + this);
 433         Map map = new HashMap();
 434         addVisibleMethods(map);
 435 
 436         /*
 437          * ... but the hash map destroys order. Methods should be
 438          * returned in a sensible order, as they are in allMethods().
 439          * So, start over with allMethods() and use the hash map
 440          * to filter that ordered collection.
 441          */
 442         //System.out.println("jj: RTI: Calling allMethods for:" + this);
 443 
 444         List list = new ArrayList(allMethods());
 445         //System.out.println("jj: allMethods = " + jjstr(list));
 446         //System.out.println("jj: map = " + map.toString());
 447         //System.out.println("jj: map = " + jjstr(map.values()));
 448         list.retainAll(map.values());
 449         //System.out.println("jj: map = " + jjstr(list));
 450         //System.exit(0);
 451         return list;
 452     }
 453 
 454     static Object prev;
 455 
 456     static public String jjstr(Collection cc) {
 457         StringBuffer buf = new StringBuffer();
 458         buf.append("[");
 459         Iterator i = cc.iterator();
 460         boolean hasNext = i.hasNext();
 461         while (hasNext) {
 462             Object o = i.next();
 463             if (prev == null) {
 464                 prev = o;
 465             } else {
 466                 System.out.println("prev == curr?" + prev.equals(o));
 467                 System.out.println("prev == curr?" + (prev == o));
 468             }
 469             buf.append( o + "@" + o.hashCode());
 470             //buf.append( ((Object)o).toString());
 471             hasNext = i.hasNext();
 472             if (hasNext)
 473                 buf.append(", ");
 474         }
 475 
 476         buf.append("]");
 477         return buf.toString();
 478     }
 479 
 480     public final List methodsByName(String name) throws ClassNotPreparedException {
 481         // visibleMethods calls checkPrepared
 482         List methods = visibleMethods();
 483         ArrayList retList = new ArrayList(methods.size());
 484         Iterator iter = methods.iterator();
 485         while (iter.hasNext()) {
 486             Method candidate = (Method)iter.next();
 487             if (candidate.name().equals(name)) {
 488                 retList.add(candidate);
 489             }
 490         }
 491         retList.trimToSize();
 492         return retList;
 493     }
 494 
 495     public final List methodsByName(String name, String signature) throws ClassNotPreparedException {
 496         // visibleMethods calls checkPrepared
 497         List methods = visibleMethods();
 498         ArrayList retList = new ArrayList(methods.size());
 499         Iterator iter = methods.iterator();
 500         while (iter.hasNext()) {
 501             Method candidate = (Method)iter.next();
 502             if (candidate.name().equals(name) &&
 503                 candidate.signature().equals(signature)) {
 504                 retList.add(candidate);
 505             }
 506         }
 507         retList.trimToSize();
 508         return retList;
 509     }
 510 
 511 
 512     List getInterfaces() {
 513         List myInterfaces;
 514         if (saKlass instanceof ArrayKlass) {
 515             // Actually, JLS says arrays implement Cloneable and Serializable
 516             // But, JVMDI-JDI just returns 0 interfaces for arrays. We follow
 517             // the same for consistency.
 518             myInterfaces = new ArrayList(0);
 519         } else {
 520             // Get a list of the sa InstanceKlass types
 521             List saInterfaces = ((InstanceKlass)saKlass).getDirectImplementedInterfaces();
 522 
 523             // Create a list of our InterfaceTypes
 524             int len = saInterfaces.size();
 525             myInterfaces = new ArrayList(len);
 526             for (int ii = 0; ii < len; ii++) {
 527                 myInterfaces.add(new InterfaceTypeImpl(vm, (InstanceKlass)saInterfaces.get(ii)));
 528             }
 529         }
 530         return myInterfaces;
 531     }
 532 
 533     public final List nestedTypes() {
 534         List nestedTypes = (nestedTypesCache != null)? (List) nestedTypesCache.get() : null;
 535         if (nestedTypes == null) {
 536             if (saKlass instanceof ArrayKlass) {
 537                 nestedTypes = new ArrayList(0);
 538             } else {
 539                 ClassLoaderReference cl = classLoader();
 540                 List classes = null;
 541                 if (cl != null) {
 542                    classes = cl.visibleClasses();
 543                 } else {
 544                    classes = vm.bootstrapClasses();
 545                 }
 546                 nestedTypes = new ArrayList();
 547                 Iterator iter = classes.iterator();
 548                 while (iter.hasNext()) {
 549                     ReferenceTypeImpl refType = (ReferenceTypeImpl)iter.next();
 550                     Symbol candidateName = refType.ref().getName();
 551                     if (((InstanceKlass)saKlass).isInnerOrLocalClassName(candidateName)) {
 552                         nestedTypes.add(refType);
 553                     }
 554                 }
 555             }
 556             nestedTypes = Collections.unmodifiableList(nestedTypes);
 557             nestedTypesCache = new SoftReference(nestedTypes);
 558         }
 559         return nestedTypes;
 560     }
 561 
 562     public Value getValue(Field sig) {
 563         List list = new ArrayList(1);
 564         list.add(sig);
 565         Map map = getValues(list);
 566         return(Value)map.get(sig);
 567     }
 568 
 569     /**
 570      * Returns a map of field values
 571      */
 572     public Map getValues(List theFields) {
 573         //validateMirrors();
 574         int size = theFields.size();
 575         Map map = new HashMap(size);
 576         for (int ii=0; ii<size; ii++) {
 577             FieldImpl fieldImpl = (FieldImpl)theFields.get(ii);
 578 
 579             validateFieldAccess(fieldImpl);
 580             // Do more validation specific to ReferenceType field getting
 581             if (!fieldImpl.isStatic()) {
 582                 throw new IllegalArgumentException(
 583                      "Attempt to use non-static field with ReferenceType: " +
 584                      fieldImpl.name());
 585             }
 586             map.put(fieldImpl, fieldImpl.getValue());
 587         }
 588         return map;
 589     }
 590 
 591     void validateFieldAccess(Field field) {
 592        /*
 593         * Field must be in this object's class, a superclass, or
 594         * implemented interface
 595         */
 596         ReferenceTypeImpl declType = (ReferenceTypeImpl)field.declaringType();
 597         if (!declType.isAssignableFrom(this)) {
 598             throw new IllegalArgumentException("Invalid field");
 599         }
 600     }
 601 
 602     public ClassObjectReference classObject() {
 603         return vm.classObjectMirror(ref().getJavaMirror());
 604     }
 605 
 606     SDE.Stratum stratum(String stratumID) {
 607         SDE sde = sourceDebugExtensionInfo();
 608         if (!sde.isValid()) {
 609             sde = NO_SDE_INFO_MARK;
 610         }
 611         return sde.stratum(stratumID);
 612     }
 613 
 614     public String sourceName() throws AbsentInformationException {
 615         return (String)(sourceNames(vm.getDefaultStratum()).get(0));
 616     }
 617 
 618     public List sourceNames(String stratumID)
 619                                 throws AbsentInformationException {
 620         SDE.Stratum stratum = stratum(stratumID);
 621         if (stratum.isJava()) {
 622             List result = new ArrayList(1);
 623             result.add(baseSourceName());
 624             return result;
 625         }
 626         return stratum.sourceNames(this);
 627     }
 628 
 629     public List sourcePaths(String stratumID)
 630                                 throws AbsentInformationException {
 631         SDE.Stratum stratum = stratum(stratumID);
 632         if (stratum.isJava()) {
 633             List result = new ArrayList(1);
 634             result.add(baseSourceDir() + baseSourceName());
 635             return result;
 636         }
 637         return stratum.sourcePaths(this);
 638     }
 639 
 640     String baseSourceName() throws AbsentInformationException {
 641       if (saKlass instanceof ArrayKlass) {
 642             throw new AbsentInformationException();
 643       }
 644       Symbol sym = ((InstanceKlass)saKlass).getSourceFileName();
 645       if (sym != null) {
 646           return sym.asString();
 647       } else {
 648           throw new AbsentInformationException();
 649       }
 650     }
 651 
 652     String baseSourcePath() throws AbsentInformationException {
 653         return baseSourceDir() + baseSourceName();
 654     }
 655 
 656     String baseSourceDir() {
 657         String typeName = name();
 658         StringBuffer sb = new StringBuffer(typeName.length() + 10);
 659         int index = 0;
 660         int nextIndex;
 661 
 662         while ((nextIndex = typeName.indexOf('.', index)) > 0) {
 663             sb.append(typeName.substring(index, nextIndex));
 664             sb.append(java.io.File.separatorChar);
 665             index = nextIndex + 1;
 666         }
 667         return sb.toString();
 668     }
 669 
 670     public String sourceDebugExtension()
 671                            throws AbsentInformationException {
 672         if (!vm.canGetSourceDebugExtension()) {
 673             throw new UnsupportedOperationException();
 674         }
 675         SDE sde = sourceDebugExtensionInfo();
 676         if (sde == NO_SDE_INFO_MARK) {
 677             throw new AbsentInformationException();
 678         }
 679         return sde.sourceDebugExtension;
 680     }
 681 
 682     private SDE sourceDebugExtensionInfo() {
 683         if (!vm.canGetSourceDebugExtension()) {
 684             return NO_SDE_INFO_MARK;
 685         }
 686         SDE sde = null;
 687         sde = (sdeRef == null) ?  null : (SDE)sdeRef.get();
 688         if (sde == null) {
 689            String extension = null;
 690            if (saKlass instanceof InstanceKlass) {
 691               Symbol sdeSym = ((InstanceKlass)saKlass).getSourceDebugExtension();
 692               extension = (sdeSym != null)? sdeSym.asString() : null;
 693            }
 694            if (extension == null) {
 695               sde = NO_SDE_INFO_MARK;
 696            } else {
 697               sde = new SDE(extension);
 698            }
 699            sdeRef = new SoftReference(sde);
 700         }
 701         return sde;
 702     }
 703 
 704     public List availableStrata() {
 705         SDE sde = sourceDebugExtensionInfo();
 706         if (sde.isValid()) {
 707             return sde.availableStrata();
 708         } else {
 709             List strata = new ArrayList();
 710             strata.add(SDE.BASE_STRATUM_NAME);
 711             return strata;
 712         }
 713     }
 714 
 715     /**
 716      * Always returns non-null stratumID
 717      */
 718     public String defaultStratum() {
 719         SDE sdei = sourceDebugExtensionInfo();
 720         if (sdei.isValid()) {
 721             return sdei.defaultStratumId;
 722         } else {
 723             return SDE.BASE_STRATUM_NAME;
 724         }
 725     }
 726 
 727     public final int modifiers() {
 728         if (modifiers == -1) {
 729             modifiers = getModifiers();
 730         }
 731         return modifiers;
 732     }
 733 
 734     // new method since 1.6.
 735     // Real body will be supplied later.
 736     public List instances(long maxInstances) {
 737         if (!vm.canGetInstanceInfo()) {
 738             throw new UnsupportedOperationException(
 739                       "target does not support getting instances");
 740         }
 741 
 742         if (maxInstances < 0) {
 743             throw new IllegalArgumentException("maxInstances is less than zero: "
 744                                               + maxInstances);
 745         }
 746 
 747         final List objects = new ArrayList(0);
 748         if (isAbstract() || (this instanceof InterfaceType)) {
 749             return objects;
 750         }
 751 
 752         final Klass givenKls = this.ref();
 753         final long max = maxInstances;
 754         vm.saObjectHeap().iterate(new DefaultHeapVisitor() {
 755                 private long instCount=0;
 756                 public boolean doObj(Oop oop) {
 757                     if (givenKls.equals(oop.getKlass())) {
 758                         objects.add(vm.objectMirror(oop));
 759                                                 instCount++;
 760                     }
 761                     if (max > 0 && instCount >= max) {
 762                         return true;
 763                                         }
 764                                         return false;
 765                 }
 766             });
 767         return objects;
 768     }
 769 
 770     int getModifiers() {
 771         return (int) saKlass.getClassModifiers();
 772     }
 773 
 774     public List allLineLocations()
 775                             throws AbsentInformationException {
 776         return allLineLocations(vm.getDefaultStratum(), null);
 777     }
 778 
 779     public List allLineLocations(String stratumID, String sourceName)
 780                             throws AbsentInformationException {
 781         checkPrepared();
 782         boolean someAbsent = false; // A method that should have info, didn't
 783         SDE.Stratum stratum = stratum(stratumID);
 784         List list = new ArrayList();  // location list
 785 
 786         for (Iterator iter = methods().iterator(); iter.hasNext(); ) {
 787             MethodImpl method = (MethodImpl)iter.next();
 788             try {
 789                 list.addAll(
 790                    method.allLineLocations(stratum.id(), sourceName));
 791             } catch(AbsentInformationException exc) {
 792                 someAbsent = true;
 793             }
 794         }
 795 
 796         // If we retrieved no line info, and at least one of the methods
 797         // should have had some (as determined by an
 798         // AbsentInformationException being thrown) then we rethrow
 799         // the AbsentInformationException.
 800         if (someAbsent && list.size() == 0) {
 801             throw new AbsentInformationException();
 802         }
 803         return list;
 804     }
 805 
 806     public List locationsOfLine(int lineNumber)
 807                            throws AbsentInformationException {
 808         return locationsOfLine(vm.getDefaultStratum(),
 809                                null,
 810                                lineNumber);
 811     }
 812 
 813     public List locationsOfLine(String stratumID,
 814                                 String sourceName,
 815                                 int lineNumber)
 816                            throws AbsentInformationException {
 817         checkPrepared();
 818         // A method that should have info, didn't
 819         boolean someAbsent = false;
 820         // A method that should have info, did
 821         boolean somePresent = false;
 822         List methods = methods();
 823         SDE.Stratum stratum = stratum(stratumID);
 824 
 825         List list = new ArrayList();
 826 
 827         Iterator iter = methods.iterator();
 828         while(iter.hasNext()) {
 829             MethodImpl method = (MethodImpl)iter.next();
 830             // eliminate native and abstract to eliminate
 831             // false positives
 832             if (!method.isAbstract() &&
 833                 !method.isNative()) {
 834                 try {
 835                     list.addAll(
 836                        method.locationsOfLine(stratum.id(),
 837                                               sourceName,
 838                                               lineNumber));
 839                     somePresent = true;
 840                 } catch(AbsentInformationException exc) {
 841                     someAbsent = true;
 842                 }
 843             }
 844         }
 845         if (someAbsent && !somePresent) {
 846             throw new AbsentInformationException();
 847         }
 848         return list;
 849     }
 850 
 851     Klass ref() {
 852         return saKlass;
 853     }
 854 
 855 
 856     /*
 857      * Return true if an instance of this type
 858      * can be assigned to a variable of the given type
 859      */
 860     abstract boolean isAssignableTo(ReferenceType type);
 861 
 862     boolean isAssignableFrom(ReferenceType type) {
 863         return ((ReferenceTypeImpl)type).isAssignableTo(this);
 864     }
 865 
 866     boolean isAssignableFrom(ObjectReference object) {
 867         return object == null ||
 868             isAssignableFrom(object.referenceType());
 869     }
 870 
 871     int indexOf(Method method) {
 872         // Make sure they're all here - the obsolete method
 873         // won't be found and so will have index -1
 874         return methods().indexOf(method);
 875     }
 876 
 877     int indexOf(Field field) {
 878         // Make sure they're all here
 879         return fields().indexOf(field);
 880     }
 881 
 882     private static boolean isPrimitiveArray(String signature) {
 883         int i = signature.lastIndexOf('[');
 884         /*
 885          * TO DO: Centralize JNI signature knowledge.
 886          *
 887          * Ref:
 888          *  jdk1.4/doc/guide/jpda/jdi/com/sun/jdi/doc-files/signature.html
 889          */
 890         boolean isPA;
 891         if (i < 0) {
 892             isPA = false;
 893         } else {
 894             char c = signature.charAt(i + 1);
 895             isPA = (c != 'L');
 896         }
 897         return isPA;
 898     }
 899 
 900     Type findType(String signature) throws ClassNotLoadedException {
 901         Type type;
 902         if (signature.length() == 1) {
 903             /* OTI FIX: Must be a primitive type or the void type */
 904             char sig = signature.charAt(0);
 905             if (sig == 'V') {
 906                 type = vm.theVoidType();
 907             } else {
 908                 type = vm.primitiveTypeMirror(sig);
 909             }
 910         } else {
 911             // Must be a reference type.
 912             ClassLoaderReferenceImpl loader =
 913                        (ClassLoaderReferenceImpl)classLoader();
 914             if ((loader == null) ||
 915                 (isPrimitiveArray(signature)) //Work around 4450091
 916                 ) {
 917                 // Caller wants type of boot class field
 918                 type = vm.findBootType(signature);
 919             } else {
 920                 // Caller wants type of non-boot class field
 921                 type = loader.findType(signature);
 922             }
 923         }
 924         return type;
 925     }
 926 
 927     String loaderString() {
 928         if (classLoader() != null) {
 929             return "loaded by " + classLoader().toString();
 930         } else {
 931             return "loaded by bootstrap loader";
 932         }
 933     }
 934 
 935     long uniqueID() {
 936         return vm.getAddressValue(ref());
 937     }
 938 
 939     // new method since 1.6
 940     public int majorVersion() {
 941         if (!vm.canGetClassFileVersion()) {
 942             throw new UnsupportedOperationException("Cannot get class file version");
 943         }
 944         return (int)((InstanceKlass)saKlass).majorVersion();
 945     }
 946 
 947     // new method since 1.6
 948     public int minorVersion() {
 949         if (!vm.canGetClassFileVersion()) {
 950             throw new UnsupportedOperationException("Cannot get class file version");
 951         }
 952         return (int)((InstanceKlass)saKlass).minorVersion();
 953     }
 954 
 955     // new method since 1.6
 956     public int constantPoolCount() {
 957         if (!vm.canGetConstantPool()) {
 958             throw new UnsupportedOperationException("Cannot get constant pool");
 959         }
 960         if (saKlass instanceof ArrayKlass) {
 961             return 0;
 962         } else {
 963             return (int)((InstanceKlass)saKlass).getConstants().getLength();
 964         }
 965     }
 966 
 967     // new method since 1.6
 968     public byte[] constantPool() {
 969         if (!vm.canGetConstantPool()) {
 970             throw new UnsupportedOperationException("Cannot get constant pool");
 971         }
 972         if (this instanceof ArrayType || this instanceof PrimitiveType) {
 973                         byte bytes[] = new byte[0];
 974             return bytes;
 975         } else {
 976             ByteArrayOutputStream bs = new ByteArrayOutputStream();
 977             try {
 978                 ((InstanceKlass)saKlass).getConstants().writeBytes(bs);
 979             } catch (IOException ex) {
 980                                 ex.printStackTrace();
 981                                 byte bytes[] = new byte[0];
 982                                 return bytes;
 983             }
 984             return bs.toByteArray();
 985         }
 986     }
 987 }