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