< prev index next >

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

Print this page

        

@@ -23,10 +23,11 @@
  * questions.
  */
 
 package java.lang.invoke;
 
+import jdk.internal.access.JavaLangAccess;
 import jdk.internal.access.SharedSecrets;
 import jdk.internal.module.IllegalAccessLogger;
 import jdk.internal.org.objectweb.asm.ClassReader;
 import jdk.internal.reflect.CallerSensitive;
 import jdk.internal.reflect.Reflection;

@@ -43,24 +44,24 @@
 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;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import static java.lang.invoke.MethodHandles.Lookup.ClassProperty.*;
 import static java.lang.invoke.MethodHandleImpl.Intrinsic;
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
 import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
 import static java.lang.invoke.MethodType.methodType;
 

@@ -178,19 +179,23 @@
      * shared with trusted parties without giving away {@code PRIVATE} and {@code PACKAGE}
      * access to the caller.
      * @param targetClass the target class
      * @param lookup the caller lookup object
      * @return a lookup object for the target class, with private access
-     * @throws IllegalArgumentException if {@code targetClass} is a primitve type or array class
+     * @throws IllegalArgumentException if {@code targetClass} is a primitive type or array class
      * @throws NullPointerException if {@code targetClass} or {@code caller} is {@code null}
      * @throws IllegalAccessException if the access check specified above fails
      * @throws SecurityException if denied by the security manager
      * @since 9
      * @spec JPMS
      * @see Lookup#dropLookupMode
      */
     public static Lookup privateLookupIn(Class<?> targetClass, Lookup lookup) throws IllegalAccessException {
+        if (lookup.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())

@@ -773,17 +778,16 @@
          * 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, FULL_POWER_MODES);
