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