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