< prev index next >
src/java.base/share/classes/java/lang/invoke/MethodHandles.java
Print this page
*** 1689,1698 ****
--- 1689,1751 ----
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.
*** 1775,1793 ****
* </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} 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.
- * (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)
* modifiable} by Java agents or tool agents using the <a href="{@docRoot}/../specs/jvmti.html">
* JVM Tool Interface</a>.
--- 1828,1845 ----
* </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>.
*** 1897,1908 ****
ensureDefineClassPermission();
if (!hasFullPrivilegeAccess()) {
throw new IllegalAccessException(this + " does not have full privilege access");
}
! Set<ClassOption> opts = options.length > 0 ? Set.of(options) : Set.of();
! return makeHiddenClassDefiner(bytes.clone(), opts, false).defineClassAsLookup(initialize);
}
/**
* Creates a <em>hidden</em> class or interface from {@code bytes} with associated
* {@linkplain MethodHandles#classData(Lookup, String, Class) class data},
--- 1949,1959 ----
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},
*** 1954,2021 ****
ensureDefineClassPermission();
if (!hasFullPrivilegeAccess()) {
throw new IllegalAccessException(this + " does not have full privilege access");
}
! Set<ClassOption> opts = options.length > 0 ? Set.of(options) : Set.of();
! return makeHiddenClassDefiner(bytes.clone(), opts, false)
.defineClassAsLookup(true, classData);
}
! private ClassDefiner makeClassDefiner(byte[] bytes) {
! return new ClassDefiner(this, bytes, STRONG_LOADER_LINK);
}
! /**
! * Returns a ClassDefiner that creates a {@code Class} object of a hidden class
* from the given bytes.
*
! * @param bytes class bytes
! * @return ClassDefiner that defines a hidden class of the given bytes.
*/
! ClassDefiner makeHiddenClassDefiner(byte[] bytes) {
! return makeHiddenClassDefiner(bytes, Set.of(), false);
}
/**
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
! * from the given bytes.
*
- * @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. This method will read the class file
! * and obtain the class name.
*
* @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
*/
ClassDefiner makeHiddenClassDefiner(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, bytes, flags);
}
/**
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
! * from the given bytes and options.
*
* @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
--- 2005,2117 ----
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
*** 2038,2066 ****
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, 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;
this.bytes = bytes;
this.classFlags = flags;
--- 2134,2143 ----
*** 2107,2134 ****
}
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() {
ProtectionDomain pd = cachedProtectionDomain;
if (pd == null) {
--- 2184,2193 ----
*** 3782,3846 ****
// 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::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.
- *
- * <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 to have 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;
- }
- }
}
/**
* Produces a method handle constructing arrays of a desired type,
* as if by the {@code anewarray} bytecode.
--- 3841,3850 ----
< prev index next >