-            // make sure we haven't accidentally picked up a privileged class:
-            checkUnprivilegedlookupClass(lookupClass);
         }
 
         private Lookup(Class<?> lookupClass, int allowedModes) {
             this.lookupClass = lookupClass;
             this.allowedModes = allowedModes;
+            assert !lookupClass.isPrimitive() && !lookupClass.isArray();
         }
 
         /**
          * Creates a lookup on the specified new lookup class.
          * The resulting object will report the specified

@@ -817,17 +821,24 @@
          * which may change due to this operation.
          *
          * @param requestedLookupClass the desired lookup class for the new lookup object
          * @return a lookup object which reports the desired lookup class, or the same object
          * if there is no change
+         * @throws IllegalArgumentException if {@code requestedLookupClass} is
+         * a primitive type or array class
          * @throws NullPointerException if the argument is null
          *
          * @revised 9
          * @spec JPMS
          */
         public Lookup in(Class<?> requestedLookupClass) {
             Objects.requireNonNull(requestedLookupClass);
+            if (requestedLookupClass.isPrimitive())
+                throw new IllegalArgumentException(requestedLookupClass + " is a primitive class");
+            if (requestedLookupClass.isArray())
+                throw new IllegalArgumentException(requestedLookupClass + " is an array class");
+
             if (allowedModes == TRUSTED)  // IMPL_LOOKUP can make any lookup at all
                 return new Lookup(requestedLookupClass, FULL_POWER_MODES);
             if (requestedLookupClass == this.lookupClass)
                 return this;  // keep same capabilities
             int newModes = (allowedModes & FULL_POWER_MODES);

@@ -898,10 +909,14 @@
         /**
          * Defines a class to 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}.
          *
+         * This method is equivalent to calling
+         * {@link #defineClass(byte[], ClassProperty[])
+         * defineClass(bytes, (ClassProperty[])null)}.
+         *
          * <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
          * from a lookup originally created by suitably privileged code to a target class in

@@ -932,17 +947,291 @@
          * @see Lookup#privateLookupIn
          * @see Lookup#dropLookupMode
          * @see ClassLoader#defineClass(String,byte[],int,int,ProtectionDomain)
          */
         public Class<?> defineClass(byte[] bytes) throws IllegalAccessException {
+            return defineClass(bytes, (ClassProperty[])null);
+        }
+
+        /**
+         * Defines a class to 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}.
+         * The {@code props} parameter specifies the properties of the class.
+         *
+         * <p> A class can be defined with the following properties:
+         * <ul>
+         * <li>A {@linkplain ClassProperty#NESTMATE <em>nestmate</em>} of the lookup class,
+         *     i.e. in the same {@linkplain Class#getNestHost nest}
+         *     of the lookup class.  The class will have access to the private members
+         *     of all classes and interfaces in the same nest.
+         *     </li>
+         * <li>A {@linkplain ClassProperty#HIDDEN <em>hidden</em>} class,
+         *     i.e. a class cannot be referenced by other classes.
+         *     A hidden class has the following properties:
+         *     <ul>
+         *     <li>Naming:
+         *     The name of this class is derived from the name of
+         *     the class in the class bytes so that the class name does not
+         *     collide with other classes defined to the same class loader.
+         *     <li>Class resolution:
+         *     The Java virtual machine does not find a hidden class with
+         *     its name.  A hidden class can reference its members
+         *     locally with the name of the class in the class bytes as if
+         *     a non-hidden class. The name returned by {@link Class#getName()}
+         *     is not known when the class bytes are generated.
+         *     <li>Class retransformation:
+         *     The class is not modifiable by Java agents or tool agents using
+         *     the <a href="{@docRoot}/../specs/jvmti.html">JVM Tool Interface</a>.
+         *     </ul>
+         *     </li>
+         * <li>A {@linkplain ClassProperty#WEAK <em>weak</em>} class,
+         *     i.e. a class may be unloaded even if its defining class loader is
+         *     <a href="../ref/package.html#reachability">reachable</a>,
+         *     as if the defining class loader would only hold a
+         *     {@linkplain java.lang.ref.WeakReference weak reference} of
+         *     the class.
+         *     A weak class is hidden.  If the {@code WEAK} property is set,
+         *     then it implies that {@code HIDDEN} property is also set.</li>
+         * </ul>
+         *
+         * <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 from a lookup originally created by
+         * suitably privileged code to a target class in the runtime package).
+         * If the class is defined as a {@linkplain ClassProperty#NESTMATE nestmate}
+         * then the {@linkplain #lookupModes() lookup modes} for this lookup must
+         * include {@link #PRIVATE PRIVATE} access. </p>
+         *
+         * <p> The {@code bytes} parameter is the class bytes of a valid class file
+         * (as defined by the <em>The Java Virtual Machine Specification</em>)
+         * with a class name in the same package as the lookup class.
+         * The class bytes of a nestmate class must not contain
+         * the {@code NestHost} attribute nor the {@code NestMembers} attribute. </p>
+         *
+         * <p> If there is a security manager, its {@code checkPermission} method is first called
+         * to check {@code RuntimePermission("defineClass")}. </p>
+         *
+         * <p> This method does not run the class initializer. The class initializer
+         * may run at a later time, as detailed in section 12.4 of the The Java Language Specification.
+         *
+         * <p> The class can obtain {@code classData} by calling
+         * the {@link Lookup#classData()} method of its {@code Lookup} object.
+         *
+         * @apiNote  An implementation of the Java Progamming Language may
+         * unload classes as specified in section 12.7 of the Java Language Specification.
+         * A class or interface may be unloaded if and only if
+         * its defining class loader may be reclaimed by the garbage collector.
+         * If the implementation supports class loading, a weak class
+         * may become weakly reachable as if the defining class loader would
+         * only hold a {@linkplain java.lang.ref.WeakReference weak reference}
+         * of the class.
+         *
+         * @param bytes      the class bytes
+         * @param props {@linkplain ClassProperty class properties}
+         * @return the {@code Class} object for the class
+         *
+         * @throws IllegalArgumentException the bytes are for a class in a different package
+         *                                  to the lookup class
+         * @throws IllegalAccessException   if this lookup does not have {@code PACKAGE} access, or
+         *                                  if {@code properties} contains {@code NESTMATE} but this lookup
+         *                                  does not have {@code PRIVATE} access
+         * @throws LinkageError             if the class is malformed ({@code ClassFormatError}), cannot be
+         *                                  verified ({@code VerifyError}), is already defined,
+         *                                  or another linkage error occurs
+         * @throws SecurityException        if denied by the security manager
+         * @throws NullPointerException     if {@code bytes} is {@code null}
+         *
+         * @since 12
+         * @jls 12.7 Unloading of Classes and Interfaces
+         * @see Lookup#privateLookupIn(Class, Lookup)
+         * @see Lookup#dropLookupMode(int)
+         */
+        public Class<?> defineClass(byte[] bytes, ClassProperty... props) throws IllegalAccessException {
+            Objects.requireNonNull(bytes);
+
+            // clone the properties before access
+            Set<ClassProperty> properties;
+            if (props == null || props.length == 0) {
+                properties = EMPTY_PROPS;
+            } else {
+                properties = Set.of(props);
+            }
+
+            // Is it ever possible to create Lookup for int.class or Object[].class?
+            assert !lookupClass.isPrimitive() && !lookupClass.isArray();
+
+            if ((lookupModes() & PACKAGE) == 0){
+                throw new IllegalAccessException("Lookup does not have PACKAGE access");
+            }
+
+            if (properties.contains(NESTMATE) && (lookupModes() & PRIVATE) == 0){
+                throw new IllegalAccessException("Lookup does not have PRIVATE access");
+            }
+
+            assert (lookupModes() & (MODULE | PUBLIC)) != 0;
+
             SecurityManager sm = System.getSecurityManager();
             if (sm != null)
                 sm.checkPermission(new RuntimePermission("defineClass"));
-            if ((lookupModes() & PACKAGE) == 0)
+
+            return defineClassWithNoCheck(bytes, classPropertiesToFlags(properties));
+        }
+
+        /**
+         * Defines a class to 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} with
+         * the given class properties and {@code classData}.
+         *
+         * <p> This method defines a class as if calling
+         * {@link #defineClass(byte[], ClassProperty...) defineClass(bytes, props)}
+         * and then the class initializer with an injected the {@code classData}
+         * as a pre-initialized static unnamed field.
+         * The injected pre-initialized static unnamed field can be
+         * obtained by calling the {@link Lookup#classData()} method of
+         * its {@code Lookup} object.
+         *
+         * <p> If there is a security manager, its {@code checkPermission} method is first called
+         * to check {@code RuntimePermission("defineClass")}. </p>
+         *
+         * @apiNote
+         * This method initializes the class, as opposed to the {@link #defineClass(byte[], ClassProperty...)}
+         * method which does not invoke {@code <clinit>}, because the returned {@code Class}
+         * is as if it contains a private static unnamed field that is initialized to
+         * the given {@code classData} along with other declared static fields
+         * via {@code <clinit>}.
+         *
+         * @param bytes      the class bytes
+         * @param classData pre-initialized class data
+         * @param props {@linkplain ClassProperty class properties}
+         * @return the {@code Class} object for the class
+         *
+         * @throws IllegalArgumentException the bytes are for a class in a different package
+         *                                  to the lookup class
+         * @throws IllegalAccessException   if this lookup does not have {@code PACKAGE} access, or
+         *                                  if {@code properties} contains {@code NESTMATE} but this lookup
+         *                                  does not have {@code PRIVATE} access
+         * @throws LinkageError             if the class is malformed ({@code ClassFormatError}), cannot be
+         *                                  verified ({@code VerifyError}), is already defined,
+         *                                  or another linkage error occurs
+         * @throws SecurityException        if denied by the security manager
+         * @throws NullPointerException     if {@code bytes} or {@code classData} is {@code null}
+         *
+         * @since 12
+         * @jls 12.7 Unloading of Classes and Interfaces
+         * @see Lookup#privateLookupIn(Class, Lookup)
+         * @see Lookup#dropLookupMode(int)
+         */
+        public Class<?> defineClassWithClassData(byte[] bytes, Object classData, ClassProperty... props)
+                throws IllegalAccessException
+        {
+            Objects.requireNonNull(bytes);
+            Objects.requireNonNull(classData);
+
+            // Is it ever possible to create Lookup for int.class or Object[].class?
+            assert !lookupClass.isPrimitive() && !lookupClass.isArray();
+
+            if ((lookupModes() & PACKAGE) == 0){
                 throw new IllegalAccessException("Lookup does not have PACKAGE access");
-            assert (lookupModes() & (MODULE|PUBLIC)) != 0;
+            }
 
+            Set<ClassProperty> properties;
+            if (props == null || props.length == 0) {
+                properties = EMPTY_PROPS;
+            } else {
+                properties = Set.of(props);
+            }
+
+            if (properties.contains(NESTMATE) && (lookupModes() & PRIVATE) == 0){
+                throw new IllegalAccessException("Lookup does not have PRIVATE access");
+            }
+
+            assert (lookupModes() & (MODULE | PUBLIC)) != 0;
+
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null)
+                sm.checkPermission(new RuntimePermission("defineClass"));
+
+            return defineClassWithNoCheck(bytes, classPropertiesToFlags(properties), classData);
+        }
+
+        private static int classPropertiesToFlags(Set<ClassProperty> props) {
+            if (props.isEmpty()) return 0;
+
+            int flags = 0;
+            for (ClassProperty cp : props) {
+                flags |= cp.flag;
+                if (cp == WEAK) {
+                    // weak class property implies hidden
+                    flags |= HIDDEN.flag;
+                }
+            }
+            return flags;
+        }
+
+        /**
+         * Returns the class data associated with this lookup class.
+         * If this lookup class was defined via
+         * {@link #defineClassWithClassData(byte[], Object, ClassProperty...)
+         * defineClassWithClassData(bytes, classData, properties)}
+         * then the supplied {@code classData} object is returned; otherwise,
+         * {@code null}.
+         *
+         * <p> This method will invoke the static class initializer of
+         * this lookup class if it has not been initialized.
+         *
+         * @apiNote
+         * A class data can be considered as
+         * private static unnamed field that has been pre-initialized
+         * and supplied at define class time.
+         *
+         * <p> For example a class can pack one or more pre-initialized objects
+         * in a {@code List} as the class data and at class initialization
+         * time unpack them for subsequent access.
+         * The class data is {@code List.of(o1, o2, o3....)}
+         * passed to {@link #defineClassWithClassData(byte[], Object, ClassProperty...)} where
+         * {@code <clinit>} of the class bytes does the following:
+         *
+         * <pre>{@code
+         *     private static final T t;
+         *     private static final R r;
+         *     static {
+         *        List<Object> data = (List<Object>) MethodHandles.lookup().classData();
+         *        t = (T)data.get(0);
+         *        r = (R)data.get(1);
+         *     }
+         *}</pre>
+         *
+         * @return the class data if this lookup class was defined via
+         * {@link #defineClassWithClassData(byte[], Object, ClassProperty...)}; otherwise {@code null}.
+         *
+         * @throws IllegalAccessException if this lookup does not have {@code PRIVATE} access
+         * @since 12
+         */
+        public Object classData() throws IllegalAccessException {
+            if ((lookupModes() & PRIVATE) == 0){
+                throw new IllegalAccessException("Lookup does not have PRIVATE access");
+            }
+
+            // should we allow clearing?  getAndClearClassData
+            return MethodHandleNatives.classData(lookupClass);
+        }
+
+        // package-private
+        static final int HIDDEN_NESTMATE = NESTMATE_CLASS|NONFINDABLE_CLASS|ACCESS_VM_ANNOTATIONS;
+        static final int WEAK_HIDDEN_NESTMATE =  WEAK_CLASS|NESTMATE_CLASS|NONFINDABLE_CLASS|ACCESS_VM_ANNOTATIONS;
+        static final Set<ClassProperty> EMPTY_PROPS = Set.of();
+
+        Class<?> defineClassWithNoCheck(byte[] bytes, int flags) {
+            return defineClassWithNoCheck(bytes, flags, null);
+        }
+
+        Class<?> defineClassWithNoCheck(byte[] bytes, int flags, Object classData) {
+            // Can't use lambda during bootstrapping
             // parse class bytes to get class name (in internal form)
             bytes = bytes.clone();
             String name;
             try {
                 ClassReader reader = new ClassReader(bytes);

@@ -963,38 +1252,41 @@
             } 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");
+                throw new IllegalArgumentException(cn + " not in same package as lookup class: " + lookupClass.getName());
+            }
+
+            if ((flags & NONFINDABLE_CLASS) != 0) {
+                // ## TODO use '/' as in the name of the VM anonymous class.
+                cn = cn + '\\' + ++seq;
             }
 
             // 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);
