Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8241667

Make Boolean, Character, Byte, and Short implement Constable

    XMLWordPrintable

    Details

    • Type: CSR
    • Status: Closed
    • Priority: P3
    • Resolution: Approved
    • Fix Version/s: 15
    • Component/s: core-libs
    • Labels:
      None
    • Subcomponent:
    • Compatibility Kind:
      behavioral
    • Compatibility Risk:
      minimal
    • Compatibility Risk Description:
      A behavioral change exists for instanceof Constable checks, as well as Class::isInstance calls where Constable.class is given as an argument and the receiver is a Class object of one of the affected classes.
    • Interface Kind:
      Java API

      Description

      Summary

      Make Boolean, Character, Byte, and Short implement Constable

      Problem

      Since the introduction of the constants API (JEP 334), projects have begun adopting this API (see for instance JDK-8227393).

      As a result of this, APIs that accept a java.lang.constant.Constable as one of it's inputs have started appearing. These APIs would be easier to use if common Java types implemented Constable (where reasonable), so that a user can pass a value of that type directly to an API accepting Constable instead of needing to convert back and forth to another Constable type. For instance: to pass a boolean in a place where Constable is expected, a user can convert it to a String reading either "true" or "false", and when retrieving this value again parse the string using Boolean::parseBoolean to convert it back to a boolean. This would be made easier if Boolean implemented Constable, since in that case a boolean/Boolean value can be passed directly in places where a Constable is expected.

      Solution

      Make the primitive wrapper types that do not implement Constable yet, implement Constable.

      For the Byte, Short and Character types this requires adding an additional public bootstrap method for converting from an Integer to one of these types, since none of the three can be represented in the constant pool directly, while Integer can.

      Not strictly needed, but useful for the implementation, are the addition of several new constants in the ConstantDescs class which can be referenced from the implementation of the various describeConstable methods. (Note that these constant can not simply be placed in the class that references them, since this creates cyclical dependencies which cause problems when initializing these classes).

      Specification

      Boolean.java

        public final class Boolean implements java.io.Serializable,
      -                                       Comparable<Boolean>
      +                                       Comparable<Boolean>, Constable
      ...
       +     /**
       +      * Returns an {@link Optional} containing the nominal descriptor for this
       +      * instance.
       +      *
       +      * @return an {@link Optional} describing the {@linkplain Boolean} instance
       +      * @since 15
       +      */
       +     @Override
       +     public Optional<DynamicConstantDesc<Boolean>> describeConstable() {
       +         return Optional.of(value ? ConstantDescs.TRUE : ConstantDescs.FALSE);
       +     }

      Byte.java

      - public final class Byte extends Number implements Comparable<Byte> {
      + public final class Byte extends Number implements Comparable<Byte>, Constable {
      ...
       +     /**
       +      * Returns an {@link Optional} containing the nominal descriptor for this
       +      * instance.
       +      *
       +      * @return an {@link Optional} describing the {@linkplain Byte} instance
       +      * @since 15
       +      */
       +     @Override
       +     public Optional<DynamicConstantDesc<Byte>> describeConstable() {
       +         return Optional.of(DynamicConstantDesc.ofNamed(BSM_EXPLICIT_CAST, DEFAULT_NAME, CD_byte, intValue()));
       +     }

      Character.java

        public final
      - class Character implements java.io.Serializable, Comparable<Character> {
      + class Character implements java.io.Serializable, Comparable<Character>, Constable {
      ...
       +     /**
       +      * Returns an {@link Optional} containing the nominal descriptor for this
       +      * instance.
       +      *
       +      * @return an {@link Optional} describing the {@linkplain Character} instance
       +      * @since 15
       +      */
       +     @Override
       +     public Optional<DynamicConstantDesc<Character>> describeConstable() {
       +         return Optional.of(DynamicConstantDesc.ofNamed(BSM_EXPLICIT_CAST, DEFAULT_NAME, CD_char, (int) value));
       +     }

      Short.java

      - public final class Short extends Number implements Comparable<Short> {
      + public final class Short extends Number implements Comparable<Short>, Constable {
      ...
       +     /**
       +      * Returns an {@link Optional} containing the nominal descriptor for this
       +      * instance.
       +      *
       +      * @return an {@link Optional} describing the {@linkplain Short} instance
       +      * @since 15
       +      */
       +     @Override
       +     public Optional<DynamicConstantDesc<Short>> describeConstable() {
       +         return Optional.of(DynamicConstantDesc.ofNamed(BSM_EXPLICIT_CAST, DEFAULT_NAME, CD_short, intValue()));
       +     }

      ConstantDescs.java

      +     /**
      +      * {@link MethodHandleDesc} representing {@link ConstantBootstraps#getStaticFinal(Lookup, String, Class, Class) ConstantBootstraps.getStaticFinal}
      +      * @since 15
      +      */
      +     public static final DirectMethodHandleDesc BSM_GET_STATIC_FINAL
      +             = ofConstantBootstrap(CD_ConstantBootstraps, "getStaticFinal",
      +             CD_Object, CD_Class);
      ...
      +     /**
      +      * {@link MethodHandleDesc} representing {@link ConstantBootstraps#explicitCast(Lookup, String, Class, Object)} ConstantBootstraps.explicitCast}
      +      * @since 15
      +      */
      +     public static final DirectMethodHandleDesc BSM_EXPLICIT_CAST
      +             = ofConstantBootstrap(CD_ConstantBootstraps, "explicitCast",
      +             CD_Object, CD_Object);
      ...
      +     /**
      +      * Nominal descriptor representing the constant {@linkplain Boolean#TRUE}
      +      * @since 15
      +      */
      +     public static final DynamicConstantDesc<Boolean> TRUE
      +             = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL,
      +                                           "TRUE", CD_Boolean, CD_Boolean);
      + 
      +     /**
      +      * Nominal descriptor representing the constant {@linkplain Boolean#TRUE}
      +      * @since 15
      +      */
      +     public static final DynamicConstantDesc<Boolean> FALSE
      +             = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL,
      +                                           "FALSE", CD_Boolean, CD_Boolean);

      ConstantBootstraps.java

      +     /**
      +      * Applies a conversion from a source type to a destination type.
      +      * <p>
      +      * Given a destination type {@code dstType} and an input
      +      * value {@code value}, one of the following will happen:
      +      * <ul>
      +      * <li>If {@code dstType} is {@code void.class},
      +      *     a {@link ClassCastException} is thrown.
      +      * <li>If {@code dstType} is {@code Object.class}, {@code value} is returned as is.
      +      * </ul>
      +      * <p>
      +      * Otherwise one of the following conversions is applied to {@code value}:
      +      * <ol>
      +      * <li>If {@code dstType} is a reference type, a reference cast
      +      *     is applied to {@code value} as if by calling {@code dstType.cast(value)}.
      +      * <li>If {@code dstType} is a primitive type, then, if the runtime type
      +      *     of {@code value} is a primitive wrapper type (such as {@link Integer}),
      +      *     a Java unboxing conversion is applied {@jls 5.1.8} followed by a
      +      *     Java casting conversion {@jls 5.5} converting either directly to
      +      *     {@code dstType}, or, if {@code dstType} is {@code boolean},
      +      *     to {@code int}, which is then converted to either {@code true}
      +      *     or {@code false} depending on whether the least-significant-bit
      +      *     is 1 or 0 respectively. If the runtime type of {@code value} is
      +      *     not a primitive wrapper type a {@link ClassCastException} is thrown.
      +      * </ol>
      +      * <p>
      +      * The result is the same as when using the following code:
      +      * <blockquote><pre>{@code
      +      * MethodHandle id = MethodHandles.identity(dstType);
      +      * MethodType mt = MethodType.methodType(dstType, Object.class);
      +      * MethodHandle conv = MethodHandles.explicitCastArguments(id, mt);
      +      * return conv.invoke(value);
      +      * }</pre></blockquote>
      +      *
      +      * @param lookup unused
      +      * @param name unused
      +      * @param dstType the destination type of the conversion
      +      * @param value the value to be converted
      +      * @return the converted value
      +      * @throws ClassCastException when {@code dstType} is {@code void},
      +      *         when a cast per (1) fails, or when {@code dstType} is a primitive type
      +      *         and the runtime type of {@code value} is not a primitive wrapper type
      +      *         (such as {@link Integer})
      +      *
      +      * @since 15
      +      */
      +     public static Object explicitCast(MethodHandles.Lookup lookup, String name, Class<?> dstType, Object value)
      +             throws ClassCastException {
      +         if (dstType == void.class)
      +             throw new ClassCastException("Can not convert to void");
      +         if (dstType == Object.class)
      +             return value;
      + 
      +         MethodHandle id = MethodHandles.identity(dstType);
      +         MethodType mt = MethodType.methodType(dstType, Object.class);
      +         MethodHandle conv = MethodHandles.explicitCastArguments(id, mt);
      +         try {
      +             return conv.invoke(value);
      +         } catch (ClassCastException e) {
      +             throw e; // specified, let CCE through
      +         } catch (Throwable throwable) {
      +             throw new InternalError(throwable); // Not specified, throw InternalError
      +         }
      +     }

      See also the full patch at: http://cr.openjdk.java.net/~jvernee/8241100/webrev.04

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              jvernee Jorn Vernee
              Reporter:
              jvernee Jorn Vernee
              Reviewed By:
              John Rose, Paul Sandoz
              Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: