JavaValueArray.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 jdk.test.lib.hprof.parser.ReadBuffer;
36import java.io.IOException;
37
38/**
39 * An array of values, that is, an array of ints, boolean, floats or the like.
40 *
41 * @author      Bill Foote
42 */
43public class JavaValueArray extends JavaLazyReadObject
44                /*imports*/ implements ArrayTypeCodes {
45
46    private static String arrayTypeName(byte sig) {
47        switch (sig) {
48            case 'B':
49                return "byte[]";
50            case 'Z':
51                return "boolean[]";
52            case 'C':
53                return "char[]";
54            case 'S':
55                return "short[]";
56            case 'I':
57                return "int[]";
58            case 'F':
59                return "float[]";
60            case 'J':
61                return "long[]";
62            case 'D':
63                return "double[]";
64            default:
65                throw new RuntimeException("invalid array element sig: " + sig);
66        }
67    }
68
69    private static int elementSize(byte type) {
70        switch (type) {
71            case T_BYTE:
72            case T_BOOLEAN:
73                return 1;
74            case T_CHAR:
75            case T_SHORT:
76                return 2;
77            case T_INT:
78            case T_FLOAT:
79                return 4;
80            case T_LONG:
81            case T_DOUBLE:
82                return 8;
83            default:
84                throw new RuntimeException("invalid array element type: " + type);
85        }
86    }
87
88    /*
89     * Java primitive array record (HPROF_GC_PRIM_ARRAY_DUMP) looks
90     * as below:
91     *
92     *    object ID
93     *    stack trace serial number (int)
94     *    number of elements (int)
95     *    element type (byte)
96     *    array data
97     */
98    @Override
99    protected final long readValueLength() throws IOException {
100        long offset = getOffset() + idSize() + 4;
101        // length of the array in elements
102        long len = buf().getInt(offset);
103        // byte length of array
104        return len * elementSize(getElementType());
105    }
106
107    private long dataStartOffset() {
108        return getOffset() + idSize() + 4 + 4 + 1;
109    }
110
111
112    @Override
113    protected final JavaThing[] readValue() throws IOException {
114        int len = getLength();
115        long offset = dataStartOffset();
116
117        JavaThing[] res = new JavaThing[len];
118        synchronized (buf()) {
119            switch (getElementType()) {
120                case 'Z': {
121                              for (int i = 0; i < len; i++) {
122                                  res[i] = new JavaBoolean(booleanAt(offset));
123                                  offset += 1;
124                              }
125                              return res;
126                }
127                case 'B': {
128                              for (int i = 0; i < len; i++) {
129                                  res[i] = new JavaByte(byteAt(offset));
130                                  offset += 1;
131                              }
132                              return res;
133                }
134                case 'C': {
135                              for (int i = 0; i < len; i++) {
136                                  res[i] = new JavaChar(charAt(offset));
137                                  offset += 2;
138                              }
139                              return res;
140                }
141                case 'S': {
142                              for (int i = 0; i < len; i++) {
143                                  res[i] = new JavaShort(shortAt(offset));
144                                  offset += 2;
145                              }
146                              return res;
147                }
148                case 'I': {
149                              for (int i = 0; i < len; i++) {
150                                  res[i] = new JavaInt(intAt(offset));
151                                  offset += 4;
152                              }
153                              return res;
154                }
155                case 'J': {
156                              for (int i = 0; i < len; i++) {
157                                  res[i] = new JavaLong(longAt(offset));
158                                  offset += 8;
159                              }
160                              return res;
161                }
162                case 'F': {
163                              for (int i = 0; i < len; i++) {
164                                  res[i] = new JavaFloat(floatAt(offset));
165                                  offset += 4;
166                              }
167                              return res;
168                }
169                case 'D': {
170                              for (int i = 0; i < len; i++) {
171                                  res[i] = new JavaDouble(doubleAt(offset));
172                                  offset += 8;
173                              }
174                              return res;
175                }
176                default: {
177                             throw new RuntimeException("unknown primitive type?");
178                }
179            }
180        }
181    }
182
183    // JavaClass set only after resolve.
184    private JavaClass clazz;
185
186    // This field contains elementSignature byte and
187    // divider to be used to calculate length. Note that
188    // length of content byte[] is not same as array length.
189    // Actual array length is (byte[].length / divider)
190    private int data;
191
192    // First 8 bits of data is used for element signature
193    private static final int SIGNATURE_MASK = 0x0FF;
194
195    // Next 8 bits of data is used for length divider
196    private static final int LENGTH_DIVIDER_MASK = 0x0FF00;
197
198    // Number of bits to shift to get length divider
199    private static final int LENGTH_DIVIDER_SHIFT = 8;
200
201    public JavaValueArray(byte elementSignature, long offset) {
202        super(offset);
203        this.data = (elementSignature & SIGNATURE_MASK);
204    }
205
206    public JavaClass getClazz() {
207        return clazz;
208    }
209
210    public void visitReferencedObjects(JavaHeapObjectVisitor v) {
211        super.visitReferencedObjects(v);
212    }
213
214    public void resolve(Snapshot snapshot) {
215        if (clazz instanceof JavaClass) {
216            return;
217        }
218        byte elementSig = getElementType();
219        clazz = snapshot.findClass(arrayTypeName(elementSig));
220        if (clazz == null) {
221            clazz = snapshot.getArrayClass("" + ((char) elementSig));
222        }
223        getClazz().addInstance(this);
224        super.resolve(snapshot);
225    }
226
227    public int getLength() {
228        int divider = (data & LENGTH_DIVIDER_MASK) >>> LENGTH_DIVIDER_SHIFT;
229        if (divider == 0) {
230            byte elementSignature = getElementType();
231            switch (elementSignature) {
232            case 'B':
233            case 'Z':
234                divider = 1;
235                break;
236            case 'C':
237            case 'S':
238                divider = 2;
239                break;
240            case 'I':
241            case 'F':
242                divider = 4;
243                break;
244            case 'J':
245            case 'D':
246                divider = 8;
247                break;
248            default:
249                throw new RuntimeException("unknown primitive type: " +
250                                elementSignature);
251            }
252            data |= (divider << LENGTH_DIVIDER_SHIFT);
253        }
254        return (int)(getValueLength() / divider);
255    }
256
257    public JavaThing[] getElements() {
258        return getValue();
259    }
260
261    public byte getElementType() {
262        return (byte) (data & SIGNATURE_MASK);
263    }
264
265    private void checkIndex(int index) {
266        if (index < 0 || index >= getLength()) {
267            throw new ArrayIndexOutOfBoundsException(index);
268        }
269    }
270
271    private void requireType(char type) {
272        if (getElementType() != type) {
273            throw new RuntimeException("not of type : " + type);
274        }
275    }
276
277    public String valueString() {
278        return valueString(true);
279    }
280
281    public String valueString(boolean bigLimit) {
282        // Char arrays deserve special treatment
283        StringBuilder result;
284        JavaThing[] things = getValue();
285        byte elementSignature = getElementType();
286        if (elementSignature == 'C')  {
287            result = new StringBuilder();
288            for (int i = 0; i < things.length; i++) {
289                result.append(things[i]);
290            }
291        } else {
292            int limit = 8;
293            if (bigLimit) {
294                limit = 1000;
295            }
296            result = new StringBuilder("{");
297            for (int i = 0; i < things.length; i++) {
298                if (i > 0) {
299                    result.append(", ");
300                }
301                if (i >= limit) {
302                    result.append("... ");
303                    break;
304                }
305                switch (elementSignature) {
306                    case 'Z': {
307                        boolean val = ((JavaBoolean)things[i]).value;
308                        if (val) {
309                            result.append("true");
310                        } else {
311                            result.append("false");
312                        }
313                        break;
314                    }
315                    case 'B': {
316                        byte val = ((JavaByte)things[i]).value;
317                        result.append("0x").append(Integer.toString(val, 16));
318                        break;
319                    }
320                    case 'S': {
321                        short val = ((JavaShort)things[i]).value;
322                        result.append(val);
323                        break;
324                    }
325                    case 'I': {
326                        int val = ((JavaInt)things[i]).value;
327                        result.append(val);
328                        break;
329                    }
330                    case 'J': {         // long
331                        long val = ((JavaLong)things[i]).value;
332                        result.append(val);
333                        break;
334                    }
335                    case 'F': {
336                        float val = ((JavaFloat)things[i]).value;
337                        result.append(val);
338                        break;
339                    }
340                    case 'D': {         // double
341                        double val = ((JavaDouble)things[i]).value;
342                        result.append(val);
343                        break;
344                    }
345                    default: {
346                        throw new RuntimeException("unknown primitive type?");
347                    }
348                }
349            }
350            result.append('}');
351        }
352        return result.toString();
353    }
354}
355