JavaObject.java revision 2779:56d1e05e0def
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
33package jdk.test.lib.hprof.model;
34
35import java.io.IOException;
36import jdk.test.lib.hprof.parser.ReadBuffer;
37
38/**
39 * Represents Java instance
40 *
41 * @author      Bill Foote
42 */
43public 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}
334