1 /*
   2  * Copyright (c) 1997, 2008, 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 
  33 package com.sun.tools.hat.internal.model;
  34 
  35 import com.sun.tools.hat.internal.parser.ReadBuffer;
  36 import 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  */
  43 public 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 }