1 /*
  2  * Copyright (c) 1997, 2017, 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.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 
 27 /*
 28  * The Original Code is HAT. The Initial Developer of the
 29  * Original Code is Bill Foote, with contributions from others
 30  * at JavaSoft/Sun.
 31  */
 32 
 33 package jdk.test.lib.hprof.model;
 34 
 35 import java.io.IOException;
 36 import jdk.test.lib.hprof.parser.ReadBuffer;
 37 
 38 /**
 39  * Represents Java instance
 40  *
 41  * @author      Bill Foote
 42  */
 43 public class JavaObject extends JavaLazyReadObject {
 44 
 45     private Object clazz;       // Number before resolve
 46                                 // JavaClass after resolve
 47     /**
 48      * Construct a new JavaObject.
 49      *
 50      * @param classID id of the class object
 51      * @param offset The offset of field data
 52      */
 53     public JavaObject(long classID, long offset) {
 54         super(offset);
 55         this.clazz = makeId(classID);
 56     }
 57 
 58     public void resolve(Snapshot snapshot) {
 59         if (clazz instanceof JavaClass) {
 60             return;
 61         }
 62         if (clazz instanceof Number) {
 63             long classID = getIdValue((Number)clazz);
 64             clazz = snapshot.findThing(classID);
 65             if (! (clazz instanceof JavaClass)) {
 66                 warn("Class " + Long.toHexString(classID) + " not found, " +
 67                      "adding fake class!");
 68                 int length;
 69                 ReadBuffer buf = snapshot.getReadBuffer();
 70                 int idSize = snapshot.getIdentifierSize();
 71                 long lenOffset = getOffset() + 2*idSize + 4;
 72                 try {
 73                     length = buf.getInt(lenOffset);
 74                 } catch (IOException exp) {
 75                     throw new RuntimeException(exp);
 76                 }
 77                 clazz = snapshot.addFakeInstanceClass(classID, length);
 78             }
 79         } else {
 80             throw new InternalError("should not reach here");
 81         }
 82 
 83         JavaClass cl = (JavaClass) clazz;
 84         cl.resolve(snapshot);
 85 
 86         // while resolving, parse fields in verbose mode.
 87         // but, getFields calls parseFields in non-verbose mode
 88         // to avoid printing warnings repeatedly.
 89         parseFields(true);
 90 
 91         cl.addInstance(this);
 92         super.resolve(snapshot);
 93     }
 94 
 95     /**
 96      * Are we the same type as other?  We are iff our clazz is the
 97      * same type as other's.
 98      */
 99     public boolean isSameTypeAs(JavaThing other) {
100         if (!(other instanceof JavaObject)) {
101             return false;
102         }
103         JavaObject oo = (JavaObject) other;
104         return getClazz().equals(oo.getClazz());
105     }
106 
107     /**
108      * Return our JavaClass object.  This may only be called after resolve.
109      */
110     public JavaClass getClazz() {
111         return (JavaClass) clazz;
112     }
113 
114     public JavaThing[] getFields() {
115         // pass false to verbose mode so that dereference
116         // warnings are not printed.
117         return parseFields(false);
118     }
119 
120     // returns the value of field of given name
121     public JavaThing getField(String name) {
122         JavaThing[] flds = getFields();
123         JavaField[] instFields = getClazz().getFieldsForInstance();
124         for (int i = 0; i < instFields.length; i++) {
125             if (instFields[i].getName().equals(name)) {
126                 return flds[i];
127             }
128         }
129         return null;
130     }
131 
132     public int compareTo(JavaThing other) {
133         if (other instanceof JavaObject) {
134             JavaObject oo = (JavaObject) other;
135             return getClazz().getName().compareTo(oo.getClazz().getName());
136         }
137         return super.compareTo(other);
138     }
139 
140     public void visitReferencedObjects(JavaHeapObjectVisitor v) {
141         super.visitReferencedObjects(v);
142         JavaThing[] flds = getFields();
143         for (int i = 0; i < flds.length; i++) {
144             if (flds[i] != null) {
145                 if (v.mightExclude()
146                     && v.exclude(getClazz().getClassForField(i),
147                                  getClazz().getFieldForInstance(i)))
148                 {
149                     // skip it
150                 } else if (flds[i] instanceof JavaHeapObject) {
151                     v.visit((JavaHeapObject) flds[i]);
152                 }
153             }
154         }
155     }
156 
157     public boolean refersOnlyWeaklyTo(Snapshot ss, JavaThing other) {
158         if (ss.getWeakReferenceClass() != null) {
159             final int referentFieldIndex = ss.getReferentFieldIndex();
160             if (ss.getWeakReferenceClass().isAssignableFrom(getClazz())) {
161                 //
162                 // REMIND:  This introduces a dependency on the JDK
163                 //      implementation that is undesirable.
164                 JavaThing[] flds = getFields();
165                 for (int i = 0; i < flds.length; i++) {
166                     if (i != referentFieldIndex && flds[i] == other) {
167                         return false;
168                     }
169                 }
170                 return true;
171             }
172         }
173         return false;
174     }
175 
176     /**
177      * Describe the reference that this thing has to target.  This will only
178      * be called if target is in the array returned by getChildrenForRootset.
179      */
180     public String describeReferenceTo(JavaThing target, Snapshot ss) {
181         JavaThing[] flds = getFields();
182         for (int i = 0; i < flds.length; i++) {
183             if (flds[i] == target) {
184                 JavaField f = getClazz().getFieldForInstance(i);
185                 return "field " + f.getName();
186             }
187         }
188         return super.describeReferenceTo(target, ss);
189     }
190 
191     public String toString() {
192         if (getClazz().isString()) {
193             JavaThing value = getField("value");
194             if (value instanceof JavaValueArray) {
195                 return ((JavaValueArray)value).valueString();
196             } else {
197                 return "null";
198             }
199         } else {
200             return super.toString();
201         }
202     }
203 
204     // Internals only below this point
205 
206     /*
207      * Java instance record (HPROF_GC_INSTANCE_DUMP) looks as below:
208      *
209      *     object ID
210      *     stack trace serial number (int)
211      *     class ID
212      *     data length (int)
213      *     byte[length]
214      */
215     @Override
216     protected final long readValueLength() throws IOException {
217         long lengthOffset = getOffset() + 2 * idSize() + 4;
218         return buf().getInt(lengthOffset);
219     }
220 
221     @Override
222     protected final JavaThing[] readValue() throws IOException {
223         return parseFields(false);
224     }
225 
226     private long dataStartOffset() {
227         return getOffset() + idSize() + 4 + idSize() + 4;
228     }
229 
230     private JavaThing[] parseFields(boolean verbose) {
231         JavaClass cl = getClazz();
232         int target = cl.getNumFieldsForInstance();
233         JavaField[] fields = cl.getFields();
234         JavaThing[] fieldValues = new JavaThing[target];
235         Snapshot snapshot = cl.getSnapshot();
236         int fieldNo = 0;
237         // In the dump file, the fields are stored in this order:
238         // fields of most derived class (immediate class) are stored
239         // first and then the super class and so on. In this object,
240         // fields are stored in the reverse ("natural") order. i.e.,
241         // fields of most super class are stored first.
242 
243         // target variable is used to compensate for the fact that
244         // the dump file starts field values from the leaf working
245         // upwards in the inheritance hierarchy, whereas JavaObject
246         // starts with the top of the inheritance hierarchy and works down.
247         target -= fields.length;
248         JavaClass currClass = cl;
249         long offset = dataStartOffset();
250         for (int i = 0; i < fieldValues.length; i++, fieldNo++) {
251             while (fieldNo >= fields.length) {
252                 currClass = currClass.getSuperclass();
253                 fields = currClass.getFields();
254                 fieldNo = 0;
255                 target -= fields.length;
256             }
257             JavaField f = fields[fieldNo];
258             char sig = f.getSignature().charAt(0);
259             try {
260                 switch (sig) {
261                     case 'L':
262                     case '[': {
263                         long id = objectIdAt(offset);
264                         offset += idSize();
265                         JavaObjectRef ref = new JavaObjectRef(id);
266                         fieldValues[target+fieldNo] = ref.dereference(snapshot, f, verbose);
267                         break;
268                     }
269                     case 'Z': {
270                         byte value = byteAt(offset);
271                         offset++;
272                         fieldValues[target+fieldNo] = new JavaBoolean(value != 0);
273                         break;
274                     }
275                     case 'B': {
276                         byte value = byteAt(offset);
277                         offset++;
278                         fieldValues[target+fieldNo] = new JavaByte(value);
279                         break;
280                     }
281                     case 'S': {
282                         short value = shortAt(offset);
283                         offset += 2;
284                         fieldValues[target+fieldNo] = new JavaShort(value);
285                         break;
286                     }
287                     case 'C': {
288                         char value = charAt(offset);
289                         offset += 2;
290                         fieldValues[target+fieldNo] = new JavaChar(value);
291                         break;
292                     }
293                     case 'I': {
294                         int value = intAt(offset);
295                         offset += 4;
296                         fieldValues[target+fieldNo] = new JavaInt(value);
297                         break;
298                     }
299                     case 'J': {
300                         long value = longAt(offset);
301                         offset += 8;
302                         fieldValues[target+fieldNo] = new JavaLong(value);
303                         break;
304                     }
305                     case 'F': {
306                         float value = floatAt(offset);
307                         offset += 4;
308                         fieldValues[target+fieldNo] = new JavaFloat(value);
309                         break;
310                     }
311                     case 'D': {
312                         double value = doubleAt(offset);
313                         offset += 8;
314                         fieldValues[target+fieldNo] = new JavaDouble(value);
315                         break;
316                     }
317                     default:
318                         throw new RuntimeException("invalid signature: " + sig);
319 
320                 }
321         } catch (IOException exp) {
322             System.err.println("lazy read failed at offset " + offset);
323             exp.printStackTrace();
324             return Snapshot.EMPTY_JAVATHING_ARRAY;
325             }
326         }
327         return fieldValues;
328     }
329 
330     private void warn(String msg) {
331         System.out.println("WARNING: " + msg);
332     }
333 }