1 /*
  2  * Copyright (c) 1997, 2022, 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 /*
 26  * The Original Code is HAT. The Initial Developer of the
 27  * Original Code is Bill Foote, with contributions from others
 28  * at JavaSoft/Sun.
 29  */
 30 
 31 package jdk.test.lib.hprof.model;
 32 
 33 import java.util.Vector;
 34 import java.util.Enumeration;
 35 import jdk.test.lib.hprof.util.CompositeEnumeration;
 36 import jdk.test.lib.hprof.parser.ReadBuffer;
 37 
 38 /**
 39  *
 40  * @author      Bill Foote
 41  */
 42 
 43 
 44 public class JavaClass extends JavaHeapObject {
 45     // my id
 46     private long id;
 47     // my name
 48     private String name;
 49 
 50     // These are JavaObjectRef before resolve
 51     private JavaThing superclass;
 52     private JavaThing loader;
 53     private JavaThing signers;
 54     private JavaThing protectionDomain;
 55 
 56     // non-static fields
 57     private JavaField[] fields;
 58     // static fields
 59     private JavaStatic[] statics;
 60 
 61     private static final JavaClass[] EMPTY_CLASS_ARRAY = new JavaClass[0];
 62     // my subclasses
 63     private JavaClass[] subclasses = EMPTY_CLASS_ARRAY;
 64 
 65     // my instances
 66     private Vector<JavaHeapObject> instances = new Vector<JavaHeapObject>();
 67 
 68     // Who I belong to.  Set on resolve.
 69     private Snapshot mySnapshot;
 70 
 71     // Size of an instance, including VM overhead
 72     private int instanceSize;
 73     // Total number of fields including inherited ones
 74     private int totalNumFields;
 75 
 76     // Size of the instance in the object of the class is inlined.
 77     // Calculated lazily.
 78     private int inlinedInstanceSize = -1;
 79 
 80     public JavaClass(long id, String name, long superclassId, long loaderId,
 81                      long signersId, long protDomainId,
 82                      JavaField[] fields, JavaStatic[] statics,
 83                      int instanceSize) {
 84         this.id = id;
 85         this.name = name;
 86         this.superclass = new JavaObjectRef(superclassId);
 87         this.loader = new JavaObjectRef(loaderId);
 88         this.signers = new JavaObjectRef(signersId);
 89         this.protectionDomain = new JavaObjectRef(protDomainId);
 90         this.fields = fields;
 91         this.statics = statics;
 92         this.instanceSize = instanceSize;
 93     }
 94 
 95     public JavaClass(String name, long superclassId, long loaderId,
 96                      long signersId, long protDomainId,
 97                      JavaField[] fields, JavaStatic[] statics,
 98                      int instanceSize) {
 99         this(-1L, name, superclassId, loaderId, signersId,
100              protDomainId, fields, statics, instanceSize);
101     }
102 
103     public final JavaClass getClazz() {
104         return mySnapshot.getJavaLangClass();
105     }
106 
107     public final int getIdentifierSize() {
108         return mySnapshot.getIdentifierSize();
109     }
110 
111     public final int getMinimumObjectSize() {
112         return mySnapshot.getMinimumObjectSize();
113     }
114 
115     public void resolve(Snapshot snapshot) {
116         if (mySnapshot != null) {
117             return;
118         }
119         mySnapshot = snapshot;
120 
121         // Resolve inlined fields. Should be done before resolveSuperclass for correct field counting
122         Snapshot.ClassInlinedFields[] inlinedFields = snapshot.findClassInlinedFields(id);
123         if (inlinedFields != null) {
124             int newCount = fields.length;
125             for (Snapshot.ClassInlinedFields f: inlinedFields) {
126                 if (f.synthFieldCount == 0) {
127                     // Empty primitive class. just skip it - no data there.
128                     continue;
129                 }
130                 JavaHeapObject clazz = snapshot.findThing(f.fieldClassID);
131                 if (clazz instanceof JavaClass fieldClass) {
132                     fieldClass.resolve(snapshot);
133 
134                     // Set new field.
135                     fields[f.fieldIndex] = new InlinedJavaField(f.fieldName, 'Q' + fieldClass.getName() + ';', fieldClass);
136                     newCount -= (f.synthFieldCount - 1);
137                     // Reset invalid fields.
138                     for (int i = 1; i < f.synthFieldCount; i++) {
139                         fields[f.fieldIndex + i] = null;
140                     }
141                 } else {
142                     // The field class not found.
143                     System.out.println("WARNING: class of inlined field not found:" + getName() + "." + f.fieldName);
144                 }
145             }
146 
147             // Set new fields.
148             JavaField[] newFields = new JavaField[newCount];
149             int oldIndex = 0;
150             for (int i = 0; i < newFields.length; i++) {
151                 while (fields[oldIndex] == null) {
152                     oldIndex++;
153                 }
154                 newFields[i] = fields[oldIndex];
155                 oldIndex++;
156             }
157             fields = newFields;
158         }
159 
160         resolveSuperclass(snapshot);
161         if (superclass != null) {
162             ((JavaClass) superclass).addSubclass(this);
163         }
164 
165         loader  = loader.dereference(snapshot, null);
166         signers  = signers.dereference(snapshot, null);
167         protectionDomain  = protectionDomain.dereference(snapshot, null);
168 
169         for (int i = 0; i < statics.length; i++) {
170             statics[i].resolve(this, snapshot);
171         }
172         snapshot.getJavaLangClass().addInstance(this);
173         super.resolve(snapshot);
174 
175         return;
176     }
177 
178     /**
179      * Resolve our superclass.  This might be called well before
180      * all instances are available (like when reading deferred
181      * instances in a 1.2 dump file :-)  Calling this is sufficient
182      * to be able to explore this class' fields.
183      */
184     public void resolveSuperclass(Snapshot snapshot) {
185         if (superclass == null) {
186             // We must be java.lang.Object, so we have no superclass.
187         } else {
188             totalNumFields = fields.length;
189             superclass = superclass.dereference(snapshot, null);
190             if (superclass == snapshot.getNullThing()) {
191                 superclass = null;
192             } else {
193                 try {
194                     JavaClass sc = (JavaClass) superclass;
195                     sc.resolveSuperclass(snapshot);
196                     totalNumFields += sc.totalNumFields;
197                 } catch (ClassCastException ex) {
198                     System.out.println("Warning!  Superclass of " + name + " is " + superclass);
199                     superclass = null;
200                 }
201             }
202         }
203     }
204 
205     public boolean isString() {
206         return mySnapshot.getJavaLangString() == this;
207     }
208 
209     public boolean isClassLoader() {
210         return mySnapshot.getJavaLangClassLoader().isAssignableFrom(this);
211     }
212 
213     /**
214      * Get a numbered field from this class
215      */
216     public JavaField getField(int i) {
217         if (i < 0 || i >= fields.length) {
218             throw new Error("No field " + i + " for " + name);
219         }
220         return fields[i];
221     }
222 
223     /**
224      * Get the total number of fields that are part of an instance of
225      * this class.  That is, include superclasses.
226      */
227     public int getNumFieldsForInstance() {
228         return totalNumFields;
229     }
230 
231     /**
232      * Get a numbered field from all the fields that are part of instance
233      * of this class.  That is, include superclasses.
234      */
235     public JavaField getFieldForInstance(int i) {
236         if (superclass != null) {
237             JavaClass sc = (JavaClass) superclass;
238             if (i < sc.totalNumFields) {
239                 return sc.getFieldForInstance(i);
240             }
241             i -= sc.totalNumFields;
242         }
243         return getField(i);
244     }
245 
246     /**
247      * Get the class responsible for field i, where i is a field number that
248      * could be passed into getFieldForInstance.
249      *
250      * @see JavaClass.getFieldForInstance()
251      */
252     public JavaClass getClassForField(int i) {
253         if (superclass != null) {
254             JavaClass sc = (JavaClass) superclass;
255             if (i < sc.totalNumFields) {
256                 return sc.getClassForField(i);
257             }
258         }
259         return this;
260     }
261 
262     public long getId() {
263         return id;
264     }
265 
266     public String getName() {
267         return name;
268     }
269 
270     public boolean isArray() {
271         return name.indexOf('[') != -1;
272     }
273 
274     public Enumeration<JavaHeapObject> getInstances(boolean includeSubclasses) {
275         if (includeSubclasses) {
276             Enumeration<JavaHeapObject> res = instances.elements();
277             for (int i = 0; i < subclasses.length; i++) {
278                 res = new CompositeEnumeration(res,
279                               subclasses[i].getInstances(true));
280             }
281             return res;
282         } else {
283             return instances.elements();
284         }
285     }
286 
287     /**
288      * @return a count of the instances of this class
289      */
290     public int getInstancesCount(boolean includeSubclasses) {
291         int result = instances.size();
292         if (includeSubclasses) {
293             for (int i = 0; i < subclasses.length; i++) {
294                 result += subclasses[i].getInstancesCount(includeSubclasses);
295             }
296         }
297         return result;
298     }
299 
300     public JavaClass[] getSubclasses() {
301         return subclasses;
302     }
303 
304     /**
305      * This can only safely be called after resolve()
306      */
307     public JavaClass getSuperclass() {
308         return (JavaClass) superclass;
309     }
310 
311     /**
312      * This can only safely be called after resolve()
313      */
314     public JavaThing getLoader() {
315         return loader;
316     }
317 
318     /**
319      * This can only safely be called after resolve()
320      */
321     public boolean isBootstrap() {
322         return loader == mySnapshot.getNullThing();
323     }
324 
325     /**
326      * This can only safely be called after resolve()
327      */
328     public JavaThing getSigners() {
329         return signers;
330     }
331 
332     /**
333      * This can only safely be called after resolve()
334      */
335     public JavaThing getProtectionDomain() {
336         return protectionDomain;
337     }
338 
339     public JavaField[] getFields() {
340         return fields;
341     }
342 
343     /**
344      * Includes superclass fields
345      */
346     public JavaField[] getFieldsForInstance() {
347         Vector<JavaField> v = new Vector<JavaField>();
348         addFields(v);
349         JavaField[] result = new JavaField[v.size()];
350         for (int i = 0; i < v.size(); i++) {
351             result[i] =  v.elementAt(i);
352         }
353         return result;
354     }
355 
356 
357     public JavaStatic[] getStatics() {
358         return statics;
359     }
360 
361     // returns value of static field of given name
362     public JavaThing getStaticField(String name) {
363         for (int i = 0; i < statics.length; i++) {
364             JavaStatic s = statics[i];
365             if (s.getField().getName().equals(name)) {
366                 return s.getValue();
367             }
368         }
369         return null;
370     }
371 
372     public String toString() {
373         return "class " + name;
374     }
375 
376     public int compareTo(JavaThing other) {
377         if (other instanceof JavaClass) {
378             return name.compareTo(((JavaClass) other).name);
379         }
380         return super.compareTo(other);
381     }
382 
383 
384     /**
385      * @return true iff a variable of type this is assignable from an instance
386      *          of other
387      */
388     public boolean isAssignableFrom(JavaClass other) {
389         if (this == other) {
390             return true;
391         } else if (other == null) {
392             return false;
393         } else {
394             return isAssignableFrom((JavaClass) other.superclass);
395             // Trivial tail recursion:  I have faith in javac.
396         }
397     }
398 
399     /**
400      * Describe the reference that this thing has to target.  This will only
401      * be called if target is in the array returned by getChildrenForRootset.
402      */
403      public String describeReferenceTo(JavaThing target, Snapshot ss) {
404         for (int i = 0; i < statics.length; i++) {
405             JavaField f = statics[i].getField();
406             if (f.hasId()) {
407                 JavaThing other = statics[i].getValue();
408                 if (other == target) {
409                     return "static field " + f.getName();
410                 }
411             }
412         }
413         return super.describeReferenceTo(target, ss);
414     }
415 
416     /**
417      * @return the size of an instance of this class.  Gives 0 for an array
418      *          type.
419      */
420     public int getInstanceSize() {
421         return instanceSize + mySnapshot.getMinimumObjectSize();
422     }
423 
424     public int getInlinedInstanceSize() {
425         if (inlinedInstanceSize < 0) {
426             int size = 0;
427             for (JavaField f: fields) {
428                 if (f instanceof InlinedJavaField inlinedField) {
429                     size += inlinedField.getInlinedFieldClass().getInlinedInstanceSize();
430                 } else {
431                     char sig = f.getSignature().charAt(0);
432                     switch (sig) {
433                         case 'Q': {
434                             System.out.println("WARNING: (getInlinedInstanceSize) field "
435                                     + getClazz().getName() + "." + f.getName()
436                                     + " is not inlined, but has Q-signature: " + f.getSignature());
437                         } // continue as 'L' object
438                         case 'L':
439                         case '[':
440                             size += mySnapshot.getIdentifierSize();
441                             break;
442                         case 'B':
443                         case 'Z':
444                             size += 1;
445                             break;
446                         case 'C':
447                         case 'S':
448                             size += 2;
449                             break;
450                         case 'I':
451                         case 'F':
452                             size += 4;
453                             break;
454                         case 'J':
455                         case 'D':
456                             size += 8;
457                             break;
458                         default:
459                             throw new RuntimeException("unknown field type: " + sig);
460                     }
461                 }
462             }
463             inlinedInstanceSize = size;
464         }
465         return inlinedInstanceSize;
466     }
467 
468     /**
469      * @return The size of all instances of this class.  Correctly handles
470      *          arrays.
471      */
472     public long getTotalInstanceSize() {
473         int count = instances.size();
474         if (count == 0 || !isArray()) {
475             return count * instanceSize;
476         }
477 
478         // array class and non-zero count, we have to
479         // get the size of each instance and sum it
480         long result = 0;
481         for (int i = 0; i < count; i++) {
482             JavaThing t = (JavaThing) instances.elementAt(i);
483             result += t.getSize();
484         }
485         return result;
486     }
487 
488     /**
489      * @return the size of this object
490      */
491     @Override
492     public long getSize() {
493         JavaClass cl = mySnapshot.getJavaLangClass();
494         if (cl == null) {
495             return 0;
496         } else {
497             return cl.getInstanceSize();
498         }
499     }
500 
501     public void visitReferencedObjects(JavaHeapObjectVisitor v) {
502         super.visitReferencedObjects(v);
503         JavaHeapObject sc = getSuperclass();
504         if (sc != null) v.visit(getSuperclass());
505 
506         JavaThing other;
507         other = getLoader();
508         if (other instanceof JavaHeapObject) {
509             v.visit((JavaHeapObject)other);
510         }
511         other = getSigners();
512         if (other instanceof JavaHeapObject) {
513             v.visit((JavaHeapObject)other);
514         }
515         other = getProtectionDomain();
516         if (other instanceof JavaHeapObject) {
517             v.visit((JavaHeapObject)other);
518         }
519 
520         for (int i = 0; i < statics.length; i++) {
521             JavaField f = statics[i].getField();
522             if (!v.exclude(this, f) && f.hasId()) {
523                 other = statics[i].getValue();
524                 if (other instanceof JavaHeapObject) {
525                     v.visit((JavaHeapObject) other);
526                 }
527             }
528         }
529     }
530 
531     // package-privates below this point
532     final ReadBuffer getReadBuffer() {
533         return mySnapshot.getReadBuffer();
534     }
535 
536     final void setNew(JavaHeapObject obj, boolean flag) {
537         mySnapshot.setNew(obj, flag);
538     }
539 
540     final boolean isNew(JavaHeapObject obj) {
541         return mySnapshot.isNew(obj);
542     }
543 
544     final StackTrace getSiteTrace(JavaHeapObject obj) {
545         return mySnapshot.getSiteTrace(obj);
546     }
547 
548     final void addReferenceFromRoot(Root root, JavaHeapObject obj) {
549         mySnapshot.addReferenceFromRoot(root, obj);
550     }
551 
552     final Root getRoot(JavaHeapObject obj) {
553         return mySnapshot.getRoot(obj);
554     }
555 
556     final Snapshot getSnapshot() {
557         return mySnapshot;
558     }
559 
560     void addInstance(JavaHeapObject inst) {
561         instances.addElement(inst);
562     }
563 
564     // Internals only below this point
565     private void addFields(Vector<JavaField> v) {
566         if (superclass != null) {
567             ((JavaClass) superclass).addFields(v);
568         }
569         for (int i = 0; i < fields.length; i++) {
570             v.addElement(fields[i]);
571         }
572     }
573 
574     private void addSubclassInstances(Vector<JavaHeapObject> v) {
575         for (int i = 0; i < subclasses.length; i++) {
576             subclasses[i].addSubclassInstances(v);
577         }
578         for (int i = 0; i < instances.size(); i++) {
579             v.addElement(instances.elementAt(i));
580         }
581     }
582 
583     private void addSubclass(JavaClass sub) {
584         JavaClass newValue[] = new JavaClass[subclasses.length + 1];
585         System.arraycopy(subclasses, 0, newValue, 0, subclasses.length);
586         newValue[subclasses.length] = sub;
587         subclasses = newValue;
588     }
589 }