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