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  */
  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', "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 }