--- /dev/null 2017-01-22 10:16:57.869617664 -0800 +++ new/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/JavaKind.java 2017-02-15 16:56:08.123142256 -0800 @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.vm.ci.meta; + +import java.lang.reflect.Array; + +//JaCoCo Exclude + +/** + * Denotes the basic kinds of types in CRI, including the all the Java primitive types, for example, + * {@link JavaKind#Int} for {@code int} and {@link JavaKind#Object} for all object types. A kind has + * a single character short name, a Java name, and a set of flags further describing its behavior. + */ +public enum JavaKind { + /** The primitive boolean kind, represented as an int on the stack. */ + Boolean('Z', "boolean", 1, true, java.lang.Boolean.TYPE, java.lang.Boolean.class), + + /** The primitive byte kind, represented as an int on the stack. */ + Byte('B', "byte", 1, true, java.lang.Byte.TYPE, java.lang.Byte.class), + + /** The primitive short kind, represented as an int on the stack. */ + Short('S', "short", 1, true, java.lang.Short.TYPE, java.lang.Short.class), + + /** The primitive char kind, represented as an int on the stack. */ + Char('C', "char", 1, true, java.lang.Character.TYPE, java.lang.Character.class), + + /** The primitive int kind, represented as an int on the stack. */ + Int('I', "int", 1, true, java.lang.Integer.TYPE, java.lang.Integer.class), + + /** The primitive float kind. */ + Float('F', "float", 1, false, java.lang.Float.TYPE, java.lang.Float.class), + + /** The primitive long kind. */ + Long('J', "long", 2, false, java.lang.Long.TYPE, java.lang.Long.class), + + /** The primitive double kind. */ + Double('D', "double", 2, false, java.lang.Double.TYPE, java.lang.Double.class), + + /** The Object kind, also used for arrays. */ + Object('A', "Object", 1, false, null, null), + + /** The void kind. */ + Void('V', "void", 0, false, java.lang.Void.TYPE, java.lang.Void.class), + + /** The non-type. */ + Illegal('-', "illegal", 0, false, null, null); + + private final char typeChar; + private final String javaName; + private final boolean isStackInt; + private final Class primitiveJavaClass; + private final Class boxedJavaClass; + private final int slotCount; + + JavaKind(char typeChar, String javaName, int slotCount, boolean isStackInt, Class primitiveJavaClass, Class boxedJavaClass) { + this.typeChar = typeChar; + this.javaName = javaName; + this.slotCount = slotCount; + this.isStackInt = isStackInt; + this.primitiveJavaClass = primitiveJavaClass; + this.boxedJavaClass = boxedJavaClass; + assert primitiveJavaClass == null || javaName.equals(primitiveJavaClass.getName()); + } + + /** + * Returns the number of stack slots occupied by this kind according to the Java bytecodes + * specification. + */ + public int getSlotCount() { + return this.slotCount; + } + + /** + * Returns whether this kind occupied two stack slots. + */ + public boolean needsTwoSlots() { + return this.slotCount == 2; + } + + /** + * Returns the name of the kind as a single upper case character. For the void and primitive + * kinds, this is the FieldType term in + * + * table 4.3-A of the JVM Specification. For {@link #Object}, the character {@code 'A'} is + * returned and for {@link #Illegal}, {@code '-'} is returned. + */ + public char getTypeChar() { + return typeChar; + } + + /** + * Returns the name of this kind which will also be it Java programming language name if it is + * {@linkplain #isPrimitive() primitive} or {@code void}. + */ + public String getJavaName() { + return javaName; + } + + /** + * Checks whether this type is a Java primitive type. + * + * @return {@code true} if this is {@link #Boolean}, {@link #Byte}, {@link #Char}, + * {@link #Short}, {@link #Int}, {@link #Long}, {@link #Float}, {@link #Double}, or + * {@link #Void}. + */ + public boolean isPrimitive() { + return primitiveJavaClass != null; + } + + /** + * Returns the kind that represents this kind when on the Java operand stack. + * + * @return the kind used on the operand stack + */ + public JavaKind getStackKind() { + if (isStackInt) { + return Int; + } + return this; + } + + /** + * Checks whether this type is a Java primitive type representing an integer number. + * + * @return {@code true} if the stack kind is {@link #Int} or {@link #Long}. + */ + public boolean isNumericInteger() { + return isStackInt || this == JavaKind.Long; + } + + /** + * Checks whether this type is a Java primitive type representing an unsigned number. + * + * @return {@code true} if the kind is {@link #Boolean} or {@link #Char}. + */ + public boolean isUnsigned() { + return this == JavaKind.Boolean || this == JavaKind.Char; + } + + /** + * Checks whether this type is a Java primitive type representing a floating point number. + * + * @return {@code true} if this is {@link #Float} or {@link #Double}. + */ + public boolean isNumericFloat() { + return this == JavaKind.Float || this == JavaKind.Double; + } + + /** + * Checks whether this represent an Object of some sort. + * + * @return {@code true} if this is {@link #Object}. + */ + public boolean isObject() { + return this == JavaKind.Object; + } + + /** + * Returns the kind corresponding to the Java type string. + * + * @param typeString the Java type string + * @return the kind + */ + public static JavaKind fromTypeString(String typeString) { + assert typeString.length() > 0; + final char first = typeString.charAt(0); + if (first == '[' || first == 'L') { + return JavaKind.Object; + } + return JavaKind.fromPrimitiveOrVoidTypeChar(first); + } + + /** + * Returns the kind of a word given the size of a word in bytes. + * + * @param wordSizeInBytes the size of a word in bytes + * @return the kind representing a word value + */ + public static JavaKind fromWordSize(int wordSizeInBytes) { + if (wordSizeInBytes == 8) { + return JavaKind.Long; + } else { + assert wordSizeInBytes == 4 : "Unsupported word size!"; + return JavaKind.Int; + } + } + + /** + * Returns the kind from the character describing a primitive or void. + * + * @param ch the character for a void or primitive kind as returned by {@link #getTypeChar()} + * @return the kind + */ + public static JavaKind fromPrimitiveOrVoidTypeChar(char ch) { + switch (ch) { + case 'Z': + return Boolean; + case 'C': + return Char; + case 'F': + return Float; + case 'D': + return Double; + case 'B': + return Byte; + case 'S': + return Short; + case 'I': + return Int; + case 'J': + return Long; + case 'V': + return Void; + } + throw new IllegalArgumentException("unknown primitive or void type character: " + ch); + } + + /** + * Returns the Kind representing the given Java class. + * + * @param klass the class + * @return the kind + */ + public static JavaKind fromJavaClass(Class klass) { + if (klass == Boolean.primitiveJavaClass) { + return Boolean; + } else if (klass == Byte.primitiveJavaClass) { + return Byte; + } else if (klass == Short.primitiveJavaClass) { + return Short; + } else if (klass == Char.primitiveJavaClass) { + return Char; + } else if (klass == Int.primitiveJavaClass) { + return Int; + } else if (klass == Long.primitiveJavaClass) { + return Long; + } else if (klass == Float.primitiveJavaClass) { + return Float; + } else if (klass == Double.primitiveJavaClass) { + return Double; + } else if (klass == Void.primitiveJavaClass) { + return Void; + } else { + return Object; + } + } + + /** + * Returns the Java class representing this kind. + * + * @return the Java class + */ + public Class toJavaClass() { + return primitiveJavaClass; + } + + /** + * Returns the Java class for instances of boxed values of this kind. + * + * @return the Java class + */ + public Class toBoxedJavaClass() { + return boxedJavaClass; + } + + /** + * Converts this value type to a string. + */ + @Override + public String toString() { + return javaName; + } + + /** + * Marker interface for types that should be {@linkplain JavaKind#format(Object) formatted} with + * their {@link Object#toString()} value. Calling {@link Object#toString()} on other objects + * poses a security risk because it can potentially call user code. + */ + public interface FormatWithToString { + } + + /** + * Classes for which invoking {@link Object#toString()} does not run user code. + */ + private static boolean isToStringSafe(Class c) { + 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; + } + + /** + * Gets a formatted string for a given value of this kind. + * + * @param value a value of this kind + * @return a formatted string for {@code value} based on this kind + */ + public String format(Object value) { + if (isPrimitive()) { + assert isToStringSafe(value.getClass()); + return value.toString(); + } else { + if (value == null) { + return "null"; + } else { + if (value instanceof String) { + String s = (String) value; + if (s.length() > 50) { + return "String:\"" + s.substring(0, 30) + "...\""; + } else { + return "String:\"" + s + '"'; + } + } else if (value instanceof JavaType) { + return "JavaType:" + ((JavaType) value).toJavaName(); + } else if (value instanceof Enum) { + return MetaUtil.getSimpleName(value.getClass(), true) + ":" + ((Enum) value).name(); + } else if (value instanceof FormatWithToString) { + return MetaUtil.getSimpleName(value.getClass(), true) + ":" + String.valueOf(value); + } else if (value instanceof Class) { + return "Class:" + ((Class) value).getName(); + } else if (isToStringSafe(value.getClass())) { + return value.toString(); + } else if (value.getClass().isArray()) { + return formatArray(value); + } else { + return MetaUtil.getSimpleName(value.getClass(), true) + "@" + System.identityHashCode(value); + } + } + } + } + + private static final int MAX_FORMAT_ARRAY_LENGTH = 5; + + private static String formatArray(Object array) { + Class componentType = array.getClass().getComponentType(); + assert componentType != null; + int arrayLength = Array.getLength(array); + StringBuilder buf = new StringBuilder(MetaUtil.getSimpleName(componentType, true)).append('[').append(arrayLength).append("]{"); + int length = Math.min(MAX_FORMAT_ARRAY_LENGTH, arrayLength); + boolean primitive = componentType.isPrimitive(); + for (int i = 0; i < length; i++) { + if (primitive) { + buf.append(Array.get(array, i)); + } else { + Object o = ((Object[]) array)[i]; + buf.append(JavaKind.Object.format(o)); + } + if (i != length - 1) { + buf.append(", "); + } + } + if (arrayLength != length) { + buf.append(", ..."); + } + return buf.append('}').toString(); + } + + /** + * Gets the minimum value that can be represented as a value of this kind. + * + * @return the minimum value represented as a {@code long} + */ + public long getMinValue() { + switch (this) { + case Boolean: + return 0; + case Byte: + return java.lang.Byte.MIN_VALUE; + case Char: + return java.lang.Character.MIN_VALUE; + case Short: + return java.lang.Short.MIN_VALUE; + case Int: + return java.lang.Integer.MIN_VALUE; + case Long: + return java.lang.Long.MIN_VALUE; + case Float: + return java.lang.Float.floatToRawIntBits(java.lang.Float.MIN_VALUE); + case Double: + return java.lang.Double.doubleToRawLongBits(java.lang.Double.MIN_VALUE); + default: + throw new IllegalArgumentException("illegal call to minValue on " + this); + } + } + + /** + * Gets the maximum value that can be represented as a value of this kind. + * + * @return the maximum value represented as a {@code long} + */ + public long getMaxValue() { + switch (this) { + case Boolean: + return 1; + case Byte: + return java.lang.Byte.MAX_VALUE; + case Char: + return java.lang.Character.MAX_VALUE; + case Short: + return java.lang.Short.MAX_VALUE; + case Int: + return java.lang.Integer.MAX_VALUE; + case Long: + return java.lang.Long.MAX_VALUE; + case Float: + return java.lang.Float.floatToRawIntBits(java.lang.Float.MAX_VALUE); + case Double: + return java.lang.Double.doubleToRawLongBits(java.lang.Double.MAX_VALUE); + default: + throw new IllegalArgumentException("illegal call to maxValue on " + this); + } + } + + /** + * Number of bytes that are necessary to represent a value of this kind. + * + * @return the number of bytes + */ + public int getByteCount() { + if (this == Boolean) { + return 1; + } else { + return getBitCount() >> 3; + } + } + + /** + * Number of bits that are necessary to represent a value of this kind. + * + * @return the number of bits + */ + public int getBitCount() { + switch (this) { + case Boolean: + return 1; + case Byte: + return 8; + case Char: + case Short: + return 16; + case Float: + return 32; + case Int: + return 32; + case Double: + return 64; + case Long: + return 64; + default: + throw new IllegalArgumentException("illegal call to bits on " + this); + } + } +}