--- old/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2020-03-27 15:15:29.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2020-03-27 15:15:29.000000000 -0700 @@ -1691,6 +1691,59 @@ } /** + * 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. + * + *

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 strong + * 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). + * + *

By default, a hidden class or interface may be unloaded + * even if the class loader that is marked as its defining loader is + * reachable. + + * + * @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 options) { + int flags = 0; + for (ClassOption cp : options) { + flags |= cp.flag; + } + return flags; + } + } + + /** * Creates a hidden class or interface from {@code bytes}, * returning a {@code Lookup} on the newly created class or interface. * @@ -1777,15 +1830,14 @@ *

If the {@code initialize} parameter is {@code true}, * then {@code C} is initialized by the Java Virtual Machine. * - *

The newly created class or interface {@code C} is hidden, in the sense that + *

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 hidden 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. - * (Given the {@code Lookup} object returned this method, its lookup class - * is a {@code Class} object for which {@link Class#getName()} returns a string - * that is not a binary name.) * 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) @@ -1899,8 +1951,7 @@ throw new IllegalAccessException(this + " does not have full privilege access"); } - Set opts = options.length > 0 ? Set.of(options) : Set.of(); - return makeHiddenClassDefiner(bytes.clone(), opts, false).defineClassAsLookup(initialize); + return makeHiddenClassDefiner(bytes.clone(), Set.of(options), false).defineClassAsLookup(initialize); } /** @@ -1956,64 +2007,109 @@ throw new IllegalAccessException(this + " does not have full privilege access"); } - Set opts = options.length > 0 ? Set.of(options) : Set.of(); - return makeHiddenClassDefiner(bytes.clone(), opts, false) + return makeHiddenClassDefiner(bytes.clone(), Set.of(options), false) .defineClassAsLookup(true, classData); } - private ClassDefiner makeClassDefiner(byte[] bytes) { - return new ClassDefiner(this, bytes, STRONG_LOADER_LINK); + /* + * 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 hidden class + + /* + * Returns a ClassDefiner that creates a {@code Class} object of a normal class * from the given bytes. * - * @param bytes class bytes - * @return ClassDefiner that defines a hidden class of 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 */ - ClassDefiner makeHiddenClassDefiner(byte[] bytes) { - return makeHiddenClassDefiner(bytes, Set.of(), false); + 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. + * 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 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. + * + * @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(String name, byte[] bytes) { - return makeHiddenClassDefiner(name, bytes, Set.of(), false); + 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. This method will read the class file - * and obtain the class name. + * 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 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 makeHiddenClassDefiner(validateAndGetClassName(bytes), bytes, options, accessVmAnnotations); + } - return new ClassDefiner(this, bytes, flags); + /** + * 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. + * 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 @@ -2040,25 +2136,6 @@ 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, int flags) { - // defining an ordinary class which must be a strongly referenced by its defining loader - assert ((flags & HIDDEN_CLASS) != 0 || (flags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK); - this.lookup = lookup; - this.bytes = bytes; - this.classFlags = flags; - 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) { assert ((flags & HIDDEN_CLASS) != 0 || (flags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK); this.lookup = lookup; @@ -2109,24 +2186,6 @@ private boolean isNestmate() { return (classFlags & NESTMATE_CLASS) != 0; } - - private static String className(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(); - 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() { @@ -3784,61 +3843,6 @@ } static ConcurrentHashMap LOOKASIDE_TABLE = new ConcurrentHashMap<>(); - - /** - * 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 { - /** - * This class option specifies the hidden class be added to - * {@linkplain Class#getNestHost nest} of a lookup class as - * a nestmate. - * - *

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 to have a strong - * 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). - * - *

By default, a hidden class or interface may be unloaded - * even if the class loader that is marked as its defining loader is - * reachable. - - * - * @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 options) { - int flags = 0; - for (ClassOption cp : options) { - flags |= cp.flag; - } - return flags; - } - } } /**