1 /*
2  * Copyright (c) 2009, 2021, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 package jdk.vm.ci.meta;
24 
25 import java.lang.reflect.Array;
26 
27 //JaCoCo Exclude
28 
29 /**
30  * Denotes the basic kinds of types in CRI, including the all the Java primitive types, for example,
31  * {@link JavaKind#Int} for {@code int} and {@link JavaKind#Object} for all object types. A kind has
32  * a single character short name, a Java name, and a set of flags further describing its behavior.
33  */
34 public enum JavaKind {
35     /** The primitive boolean kind, represented as an int on the stack. */
36     Boolean('Z', 4, "boolean", 1, true, java.lang.Boolean.TYPE, java.lang.Boolean.class),
37 
38     /** The primitive byte kind, represented as an int on the stack. */
39     Byte('B', 8, "byte", 1, true, java.lang.Byte.TYPE, java.lang.Byte.class),
40 
41     /** The primitive short kind, represented as an int on the stack. */
42     Short('S', 9, "short", 1, true, java.lang.Short.TYPE, java.lang.Short.class),
43 
44     /** The primitive char kind, represented as an int on the stack. */
45     Char('C', 5, "char", 1, true, java.lang.Character.TYPE, java.lang.Character.class),
46 
47     /** The primitive int kind, represented as an int on the stack. */
48     Int('I', 10, "int", 1, true, java.lang.Integer.TYPE, java.lang.Integer.class),
49 
50     /** The primitive float kind. */
51     Float('F', 6, "float", 1, false, java.lang.Float.TYPE, java.lang.Float.class),
52 
53     /** The primitive long kind. */
54     Long('J', 11, "long", 2, false, java.lang.Long.TYPE, java.lang.Long.class),
55 
56     /** The primitive double kind. */
57     Double('D', 7, "double", 2, false, java.lang.Double.TYPE, java.lang.Double.class),
58 
59     /** The Object kind, also used for arrays. */
60     Object('A', 12, "Object", 1, false, null, null),
61 
62     /** The void kind. */
63     Void('V', 14, "void", 0, false, java.lang.Void.TYPE, java.lang.Void.class),
64 
65     /** The non-type. */
66     Illegal('-', 99, "illegal", 0, false, null, null);
67 
68     private final char typeChar;
69     private final String javaName;
70     private final boolean isStackInt;
71     private final Class<?> primitiveJavaClass;
72     private final Class<?> boxedJavaClass;
73     private final int slotCount;
74     private final int basicType;
75 
JavaKind(char typeChar, int basicType, String javaName, int slotCount, boolean isStackInt, Class<?> primitiveJavaClass, Class<?> boxedJavaClass)76     JavaKind(char typeChar, int basicType, String javaName, int slotCount, boolean isStackInt, Class<?> primitiveJavaClass, Class<?> boxedJavaClass) {
77         this.typeChar = typeChar;
78         this.javaName = javaName;
79         this.slotCount = slotCount;
80         this.isStackInt = isStackInt;
81         this.primitiveJavaClass = primitiveJavaClass;
82         this.boxedJavaClass = boxedJavaClass;
83         this.basicType = basicType;
84         assert primitiveJavaClass == null || javaName.equals(primitiveJavaClass.getName());
85     }
86 
87     /**
88      * Returns the number of stack slots occupied by this kind according to the Java bytecodes
89      * specification.
90      */
getSlotCount()91     public int getSlotCount() {
92         return this.slotCount;
93     }
94 
95     /**
96      * Returns whether this kind occupied two stack slots.
97      */
needsTwoSlots()98     public boolean needsTwoSlots() {
99         return this.slotCount == 2;
100     }
101 
102     /**
103      * Returns the name of the kind as a single upper case character. For the void and primitive
104      * kinds, this is the <i>FieldType</i> term in
105      * <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.2-200">
106      * table 4.3-A</a> of the JVM Specification. For {@link #Object}, the character {@code 'A'} is
107      * returned and for {@link #Illegal}, {@code '-'} is returned.
108      */
getTypeChar()109     public char getTypeChar() {
110         return typeChar;
111     }
112 
113     /**
114      * Returns the JVM BasicType value for this type.
115      */
getBasicType()116     public int getBasicType() {
117         return basicType;
118     }
119 
120     /**
121      * Returns the name of this kind which will also be it Java programming language name if it is
122      * {@linkplain #isPrimitive() primitive} or {@code void}.
123      */
getJavaName()124     public String getJavaName() {
125         return javaName;
126     }
127 
128     /**
129      * Checks whether this type is a Java primitive type.
130      *
131      * @return {@code true} if this is {@link #Boolean}, {@link #Byte}, {@link #Char},
132      *         {@link #Short}, {@link #Int}, {@link #Long}, {@link #Float}, {@link #Double}, or
133      *         {@link #Void}.
134      */
isPrimitive()135     public boolean isPrimitive() {
136         return primitiveJavaClass != null;
137     }
138 
139     /**
140      * Returns the kind that represents this kind when on the Java operand stack.
141      *
142      * @return the kind used on the operand stack
143      */
getStackKind()144     public JavaKind getStackKind() {
145         if (isStackInt) {
146             return Int;
147         }
148         return this;
149     }
150 
151     /**
152      * Checks whether this type is a Java primitive type representing an integer number.
153      *
154      * @return {@code true} if the stack kind is {@link #Int} or {@link #Long}.
155      */
isNumericInteger()156     public boolean isNumericInteger() {
157         return isStackInt || this == JavaKind.Long;
158     }
159 
160     /**
161      * Checks whether this type is a Java primitive type representing an unsigned number.
162      *
163      * @return {@code true} if the kind is {@link #Boolean} or {@link #Char}.
164      */
isUnsigned()165     public boolean isUnsigned() {
166         return this == JavaKind.Boolean || this == JavaKind.Char;
167     }
168 
169     /**
170      * Checks whether this type is a Java primitive type representing a floating point number.
171      *
172      * @return {@code true} if this is {@link #Float} or {@link #Double}.
173      */
isNumericFloat()174     public boolean isNumericFloat() {
175         return this == JavaKind.Float || this == JavaKind.Double;
176     }
177 
178     /**
179      * Checks whether this represent an Object of some sort.
180      *
181      * @return {@code true} if this is {@link #Object}.
182      */
isObject()183     public boolean isObject() {
184         return this == JavaKind.Object;
185     }
186 
187     /**
188      * Returns the kind corresponding to the Java type string.
189      *
190      * @param typeString the Java type string
191      * @return the kind
192      */
fromTypeString(String typeString)193     public static JavaKind fromTypeString(String typeString) {
194         assert typeString.length() > 0;
195         final char first = typeString.charAt(0);
196         if (first == '[' || first == 'L') {
197             return JavaKind.Object;
198         }
199         return JavaKind.fromPrimitiveOrVoidTypeChar(first);
200     }
201 
202     /**
203      * Returns the kind of a word given the size of a word in bytes.
204      *
205      * @param wordSizeInBytes the size of a word in bytes
206      * @return the kind representing a word value
207      */
fromWordSize(int wordSizeInBytes)208     public static JavaKind fromWordSize(int wordSizeInBytes) {
209         if (wordSizeInBytes == 8) {
210             return JavaKind.Long;
211         } else {
212             assert wordSizeInBytes == 4 : "Unsupported word size!";
213             return JavaKind.Int;
214         }
215     }
216 
217     /**
218      * Returns the kind from the character describing a primitive or void.
219      *
220      * @param ch the character for a void or primitive kind as returned by {@link #getTypeChar()}
221      * @return the kind
222      */
fromPrimitiveOrVoidTypeChar(char ch)223     public static JavaKind fromPrimitiveOrVoidTypeChar(char ch) {
224         switch (ch) {
225             case 'Z':
226                 return Boolean;
227             case 'C':
228                 return Char;
229             case 'F':
230                 return Float;
231             case 'D':
232                 return Double;
233             case 'B':
234                 return Byte;
235             case 'S':
236                 return Short;
237             case 'I':
238                 return Int;
239             case 'J':
240                 return Long;
241             case 'V':
242                 return Void;
243         }
244         throw new IllegalArgumentException("unknown primitive or void type character: " + ch);
245     }
246 
247     /**
248      * Returns the Kind representing the given Java class.
249      *
250      * @param klass the class
251      * @return the kind
252      */
fromJavaClass(Class<?> klass)253     public static JavaKind fromJavaClass(Class<?> klass) {
254         if (klass == Boolean.primitiveJavaClass) {
255             return Boolean;
256         } else if (klass == Byte.primitiveJavaClass) {
257             return Byte;
258         } else if (klass == Short.primitiveJavaClass) {
259             return Short;
260         } else if (klass == Char.primitiveJavaClass) {
261             return Char;
262         } else if (klass == Int.primitiveJavaClass) {
263             return Int;
264         } else if (klass == Long.primitiveJavaClass) {
265             return Long;
266         } else if (klass == Float.primitiveJavaClass) {
267             return Float;
268         } else if (klass == Double.primitiveJavaClass) {
269             return Double;
270         } else if (klass == Void.primitiveJavaClass) {
271             return Void;
272         } else {
273             return Object;
274         }
275     }
276 
277     /**
278      * Returns the Java class representing this kind.
279      *
280      * @return the Java class
281      */
toJavaClass()282     public Class<?> toJavaClass() {
283         return primitiveJavaClass;
284     }
285 
286     /**
287      * Returns the Java class for instances of boxed values of this kind.
288      *
289      * @return the Java class
290      */
toBoxedJavaClass()291     public Class<?> toBoxedJavaClass() {
292         return boxedJavaClass;
293     }
294 
295     /**
296      * Converts this value type to a string.
297      */
298     @Override
toString()299     public String toString() {
300         return javaName;
301     }
302 
303     /**
304      * Marker interface for types that should be {@linkplain JavaKind#format(Object) formatted} with
305      * their {@link Object#toString()} value. Calling {@link Object#toString()} on other objects
306      * poses a security risk because it can potentially call user code.
307      */
308     public interface FormatWithToString {
309     }
310 
311     /**
312      * Classes for which invoking {@link Object#toString()} does not run user code.
313      */
isToStringSafe(Class<?> c)314     private static boolean isToStringSafe(Class<?> c) {
315         return c == Boolean.class || c == Byte.class || c == Character.class || c == Short.class || c == Integer.class || c == Float.class || c == Long.class || c == Double.class;
316     }
317 
318     /**
319      * Gets a formatted string for a given value of this kind.
320      *
321      * @param value a value of this kind
322      * @return a formatted string for {@code value} based on this kind
323      */
format(Object value)324     public String format(Object value) {
325         if (isPrimitive()) {
326             assert isToStringSafe(value.getClass());
327             return value.toString();
328         } else {
329             if (value == null) {
330                 return "null";
331             } else {
332                 if (value instanceof String) {
333                     String s = (String) value;
334                     if (s.length() > 50) {
335                         return "String:\"" + s.substring(0, 30) + "...\"";
336                     } else {
337                         return "String:\"" + s + '"';
338                     }
339                 } else if (value instanceof JavaType) {
340                     return "JavaType:" + ((JavaType) value).toJavaName();
341                 } else if (value instanceof Enum) {
342                     return MetaUtil.getSimpleName(value.getClass(), true) + ":" + ((Enum<?>) value).name();
343                 } else if (value instanceof FormatWithToString) {
344                     return MetaUtil.getSimpleName(value.getClass(), true) + ":" + String.valueOf(value);
345                 } else if (value instanceof Class<?>) {
346                     return "Class:" + ((Class<?>) value).getName();
347                 } else if (isToStringSafe(value.getClass())) {
348                     return value.toString();
349                 } else if (value.getClass().isArray()) {
350                     return formatArray(value);
351                 } else {
352                     return MetaUtil.getSimpleName(value.getClass(), true) + "@" + System.identityHashCode(value);
353                 }
354             }
355         }
356     }
357 
358     private static final int MAX_FORMAT_ARRAY_LENGTH = 5;
359 
formatArray(Object array)360     private static String formatArray(Object array) {
361         Class<?> componentType = array.getClass().getComponentType();
362         assert componentType != null;
363         int arrayLength = Array.getLength(array);
364         StringBuilder buf = new StringBuilder(MetaUtil.getSimpleName(componentType, true)).append('[').append(arrayLength).append("]{");
365         int length = Math.min(MAX_FORMAT_ARRAY_LENGTH, arrayLength);
366         boolean primitive = componentType.isPrimitive();
367         for (int i = 0; i < length; i++) {
368             if (primitive) {
369                 buf.append(Array.get(array, i));
370             } else {
371                 Object o = ((Object[]) array)[i];
372                 buf.append(JavaKind.Object.format(o));
373             }
374             if (i != length - 1) {
375                 buf.append(", ");
376             }
377         }
378         if (arrayLength != length) {
379             buf.append(", ...");
380         }
381         return buf.append('}').toString();
382     }
383 
384     /**
385      * Gets the minimum value that can be represented as a value of this kind.
386      *
387      * @return the minimum value represented as a {@code long}
388      */
getMinValue()389     public long getMinValue() {
390         switch (this) {
391             case Boolean:
392                 return 0;
393             case Byte:
394                 return java.lang.Byte.MIN_VALUE;
395             case Char:
396                 return java.lang.Character.MIN_VALUE;
397             case Short:
398                 return java.lang.Short.MIN_VALUE;
399             case Int:
400                 return java.lang.Integer.MIN_VALUE;
401             case Long:
402                 return java.lang.Long.MIN_VALUE;
403             case Float:
404                 return java.lang.Float.floatToRawIntBits(java.lang.Float.MIN_VALUE);
405             case Double:
406                 return java.lang.Double.doubleToRawLongBits(java.lang.Double.MIN_VALUE);
407             default:
408                 throw new IllegalArgumentException("illegal call to minValue on " + this);
409         }
410     }
411 
412     /**
413      * Gets the maximum value that can be represented as a value of this kind.
414      *
415      * @return the maximum value represented as a {@code long}
416      */
getMaxValue()417     public long getMaxValue() {
418         switch (this) {
419             case Boolean:
420                 return 1;
421             case Byte:
422                 return java.lang.Byte.MAX_VALUE;
423             case Char:
424                 return java.lang.Character.MAX_VALUE;
425             case Short:
426                 return java.lang.Short.MAX_VALUE;
427             case Int:
428                 return java.lang.Integer.MAX_VALUE;
429             case Long:
430                 return java.lang.Long.MAX_VALUE;
431             case Float:
432                 return java.lang.Float.floatToRawIntBits(java.lang.Float.MAX_VALUE);
433             case Double:
434                 return java.lang.Double.doubleToRawLongBits(java.lang.Double.MAX_VALUE);
435             default:
436                 throw new IllegalArgumentException("illegal call to maxValue on " + this);
437         }
438     }
439 
440     /**
441      * Number of bytes that are necessary to represent a value of this kind.
442      *
443      * @return the number of bytes
444      */
getByteCount()445     public int getByteCount() {
446         if (this == Boolean) {
447             return 1;
448         } else {
449             return getBitCount() >> 3;
450         }
451     }
452 
453     /**
454      * Number of bits that are necessary to represent a value of this kind.
455      *
456      * @return the number of bits
457      */
getBitCount()458     public int getBitCount() {
459         switch (this) {
460             case Boolean:
461                 return 1;
462             case Byte:
463                 return 8;
464             case Char:
465             case Short:
466                 return 16;
467             case Float:
468                 return 32;
469             case Int:
470                 return 32;
471             case Double:
472                 return 64;
473             case Long:
474                 return 64;
475             default:
476                 throw new IllegalArgumentException("illegal call to getBitCount() on " + this);
477         }
478     }
479 }
480