1 /*
   2  * Copyright (c) 2000, 2023, 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.oops;
  26 
  27 import java.io.*;
  28 import java.util.*;
  29 import sun.jvm.hotspot.classfile.ClassLoaderData;
  30 import sun.jvm.hotspot.code.CompressedReadStream;
  31 import sun.jvm.hotspot.debugger.*;
  32 import sun.jvm.hotspot.memory.*;
  33 import sun.jvm.hotspot.runtime.*;
  34 import sun.jvm.hotspot.types.*;
  35 import sun.jvm.hotspot.utilities.*;
  36 import sun.jvm.hotspot.utilities.Observable;
  37 import sun.jvm.hotspot.utilities.Observer;
  38 
  39 // An InstanceKlass is the VM level representation of a Java class.
  40 
  41 public class InstanceKlass extends Klass {
  42   static {
  43     VM.registerVMInitializedObserver(new Observer() {
  44         public void update(Observable o, Object data) {
  45           initialize(VM.getVM().getTypeDataBase());
  46         }
  47       });
  48   }
  49 
  50   // internal field flags constants
  51   static int FIELD_FLAG_IS_INITIALIZED;
  52   static int FIELD_FLAG_IS_INJECTED;
  53   static int FIELD_FLAG_IS_GENERIC;
  54   static int FIELD_FLAG_IS_STABLE;
  55   static int FIELD_FLAG_IS_CONTENDED;
  56 
  57   // ClassState constants
  58   private static int CLASS_STATE_ALLOCATED;
  59   private static int CLASS_STATE_LOADED;
  60   private static int CLASS_STATE_LINKED;
  61   private static int CLASS_STATE_BEING_INITIALIZED;
  62   private static int CLASS_STATE_FULLY_INITIALIZED;
  63   private static int CLASS_STATE_INITIALIZATION_ERROR;
  64 
  65 
  66   private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
  67     Type type            = db.lookupType("InstanceKlass");
  68     annotations          = type.getAddressField("_annotations");
  69     arrayKlasses         = new MetadataField(type.getAddressField("_array_klasses"), 0);
  70     methods              = type.getAddressField("_methods");
  71     defaultMethods       = type.getAddressField("_default_methods");
  72     methodOrdering       = type.getAddressField("_method_ordering");
  73     localInterfaces      = type.getAddressField("_local_interfaces");
  74     transitiveInterfaces = type.getAddressField("_transitive_interfaces");
  75     fieldinfoStream      = type.getAddressField("_fieldinfo_stream");
  76     constants            = new MetadataField(type.getAddressField("_constants"), 0);
  77     sourceDebugExtension = type.getAddressField("_source_debug_extension");
  78     innerClasses         = type.getAddressField("_inner_classes");
  79     nestMembers          = type.getAddressField("_nest_members");
  80     nonstaticFieldSize   = new CIntField(type.getCIntegerField("_nonstatic_field_size"), 0);
  81     staticFieldSize      = new CIntField(type.getCIntegerField("_static_field_size"), 0);
  82     staticOopFieldCount  = new CIntField(type.getCIntegerField("_static_oop_field_count"), 0);
  83     nonstaticOopMapSize  = new CIntField(type.getCIntegerField("_nonstatic_oop_map_size"), 0);
  84     initState            = new CIntField(type.getCIntegerField("_init_state"), 0);
  85     itableLen            = new CIntField(type.getCIntegerField("_itable_len"), 0);
  86     nestHostIndex        = new CIntField(type.getCIntegerField("_nest_host_index"), 0);
  87     if (VM.getVM().isJvmtiSupported()) {
  88       breakpoints        = type.getAddressField("_breakpoints");
  89     }
  90     headerSize           = type.getSize();
  91 
  92     // read internal field flags constants
  93     FIELD_FLAG_IS_INITIALIZED      = db.lookupIntConstant("FieldInfo::FieldFlags::_ff_initialized");
  94     FIELD_FLAG_IS_INJECTED         = db.lookupIntConstant("FieldInfo::FieldFlags::_ff_injected");
  95     FIELD_FLAG_IS_GENERIC          = db.lookupIntConstant("FieldInfo::FieldFlags::_ff_generic");
  96     FIELD_FLAG_IS_STABLE           = db.lookupIntConstant("FieldInfo::FieldFlags::_ff_stable");
  97     FIELD_FLAG_IS_CONTENDED        = db.lookupIntConstant("FieldInfo::FieldFlags::_ff_contended");
  98 
  99 
 100     // read ClassState constants
 101     CLASS_STATE_ALLOCATED = db.lookupIntConstant("InstanceKlass::allocated").intValue();
 102     CLASS_STATE_LOADED = db.lookupIntConstant("InstanceKlass::loaded").intValue();
 103     CLASS_STATE_LINKED = db.lookupIntConstant("InstanceKlass::linked").intValue();
 104     CLASS_STATE_BEING_INITIALIZED = db.lookupIntConstant("InstanceKlass::being_initialized").intValue();
 105     CLASS_STATE_FULLY_INITIALIZED = db.lookupIntConstant("InstanceKlass::fully_initialized").intValue();
 106     CLASS_STATE_INITIALIZATION_ERROR = db.lookupIntConstant("InstanceKlass::initialization_error").intValue();
 107     // We need a new fieldsCache each time we attach.
 108     fieldsCache = new WeakHashMap<Address, Field[]>();
 109   }
 110 
 111   public InstanceKlass(Address addr) {
 112     super(addr);
 113 
 114     // If the class hasn't yet reached the "loaded" init state, then don't go any further
 115     // or we'll run into problems trying to look at fields that are not yet setup.
 116     // Attempted lookups of this InstanceKlass via ClassLoaderDataGraph, ClassLoaderData,
 117     // and Dictionary will all refuse to return it. The main purpose of allowing this
 118     // InstanceKlass to initialize is so ClassLoaderData.getKlasses() will succeed, allowing
 119     // ClassLoaderData.classesDo() to iterate over all Klasses (skipping those that are
 120     // not yet fully loaded).
 121     if (!isLoaded()) {
 122         return;
 123     }
 124 
 125     if (getJavaFieldsCount() != getAllFieldsCount()) {
 126       // Exercise the injected field logic
 127       for (int i = getJavaFieldsCount(); i < getAllFieldsCount(); i++) {
 128         getFieldName(i);
 129         getFieldSignature(i);
 130       }
 131     }
 132   }
 133 
 134   private static AddressField  annotations;
 135   private static MetadataField arrayKlasses;
 136   private static AddressField  methods;
 137   private static AddressField  defaultMethods;
 138   private static AddressField  methodOrdering;
 139   private static AddressField  localInterfaces;
 140   private static AddressField  transitiveInterfaces;
 141   private static AddressField  fieldinfoStream;
 142   private static MetadataField constants;
 143   private static AddressField  sourceDebugExtension;
 144   private static AddressField  innerClasses;
 145   private static AddressField  nestMembers;
 146   private static CIntField nonstaticFieldSize;
 147   private static CIntField staticFieldSize;
 148   private static CIntField staticOopFieldCount;
 149   private static CIntField nonstaticOopMapSize;
 150   private static CIntField initState;
 151   private static CIntField itableLen;
 152   private static CIntField nestHostIndex;
 153   private static AddressField breakpoints;
 154 
 155   // type safe enum for ClassState from instanceKlass.hpp
 156   public static class ClassState {
 157      public static final ClassState ALLOCATED    = new ClassState("allocated");
 158      public static final ClassState LOADED       = new ClassState("loaded");
 159      public static final ClassState LINKED       = new ClassState("linked");
 160      public static final ClassState BEING_INITIALIZED      = new ClassState("beingInitialized");
 161      public static final ClassState FULLY_INITIALIZED    = new ClassState("fullyInitialized");
 162      public static final ClassState INITIALIZATION_ERROR = new ClassState("initializationError");
 163 
 164      private ClassState(String value) {
 165         this.value = value;
 166      }
 167 
 168      public String toString() {
 169         return value;
 170      }
 171 
 172      private String value;
 173   }
 174 
 175   public int  getInitStateAsInt() { return (int) initState.getValue(this); }
 176   public ClassState getInitState() {
 177      int state = getInitStateAsInt();
 178      if (state == CLASS_STATE_ALLOCATED) {
 179         return ClassState.ALLOCATED;
 180      } else if (state == CLASS_STATE_LOADED) {
 181         return ClassState.LOADED;
 182      } else if (state == CLASS_STATE_LINKED) {
 183         return ClassState.LINKED;
 184      } else if (state == CLASS_STATE_BEING_INITIALIZED) {
 185         return ClassState.BEING_INITIALIZED;
 186      } else if (state == CLASS_STATE_FULLY_INITIALIZED) {
 187         return ClassState.FULLY_INITIALIZED;
 188      } else if (state == CLASS_STATE_INITIALIZATION_ERROR) {
 189         return ClassState.INITIALIZATION_ERROR;
 190      } else {
 191         throw new RuntimeException("should not reach here");
 192      }
 193   }
 194 
 195   // initialization state quaries
 196   public boolean isLoaded() {
 197      return getInitStateAsInt() >= CLASS_STATE_LOADED;
 198   }
 199 
 200   public boolean isLinked() {
 201      return getInitStateAsInt() >= CLASS_STATE_LINKED;
 202   }
 203 
 204   public boolean isInitialized() {
 205      return getInitStateAsInt() == CLASS_STATE_FULLY_INITIALIZED;
 206   }
 207 
 208   public boolean isNotInitialized() {
 209      return getInitStateAsInt() < CLASS_STATE_BEING_INITIALIZED;
 210   }
 211 
 212   public boolean isBeingInitialized() {
 213      return getInitStateAsInt() == CLASS_STATE_BEING_INITIALIZED;
 214   }
 215 
 216   public boolean isInErrorState() {
 217      return getInitStateAsInt() == CLASS_STATE_INITIALIZATION_ERROR;
 218   }
 219 
 220   public int getClassStatus() {
 221      int result = 0;
 222      if (isLinked()) {
 223         result |= JVMDIClassStatus.VERIFIED | JVMDIClassStatus.PREPARED;
 224      }
 225 
 226      if (isInitialized()) {
 227         if (Assert.ASSERTS_ENABLED) {
 228            Assert.that(isLinked(), "Class status is not consistent");
 229         }
 230         result |= JVMDIClassStatus.INITIALIZED;
 231      }
 232 
 233      if (isInErrorState()) {
 234         result |= JVMDIClassStatus.ERROR;
 235      }
 236      return result;
 237   }
 238 
 239   // Byteside of the header
 240   private static long headerSize;
 241 
 242   public long getObjectSize(Oop object) {
 243     return getSizeHelper() * VM.getVM().getAddressSize();
 244   }
 245 
 246   public long getSize() { // in number of bytes
 247     long wordLength = VM.getVM().getBytesPerWord();
 248     long size = getHeaderSize() +
 249                 (getVtableLen() +
 250                  getItableLen() +
 251                  getNonstaticOopMapSize()) * wordLength;
 252     if (isInterface()) {
 253       size += wordLength;
 254     }
 255     return alignSize(size);
 256   }
 257 
 258   public static long getHeaderSize() { return headerSize; }
 259 
 260   // Each InstanceKlass mirror instance will cache the Field[] array after it is decoded,
 261   // but since there can be multiple InstanceKlass mirror instances per hotspot InstanceKlass,
 262   // we also have a global cache that uses the Address of the hotspot InstanceKlass as the key.
 263   private Field[] fields;
 264   private static Map<Address, Field[]> fieldsCache;
 265 
 266   Field getField(int index) {
 267     synchronized (this) {
 268       fields = fieldsCache.get(this.getAddress());
 269       if (fields == null) {
 270         fields = Field.getFields(this);
 271         fieldsCache.put(this.getAddress(), fields);
 272       } else {
 273       }
 274     }
 275     return fields[index];
 276   }
 277 
 278   public short getFieldAccessFlags(int index) {
 279     return (short)getField(index).getAccessFlags();
 280   }
 281 
 282   public int getFieldNameIndex(int index) {
 283     if (index >= getJavaFieldsCount()) throw new IndexOutOfBoundsException("not a Java field;");
 284     return getField(index).getNameIndex();
 285   }
 286 
 287   public Symbol getFieldName(int index) {
 288     // Cannot use getFieldNameIndex() because this method is also used for injected fields
 289     return getField(index).getName();
 290   }
 291 
 292   public Symbol getSymbolFromIndex(int cpIndex, boolean injected) {
 293     if (injected) {
 294       return vmSymbols.symbolAt(cpIndex);
 295     } else {
 296       return getConstants().getSymbolAt(cpIndex);
 297     }
 298   }
 299 
 300   public int getFieldSignatureIndex(int index) {
 301     if (index >= getJavaFieldsCount()) throw new IndexOutOfBoundsException("not a Java field;");
 302     return getField(index).getSignatureIndex();
 303   }
 304 
 305   public Symbol getFieldSignature(int index) {
 306     // Cannot use getFieldSignatureIndex() because this method is also use for injected fields
 307     return getField(index).getSignature();
 308   }
 309 
 310   public int getFieldGenericSignatureIndex(int index) {
 311     return getField(index).getGenericSignatureIndex();
 312   }
 313 
 314   public Symbol getFieldGenericSignature(int index) {
 315     return getField(index).getGenericSignature();
 316   }
 317 
 318   public int getFieldInitialValueIndex(int index) {
 319     if (index >= getJavaFieldsCount()) throw new IndexOutOfBoundsException("not a Java field;");
 320     return getField(index).getInitialValueIndex();
 321   }
 322 
 323   public int getFieldOffset(int index) {
 324     return (int)getField(index).getOffset();
 325   }
 326 
 327   // Accessors for declared fields
 328   public Klass     getArrayKlasses()        { return (Klass)        arrayKlasses.getValue(this); }
 329   public MethodArray  getMethods()              { return new MethodArray(methods.getValue(getAddress())); }
 330 
 331   public MethodArray  getDefaultMethods() {
 332     if (defaultMethods != null) {
 333       Address addr = defaultMethods.getValue(getAddress());
 334       if ((addr != null) && (addr.getAddressAt(0) != null)) {
 335         return new MethodArray(addr);
 336       } else {
 337         return null;
 338       }
 339     } else {
 340       return null;
 341     }
 342   }
 343 
 344   private int javaFieldsCount = -1;
 345   private int allFieldsCount = -1;
 346 
 347   private void initFieldCounts() {
 348     CompressedReadStream crs = new CompressedReadStream(getFieldInfoStream().getDataStart());
 349     javaFieldsCount = crs.readInt(); // read num_java_fields
 350     allFieldsCount = javaFieldsCount + crs.readInt(); // read num_injected_fields;
 351   }
 352 
 353   public int getJavaFieldsCount() {
 354     if (javaFieldsCount == -1) {
 355       initFieldCounts();
 356     }
 357     return javaFieldsCount;
 358   }
 359 
 360   public int getAllFieldsCount() {
 361     if (allFieldsCount == -1) {
 362       initFieldCounts();
 363     }
 364     return allFieldsCount;
 365   }
 366 
 367   public KlassArray   getLocalInterfaces()      { return new KlassArray(localInterfaces.getValue(getAddress())); }
 368   public KlassArray   getTransitiveInterfaces() { return new KlassArray(transitiveInterfaces.getValue(getAddress())); }
 369   public ConstantPool getConstants()        { return (ConstantPool) constants.getValue(this); }
 370   public Symbol    getSourceFileName()      { return                getConstants().getSourceFileName(); }
 371   public String    getSourceDebugExtension(){ return                CStringUtilities.getString(sourceDebugExtension.getValue(getAddress())); }
 372   public long      getNonstaticFieldSize()  { return                nonstaticFieldSize.getValue(this); }
 373   public long      getStaticOopFieldCount() { return                staticOopFieldCount.getValue(this); }
 374   public long      getNonstaticOopMapSize() { return                nonstaticOopMapSize.getValue(this); }
 375   public long      getItableLen()           { return                itableLen.getValue(this); }
 376   public short     getNestHostIndex()       { return                (short) nestHostIndex.getValue(this); }
 377   public long      majorVersion()           { return                getConstants().majorVersion(); }
 378   public long      minorVersion()           { return                getConstants().minorVersion(); }
 379   public Symbol    getGenericSignature()    { return                getConstants().getGenericSignature(); }
 380   // "size helper" == instance size in words
 381   public long getSizeHelper() {
 382     int lh = getLayoutHelper();
 383     if (Assert.ASSERTS_ENABLED) {
 384       Assert.that(lh > 0, "layout helper initialized for instance class");
 385     }
 386     return lh / VM.getVM().getAddressSize();
 387   }
 388   public Annotations  getAnnotations() {
 389     Address addr = annotations.getValue(getAddress());
 390     return VMObjectFactory.newObject(Annotations.class, addr);
 391   }
 392 
 393   // same as enum InnerClassAttributeOffset in VM code.
 394   private static class InnerClassAttributeOffset {
 395     // from JVM spec. "InnerClasses" attribute
 396     public static int innerClassInnerClassInfoOffset;
 397     public static int innerClassOuterClassInfoOffset;
 398     public static int innerClassInnerNameOffset;
 399     public static int innerClassAccessFlagsOffset;
 400     public static int innerClassNextOffset;
 401     static {
 402       VM.registerVMInitializedObserver(new Observer() {
 403           public void update(Observable o, Object data) {
 404               initialize(VM.getVM().getTypeDataBase());
 405           }
 406       });
 407     }
 408 
 409     private static synchronized void initialize(TypeDataBase db) {
 410       innerClassInnerClassInfoOffset = db.lookupIntConstant(
 411           "InstanceKlass::inner_class_inner_class_info_offset").intValue();
 412       innerClassOuterClassInfoOffset = db.lookupIntConstant(
 413           "InstanceKlass::inner_class_outer_class_info_offset").intValue();
 414       innerClassInnerNameOffset = db.lookupIntConstant(
 415           "InstanceKlass::inner_class_inner_name_offset").intValue();
 416       innerClassAccessFlagsOffset = db.lookupIntConstant(
 417           "InstanceKlass::inner_class_access_flags_offset").intValue();
 418       innerClassNextOffset = db.lookupIntConstant(
 419           "InstanceKlass::inner_class_next_offset").intValue();
 420     }
 421   }
 422 
 423   private static class EnclosingMethodAttributeOffset {
 424     public static int enclosingMethodAttributeSize;
 425     static {
 426       VM.registerVMInitializedObserver(new Observer() {
 427           public void update(Observable o, Object data) {
 428               initialize(VM.getVM().getTypeDataBase());
 429           }
 430       });
 431     }
 432     private static synchronized void initialize(TypeDataBase db) {
 433       enclosingMethodAttributeSize = db.lookupIntConstant("InstanceKlass::enclosing_method_attribute_size").intValue();
 434     }
 435   }
 436 
 437   // refer to compute_modifier_flags in VM code.
 438   public long computeModifierFlags() {
 439     long access = getAccessFlags();
 440     // But check if it happens to be member class.
 441     U2Array innerClassList = getInnerClasses();
 442     int length = (innerClassList == null)? 0 : innerClassList.length();
 443     if (length > 0) {
 444        if (Assert.ASSERTS_ENABLED) {
 445           Assert.that(length % InnerClassAttributeOffset.innerClassNextOffset == 0 ||
 446                       length % InnerClassAttributeOffset.innerClassNextOffset == EnclosingMethodAttributeOffset.enclosingMethodAttributeSize,
 447                       "just checking");
 448        }
 449        for (int i = 0; i < length; i += InnerClassAttributeOffset.innerClassNextOffset) {
 450           if (i == length - EnclosingMethodAttributeOffset.enclosingMethodAttributeSize) {
 451               break;
 452           }
 453           int ioff = innerClassList.at(i +
 454                          InnerClassAttributeOffset.innerClassInnerClassInfoOffset);
 455           // 'ioff' can be zero.
 456           // refer to JVM spec. section 4.7.5.
 457           if (ioff != 0) {
 458              // only look at classes that are already loaded
 459              // since we are looking for the flags for our self.
 460              Symbol name = getConstants().getKlassNameAt(ioff);
 461 
 462              if (name.equals(getName())) {
 463                 // This is really a member class
 464                 access = innerClassList.at(i +
 465                         InnerClassAttributeOffset.innerClassAccessFlagsOffset);
 466                 break;
 467              }
 468           }
 469        } // for inner classes
 470     }
 471 
 472     // Remember to strip ACC_SUPER bit
 473     return (access & (~JVM_ACC_SUPER)) & JVM_ACC_WRITTEN_FLAGS;
 474   }
 475 
 476 
 477   // whether given Symbol is name of an inner/nested Klass of this Klass?
 478   // anonymous and local classes are excluded.
 479   public boolean isInnerClassName(Symbol sym) {
 480     return isInInnerClasses(sym, false);
 481   }
 482 
 483   // whether given Symbol is name of an inner/nested Klass of this Klass?
 484   // anonymous classes excluded, but local classes are included.
 485   public boolean isInnerOrLocalClassName(Symbol sym) {
 486     return isInInnerClasses(sym, true);
 487   }
 488 
 489   private boolean isInInnerClasses(Symbol sym, boolean includeLocals) {
 490     U2Array innerClassList = getInnerClasses();
 491     int length = ( innerClassList == null)? 0 : innerClassList.length();
 492     if (length > 0) {
 493        if (Assert.ASSERTS_ENABLED) {
 494          Assert.that(length % InnerClassAttributeOffset.innerClassNextOffset == 0 ||
 495                      length % InnerClassAttributeOffset.innerClassNextOffset == EnclosingMethodAttributeOffset.enclosingMethodAttributeSize,
 496                      "just checking");
 497        }
 498        for (int i = 0; i < length; i += InnerClassAttributeOffset.innerClassNextOffset) {
 499          if (i == length - EnclosingMethodAttributeOffset.enclosingMethodAttributeSize) {
 500              break;
 501          }
 502          int ioff = innerClassList.at(i +
 503                         InnerClassAttributeOffset.innerClassInnerClassInfoOffset);
 504          // 'ioff' can be zero.
 505          // refer to JVM spec. section 4.7.5.
 506          if (ioff != 0) {
 507             Symbol innerName = getConstants().getKlassNameAt(ioff);
 508             Symbol myname = getName();
 509             int ooff = innerClassList.at(i +
 510                         InnerClassAttributeOffset.innerClassOuterClassInfoOffset);
 511             // for anonymous classes inner_name_index of InnerClasses
 512             // attribute is zero.
 513             int innerNameIndex = innerClassList.at(i +
 514                         InnerClassAttributeOffset.innerClassInnerNameOffset);
 515             // if this is not a member (anonymous, local etc.), 'ooff' will be zero
 516             // refer to JVM spec. section 4.7.5.
 517             if (ooff == 0) {
 518                if (includeLocals) {
 519                   // does it looks like my local class?
 520                   if (innerName.equals(sym) &&
 521                      innerName.asString().startsWith(myname.asString())) {
 522                      // exclude anonymous classes.
 523                      return (innerNameIndex != 0);
 524                   }
 525                }
 526             } else {
 527                Symbol outerName = getConstants().getKlassNameAt(ooff);
 528 
 529                // include only if current class is outer class.
 530                if (outerName.equals(myname) && innerName.equals(sym)) {
 531                   return true;
 532                }
 533            }
 534          }
 535        } // for inner classes
 536        return false;
 537     } else {
 538        return false;
 539     }
 540   }
 541 
 542   public boolean implementsInterface(Klass k) {
 543     if (Assert.ASSERTS_ENABLED) {
 544       Assert.that(k.isInterface(), "should not reach here");
 545     }
 546     KlassArray interfaces =  getTransitiveInterfaces();
 547     final int len = interfaces.length();
 548     for (int i = 0; i < len; i++) {
 549       if (interfaces.getAt(i).equals(k)) return true;
 550     }
 551     return false;
 552   }
 553 
 554   boolean computeSubtypeOf(Klass k) {
 555     if (k.isInterface()) {
 556       return implementsInterface(k);
 557     } else {
 558       return super.computeSubtypeOf(k);
 559     }
 560   }
 561 
 562   public void printValueOn(PrintStream tty) {
 563     tty.print("InstanceKlass for " + getName().asString());
 564   }
 565 
 566   public void iterateFields(MetadataVisitor visitor) {
 567     super.iterateFields(visitor);
 568     visitor.doMetadata(arrayKlasses, true);
 569     // visitor.doOop(methods, true);
 570     // visitor.doOop(localInterfaces, true);
 571     // visitor.doOop(transitiveInterfaces, true);
 572       visitor.doCInt(nonstaticFieldSize, true);
 573       visitor.doCInt(staticFieldSize, true);
 574       visitor.doCInt(staticOopFieldCount, true);
 575       visitor.doCInt(nonstaticOopMapSize, true);
 576       visitor.doCInt(initState, true);
 577       visitor.doCInt(itableLen, true);
 578     }
 579 
 580   /*
 581    *  Visit the static fields of this InstanceKlass with the obj of
 582    *  the visitor set to the oop holding the fields, which is
 583    *  currently the java mirror.
 584    */
 585   public void iterateStaticFields(OopVisitor visitor) {
 586     visitor.setObj(getJavaMirror());
 587     visitor.prologue();
 588     iterateStaticFieldsInternal(visitor);
 589     visitor.epilogue();
 590 
 591   }
 592 
 593   void iterateStaticFieldsInternal(OopVisitor visitor) {
 594     int length = getJavaFieldsCount();
 595     for (int index = 0; index < length; index++) {
 596       short accessFlags    = getFieldAccessFlags(index);
 597       FieldType   type   = new FieldType(getFieldSignature(index));
 598       AccessFlags access = new AccessFlags(accessFlags);
 599       if (access.isStatic()) {
 600         visitField(visitor, type, index);
 601       }
 602     }
 603   }
 604 
 605   public Klass getJavaSuper() {
 606     return getSuper();
 607   }
 608 
 609   public static class StaticField {
 610     public AccessFlags flags;
 611     public Field field;
 612 
 613     StaticField(Field field, AccessFlags flags) {
 614       this.field = field;
 615       this.flags = flags;
 616     }
 617   }
 618 
 619   public Field[] getStaticFields() {
 620     int length = getJavaFieldsCount();
 621     ArrayList<Field> result = new ArrayList<>();
 622     for (int index = 0; index < length; index++) {
 623       Field f = newField(index);
 624       if (f.isStatic()) {
 625         result.add(f);
 626       }
 627     }
 628     return result.toArray(new Field[result.size()]);
 629   }
 630 
 631   public void iterateNonStaticFields(OopVisitor visitor, Oop obj) {
 632     if (getSuper() != null) {
 633       ((InstanceKlass) getSuper()).iterateNonStaticFields(visitor, obj);
 634     }
 635     int length = getJavaFieldsCount();
 636     for (int index = 0; index < length; index++) {
 637       short accessFlags    = getFieldAccessFlags(index);
 638       FieldType   type   = new FieldType(getFieldSignature(index));
 639       AccessFlags access = new AccessFlags(accessFlags);
 640       if (!access.isStatic()) {
 641         visitField(visitor, type, index);
 642       }
 643     }
 644   }
 645 
 646   /** Field access by name. */
 647   public Field findLocalField(String name, String sig) {
 648     int length = getJavaFieldsCount();
 649     for (int i = 0; i < length; i++) {
 650       Symbol f_name = getFieldName(i);
 651       Symbol f_sig  = getFieldSignature(i);
 652       if (f_name.equals(name) && f_sig.equals(sig)) {
 653         return newField(i);
 654       }
 655     }
 656 
 657     return null;
 658   }
 659 
 660   /** Find field in direct superinterfaces. */
 661   public Field findInterfaceField(String name, String sig) {
 662     KlassArray interfaces = getLocalInterfaces();
 663     int n = interfaces.length();
 664     for (int i = 0; i < n; i++) {
 665       InstanceKlass intf1 = (InstanceKlass) interfaces.getAt(i);
 666       if (Assert.ASSERTS_ENABLED) {
 667         Assert.that(intf1.isInterface(), "just checking type");
 668      }
 669       // search for field in current interface
 670       Field f = intf1.findLocalField(name, sig);
 671       if (f != null) {
 672         if (Assert.ASSERTS_ENABLED) {
 673           Assert.that(f.getAccessFlagsObj().isStatic(), "interface field must be static");
 674         }
 675         return f;
 676       }
 677       // search for field in direct superinterfaces
 678       f = intf1.findInterfaceField(name, sig);
 679       if (f != null) return f;
 680     }
 681     // otherwise field lookup fails
 682     return null;
 683   }
 684 
 685   /** Find field according to JVM spec 5.4.3.2, returns the klass in
 686       which the field is defined. */
 687   public Field findField(String name, String sig) {
 688     // search order according to newest JVM spec (5.4.3.2, p.167).
 689     // 1) search for field in current klass
 690     Field f = findLocalField(name, sig);
 691     if (f != null) return f;
 692 
 693     // 2) search for field recursively in direct superinterfaces
 694     f = findInterfaceField(name, sig);
 695     if (f != null) return f;
 696 
 697     // 3) apply field lookup recursively if superclass exists
 698     InstanceKlass supr = (InstanceKlass) getSuper();
 699     if (supr != null) return supr.findField(name, sig);
 700 
 701     // 4) otherwise field lookup fails
 702     return null;
 703   }
 704 
 705   /** Find field according to JVM spec 5.4.3.2, returns the klass in
 706       which the field is defined (retained only for backward
 707       compatibility with jdbx) */
 708   public Field findFieldDbg(String name, String sig) {
 709     return findField(name, sig);
 710   }
 711 
 712   /** Get field by its index in the fields array. Only designed for
 713       use in a debugging system. */
 714   public Field getFieldByIndex(int fieldIndex) {
 715     return newField(fieldIndex);
 716   }
 717 
 718 
 719     /** Return a List of SA Fields for the fields declared in this class.
 720         Inherited fields are not included.
 721         Return an empty list if there are no fields declared in this class.
 722         Only designed for use in a debugging system. */
 723     public List<Field> getImmediateFields() {
 724         // A list of Fields for each field declared in this class/interface,
 725         // not including inherited fields.
 726         int length = getJavaFieldsCount();
 727         List<Field> immediateFields = new ArrayList<>(length);
 728         for (int index = 0; index < length; index++) {
 729             immediateFields.add(getFieldByIndex(index));
 730         }
 731 
 732         return immediateFields;
 733     }
 734 
 735     /** Return a List of SA Fields for all the java fields in this class,
 736         including all inherited fields.  This includes hidden
 737         fields.  Thus the returned list can contain fields with
 738         the same name.
 739         Return an empty list if there are no fields.
 740         Only designed for use in a debugging system. */
 741     public List<Field> getAllFields() {
 742         // Contains a Field for each field in this class, including immediate
 743         // fields and inherited fields.
 744         List<Field> allFields = getImmediateFields();
 745 
 746         // transitiveInterfaces contains all interfaces implemented
 747         // by this class and its superclass chain with no duplicates.
 748 
 749         KlassArray interfaces = getTransitiveInterfaces();
 750         int n = interfaces.length();
 751         for (int i = 0; i < n; i++) {
 752             InstanceKlass intf1 = (InstanceKlass) interfaces.getAt(i);
 753             if (Assert.ASSERTS_ENABLED) {
 754                 Assert.that(intf1.isInterface(), "just checking type");
 755             }
 756             allFields.addAll(intf1.getImmediateFields());
 757         }
 758 
 759         // Get all fields in the superclass, recursively.  But, don't
 760         // include fields in interfaces implemented by superclasses;
 761         // we already have all those.
 762         if (!isInterface()) {
 763             InstanceKlass supr;
 764             if  ( (supr = (InstanceKlass) getSuper()) != null) {
 765                 allFields.addAll(supr.getImmediateFields());
 766             }
 767         }
 768 
 769         return allFields;
 770     }
 771 
 772 
 773     /** Return a List of SA Methods declared directly in this class/interface.
 774         Return an empty list if there are none, or if this isn't a class/
 775         interface.
 776     */
 777     public List<Method> getImmediateMethods() {
 778       // Contains a Method for each method declared in this class/interface
 779       // not including inherited methods.
 780 
 781       MethodArray methods = getMethods();
 782       int length = methods.length();
 783       Method[] tmp = new Method[length];
 784 
 785       IntArray methodOrdering = getMethodOrdering();
 786       if (methodOrdering.length() != length) {
 787          // no ordering info present
 788          for (int index = 0; index < length; index++) {
 789             tmp[index] = methods.at(index);
 790          }
 791       } else {
 792          for (int index = 0; index < length; index++) {
 793             int originalIndex = methodOrdering.at(index);
 794             tmp[originalIndex] = methods.at(index);
 795          }
 796       }
 797 
 798       return Arrays.asList(tmp);
 799     }
 800 
 801     /** Return a List containing an SA InstanceKlass for each
 802         interface named in this class's 'implements' clause.
 803     */
 804     public List<Klass> getDirectImplementedInterfaces() {
 805         // Contains an InstanceKlass for each interface in this classes
 806         // 'implements' clause.
 807 
 808         KlassArray interfaces = getLocalInterfaces();
 809         int length = interfaces.length();
 810         List<Klass> directImplementedInterfaces = new ArrayList<>(length);
 811 
 812         for (int index = 0; index < length; index ++) {
 813             directImplementedInterfaces.add(interfaces.getAt(index));
 814         }
 815 
 816         return directImplementedInterfaces;
 817     }
 818 
 819   public Klass arrayKlassImpl(boolean orNull, int n) {
 820     // FIXME: in reflective system this would need to change to
 821     // actually allocate
 822     if (getArrayKlasses() == null) { return null; }
 823     ObjArrayKlass oak = (ObjArrayKlass) getArrayKlasses();
 824     if (orNull) {
 825       return oak.arrayKlassOrNull(n);
 826     }
 827     return oak.arrayKlass(n);
 828   }
 829 
 830   public Klass arrayKlassImpl(boolean orNull) {
 831     return arrayKlassImpl(orNull, 1);
 832   }
 833 
 834   public String signature() {
 835      return "L" + super.signature() + ";";
 836   }
 837 
 838   /** Find method in vtable. */
 839   public Method findMethod(String name, String sig) {
 840     return findMethod(getMethods(), name, sig);
 841   }
 842 
 843   /** Breakpoint support (see methods on Method* for details) */
 844   public BreakpointInfo getBreakpoints() {
 845     if (!VM.getVM().isJvmtiSupported()) {
 846       return null;
 847     }
 848     Address addr = getAddress().getAddressAt(breakpoints.getOffset());
 849     return VMObjectFactory.newObject(BreakpointInfo.class, addr);
 850   }
 851 
 852   public IntArray  getMethodOrdering() {
 853     Address addr = getAddress().getAddressAt(methodOrdering.getOffset());
 854     return VMObjectFactory.newObject(IntArray.class, addr);
 855   }
 856 
 857   public U1Array getFieldInfoStream() {
 858     Address addr = getAddress().getAddressAt(fieldinfoStream.getOffset());
 859     return VMObjectFactory.newObject(U1Array.class, addr);
 860   }
 861 
 862   public U2Array getInnerClasses() {
 863     Address addr = getAddress().getAddressAt(innerClasses.getOffset());
 864     return VMObjectFactory.newObject(U2Array.class, addr);
 865   }
 866 
 867   public U1Array getClassAnnotations() {
 868     Annotations annotations = getAnnotations();
 869     if (annotations != null) {
 870       return annotations.getClassAnnotations();
 871     } else {
 872       return null;
 873     }
 874   }
 875 
 876   public U1Array getClassTypeAnnotations() {
 877     Annotations annotations = getAnnotations();
 878     if (annotations != null) {
 879       return annotations.getClassTypeAnnotations();
 880     } else {
 881       return null;
 882     }
 883   }
 884 
 885   public U1Array getFieldAnnotations(int fieldIndex) {
 886     Annotations annotations = getAnnotations();
 887     if (annotations != null) {
 888       return annotations.getFieldAnnotations(fieldIndex);
 889     } else {
 890       return null;
 891     }
 892   }
 893 
 894   public U1Array getFieldTypeAnnotations(int fieldIndex) {
 895     Annotations annotations = getAnnotations();
 896     if (annotations != null) {
 897       return annotations.getFieldTypeAnnotations(fieldIndex);
 898     } else {
 899       return null;
 900     }
 901   }
 902 
 903   public U2Array getNestMembers() {
 904     Address addr = getAddress().getAddressAt(nestMembers.getOffset());
 905     return VMObjectFactory.newObject(U2Array.class, addr);
 906   }
 907 
 908   //----------------------------------------------------------------------
 909   // Internals only below this point
 910   //
 911 
 912   private void visitField(OopVisitor visitor, FieldType type, int index) {
 913     Field f = newField(index);
 914     if (type.isOop()) {
 915       visitor.doOop((OopField) f, false);
 916       return;
 917     }
 918     if (type.isByte()) {
 919       visitor.doByte((ByteField) f, false);
 920       return;
 921     }
 922     if (type.isChar()) {
 923       visitor.doChar((CharField) f, false);
 924       return;
 925     }
 926     if (type.isDouble()) {
 927       visitor.doDouble((DoubleField) f, false);
 928       return;
 929     }
 930     if (type.isFloat()) {
 931       visitor.doFloat((FloatField) f, false);
 932       return;
 933     }
 934     if (type.isInt()) {
 935       visitor.doInt((IntField) f, false);
 936       return;
 937     }
 938     if (type.isLong()) {
 939       visitor.doLong((LongField) f, false);
 940       return;
 941     }
 942     if (type.isShort()) {
 943       visitor.doShort((ShortField) f, false);
 944       return;
 945     }
 946     if (type.isBoolean()) {
 947       visitor.doBoolean((BooleanField) f, false);
 948       return;
 949     }
 950   }
 951 
 952   // Creates new field from index in fields TypeArray
 953   private Field newField(int index) {
 954     FieldType type = new FieldType(getFieldSignature(index));
 955     if (type.isOop()) {
 956      if (VM.getVM().isCompressedOopsEnabled()) {
 957         return new NarrowOopField(this, index);
 958      } else {
 959         return new OopField(this, index);
 960      }
 961     }
 962     if (type.isByte()) {
 963       return new ByteField(this, index);
 964     }
 965     if (type.isChar()) {
 966       return new CharField(this, index);
 967     }
 968     if (type.isDouble()) {
 969       return new DoubleField(this, index);
 970     }
 971     if (type.isFloat()) {
 972       return new FloatField(this, index);
 973     }
 974     if (type.isInt()) {
 975       return new IntField(this, index);
 976     }
 977     if (type.isLong()) {
 978       return new LongField(this, index);
 979     }
 980     if (type.isShort()) {
 981       return new ShortField(this, index);
 982     }
 983     if (type.isBoolean()) {
 984       return new BooleanField(this, index);
 985     }
 986     throw new RuntimeException("Illegal field type at index " + index);
 987   }
 988 
 989   private static Method findMethod(MethodArray methods, String name, String signature) {
 990     int index = linearSearch(methods, name, signature);
 991     if (index != -1) {
 992       return methods.at(index);
 993     } else {
 994       return null;
 995     }
 996   }
 997 
 998   private static int linearSearch(MethodArray methods, String name, String signature) {
 999     int len = methods.length();
1000     for (int index = 0; index < len; index++) {
1001       Method m = methods.at(index);
1002       if (m.getSignature().equals(signature) && m.getName().equals(name)) {
1003         return index;
1004       }
1005     }
1006     return -1;
1007   }
1008 
1009   public void dumpReplayData(PrintStream out) {
1010     ConstantPool cp = getConstants();
1011 
1012     // Try to record related loaded classes
1013     Klass sub = getSubklassKlass();
1014     while (sub != null) {
1015         if (sub instanceof InstanceKlass) {
1016             out.println("instanceKlass " + sub.getName().asString());
1017         }
1018         sub = sub.getNextSiblingKlass();
1019     }
1020 
1021     final int length = cp.getLength();
1022     out.print("ciInstanceKlass " + getName().asString() + " " + (isLinked() ? 1 : 0) + " " + (isInitialized() ? 1 : 0) + " " + length);
1023     for (int index = 1; index < length; index++) {
1024       out.print(" " + cp.getTags().at(index));
1025     }
1026     out.println();
1027     if (isInitialized()) {
1028       Field[] staticFields = getStaticFields();
1029       for (int i = 0; i < staticFields.length; i++) {
1030         Field f = staticFields[i];
1031         Oop mirror = getJavaMirror();
1032         if (f.isFinal() && !f.hasInitialValue()) {
1033           out.print("staticfield " + getName().asString() + " " +
1034                     OopUtilities.escapeString(f.getID().getName()) + " " +
1035                     f.getFieldType().getSignature().asString() + " ");
1036           if (f instanceof ByteField) {
1037             ByteField bf = (ByteField)f;
1038             out.println(bf.getValue(mirror));
1039           } else if (f instanceof BooleanField) {
1040             BooleanField bf = (BooleanField)f;
1041             out.println(bf.getValue(mirror) ? 1 : 0);
1042           } else if (f instanceof ShortField) {
1043             ShortField bf = (ShortField)f;
1044             out.println(bf.getValue(mirror));
1045           } else if (f instanceof CharField) {
1046             CharField bf = (CharField)f;
1047             out.println(bf.getValue(mirror) & 0xffff);
1048           } else if (f instanceof IntField) {
1049             IntField bf = (IntField)f;
1050             out.println(bf.getValue(mirror));
1051           } else  if (f instanceof LongField) {
1052             LongField bf = (LongField)f;
1053             out.println(bf.getValue(mirror));
1054           } else if (f instanceof FloatField) {
1055             FloatField bf = (FloatField)f;
1056             out.println(Float.floatToRawIntBits(bf.getValue(mirror)));
1057           } else if (f instanceof DoubleField) {
1058             DoubleField bf = (DoubleField)f;
1059             out.println(Double.doubleToRawLongBits(bf.getValue(mirror)));
1060           } else if (f instanceof OopField) {
1061             OopField bf = (OopField)f;
1062 
1063             Oop value = bf.getValue(mirror);
1064             if (value == null) {
1065               out.println("null");
1066             } else if (value.isInstance()) {
1067               Instance inst = (Instance)value;
1068               if (inst.isA(SystemDictionary.getStringKlass())) {
1069                 out.println("\"" + OopUtilities.stringOopToEscapedString(inst) + "\"");
1070               } else {
1071                 out.println(inst.getKlass().getName().asString());
1072               }
1073             } else if (value.isObjArray()) {
1074               ObjArray oa = (ObjArray)value;
1075               Klass ek = (ObjArrayKlass)oa.getKlass();
1076               out.println(oa.getLength() + " " + ek.getName().asString());
1077             } else if (value.isTypeArray()) {
1078               TypeArray ta = (TypeArray)value;
1079               out.println(ta.getLength());
1080             } else {
1081               out.println(value);
1082             }
1083           }
1084         }
1085       }
1086     }
1087   }
1088 }