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 import java.util.Objects;
 38 
 39 /**
 40  * An array of values, that is, an array of ints, boolean, floats or the like.
 41  *
 42  * @author      Bill Foote
 43  */
 44 public class JavaValueArray extends JavaLazyReadObject
 45                 /*imports*/ implements ArrayTypeCodes {
 46 
 47     private static String arrayTypeName(byte sig) {
 48         switch (sig) {
 49             case 'B':
 50                 return "byte[]";
 51             case 'Z':
 52                 return "boolean[]";
 53             case 'C':
 54                 return "char[]";
 55             case 'S':
 56                 return "short[]";
 57             case 'I':
 58                 return "int[]";
 59             case 'F':
 60                 return "float[]";
 61             case 'J':
 62                 return "long[]";
 63             case 'D':
 64                 return "double[]";
 65             default:
 66                 throw new RuntimeException("invalid array element sig: " + sig);
 67         }
 68     }
 69 
 70     private static int elementSize(byte type) {
 71         switch (type) {
 72             case T_BYTE:
 73             case T_BOOLEAN:
 74                 return 1;
 75             case T_CHAR:
 76             case T_SHORT:
 77                 return 2;
 78             case T_INT:
 79             case T_FLOAT:
 80                 return 4;
 81             case T_LONG:
 82             case T_DOUBLE:
 83                 return 8;
 84             default:
 85                 throw new RuntimeException("invalid array element type: " + type);
 86         }
 87     }
 88 
 89     /*
 90      * Java primitive array record (HPROF_GC_PRIM_ARRAY_DUMP) looks
 91      * as below:
 92      *
 93      *    object ID
 94      *    stack trace serial number (int)
 95      *    number of elements (int)
 96      *    element type (byte)
 97      *    array data
 98      */
 99     @Override
100     protected final long readValueLength() throws IOException {
101         long offset = getOffset() + idSize() + 4;
102         // length of the array in elements
103         long len = buf().getInt(offset);
104         // byte length of array
105         return len * elementSize(getElementType());
106     }
107 
108     private long dataStartOffset() {
109         return getOffset() + idSize() + 4 + 4 + 1;
110     }
111 
112 
113     @Override
114     protected final JavaThing[] readValue() throws IOException {
115         int len = getLength();
116         long offset = dataStartOffset();
117 
118         JavaThing[] res = new JavaThing[len];
119         synchronized (buf()) {
120             switch (getElementType()) {
121                 case 'Z': {
122                               for (int i = 0; i < len; i++) {
123                                   res[i] = new JavaBoolean(booleanAt(offset));
124                                   offset += 1;
125                               }
126                               return res;
127                 }
128                 case 'B': {
129                               for (int i = 0; i < len; i++) {
130                                   res[i] = new JavaByte(byteAt(offset));
131                                   offset += 1;
132                               }
133                               return res;
134                 }
135                 case 'C': {
136                               for (int i = 0; i < len; i++) {
137                                   res[i] = new JavaChar(charAt(offset));
138                                   offset += 2;
139                               }
140                               return res;
141                 }
142                 case 'S': {
143                               for (int i = 0; i < len; i++) {
144                                   res[i] = new JavaShort(shortAt(offset));
145                                   offset += 2;
146                               }
147                               return res;
148                 }
149                 case 'I': {
150                               for (int i = 0; i < len; i++) {
151                                   res[i] = new JavaInt(intAt(offset));
152                                   offset += 4;
153                               }
154                               return res;
155                 }
156                 case 'J': {
157                               for (int i = 0; i < len; i++) {
158                                   res[i] = new JavaLong(longAt(offset));
159                                   offset += 8;
160                               }
161                               return res;
162                 }
163                 case 'F': {
164                               for (int i = 0; i < len; i++) {
165                                   res[i] = new JavaFloat(floatAt(offset));
166                                   offset += 4;
167                               }
168                               return res;
169                 }
170                 case 'D': {
171                               for (int i = 0; i < len; i++) {
172                                   res[i] = new JavaDouble(doubleAt(offset));
173                                   offset += 8;
174                               }
175                               return res;
176                 }
177                 default: {
178                              throw new RuntimeException("unknown primitive type?");
179                 }
180             }
181         }
182     }
183 
184     // JavaClass set only after resolve.
185     private JavaClass clazz;
186 
187     // This field contains elementSignature byte and
188     // divider to be used to calculate length. Note that
189     // length of content byte[] is not same as array length.
190     // Actual array length is (byte[].length / divider)
191     private int data;
192 
193     // First 8 bits of data is used for element signature
194     private static final int SIGNATURE_MASK = 0x0FF;
195 
196     // Next 8 bits of data is used for length divider
197     private static final int LENGTH_DIVIDER_MASK = 0x0FF00;
198 
199     // Number of bits to shift to get length divider
200     private static final int LENGTH_DIVIDER_SHIFT = 8;
201 
202     public JavaValueArray(byte elementSignature, long offset) {
203         super(offset);
204         this.data = (elementSignature & SIGNATURE_MASK);
205     }
206 
207     public JavaClass getClazz() {
208         return clazz;
209     }
210 
211     public void visitReferencedObjects(JavaHeapObjectVisitor v) {
212         super.visitReferencedObjects(v);
213     }
214 
215     public void resolve(Snapshot snapshot) {
216         if (clazz instanceof JavaClass) {
217             return;
218         }
219         byte elementSig = getElementType();
220         clazz = snapshot.findClass(arrayTypeName(elementSig));
221         if (clazz == null) {
222             clazz = snapshot.getArrayClass("" + ((char) elementSig));
223         }
224         getClazz().addInstance(this);
225         super.resolve(snapshot);
226     }
227 
228     public int getLength() {
229         int divider = (data & LENGTH_DIVIDER_MASK) >>> LENGTH_DIVIDER_SHIFT;
230         if (divider == 0) {
231             byte elementSignature = getElementType();
232             switch (elementSignature) {
233             case 'B':
234             case 'Z':
235                 divider = 1;
236                 break;
237             case 'C':
238             case 'S':
239                 divider = 2;
240                 break;
241             case 'I':
242             case 'F':
243                 divider = 4;
244                 break;
245             case 'J':
246             case 'D':
247                 divider = 8;
248                 break;
249             default:
250                 throw new RuntimeException("unknown primitive type: " +
251                                 elementSignature);
252             }
253             data |= (divider << LENGTH_DIVIDER_SHIFT);
254         }
255         return (int)(getValueLength() / divider);
256     }
257 
258     public JavaThing[] getElements() {
259         return getValue();
260     }
261 
262     public byte getElementType() {
263         return (byte) (data & SIGNATURE_MASK);
264     }
265 
266     private void checkIndex(int index) {
267         Objects.checkIndex(index, getLength());
268     }
269 
270     private void requireType(char type) {
271         if (getElementType() != type) {
272             throw new RuntimeException("not of type : " + type);
273         }
274     }
275 
276     public String valueString() {
277         return valueString(true);
278     }
279 
280     public String valueString(boolean bigLimit) {
281         // Char arrays deserve special treatment
282         StringBuilder result;
283         JavaThing[] things = getValue();
284         byte elementSignature = getElementType();
285         if (elementSignature == 'C')  {
286             result = new StringBuilder();
287             for (int i = 0; i < things.length; i++) {
288                 result.append(things[i]);
289             }
290         } else {
291             int limit = 8;
292             if (bigLimit) {
293                 limit = 1000;
294             }
295             result = new StringBuilder("{");
296             for (int i = 0; i < things.length; i++) {
297                 if (i > 0) {
298                     result.append(", ");
299                 }
300                 if (i >= limit) {
301                     result.append("... ");
302                     break;
303                 }
304                 switch (elementSignature) {
305                     case 'Z': {
306                         boolean val = ((JavaBoolean)things[i]).value;
307                         if (val) {
308                             result.append("true");
309                         } else {
310                             result.append("false");
311                         }
312                         break;
313                     }
314                     case 'B': {
315                         byte val = ((JavaByte)things[i]).value;
316                         result.append("0x").append(Integer.toString(val, 16));
317                         break;
318                     }
319                     case 'S': {
320                         short val = ((JavaShort)things[i]).value;
321                         result.append(val);
322                         break;
323                     }
324                     case 'I': {
325                         int val = ((JavaInt)things[i]).value;
326                         result.append(val);
327                         break;
328                     }
329                     case 'J': {         // long
330                         long val = ((JavaLong)things[i]).value;
331                         result.append(val);
332                         break;
333                     }
334                     case 'F': {
335                         float val = ((JavaFloat)things[i]).value;
336                         result.append(val);
337                         break;
338                     }
339                     case 'D': {         // double
340                         double val = ((JavaDouble)things[i]).value;
341                         result.append(val);
342                         break;
343                     }
344                     default: {
345                         throw new RuntimeException("unknown primitive type?");
346                     }
347                 }
348             }
349             result.append('}');
350         }
351         return result.toString();
352     }
353 }