JavaValueArray.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 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 * length of the instance data (int) 95 * element type (byte) 96 * array data 97 */ 98 protected final int readValueLength() throws IOException { 99 JavaClass cl = getClazz(); 100 ReadBuffer buf = cl.getReadBuffer(); 101 int idSize = cl.getIdentifierSize(); 102 long offset = getOffset() + idSize + 4; 103 // length of the array 104 int len = buf.getInt(offset); 105 // typecode of array element type 106 byte type = buf.getByte(offset + 4); 107 return len * elementSize(type); 108 } 109 110 protected final byte[] readValue() throws IOException { 111 JavaClass cl = getClazz(); 112 ReadBuffer buf = cl.getReadBuffer(); 113 int idSize = cl.getIdentifierSize(); 114 long offset = getOffset() + idSize + 4; 115 // length of the array 116 int length = buf.getInt(offset); 117 // typecode of array element type 118 byte type = buf.getByte(offset + 4); 119 if (length == 0) { 120 return Snapshot.EMPTY_BYTE_ARRAY; 121 } else { 122 length *= elementSize(type); 123 byte[] res = new byte[length]; 124 buf.get(offset + 5, res); 125 return res; 126 } 127 } 128 129 // JavaClass set only after resolve. 130 private JavaClass clazz; 131 132 // This field contains elementSignature byte and 133 // divider to be used to calculate length. Note that 134 // length of content byte[] is not same as array length. 135 // Actual array length is (byte[].length / divider) 136 private int data; 137 138 // First 8 bits of data is used for element signature 139 private static final int SIGNATURE_MASK = 0x0FF; 140 141 // Next 8 bits of data is used for length divider 142 private static final int LENGTH_DIVIDER_MASK = 0x0FF00; 143 144 // Number of bits to shift to get length divider 145 private static final int LENGTH_DIVIDER_SHIFT = 8; 146 147 public JavaValueArray(byte elementSignature, long offset) { 148 super(offset); 149 this.data = (elementSignature & SIGNATURE_MASK); 150 } 151 152 public JavaClass getClazz() { 153 return clazz; 154 } 155 156 public void visitReferencedObjects(JavaHeapObjectVisitor v) { 157 super.visitReferencedObjects(v); 158 } 159 160 public void resolve(Snapshot snapshot) { 161 if (clazz instanceof JavaClass) { 162 return; 163 } 164 byte elementSig = getElementType(); 165 clazz = snapshot.findClass(arrayTypeName(elementSig)); 166 if (clazz == null) { 167 clazz = snapshot.getArrayClass("" + ((char) elementSig)); 168 } 169 getClazz().addInstance(this); 170 super.resolve(snapshot); 171 } 172 173 public int getLength() { 174 int divider = (data & LENGTH_DIVIDER_MASK) >>> LENGTH_DIVIDER_SHIFT; 175 if (divider == 0) { 176 byte elementSignature = getElementType(); 177 switch (elementSignature) { 178 case 'B': 179 case 'Z': 180 divider = 1; 181 break; 182 case 'C': 183 case 'S': 184 divider = 2; 185 break; 186 case 'I': 187 case 'F': 188 divider = 4; 189 break; 190 case 'J': 191 case 'D': 192 divider = 8; 193 break; 194 default: 195 throw new RuntimeException("unknown primitive type: " + 196 elementSignature); 197 } 198 data |= (divider << LENGTH_DIVIDER_SHIFT); 199 } 200 return (getValueLength() / divider); 201 } 202 203 public Object getElements() { 204 final int len = getLength(); 205 final byte et = getElementType(); 206 byte[] data = getValue(); 207 int index = 0; 208 switch (et) { 209 case 'Z': { 210 boolean[] res = new boolean[len]; 211 for (int i = 0; i < len; i++) { 212 res[i] = booleanAt(index, data); 213 index++; 214 } 215 return res; 216 } 217 case 'B': { 218 byte[] res = new byte[len]; 219 for (int i = 0; i < len; i++) { 220 res[i] = byteAt(index, data); 221 index++; 222 } 223 return res; 224 } 225 case 'C': { 226 char[] res = new char[len]; 227 for (int i = 0; i < len; i++) { 228 res[i] = charAt(index, data); 229 index += 2; 230 } 231 return res; 232 } 233 case 'S': { 234 short[] res = new short[len]; 235 for (int i = 0; i < len; i++) { 236 res[i] = shortAt(index, data); 237 index += 2; 238 } 239 return res; 240 } 241 case 'I': { 242 int[] res = new int[len]; 243 for (int i = 0; i < len; i++) { 244 res[i] = intAt(index, data); 245 index += 4; 246 } 247 return res; 248 } 249 case 'J': { 250 long[] res = new long[len]; 251 for (int i = 0; i < len; i++) { 252 res[i] = longAt(index, data); 253 index += 8; 254 } 255 return res; 256 } 257 case 'F': { 258 float[] res = new float[len]; 259 for (int i = 0; i < len; i++) { 260 res[i] = floatAt(index, data); 261 index += 4; 262 } 263 return res; 264 } 265 case 'D': { 266 double[] res = new double[len]; 267 for (int i = 0; i < len; i++) { 268 res[i] = doubleAt(index, data); 269 index += 8; 270 } 271 return res; 272 } 273 default: { 274 throw new RuntimeException("unknown primitive type?"); 275 } 276 } 277 } 278 279 public byte getElementType() { 280 return (byte) (data & SIGNATURE_MASK); 281 } 282 283 private void checkIndex(int index) { 284 if (index < 0 || index >= getLength()) { 285 throw new ArrayIndexOutOfBoundsException(index); 286 } 287 } 288 289 private void requireType(char type) { 290 if (getElementType() != type) { 291 throw new RuntimeException("not of type : " + type); 292 } 293 } 294 295 public boolean getBooleanAt(int index) { 296 checkIndex(index); 297 requireType('Z'); 298 return booleanAt(index, getValue()); 299 } 300 301 public byte getByteAt(int index) { 302 checkIndex(index); 303 requireType('B'); 304 return byteAt(index, getValue()); 305 } 306 307 public char getCharAt(int index) { 308 checkIndex(index); 309 requireType('C'); 310 return charAt(index << 1, getValue()); 311 } 312 313 public short getShortAt(int index) { 314 checkIndex(index); 315 requireType('S'); 316 return shortAt(index << 1, getValue()); 317 } 318 319 public int getIntAt(int index) { 320 checkIndex(index); 321 requireType('I'); 322 return intAt(index << 2, getValue()); 323 } 324 325 public long getLongAt(int index) { 326 checkIndex(index); 327 requireType('J'); 328 return longAt(index << 3, getValue()); 329 } 330 331 public float getFloatAt(int index) { 332 checkIndex(index); 333 requireType('F'); 334 return floatAt(index << 2, getValue()); 335 } 336 337 public double getDoubleAt(int index) { 338 checkIndex(index); 339 requireType('D'); 340 return doubleAt(index << 3, getValue()); 341 } 342 343 public String valueString() { 344 return valueString(true); 345 } 346 347 public String valueString(boolean bigLimit) { 348 // Char arrays deserve special treatment 349 StringBuilder result; 350 byte[] value = getValue(); 351 int max = value.length; 352 byte elementSignature = getElementType(); 353 if (elementSignature == 'C') { 354 result = new StringBuilder(); 355 for (int i = 0; i < value.length; ) { 356 char val = charAt(i, value); 357 result.append(val); 358 i += 2; 359 } 360 } else { 361 int limit = 8; 362 if (bigLimit) { 363 limit = 1000; 364 } 365 result = new StringBuilder("{"); 366 int num = 0; 367 for (int i = 0; i < value.length; ) { 368 if (num > 0) { 369 result.append(", "); 370 } 371 if (num >= limit) { 372 result.append("... "); 373 break; 374 } 375 num++; 376 switch (elementSignature) { 377 case 'Z': { 378 boolean val = booleanAt(i, value); 379 if (val) { 380 result.append("true"); 381 } else { 382 result.append("false"); 383 } 384 i++; 385 break; 386 } 387 case 'B': { 388 int val = 0xFF & byteAt(i, value); 389 result.append("0x").append(Integer.toString(val, 16)); 390 i++; 391 break; 392 } 393 case 'S': { 394 short val = shortAt(i, value); 395 i += 2; 396 result.append(val); 397 break; 398 } 399 case 'I': { 400 int val = intAt(i, value); 401 i += 4; 402 result.append(val); 403 break; 404 } 405 case 'J': { // long 406 long val = longAt(i, value); 407 result.append(val); 408 i += 8; 409 break; 410 } 411 case 'F': { 412 float val = floatAt(i, value); 413 result.append(val); 414 i += 4; 415 break; 416 } 417 case 'D': { // double 418 double val = doubleAt(i, value); 419 result.append(val); 420 i += 8; 421 break; 422 } 423 default: { 424 throw new RuntimeException("unknown primitive type?"); 425 } 426 } 427 } 428 result.append('}'); 429 } 430 return result.toString(); 431 } 432 433} 434