JDK-8308916 : Update core reflection for JEP 445 (preview)
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Priority: P4
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 21
  • Submitted: 2023-05-26
  • Updated: 2023-11-08
  • Resolved: 2023-06-02
Related Reports
CSR :  
Description
Summary
-------

Update `java.lang.Class` to describe how the names of unnamed classes are modeled in core reflection.

Problem
-------

While the unnamed classes of JEP 445 do not have names at the source files, they are compiled to class files which do have names.

Solution
--------

Add a general discussion of unnamed classes to the prologue of `java.lang.Class` and add a predicate to determine whether or not a class is unnamed.

Specification
-------------

    diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java
    index 64f022c4a34..2ca95dc5a3e 100644
    --- a/src/java.base/share/classes/java/lang/Class.java
    +++ b/src/java.base/share/classes/java/lang/Class.java
    @@ -25,6 +25,9 @@
     
     package java.lang;
     
    +import jdk.internal.javac.PreviewFeature;
    +import jdk.internal.misc.PreviewFeatures;
    +
     import java.lang.annotation.Annotation;
     import java.lang.constant.ClassDesc;
     import java.lang.constant.ConstantDescs;
    @@ -188,12 +191,12 @@ import sun.reflect.misc.ReflectUtil;
      *
      * <h2><a id=unnamedClasses>Unnamed Classes</a></h2>
      *
    - * A {@code class} file representing an unnamed class is generated by
    + * A {@code class} file representing an {@linkplain #isUnnamedClass unnamed class} is generated by
      * a Java compiler from a source file for an unnamed class. The {@code
      * Class} object representing an unnamed class is top-level,
      * {@linkplain #isSynthetic synthetic}, and {@code final}. While an
      * unnamed class does <em>not</em> have a name in its Java source
    - * form, the various name-related methods of {@code java.lang.Class}
    + * form, several of the name-related methods of {@code java.lang.Class}
      * do return non-null and non-empty results for the {@code Class}
      * object representing an unnamed class.
      *
    @@ -205,10 +208,12 @@ import sun.reflect.misc.ReflectUtil;
      * class} files.
      *
      * For the {@code Class} object for an unnamed class {@code
    - * HelloWorld}, the methods to get the {@linkplain #getName name},
    - * {@linkplain #getTypeName type name}, {@linkplain #getSimpleName
    - * simple name}, and {@linkplain #getCanonicalName canonical name} all
    - * return results equal to {@code "HelloWorld"}.
    + * HelloWorld}, the methods to get the {@linkplain #getName name} and
    + * {@linkplain #getTypeName type name}
    + * return results equal to {@code "HelloWorld"} while the
    + * {@linkplain #getSimpleName
    + * simple name} of such an unnamed class is the empty string and the
    + * {@linkplain #getCanonicalName canonical name} is {@code null}.
      *
      * @param <T> the type of the class modeled by this {@code Class}
      * object.  For example, the type of {@code String.class} is {@code
    @@ -1742,7 +1747,7 @@ public final class Class<T> implements java.io.Serializable,
         /**
          * Returns the simple name of the underlying class as given in the
          * source code. An empty string is returned if the underlying class is
    -     * {@linkplain #isAnonymousClass() anonymous}.
    +     * {@linkplain #isAnonymousClass() anonymous} or {@linkplain #isUnnamedClass() unnamed}.
          * A {@linkplain #isSynthetic() synthetic class}, one not present
          * in source code, can have a non-empty name including special
          * characters, such as "{@code $}".
    @@ -1755,6 +1760,9 @@ public final class Class<T> implements java.io.Serializable,
          * @since 1.5
          */
         public String getSimpleName() {
    +        if (isUnnamedClass()) {
    +            return "";
    +        }
             ReflectionData<T> rd = reflectionData();
             String simpleName = rd.simpleName;
             if (simpleName == null) {
    @@ -1804,6 +1812,7 @@ public final class Class<T> implements java.io.Serializable,
          * <ul>
          * <li>a {@linkplain #isLocalClass() local class}
          * <li>a {@linkplain #isAnonymousClass() anonymous class}
    +     * <li>an {@linkplain #isUnnamedClass() unnamed class}
          * <li>a {@linkplain #isHidden() hidden class}
          * <li>an array whose component type does not have a canonical name</li>
          * </ul>
    @@ -1823,6 +1832,9 @@ public final class Class<T> implements java.io.Serializable,
          * @since 1.5
          */
         public String getCanonicalName() {
    +        if (isUnnamedClass()) {
    +            return null;
    +        }
             ReflectionData<T> rd = reflectionData();
             String canonicalName = rd.canonicalName;
             if (canonicalName == null) {
    @@ -1857,12 +1869,29 @@ public final class Class<T> implements java.io.Serializable,
             }
         }
     
    +    /**
    +     * {@return {@code true} if and only if the underlying class
    +     * is an unnamed class}
    +     *
    +     * @apiNote
    +     * An unnamed class is not an {@linkplain #isAnonymousClass anonymous class}.
    +     *
    +     * @since 21
    +     */
    +    @PreviewFeature(feature=PreviewFeature.Feature.UNNAMED, // change to UNNAMED_CLASSES
    +                    reflective=true)
    +    public boolean isUnnamedClass() {
    +        return isSynthetic() && PreviewFeatures.isEnabled() && isTopLevelClass();
    +    }
    +
    +
         /**


Comments
Moving to Approved.
02-06-2023

The spec update looks good.
02-06-2023

Integrated into https://github.com/openjdk/jdk/pull/13689
02-06-2023

Should change `return isSynthetic() && PreviewFeatures.isEnabled() && isTopLevelClass();` to `return PreviewFeatures.isEnabled() && isSynthetic() && isTopLevelClass();`. `PreviewFeatures.isEnabled()` is a `private static final` test that will effective optimize out `isUnnamedClass()` if not in preview.
02-06-2023

Updated the proposed change along the line discussed in the PR; [~jlaskey] and [~mchung] please take another look.
02-06-2023

Please change "{@code HelloWorld}" to {@code "HelloWorld"}
01-06-2023