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