1 /*
  2  * Copyright (c) 1997, 2022, 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 java.io.IOException;
 36 import java.util.Objects;
 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         Objects.checkIndex(index, getLength());
267     }
268 
269     private void requireType(char type) {
270         if (getElementType() != type) {
271             throw new RuntimeException("not of type : " + type);
272         }
273     }
274 
275     public String valueString() {
276         return valueString(true);
277     }
278 
279     public String valueString(boolean bigLimit) {
280         // Char arrays deserve special treatment
281         StringBuilder result;
282         JavaThing[] things = getValue();
283         byte elementSignature = getElementType();
284         if (elementSignature == 'C')  {
285             result = new StringBuilder();
286             for (int i = 0; i < things.length; i++) {
287                 result.append(things[i]);
288             }
289         } else {
290             int limit = 8;
291             if (bigLimit) {
292                 limit = 1000;
293             }
294             result = new StringBuilder("{");
295             for (int i = 0; i < things.length; i++) {
296                 if (i > 0) {
297                     result.append(", ");
298                 }
299                 if (i >= limit) {
300                     result.append("... ");
301                     break;
302                 }
303                 switch (elementSignature) {
304                     case 'Z': {
305                         boolean val = ((JavaBoolean)things[i]).value;
306                         if (val) {
307                             result.append("true");
308                         } else {
309                             result.append("false");
310                         }
311                         break;
312                     }
313                     case 'B': {
314                         byte val = ((JavaByte)things[i]).value;
315                         result.append("0x").append(Integer.toString(val, 16));
316                         break;
317                     }
318                     case 'S': {
319                         short val = ((JavaShort)things[i]).value;
320                         result.append(val);
321                         break;
322                     }
323                     case 'I': {
324                         int val = ((JavaInt)things[i]).value;
325                         result.append(val);
326                         break;
327                     }
328                     case 'J': {         // long
329                         long val = ((JavaLong)things[i]).value;
330                         result.append(val);
331                         break;
332                     }
333                     case 'F': {
334                         float val = ((JavaFloat)things[i]).value;
335                         result.append(val);
336                         break;
337                     }
338                     case 'D': {         // double
339                         double val = ((JavaDouble)things[i]).value;
340                         result.append(val);
341                         break;
342                     }
343                     default: {
344                         throw new RuntimeException("unknown primitive type?");
345                     }
346                 }
347             }
348             result.append('}');
349         }
350         return result.toString();
351     }
352 }