001/*
002 * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package jdk.internal.jvmci.meta;
024
025import java.lang.reflect.*;
026
027//JaCoCo Exclude
028
029/**
030 * Denotes the basic kinds of types in CRI, including the all the Java primitive types, for example,
031 * {@link Kind#Int} for {@code int} and {@link Kind#Object} for all object types. A kind has a
032 * single character short name, a Java name, and a set of flags further describing its behavior.
033 */
034public enum Kind implements PlatformKind {
035    /** The primitive boolean kind, represented as an int on the stack. */
036    Boolean('z', "boolean", 1, true, java.lang.Boolean.TYPE, java.lang.Boolean.class),
037
038    /** The primitive byte kind, represented as an int on the stack. */
039    Byte('b', "byte", 1, true, java.lang.Byte.TYPE, java.lang.Byte.class),
040
041    /** The primitive short kind, represented as an int on the stack. */
042    Short('s', "short", 1, true, java.lang.Short.TYPE, java.lang.Short.class),
043
044    /** The primitive char kind, represented as an int on the stack. */
045    Char('c', "char", 1, true, java.lang.Character.TYPE, java.lang.Character.class),
046
047    /** The primitive int kind, represented as an int on the stack. */
048    Int('i', "int", 1, true, java.lang.Integer.TYPE, java.lang.Integer.class),
049
050    /** The primitive float kind. */
051    Float('f', "float", 1, false, java.lang.Float.TYPE, java.lang.Float.class),
052
053    /** The primitive long kind. */
054    Long('j', "long", 2, false, java.lang.Long.TYPE, java.lang.Long.class),
055
056    /** The primitive double kind. */
057    Double('d', "double", 2, false, java.lang.Double.TYPE, java.lang.Double.class),
058
059    /** The Object kind, also used for arrays. */
060    Object('a', "Object", 1, false, null, null),
061
062    /** The void float kind. */
063    Void('v', "void", 0, false, java.lang.Void.TYPE, java.lang.Void.class),
064
065    /** The non-type. */
066    Illegal('-', "illegal", 0, false, null, null);
067
068    private final char typeChar;
069    private final String javaName;
070    private final boolean isStackInt;
071    private final Class<?> primitiveJavaClass;
072    private final Class<?> boxedJavaClass;
073    private final EnumKey key = new EnumKey(this);
074    private final int slotCount;
075
076    private Kind(char typeChar, String javaName, int slotCount, boolean isStackInt, Class<?> primitiveJavaClass, Class<?> boxedJavaClass) {
077        this.typeChar = typeChar;
078        this.javaName = javaName;
079        this.slotCount = slotCount;
080        this.isStackInt = isStackInt;
081        this.primitiveJavaClass = primitiveJavaClass;
082        this.boxedJavaClass = boxedJavaClass;
083        assert primitiveJavaClass == null || javaName.equals(primitiveJavaClass.getName());
084    }
085
086    /**
087     * Returns the number of stack slots occupied by this kind according to the Java bytecodes
088     * specification.
089     */
090    public int getSlotCount() {
091        return this.slotCount;
092    }
093
094    /**
095     * Returns whether this kind occupied two stack slots.
096     */
097    public boolean needsTwoSlots() {
098        return this.slotCount == 2;
099    }
100
101    /**
102     * Returns the name of the kind as a single character.
103     */
104    public char getTypeChar() {
105        return typeChar;
106    }
107
108    /**
109     * Returns the name of this kind which will also be it Java programming language name if it is
110     * {@linkplain #isPrimitive() primitive} or {@code void}.
111     */
112    public String getJavaName() {
113        return javaName;
114    }
115
116    public Key getKey() {
117        return key;
118    }
119
120    /**
121     * Checks whether this type is a Java primitive type.
122     *
123     * @return {@code true} if this is {@link #Boolean}, {@link #Byte}, {@link #Char},
124     *         {@link #Short}, {@link #Int}, {@link #Long}, {@link #Float}, {@link #Double}, or
125     *         {@link #Void}.
126     */
127    public boolean isPrimitive() {
128        return primitiveJavaClass != null;
129    }
130
131    /**
132     * Returns the kind that represents this kind when on the Java operand stack.
133     *
134     * @return the kind used on the operand stack
135     */
136    public Kind getStackKind() {
137        if (isStackInt) {
138            return Int;
139        }
140        return this;
141    }
142
143    /**
144     * Checks whether this type is a Java primitive type representing an integer number.
145     *
146     * @return {@code true} if the stack kind is {@link #Int} or {@link #Long}.
147     */
148    public boolean isNumericInteger() {
149        return isStackInt || this == Kind.Long;
150    }
151
152    /**
153     * Checks whether this type is a Java primitive type representing an unsigned number.
154     *
155     * @return {@code true} if the kind is {@link #Boolean} or {@link #Char}.
156     */
157    public boolean isUnsigned() {
158        return this == Kind.Boolean || this == Kind.Char;
159    }
160
161    /**
162     * Checks whether this type is a Java primitive type representing a floating point number.
163     *
164     * @return {@code true} if this is {@link #Float} or {@link #Double}.
165     */
166    public boolean isNumericFloat() {
167        return this == Kind.Float || this == Kind.Double;
168    }
169
170    /**
171     * Checks whether this represent an Object of some sort.
172     *
173     * @return {@code true} if this is {@link #Object}.
174     */
175    public boolean isObject() {
176        return this == Kind.Object;
177    }
178
179    /**
180     * Returns the kind corresponding to the Java type string.
181     *
182     * @param typeString the Java type string
183     * @return the kind
184     */
185    public static Kind fromTypeString(String typeString) {
186        assert typeString.length() > 0;
187        final char first = typeString.charAt(0);
188        if (first == '[' || first == 'L') {
189            return Kind.Object;
190        }
191        return Kind.fromPrimitiveOrVoidTypeChar(first);
192    }
193
194    /**
195     * Returns the kind of a word given the size of a word in bytes.
196     *
197     * @param wordSizeInBytes the size of a word in bytes
198     * @return the kind representing a word value
199     */
200    public static Kind fromWordSize(int wordSizeInBytes) {
201        if (wordSizeInBytes == 8) {
202            return Kind.Long;
203        } else {
204            assert wordSizeInBytes == 4 : "Unsupported word size!";
205            return Kind.Int;
206        }
207    }
208
209    /**
210     * Returns the kind from the character describing a primitive or void.
211     *
212     * @param ch the character
213     * @return the kind
214     */
215    public static Kind fromPrimitiveOrVoidTypeChar(char ch) {
216        switch (ch) {
217            case 'Z':
218                return Boolean;
219            case 'C':
220                return Char;
221            case 'F':
222                return Float;
223            case 'D':
224                return Double;
225            case 'B':
226                return Byte;
227            case 'S':
228                return Short;
229            case 'I':
230                return Int;
231            case 'J':
232                return Long;
233            case 'V':
234                return Void;
235        }
236        throw new IllegalArgumentException("unknown primitive or void type character: " + ch);
237    }
238
239    /**
240     * Returns the Kind representing the given Java class.
241     *
242     * @param klass the class
243     * @return the kind
244     */
245    public static Kind fromJavaClass(Class<?> klass) {
246        if (klass == Boolean.primitiveJavaClass) {
247            return Boolean;
248        } else if (klass == Byte.primitiveJavaClass) {
249            return Byte;
250        } else if (klass == Short.primitiveJavaClass) {
251            return Short;
252        } else if (klass == Char.primitiveJavaClass) {
253            return Char;
254        } else if (klass == Int.primitiveJavaClass) {
255            return Int;
256        } else if (klass == Long.primitiveJavaClass) {
257            return Long;
258        } else if (klass == Float.primitiveJavaClass) {
259            return Float;
260        } else if (klass == Double.primitiveJavaClass) {
261            return Double;
262        } else if (klass == Void.primitiveJavaClass) {
263            return Void;
264        } else {
265            return Object;
266        }
267    }
268
269    /**
270     * Returns the Java class representing this kind.
271     *
272     * @return the Java class
273     */
274    public Class<?> toJavaClass() {
275        return primitiveJavaClass;
276    }
277
278    /**
279     * Returns the Java class for instances of boxed values of this kind.
280     *
281     * @return the Java class
282     */
283    public Class<?> toBoxedJavaClass() {
284        return boxedJavaClass;
285    }
286
287    /**
288     * Converts this value type to a string.
289     */
290    @Override
291    public String toString() {
292        return javaName;
293    }
294
295    /**
296     * Marker interface for types that should be {@linkplain Kind#format(Object) formatted} with
297     * their {@link Object#toString()} value. Calling {@link Object#toString()} on other objects
298     * poses a security risk because it can potentially call user code.
299     */
300    public interface FormatWithToString {
301    }
302
303    /**
304     * Classes for which invoking {@link Object#toString()} does not run user code.
305     */
306    private static boolean isToStringSafe(Class<?> c) {
307        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;
308    }
309
310    /**
311     * Gets a formatted string for a given value of this kind.
312     *
313     * @param value a value of this kind
314     * @return a formatted string for {@code value} based on this kind
315     */
316    public String format(Object value) {
317        if (isPrimitive()) {
318            assert isToStringSafe(value.getClass());
319            return value.toString();
320        } else {
321            if (value == null) {
322                return "null";
323            } else {
324                if (value instanceof String) {
325                    String s = (String) value;
326                    if (s.length() > 50) {
327                        return "String:\"" + s.substring(0, 30) + "...\"";
328                    } else {
329                        return "String:\"" + s + '"';
330                    }
331                } else if (value instanceof JavaType) {
332                    return "JavaType:" + ((JavaType) value).toJavaName();
333                } else if (value instanceof Enum) {
334                    return MetaUtil.getSimpleName(value.getClass(), true) + ":" + ((Enum<?>) value).name();
335                } else if (value instanceof FormatWithToString) {
336                    return MetaUtil.getSimpleName(value.getClass(), true) + ":" + String.valueOf(value);
337                } else if (value instanceof Class<?>) {
338                    return "Class:" + ((Class<?>) value).getName();
339                } else if (isToStringSafe(value.getClass())) {
340                    return value.toString();
341                } else if (value.getClass().isArray()) {
342                    return formatArray(value);
343                } else {
344                    return MetaUtil.getSimpleName(value.getClass(), true) + "@" + System.identityHashCode(value);
345                }
346            }
347        }
348    }
349
350    private static final int MAX_FORMAT_ARRAY_LENGTH = 5;
351
352    private static String formatArray(Object array) {
353        Class<?> componentType = array.getClass().getComponentType();
354        assert componentType != null;
355        int arrayLength = Array.getLength(array);
356        StringBuilder buf = new StringBuilder(MetaUtil.getSimpleName(componentType, true)).append('[').append(arrayLength).append("]{");
357        int length = Math.min(MAX_FORMAT_ARRAY_LENGTH, arrayLength);
358        boolean primitive = componentType.isPrimitive();
359        for (int i = 0; i < length; i++) {
360            if (primitive) {
361                buf.append(Array.get(array, i));
362            } else {
363                Object o = ((Object[]) array)[i];
364                buf.append(Kind.Object.format(o));
365            }
366            if (i != length - 1) {
367                buf.append(", ");
368            }
369        }
370        if (arrayLength != length) {
371            buf.append(", ...");
372        }
373        return buf.append('}').toString();
374    }
375
376    /**
377     * The minimum value that can be represented as a value of this kind.
378     *
379     * @return the minimum value
380     */
381    public long getMinValue() {
382        switch (this) {
383            case Boolean:
384                return 0;
385            case Byte:
386                return java.lang.Byte.MIN_VALUE;
387            case Char:
388                return java.lang.Character.MIN_VALUE;
389            case Short:
390                return java.lang.Short.MIN_VALUE;
391            case Int:
392                return java.lang.Integer.MIN_VALUE;
393            case Long:
394                return java.lang.Long.MIN_VALUE;
395            default:
396                throw new IllegalArgumentException("illegal call to minValue on " + this);
397        }
398    }
399
400    /**
401     * The maximum value that can be represented as a value of this kind.
402     *
403     * @return the maximum value
404     */
405    public long getMaxValue() {
406        switch (this) {
407            case Boolean:
408                return 1;
409            case Byte:
410                return java.lang.Byte.MAX_VALUE;
411            case Char:
412                return java.lang.Character.MAX_VALUE;
413            case Short:
414                return java.lang.Short.MAX_VALUE;
415            case Int:
416                return java.lang.Integer.MAX_VALUE;
417            case Long:
418                return java.lang.Long.MAX_VALUE;
419            default:
420                throw new IllegalArgumentException("illegal call to maxValue on " + this);
421        }
422    }
423
424    /**
425     * Number of bytes that are necessary to represent a value of this kind.
426     *
427     * @return the number of bytes
428     */
429    public int getByteCount() {
430        if (this == Boolean) {
431            return 1;
432        } else {
433            return getBitCount() >> 3;
434        }
435    }
436
437    /**
438     * Number of bits that are necessary to represent a value of this kind.
439     *
440     * @return the number of bits
441     */
442    public int getBitCount() {
443        switch (this) {
444            case Boolean:
445                return 1;
446            case Byte:
447                return 8;
448            case Char:
449            case Short:
450                return 16;
451            case Float:
452                return 32;
453            case Int:
454                return 32;
455            case Double:
456                return 64;
457            case Long:
458                return 64;
459            default:
460                throw new IllegalArgumentException("illegal call to bits on " + this);
461        }
462    }
463
464    public JavaConstant getDefaultValue() {
465        switch (this) {
466            case Boolean:
467                return JavaConstant.FALSE;
468            case Int:
469                return JavaConstant.INT_0;
470            case Long:
471                return JavaConstant.LONG_0;
472            case Float:
473                return JavaConstant.FLOAT_0;
474            case Double:
475                return JavaConstant.DOUBLE_0;
476            case Object:
477                return JavaConstant.NULL_POINTER;
478            case Byte:
479            case Char:
480            case Short:
481                return new PrimitiveConstant(this, 0);
482            default:
483                throw new IllegalArgumentException("illegal call to getDefaultValue on " + this);
484        }
485    }
486}