mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
8238358: Implementation of JEP 371: Hidden Classes
Co-authored-by: Lois Foltan <lois.foltan@oracle.com> Co-authored-by: David Holmes <david.holmes@oracle.com> Co-authored-by: Harold Seigel <harold.seigel@oracle.com> Co-authored-by: Serguei Spitsyn <serguei.spitsyn@oracle.com> Co-authored-by: Alex Buckley <alex.buckley@oracle.com> Co-authored-by: Jamsheed Mohammed C M <jamsheed.c.m@oracle.com> Co-authored-by: Jan Lahoda <jan.lahoda@oracle.com> Co-authored-by: Amy Lu <amy.lu@oracle.com> Reviewed-by: alanb, cjplummer, coleenp, dholmes, dlong, forax, jlahoda, psandoz, plevart, sspitsyn, vromero
This commit is contained in:
parent
642041adbc
commit
7cc1371059
198 changed files with 9526 additions and 1575 deletions
|
@ -25,9 +25,12 @@
|
|||
|
||||
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;
|
||||
|
@ -45,8 +48,6 @@ 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;
|
||||
|
@ -219,6 +220,10 @@ public class MethodHandles {
|
|||
* @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())
|
||||
|
@ -262,6 +267,57 @@ public class MethodHandles {
|
|||
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>.
|
||||
|
@ -517,7 +573,7 @@ public class MethodHandles {
|
|||
* 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}.)
|
||||
* (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>"}).
|
||||
|
@ -1400,8 +1456,6 @@ public class MethodHandles {
|
|||
*/
|
||||
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) {
|
||||
|
@ -1508,7 +1562,7 @@ public class MethodHandles {
|
|||
}
|
||||
// Allow nestmate lookups to be created without special privilege:
|
||||
if ((newModes & PRIVATE) != 0
|
||||
&& !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) {
|
||||
&& !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) {
|
||||
newModes &= ~(PRIVATE|PROTECTED);
|
||||
}
|
||||
if ((newModes & (PUBLIC|UNCONDITIONAL)) != 0
|
||||
|
@ -1577,9 +1631,12 @@ public class MethodHandles {
|
|||
}
|
||||
|
||||
/**
|
||||
* Defines a class to the same class loader and in the same runtime package and
|
||||
* 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}.
|
||||
* {@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
|
||||
|
@ -1602,11 +1659,12 @@ public class MethodHandles {
|
|||
*
|
||||
* @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 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 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}
|
||||
|
@ -1617,66 +1675,537 @@ public class MethodHandles {
|
|||
* @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");
|
||||
}
|
||||
|
||||
// parse class bytes to get class name (in internal form)
|
||||
bytes = bytes.clone();
|
||||
String name;
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* <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}:
|
||||
* <ul>
|
||||
* <li> {@link Class#getName()} returns the string {@code GN + "/" + <suffix>},
|
||||
* even though this is not a valid binary class or interface name.</li>
|
||||
* <li> {@link Class#descriptorString()} returns the string
|
||||
* {@code "L" + N + "." + <suffix> + ";"},
|
||||
* even though this is not a valid type descriptor name.</li>
|
||||
* <li> {@link Class#describeConstable()} returns an empty optional as {@code C}
|
||||
* cannot be described in {@linkplain java.lang.constant.ClassDesc nominal form}.</li>
|
||||
* </ul>
|
||||
* </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);
|
||||
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().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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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");
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
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 = 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(); }
|
||||
|
||||
|
@ -1689,6 +2218,8 @@ public class MethodHandles {
|
|||
*/
|
||||
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."))
|
||||
|
@ -1749,7 +2280,7 @@ public class MethodHandles {
|
|||
return cname + "/package";
|
||||
case FULL_POWER_MODES & (~PROTECTED):
|
||||
case FULL_POWER_MODES & ~(PROTECTED|MODULE):
|
||||
return cname + "/private";
|
||||
return cname + "/private";
|
||||
case FULL_POWER_MODES:
|
||||
case FULL_POWER_MODES & (~MODULE):
|
||||
return cname;
|
||||
|
@ -2639,8 +3170,13 @@ return mh1;
|
|||
|
||||
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);
|
||||
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()));
|
||||
|
@ -3201,7 +3737,8 @@ return mh1;
|
|||
}
|
||||
refc = lookupClass();
|
||||
}
|
||||
return VarHandles.makeFieldHandle(getField, refc, getField.getFieldType(), this.allowedModes == TRUSTED);
|
||||
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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue