< prev index next >

src/java.base/share/classes/java/lang/Class.java

Print this page
rev 58768 : 8238358: Implementation of JEP 371: Hidden Classes
Reviewed-by: alanb, cjplummer, coleenp, dholmes, dlong, forax, jlahoda, psandoz, plevart, vromero
Contributed-by: mandy.chung@oracle.com, lois.foltan@oracle.com, david.holmes@oracle.com, harold.seigel@oracle.com, serguei.spitsyn@oracle.com, alex.buckley@oracle.com, jamsheed.c.m@oracle.com, jan.lahoda@oracle.com, amy.lu@oracle.com
rev 58769 : [mq]: type-descriptor-name

@@ -161,14 +161,22 @@
  * hidden classes; all kinds of interface, including annotation types,
  * may be hidden interfaces.
  *
  * The {@linkplain #getName() name of a hidden class or interface} is
  * not a <a href="ClassLoader.html#binary-name">binary name</a>,
- * which means that a hidden class or interface cannot be
- * referenced by the constant pools of other classes and interfaces,
- * and cannot be discovered by {@link #forName Class::forName} or
- * {@link ClassLoader#loadClass(String, boolean) ClassLoader::loadClass}.
+ * which means the following:
+ * <ul>
+ * <li>A hidden class or interface cannot be referenced by the constant pools
+ *     of other classes and interfaces.
+ * <li>A hidden class or interface cannot be described in
+ *     {@linkplain java.lang.constant.ConstantDesc <em>nominal form</em>} by
+ *     {@link #describeConstable() Class::describeConstable},
+ *     {@link ClassDesc#of(String) ClassDesc::of}, or
+ *     {@link ClassDesc#ofDescriptor(String) ClassDesc::ofDescriptor}.
+ * <li>A hidden class or interface cannot be discovered by {@link #forName Class::forName}
+ *     or {@link ClassLoader#loadClass(String, boolean) ClassLoader::loadClass}.
+ * </ul>
  *
  * A hidden class or interface is never an array class, but may be
  * the element type of an array. In all other respects, the fact that
  * a class or interface is hidden has no bearing on the characteristics
  * exposed by the methods of class {@code Class}.

@@ -1063,14 +1071,11 @@
      * @jls 6.7 Fully Qualified Names
      */
     public String getPackageName() {
         String pn = this.packageName;
         if (pn == null) {
-            Class<?> c = this;
-            while (c.isArray()) {
-                c = c.getComponentType();
-            }
+            Class<?> c = isArray() ? elementType() : this;
             if (c.isPrimitive()) {
                 pn = "java.lang";
             } else {
                 String cn = c.getName();
                 int dot = cn.lastIndexOf('.');

@@ -1224,10 +1229,24 @@
         }
     }
 
     private final Class<?> componentType;
 
+    /*
+     * Returns the {@code Class} representing the element type of an array class.
+     * If this class does not represent an array class, then this method returns
+     * {@code null}.
+     */
+    private Class<?> elementType() {
+        if (!isArray()) return null;
+
+        Class<?> c = this;
+        while (c.isArray()) {
+            c = c.getComponentType();
+        }
+        return c;
+    }
 
     /**
      * Returns the Java language modifiers for this class or interface, encoded
      * in an integer. The modifiers consist of the Java Virtual Machine's
      * constants for {@code public}, {@code protected},

@@ -3021,14 +3040,11 @@
      * Add a package name prefix if the name is not absolute Remove leading "/"
      * if name is absolute
      */
     private String resolveName(String name) {
         if (!name.startsWith("/")) {
-            Class<?> c = this;
-            while (c.isArray()) {
-                c = c.getComponentType();
-            }
+            Class<?> c = isArray() ? elementType() : this;
             String baseName = c.getPackageName();
             if (baseName != null && !baseName.isEmpty()) {
                 name = baseName.replace('.', '/') + "/" + name;
             }
         } else {

@@ -4239,28 +4255,77 @@
         }
         return members;
     }
 
     /**
-     * Returns the type descriptor string for this class.
-     * <p>
-     * Note that this is not a strict inverse of {@link #forName};
+     * Returns the descriptor string of the entity (class, interface, array class,
+     * primitive type, or {@code void}) represented by this {@code Class} object.
+     *
+     * <p> If this {@code Class} object represents a class or interface,
+     * not an array class, then:
+     * <ul>
+     * <li> If the class or interface is not {@linkplain Class#isHidden() hidden},
+     *      then the result is a field descriptor (JVMS {@jvms 4.3.2})
+     *      for the class or interface. Calling
+     *      {@link ClassDesc#ofDescriptor(String) ClassDesc::ofDescriptor}
+     *      with the result descriptor string produces a {@link ClassDesc ClassDesc}
+     *      describing this class or interface.
+     * <li> If the class or interface is {@linkplain Class#isHidden() hidden},
+     *      then the result is a string of the form:
+     *      <blockquote>
+     *      {@code "L" +} <em>N</em> {@code + "." + <suffix> + ";"}
+     *      </blockquote>
+     *      where <em>N</em> is the <a href="ClassLoader.html#binary-name">binary name</a>
+     *      encoded in internal form indicated by the {@code class} file passed to
+     *      {@link MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
+     *      Lookup::defineHiddenClass}, and {@code <suffix>} is an unqualified name.
+     *      A hidden class or interface has no {@linkplain ClassDesc nominal descriptor}.
+     *      The result string is not a type descriptor.
+     * </ul>
+     *
+     * <p> If this {@code Class} object represents an array class, then
+     * the result is a string consisting of one or more '{@code [}' characters
+     * representing the depth of the array nesting, followed by the
+     * descriptor string of the element type.
+     * <ul>
+     * <li> If the element type is not a {@linkplain Class#isHidden() hidden} class
+     * or interface, then this array class can be described nominally.
+     * Calling {@link ClassDesc#ofDescriptor(String) ClassDesc::ofDescriptor}
+     * with the result descriptor string produces a {@link ClassDesc ClassDesc}
+     * describing this array class.
+     * <li> If the element type is a {@linkplain Class#isHidden() hidden} class or
+     * interface, then this array class cannot be described nominally.
+     * The result string is not a type descriptor.
+     * </ul>
+     *
+     * <p> If this {@code Class} object represents a primitive type or
+     * {@code void}, then the result is a field descriptor string which
+     * is a one-letter code corresponding to a primitive type or {@code void}
+     * ({@code "B", "C", "D", "F", "I", "J", "S", "Z", "V"}) (JVMS {@jvms 4.3.2}).
+     *
+     * @apiNote
+     * This is not a strict inverse of {@link #forName};
      * distinct classes which share a common name but have different class loaders
      * will have identical descriptor strings.
      *
-     * @return the type descriptor representation
+     * @return the descriptor string for this {@code Class} object
      * @jvms 4.3.2 Field Descriptors
      * @since 12
      */
     @Override
     public String descriptorString() {
         if (isPrimitive())
             return Wrapper.forPrimitiveType(this).basicTypeString();
-        else if (isArray()) {
+
+        if (isArray()) {
             return "[" + componentType.descriptorString();
-        }
-        else {
+        } else if (isHidden()) {
+            String name = getName();
+            int index = name.indexOf('/');
+            return "L" + name.substring(0, index).replace('.', '/')
+                       + "." + name.substring(index+1) + ";";
+        } else {
             return "L" + getName().replace('.', '/') + ";";
         }
     }
 
     /**

@@ -4299,11 +4364,13 @@
      * or an empty {@link Optional} if one cannot be constructed.
      * @since 12
      */
     @Override
     public Optional<ClassDesc> describeConstable() {
-        return Optional.of(ClassDesc.ofDescriptor(descriptorString()));
+        Class<?> c = isArray() ? elementType() : this;
+        return c.isHidden() ? Optional.empty()
+                            : Optional.of(ClassDesc.ofDescriptor(descriptorString()));
    }
 
     /**
      * Returns {@code true} if and only if the underlying class is a hidden class.
      *
< prev index next >