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