+            Class<?> clazz = JLA.defineClass(loader, lookupClass, cn, bytes, pd, flags, classData);
+            assert clazz.getClassLoader() == lookupClass.getClassLoader()
+                   && clazz.getPackageName().equals(lookupClass.getPackageName());
+
             return clazz;
         }
 
+        private static volatile int seq = 0;
+
         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, TRUSTED);

@@ -1003,10 +1295,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, (PUBLIC|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);
         }

@@ -1284,18 +1578,18 @@
          * load the requested class, and then determines whether the class is accessible to this lookup object.
          *
          * @param targetName the fully qualified name of the class to be looked up.
          * @return the requested class.
          * @exception SecurityException if a security manager is present and it
-         *            <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+         * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws LinkageError if the linkage fails
-         * @throws ClassNotFoundException if the class cannot be loaded by the lookup class' loader.
-         * @throws IllegalAccessException if the class is not accessible, using the allowed access
-         * modes.
-         * @exception SecurityException if a security manager is present and it
-         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+         * @throws ClassNotFoundException if the class cannot be loaded by the lookup class' loader
+         *                                or the class is not {@linkplain Class#isHidden hidden}
+         * @throws IllegalAccessException if the class is not accessible, using the allowed access modes.
+
          * @since 9
+         * @see Class#isHidden
          */
         public Class<?> findClass(String targetName) throws ClassNotFoundException, IllegalAccessException {
             Class<?> targetClass = Class.forName(targetName, false, lookupClass.getClassLoader());
             return accessClass(targetClass);
         }

@@ -2552,10 +2846,56 @@
             // oops
             throw newIllegalArgumentException("bad MethodHandle constant #"+member);
         }
 
         static ConcurrentHashMap<MemberName, DirectMethodHandle> LOOKASIDE_TABLE = new ConcurrentHashMap<>();
