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