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