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 
  33 package jdk.test.lib.hprof.model;
  34 
  35 import jdk.test.lib.hprof.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      *    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 }