< prev index next >

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

Print this page

        

@@ -23,13 +23,16 @@
  * questions.
  */
 
 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;
 import jdk.internal.vm.annotation.ForceInline;
 import sun.invoke.util.ValueConversions;
 import sun.invoke.util.VerifyAccess;

@@ -43,12 +46,10 @@
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.ReflectPermission;
 import java.nio.ByteOrder;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
 import java.security.ProtectionDomain;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Iterator;

@@ -217,10 +218,14 @@
      * @spec JPMS
      * @see Lookup#dropLookupMode
      * @see <a href="MethodHandles.Lookup.html#cross-module-lookup">Cross-module lookups</a>
      */
     public static Lookup privateLookupIn(Class<?> targetClass, Lookup caller) throws IllegalAccessException {
+        if (caller.allowedModes == Lookup.TRUSTED) {
+            return new Lookup(targetClass);
+        }
+
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
         if (targetClass.isPrimitive())
             throw new IllegalArgumentException(targetClass + " is a primitive class");
         if (targetClass.isArray())

@@ -261,10 +266,61 @@
         }
         return Lookup.newLookup(targetClass, newPreviousClass, newModes);
     }
 
     /**
+     * Returns the <em>class data</em> associated with the lookup class
+     * of the specified {@code Lookup} object, or {@code null}.
+     *
+     * <p> Classes can be created with class data by calling
+     * {@link Lookup#defineHiddenClassWithClassData(byte[], Object, Lookup.ClassOption...)
+     * Lookup::defineHiddenClassWithClassData}.
+     * A hidden class with a class data behaves as if the hidden class
+     * has a private static final unnamed field pre-initialized with
+     * the class data and this method is equivalent as if calling
+     * {@link ConstantBootstraps#getStaticFinal(Lookup, String, Class)} to
+     * obtain the value of such field corresponding to the class data.
+     *
+     * <p> The {@linkplain Lookup#lookupModes() lookup modes} for this lookup
+     * must have {@link Lookup#ORIGINAL ORIGINAL} access in order to retrieve
+     * the class data.
+     *
+     * @apiNote
+     * This method can be called as a bootstrap method for a dynamically computed
+     * constant.  A framework can create a hidden class with class data, for
+     * example that can be {@code List.of(o1, o2, o3....)} containing more than
+     * one live object.  The class data is accessible only to the lookup object
+     * created by the original caller but inaccessible to other members
+     * in the same nest.  If a framework passes security sensitive live objects
+     * to a hidden class via class data, it is recommended to load the value
+     * of class data as a dynamically computed constant instead of storing
+     * the live objects in private fields which are accessible to other
+     * nestmates.
+     *
+     * @param <T> the type to cast the class data object to
+     * @param caller the lookup context describing the class performing the
+     * operation (normally stacked by the JVM)
+     * @param name ignored
+     * @param type the type of the class data
+     * @return the value of the class data if present in the lookup class;
+     * otherwise {@code null}
+     * @throws IllegalAccessException if the lookup context does not have
+     * original caller access
+     * @throws ClassCastException if the class data cannot be converted to
+     * the specified {@code type}
+     * @see Lookup#defineHiddenClassWithClassData(byte[], Object, Lookup.ClassOption...)
+     * @since 15
+     */
+    static <T> T classData(Lookup caller, String name, Class<T> type) throws IllegalAccessException {
+        if (!caller.hasFullPrivilegeAccess()) {
+            throw new IllegalAccessException(caller + " does not have full privilege access");
+        }
+        Object classData = MethodHandleNatives.classData(caller.lookupClass);
+        return type.cast(classData);
+    }
+
+    /**
      * Performs an unchecked "crack" of a
      * <a href="MethodHandleInfo.html#directmh">direct method handle</a>.
      * The result is as if the user had obtained a lookup object capable enough
      * to crack the target method handle, called
      * {@link java.lang.invoke.MethodHandles.Lookup#revealDirect Lookup.revealDirect}

@@ -1398,12 +1454,10 @@
          * Must be called by from a method in this package,
          * which in turn is called by a method not in this package.
          */
         Lookup(Class<?> lookupClass) {
             this(lookupClass, null, FULL_POWER_MODES);
-            // make sure we haven't accidentally picked up a privileged class:
-            checkUnprivilegedlookupClass(lookupClass);
         }
 
         private Lookup(Class<?> lookupClass, Class<?> prevLookupClass, int allowedModes) {
             assert prevLookupClass == null || ((allowedModes & MODULE) == 0
                     && prevLookupClass.getModule() != lookupClass.getModule());

@@ -1575,13 +1629,16 @@
             if (newModes == oldModes) return this;  // return self if no change
             return newLookup(lookupClass(), previousLookupClass(), newModes);
         }
 
         /**
-         * Defines a class to the same class loader and in the same runtime package and
+         * Creates and links a class or interface from {@code bytes}
+         * with the same class loader and in the same runtime package and
          * {@linkplain java.security.ProtectionDomain protection domain} as this lookup's
-         * {@linkplain #lookupClass() lookup class}.
+         * {@linkplain #lookupClass() lookup class} as if calling
+         * {@link ClassLoader#defineClass(String,byte[],int,int,ProtectionDomain)
+         * ClassLoader::defineClass}.
          *
          * <p> The {@linkplain #lookupModes() lookup modes} for this lookup must include
          * {@link #PACKAGE PACKAGE} access as default (package) members will be
          * accessible to the class. The {@code PACKAGE} lookup mode serves to authenticate
          * that the lookup object was created by a caller in the runtime package (or derived

@@ -1615,70 +1672,476 @@
          * @see Lookup#privateLookupIn
          * @see Lookup#dropLookupMode
          * @see ClassLoader#defineClass(String,byte[],int,int,ProtectionDomain)
          */
         public Class<?> defineClass(byte[] bytes) throws IllegalAccessException {
+            ensureDefineClassPermission();
+            if ((lookupModes() & PACKAGE) == 0)
+                throw new IllegalAccessException("Lookup does not have PACKAGE access");
+            return makeClassDefiner(bytes.clone()).defineClass(false);
+        }
+
+        private void ensureDefineClassPermission() {
+            if (allowedModes == TRUSTED)  return;
+
             if (!hasFullPrivilegeAccess()) {
                 SecurityManager sm = System.getSecurityManager();
                 if (sm != null)
                     sm.checkPermission(new RuntimePermission("defineClass"));
             }
-            if ((lookupModes() & PACKAGE) == 0)
-                throw new IllegalAccessException("Lookup does not have PACKAGE access");
+        }
+
+        /**
+         * Creates a <em>hidden</em> class or interface from {@code bytes},
+         * returning a {@code Lookup} on the newly created class or interface.
+         *
+         * <p> Ordinarily, a class or interface {@code C} is created by a class loader,
+         * which either defines {@code C} directly or delegates to another class loader.
+         * A class loader defines {@code C} directly by invoking
+         * {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)
+         * ClassLoader::defineClass}, which causes the Java Virtual Machine
+         * to derive {@code C} from a purported representation in {@code class} file format.
+         * In situations where use of a class loader is undesirable, a class or interface
+         * {@code C} can be created by this method instead. This method is capable of
+         * defining {@code C}, and thereby creating it, without invoking
+         * {@code ClassLoader::defineClass}.
+         * Instead, this method defines {@code C} as if by arranging for
+         * the Java Virtual Machine to derive a nonarray class or interface {@code C}
+         * from a purported representation in {@code class} file format
+         * using the following rules:
+         *
+         * <ol>
+         * <li> The {@linkplain #lookupModes() lookup modes} for this {@code Lookup}
+         * must include {@linkplain #hasFullPrivilegeAccess() full privilege} access.
+         * This level of access is needed to create {@code C} in the module
+         * of the lookup class of this {@code Lookup}.</li>
+         *
+         * <li> The purported representation in {@code bytes} must be a {@code ClassFile}
+         * structure of a supported major and minor version. The major and minor version
+         * may differ from the {@code class} file version of the lookup class of this
+         * {@code Lookup}.</li>
+         *
+         * <li> The value of {@code this_class} must be a valid index in the
+         * {@code constant_pool} table, and the entry at that index must be a valid
+         * {@code CONSTANT_Class_info} structure. Let {@code N} be the binary name
+         * encoded in internal form that is specified by this structure. {@code N} must
+         * denote a class or interface in the same package as the lookup class.</li>
+         *
+         * <li> Let {@code CN} be the string {@code N + "." + <suffix>},
+         * where {@code <suffix>} is an unqualified name that is guaranteed to be unique
+         * during this execution of the JVM.
+         *
+         * <p> Let {@code newBytes} be the {@code ClassFile} structure given by
+         * {@code bytes} with an additional entry in the {@code constant_pool} table,
+         * indicating a {@code CONSTANT_Utf8_info} structure for {@code CN}, and
+         * where the {@code CONSTANT_Class_info} structure indicated by {@code this_class}
+         * refers to the new {@code CONSTANT_Utf8_info} structure.
+         *
+         * <p> Let {@code L} be the defining class loader of the lookup class of this {@code Lookup}.
+         *
+         * <p> {@code C} is derived with name {@code CN}, class loader {@code L}, and
+         * purported representation {@code newBytes} as if by the rules of JVMS 5.3.5,
+         * with the following adjustments:
+         * <ul>
+         * <li> The constant indicated by {@code this_class} is permitted to specify a name
+         * that includes a single {@code "."} character, even though this is not a valid
+         * binary class or interface name in internal form.</li>
+         *
+         * <li> The Java Virtual Machine marks {@code L} as the defining class loader of {@code C},
+         * but no class loader is recorded as an initiating class loader of {@code C}.</li>
+         *
+         * <li> {@code C} is considered to have the same runtime package and
+         * {@linkplain java.security.ProtectionDomain protection domain}
+         * as the lookup class of this {@code Lookup}.
+         *
+         * <li> Let {@code GN} be the binary name obtained by taking {@code N}
+         * (a binary name encoded in internal form) and replacing ASCII forward slashes with
+         * ASCII periods. For the instance of {@link java.lang.Class} representing {@code C},
+         * {@link Class#getName()} returns the string {@code GN + "/" + <suffix>}, even though
+         * this is not a valid binary class or interface name.</li>
+         * </ul>
+         * </li>
+         * </ol>
+         *
+         * <p> After {@code C} is derived, it is linked by the Java Virtual Machine.
+         * Linkage occurs as specified in JVMS 5.4.3, with the following adjustments:
+         * <ul>
+         * <li> During verification, whenever it is necessary to load the class named
+         * {@code CN}, the attempt succeeds, producing class {@code C}. No request is
+         * made of any class loader.</li>
+         *
+         * <li> On any attempt to resolve the entry in the run-time constant pool indicated
+         * by {@code this_class}, the symbolic reference is considered to be resolved to
+         * {@code C} and resolution always succeeds immediately.</li>
+         * </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
+         * 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.
+         * (Given the {@code Lookup} object returned this method, its lookup class
+         * is a {@code Class} object for which {@link Class#getName()} returns a string
+         * that is not a binary name.)
+         * A hidden class or interface is not discoverable by {@link Class#forName(String, boolean, ClassLoader)},
+         * {@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> 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 class loader marked 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 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> 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.
+         *
+         * @param bytes the bytes that make up the class data,
+         * in the format of a valid {@code class} file as defined by
+         * <cite>The Java Virtual Machine Specification</cite>.
+         * @param initialize if {@code true} the class will be initialized.
+         * @param options {@linkplain ClassOption class options}
+         * @return the {@code Lookup} object on the hidden class
+         *
+         * @throws IllegalAccessException if this {@code Lookup} does not have
+         * {@linkplain #hasFullPrivilegeAccess() full privilege} access
+         * @throws SecurityException if a security manager is present and it
+         * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+         * @throws ClassFormatError if {@code bytes} is not a {@code ClassFile} structure
+         * @throws UnsupportedClassVersionError if {@code bytes} is not of a supported major or minor version
+         * @throws IllegalArgumentException if {@code bytes} is not a class or interface or
+         * {@bytes} denotes a class in a different package than the lookup class
+         * @throws IncompatibleClassChangeError if the class or interface named as
+         * the direct superclass of {@code C} is in fact an interface, or if any of the classes
+         * or interfaces named as direct superinterfaces of {@code C} are not in fact interfaces
+         * @throws ClassCircularityError if any of the superclasses or superinterfaces of
+         * {@code C} is {@code C} itself
+         * @throws VerifyError if the newly created class cannot be verified
+         * @throws LinkageError if the newly created class cannot be linked for any other reason
+         * @throws NullPointerException if any parameter is {@code null}
+         *
+         * @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);
+            Objects.requireNonNull(options);
+
+            ensureDefineClassPermission();
+            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, false).defineClassAsLookup(initialize);
+        }
+
+        /**
+         * Creates a <em>hidden</em> class or interface from {@code bytes} with associated
+         * {@linkplain MethodHandles#classData(Lookup, String, Class) class data},
+         * returning a {@code Lookup} on the newly created class or interface.
+         *
+         * <p> This method is equivalent to calling
+         * {@link #defineHiddenClass(byte[], boolean, ClassOption...) defineHiddenClass(bytes, true, options)}
+         * as if the hidden class has a private static final unnamed field whose value
+         * is initialized to {@code classData} right before the class initializer is
+         * executed.  The newly created class is linked and initialized by the Java
+         * Virtual Machine.
+         *
+         * <p> The {@link MethodHandles#classData(Lookup, String, Class) MethodHandles::classData}
+         * method can be used to retrieve the {@code classData}.
+         *
+         * @param bytes     the class bytes
+         * @param classData pre-initialized class data
+         * @param options   {@linkplain ClassOption class options}
+         * @return the {@code Lookup} object on the hidden class
+         *
+         * @throws IllegalAccessException if this {@code Lookup} does not have
+         * {@linkplain #hasFullPrivilegeAccess() full privilege} access
+         * @throws SecurityException if a security manager is present and it
+         * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+         * @throws ClassFormatError if {@code bytes} is not a {@code ClassFile} structure
+         * @throws UnsupportedClassVersionError if {@code bytes} is not of a supported major or minor version
+         * @throws IllegalArgumentException if {@code bytes} is not a class or interface or
+         * {@bytes} denotes a class in a different package than the lookup class
+         * @throws IncompatibleClassChangeError if the class or interface named as
+         * the direct superclass of {@code C} is in fact an interface, or if any of the classes
+         * or interfaces named as direct superinterfaces of {@code C} are not in fact interfaces
+         * @throws ClassCircularityError if any of the superclasses or superinterfaces of
+         * {@code C} is {@code C} itself
+         * @throws VerifyError if the newly created class cannot be verified
+         * @throws LinkageError if the newly created class cannot be linked for any other reason
+         * @throws NullPointerException if any parameter is {@code null}
+         *
+         * @since 15
+         * @see Lookup#defineHiddenClass(byte[], boolean, ClassOption...)
+         * @see Class#isHiddenClass()
+         */
+        /* package-private */ Lookup defineHiddenClassWithClassData(byte[] bytes, Object classData, ClassOption... options)
+                throws IllegalAccessException
+        {
+            Objects.requireNonNull(bytes);
+            Objects.requireNonNull(classData);
+            Objects.requireNonNull(options);
+
+            ensureDefineClassPermission();
+            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, false)
+                       .defineClassAsLookup(true, classData);
+        }
+
+        private ClassDefiner makeClassDefiner(byte[] bytes) {
+            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.
+         *
+         * @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,
+                                            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);
+        }
+
+        /**
+         * 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
+         * @param options class options
+         * @param accessVmAnnotations true to give the hidden class access to VM annotations
+         */
+        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, 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;
+                this.name = className(bytes);
+
+                int index = name.lastIndexOf('.');
+                String pn = (index == -1) ? "" : name.substring(0, index);
+                if (!pn.equals(lookup.lookupClass().getPackageName())) {
+                    throw newIllegalArgumentException(name + " not in same package as lookup class: " +
+                            lookup.lookupClass().getName());
+                }
+            }
+
+            // 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;
+            }
+
+            String className() {
+                return name;
+            }
+
+            Class<?> defineClass(boolean initialize) {
+                return defineClass(initialize, null);
+            }
+
+            Lookup defineClassAsLookup(boolean initialize) {
+                Class<?> c = defineClass(initialize, null);
+                return new Lookup(c, null, FULL_POWER_MODES);
+            }
+
+            /**
+             * Defines the class of the given bytes and the given classData.
+             * If {@code initialize} parameter is true, then the class will be initialized.
+             *
+             * @param initialize true if the class to be initialized
+             * @param classData classData or null
+             * @return the class
+             *
+             * @throws LinkageError linkage error
+             */
+            Class<?> defineClass(boolean initialize, Object classData) {
+                Class<?> lookupClass = lookup.lookupClass();
+                ClassLoader loader = lookupClass.getClassLoader();
+                ProtectionDomain pd = (loader != null) ? lookup.lookupClassProtectionDomain() : null;
+                Class<?> c = JLA.defineClass(loader, lookupClass, name, bytes, pd, initialize, classFlags, classData);
+                assert !isNestmate() || c.getNestHost() == lookupClass.getNestHost();
+                return c;
+            }
+
+            Lookup defineClassAsLookup(boolean initialize, Object classData) {
+                // initialize must be true if classData is non-null
+                assert classData == null || initialize == true;
+                Class<?> c = defineClass(initialize, classData);
+                return new Lookup(c, null, FULL_POWER_MODES);
+            }
+
+            private boolean isNestmate() {
+                return (classFlags & NESTMATE_CLASS) != 0;
+            }
 
-            // parse class bytes to get class name (in internal form)
-            bytes = bytes.clone();
-            String name;
+            private static String className(byte[] bytes) {
             try {
                 ClassReader reader = new ClassReader(bytes);
-                name = reader.getClassName();
+                    if ((reader.getAccess() & Opcodes.ACC_MODULE) != 0) {
+                        throw newIllegalArgumentException("Not a class or interface: ACC_MODULE flag is set");
+                    }
+                    String name = reader.getClassName();
+                    return name.replace('/', '.');
+                } catch (IllegalArgumentException e) {
+                    throw e;
             } catch (RuntimeException e) {
                 // ASM exceptions are poorly specified
                 ClassFormatError cfe = new ClassFormatError();
                 cfe.initCause(e);
                 throw cfe;
             }
-
-            // get package and class name in binary form
-            String cn, pn;
-            int index = name.lastIndexOf('/');
-            if (index == -1) {
-                cn = name;
-                pn = "";
-            } else {
-                cn = name.replace('/', '.');
-                pn = cn.substring(0, index);
             }
-            if (!pn.equals(lookupClass.getPackageName())) {
-                throw new IllegalArgumentException("Class not in same package as lookup class");
-            }
-
-            // invoke the class loader's defineClass method
-            ClassLoader loader = lookupClass.getClassLoader();
-            ProtectionDomain pd = (loader != null) ? lookupClassProtectionDomain() : null;
-            String source = "__Lookup_defineClass__";
-            Class<?> clazz = SharedSecrets.getJavaLangAccess().defineClass(loader, cn, bytes, pd, source);
-            return clazz;
         }
 
         private ProtectionDomain lookupClassProtectionDomain() {
             ProtectionDomain pd = cachedProtectionDomain;
             if (pd == null) {
-                cachedProtectionDomain = pd = protectionDomain(lookupClass);
+                cachedProtectionDomain = pd = JLA.protectionDomain(lookupClass);
             }
             return pd;
         }
 
-        private ProtectionDomain protectionDomain(Class<?> clazz) {
-            PrivilegedAction<ProtectionDomain> pa = clazz::getProtectionDomain;
-            return AccessController.doPrivileged(pa);
-        }
-
         // cached protection domain
         private volatile ProtectionDomain cachedProtectionDomain;
 
-
         // Make sure outer class is initialized first.
         static { IMPL_NAMES.getClass(); }
 
         /** Package-private version of lookup which is trusted. */
         static final Lookup IMPL_LOOKUP = new Lookup(Object.class, null, TRUSTED);

@@ -1687,10 +2150,12 @@
          *  It can only be used to create method handles to publicly accessible
          *  members in packages that are exported unconditionally.
          */
         static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, null, UNCONDITIONAL);
 
+        static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
+
         private static void checkUnprivilegedlookupClass(Class<?> lookupClass) {
             String name = lookupClass.getName();
             if (name.startsWith("java.lang.invoke."))
                 throw newIllegalArgumentException("illegal lookupClass: "+lookupClass);
         }

@@ -2637,12 +3102,17 @@
             return unreflectField(f, true);
         }
 
         private MethodHandle unreflectField(Field f, boolean isSetter) throws IllegalAccessException {
             MemberName field = new MemberName(f, isSetter);
-            if (isSetter && field.isStatic() && field.isFinal())
+            if (isSetter && field.isFinal()) {
+                if (field.isStatic()) {
                 throw field.makeAccessException("static final field has no write access", this);
+                } else if (field.getDeclaringClass().isHiddenClass()){
+                    throw field.makeAccessException("final field in a hidden class has no write access", this);
+                }
+            }
             assert(isSetter
                     ? MethodHandleNatives.refKindIsSetter(field.getReferenceKind())
                     : MethodHandleNatives.refKindIsGetter(field.getReferenceKind()));
             @SuppressWarnings("deprecation")
             Lookup lookup = f.isAccessible() ? IMPL_LOOKUP : this;

@@ -3199,11 +3669,12 @@
                 if (!getField.getDeclaringClass().isAssignableFrom(lookupClass())) {
                     throw getField.makeAccessException("caller class must be a subclass below the method", lookupClass());
                 }
                 refc = lookupClass();
             }
-            return VarHandles.makeFieldHandle(getField, refc, getField.getFieldType(), this.allowedModes == TRUSTED);
+            return VarHandles.makeFieldHandle(getField, refc, getField.getFieldType(),
+                                             this.allowedModes == TRUSTED && !getField.getDeclaringClass().isHiddenClass());
         }
         /** Check access and get the requested constructor. */
         private MethodHandle getDirectConstructor(Class<?> refc, MemberName ctor) throws IllegalAccessException {
             final boolean checkSecurity = true;
             return getDirectConstructorCommon(refc, ctor, checkSecurity);

@@ -3311,10 +3782,65 @@
             // oops
             throw newIllegalArgumentException("bad MethodHandle constant #"+member);
         }
 
         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::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 {
+            /**
+             * This class option specifies the hidden class be added to
+             * {@linkplain Class#getNestHost nest} of a lookup class as
+             * a nestmate.
+             *
+             * <p> A hidden nestmate class has access to the private members of all
+             * classes and interfaces in the same nest.
+             *
+             * @see Class#getNestHost()
+             */
+            NESTMATE(NESTMATE_CLASS),
+
+            /**
+             *
+             * 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
+             */
+            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;
+            }
+
+            static int optionsToFlag(Set<ClassOption> options) {
+                int flags = 0;
+                for (ClassOption cp : options) {
+                    flags |= cp.flag;
+                }
+                return flags;
+            }
+        }
     }
 
     /**
      * Produces a method handle constructing arrays of a desired type,
      * as if by the {@code anewarray} bytecode.
< prev index next >