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

ConstantBootstraps.explicitCast contradictory specification for null-to-primitive

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P4 P4
    • 26
    • core-libs
    • None
    • behavioral
    • minimal
    • No behavioral change.
    • Java API
    • SE

      Summary

      Fix the self-contradictory specification in java.lang.invoke.ConstantBoostraps::explicitCast when value is null and dstType is primitive, and correct about conversions to other primitives when value is Boolean.

      Problem

      Parts of the specification of explicitCast, including one paragraph and the @throws clause, indicate ClassCastException is thrown for a null value and a primitive (non-void) dstType. This is contradictory to the "The result is the same as when using the following code" block below and the actual behavior, which returns the zero value of the primitive type instead.

      In addition, the specification implies Boolean cannot convert to int or other primitives (excluding void, same elsewhere in this paragraph) because there is no Java primitive conversions from boolean to other primitives. However, it is treated as if it's int with value 1 or 0 for true/false. We need to clarify that as well.

      Solution

      Correct the wrong parts of the specification, using the specified "following code" behavior uniformly, which is already the long-standing implementation behavior.

      Also note value can be null, and clarify the CCE conditions.

      Specification

      --- a/src/java.base/share/classes/java/lang/invoke/ConstantBootstraps.java
      +++ b/src/java.base/share/classes/java/lang/invoke/ConstantBootstraps.java
      @@ -369,17 +369,32 @@ public static VarHandle arrayVarHandle(MethodHandles.Lookup lookup, String name,
            * <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.
      +     * <li>If {@code dstType} is a reference type, a reference cast is applied
      +     *     to {@code value} as if by calling {@link Class#cast(Object)
      +     *     dstType.cast(value)}.
      +     * <li>Otherwise, {@code dstType} is a primitive type:
      +     *     <ol>
      +     *     <li>If {@code value} is null, the default value (JVMS {@jvms 2.3})
      +     *         of {@code dstType} is returned.
      +     *     <li>If the runtime type of {@code value} is a primitive wrapper type
      +     *         (such as {@link Integer}), a Java unboxing conversion is applied
      +     *         (JLS {@jls 5.1.8}).
      +     *         <ul>
      +     *         <li>If the runtime type is {@link Boolean}, the unboxing result
      +     *             is then converted to {@code int}, where {@code true} becomes
      +     *             {@code 1} and {@code false} becomes {@code 0}.
      +     *         </ul>
      +     *         Followed by a Java casting conversion (JLS {@jls 5.5}):
      +     *         <ul>
      +     *         <li>If {@code dstType} is not {@code boolean}, the cast converts
      +     *             directly to {@code dstType}.
      +     *         <li>If {@code dstType} is {@code boolean}, the cast converts to
      +     *             {@code int}, and the resulting {@code boolean} is produced
      +     *             by testing whether the least significant bit of the cast
      +     *             {@code int} is 1.
      +     *         </ul>
      +     *     <li>Otherwise, a {@link ClassCastException} is thrown.
      +     *     </ol>
            * </ol>
            * <p>
            * The result is the same as when using the following code:
      @@ -393,13 +408,10 @@ public static VarHandle arrayVarHandle(MethodHandles.Lookup lookup, String name,
            * @param lookup unused
            * @param name unused
            * @param dstType the destination type of the conversion
      -     * @param value the value to be converted
      +     * @param value the value to be converted, may be null
            * @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})
      -     *
      +     * @throws ClassCastException when {@code dstType} is {@code void}; when
      +     *         {@code dstType} is a reference type, and the reference cast fails; or
      +     *         when {@code dstType} is primitive, and {@code value} is an
      +     *         instance of a reference type that is not a wrapper class
            * @since 15
            */
           public static Object explicitCast(MethodHandles.Lookup lookup, String name, Class<?> dstType, Object value)

            liach Chen Liang
            webbuggrp Webbug Group
            Jorn Vernee
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: