< prev index next >

src/java.base/share/classes/java/lang/invoke/MethodHandles.java

Print this page

        

@@ -25,10 +25,11 @@
 
 package java.lang.invoke;
 
 import jdk.internal.access.JavaLangAccess;
 import jdk.internal.access.SharedSecrets;
+import jdk.internal.misc.VM;
 import jdk.internal.module.IllegalAccessLogger;
 import jdk.internal.org.objectweb.asm.ClassReader;
 import jdk.internal.org.objectweb.asm.Opcodes;
 import jdk.internal.reflect.CallerSensitive;
 import jdk.internal.reflect.Reflection;

@@ -1823,11 +1824,11 @@
          * </ul>
          *
          * <p> If the {@code initialize} parameter is {@code true},
          * then {@code C} is initialized by the Java Virtual Machine.
          *
-         * <p>The newly created class or interface {@code C} is <em>hidden</em>, in the sense that
+         * <p> The newly created class or interface {@code C} is <em>hidden</em>, in the sense that
          * no other class or interface can refer to {@code C} via a constant pool entry.
          * That is, a hidden class or interface cannot be named as a supertype, a field type,
          * a method parameter type, or a method return type by any other class.
          * This is because a hidden class or interface does not have a binary name, so
          * there is no internal form available to record in any class's constant pool.

@@ -1838,27 +1839,64 @@
          * {@link ClassLoader#loadClass(String, boolean)}, or {@link #findClass(String)}, and
          * is not {@linkplain java.lang.instrument.Instrumentation#isModifiableClass(Class)
          * modifiable} by Java agents or tool agents using the <a href="{@docRoot}/../specs/jvmti.html">
          * JVM Tool Interface</a>.
          *
-         * <p> If {@code options} has the {@link ClassOption#NESTMATE NESTMATE} option, then
-         * the newly created class or interface {@code C} is a member of a nest. The nest
-         * to which {@code C} belongs is not based on any {@code NestHost} attribute in
-         * the {@code ClassFile} structure from which {@code C} was derived.
+         * <p> A class or interface created by
+         * {@linkplain ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)
+         * a class loader} has a strong relationship with that class loader.
+         * That is, every {@code Class} object contains a reference to the {@code ClassLoader}
+         * that {@linkplain Class#getClassLoader() defined it}.
+         * This means that a class created by a class loader may be unloaded if and
+         * only if its defining loader is not reachable and thus may be reclaimed
+         * by a garbage collector (JLS 12.7).
+         *
+         * By default, however, a hidden class or interface may be unloaded even if
+         * the class loader that is marked as its defining loader is
+         * <a href="../ref/package.html#reachability">reachable</a>.
+         * This behavior is useful when a hidden class or interface serves multiple
+         * classes defined by arbitrary class loaders.  In other cases, a hidden
+         * class or interface may be linked to a single class (or a small number of classes)
+         * with the same defining loader as the hidden class or interface.
+         * In such cases, where the hidden class or interface must be coterminous
+         * with a normal class or interface, the {@link ClassOption#STRONG STRONG}
+         * option may be passed in {@code options}.
+         * This arranges for a hidden class to have the same strong relationship
+         * with the class loader marked as its defining loader,
+         * as a normal class or interface has with its own defining loader.
+         *
+         * If {@code STRONG} is not used, then the invoker of {@code defineHiddenClass}
+         * may still prevent a hidden class or interface from being
+         * unloaded by ensuring that the {@code Class} object is reachable.
+         *
+         * <p> The unloading characteristics are set for each hidden class when it is
+         * defined, and cannot be changed later.  An advantage of allowing hidden classes
+         * to be unloaded independently of the loader deemed as their defining loader
+         * is that a very large number of hidden classes may be created by an application.
+         * In contrast, if {@code STRONG} is used, then the JVM may run out of memory,
+         * just as if normal classes were created by class loaders.
+         *
+         * <p> Classes and interfaces in a nest are allowed to have mutual access to
+         * their private members.  The nest relationship is determined by
+         * the {@code NestHost} attribute (JVMS 4.7.28) and
+         * the {@code NestMembers} attribute (JVMS 4.7.29) in a {@code class} file.
+         * By default, a hidden class belongs to a nest consisting only of itself
+         * because a hidden class has no binary name.
+         * The {@link ClassOption#NESTMATE NESTMATE} option can be passed in {@code options}
+         * to create a hidden class or interface {@code C} as a member of a nest.
+         * The nest to which {@code C} belongs is not based on any {@code NestHost} attribute
+         * in the {@code ClassFile} structure from which {@code C} was derived.
          * Instead, the following rules determine the nest host of {@code C}:
          * <ul>
          * <li>If the nest host of the lookup class of this {@code Lookup} has previously
-         * been determined, then {@code H} be the nest host of the lookup class.</li>
-         * <li>Otherwise, it is determined using the algorithm in JVMS 5.4.4, yielding {@code H}.</li>
-         * <li>The nest host of {@code C} is determined to be {@code H}, the nest host of the lookup class.</li>
+         *     been determined, then let {@code H} be the nest host of the lookup class.
+         *     Otherwise, the nest host of the lookup class is determined using the
+         *     algorithm in JVMS 5.4.4, yielding {@code H}.</li>
+         * <li>The nest host of {@code C} is determined to be {@code H},
+         *     the nest host of the lookup class.</li>
          * </ul>
          *
-         * <p> If {@code options} has {@link ClassOption#WEAK WEAK} option, then
-         * the newly created class or interface is <em>not strongly referenced</em> from
-         * its defining class loader. Therefore, it may be unloaded while
-         * its defining class loader is strongly reachable.
-         *
          * <p> A hidden class or interface may be serializable, but this requires a custom
          * serialization mechanism in order to ensure that instances are properly serialized
          * and deserialized. The default serialization mechanism supports only classes and
          * interfaces that are discoverable by their class name.
          *

@@ -1888,15 +1926,18 @@
          *
          * @since 15
          * @see Class#isHiddenClass()
          * @jvms 4.2.1 Binary Class and Interface Names
          * @jvms 4.2.2 Unqualified Names
+         * @jvms 4.7.28 The {@code NestHost} Attribute
+         * @jvms 4.7.29 The {@code NestMembers} Attribute
          * @jvms 5.4.3.1 Class and Interface Resolution
          * @jvms 5.4.4 Access Control
          * @jvms 5.3.5 Deriving a {@code Class} from a {@code class} File Representation
          * @jvms 5.4 Linking
          * @jvms 5.5 Initialization
+         * @jls 12.7 Unloading of Classes and Interfaces
          */
         public Lookup defineHiddenClass(byte[] bytes, boolean initialize, ClassOption... options)
                 throws IllegalAccessException
         {
             Objects.requireNonNull(bytes);

@@ -1906,11 +1947,11 @@
             if (!hasFullPrivilegeAccess()) {
                 throw new IllegalAccessException(this + " does not have full privilege access");
             }
 
             Set<ClassOption> opts = options.length > 0 ? Set.of(options) : Set.of();
-            return makeHiddenClassDefiner(bytes.clone(), opts).defineClassAsLookup(initialize);
+            return makeHiddenClassDefiner(bytes.clone(), opts, false).defineClassAsLookup(initialize);
         }
 
         /**
          * Creates a <em>hidden</em> class or interface from {@code bytes} with {@code classData},
          * returning a {@code Lookup} on the newly created class or interface.

@@ -1959,52 +2000,101 @@
             if (!hasFullPrivilegeAccess()) {
                 throw new IllegalAccessException(this + " does not have full privilege access");
             }
 
             Set<ClassOption> opts = options.length > 0 ? Set.of(options) : Set.of();
-            return makeHiddenClassDefiner(bytes.clone(), opts).defineClassAsLookup(initialize, classData);
+            return makeHiddenClassDefiner(bytes.clone(), opts, false)
+                       .defineClassAsLookup(initialize, classData);
         }
 
         private ClassDefiner makeClassDefiner(byte[] bytes) {
-            return new ClassDefiner(this, bytes, Set.of(), 0);
+            return new ClassDefiner(this, bytes, STRONG_LOADER_LINK);
         }
 
         /**
          * Returns a ClassDefiner that creates a {@code Class} object of a hidden class
-         * from the given bytes and options.
+         * from the given bytes.
+         *
+         * @param bytes   class bytes
+         * @return ClassDefiner that defines a hidden class of the given bytes.
+         */
+        ClassDefiner makeHiddenClassDefiner(byte[] bytes) {
+            return makeHiddenClassDefiner(bytes, Set.of(), false);
+        }
+
+        /**
+         * Returns a ClassDefiner that creates a {@code Class} object of a hidden class
+         * from the given bytes.
+         *
+         * @param name    fully-qualified name that specifies the prefix of the hidden class
+         * @param bytes   class bytes
+         * @return ClassDefiner that defines a hidden class of the given bytes.
+         */
+        ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes) {
+            return makeHiddenClassDefiner(name, bytes, Set.of(), false);
+        }
+
+        /**
+         * Returns a ClassDefiner that creates a {@code Class} object of a hidden class
+         * from the given bytes and options.  This method will read the class file
+         * and obtain the class name.
          *
          * @param bytes class bytes
          * @param options class options
+         * @param accessVmAnnotations true to give the hidden class access to VM annotations
          * @return ClassDefiner that defines a hidden class of the given bytes and options
          */
-        ClassDefiner makeHiddenClassDefiner(byte[] bytes, Set<ClassOption> options) {
-            return new ClassDefiner(this, bytes, options, HIDDEN_CLASS);
+        ClassDefiner makeHiddenClassDefiner(byte[] bytes,
+                                            Set<ClassOption> options,
+                                            boolean accessVmAnnotations) {
+            int flags = HIDDEN_CLASS | ClassOption.optionsToFlag(options);
+            if (accessVmAnnotations | VM.isSystemDomainLoader(lookupClass.getClassLoader())) {
+                // jdk.internal.vm.annotations are permitted for classes
+                // defined to boot loader and platform loader
+                flags |= ACCESS_VM_ANNOTATIONS;
+            }
+
+            return new ClassDefiner(this, bytes, flags);
         }
 
         /**
-         * This method is only called by MethodHandleImpl.BindCaller.makeInjectedInvoker.
+         * Returns a ClassDefiner that creates a {@code Class} object of a hidden class
+         * from the given bytes and options.
          *
          * @param name the name of the class and the name in the class bytes is ignored.
          * @param bytes class bytes
-         * @return ClassDefiner that defines a hidden class of the given bytes
+         * @param options class options
+         * @param accessVmAnnotations true to give the hidden class access to VM annotations
          */
-        ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes, int flags) {
-            return new ClassDefiner(this, name, bytes, HIDDEN_CLASS|flags);
+        ClassDefiner makeHiddenClassDefiner(String name,
+                                            byte[] bytes,
+                                            Set<ClassOption> options,
+                                            boolean accessVmAnnotations) {
+            int flags = HIDDEN_CLASS | ClassOption.optionsToFlag(options);
+            if (accessVmAnnotations | VM.isSystemDomainLoader(lookupClass.getClassLoader())) {
+                // jdk.internal.vm.annotations are permitted for classes
+                // defined to boot loader and platform loader
+                flags |= ACCESS_VM_ANNOTATIONS;
+            }
+
+            return new ClassDefiner(this, name, bytes, flags);
         }
 
         static class ClassDefiner {
             private final Lookup lookup;
             private final String name;
             private final byte[] bytes;
             private final int classFlags;
 
             // caller should make a defensive copy of the arguments if needed
             // before calling this constructor
-            private ClassDefiner(Lookup lookup, byte[] bytes, Set<ClassOption> options, int flags) {
+            private ClassDefiner(Lookup lookup, byte[] bytes, int flags) {
+                // defining an ordinary class which must be a strongly referenced by its defining loader
+                assert ((flags & HIDDEN_CLASS) != 0 || (flags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK);
                 this.lookup = lookup;
                 this.bytes = bytes;
-                this.classFlags = flags | ClassOption.optionsToFlag(options);
+                this.classFlags = flags;
                 this.name = className(bytes);
 
                 int index = name.lastIndexOf('.');
                 String pn = (index == -1) ? "" : name.substring(0, index);
                 if (!pn.equals(lookup.lookupClass().getPackageName())) {

@@ -2013,10 +2103,11 @@
                 }
             }
 
             // skip package name check
             private ClassDefiner(Lookup lookup, String name, byte[] bytes, int flags) {
+                assert ((flags & HIDDEN_CLASS) != 0 || (flags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK);
                 this.lookup = lookup;
                 this.bytes = bytes;
                 this.classFlags = flags;
                 this.name = name;
             }

@@ -3739,14 +3830,13 @@
         static ConcurrentHashMap<MemberName, DirectMethodHandle> LOOKASIDE_TABLE = new ConcurrentHashMap<>();
 
         /**
          * The set of class options that specify whether a hidden class created by
          * {@link Lookup#defineHiddenClass(byte[], boolean, ClassOption...)
-         * Lookup::defineHiddenMethod} method is dynamically added as
-         * a new member to the nest of a lookup class and whether a hidden class
-         * is weak such that it may be unloaded while its defining class loader
-         * is <a href="../ref/package.html#reachability">strongly reachable</a>.
+         * Lookup::defineHiddenClass} method is dynamically added as a new member
+         * to the nest of a lookup class and/or whether a hidden class has
+         * a strong relationship with the class loader marked as its defining loader.
          *
          * @since 15
          */
         public enum ClassOption {
             /**

@@ -3760,18 +3850,26 @@
              * @see Class#getNestHost()
              */
             NESTMATE(NESTMATE_CLASS),
 
             /**
-             * This class option specifies the hidden class be weak so that
-             * the class is not strongly referenced by its defining class loader.
-             * A weak class may be unloaded while its defining class loader is
-             * <a href="../ref/package.html#reachability">strongly reachable</a>.
+             *
+             * This class option specifies the hidden class to have a <em>strong</em>
+             * relationship with the class loader marked as its defining loader,
+             * as a normal class or interface has with its own defining loader.
+             * This means that the hidden class may be unloaded if and only if
+             * its defining loader is not reachable and thus may be reclaimed
+             * by a garbage collector (JLS 12.7).
+             *
+             * <p> By default, a hidden class or interface may be unloaded
+             * even if the class loader that is marked as its defining loader is
+             * <a href="../ref/package.html#reachability">reachable</a>.
+
              *
              * @jls 12.7 Unloading of Classes and Interfaces
              */
-            WEAK(WEAK_CLASS);
+            STRONG(STRONG_LOADER_LINK);
 
             /* the flag value is used by VM at define class time */
             private final int flag;
             ClassOption(int flag) {
                 this.flag = flag;
< prev index next >