1 /*
   2  * Copyright (c) 2000, 2024, 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   // whether given Symbol is name of an inner/nested Klass of this Klass?
 438   // anonymous and local classes are excluded.
 439   public boolean isInnerClassName(Symbol sym) {
 440     return isInInnerClasses(sym, false);
 441   }
 442 
 443   // whether given Symbol is name of an inner/nested Klass of this Klass?
 444   // anonymous classes excluded, but local classes are included.
 445   public boolean isInnerOrLocalClassName(Symbol sym) {
 446     return isInInnerClasses(sym, true);
 447   }
 448 
 449   private boolean isInInnerClasses(Symbol sym, boolean includeLocals) {
 450     U2Array innerClassList = getInnerClasses();
 451     int length = ( innerClassList == null)? 0 : innerClassList.length();
 452     if (length > 0) {
 453        if (Assert.ASSERTS_ENABLED) {
 454          Assert.that(length % InnerClassAttributeOffset.innerClassNextOffset == 0 ||
 455                      length % InnerClassAttributeOffset.innerClassNextOffset == EnclosingMethodAttributeOffset.enclosingMethodAttributeSize,
 456                      "just checking");
 457        }
 458        for (int i = 0; i < length; i += InnerClassAttributeOffset.innerClassNextOffset) {
 459          if (i == length - EnclosingMethodAttributeOffset.enclosingMethodAttributeSize) {
 460              break;
 461          }
 462          int ioff = innerClassList.at(i +
 463                         InnerClassAttributeOffset.innerClassInnerClassInfoOffset);
 464          // 'ioff' can be zero.
 465          // refer to JVM spec. section 4.7.5.
 466          if (ioff != 0) {
 467             Symbol innerName = getConstants().getKlassNameAt(ioff);
 468             Symbol myname = getName();
 469             int ooff = innerClassList.at(i +
 470                         InnerClassAttributeOffset.innerClassOuterClassInfoOffset);
 471             // for anonymous classes inner_name_index of InnerClasses
 472             // attribute is zero.
 473             int innerNameIndex = innerClassList.at(i +
 474                         InnerClassAttributeOffset.innerClassInnerNameOffset);
 475             // if this is not a member (anonymous, local etc.), 'ooff' will be zero
 476             // refer to JVM spec. section 4.7.5.
 477             if (ooff == 0) {
 478                if (includeLocals) {
 479                   // does it looks like my local class?
 480                   if (innerName.equals(sym) &&
 481                      innerName.asString().startsWith(myname.asString())) {
 482                      // exclude anonymous classes.
 483                      return (innerNameIndex != 0);
 484                   }
 485                }
 486             } else {
 487                Symbol outerName = getConstants().getKlassNameAt(ooff);
 488 
 489                // include only if current class is outer class.
 490                if (outerName.equals(myname) && innerName.equals(sym)) {
 491                   return true;
 492                }
 493            }
 494          }
 495        } // for inner classes
 496        return false;
 497     } else {
 498        return false;
 499     }
 500   }
 501 
 502   public boolean implementsInterface(Klass k) {
 503     if (Assert.ASSERTS_ENABLED) {
 504       Assert.that(k.isInterface(), "should not reach here");
 505     }
 506     KlassArray interfaces =  getTransitiveInterfaces();
 507     final int len = interfaces.length();
 508     for (int i = 0; i < len; i++) {
 509       if (interfaces.getAt(i).equals(k)) return true;
 510     }
 511     return false;
 512   }
 513 
 514   boolean computeSubtypeOf(Klass k) {
 515     if (k.isInterface()) {
 516       return implementsInterface(k);
 517     } else {
 518       return super.computeSubtypeOf(k);
 519     }
 520   }
 521 
 522   public void printValueOn(PrintStream tty) {
 523     tty.print("InstanceKlass for " + getName().asString());
 524   }
 525 
 526   public void iterateFields(MetadataVisitor visitor) {
 527     super.iterateFields(visitor);
 528     visitor.doMetadata(arrayKlasses, true);
 529     // visitor.doOop(methods, true);
 530     // visitor.doOop(localInterfaces, true);
 531     // visitor.doOop(transitiveInterfaces, true);
 532       visitor.doCInt(nonstaticFieldSize, true);
 533       visitor.doCInt(staticFieldSize, true);
 534       visitor.doCInt(staticOopFieldCount, true);
 535       visitor.doCInt(nonstaticOopMapSize, true);
 536       visitor.doCInt(initState, true);
 537       visitor.doCInt(itableLen, true);
 538     }
 539 
 540   /*
 541    *  Visit the static fields of this InstanceKlass with the obj of
 542    *  the visitor set to the oop holding the fields, which is
 543    *  currently the java mirror.
 544    */
 545   public void iterateStaticFields(OopVisitor visitor) {
 546     visitor.setObj(getJavaMirror());
 547     visitor.prologue();
 548     iterateStaticFieldsInternal(visitor);
 549     visitor.epilogue();
 550 
 551   }
 552 
 553   void iterateStaticFieldsInternal(OopVisitor visitor) {
 554     int length = getJavaFieldsCount();
 555     for (int index = 0; index < length; index++) {
 556       short accessFlags    = getFieldAccessFlags(index);
 557       FieldType   type   = new FieldType(getFieldSignature(index));
 558       AccessFlags access = new AccessFlags(accessFlags);
 559       if (access.isStatic()) {
 560         visitField(visitor, type, index);
 561       }
 562     }
 563   }
 564 
 565   public Klass getJavaSuper() {
 566     return getSuper();
 567   }
 568 
 569   public static class StaticField {
 570     public AccessFlags flags;
 571     public Field field;
 572 
 573     StaticField(Field field, AccessFlags flags) {
 574       this.field = field;
 575       this.flags = flags;
 576     }
 577   }
 578 
 579   public Field[] getStaticFields() {
 580     int length = getJavaFieldsCount();
 581     ArrayList<Field> result = new ArrayList<>();
 582     for (int index = 0; index < length; index++) {
 583       Field f = newField(index);
 584       if (f.isStatic()) {
 585         result.add(f);
 586       }
 587     }
 588     return result.toArray(new Field[result.size()]);
 589   }
 590 
 591   public void iterateNonStaticFields(OopVisitor visitor, Oop obj) {
 592     if (getSuper() != null) {
 593       ((InstanceKlass) getSuper()).iterateNonStaticFields(visitor, obj);
 594     }
 595     int length = getJavaFieldsCount();
 596     for (int index = 0; index < length; index++) {
 597       short accessFlags    = getFieldAccessFlags(index);
 598       FieldType   type   = new FieldType(getFieldSignature(index));
 599       AccessFlags access = new AccessFlags(accessFlags);
 600       if (!access.isStatic()) {
 601         visitField(visitor, type, index);
 602       }
 603     }
 604   }
 605 
 606   /** Field access by name. */
 607   public Field findLocalField(String name, String sig) {
 608     int length = getJavaFieldsCount();
 609     for (int i = 0; i < length; i++) {
 610       Symbol f_name = getFieldName(i);
 611       Symbol f_sig  = getFieldSignature(i);
 612       if (f_name.equals(name) && f_sig.equals(sig)) {
 613         return newField(i);
 614       }
 615     }
 616 
 617     return null;
 618   }
 619 
 620   /** Find field in direct superinterfaces. */
 621   public Field findInterfaceField(String name, String sig) {
 622     KlassArray interfaces = getLocalInterfaces();
 623     int n = interfaces.length();
 624     for (int i = 0; i < n; i++) {
 625       InstanceKlass intf1 = (InstanceKlass) interfaces.getAt(i);
 626       if (Assert.ASSERTS_ENABLED) {
 627         Assert.that(intf1.isInterface(), "just checking type");
 628      }
 629       // search for field in current interface
 630       Field f = intf1.findLocalField(name, sig);
 631       if (f != null) {
 632         if (Assert.ASSERTS_ENABLED) {
 633           Assert.that(f.getAccessFlagsObj().isStatic(), "interface field must be static");
 634         }
 635         return f;
 636       }
 637       // search for field in direct superinterfaces
 638       f = intf1.findInterfaceField(name, sig);
 639       if (f != null) return f;
 640     }
 641     // otherwise field lookup fails
 642     return null;
 643   }
 644 
 645   /** Find field according to JVM spec 5.4.3.2, returns the klass in
 646       which the field is defined. */
 647   public Field findField(String name, String sig) {
 648     // search order according to newest JVM spec (5.4.3.2, p.167).
 649     // 1) search for field in current klass
 650     Field f = findLocalField(name, sig);
 651     if (f != null) return f;
 652 
 653     // 2) search for field recursively in direct superinterfaces
 654     f = findInterfaceField(name, sig);
 655     if (f != null) return f;
 656 
 657     // 3) apply field lookup recursively if superclass exists
 658     InstanceKlass supr = (InstanceKlass) getSuper();
 659     if (supr != null) return supr.findField(name, sig);
 660 
 661     // 4) otherwise field lookup fails
 662     return null;
 663   }
 664 
 665   /** Find field according to JVM spec 5.4.3.2, returns the klass in
 666       which the field is defined (retained only for backward
 667       compatibility with jdbx) */
 668   public Field findFieldDbg(String name, String sig) {
 669     return findField(name, sig);
 670   }
 671 
 672   /** Get field by its index in the fields array. Only designed for
 673       use in a debugging system. */
 674   public Field getFieldByIndex(int fieldIndex) {
 675     return newField(fieldIndex);
 676   }
 677 
 678 
 679     /** Return a List of SA Fields for the fields declared in this class.
 680         Inherited fields are not included.
 681         Return an empty list if there are no fields declared in this class.
 682         Only designed for use in a debugging system. */
 683     public List<Field> getImmediateFields() {
 684         // A list of Fields for each field declared in this class/interface,
 685         // not including inherited fields.
 686         int length = getJavaFieldsCount();
 687         List<Field> immediateFields = new ArrayList<>(length);
 688         for (int index = 0; index < length; index++) {
 689             immediateFields.add(getFieldByIndex(index));
 690         }
 691 
 692         return immediateFields;
 693     }
 694 
 695     /** Return a List of SA Fields for all the java fields in this class,
 696         including all inherited fields.  This includes hidden
 697         fields.  Thus the returned list can contain fields with
 698         the same name.
 699         Return an empty list if there are no fields.
 700         Only designed for use in a debugging system. */
 701     public List<Field> getAllFields() {
 702         // Contains a Field for each field in this class, including immediate
 703         // fields and inherited fields.
 704         List<Field> allFields = getImmediateFields();
 705 
 706         // transitiveInterfaces contains all interfaces implemented
 707         // by this class and its superclass chain with no duplicates.
 708 
 709         KlassArray interfaces = getTransitiveInterfaces();
 710         int n = interfaces.length();
 711         for (int i = 0; i < n; i++) {
 712             InstanceKlass intf1 = (InstanceKlass) interfaces.getAt(i);
 713             if (Assert.ASSERTS_ENABLED) {
 714                 Assert.that(intf1.isInterface(), "just checking type");
 715             }
 716             allFields.addAll(intf1.getImmediateFields());
 717         }
 718 
 719         // Get all fields in the superclass, recursively.  But, don't
 720         // include fields in interfaces implemented by superclasses;
 721         // we already have all those.
 722         if (!isInterface()) {
 723             InstanceKlass supr;
 724             if  ( (supr = (InstanceKlass) getSuper()) != null) {
 725                 allFields.addAll(supr.getImmediateFields());
 726             }
 727         }
 728 
 729         return allFields;
 730     }
 731 
 732 
 733     /** Return a List of SA Methods declared directly in this class/interface.
 734         Return an empty list if there are none, or if this isn't a class/
 735         interface.
 736     */
 737     public List<Method> getImmediateMethods() {
 738       // Contains a Method for each method declared in this class/interface
 739       // not including inherited methods.
 740 
 741       MethodArray methods = getMethods();
 742       int length = methods.length();
 743       Method[] tmp = new Method[length];
 744 
 745       IntArray methodOrdering = getMethodOrdering();
 746       if (methodOrdering.length() != length) {
 747          // no ordering info present
 748          for (int index = 0; index < length; index++) {
 749             tmp[index] = methods.at(index);
 750          }
 751       } else {
 752          for (int index = 0; index < length; index++) {
 753             int originalIndex = methodOrdering.at(index);
 754             tmp[originalIndex] = methods.at(index);
 755          }
 756       }
 757 
 758       return Arrays.asList(tmp);
 759     }
 760 
 761     /** Return a List containing an SA InstanceKlass for each
 762         interface named in this class's 'implements' clause.
 763     */
 764     public List<Klass> getDirectImplementedInterfaces() {
 765         // Contains an InstanceKlass for each interface in this classes
 766         // 'implements' clause.
 767 
 768         KlassArray interfaces = getLocalInterfaces();
 769         int length = interfaces.length();
 770         List<Klass> directImplementedInterfaces = new ArrayList<>(length);
 771 
 772         for (int index = 0; index < length; index ++) {
 773             directImplementedInterfaces.add(interfaces.getAt(index));
 774         }
 775 
 776         return directImplementedInterfaces;
 777     }
 778 
 779   public Klass arrayKlassImpl(boolean orNull, int n) {
 780     // FIXME: in reflective system this would need to change to
 781     // actually allocate
 782     if (getArrayKlasses() == null) { return null; }
 783     ObjArrayKlass oak = (ObjArrayKlass) getArrayKlasses();
 784     if (orNull) {
 785       return oak.arrayKlassOrNull(n);
 786     }
 787     return oak.arrayKlass(n);
 788   }
 789 
 790   public Klass arrayKlassImpl(boolean orNull) {
 791     return arrayKlassImpl(orNull, 1);
 792   }
 793 
 794   public String signature() {
 795      return "L" + super.signature() + ";";
 796   }
 797 
 798   /** Find method in vtable. */
 799   public Method findMethod(String name, String sig) {
 800     return findMethod(getMethods(), name, sig);
 801   }
 802 
 803   /** Breakpoint support (see methods on Method* for details) */
 804   public BreakpointInfo getBreakpoints() {
 805     if (!VM.getVM().isJvmtiSupported()) {
 806       return null;
 807     }
 808     Address addr = getAddress().getAddressAt(breakpoints.getOffset());
 809     return VMObjectFactory.newObject(BreakpointInfo.class, addr);
 810   }
 811 
 812   public IntArray  getMethodOrdering() {
 813     Address addr = getAddress().getAddressAt(methodOrdering.getOffset());
 814     return VMObjectFactory.newObject(IntArray.class, addr);
 815   }
 816 
 817   public U1Array getFieldInfoStream() {
 818     Address addr = getAddress().getAddressAt(fieldinfoStream.getOffset());
 819     return VMObjectFactory.newObject(U1Array.class, addr);
 820   }
 821 
 822   public U2Array getInnerClasses() {
 823     Address addr = getAddress().getAddressAt(innerClasses.getOffset());
 824     return VMObjectFactory.newObject(U2Array.class, addr);
 825   }
 826 
 827   public U1Array getClassAnnotations() {
 828     Annotations annotations = getAnnotations();
 829     if (annotations != null) {
 830       return annotations.getClassAnnotations();
 831     } else {
 832       return null;
 833     }
 834   }
 835 
 836   public U1Array getClassTypeAnnotations() {
 837     Annotations annotations = getAnnotations();
 838     if (annotations != null) {
 839       return annotations.getClassTypeAnnotations();
 840     } else {
 841       return null;
 842     }
 843   }
 844 
 845   public U1Array getFieldAnnotations(int fieldIndex) {
 846     Annotations annotations = getAnnotations();
 847     if (annotations != null) {
 848       return annotations.getFieldAnnotations(fieldIndex);
 849     } else {
 850       return null;
 851     }
 852   }
 853 
 854   public U1Array getFieldTypeAnnotations(int fieldIndex) {
 855     Annotations annotations = getAnnotations();
 856     if (annotations != null) {
 857       return annotations.getFieldTypeAnnotations(fieldIndex);
 858     } else {
 859       return null;
 860     }
 861   }
 862 
 863   public U2Array getNestMembers() {
 864     Address addr = getAddress().getAddressAt(nestMembers.getOffset());
 865     return VMObjectFactory.newObject(U2Array.class, addr);
 866   }
 867 
 868   //----------------------------------------------------------------------
 869   // Internals only below this point
 870   //
 871 
 872   private void visitField(OopVisitor visitor, FieldType type, int index) {
 873     Field f = newField(index);
 874     if (type.isOop()) {
 875       visitor.doOop((OopField) f, false);
 876       return;
 877     }
 878     if (type.isByte()) {
 879       visitor.doByte((ByteField) f, false);
 880       return;
 881     }
 882     if (type.isChar()) {
 883       visitor.doChar((CharField) f, false);
 884       return;
 885     }
 886     if (type.isDouble()) {
 887       visitor.doDouble((DoubleField) f, false);
 888       return;
 889     }
 890     if (type.isFloat()) {
 891       visitor.doFloat((FloatField) f, false);
 892       return;
 893     }
 894     if (type.isInt()) {
 895       visitor.doInt((IntField) f, false);
 896       return;
 897     }
 898     if (type.isLong()) {
 899       visitor.doLong((LongField) f, false);
 900       return;
 901     }
 902     if (type.isShort()) {
 903       visitor.doShort((ShortField) f, false);
 904       return;
 905     }
 906     if (type.isBoolean()) {
 907       visitor.doBoolean((BooleanField) f, false);
 908       return;
 909     }
 910   }
 911 
 912   // Creates new field from index in fields TypeArray
 913   private Field newField(int index) {
 914     FieldType type = new FieldType(getFieldSignature(index));
 915     if (type.isOop()) {
 916      if (VM.getVM().isCompressedOopsEnabled()) {
 917         return new NarrowOopField(this, index);
 918      } else {
 919         return new OopField(this, index);
 920      }
 921     }
 922     if (type.isByte()) {
 923       return new ByteField(this, index);
 924     }
 925     if (type.isChar()) {
 926       return new CharField(this, index);
 927     }
 928     if (type.isDouble()) {
 929       return new DoubleField(this, index);
 930     }
 931     if (type.isFloat()) {
 932       return new FloatField(this, index);
 933     }
 934     if (type.isInt()) {
 935       return new IntField(this, index);
 936     }
 937     if (type.isLong()) {
 938       return new LongField(this, index);
 939     }
 940     if (type.isShort()) {
 941       return new ShortField(this, index);
 942     }
 943     if (type.isBoolean()) {
 944       return new BooleanField(this, index);
 945     }
 946     throw new RuntimeException("Illegal field type at index " + index);
 947   }
 948 
 949   private static Method findMethod(MethodArray methods, String name, String signature) {
 950     int index = linearSearch(methods, name, signature);
 951     if (index != -1) {
 952       return methods.at(index);
 953     } else {
 954       return null;
 955     }
 956   }
 957 
 958   private static int linearSearch(MethodArray methods, String name, String signature) {
 959     int len = methods.length();
 960     for (int index = 0; index < len; index++) {
 961       Method m = methods.at(index);
 962       if (m.getSignature().equals(signature) && m.getName().equals(name)) {
 963         return index;
 964       }
 965     }
 966     return -1;
 967   }
 968 
 969   public void dumpReplayData(PrintStream out) {
 970     ConstantPool cp = getConstants();
 971 
 972     // Try to record related loaded classes
 973     Klass sub = getSubklassKlass();
 974     while (sub != null) {
 975         if (sub instanceof InstanceKlass) {
 976             out.println("instanceKlass " + sub.getName().asString());
 977         }
 978         sub = sub.getNextSiblingKlass();
 979     }
 980 
 981     final int length = cp.getLength();
 982     out.print("ciInstanceKlass " + getName().asString() + " " + (isLinked() ? 1 : 0) + " " + (isInitialized() ? 1 : 0) + " " + length);
 983     for (int index = 1; index < length; index++) {
 984       out.print(" " + cp.getTags().at(index));
 985     }
 986     out.println();
 987     if (isInitialized()) {
 988       Field[] staticFields = getStaticFields();
 989       for (int i = 0; i < staticFields.length; i++) {
 990         Field f = staticFields[i];
 991         Oop mirror = getJavaMirror();
 992         if (f.isFinal() && !f.hasInitialValue()) {
 993           out.print("staticfield " + getName().asString() + " " +
 994                     OopUtilities.escapeString(f.getID().getName()) + " " +
 995                     f.getFieldType().getSignature().asString() + " ");
 996           if (f instanceof ByteField) {
 997             ByteField bf = (ByteField)f;
 998             out.println(bf.getValue(mirror));
 999           } else if (f instanceof BooleanField) {
1000             BooleanField bf = (BooleanField)f;
1001             out.println(bf.getValue(mirror) ? 1 : 0);
1002           } else if (f instanceof ShortField) {
1003             ShortField bf = (ShortField)f;
1004             out.println(bf.getValue(mirror));
1005           } else if (f instanceof CharField) {
1006             CharField bf = (CharField)f;
1007             out.println(bf.getValue(mirror) & 0xffff);
1008           } else if (f instanceof IntField) {
1009             IntField bf = (IntField)f;
1010             out.println(bf.getValue(mirror));
1011           } else  if (f instanceof LongField) {
1012             LongField bf = (LongField)f;
1013             out.println(bf.getValue(mirror));
1014           } else if (f instanceof FloatField) {
1015             FloatField bf = (FloatField)f;
1016             out.println(Float.floatToRawIntBits(bf.getValue(mirror)));
1017           } else if (f instanceof DoubleField) {
1018             DoubleField bf = (DoubleField)f;
1019             out.println(Double.doubleToRawLongBits(bf.getValue(mirror)));
1020           } else if (f instanceof OopField) {
1021             OopField bf = (OopField)f;
1022 
1023             Oop value = bf.getValue(mirror);
1024             if (value == null) {
1025               out.println("null");
1026             } else if (value.isInstance()) {
1027               Instance inst = (Instance)value;
1028               if (inst.isA(SystemDictionary.getStringKlass())) {
1029                 out.println("\"" + OopUtilities.stringOopToEscapedString(inst) + "\"");
1030               } else {
1031                 out.println(inst.getKlass().getName().asString());
1032               }
1033             } else if (value.isObjArray()) {
1034               ObjArray oa = (ObjArray)value;
1035               Klass ek = (ObjArrayKlass)oa.getKlass();
1036               out.println(oa.getLength() + " " + ek.getName().asString());
1037             } else if (value.isTypeArray()) {
1038               TypeArray ta = (TypeArray)value;
1039               out.println(ta.getLength());
1040             } else {
1041               out.println(value);
1042             }
1043           }
1044         }
1045       }
1046     }
1047   }
1048 }