< prev index next >

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

Print this page

        

*** 23,32 **** --- 23,33 ---- * questions. */ package java.lang.invoke; + import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.module.IllegalAccessLogger; import jdk.internal.org.objectweb.asm.ClassReader; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection;
*** 43,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; --- 44,53 ----
*** 211,220 **** --- 210,223 ---- * @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())
*** 1093,1104 **** * to allow applications to check such cross-loader references. * These checks apply to both the {@code MethodHandles.Lookup} API * and the Core Reflection API * (as found on {@link java.lang.Class Class}). * <p> ! * If a security manager is present, member and class lookups are subject to ! * additional checks. * From one to three calls are made to the security manager. * Any of these calls can refuse access by throwing a * {@link java.lang.SecurityException SecurityException}. * Define {@code smgr} as the security manager, * {@code lookc} as the lookup class of the current lookup object, --- 1096,1107 ---- * to allow applications to check such cross-loader references. * These checks apply to both the {@code MethodHandles.Lookup} API * and the Core Reflection API * (as found on {@link java.lang.Class Class}). * <p> ! * If a security manager is present, member and class lookups are ! * subject to additional checks. * From one to three calls are made to the security manager. * Any of these calls can refuse access by throwing a * {@link java.lang.SecurityException SecurityException}. * Define {@code smgr} as the security manager, * {@code lookc} as the lookup class of the current lookup object,
*** 1138,1147 **** --- 1141,1157 ---- * </ul> * Security checks are performed after other access checks have passed. * Therefore, the above rules presuppose a member or class that is public, * or else that is being accessed from a lookup class that has * rights to access the member or class. + * <p> + * If a security manager is present and the current lookup object does not have + * <a href="MethodHandles.Lookup.html#privacc">private access</a>, then + * {@link #defineClass(byte[]) defineClass} and + * {@link #defineHiddenClass(byte[], boolean, ClassOption...) defineHiddenClass} + * call {@link SecurityManager#checkPermission smgr.checkPermission} + * with {@code RuntimePermission("defineClass")} is called. * * <h2><a id="callsens"></a>Caller sensitive methods</h2> * A small number of Java methods have a special property called caller sensitivity. * A <em>caller-sensitive</em> method can behave differently depending on the * identity of its immediate caller.
*** 1375,1395 **** * 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()); assert !lookupClass.isArray() && !lookupClass.isPrimitive(); this.lookupClass = lookupClass; this.prevLookupClass = prevLookupClass; this.allowedModes = allowedModes; } private static Lookup newLookup(Class<?> lookupClass, Class<?> prevLookupClass, int allowedModes) { // make sure we haven't accidentally picked up a privileged class: checkUnprivilegedlookupClass(lookupClass); --- 1385,1404 ---- * 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); } private Lookup(Class<?> lookupClass, Class<?> prevLookupClass, int allowedModes) { assert prevLookupClass == null || ((allowedModes & MODULE) == 0 && prevLookupClass.getModule() != lookupClass.getModule()); assert !lookupClass.isArray() && !lookupClass.isPrimitive(); this.lookupClass = lookupClass; this.prevLookupClass = prevLookupClass; this.allowedModes = allowedModes; + assert !lookupClass.isPrimitive() && !lookupClass.isArray(); } private static Lookup newLookup(Class<?> lookupClass, Class<?> prevLookupClass, int allowedModes) { // make sure we haven't accidentally picked up a privileged class: checkUnprivilegedlookupClass(lookupClass);
*** 1500,1511 **** /** * Creates a lookup on the same lookup class which this lookup object * finds members, but with a lookup mode that has lost the given lookup mode. * The lookup mode to drop is one of {@link #PUBLIC PUBLIC}, {@link #MODULE * MODULE}, {@link #PACKAGE PACKAGE}, {@link #PROTECTED PROTECTED} or {@link #PRIVATE PRIVATE}. ! * {@link #PROTECTED PROTECTED} is always ! * dropped and so the resulting lookup mode will never have this access capability. * When dropping {@code PACKAGE} then the resulting lookup will not have {@code PACKAGE} * or {@code PRIVATE} access. When dropping {@code MODULE} then the resulting lookup will * not have {@code MODULE}, {@code PACKAGE}, or {@code PRIVATE} access. If {@code PUBLIC} * is dropped then the resulting lookup has no access. If {@code UNCONDITIONAL} * is dropped then the resulting lookup has no access. --- 1509,1520 ---- /** * Creates a lookup on the same lookup class which this lookup object * finds members, but with a lookup mode that has lost the given lookup mode. * The lookup mode to drop is one of {@link #PUBLIC PUBLIC}, {@link #MODULE * MODULE}, {@link #PACKAGE PACKAGE}, {@link #PROTECTED PROTECTED} or {@link #PRIVATE PRIVATE}. ! * {@link #PROTECTED PROTECTED} is always dropped and ! * so the resulting lookup mode will never have this access capability. * When dropping {@code PACKAGE} then the resulting lookup will not have {@code PACKAGE} * or {@code PRIVATE} access. When dropping {@code MODULE} then the resulting lookup will * not have {@code MODULE}, {@code PACKAGE}, or {@code PRIVATE} access. If {@code PUBLIC} * is dropped then the resulting lookup has no access. If {@code UNCONDITIONAL} * is dropped then the resulting lookup has no access.
*** 1587,1649 **** if (sm != null) sm.checkPermission(new RuntimePermission("defineClass")); if ((lookupModes() & PACKAGE) == 0) throw new IllegalAccessException("Lookup does not have PACKAGE access"); assert (lookupModes() & (MODULE|PUBLIC)) != 0; ! // 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); --- 1596,1876 ---- if (sm != null) sm.checkPermission(new RuntimePermission("defineClass")); if ((lookupModes() & PACKAGE) == 0) throw new IllegalAccessException("Lookup does not have PACKAGE access"); assert (lookupModes() & (MODULE|PUBLIC)) != 0; + return makeClassDefiner(bytes.clone()).defineClass(false); + } + + private void checkDefineClassPermission() { + SecurityManager sm = System.getSecurityManager(); + if (sm == null) return; + if (allowedModes == TRUSTED) return; + + if (!hasPrivateAccess()) { + sm.checkPermission(new RuntimePermission("defineClass")); + } + } + + /** + * Creates a {@code Lookup} on a <em>hidden class</em> defined 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}. + * + * If {@code options} has {@link ClassOption#NESTMATE NESTMATE} class + * option, then the hidden class is added as a member into + * {@linkplain Class#getNestHost() the nest} of this lookup's lookup class. + * + * If {@code options} has {@link ClassOption#WEAK WEAK}, then + * the hidden class is weakly referenced from its defining class loader + * and may be unloaded while its defining class loader is strongly reachable. + * + * The hidden class is initialized if the {@code initialize} parameter is + * {@code true}. + * + * <p> A {@link Class#isHiddenClass() <em>hidden</em>} class has the + * following additional properties: + * <ul> + * <li>Naming: + * The name of a hidden class returned by {@link Class#getName()} is + * defined by the JVM of this form: + * {@code <fully-qualified binary name> + '/' + <suffix>}<br> + * where {@code <fully-qualified binary name>} is the class name + * from the class bytes and {@code <suffix>} must be an unique unqualified + * name (see JVMS 4.2.2) + * <li>Class resolution: + * A hidden class cannot be referenced in other classes and cannot be + * named as a field type, a method parameter type and a method return type. + * It is not discoverable by its class loader for example via + * {@link Class#forName(String, boolean, ClassLoader)}, + * {@link ClassLoader#loadClass(String, boolean)} and also bytecode linkage. + * <li>Class retransformation: + * A hidden class 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>. + * <li>Serialization: + * The default serialization mechanism records the name of a class in + * its serialized form and finds the class by name during deserialization. + * A <em>serializable</em> hidden class requires a custom serialization + * mechanism in order to ensure that instances are properly serialized + * and deserialized. + * </ul> + * + * <p> The {@linkplain #lookupModes() lookup modes} for this lookup must + * have {@code PRIVATE} and {@code MODULE} access to create a hidden class + * in the module of this lookup class. + * + * <p> The {@code bytes} parameter is the class bytes of a valid class file + * (as defined by the <em>The Java Virtual Machine Specification</em>) + * with a class name in the same package as the lookup class. + * + * @param bytes the class bytes + * @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 IllegalArgumentException the bytes are for a class in a different package + * to the lookup class + * @throws IllegalAccessException if this lookup does not have {@code PRIVATE} and {@code MODULE} 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 14 + * @see Class#isHiddenClass() + * @jvms 4.2.2 Unqualified Names + * @jls 12.3 Linking of Classes and Interfaces + * @jls 12.4 Initialization of Classes and Interfaces + */ + public Lookup defineHiddenClass(byte[] bytes, boolean initialize, ClassOption... options) + throws IllegalAccessException + { + Objects.requireNonNull(bytes); + + checkDefineClassPermission(); + if ((lookupModes() & (PRIVATE|MODULE)) != (PRIVATE|MODULE)){ + throw new IllegalAccessException(this + " does not have PRIVATE or MODULE access"); + } + + Set<ClassOption> opts = (options != null && options.length > 0) ? Set.of(options) : Set.of(); + Class<?> c = makeHiddenClassDefiner(bytes.clone(), opts).defineClass(initialize, null); + return new Lookup(c, null, FULL_POWER_MODES); + } + + /** + * Creates a {@code Lookup} on a <em>hidden class</em> with {@code classData} + * defined to the same class loader and in the same runtime package + * and {@linkplain java.security.ProtectionDomain protection domain} as + * this lookup's {@linkplain #lookupClass() lookup class} with + * the given class options. + * + * <p> This method is equivalent to calling + * {@link #defineHiddenClass(byte[], boolean, ClassOption...) defineHiddenClass(bytes, initialize, options)} + * as if the hidden class has a private static final unnamed field + * pre-initialized with the given {@code classData}. + * The {@link MethodHandles#classData(Lookup, String, Class) MethodHandles::classData} method + * can be used to retrieve the {@code classData}. + * + * <p> The {@linkplain #lookupModes() lookup modes} for this lookup must + * have {@code PRIVATE} and {@code MODULE} access in order to create a + * hidden class in the module of this lookup class. + * + * @param bytes the class bytes + * @param classData pre-initialized class data + * @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 IllegalArgumentException the bytes are for a class in a different package + * to the lookup class + * @throws IllegalAccessException if this lookup does not have {@code PRIVATE} and {@code MODULE} 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 14 + * @see Lookup#defineHiddenClass(byte[], boolean, ClassOption...) + * @see Class#isHiddenClass() + */ + /* package-private */ Lookup defineHiddenClassWithClassData(byte[] bytes, Object classData, boolean initialize, ClassOption... options) + throws IllegalAccessException + { + Objects.requireNonNull(bytes); + Objects.requireNonNull(classData); + + checkDefineClassPermission(); + if ((lookupModes() & (PRIVATE|MODULE)) != (PRIVATE|MODULE)){ + throw new IllegalAccessException(this + " does not have PRIVATE or MODULE access"); + } + + Set<ClassOption> opts = (options != null && options.length > 0) ? Set.of(options) : Set.of(); + Class<?> c = makeHiddenClassDefiner(bytes.clone(), opts).defineClass(initialize, classData); + return new Lookup(c, null, FULL_POWER_MODES); + } + + private ClassDefiner makeClassDefiner(byte[] bytes) { + return new ClassDefiner(this, bytes, Set.of(), 0); + } ! /** ! * Returns a ClassDefiner that creates a {@code Class} object of a hidden class ! * from the given bytes and options. ! * ! * @param bytes class bytes ! * @param options class options ! * @return ClassDefiner that defines a hidden class of the given bytes and options ! */ ! ClassDefiner makeHiddenClassDefiner(byte[] bytes, Set<ClassOption> options) { ! return new ClassDefiner(this, bytes, options, HIDDEN_CLASS); ! } ! ! /** ! * This method is only called by MethodHandleImpl.BindCaller.makeInjectedInvoker. ! * ! * @param name the name of the class and the name in the class bytes is ignored. ! * @param bytes class bytes ! * @return ClassDefiner that defines a hidden class of the given bytes ! */ ! ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes) { ! return new ClassDefiner(this, name, bytes, HIDDEN_CLASS); ! } ! ! static class ClassDefiner { ! private final Lookup lookup; ! private final String name; ! private final byte[] bytes; ! private final int classFlags; ! ! // caller should make a defensive copy of the arguments if needed ! // before calling this constructor ! private ClassDefiner(Lookup lookup, byte[] bytes, Set<ClassOption> options, int flags) { ! this.lookup = lookup; ! this.bytes = bytes; ! this.classFlags = flags | ClassOption.optionsToFlag(options); ! this.name = className(bytes); ! ! int index = name.lastIndexOf('.'); ! String pn = (index == -1) ? "" : name.substring(0, index); ! if (!pn.equals(lookup.lookupClass().getPackageName())) { ! throw newIllegalArgumentException(name + " not in same package as lookup class: " + ! lookup.lookupClass().getName()); ! } ! } ! ! // skip package name check ! private ClassDefiner(Lookup lookup, String name, byte[] bytes, int flags) { ! this.lookup = lookup; ! this.bytes = bytes; ! this.classFlags = flags; ! this.name = name; ! } ! ! String className() { ! return name; ! } ! ! Class<?> defineClass(boolean initialize) { ! return defineClass(initialize, null); ! } ! ! /** ! * 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; ! } ! ! private boolean isNestmate() { ! return (classFlags & NESTMATE_CLASS) != 0; ! } ! ! private static String className(byte[] bytes) { try { ClassReader reader = new ClassReader(bytes); ! String name = reader.getClassName(); ! return name.replace('/', '.'); ! } catch (IllegalArgumentException e) { ! throw e; } catch (RuntimeException e) { // ASM exceptions are poorly specified ClassFormatError cfe = new ClassFormatError(); cfe.initCause(e); throw cfe; } } } 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);
*** 1652,1661 **** --- 1879,1890 ---- * 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); }
*** 1710,1724 **** case PUBLIC|MODULE: return cname + "/module"; case PUBLIC|PACKAGE: case PUBLIC|MODULE|PACKAGE: return cname + "/package"; ! case FULL_POWER_MODES & (~PROTECTED): ! case FULL_POWER_MODES & ~(PROTECTED|MODULE): return cname + "/private"; case FULL_POWER_MODES: ! case FULL_POWER_MODES & (~MODULE): return cname; case TRUSTED: return "/trusted"; // internal only; not exported default: // Should not happen, but it's a bitfield... cname = cname + "/" + Integer.toHexString(allowedModes); --- 1939,1953 ---- case PUBLIC|MODULE: return cname + "/module"; case PUBLIC|PACKAGE: case PUBLIC|MODULE|PACKAGE: return cname + "/package"; ! case PUBLIC|PACKAGE|PRIVATE: ! case PUBLIC|MODULE|PACKAGE|PRIVATE: return cname + "/private"; case FULL_POWER_MODES: ! case FULL_POWER_MODES & ~(MODULE): return cname; case TRUSTED: return "/trusted"; // internal only; not exported default: // Should not happen, but it's a bitfield... cname = cname + "/" + Integer.toHexString(allowedModes);
*** 3258,3267 **** --- 3487,3543 ---- // oops throw newIllegalArgumentException("bad MethodHandle constant #"+member); } static ConcurrentHashMap<MemberName, DirectMethodHandle> LOOKASIDE_TABLE = new ConcurrentHashMap<>(); + + /** + * The set of class options that specify whether a hidden class created by + * {@link Lookup#defineHiddenClass(byte[], boolean, ClassOption...) + * Lookup::defineHiddenMethod} method is dynamically added as + * a new member to the nest of a lookup class and whether a hidden class + * is weakly referenced by its defining class loader. + * + * @since 14 + */ + public enum ClassOption { + /** + * This class option specifies the hidden class be added to + * {@linkplain Class#getNestHost nest} of a lookup class as + * a nestmate. + * + * <p> A hidden nestmate class has access to the private members of all + * classes and interfaces in the same nest. + * + * @see Class#getNestHost() + */ + NESTMATE(NESTMATE_CLASS), + + /** + * This class option specifies the hidden class be weakly + * referenced by its defining class loader such that it + * may be unloaded while its defining class loader is + * <a href="../ref/package.html#reachability">strongly reachable</a>. + * + * @jls 12.7 Unloading of Classes and Interfaces + */ + WEAK(WEAK_CLASS); + + /* the flag value is used by VM at define class time */ + private final int flag; + ClassOption(int flag) { + this.flag = flag; + } + + static int optionsToFlag(Set<ClassOption> options) { + int flags = 0; + for (ClassOption cp : options) { + flags |= cp.flag; + } + return flags; + } + } } /** * Produces a method handle constructing arrays of a desired type, * as if by the {@code anewarray} bytecode.
< prev index next >