< 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 >