--- 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;
- }
- }
}
/**