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.*;
  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 implements PlatformKind {
  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 float 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 EnumKey<JavaKind> key = new EnumKey<>(this);
  74     private final int slotCount;
  75 
  76     private JavaKind(char typeChar, String javaName, int slotCount, boolean isStackInt, Class<?> primitiveJavaClass, Class<?> boxedJavaClass) {
  77         this.typeChar = typeChar;
  78         this.javaName = javaName;
  79         this.slotCount = slotCount;
  80         this.isStackInt = isStackInt;
  81         this.primitiveJavaClass = primitiveJavaClass;
  82         this.boxedJavaClass = boxedJavaClass;
  83         assert primitiveJavaClass == null || javaName.equals(primitiveJavaClass.getName());
  84     }
  85 
  86     /**
  87      * Returns the number of stack slots occupied by this kind according to the Java bytecodes
  88      * specification.
  89      */
  90     public int getSlotCount() {
  91         return this.slotCount;
  92     }
  93 
  94     /**
  95      * Returns whether this kind occupied two stack slots.
  96      */
  97     public boolean needsTwoSlots() {
  98         return this.slotCount == 2;
  99     }
 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 JavaKind 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 == JavaKind.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 == JavaKind.Boolean || this == JavaKind.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 == JavaKind.Float || this == JavaKind.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 == JavaKind.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 JavaKind fromTypeString(String typeString) {
 186         assert typeString.length() > 0;
 187         final char first = typeString.charAt(0);
 188         if (first == '[' || first == 'L') {
 189             return JavaKind.Object;
 190         }
 191         return JavaKind.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 JavaKind fromWordSize(int wordSizeInBytes) {
 201         if (wordSizeInBytes == 8) {
 202             return JavaKind.Long;
 203         } else {
 204             assert wordSizeInBytes == 4 : "Unsupported word size!";
 205             return JavaKind.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 JavaKind 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 JavaKind 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 JavaKind#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(JavaKind.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 
 487     @Override
 488     public int getSizeInBytes() {
 489         return getByteCount();
 490     }
 491 
 492     @Override
 493     public int getVectorLength() {
 494         return 1;
 495     }
 496 }