blob: 6da260966354519cf15ceadb716e2c48f79714d3 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27/*
28 * The contents of this file are subject to the Sun Public License
29 * Version 1.0 (the "License"); you may not use this file except in
30 * compliance with the License. A copy of the License is available at
31 * http://www.sun.com/, and in the file LICENSE.html in the
32 * doc directory.
33 *
34 * The Original Code is HAT. The Initial Developer of the
35 * Original Code is Bill Foote, with contributions from others
36 * at JavaSoft/Sun. Portions created by Bill Foote and others
37 * at Javasoft/Sun are Copyright (C) 1997-2004. All Rights Reserved.
38 *
39 * In addition to the formal license, I ask that you don't
40 * change the history or donations files without permission.
41 *
42 */
43
44package com.sun.tools.hat.internal.model;
45
46import com.sun.tools.hat.internal.parser.ReadBuffer;
47import java.io.IOException;
48
49/**
50 * An array of values, that is, an array of ints, boolean, floats or the like.
51 *
52 * @author Bill Foote
53 */
54public class JavaValueArray extends JavaLazyReadObject
55 /*imports*/ implements ArrayTypeCodes {
56
57 private static String arrayTypeName(byte sig) {
58 switch (sig) {
59 case 'B':
60 return "byte[]";
61 case 'Z':
62 return "boolean[]";
63 case 'C':
64 return "char[]";
65 case 'S':
66 return "short[]";
67 case 'I':
68 return "int[]";
69 case 'F':
70 return "float[]";
71 case 'J':
72 return "long[]";
73 case 'D':
74 return "double[]";
75 default:
76 throw new RuntimeException("invalid array element sig: " + sig);
77 }
78 }
79
80 private static int elementSize(byte type) {
81 switch (type) {
82 case T_BYTE:
83 case T_BOOLEAN:
84 return 1;
85 case T_CHAR:
86 case T_SHORT:
87 return 2;
88 case T_INT:
89 case T_FLOAT:
90 return 4;
91 case T_LONG:
92 case T_DOUBLE:
93 return 8;
94 default:
95 throw new RuntimeException("invalid array element type: " + type);
96 }
97 }
98
99 /*
100 * Java primitive array record (HPROF_GC_PRIM_ARRAY_DUMP) looks
101 * as below:
102 *
103 * object ID
104 * stack trace serial number (int)
105 * length of the instance data (int)
106 * element type (byte)
107 * array data
108 */
109 protected final int readValueLength() throws IOException {
110 JavaClass cl = getClazz();
111 ReadBuffer buf = cl.getReadBuffer();
112 int idSize = cl.getIdentifierSize();
113 long offset = getOffset() + idSize + 4;
114 // length of the array
115 int len = buf.getInt(offset);
116 // typecode of array element type
117 byte type = buf.getByte(offset + 4);
118 return len * elementSize(type);
119 }
120
121 protected final byte[] readValue() throws IOException {
122 JavaClass cl = getClazz();
123 ReadBuffer buf = cl.getReadBuffer();
124 int idSize = cl.getIdentifierSize();
125 long offset = getOffset() + idSize + 4;
126 // length of the array
127 int length = buf.getInt(offset);
128 // typecode of array element type
129 byte type = buf.getByte(offset + 4);
130 if (length == 0) {
131 return Snapshot.EMPTY_BYTE_ARRAY;
132 } else {
133 length *= elementSize(type);
134 byte[] res = new byte[length];
135 buf.get(offset + 5, res);
136 return res;
137 }
138 }
139
140 // JavaClass set only after resolve.
141 private JavaClass clazz;
142
143 // This field contains elementSignature byte and
144 // divider to be used to calculate length. Note that
145 // length of content byte[] is not same as array length.
146 // Actual array length is (byte[].length / divider)
147 private int data;
148
149 // First 8 bits of data is used for element signature
150 private static final int SIGNATURE_MASK = 0x0FF;
151
152 // Next 8 bits of data is used for length divider
153 private static final int LENGTH_DIVIDER_MASK = 0x0FF00;
154
155 // Number of bits to shift to get length divider
156 private static final int LENGTH_DIVIDER_SHIFT = 8;
157
158 public JavaValueArray(byte elementSignature, long offset) {
159 super(offset);
160 this.data = (elementSignature & SIGNATURE_MASK);
161 }
162
163 public JavaClass getClazz() {
164 return clazz;
165 }
166
167 public void visitReferencedObjects(JavaHeapObjectVisitor v) {
168 super.visitReferencedObjects(v);
169 }
170
171 public void resolve(Snapshot snapshot) {
172 if (clazz instanceof JavaClass) {
173 return;
174 }
175 byte elementSig = getElementType();
176 clazz = snapshot.findClass(arrayTypeName(elementSig));
177 if (clazz == null) {
178 clazz = snapshot.getArrayClass("" + ((char) elementSig));
179 }
180 getClazz().addInstance(this);
181 super.resolve(snapshot);
182 }
183
184 public int getLength() {
185 int divider = (data & LENGTH_DIVIDER_MASK) >>> LENGTH_DIVIDER_SHIFT;
186 if (divider == 0) {
187 byte elementSignature = getElementType();
188 switch (elementSignature) {
189 case 'B':
190 case 'Z':
191 divider = 1;
192 break;
193 case 'C':
194 case 'S':
195 divider = 2;
196 break;
197 case 'I':
198 case 'F':
199 divider = 4;
200 break;
201 case 'J':
202 case 'D':
203 divider = 8;
204 break;
205 default:
206 throw new RuntimeException("unknown primitive type: " +
207 elementSignature);
208 }
209 data |= (divider << LENGTH_DIVIDER_SHIFT);
210 }
211 return (getValueLength() / divider);
212 }
213
214 public Object getElements() {
215 final int len = getLength();
216 final byte et = getElementType();
217 byte[] data = getValue();
218 int index = 0;
219 switch (et) {
220 case 'Z': {
221 boolean[] res = new boolean[len];
222 for (int i = 0; i < len; i++) {
223 res[i] = booleanAt(index, data);
224 index++;
225 }
226 return res;
227 }
228 case 'B': {
229 byte[] res = new byte[len];
230 for (int i = 0; i < len; i++) {
231 res[i] = byteAt(index, data);
232 index++;
233 }
234 return res;
235 }
236 case 'C': {
237 char[] res = new char[len];
238 for (int i = 0; i < len; i++) {
239 res[i] = charAt(index, data);
240 index += 2;
241 }
242 return res;
243 }
244 case 'S': {
245 short[] res = new short[len];
246 for (int i = 0; i < len; i++) {
247 res[i] = shortAt(index, data);
248 index += 2;
249 }
250 return res;
251 }
252 case 'I': {
253 int[] res = new int[len];
254 for (int i = 0; i < len; i++) {
255 res[i] = intAt(index, data);
256 index += 4;
257 }
258 return res;
259 }
260 case 'J': {
261 long[] res = new long[len];
262 for (int i = 0; i < len; i++) {
263 res[i] = longAt(index, data);
264 index += 8;
265 }
266 return res;
267 }
268 case 'F': {
269 float[] res = new float[len];
270 for (int i = 0; i < len; i++) {
271 res[i] = floatAt(index, data);
272 index += 4;
273 }
274 return res;
275 }
276 case 'D': {
277 double[] res = new double[len];
278 for (int i = 0; i < len; i++) {
279 res[i] = doubleAt(index, data);
280 index += 8;
281 }
282 return res;
283 }
284 default: {
285 throw new RuntimeException("unknown primitive type?");
286 }
287 }
288 }
289
290 public byte getElementType() {
291 return (byte) (data & SIGNATURE_MASK);
292 }
293
294 private void checkIndex(int index) {
295 if (index < 0 || index >= getLength()) {
296 throw new ArrayIndexOutOfBoundsException(index);
297 }
298 }
299
300 private void requireType(char type) {
301 if (getElementType() != type) {
302 throw new RuntimeException("not of type : " + type);
303 }
304 }
305
306 public boolean getBooleanAt(int index) {
307 checkIndex(index);
308 requireType('Z');
309 return booleanAt(index, getValue());
310 }
311
312 public byte getByteAt(int index) {
313 checkIndex(index);
314 requireType('B');
315 return byteAt(index, getValue());
316 }
317
318 public char getCharAt(int index) {
319 checkIndex(index);
320 requireType('C');
321 return charAt(index << 1, getValue());
322 }
323
324 public short getShortAt(int index) {
325 checkIndex(index);
326 requireType('S');
327 return shortAt(index << 1, getValue());
328 }
329
330 public int getIntAt(int index) {
331 checkIndex(index);
332 requireType('I');
333 return intAt(index << 2, getValue());
334 }
335
336 public long getLongAt(int index) {
337 checkIndex(index);
338 requireType('J');
339 return longAt(index << 3, getValue());
340 }
341
342 public float getFloatAt(int index) {
343 checkIndex(index);
344 requireType('F');
345 return floatAt(index << 2, getValue());
346 }
347
348 public double getDoubleAt(int index) {
349 checkIndex(index);
350 requireType('D');
351 return doubleAt(index << 3, getValue());
352 }
353
354 public String valueString() {
355 return valueString(true);
356 }
357
358 public String valueString(boolean bigLimit) {
359 // Char arrays deserve special treatment
360 StringBuffer result;
361 byte[] value = getValue();
362 int max = value.length;
363 byte elementSignature = getElementType();
364 if (elementSignature == 'C') {
365 result = new StringBuffer();
366 for (int i = 0; i < value.length; ) {
367 char val = charAt(i, value);
368 result.append(val);
369 i += 2;
370 }
371 } else {
372 int limit = 8;
373 if (bigLimit) {
374 limit = 1000;
375 }
376 result = new StringBuffer("{");
377 int num = 0;
378 for (int i = 0; i < value.length; ) {
379 if (num > 0) {
380 result.append(", ");
381 }
382 if (num >= limit) {
383 result.append("... ");
384 break;
385 }
386 num++;
387 switch (elementSignature) {
388 case 'Z': {
389 boolean val = booleanAt(i, value);
390 if (val) {
391 result.append("true");
392 } else {
393 result.append("false");
394 }
395 i++;
396 break;
397 }
398 case 'B': {
399 int val = 0xFF & byteAt(i, value);
400 result.append("0x" + Integer.toString(val, 16));
401 i++;
402 break;
403 }
404 case 'S': {
405 short val = shortAt(i, value);
406 i += 2;
407 result.append("" + val);
408 break;
409 }
410 case 'I': {
411 int val = intAt(i, value);
412 i += 4;
413 result.append("" + val);
414 break;
415 }
416 case 'J': { // long
417 long val = longAt(i, value);
418 result.append("" + val);
419 i += 8;
420 break;
421 }
422 case 'F': {
423 float val = floatAt(i, value);
424 result.append("" + val);
425 i += 4;
426 break;
427 }
428 case 'D': { // double
429 double val = doubleAt(i, value);
430 result.append("" + val);
431 i += 8;
432 break;
433 }
434 default: {
435 throw new RuntimeException("unknown primitive type?");
436 }
437 }
438 }
439 result.append("}");
440 }
441 return result.toString();
442 }
443
444}