< prev index next >
src/java.base/share/classes/java/lang/invoke/MethodHandles.java
Print this page
rev 58565 : 8238358: Implementation of JEP 371: Hidden Classes
Reviewed-by: duke
Contributed-by: mandy.chung@oracle.com, lois.foltan@oracle.com, david.holmes@oracle.com, harold.seigel@oracle.com, serguei.spitsyn@oracle.com, alex.buckley@oracle.com, jamsheed.c.m@oracle.com
rev 58566 : [mq]: hidden-class-3-detla
rev 58567 : [mq]: rename-isHidden
rev 58569 : imported patch hidden-class-3-jdarcy
*** 23,35 ****
--- 23,38 ----
* 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,54 ****
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;
--- 46,55 ----
*** 217,226 ****
--- 218,231 ----
* @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,270 ****
--- 266,326 ----
}
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}
*** 515,525 ****
* <p>
* The JVM imposes a similar requirement on {@code invokespecial} instruction,
* that the receiver argument must match both the resolved method <em>and</em>
* the current class. Again, this requirement is enforced by narrowing the
* type of the leading parameter to the resulting method handle.
! * (See the Java Virtual Machine Specification, section {@jmvs 4.10.1.9}.)
* <p>
* The JVM represents constructors and static initializer blocks as internal methods
* with special names ({@code "<init>"} and {@code "<clinit>"}).
* The internal syntax of invocation instructions allows them to refer to such internal
* methods as if they were normal methods, but the JVM bytecode verifier rejects them.
--- 571,581 ----
* <p>
* The JVM imposes a similar requirement on {@code invokespecial} instruction,
* that the receiver argument must match both the resolved method <em>and</em>
* the current class. Again, this requirement is enforced by narrowing the
* type of the leading parameter to the resulting method handle.
! * (See the Java Virtual Machine Specification, section {@jvms 4.10.1.9}.)
* <p>
* The JVM represents constructors and static initializer blocks as internal methods
* with special names ({@code "<init>"} and {@code "<clinit>"}).
* The internal syntax of invocation instructions allows them to refer to such internal
* methods as if they were normal methods, but the JVM bytecode verifier rejects them.
*** 1398,1409 ****
* 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());
--- 1454,1463 ----
*** 1575,1587 ****
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
* {@linkplain java.security.ProtectionDomain protection domain} as this lookup's
! * {@linkplain #lookupClass() lookup class}.
*
* <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
--- 1629,1644 ----
if (newModes == oldModes) return this; // return self if no change
return newLookup(lookupClass(), previousLookupClass(), newModes);
}
/**
! * Creates 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} 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
*** 1600,1684 ****
* #hasFullPrivilegeAccess() full privilege access}, its {@code checkPermission} method
* is first called to check {@code RuntimePermission("defineClass")}. </p>
*
* @param bytes the class bytes
* @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
! * @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 a security manager is present and it
* <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
* @throws NullPointerException if {@code bytes} is {@code null}
* @since 9
* @spec JPMS
* @see Lookup#privateLookupIn
* @see Lookup#dropLookupMode
* @see ClassLoader#defineClass(String,byte[],int,int,ProtectionDomain)
*/
public Class<?> defineClass(byte[] bytes) throws IllegalAccessException {
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");
! // parse class bytes to get class name (in internal form)
! bytes = bytes.clone();
! String name;
try {
ClassReader reader = new ClassReader(bytes);
! name = reader.getClassName();
} 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);
}
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);
--- 1657,2207 ----
* #hasFullPrivilegeAccess() full privilege access}, its {@code checkPermission} method
* is first called to check {@code RuntimePermission("defineClass")}. </p>
*
* @param bytes the class bytes
* @return the {@code Class} object for the class
+ * @throws IllegalAccessException if this lookup does not have {@code PACKAGE} access
+ * @throws ClassFormatError if {@code bytes} is not a {@code ClassFile} structure
* @throws IllegalArgumentException the bytes are for a class in a different package
* to the lookup class
! * @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 SecurityException if a security manager is present and it
* <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
* @throws NullPointerException if {@code bytes} is {@code null}
* @since 9
* @spec JPMS
* @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"));
}
! }
!
! /**
! * 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 {
! /**
! * Specifies that a 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),
!
! /**
! * Specifies that a hidden class has 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;
! }
! }
!
! /**
! * 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 {@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
! * {@linkplain Class#getPackage() package}, {@linkplain Class#getModule() module}
! * 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 {@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} serves as the
! * {@linkplain #lookupClass() lookup class} of the {@code Lookup} object
! * returned by this method. {@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.
! * 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 {@jvms 4.7.28}) and
! * the {@code NestMembers} attribute (JVMS {@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 {@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#isHidden()
! * @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");
! }
!
! return makeHiddenClassDefiner(bytes.clone(), Set.of(options), 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#isHidden()
! */
! /* 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");
! }
!
! return makeHiddenClassDefiner(bytes.clone(), Set.of(options), false)
! .defineClassAsLookup(true, classData);
! }
!
! /*
! * Validates the given bytes to be a class or interface and the class name
! * is in the same package as the lookup class.
! *
! * This method returns the class name.
! */
! private String validateAndGetClassName(byte[] bytes) {
try {
ClassReader reader = new ClassReader(bytes);
! if ((reader.getAccess() & Opcodes.ACC_MODULE) != 0) {
! throw newIllegalArgumentException("Not a class or interface: ACC_MODULE flag is set");
! }
! String name = reader.getClassName().replace('/', '.');
! int index = name.lastIndexOf('.');
! String pn = (index == -1) ? "" : name.substring(0, index);
! if (!pn.equals(lookupClass.getPackageName())) {
! throw newIllegalArgumentException(name + " not in same package as lookup class: " +
! lookupClass.getName());
! }
! return name;
! } catch (IllegalArgumentException e) {
! throw e;
} catch (RuntimeException e) {
// ASM exceptions are poorly specified
ClassFormatError cfe = new ClassFormatError();
cfe.initCause(e);
throw cfe;
}
+ }
!
! /*
! * Returns a ClassDefiner that creates a {@code Class} object of a normal class
! * from the given bytes.
! *
! * Caller should make a defensive copy of the arguments if needed
! * before calling this factory method.
! *
! * @throws IllegalArgumentException if {@code bytes} is not a class or interface or
! * {@bytes} denotes a class in a different package than the lookup class
! */
! private ClassDefiner makeClassDefiner(byte[] bytes) {
! return new ClassDefiner(this, validateAndGetClassName(bytes), bytes, STRONG_LOADER_LINK);
}
!
! /**
! * Returns a ClassDefiner that creates a {@code Class} object of a hidden class
! * from the given bytes. The name must be in the same package as the lookup class.
! *
! * Caller should make a defensive copy of the arguments if needed
! * before calling this factory method.
! *
! * @param bytes class bytes
! * @return ClassDefiner that defines a hidden class of the given bytes.
! *
! * @throws IllegalArgumentException if {@code bytes} is not a class or interface or
! * {@bytes} denotes a class in a different package than the lookup class
! */
! ClassDefiner makeHiddenClassDefiner(byte[] bytes) {
! return makeHiddenClassDefiner(validateAndGetClassName(bytes), bytes, Set.of(), false);
! }
!
! /**
! * Returns a ClassDefiner that creates a {@code Class} object of a hidden class
! * from the given bytes and options.
! * The name must be in the same package as the lookup class.
! *
! * Caller should make a defensive copy of the arguments if needed
! * before calling this factory method.
! *
! * @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
! *
! * @throws IllegalArgumentException if {@code bytes} is not a class or interface or
! * {@bytes} denotes a class in a different package than the lookup class
! */
! ClassDefiner makeHiddenClassDefiner(byte[] bytes,
! Set<ClassOption> options,
! boolean accessVmAnnotations) {
! return makeHiddenClassDefiner(validateAndGetClassName(bytes), bytes, options, accessVmAnnotations);
! }
!
! /**
! * Returns a ClassDefiner that creates a {@code Class} object of a hidden class
! * from the given bytes. No package name check on the given name.
! *
! * @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. No package name check on the given name.
! *
! * @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;
!
! 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;
! }
}
private ProtectionDomain lookupClassProtectionDomain() {
ProtectionDomain pd = cachedProtectionDomain;
if (pd == null) {
! cachedProtectionDomain = pd = JLA.protectionDomain(lookupClass);
}
return pd;
}
// 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,1696 ****
--- 2210,2221 ----
* 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,2648 ****
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())
throw field.makeAccessException("static final field has no write access", this);
assert(isSetter
? MethodHandleNatives.refKindIsSetter(field.getReferenceKind())
: MethodHandleNatives.refKindIsGetter(field.getReferenceKind()));
@SuppressWarnings("deprecation")
Lookup lookup = f.isAccessible() ? IMPL_LOOKUP : this;
--- 3162,3178 ----
return unreflectField(f, true);
}
private MethodHandle unreflectField(Field f, boolean isSetter) throws IllegalAccessException {
MemberName field = new MemberName(f, isSetter);
! if (isSetter && field.isFinal()) {
! if (field.isStatic()) {
throw field.makeAccessException("static final field has no write access", this);
+ } else if (field.getDeclaringClass().isHidden()){
+ 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,3209 ****
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);
}
/** 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);
--- 3729,3740 ----
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 && !getField.getDeclaringClass().isHidden());
}
/** 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);
< prev index next >