JavaObject.java revision 2224:2a8815d86b93
1/*
2 * Copyright (c) 1997, 2016, 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(getValue(), 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(getValue(), 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    protected final int readValueLength() throws IOException {
216        JavaClass cl = getClazz();
217        int idSize = cl.getIdentifierSize();
218        long lengthOffset = getOffset() + 2*idSize + 4;
219        return cl.getReadBuffer().getInt(lengthOffset);
220    }
221
222    protected final byte[] readValue() throws IOException {
223        JavaClass cl = getClazz();
224        int idSize = cl.getIdentifierSize();
225        ReadBuffer buf = cl.getReadBuffer();
226        long offset = getOffset() + 2*idSize + 4;
227        int length = buf.getInt(offset);
228        if (length == 0) {
229            return Snapshot.EMPTY_BYTE_ARRAY;
230        } else {
231            byte[] res = new byte[length];
232            buf.get(offset + 4, res);
233            return res;
234        }
235    }
236
237    private JavaThing[] parseFields(byte[] data, boolean verbose) {
238        JavaClass cl = getClazz();
239        int target = cl.getNumFieldsForInstance();
240        JavaField[] fields = cl.getFields();
241        JavaThing[] fieldValues = new JavaThing[target];
242        Snapshot snapshot = cl.getSnapshot();
243        int idSize = snapshot.getIdentifierSize();
244        int fieldNo = 0;
245        // In the dump file, the fields are stored in this order:
246        // fields of most derived class (immediate class) are stored
247        // first and then the super class and so on. In this object,
248        // fields are stored in the reverse ("natural") order. i.e.,
249        // fields of most super class are stored first.
250
251        // target variable is used to compensate for the fact that
252        // the dump file starts field values from the leaf working
253        // upwards in the inheritance hierarchy, whereas JavaObject
254        // starts with the top of the inheritance hierarchy and works down.
255        target -= fields.length;
256        JavaClass currClass = cl;
257        int index = 0;
258        for (int i = 0; i < fieldValues.length; i++, fieldNo++) {
259            while (fieldNo >= fields.length) {
260                currClass = currClass.getSuperclass();
261                fields = currClass.getFields();
262                fieldNo = 0;
263                target -= fields.length;
264            }
265            JavaField f = fields[fieldNo];
266            char sig = f.getSignature().charAt(0);
267            switch (sig) {
268                case 'L':
269                case '[': {
270                    long id = objectIdAt(index, data);
271                    index += idSize;
272                    JavaObjectRef ref = new JavaObjectRef(id);
273                    fieldValues[target+fieldNo] = ref.dereference(snapshot, f, verbose);
274                    break;
275                }
276                case 'Z': {
277                    byte value = byteAt(index, data);
278                    index++;
279                    fieldValues[target+fieldNo] = new JavaBoolean(value != 0);
280                    break;
281                }
282                case 'B': {
283                    byte value = byteAt(index, data);
284                    index++;
285                    fieldValues[target+fieldNo] = new JavaByte(value);
286                    break;
287                }
288                case 'S': {
289                    short value = shortAt(index, data);
290                    index += 2;
291                    fieldValues[target+fieldNo] = new JavaShort(value);
292                    break;
293                }
294                case 'C': {
295                    char value = charAt(index, data);
296                    index += 2;
297                    fieldValues[target+fieldNo] = new JavaChar(value);
298                    break;
299                }
300                case 'I': {
301                    int value = intAt(index, data);
302                    index += 4;
303                    fieldValues[target+fieldNo] = new JavaInt(value);
304                    break;
305                }
306                case 'J': {
307                    long value = longAt(index, data);
308                    index += 8;
309                    fieldValues[target+fieldNo] = new JavaLong(value);
310                    break;
311                }
312                case 'F': {
313                    float value = floatAt(index, data);
314                    index += 4;
315                    fieldValues[target+fieldNo] = new JavaFloat(value);
316                    break;
317                }
318                case 'D': {
319                    double value = doubleAt(index, data);
320                    index += 8;
321                    fieldValues[target+fieldNo] = new JavaDouble(value);
322                    break;
323                }
324                default:
325                    throw new RuntimeException("invalid signature: " + sig);
326            }
327        }
328        return fieldValues;
329    }
330
331    private void warn(String msg) {
332        System.out.println("WARNING: " + msg);
333    }
334}
335