+
+        /**
+         * Property of a class to be defined via the
+         * {@link Lookup#defineClass(byte[], ClassProperty[]) Lookup.defineClass} method.
+         *
+         * @since 12
+         * @see Lookup#defineClass(byte[], ClassProperty[])
+         * @see Lookup#defineClassWithClassData(byte[], Object, ClassProperty[])
+         */
+        public enum ClassProperty {
+            /**
+             * A nestmate is a class that is in the same {@linkplain Class#getNestHost nest}
+             * of a lookup class.  It has access to the private members of all
+             * classes and interfaces in the same nest.
+             *
+             * @see Class#getNestHost()
+             */
+            NESTMATE(NESTMATE_CLASS),
+
+            /**
+             * A hidden class is a class that cannot be referenced by other
+             * classes.  A Java Virtual Machine implementation may hide
+             * the hidden frames from {@linkplain Throwable#getStackTrace()
+             * stack traces}.
+             *
+             * @see Class#isHidden()
+             * @see StackWalker.Option#SHOW_HIDDEN_FRAMES
+             */
+            HIDDEN(NONFINDABLE_CLASS),
+
+            /**
+             * A weak class is a class that may be unloaded even if
+             * its defining class loader is
+             * <a href="../ref/package.html#reachability">reachable</a>.
+             * A weak class is {@linkplain #HIDDEN hidden}.
+             *
+             * @jls 12.7 Unloading of Classes and Interfaces
+             */
+            WEAK(WEAK_CLASS);
+
+            /* the flag value is used by VM at define class time */
+            final int flag;
+            ClassProperty(int flag) {
+                this.flag = flag;
+            }
+        }
     }
 
     /**
      * Produces a method handle constructing arrays of a desired type,
      * as if by the {@code anewarray} bytecode.
< prev index next >