< 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 : imported patch 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 string (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 valid 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 valid 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, name.length());
+ } 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 >