< prev index next >

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

Print this page

        

@@ -26,10 +26,11 @@
 package java.lang;
 
 import java.lang.annotation.Annotation;
 import java.lang.constant.ClassDesc;
 import java.lang.invoke.TypeDescriptor;
+import java.lang.invoke.MethodHandles;
 import java.lang.module.ModuleReader;
 import java.lang.ref.SoftReference;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.ObjectStreamField;

@@ -275,11 +276,12 @@
                 sb.append(Arrays.stream(typeparms)
                           .map(Class::typeVarBounds)
                           .collect(Collectors.joining(",", "<", ">")));
             }
 
-            if (arrayDepth > 0) sb.append("[]".repeat(arrayDepth));
+            for (int i = 0; i < arrayDepth; i++)
+                sb.append("[]");
 
             return sb.toString();
         }
     }
 

@@ -322,11 +324,14 @@
      * @return     the {@code Class} object for the class with the
      *             specified name.
      * @exception LinkageError if the linkage fails
      * @exception ExceptionInInitializerError if the initialization provoked
      *            by this method fails
-     * @exception ClassNotFoundException if the class cannot be located
+     * @exception ClassNotFoundException if the class cannot be located, or
+     *            the class is {@linkplain #isHidden() hidden}
+     *
+     * @see Class#isHidden
      */
     @CallerSensitive
     public static Class<?> forName(String className)
                 throws ClassNotFoundException {
         Class<?> caller = Reflection.getCallerClass();

@@ -381,19 +386,21 @@
      *
      * @exception LinkageError if the linkage fails
      * @exception ExceptionInInitializerError if the initialization provoked
      *            by this method fails
      * @exception ClassNotFoundException if the class cannot be located by
-     *            the specified class loader
+     *            the specified class loader, or
+     *            the class is {@linkplain #isHidden() hidden}
      * @exception SecurityException
      *            if a security manager is present, and the {@code loader} is
      *            {@code null}, and the caller's class loader is not
      *            {@code null}, and the caller does not have the
      *            {@link RuntimePermission}{@code ("getClassLoader")}
      *
      * @see       java.lang.Class#forName(String)
      * @see       java.lang.ClassLoader
+     * @see       java.lang.Class#isHidden
      * @since     1.2
      */
     @CallerSensitive
     public static Class<?> forName(String name, boolean initialize,
                                    ClassLoader loader)

@@ -447,11 +454,12 @@
      *
      * @param  module   A module
      * @param  name     The <a href="ClassLoader.html#binary-name">binary name</a>
      *                  of the class
      * @return {@code Class} object of the given name defined in the given module;
-     *         {@code null} if not found.
+     *         {@code null} if not found or the class is defined in
+     *         the given module but {@linkplain #isHidden() hidden}
      *
      * @throws NullPointerException if the given module or name is {@code null}
      *
      * @throws LinkageError if the linkage fails
      *

@@ -463,10 +471,11 @@
      *         permission check will be performed when a class loader calls
      *         {@link ModuleReader#open(String)} to read the bytes of a class file
      *         in a module.</li>
      *         </ul>
      *
+     * @see       java.lang.Class#isHidden
      * @since 9
      * @spec JPMS
      */
     @CallerSensitive
     public static Class<?> forName(Module module, String name) {

@@ -863,10 +872,16 @@
     }
 
     // set by VM
     private transient Module module;
 
+    // set by VM (to be revisited if needed)
+    // for static nestmate, set to non-null when the first time Class::getNestHost is called
+    // for dynamic nestmate, set to non-null when it's defined and this keeps the host alive
+    // (the host may be temporary)
+    private transient Class<?> nestHost;
+
     // Initialized in JVM not by private constructor
     // This field is filtered from reflection access, i.e. getDeclaredField
     // will throw NoSuchFieldException
     private final ClassLoader classLoader;
 

@@ -1586,11 +1601,16 @@
                 int dimensions = 0;
                 do {
                     dimensions++;
                     cl = cl.getComponentType();
                 } while (cl.isArray());
-                return cl.getName() + "[]".repeat(dimensions);
+                StringBuilder sb = new StringBuilder();
+                sb.append(cl.getName());
+                for (int i = 0; i < dimensions; i++) {
+                    sb.append("[]");
+                }
+                return sb.toString();
             } catch (Throwable e) { /*FALLTHRU*/ }
         }
         return getName();
     }
 

@@ -2795,10 +2815,15 @@
     public java.security.ProtectionDomain getProtectionDomain() {
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
             sm.checkPermission(SecurityConstants.GET_PD_PERMISSION);
         }
+        return protectionDomain();
+    }
+
+    // package-private
+    java.security.ProtectionDomain protectionDomain() {
         java.security.ProtectionDomain pd = getProtectionDomain0();
         if (pd == null) {
             if (allPermDomain == null) {
                 java.security.Permissions perms =
                     new java.security.Permissions();

@@ -2809,11 +2834,10 @@
             pd = allPermDomain;
         }
         return pd;
     }
 
-
     /**
      * Returns the ProtectionDomain of this class.
      */
     private native java.security.ProtectionDomain getProtectionDomain0();
 

@@ -3410,16 +3434,19 @@
 
     /**
      * Helper method to get the method name from arguments.
      */
     private String methodToString(String name, Class<?>[] argTypes) {
-        return getName() + '.' + name +
-                ((argTypes == null || argTypes.length == 0) ?
-                "()" :
-                Arrays.stream(argTypes)
-                        .map(c -> c == null ? "null" : c.getName())
-                        .collect(Collectors.joining(",", "(", ")")));
+        StringBuilder sb = new StringBuilder();
+        sb.append(getName() + "." + name + "(");
+        if (argTypes != null) {
+            sb.append(Arrays.stream(argTypes)
+                      .map(c -> (c == null) ? "null" : c.getName())
+                      .collect(Collectors.joining(",")));
+        }
+        sb.append(")");
+        return sb.toString();
     }
 
     /** use serialVersionUID from JDK 1.1 for interoperability */
     private static final long serialVersionUID = 3206093459760846163L;
 

@@ -3897,10 +3924,14 @@
      * version 55.0 or greater may act as a nest host by enumerating the nest's
      * other members with the
      * {@code NestMembers} attribute (JVMS 4.7.29).
      * A {@code class} file of version 54.0 or lower does not use these
      * attributes.
+     * A class defined at runtime in a nest via
+     * {@link MethodHandles.Lookup#defineClass(byte[], MethodHandles.Lookup.ClassProperty[])}
+     * must not have the {@code NestHost} attribute and is not enumerated
+     * in the {@code NestMembers} attribute of the class file of the nest host.
      *
      * @return the nest host of this class or interface
      *
      * @throws SecurityException
      *         If the returned class is not the current class, and

@@ -3988,10 +4019,21 @@
      * <p>This method validates that, for each class or interface which is
      * recorded as a member of the nest by the nest host, that class or
      * interface records itself as a member of that same nest. Any exceptions
      * that occur during this validation are rethrown by this method.
      *
+     * <p>This method does not return the
+     * {@linkplain MethodHandles.Lookup.ClassProperty#NESTMATE nest members}
+     * defined dynamically via
+     * {@link MethodHandles.Lookup#defineClass(byte[], MethodHandles.Lookup.ClassProperty...)}.
+     *
+     * @apiNote
+     * Reflection API presents the static view of this class. The dynamic
+     * nestmates are not listed in the {@code NestMembers} attribute.
+     * We can revisit this in the future if there is a need to find
+     * all dynamically defined nest members.
+     *
      * @return an array of all classes and interfaces in the same nest as
      * this class
      *
      * @throws LinkageError
      *         If there is any problem loading or validating a nest member or

@@ -4004,10 +4046,11 @@
      *         SecurityManager#checkPackageAccess s.checkPackageAccess()}
      *         denies access to the package of that returned class
      *
      * @since 11
      * @see #getNestHost()
+     * @see MethodHandles.Lookup#defineClass(byte[], MethodHandles.Lookup.ClassProperty...)
      */
     @CallerSensitive
     public Class<?>[] getNestMembers() {
         if (isPrimitive() || isArray()) {
             return new Class<?>[] { this };

@@ -4088,7 +4131,44 @@
      * @since 12
      */
     @Override
     public Optional<ClassDesc> describeConstable() {
         return Optional.of(ClassDesc.ofDescriptor(descriptorString()));
+   }
+
+    /**
+     * Returns {@code true} if this class is hidden from being referenced
+     * by other classes; otherwise, {@code false}.
+     *
+     * <p> A <em>hidden class</em> is a class that cannot be referenced
+     * by other classes and cannot be loaded by its name.
+     * <p> A hidden class can be defined via
+     * {@link MethodHandles.Lookup#defineClass(byte[], MethodHandles.Lookup.ClassProperty[])
+     * Lookup.defineClass} with
+     * {@link MethodHandles.Lookup.ClassProperty#HIDDEN HIDDEN} property.
+     *
+     * <p> If this class is hidden then it cannot be found via its name.
+     * For example, {@code Class.forName(this.getName())} cannot find this class
+     * and {@code ClassNotFoundException} will be thrown.
+     *
+     * <p> If this class is an array class and its component class is hidden,
+     * then this array class is also hidden.
+     *
+     * @return {@code true} if this class is hidden;
+     * otherwise {@code false}.
+     *
+     * @since 12
+     * @see MethodHandles.Lookup#defineClass(byte[], MethodHandles.Lookup.ClassProperty[])
+     */
+    public boolean isHidden() {
+        return getName().indexOf('\\') > -1 || isVMAnonymousClass();
+    }
+
+    /**
+     * Checks if this {@code Class} is a VM-anonymous class
+     * as defined by {@link jdk.internal.misc.Unsafe#defineAnonymousClass}
+     * (not to be confused with a Java Language anonymous inner class).
+     */
+    private boolean isVMAnonymousClass() {
+        return getName().indexOf('/') > -1;
     }
 }
< prev index next >