blob: fe657723a83f6169b6adfab3a1854f5dbe17751a [file] [log] [blame]
/*
* 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 <i>FieldType</i> term in
* <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.2-200">
* table 4.3-A</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);
}
}
}