mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
8238358: Implementation of JEP 371: Hidden Classes
Co-authored-by: Lois Foltan <lois.foltan@oracle.com> Co-authored-by: David Holmes <david.holmes@oracle.com> Co-authored-by: Harold Seigel <harold.seigel@oracle.com> Co-authored-by: Serguei Spitsyn <serguei.spitsyn@oracle.com> Co-authored-by: Alex Buckley <alex.buckley@oracle.com> Co-authored-by: Jamsheed Mohammed C M <jamsheed.c.m@oracle.com> Co-authored-by: Jan Lahoda <jan.lahoda@oracle.com> Co-authored-by: Amy Lu <amy.lu@oracle.com> Reviewed-by: alanb, cjplummer, coleenp, dholmes, dlong, forax, jlahoda, psandoz, plevart, sspitsyn, vromero
This commit is contained in:
parent
642041adbc
commit
7cc1371059
198 changed files with 9526 additions and 1575 deletions
|
@ -28,6 +28,7 @@ package java.lang;
|
|||
import java.lang.annotation.Annotation;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.invoke.TypeDescriptor;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.module.ModuleReader;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.io.IOException;
|
||||
|
@ -63,8 +64,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
@ -100,16 +99,42 @@ import sun.reflect.misc.ReflectUtil;
|
|||
* keyword {@code void} are also represented as {@code Class} objects.
|
||||
*
|
||||
* <p> {@code Class} has no public constructor. Instead a {@code Class}
|
||||
* object is constructed automatically by the Java Virtual Machine
|
||||
* when a class loader invokes one of the
|
||||
* {@link ClassLoader#defineClass(String,byte[], int,int) defineClass} methods
|
||||
* and passes the bytes of a {@code class} file.
|
||||
* object is constructed automatically by the Java Virtual Machine when
|
||||
* a class is derived from the bytes of a {@code class} file through
|
||||
* the invocation of one of the following methods:
|
||||
* <ul>
|
||||
* <li> {@link ClassLoader#defineClass(String, byte[], int, int) ClassLoader::defineClass}
|
||||
* <li> {@link java.lang.invoke.MethodHandles.Lookup#defineClass(byte[])
|
||||
* java.lang.invoke.MethodHandles.Lookup::defineClass}
|
||||
* <li> {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
|
||||
* java.lang.invoke.MethodHandles.Lookup::defineHiddenClass}
|
||||
* </ul>
|
||||
*
|
||||
* <p> The methods of class {@code Class} expose many characteristics of a
|
||||
* class or interface. Most characteristics are derived from the {@code class}
|
||||
* file that the class loader passed to the Java Virtual Machine. A few
|
||||
* characteristics are determined by the class loading environment at run time,
|
||||
* such as the module returned by {@link #getModule() getModule()}.
|
||||
* file that the class loader passed to the Java Virtual Machine or
|
||||
* from the {@code class} file passed to {@code Lookup::defineClass}
|
||||
* or {@code Lookup::defineHiddenClass}.
|
||||
* A few characteristics are determined by the class loading environment
|
||||
* at run time, such as the module returned by {@link #getModule() getModule()}.
|
||||
*
|
||||
* <p> The following example uses a {@code Class} object to print the
|
||||
* class name of an object:
|
||||
*
|
||||
* <blockquote><pre>
|
||||
* void printClassName(Object obj) {
|
||||
* System.out.println("The class of " + obj +
|
||||
* " is " + obj.getClass().getName());
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* It is also possible to get the {@code Class} object for a named
|
||||
* type (or for {@code void}) using a <i>class literal</i>.
|
||||
* For example:
|
||||
*
|
||||
* <blockquote>
|
||||
* {@code System.out.println("The name of class Foo is: "+Foo.class.getName());}
|
||||
* </blockquote>
|
||||
*
|
||||
* <p> Some methods of class {@code Class} expose whether the declaration of
|
||||
* a class or interface in Java source code was <em>enclosed</em> within
|
||||
|
@ -128,24 +153,33 @@ import sun.reflect.misc.ReflectUtil;
|
|||
* other members are the classes and interfaces whose declarations are
|
||||
* enclosed within the top-level class declaration.
|
||||
*
|
||||
* <p> The following example uses a {@code Class} object to print the
|
||||
* class name of an object:
|
||||
* <p> A class or interface created by the invocation of
|
||||
* {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
|
||||
* Lookup::defineHiddenClass} is a {@linkplain Class#isHidden() <em>hidden</em>}
|
||||
* class or interface.
|
||||
* All kinds of class, including enum types and record types, may be
|
||||
* hidden classes; all kinds of interface, including annotation types,
|
||||
* may be hidden interfaces.
|
||||
*
|
||||
* <blockquote><pre>
|
||||
* void printClassName(Object obj) {
|
||||
* System.out.println("The class of " + obj +
|
||||
* " is " + obj.getClass().getName());
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
* The {@linkplain #getName() name of a hidden class or interface} is
|
||||
* not a <a href="ClassLoader.html#binary-name">binary name</a>,
|
||||
* which means the following:
|
||||
* <ul>
|
||||
* <li>A hidden class or interface cannot be referenced by the constant pools
|
||||
* of other classes and interfaces.
|
||||
* <li>A hidden class or interface cannot be described in
|
||||
* {@linkplain java.lang.constant.ConstantDesc <em>nominal form</em>} by
|
||||
* {@link #describeConstable() Class::describeConstable},
|
||||
* {@link ClassDesc#of(String) ClassDesc::of}, or
|
||||
* {@link ClassDesc#ofDescriptor(String) ClassDesc::ofDescriptor}.
|
||||
* <li>A hidden class or interface cannot be discovered by {@link #forName Class::forName}
|
||||
* or {@link ClassLoader#loadClass(String, boolean) ClassLoader::loadClass}.
|
||||
* </ul>
|
||||
*
|
||||
* <p> It is also possible to get the {@code Class} object for a named
|
||||
* type (or for void) using a class literal. See Section {@jls
|
||||
* 15.8.2} of <cite>The Java™ Language Specification</cite>.
|
||||
* For example:
|
||||
*
|
||||
* <blockquote>
|
||||
* {@code System.out.println("The name of class Foo is: " + Foo.class.getName());}
|
||||
* </blockquote>
|
||||
* A hidden class or interface is never an array class, but may be
|
||||
* the element type of an array. In all other respects, the fact that
|
||||
* a class or interface is hidden has no bearing on the characteristics
|
||||
* exposed by the methods of class {@code Class}.
|
||||
*
|
||||
* @param <T> the type of the class modeled by this {@code Class}
|
||||
* object. For example, the type of {@code String.class} is {@code
|
||||
|
@ -155,6 +189,7 @@ import sun.reflect.misc.ReflectUtil;
|
|||
* @author unascribed
|
||||
* @see java.lang.ClassLoader#defineClass(byte[], int, int)
|
||||
* @since 1.0
|
||||
* @jls 15.8.2 Class Literals
|
||||
*/
|
||||
public final class Class<T> implements java.io.Serializable,
|
||||
GenericDeclaration,
|
||||
|
@ -186,9 +221,9 @@ public final class Class<T> implements java.io.Serializable,
|
|||
/**
|
||||
* Converts the object to a string. The string representation is the
|
||||
* string "class" or "interface", followed by a space, and then by the
|
||||
* fully qualified name of the class in the format returned by
|
||||
* {@code getName}. If this {@code Class} object represents a
|
||||
* primitive type, this method returns the name of the primitive type. If
|
||||
* name of the class in the format returned by {@code getName}.
|
||||
* If this {@code Class} object represents a primitive type,
|
||||
* this method returns the name of the primitive type. If
|
||||
* this {@code Class} object represents void this method returns
|
||||
* "void". If this {@code Class} object represents an array type,
|
||||
* this method returns "class " followed by {@code getName}.
|
||||
|
@ -745,11 +780,12 @@ public final class Class<T> implements java.io.Serializable,
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this class is a synthetic class;
|
||||
* returns {@code false} otherwise.
|
||||
* @return {@code true} if and only if this class is a synthetic class as
|
||||
* defined by <cite>The Java™ Language Specification</cite>.
|
||||
* Returns {@code true} if and only if this class has the synthetic modifier
|
||||
* bit set.
|
||||
*
|
||||
* @return {@code true} if and only if this class has the synthetic modifier bit set
|
||||
* @jls 13.1 The Form of a Binary
|
||||
* @jvms 4.1 The {@code ClassFile} Structure
|
||||
* @since 1.5
|
||||
*/
|
||||
public boolean isSynthetic() {
|
||||
|
@ -758,22 +794,26 @@ public final class Class<T> implements java.io.Serializable,
|
|||
|
||||
/**
|
||||
* Returns the name of the entity (class, interface, array class,
|
||||
* primitive type, or void) represented by this {@code Class} object,
|
||||
* as a {@code String}.
|
||||
* primitive type, or void) represented by this {@code Class} object.
|
||||
*
|
||||
* <p> If this {@code Class} object represents a reference type that is
|
||||
* not an array type then the binary name of the class is
|
||||
* returned, as specified by <cite>The Java™ Language
|
||||
* Specification</cite>.
|
||||
* <p> If this {@code Class} object represents a class or interface,
|
||||
* not an array class, then:
|
||||
* <ul>
|
||||
* <li> If the class or interface is not {@linkplain #isHidden() hidden},
|
||||
* then the <a href="ClassLoader.html#binary-name">binary name</a>
|
||||
* of the class or interface is returned.
|
||||
* <li> If the class or interface is hidden, then the result is a string
|
||||
* of the form: {@code N + '/' + <suffix>}
|
||||
* where {@code N} is the <a href="ClassLoader.html#binary-name">binary name</a>
|
||||
* indicated by the {@code class} file passed to
|
||||
* {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
|
||||
* Lookup::defineHiddenClass}, and {@code <suffix>} is an unqualified name.
|
||||
* </ul>
|
||||
*
|
||||
* <p> If this {@code Class} object represents a primitive type or void, then the
|
||||
* name returned is a {@code String} equal to the Java language
|
||||
* keyword corresponding to the primitive type or void.
|
||||
*
|
||||
* <p> If this {@code Class} object represents a class of arrays, then the internal
|
||||
* form of the name consists of the name of the element type preceded by
|
||||
* one or more '{@code [}' characters representing the depth of the array
|
||||
* nesting. The encoding of element type names is as follows:
|
||||
* <p> If this {@code Class} object represents an array class, then
|
||||
* the result is a string consisting of one or more '{@code [}' characters
|
||||
* representing the depth of the array nesting, followed by the element
|
||||
* type as encoded using the following table:
|
||||
*
|
||||
* <blockquote><table class="striped">
|
||||
* <caption style="display:none">Element types and encodings</caption>
|
||||
|
@ -781,21 +821,22 @@ public final class Class<T> implements java.io.Serializable,
|
|||
* <tr><th scope="col"> Element Type <th scope="col"> Encoding
|
||||
* </thead>
|
||||
* <tbody style="text-align:left">
|
||||
* <tr><th scope="row"> boolean <td style="text-align:center"> Z
|
||||
* <tr><th scope="row"> byte <td style="text-align:center"> B
|
||||
* <tr><th scope="row"> char <td style="text-align:center"> C
|
||||
* <tr><th scope="row"> class or interface
|
||||
* <td style="text-align:center"> L<i>classname</i>;
|
||||
* <tr><th scope="row"> double <td style="text-align:center"> D
|
||||
* <tr><th scope="row"> float <td style="text-align:center"> F
|
||||
* <tr><th scope="row"> int <td style="text-align:center"> I
|
||||
* <tr><th scope="row"> long <td style="text-align:center"> J
|
||||
* <tr><th scope="row"> short <td style="text-align:center"> S
|
||||
* <tr><th scope="row"> {@code boolean} <td style="text-align:center"> {@code Z}
|
||||
* <tr><th scope="row"> {@code byte} <td style="text-align:center"> {@code B}
|
||||
* <tr><th scope="row"> {@code char} <td style="text-align:center"> {@code C}
|
||||
* <tr><th scope="row"> class or interface with <a href="ClassLoader.html#binary-name">binary name</a> <i>N</i>
|
||||
* <td style="text-align:center"> {@code L}<em>N</em>{@code ;}
|
||||
* <tr><th scope="row"> {@code double} <td style="text-align:center"> {@code D}
|
||||
* <tr><th scope="row"> {@code float} <td style="text-align:center"> {@code F}
|
||||
* <tr><th scope="row"> {@code int} <td style="text-align:center"> {@code I}
|
||||
* <tr><th scope="row"> {@code long} <td style="text-align:center"> {@code J}
|
||||
* <tr><th scope="row"> {@code short} <td style="text-align:center"> {@code S}
|
||||
* </tbody>
|
||||
* </table></blockquote>
|
||||
*
|
||||
* <p> The class or interface name <i>classname</i> is the binary name of
|
||||
* the class specified above.
|
||||
* <p> If this {@code Class} object represents a primitive type or {@code void},
|
||||
* then the result is a string with the same spelling as the Java language
|
||||
* keyword which corresponds to the primitive type or {@code void}.
|
||||
*
|
||||
* <p> Examples:
|
||||
* <blockquote><pre>
|
||||
|
@ -809,8 +850,9 @@ public final class Class<T> implements java.io.Serializable,
|
|||
* returns "[[[[[[[I"
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @return the name of the class or interface
|
||||
* @return the name of the class, interface, or other entity
|
||||
* represented by this {@code Class} object.
|
||||
* @jls 13.1 The Form of a Binary
|
||||
*/
|
||||
public String getName() {
|
||||
String name = this.name;
|
||||
|
@ -888,6 +930,14 @@ public final class Class<T> implements java.io.Serializable,
|
|||
// will throw NoSuchFieldException
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
// Set by VM
|
||||
private transient Object classData;
|
||||
|
||||
// package-private
|
||||
Object getClassData() {
|
||||
return classData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of {@code TypeVariable} objects that represent the
|
||||
* type variables declared by the generic declaration represented by this
|
||||
|
@ -900,7 +950,7 @@ public final class Class<T> implements java.io.Serializable,
|
|||
* @throws java.lang.reflect.GenericSignatureFormatError if the generic
|
||||
* signature of this generic declaration does not conform to
|
||||
* the format specified in section {@jvms 4.7.9} of
|
||||
* <cite>The Java™ Virtual Machine Specification</cite>,
|
||||
* <cite>The Java™ Virtual Machine Specification</cite>
|
||||
* @since 1.5
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -1023,10 +1073,7 @@ public final class Class<T> implements java.io.Serializable,
|
|||
public String getPackageName() {
|
||||
String pn = this.packageName;
|
||||
if (pn == null) {
|
||||
Class<?> c = this;
|
||||
while (c.isArray()) {
|
||||
c = c.getComponentType();
|
||||
}
|
||||
Class<?> c = isArray() ? elementType() : this;
|
||||
if (c.isPrimitive()) {
|
||||
pn = "java.lang";
|
||||
} else {
|
||||
|
@ -1184,6 +1231,20 @@ public final class Class<T> implements java.io.Serializable,
|
|||
|
||||
private final Class<?> componentType;
|
||||
|
||||
/*
|
||||
* Returns the {@code Class} representing the element type of an array class.
|
||||
* If this class does not represent an array class, then this method returns
|
||||
* {@code null}.
|
||||
*/
|
||||
private Class<?> elementType() {
|
||||
if (!isArray()) return null;
|
||||
|
||||
Class<?> c = this;
|
||||
while (c.isArray()) {
|
||||
c = c.getComponentType();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Java language modifiers for this class or interface, encoded
|
||||
|
@ -1614,11 +1675,17 @@ public final class Class<T> implements java.io.Serializable,
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the canonical name of the underlying class as defined
|
||||
* by <cite>The Java™ Language Specification</cite>, section
|
||||
* {@jls 6.7}. Returns null if the underlying class does not have
|
||||
* a canonical name (i.e., if it is a local or anonymous class or
|
||||
* an array whose component type does not have a canonical name).
|
||||
* Returns the canonical name of the underlying class as
|
||||
* defined by <cite>The Java™ Language Specification</cite>.
|
||||
* Returns {@code null} if the underlying class does not have a canonical
|
||||
* name. Classes without canonical names include:
|
||||
* <ul>
|
||||
* <li>a {@linkplain #isLocalClass() local class}
|
||||
* <li>a {@linkplain #isAnonymousClass() anonymous class}
|
||||
* <li>a {@linkplain #isHidden() hidden class}
|
||||
* <li>an array whose component type does not have a canonical name</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the canonical name of the underlying class if it exists, and
|
||||
* {@code null} otherwise.
|
||||
* @since 1.5
|
||||
|
@ -1640,7 +1707,7 @@ public final class Class<T> implements java.io.Serializable,
|
|||
else
|
||||
return ReflectionData.NULL_SENTINEL;
|
||||
}
|
||||
if (isLocalOrAnonymousClass())
|
||||
if (isHidden() || isLocalOrAnonymousClass())
|
||||
return ReflectionData.NULL_SENTINEL;
|
||||
Class<?> enclosingClass = getEnclosingClass();
|
||||
if (enclosingClass == null) { // top level class
|
||||
|
@ -1657,6 +1724,9 @@ public final class Class<T> implements java.io.Serializable,
|
|||
* Returns {@code true} if and only if the underlying class
|
||||
* is an anonymous class.
|
||||
*
|
||||
* @apiNote
|
||||
* An anonymous class is not a {@linkplain #isHidden() hidden class}.
|
||||
*
|
||||
* @return {@code true} if and only if this class is an anonymous class.
|
||||
* @since 1.5
|
||||
*/
|
||||
|
@ -2882,6 +2952,11 @@ public final class Class<T> implements java.io.Serializable,
|
|||
if (sm != null) {
|
||||
sm.checkPermission(SecurityConstants.GET_PD_PERMISSION);
|
||||
}
|
||||
return protectionDomain();
|
||||
}
|
||||
|
||||
// package-private
|
||||
java.security.ProtectionDomain protectionDomain() {
|
||||
java.security.ProtectionDomain pd = getProtectionDomain0();
|
||||
if (pd == null) {
|
||||
if (allPermDomain == null) {
|
||||
|
@ -2896,7 +2971,6 @@ public final class Class<T> implements java.io.Serializable,
|
|||
return pd;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ProtectionDomain of this class.
|
||||
*/
|
||||
|
@ -2968,10 +3042,7 @@ public final class Class<T> implements java.io.Serializable,
|
|||
*/
|
||||
private String resolveName(String name) {
|
||||
if (!name.startsWith("/")) {
|
||||
Class<?> c = this;
|
||||
while (c.isArray()) {
|
||||
c = c.getComponentType();
|
||||
}
|
||||
Class<?> c = isArray() ? elementType() : this;
|
||||
String baseName = c.getPackageName();
|
||||
if (baseName != null && !baseName.isEmpty()) {
|
||||
name = baseName.replace('.', '/') + "/" + name;
|
||||
|
@ -4042,30 +4113,23 @@ public final class Class<T> implements java.io.Serializable,
|
|||
/**
|
||||
* Returns the nest host of the <a href=#nest>nest</a> to which the class
|
||||
* or interface represented by this {@code Class} object belongs.
|
||||
* Every class and interface is a member of exactly one nest.
|
||||
* A class or interface that is not recorded as belonging to a nest
|
||||
* belongs to the nest consisting only of itself, and is the nest
|
||||
* host.
|
||||
* Every class and interface belongs to exactly one nest.
|
||||
*
|
||||
* <p>Each of the {@code Class} objects representing array types,
|
||||
* primitive types, and {@code void} returns {@code this} to indicate
|
||||
* that the represented entity belongs to the nest consisting only of
|
||||
* If the nest host of this class or interface has previously
|
||||
* been determined, then this method returns the nest host.
|
||||
* If the nest host of this class or interface has
|
||||
* not previously been determined, then this method determines the nest
|
||||
* host using the algorithm of JVMS 5.4.4, and returns it.
|
||||
*
|
||||
* Often, a class or interface belongs to a nest consisting only of itself,
|
||||
* in which case this method returns {@code this} to indicate that the class
|
||||
* or interface is the nest host.
|
||||
*
|
||||
* <p>If this {@code Class} object represents a primitive type, an array type,
|
||||
* or {@code void}, then this method returns {@code this},
|
||||
* indicating that the represented entity belongs to the nest consisting only of
|
||||
* itself, and is the nest host.
|
||||
*
|
||||
* <p>If there is a {@linkplain LinkageError linkage error} accessing
|
||||
* the nest host, or if this class or interface is not enumerated as
|
||||
* a member of the nest by the nest host, then it is considered to belong
|
||||
* to its own nest and {@code this} is returned as the host.
|
||||
*
|
||||
* @apiNote A {@code class} file of version 55.0 or greater may record the
|
||||
* host of the nest to which it belongs by using the {@code NestHost}
|
||||
* attribute (JVMS {@jvms 4.7.28}). Alternatively, a {@code class} file of
|
||||
* version 55.0 or greater may act as a nest host by enumerating the nest's
|
||||
* other members with the
|
||||
* {@code NestMembers} attribute (JVMS {@jvms 4.7.29}).
|
||||
* A {@code class} file of version 54.0 or lower does not use these
|
||||
* attributes.
|
||||
*
|
||||
* @return the nest host of this class or interface
|
||||
*
|
||||
* @throws SecurityException
|
||||
|
@ -4085,17 +4149,9 @@ public final class Class<T> implements java.io.Serializable,
|
|||
if (isPrimitive() || isArray()) {
|
||||
return this;
|
||||
}
|
||||
Class<?> host;
|
||||
try {
|
||||
host = getNestHost0();
|
||||
} catch (LinkageError e) {
|
||||
// if we couldn't load our nest-host then we
|
||||
// act as-if we have no nest-host attribute
|
||||
return this;
|
||||
}
|
||||
// if null then nest membership validation failed, so we
|
||||
// act as-if we have no nest-host attribute
|
||||
if (host == null || host == this) {
|
||||
|
||||
Class<?> host = getNestHost0();
|
||||
if (host == this) {
|
||||
return this;
|
||||
}
|
||||
// returning a different class requires a security check
|
||||
|
@ -4127,11 +4183,8 @@ public final class Class<T> implements java.io.Serializable,
|
|||
c.isPrimitive() || c.isArray()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return getNestHost0() == c.getNestHost0();
|
||||
} catch (LinkageError e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getNestHost() == c.getNestHost();
|
||||
}
|
||||
|
||||
private native Class<?>[] getNestMembers0();
|
||||
|
@ -4140,39 +4193,47 @@ public final class Class<T> implements java.io.Serializable,
|
|||
* Returns an array containing {@code Class} objects representing all the
|
||||
* classes and interfaces that are members of the nest to which the class
|
||||
* or interface represented by this {@code Class} object belongs.
|
||||
* The {@linkplain #getNestHost() nest host} of that nest is the zeroth
|
||||
* element of the array. Subsequent elements represent any classes or
|
||||
* interfaces that are recorded by the nest host as being members of
|
||||
* the nest; the order of such elements is unspecified. Duplicates are
|
||||
* permitted.
|
||||
* If the nest host of that nest does not enumerate any members, then the
|
||||
* array has a single element containing {@code this}.
|
||||
*
|
||||
* <p>Each of the {@code Class} objects representing array types,
|
||||
* primitive types, and {@code void} returns an array containing only
|
||||
* First, this method obtains the {@linkplain #getNestHost() nest host},
|
||||
* {@code H}, of the nest to which the class or interface represented by
|
||||
* this {@code Class} object belongs. The zeroth element of the returned
|
||||
* array is {@code H}.
|
||||
*
|
||||
* Then, for each class or interface {@code C} which is recorded by {@code H}
|
||||
* as being a member of its nest, this method attempts to obtain the {@code Class}
|
||||
* object for {@code C} (using {@linkplain #getClassLoader() the defining class
|
||||
* loader} of the current {@code Class} object), and then obtains the
|
||||
* {@linkplain #getNestHost() nest host} of the nest to which {@code C} belongs.
|
||||
* The classes and interfaces which are recorded by {@code H} as being members
|
||||
* of its nest, and for which {@code H} can be determined as their nest host,
|
||||
* are indicated by subsequent elements of the returned array. The order of
|
||||
* such elements is unspecified. Duplicates are permitted.
|
||||
*
|
||||
* <p>If this {@code Class} object represents a primitive type, an array type,
|
||||
* or {@code void}, then this method returns a single-element array containing
|
||||
* {@code this}.
|
||||
*
|
||||
* <p>This method validates that, for each class or interface which is
|
||||
* recorded as a member of the nest by the nest host, that class or
|
||||
* interface records itself as a member of that same nest. Any exceptions
|
||||
* that occur during this validation are rethrown by this method.
|
||||
* @apiNote
|
||||
* The returned array includes only the nest members recorded in the {@code NestMembers}
|
||||
* attribute, and not any hidden classes that were added to the nest via
|
||||
* {@link MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
|
||||
* Lookup::defineHiddenClass}.
|
||||
*
|
||||
* @return an array of all classes and interfaces in the same nest as
|
||||
* this class
|
||||
* this class or interface
|
||||
*
|
||||
* @throws LinkageError
|
||||
* If there is any problem loading or validating a nest member or
|
||||
* its nest host
|
||||
* @throws SecurityException
|
||||
* If any returned class is not the current class, and
|
||||
* if a security manager, <i>s</i>, is present and the caller's
|
||||
* class loader is not the same as or an ancestor of the class
|
||||
* loader for that returned class and invocation of {@link
|
||||
* SecurityManager#checkPackageAccess s.checkPackageAccess()}
|
||||
* denies access to the package of that returned class
|
||||
* If any returned class is not the current class, and
|
||||
* if a security manager, <i>s</i>, is present and the caller's
|
||||
* class loader is not the same as or an ancestor of the class
|
||||
* loader for that returned class and invocation of {@link
|
||||
* SecurityManager#checkPackageAccess s.checkPackageAccess()}
|
||||
* denies access to the package of that returned class
|
||||
*
|
||||
* @since 11
|
||||
* @see #getNestHost()
|
||||
* @jvms 4.7.28 The {@code NestHost} Attribute
|
||||
* @jvms 4.7.29 The {@code NestMembers} Attribute
|
||||
*/
|
||||
@CallerSensitive
|
||||
public Class<?>[] getNestMembers() {
|
||||
|
@ -4196,13 +4257,57 @@ public final class Class<T> implements java.io.Serializable,
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the type descriptor string for this class.
|
||||
* <p>
|
||||
* Note that this is not a strict inverse of {@link #forName};
|
||||
* Returns the descriptor string of the entity (class, interface, array class,
|
||||
* primitive type, or {@code void}) represented by this {@code Class} object.
|
||||
*
|
||||
* <p> If this {@code Class} object represents a class or interface,
|
||||
* not an array class, then:
|
||||
* <ul>
|
||||
* <li> If the class or interface is not {@linkplain Class#isHidden() hidden},
|
||||
* then the result is a field descriptor (JVMS {@jvms 4.3.2})
|
||||
* for the class or interface. Calling
|
||||
* {@link ClassDesc#ofDescriptor(String) ClassDesc::ofDescriptor}
|
||||
* with the result descriptor string produces a {@link ClassDesc ClassDesc}
|
||||
* describing this class or interface.
|
||||
* <li> If the class or interface is {@linkplain Class#isHidden() hidden},
|
||||
* then the result is a string of the form:
|
||||
* <blockquote>
|
||||
* {@code "L" +} <em>N</em> {@code + "." + <suffix> + ";"}
|
||||
* </blockquote>
|
||||
* where <em>N</em> is the <a href="ClassLoader.html#binary-name">binary name</a>
|
||||
* encoded in internal form indicated by the {@code class} file passed to
|
||||
* {@link MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
|
||||
* Lookup::defineHiddenClass}, and {@code <suffix>} is an unqualified name.
|
||||
* A hidden class or interface has no {@linkplain ClassDesc nominal descriptor}.
|
||||
* The result string is not a type descriptor.
|
||||
* </ul>
|
||||
*
|
||||
* <p> If this {@code Class} object represents an array class, then
|
||||
* the result is a string consisting of one or more '{@code [}' characters
|
||||
* representing the depth of the array nesting, followed by the
|
||||
* descriptor string of the element type.
|
||||
* <ul>
|
||||
* <li> If the element type is not a {@linkplain Class#isHidden() hidden} class
|
||||
* or interface, then this array class can be described nominally.
|
||||
* Calling {@link ClassDesc#ofDescriptor(String) ClassDesc::ofDescriptor}
|
||||
* with the result descriptor string produces a {@link ClassDesc ClassDesc}
|
||||
* describing this array class.
|
||||
* <li> If the element type is a {@linkplain Class#isHidden() hidden} class or
|
||||
* interface, then this array class cannot be described nominally.
|
||||
* The result string is not a type descriptor.
|
||||
* </ul>
|
||||
*
|
||||
* <p> If this {@code Class} object represents a primitive type or
|
||||
* {@code void}, then the result is a field descriptor string which
|
||||
* is a one-letter code corresponding to a primitive type or {@code void}
|
||||
* ({@code "B", "C", "D", "F", "I", "J", "S", "Z", "V"}) (JVMS {@jvms 4.3.2}).
|
||||
*
|
||||
* @apiNote
|
||||
* This is not a strict inverse of {@link #forName};
|
||||
* distinct classes which share a common name but have different class loaders
|
||||
* will have identical descriptor strings.
|
||||
*
|
||||
* @return the type descriptor representation
|
||||
* @return the descriptor string for this {@code Class} object
|
||||
* @jvms 4.3.2 Field Descriptors
|
||||
* @since 12
|
||||
*/
|
||||
|
@ -4210,10 +4315,15 @@ public final class Class<T> implements java.io.Serializable,
|
|||
public String descriptorString() {
|
||||
if (isPrimitive())
|
||||
return Wrapper.forPrimitiveType(this).basicTypeString();
|
||||
else if (isArray()) {
|
||||
|
||||
if (isArray()) {
|
||||
return "[" + componentType.descriptorString();
|
||||
}
|
||||
else {
|
||||
} else if (isHidden()) {
|
||||
String name = getName();
|
||||
int index = name.indexOf('/');
|
||||
return "L" + name.substring(0, index).replace('.', '/')
|
||||
+ "." + name.substring(index+1) + ";";
|
||||
} else {
|
||||
return "L" + getName().replace('.', '/') + ";";
|
||||
}
|
||||
}
|
||||
|
@ -4256,6 +4366,20 @@ public final class Class<T> implements java.io.Serializable,
|
|||
*/
|
||||
@Override
|
||||
public Optional<ClassDesc> describeConstable() {
|
||||
return Optional.of(ClassDesc.ofDescriptor(descriptorString()));
|
||||
}
|
||||
Class<?> c = isArray() ? elementType() : this;
|
||||
return c.isHidden() ? Optional.empty()
|
||||
: Optional.of(ClassDesc.ofDescriptor(descriptorString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if and only if the underlying class is a hidden class.
|
||||
*
|
||||
* @return {@code true} if and only if this class is a hidden class.
|
||||
*
|
||||
* @since 15
|
||||
* @see MethodHandles.Lookup#defineHiddenClass
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native boolean isHidden();
|
||||
|
||||
}
|
||||
|
|
|
@ -1115,6 +1115,29 @@ public abstract class ClassLoader {
|
|||
int off, int len, ProtectionDomain pd,
|
||||
String source);
|
||||
|
||||
/**
|
||||
* Defines a class of the given flags via Lookup.defineClass.
|
||||
*
|
||||
* @param loader the defining loader
|
||||
* @param lookup nest host of the Class to be defined
|
||||
* @param name the binary name or {@code null} if not findable
|
||||
* @param b class bytes
|
||||
* @param off the start offset in {@code b} of the class bytes
|
||||
* @param len the length of the class bytes
|
||||
* @param pd protection domain
|
||||
* @param initialize initialize the class
|
||||
* @param flags flags
|
||||
* @param classData class data
|
||||
*/
|
||||
static native Class<?> defineClass0(ClassLoader loader,
|
||||
Class<?> lookup,
|
||||
String name,
|
||||
byte[] b, int off, int len,
|
||||
ProtectionDomain pd,
|
||||
boolean initialize,
|
||||
int flags,
|
||||
Object classData);
|
||||
|
||||
// true if the name is null or has the potential to be a valid binary name
|
||||
private boolean checkName(String name) {
|
||||
if ((name == null) || (name.isEmpty()))
|
||||
|
|
|
@ -28,6 +28,10 @@ package java.lang;
|
|||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
/**
|
||||
* Helper for string concatenation. These methods are mostly looked up with private lookups
|
||||
* from {@link java.lang.invoke.StringConcatFactory}, and used in {@link java.lang.invoke.MethodHandle}
|
||||
|
@ -466,4 +470,13 @@ final class StringConcatHelper {
|
|||
return String.COMPACT_STRINGS ? LATIN1 : UTF16;
|
||||
}
|
||||
|
||||
static MethodHandle lookupStatic(String name, MethodType methodType) {
|
||||
try {
|
||||
return MethodHandles.lookup().findStatic(StringConcatHelper.class, name, methodType);
|
||||
} catch (NoSuchMethodException|IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -35,6 +35,8 @@ import java.io.InputStream;
|
|||
import java.io.PrintStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
|
@ -2175,6 +2177,10 @@ public final class System {
|
|||
public Class<?> defineClass(ClassLoader loader, String name, byte[] b, ProtectionDomain pd, String source) {
|
||||
return ClassLoader.defineClass1(loader, name, b, 0, b.length, pd, source);
|
||||
}
|
||||
public Class<?> defineClass(ClassLoader loader, Class<?> lookup, String name, byte[] b, ProtectionDomain pd,
|
||||
boolean initialize, int flags, Object classData) {
|
||||
return ClassLoader.defineClass0(loader, lookup, name, b, 0, b.length, pd, initialize, flags, classData);
|
||||
}
|
||||
public Class<?> findBootstrapClassOrNull(ClassLoader cl, String name) {
|
||||
return cl.findBootstrapClassOrNull(name);
|
||||
}
|
||||
|
@ -2257,6 +2263,18 @@ public final class System {
|
|||
public void setCause(Throwable t, Throwable cause) {
|
||||
t.setCause(cause);
|
||||
}
|
||||
|
||||
public ProtectionDomain protectionDomain(Class<?> c) {
|
||||
return c.protectionDomain();
|
||||
}
|
||||
|
||||
public MethodHandle stringConcatHelper(String name, MethodType methodType) {
|
||||
return StringConcatHelper.lookupStatic(name, methodType);
|
||||
}
|
||||
|
||||
public Object classData(Class<?> c) {
|
||||
return c.getClassData();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ import static sun.invoke.util.Wrapper.isWrapperType;
|
|||
* System.out.printf(">>> %s\n", iii.foo(44));
|
||||
* }}
|
||||
*/
|
||||
final MethodHandles.Lookup caller; // The caller's lookup context
|
||||
final Class<?> targetClass; // The class calling the meta-factory via invokedynamic "class X"
|
||||
final MethodType invokedType; // The type of the invoked method "(CC)II"
|
||||
final Class<?> samBase; // The type of the returned instance "interface JJ"
|
||||
|
@ -120,6 +121,7 @@ import static sun.invoke.util.Wrapper.isWrapperType;
|
|||
"Invalid caller: %s",
|
||||
caller.lookupClass().getName()));
|
||||
}
|
||||
this.caller = caller;
|
||||
this.targetClass = caller.lookupClass();
|
||||
this.invokedType = invokedType;
|
||||
|
||||
|
@ -143,8 +145,20 @@ import static sun.invoke.util.Wrapper.isWrapperType;
|
|||
case REF_invokeSpecial:
|
||||
// JDK-8172817: should use referenced class here, but we don't know what it was
|
||||
this.implClass = implInfo.getDeclaringClass();
|
||||
this.implKind = REF_invokeSpecial;
|
||||
this.implIsInstanceMethod = true;
|
||||
|
||||
// Classes compiled prior to dynamic nestmate support invokes a private instance
|
||||
// method with REF_invokeSpecial.
|
||||
//
|
||||
// invokespecial should only be used to invoke private nestmate constructors.
|
||||
// The lambda proxy class will be defined as a nestmate of targetClass.
|
||||
// If the method to be invoked is an instance method of targetClass, then
|
||||
// convert to use invokevirtual or invokeinterface.
|
||||
if (targetClass == implClass && !implInfo.getName().equals("<init>")) {
|
||||
this.implKind = implClass.isInterface() ? REF_invokeInterface : REF_invokeVirtual;
|
||||
} else {
|
||||
this.implKind = REF_invokeSpecial;
|
||||
}
|
||||
break;
|
||||
case REF_invokeStatic:
|
||||
case REF_newInvokeSpecial:
|
||||
|
|
|
@ -196,24 +196,20 @@ class GenerateJLIClassesHelper {
|
|||
private static byte[] generateCodeBytesForLFs(String className,
|
||||
String[] names, LambdaForm[] forms) {
|
||||
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(Opcodes.V1_8, Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER,
|
||||
className, null, InvokerBytecodeGenerator.INVOKER_SUPER_NAME, null);
|
||||
cw.visitSource(className.substring(className.lastIndexOf('/') + 1), null);
|
||||
|
||||
for (int i = 0; i < forms.length; i++) {
|
||||
addMethod(className, names[i], forms[i],
|
||||
forms[i].methodType(), cw);
|
||||
InvokerBytecodeGenerator g
|
||||
= new InvokerBytecodeGenerator(className, names[i], forms[i], forms[i].methodType());
|
||||
g.setClassWriter(cw);
|
||||
g.addMethod();
|
||||
}
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
private static void addMethod(String className, String methodName, LambdaForm form,
|
||||
MethodType type, ClassWriter cw) {
|
||||
InvokerBytecodeGenerator g
|
||||
= new InvokerBytecodeGenerator(className, methodName, form, type);
|
||||
g.setClassWriter(cw);
|
||||
g.addMethod();
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
private static LambdaForm makeReinvokerFor(MethodType type) {
|
||||
|
|
|
@ -27,13 +27,14 @@ package java.lang.invoke;
|
|||
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import sun.invoke.util.BytecodeDescriptor;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
import sun.security.action.GetBooleanAction;
|
||||
|
||||
import java.io.FilePermission;
|
||||
import java.io.Serializable;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.LinkedHashSet;
|
||||
|
@ -41,6 +42,8 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
import java.util.PropertyPermission;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
|
@ -50,13 +53,10 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
* @see LambdaMetafactory
|
||||
*/
|
||||
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
|
||||
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
|
||||
private static final int CLASSFILE_VERSION = 52;
|
||||
private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
|
||||
private static final String JAVA_LANG_OBJECT = "java/lang/Object";
|
||||
private static final String NAME_CTOR = "<init>";
|
||||
private static final String NAME_FACTORY = "get$Lambda";
|
||||
|
||||
//Serialization support
|
||||
private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
|
||||
|
@ -64,13 +64,17 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
|
||||
private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
|
||||
private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
|
||||
private static final String DESCR_SET_IMPL_METHOD = "(Ljava/lang/invoke/MethodHandle;)V";
|
||||
|
||||
private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
|
||||
private static final String NAME_METHOD_READ_OBJECT = "readObject";
|
||||
private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
|
||||
private static final String NAME_FIELD_IMPL_METHOD = "protectedImplMethod";
|
||||
|
||||
private static final String DESCR_CLASS = "Ljava/lang/Class;";
|
||||
private static final String DESCR_STRING = "Ljava/lang/String;";
|
||||
private static final String DESCR_OBJECT = "Ljava/lang/Object;";
|
||||
private static final String DESCR_METHOD_HANDLE = "Ljava/lang/invoke/MethodHandle;";
|
||||
private static final String DESCR_CTOR_SERIALIZED_LAMBDA
|
||||
= "(" + DESCR_CLASS + DESCR_STRING + DESCR_STRING + DESCR_STRING + "I"
|
||||
+ DESCR_STRING + DESCR_STRING + DESCR_STRING + DESCR_STRING + "[" + DESCR_OBJECT + ")V";
|
||||
|
@ -78,8 +82,6 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION = "(Ljava/lang/String;)V";
|
||||
private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
|
||||
|
||||
private static final String DESCR_HIDDEN = "Ljdk/internal/vm/annotation/Hidden;";
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
// Used to ensure that each spun class name is unique
|
||||
|
@ -108,6 +110,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
private final String[] argNames; // Generated names for the constructor arguments
|
||||
private final String[] argDescs; // Type descriptors for the constructor arguments
|
||||
private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1"
|
||||
private final boolean useImplMethodHandle; // use MethodHandle invocation instead of symbolic bytecode invocation
|
||||
|
||||
/**
|
||||
* General meta-factory constructor, supporting both standard cases and
|
||||
|
@ -163,7 +166,9 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
implMethodName = implInfo.getName();
|
||||
implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
|
||||
constructorType = invokedType.changeReturnType(Void.TYPE);
|
||||
lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
|
||||
lambdaClassName = lambdaClassName(targetClass);
|
||||
useImplMethodHandle = !implClass.getPackageName().equals(implInfo.getDeclaringClass().getPackageName())
|
||||
&& !Modifier.isPublic(implInfo.getModifiers());
|
||||
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
int parameterCount = invokedType.parameterCount();
|
||||
if (parameterCount > 0) {
|
||||
|
@ -178,6 +183,15 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
}
|
||||
}
|
||||
|
||||
private static String lambdaClassName(Class<?> targetClass) {
|
||||
String name = targetClass.getName();
|
||||
if (targetClass.isHidden()) {
|
||||
// use the original class name
|
||||
name = name.replace('/', '_');
|
||||
}
|
||||
return name.replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the CallSite. Generate a class file which implements the functional
|
||||
* interface, define the class, if there are no parameters create an instance
|
||||
|
@ -217,20 +231,14 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
try {
|
||||
Object inst = ctrs[0].newInstance();
|
||||
return new ConstantCallSite(MethodHandles.constant(samBase, inst));
|
||||
}
|
||||
catch (ReflectiveOperationException e) {
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new LambdaConversionException("Exception instantiating lambda object", e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (!disableEagerInitialization) {
|
||||
UNSAFE.ensureClassInitialized(innerClass);
|
||||
}
|
||||
return new ConstantCallSite(
|
||||
MethodHandles.Lookup.IMPL_LOOKUP
|
||||
.findStatic(innerClass, NAME_FACTORY, invokedType));
|
||||
}
|
||||
catch (ReflectiveOperationException e) {
|
||||
MethodHandle mh = caller.findConstructor(innerClass, invokedType.changeReturnType(void.class));
|
||||
return new ConstantCallSite(mh.asType(invokedType));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new LambdaConversionException("Exception finding constructor", e);
|
||||
}
|
||||
}
|
||||
|
@ -283,14 +291,9 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
|
||||
generateConstructor();
|
||||
|
||||
if (invokedType.parameterCount() != 0 || disableEagerInitialization) {
|
||||
generateFactory();
|
||||
}
|
||||
|
||||
// Forward the SAM method
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
|
||||
samMethodType.toMethodDescriptorString(), null, null);
|
||||
mv.visitAnnotation(DESCR_HIDDEN, true);
|
||||
new ForwardingMethodGenerator(mv).generate(samMethodType);
|
||||
|
||||
// Forward the bridges
|
||||
|
@ -298,11 +301,18 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
for (MethodType mt : additionalBridges) {
|
||||
mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
|
||||
mt.toMethodDescriptorString(), null, null);
|
||||
mv.visitAnnotation(DESCR_HIDDEN, true);
|
||||
new ForwardingMethodGenerator(mv).generate(mt);
|
||||
}
|
||||
}
|
||||
|
||||
if (useImplMethodHandle) {
|
||||
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_STATIC,
|
||||
NAME_FIELD_IMPL_METHOD,
|
||||
DESCR_METHOD_HANDLE,
|
||||
null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
|
||||
if (isSerializable)
|
||||
generateSerializationFriendlyMethods();
|
||||
else if (accidentallySerializable)
|
||||
|
@ -313,7 +323,6 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
// Define the generated class in this VM.
|
||||
|
||||
final byte[] classBytes = cw.toByteArray();
|
||||
|
||||
// If requested, dump out to a file for debugging purposes
|
||||
if (dumper != null) {
|
||||
AccessController.doPrivileged(new PrivilegedAction<>() {
|
||||
|
@ -327,28 +336,26 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
// createDirectories may need it
|
||||
new PropertyPermission("user.dir", "read"));
|
||||
}
|
||||
|
||||
return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the factory method for the class
|
||||
*/
|
||||
private void generateFactory() {
|
||||
MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null);
|
||||
m.visitCode();
|
||||
m.visitTypeInsn(NEW, lambdaClassName);
|
||||
m.visitInsn(Opcodes.DUP);
|
||||
int parameterCount = invokedType.parameterCount();
|
||||
for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) {
|
||||
Class<?> argType = invokedType.parameterType(typeIndex);
|
||||
m.visitVarInsn(getLoadOpcode(argType), varIndex);
|
||||
varIndex += getParameterSize(argType);
|
||||
try {
|
||||
// this class is linked at the indy callsite; so define a hidden nestmate
|
||||
Lookup lookup = caller.defineHiddenClass(classBytes, !disableEagerInitialization, NESTMATE, STRONG);
|
||||
if (useImplMethodHandle) {
|
||||
// If the target class invokes a method reference this::m which is
|
||||
// resolved to a protected method inherited from a superclass in a different
|
||||
// package, the target class does not have a bridge and this method reference
|
||||
// has been changed from public to protected after the target class was compiled.
|
||||
// This lambda proxy class has no access to the resolved method.
|
||||
// So this workaround by passing the live implMethod method handle
|
||||
// to the proxy class to invoke directly.
|
||||
MethodHandle mh = lookup.findStaticSetter(lookup.lookupClass(), NAME_FIELD_IMPL_METHOD, MethodHandle.class);
|
||||
mh.invokeExact(implMethod);
|
||||
}
|
||||
return lookup.lookupClass();
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new LambdaConversionException("Exception defining lambda proxy class", e);
|
||||
} catch (Throwable t) {
|
||||
throw new InternalError(t);
|
||||
}
|
||||
m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false);
|
||||
m.visitInsn(ARETURN);
|
||||
m.visitMaxs(-1, -1);
|
||||
m.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -464,6 +471,10 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
visitTypeInsn(NEW, implMethodClassName);
|
||||
visitInsn(DUP);
|
||||
}
|
||||
if (useImplMethodHandle) {
|
||||
visitVarInsn(ALOAD, 0);
|
||||
visitFieldInsn(GETSTATIC, lambdaClassName, NAME_FIELD_IMPL_METHOD, DESCR_METHOD_HANDLE);
|
||||
}
|
||||
for (int i = 0; i < argNames.length; i++) {
|
||||
visitVarInsn(ALOAD, 0);
|
||||
visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
|
||||
|
@ -471,11 +482,16 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||
|
||||
convertArgumentTypes(methodType);
|
||||
|
||||
// Invoke the method we want to forward to
|
||||
visitMethodInsn(invocationOpcode(), implMethodClassName,
|
||||
implMethodName, implMethodDesc,
|
||||
implClass.isInterface());
|
||||
|
||||
if (useImplMethodHandle) {
|
||||
MethodType mtype = implInfo.getMethodType().insertParameterTypes(0, implClass);
|
||||
visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
|
||||
"invokeExact", mtype.descriptorString(), false);
|
||||
} else {
|
||||
// Invoke the method we want to forward to
|
||||
visitMethodInsn(invocationOpcode(), implMethodClassName,
|
||||
implMethodName, implMethodDesc,
|
||||
implClass.isInterface());
|
||||
}
|
||||
// Convert the return value (if any) and return it
|
||||
// Note: if adapting from non-void to void, the 'return'
|
||||
// instruction will pop the unneeded result
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.FieldVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Label;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
|
@ -42,6 +43,7 @@ import java.lang.reflect.Modifier;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.invoke.LambdaForm.BasicType;
|
||||
|
@ -49,6 +51,7 @@ import static java.lang.invoke.LambdaForm.BasicType.*;
|
|||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.*;
|
||||
|
||||
/**
|
||||
* Code generation backend for LambdaForm.
|
||||
|
@ -67,6 +70,8 @@ class InvokerBytecodeGenerator {
|
|||
|
||||
private static final String LOOP_CLAUSES = MHI + "$LoopClauses";
|
||||
private static final String MHARY2 = "[[L" + MH + ";";
|
||||
private static final String MH_SIG = "L" + MH + ";";
|
||||
|
||||
|
||||
private static final String LF_SIG = "L" + LF + ";";
|
||||
private static final String LFN_SIG = "L" + LFN + ";";
|
||||
|
@ -92,6 +97,7 @@ class InvokerBytecodeGenerator {
|
|||
/** ASM bytecode generation. */
|
||||
private ClassWriter cw;
|
||||
private MethodVisitor mv;
|
||||
private final List<ClassData> classData = new ArrayList<>();
|
||||
|
||||
/** Single element internal class name lookup cache. */
|
||||
private Class<?> lastClass;
|
||||
|
@ -99,6 +105,15 @@ class InvokerBytecodeGenerator {
|
|||
|
||||
private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
|
||||
private static final Class<?> HOST_CLASS = LambdaForm.class;
|
||||
private static final MethodHandles.Lookup LOOKUP = lookup();
|
||||
|
||||
private static MethodHandles.Lookup lookup() {
|
||||
try {
|
||||
return MethodHandles.privateLookupIn(HOST_CLASS, IMPL_LOOKUP);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw newInternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Main constructor; other constructors delegate to this one. */
|
||||
private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
|
||||
|
@ -221,41 +236,52 @@ class InvokerBytecodeGenerator {
|
|||
return className;
|
||||
}
|
||||
|
||||
class CpPatch {
|
||||
final int index;
|
||||
public static class ClassData {
|
||||
final String name;
|
||||
final String desc;
|
||||
final Object value;
|
||||
CpPatch(int index, Object value) {
|
||||
this.index = index;
|
||||
|
||||
ClassData(String name, String desc, Object value) {
|
||||
this.name = name;
|
||||
this.desc = desc;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String name() { return name; }
|
||||
public String toString() {
|
||||
return "CpPatch/index="+index+",value="+value;
|
||||
return name + ",value="+value;
|
||||
}
|
||||
}
|
||||
|
||||
private final ArrayList<CpPatch> cpPatches = new ArrayList<>();
|
||||
String classData(Object arg) {
|
||||
String desc;
|
||||
if (arg instanceof Class) {
|
||||
desc = "Ljava/lang/Class;";
|
||||
} else if (arg instanceof MethodHandle) {
|
||||
desc = MH_SIG;
|
||||
} else if (arg instanceof LambdaForm) {
|
||||
desc = LF_SIG;
|
||||
} else {
|
||||
desc = "Ljava/lang/Object;";
|
||||
}
|
||||
|
||||
private int cph = 0; // for counting constant placeholders
|
||||
|
||||
String constantPlaceholder(Object arg) {
|
||||
String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
|
||||
if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(arg) + ">>";
|
||||
// TODO check if arg is already in the constant pool
|
||||
// insert placeholder in CP and remember the patch
|
||||
int index = cw.newConst((Object) cpPlaceholder);
|
||||
cpPatches.add(new CpPatch(index, arg));
|
||||
return cpPlaceholder;
|
||||
Class<?> c = arg.getClass();
|
||||
while (c.isArray()) {
|
||||
c = c.getComponentType();
|
||||
}
|
||||
// unique static variable name
|
||||
String name = "_DATA_" + c.getSimpleName() + "_" + classData.size();
|
||||
ClassData cd = new ClassData(name, desc, arg);
|
||||
classData.add(cd);
|
||||
return cd.name();
|
||||
}
|
||||
|
||||
Object[] cpPatches(byte[] classFile) {
|
||||
int size = getConstantPoolSize(classFile);
|
||||
Object[] res = new Object[size];
|
||||
for (CpPatch p : cpPatches) {
|
||||
if (p.index >= size)
|
||||
throw new InternalError("in cpool["+size+"]: "+p+"\n"+Arrays.toString(Arrays.copyOf(classFile, 20)));
|
||||
res[p.index] = p.value;
|
||||
List<Object> classDataValues() {
|
||||
Object[] data = new Object[classData.size()];
|
||||
for (int i = 0; i < classData.size(); i++) {
|
||||
data[i] = classData.get(i).value;
|
||||
}
|
||||
return res;
|
||||
return List.of(data);
|
||||
}
|
||||
|
||||
private static String debugString(Object arg) {
|
||||
|
@ -288,19 +314,11 @@ class InvokerBytecodeGenerator {
|
|||
* Extract the MemberName of a newly-defined method.
|
||||
*/
|
||||
private MemberName loadMethod(byte[] classFile) {
|
||||
Class<?> invokerClass = loadAndInitializeInvokerClass(classFile, cpPatches(classFile));
|
||||
Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(classFile)
|
||||
.defineClass(true, classDataValues());
|
||||
return resolveInvokerMember(invokerClass, invokerName, invokerType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a given class as anonymous class in the runtime system.
|
||||
*/
|
||||
private static Class<?> loadAndInitializeInvokerClass(byte[] classBytes, Object[] patches) {
|
||||
Class<?> invokerClass = UNSAFE.defineAnonymousClass(HOST_CLASS, classBytes, patches);
|
||||
UNSAFE.ensureClassInitialized(invokerClass); // Make sure the class is initialized; VM might complain.
|
||||
return invokerClass;
|
||||
}
|
||||
|
||||
private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) {
|
||||
MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic);
|
||||
try {
|
||||
|
@ -316,7 +334,8 @@ class InvokerBytecodeGenerator {
|
|||
*/
|
||||
private ClassWriter classFilePrologue() {
|
||||
final int NOT_ACC_PUBLIC = 0; // not ACC_PUBLIC
|
||||
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
||||
setClassWriter(cw);
|
||||
cw.visit(Opcodes.V1_8, NOT_ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER,
|
||||
CLASS_PREFIX + className, null, INVOKER_SUPER_NAME, null);
|
||||
cw.visitSource(SOURCE_PREFIX + className, null);
|
||||
|
@ -336,6 +355,51 @@ class InvokerBytecodeGenerator {
|
|||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private String className() {
|
||||
return CLASS_PREFIX + className;
|
||||
}
|
||||
|
||||
private void clinit() {
|
||||
clinit(cw, className(), classData);
|
||||
}
|
||||
|
||||
/*
|
||||
* <clinit> to initialize the static final fields with the live class data
|
||||
* LambdaForms can't use condy due to bootstrapping issue.
|
||||
*/
|
||||
static void clinit(ClassWriter cw, String className, List<ClassData> classData) {
|
||||
if (classData.isEmpty())
|
||||
return;
|
||||
|
||||
for (ClassData p : classData) {
|
||||
// add the static field
|
||||
FieldVisitor fv = cw.visitField(Opcodes.ACC_STATIC|Opcodes.ACC_FINAL, p.name, p.desc, null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitLdcInsn(Type.getType("L" + className + ";"));
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandleNatives",
|
||||
"classData", "(Ljava/lang/Class;)Ljava/lang/Object;", false);
|
||||
// we should optimize one single element case that does not need to create a List
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/util/List");
|
||||
mv.visitVarInsn(Opcodes.ASTORE, 0);
|
||||
int index = 0;
|
||||
for (ClassData p : classData) {
|
||||
// initialize the static field
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
emitIconstInsn(mv, index++);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/List",
|
||||
"get", "(I)Ljava/lang/Object;", true);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, p.desc.substring(1, p.desc.length()-1));
|
||||
mv.visitFieldInsn(Opcodes.PUTSTATIC, className, p.name, p.desc);
|
||||
}
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
mv.visitMaxs(2, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
/*
|
||||
* Low-level emit helpers.
|
||||
*/
|
||||
|
@ -408,6 +472,10 @@ class InvokerBytecodeGenerator {
|
|||
}
|
||||
|
||||
private void emitIconstInsn(final int cst) {
|
||||
emitIconstInsn(mv, cst);
|
||||
}
|
||||
|
||||
private static void emitIconstInsn(MethodVisitor mv, int cst) {
|
||||
if (cst >= -1 && cst <= 5) {
|
||||
mv.visitInsn(Opcodes.ICONST_0 + cst);
|
||||
} else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
|
||||
|
@ -577,8 +645,7 @@ class InvokerBytecodeGenerator {
|
|||
String sig = getInternalName(cls);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, sig);
|
||||
} else {
|
||||
mv.visitLdcInsn(constantPlaceholder(cls));
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(cls), "Ljava/lang/Class;");
|
||||
mv.visitInsn(Opcodes.SWAP);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG, false);
|
||||
if (Object[].class.isAssignableFrom(cls))
|
||||
|
@ -737,6 +804,7 @@ class InvokerBytecodeGenerator {
|
|||
private byte[] generateCustomizedCodeBytes() {
|
||||
classFilePrologue();
|
||||
addMethod();
|
||||
clinit();
|
||||
bogusMethod(lambdaForm);
|
||||
|
||||
final byte[] classFile = toByteArray();
|
||||
|
@ -764,14 +832,14 @@ class InvokerBytecodeGenerator {
|
|||
mv.visitAnnotation(DONTINLINE_SIG, true);
|
||||
}
|
||||
|
||||
constantPlaceholder(lambdaForm); // keep LambdaForm instance & its compiled form lifetime tightly coupled.
|
||||
classData(lambdaForm); // keep LambdaForm instance & its compiled form lifetime tightly coupled.
|
||||
|
||||
if (lambdaForm.customized != null) {
|
||||
// Since LambdaForm is customized for a particular MethodHandle, it's safe to substitute
|
||||
// receiver MethodHandle (at slot #0) with an embedded constant and use it instead.
|
||||
// It enables more efficient code generation in some situations, since embedded constants
|
||||
// are compile-time constants for JIT compiler.
|
||||
mv.visitLdcInsn(constantPlaceholder(lambdaForm.customized));
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(lambdaForm.customized), MH_SIG);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
|
||||
assert(checkActualReceiver()); // expects MethodHandle on top of the stack
|
||||
mv.visitVarInsn(Opcodes.ASTORE, localsMap[0]);
|
||||
|
@ -901,7 +969,7 @@ class InvokerBytecodeGenerator {
|
|||
// push receiver
|
||||
MethodHandle target = name.function.resolvedHandle();
|
||||
assert(target != null) : name.exprString();
|
||||
mv.visitLdcInsn(constantPlaceholder(target));
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(target), MH_SIG);
|
||||
emitReferenceCast(MethodHandle.class, target);
|
||||
} else {
|
||||
// load receiver
|
||||
|
@ -957,7 +1025,9 @@ class InvokerBytecodeGenerator {
|
|||
return false; // inner class of some sort
|
||||
if (cls.getClassLoader() != MethodHandle.class.getClassLoader())
|
||||
return false; // not on BCP
|
||||
if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: switch to supported API once it is added
|
||||
if (cls.isHidden())
|
||||
return false;
|
||||
if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: Unsafe::defineAnonymousClass to be removed
|
||||
return false;
|
||||
if (!isStaticallyInvocableType(member.getMethodOrFieldType()))
|
||||
return false;
|
||||
|
@ -981,14 +1051,16 @@ class InvokerBytecodeGenerator {
|
|||
if (cls == Object.class)
|
||||
return true;
|
||||
if (MethodHandle.class.isAssignableFrom(cls)) {
|
||||
assert(!ReflectUtil.isVMAnonymousClass(cls));
|
||||
assert(!cls.isHidden());
|
||||
return true;
|
||||
}
|
||||
while (cls.isArray())
|
||||
cls = cls.getComponentType();
|
||||
if (cls.isPrimitive())
|
||||
return true; // int[].class, for example
|
||||
if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: switch to supported API once it is added
|
||||
if (cls.isHidden())
|
||||
return false;
|
||||
if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: Unsafe::defineAnonymousClass to be removed
|
||||
return false;
|
||||
// could use VerifyAccess.isClassAccessible but the following is a safe approximation
|
||||
if (cls.getClassLoader() != Object.class.getClassLoader())
|
||||
|
@ -1060,7 +1132,7 @@ class InvokerBytecodeGenerator {
|
|||
}
|
||||
assert(java.lang.reflect.Array.getLength(emptyArray) == 0);
|
||||
assert(emptyArray.getClass() == rtype); // exact typing
|
||||
mv.visitLdcInsn(constantPlaceholder(emptyArray));
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(emptyArray), "Ljava/lang/Object;");
|
||||
emitReferenceCast(rtype, emptyArray);
|
||||
return;
|
||||
}
|
||||
|
@ -1623,7 +1695,7 @@ class InvokerBytecodeGenerator {
|
|||
if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) {
|
||||
emitConst(arg);
|
||||
} else {
|
||||
mv.visitLdcInsn(constantPlaceholder(arg));
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(arg), "Ljava/lang/Object;");
|
||||
emitImplicitConversion(L_TYPE, ptype, arg);
|
||||
}
|
||||
}
|
||||
|
@ -1815,6 +1887,7 @@ class InvokerBytecodeGenerator {
|
|||
emitReturnInsn(basicType(rtype));
|
||||
|
||||
methodEpilogue();
|
||||
clinit();
|
||||
bogusMethod(invokerType);
|
||||
|
||||
final byte[] classFile = cw.toByteArray();
|
||||
|
@ -1883,6 +1956,7 @@ class InvokerBytecodeGenerator {
|
|||
emitReturnInsn(L_TYPE); // NOTE: NamedFunction invokers always return a reference value.
|
||||
|
||||
methodEpilogue();
|
||||
clinit();
|
||||
bogusMethod(dstType);
|
||||
|
||||
final byte[] classFile = cw.toByteArray();
|
||||
|
|
|
@ -27,7 +27,6 @@ package java.lang.invoke;
|
|||
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
|
@ -40,6 +39,7 @@ import sun.invoke.util.ValueConversions;
|
|||
import sun.invoke.util.VerifyType;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
|
@ -1157,10 +1157,24 @@ abstract class MethodHandleImpl {
|
|||
return restoreToType(bccInvoker.bindTo(vamh), mh, hostClass);
|
||||
}
|
||||
|
||||
private static MethodHandle makeInjectedInvoker(Class<?> hostClass) {
|
||||
private static MethodHandle makeInjectedInvoker(Class<?> targetClass) {
|
||||
try {
|
||||
Class<?> invokerClass = UNSAFE.defineAnonymousClass(hostClass, INJECTED_INVOKER_TEMPLATE, null);
|
||||
assert checkInjectedInvoker(hostClass, invokerClass);
|
||||
/*
|
||||
* The invoker class defined to the same class loader as the lookup class
|
||||
* but in an unnamed package so that the class bytes can be cached and
|
||||
* reused for any @CSM.
|
||||
*
|
||||
* @CSM must be public and exported if called by any module.
|
||||
*/
|
||||
String name = targetClass.getName() + "$$InjectedInvoker";
|
||||
if (targetClass.isHidden()) {
|
||||
// use the original class name
|
||||
name = name.replace('/', '_');
|
||||
}
|
||||
Class<?> invokerClass = new Lookup(targetClass)
|
||||
.makeHiddenClassDefiner(name, INJECTED_INVOKER_TEMPLATE)
|
||||
.defineClass(true);
|
||||
assert checkInjectedInvoker(targetClass, invokerClass);
|
||||
return IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw uncaughtException(ex);
|
||||
|
@ -1256,10 +1270,6 @@ abstract class MethodHandleImpl {
|
|||
"(Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;",
|
||||
null, null);
|
||||
|
||||
// Suppress invoker method in stack traces.
|
||||
AnnotationVisitor av0 = mv.visitAnnotation(InvokerBytecodeGenerator.HIDDEN_SIG, true);
|
||||
av0.visitEnd();
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.ref.CleanerFactory;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
|
@ -137,6 +139,15 @@ class MethodHandleNatives {
|
|||
REF_newInvokeSpecial = 8,
|
||||
REF_invokeInterface = 9,
|
||||
REF_LIMIT = 10;
|
||||
|
||||
/**
|
||||
* Flags for Lookup.ClassOptions
|
||||
*/
|
||||
static final int
|
||||
NESTMATE_CLASS = 0x00000001,
|
||||
HIDDEN_CLASS = 0x00000002,
|
||||
STRONG_LOADER_LINK = 0x00000004,
|
||||
ACCESS_VM_ANNOTATIONS = 0x00000008;
|
||||
}
|
||||
|
||||
static boolean refKindIsValid(int refKind) {
|
||||
|
@ -659,4 +670,13 @@ class MethodHandleNatives {
|
|||
return (definingClass.isAssignableFrom(symbolicRefClass) || // Msym overrides Mdef
|
||||
symbolicRefClass.isInterface()); // Mdef implements Msym
|
||||
}
|
||||
|
||||
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||
/*
|
||||
* A convenient method for LambdaForms to get the class data of a given class.
|
||||
* LambdaForms cannot use condy via MethodHandles.classData
|
||||
*/
|
||||
static Object classData(Class<?> c) {
|
||||
return JLA.classData(c);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,9 +25,12 @@
|
|||
|
||||
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;
|
||||
|
@ -45,8 +48,6 @@ 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;
|
||||
|
@ -219,6 +220,10 @@ public class MethodHandles {
|
|||
* @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())
|
||||
|
@ -262,6 +267,57 @@ public class MethodHandles {
|
|||
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>.
|
||||
|
@ -517,7 +573,7 @@ public class MethodHandles {
|
|||
* 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}.)
|
||||
* (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>"}).
|
||||
|
@ -1400,8 +1456,6 @@ public class MethodHandles {
|
|||
*/
|
||||
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) {
|
||||
|
@ -1508,7 +1562,7 @@ public class MethodHandles {
|
|||
}
|
||||
// Allow nestmate lookups to be created without special privilege:
|
||||
if ((newModes & PRIVATE) != 0
|
||||
&& !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) {
|
||||
&& !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) {
|
||||
newModes &= ~(PRIVATE|PROTECTED);
|
||||
}
|
||||
if ((newModes & (PUBLIC|UNCONDITIONAL)) != 0
|
||||
|
@ -1577,9 +1631,12 @@ public class MethodHandles {
|
|||
}
|
||||
|
||||
/**
|
||||
* Defines a class to the same class loader and in the same runtime package and
|
||||
* 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}.
|
||||
* {@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
|
||||
|
@ -1602,11 +1659,12 @@ public class MethodHandles {
|
|||
*
|
||||
* @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 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 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}
|
||||
|
@ -1617,66 +1675,537 @@ public class MethodHandles {
|
|||
* @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"));
|
||||
}
|
||||
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;
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* <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}:
|
||||
* <ul>
|
||||
* <li> {@link Class#getName()} returns the string {@code GN + "/" + <suffix>},
|
||||
* even though this is not a valid binary class or interface name.</li>
|
||||
* <li> {@link Class#descriptorString()} returns the string
|
||||
* {@code "L" + N + "." + <suffix> + ";"},
|
||||
* even though this is not a valid type descriptor name.</li>
|
||||
* <li> {@link Class#describeConstable()} returns an empty optional as {@code C}
|
||||
* cannot be described in {@linkplain java.lang.constant.ClassDesc nominal form}.</li>
|
||||
* </ul>
|
||||
* </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);
|
||||
name = reader.getClassName();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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");
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
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 = protectionDomain(lookupClass);
|
||||
cachedProtectionDomain = pd = JLA.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(); }
|
||||
|
||||
|
@ -1689,6 +2218,8 @@ public class MethodHandles {
|
|||
*/
|
||||
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."))
|
||||
|
@ -1749,7 +2280,7 @@ public class MethodHandles {
|
|||
return cname + "/package";
|
||||
case FULL_POWER_MODES & (~PROTECTED):
|
||||
case FULL_POWER_MODES & ~(PROTECTED|MODULE):
|
||||
return cname + "/private";
|
||||
return cname + "/private";
|
||||
case FULL_POWER_MODES:
|
||||
case FULL_POWER_MODES & (~MODULE):
|
||||
return cname;
|
||||
|
@ -2639,8 +3170,13 @@ return mh1;
|
|||
|
||||
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);
|
||||
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()));
|
||||
|
@ -3201,7 +3737,8 @@ return mh1;
|
|||
}
|
||||
refc = lookupClass();
|
||||
}
|
||||
return VarHandles.makeFieldHandle(getField, refc, getField.getFieldType(), this.allowedModes == TRUSTED);
|
||||
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 {
|
||||
|
|
|
@ -99,6 +99,36 @@ import static java.lang.invoke.MethodType.fromDescriptor;
|
|||
* all classes named in the descriptor must be accessible, and will be loaded.
|
||||
* (But the classes need not be initialized, as is the case with a {@code CONSTANT_Class}.)
|
||||
* This loading may occur at any time before the {@code MethodType} object is first derived.
|
||||
* <p>
|
||||
* <b><a id="descriptor">Nominal Descriptors</a></b>
|
||||
* <p>
|
||||
* A {@code MethodType} can be described in {@linkplain MethodTypeDesc nominal form}
|
||||
* if and only if all of the parameter types and return type can be described
|
||||
* with a {@link Class#describeConstable() nominal descriptor} represented by
|
||||
* {@link ClassDesc}. If a method type can be described norminally, then:
|
||||
* <ul>
|
||||
* <li>The method type has a {@link MethodTypeDesc nominal descriptor}
|
||||
* returned by {@link #describeConstable() MethodType::describeConstable}.</li>
|
||||
* <li>The descriptor string returned by
|
||||
* {@link #descriptorString() MethodType::descriptorString} or
|
||||
* {@link #toMethodDescriptorString() MethodType::toMethodDescriptorString}
|
||||
* for the method type is a method descriptor (JVMS {@jvms 4.3.3}).</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* If any of the parameter types or return type cannot be described
|
||||
* nominally, i.e. {@link Class#describeConstable() Class::describeConstable}
|
||||
* returns an empty optional for that type,
|
||||
* then the method type cannot be described nominally:
|
||||
* <ul>
|
||||
* <li>The method type has no {@link MethodTypeDesc nominal descriptor} and
|
||||
* {@link #describeConstable() MethodType::describeConstable} returns
|
||||
* an empty optional.</li>
|
||||
* <li>The descriptor string returned by
|
||||
* {@link #descriptorString() MethodType::descriptorString} or
|
||||
* {@link #toMethodDescriptorString() MethodType::toMethodDescriptorString}
|
||||
* for the method type is not a type descriptor.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author John Rose, JSR 292 EG
|
||||
* @since 1.7
|
||||
*/
|
||||
|
@ -1140,7 +1170,9 @@ class MethodType
|
|||
}
|
||||
|
||||
/**
|
||||
* Produces a bytecode descriptor representation of the method type.
|
||||
* Returns a descriptor string for the method type. This method
|
||||
* is equivalent to calling {@link #descriptorString() MethodType::descriptorString}.
|
||||
*
|
||||
* <p>
|
||||
* Note that this is not a strict inverse of {@link #fromMethodDescriptorString fromMethodDescriptorString}.
|
||||
* Two distinct classes which share a common name but have different class loaders
|
||||
|
@ -1150,7 +1182,9 @@ class MethodType
|
|||
* generate bytecodes that process method handles and {@code invokedynamic}.
|
||||
* {@link #fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader) fromMethodDescriptorString},
|
||||
* because the latter requires a suitable class loader argument.
|
||||
* @return the bytecode type descriptor representation
|
||||
* @return the descriptor string for this method type
|
||||
* @jvms 4.3.3 Method Descriptors
|
||||
* @see <a href="#descriptor">Nominal Descriptor for {@code MethodType}</a>
|
||||
*/
|
||||
public String toMethodDescriptorString() {
|
||||
String desc = methodDescriptor;
|
||||
|
@ -1162,11 +1196,28 @@ class MethodType
|
|||
}
|
||||
|
||||
/**
|
||||
* Return a field type descriptor string for this type
|
||||
* Returns a descriptor string for this method type.
|
||||
*
|
||||
* @return the descriptor string
|
||||
* @jvms 4.3.2 Field Descriptors
|
||||
* <p>
|
||||
* If this method type can be <a href="#descriptor">described nominally</a>,
|
||||
* then the result is a method type descriptor (JVMS {@jvms 4.3.3}).
|
||||
* {@link MethodTypeDesc MethodTypeDesc} for this method type
|
||||
* can be produced by calling {@link MethodTypeDesc#ofDescriptor(String)
|
||||
* MethodTypeDesc::ofDescriptor} with the result descriptor string.
|
||||
* <p>
|
||||
* If this method type cannot be <a href="#descriptor">described nominally</a>
|
||||
* and the result is a string of the form:
|
||||
* <blockquote>{@code "(<parameter-descriptors>)<return-descriptor>"}</blockquote>
|
||||
* where {@code <parameter-descriptors>} is the concatenation of the
|
||||
* {@linkplain Class#descriptorString() descriptor string} of all
|
||||
* of the parameter types and the {@linkplain Class#descriptorString() descriptor string}
|
||||
* of the return type. No {@link java.lang.constant.MethodTypeDesc MethodTypeDesc}
|
||||
* can be produced from the result string.
|
||||
*
|
||||
* @return the descriptor string for this method type
|
||||
* @since 12
|
||||
* @jvms 4.3.3 Method Descriptors
|
||||
* @see <a href="#descriptor">Nominal Descriptor for {@code MethodType}</a>
|
||||
*/
|
||||
@Override
|
||||
public String descriptorString() {
|
||||
|
@ -1179,12 +1230,13 @@ class MethodType
|
|||
}
|
||||
|
||||
/**
|
||||
* Return a nominal descriptor for this instance, if one can be
|
||||
* Returns a nominal descriptor for this instance, if one can be
|
||||
* constructed, or an empty {@link Optional} if one cannot be.
|
||||
*
|
||||
* @return An {@link Optional} containing the resulting nominal descriptor,
|
||||
* or an empty {@link Optional} if one cannot be constructed.
|
||||
* @since 12
|
||||
* @see <a href="#descriptor">Nominal Descriptor for {@code MethodType}</a>
|
||||
*/
|
||||
@Override
|
||||
public Optional<MethodTypeDesc> describeConstable() {
|
||||
|
|
|
@ -25,7 +25,8 @@
|
|||
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Label;
|
||||
|
@ -42,6 +43,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.lang.invoke.MethodHandles.lookup;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
|
@ -133,6 +137,8 @@ public final class StringConcatFactory {
|
|||
*/
|
||||
private static final Strategy DEFAULT_STRATEGY = Strategy.MH_INLINE_SIZED_EXACT;
|
||||
|
||||
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||
|
||||
private enum Strategy {
|
||||
/**
|
||||
* Bytecode generator, calling into {@link java.lang.StringBuilder}.
|
||||
|
@ -189,8 +195,6 @@ public final class StringConcatFactory {
|
|||
*/
|
||||
private static final ProxyClassesDumper DUMPER;
|
||||
|
||||
private static final Class<?> STRING_HELPER;
|
||||
|
||||
static {
|
||||
// In case we need to double-back onto the StringConcatFactory during this
|
||||
// static initialization, make sure we have the reasonable defaults to complete
|
||||
|
@ -202,12 +206,6 @@ public final class StringConcatFactory {
|
|||
// DEBUG = false; // implied
|
||||
// DUMPER = null; // implied
|
||||
|
||||
try {
|
||||
STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
|
||||
} catch (Throwable e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
final String strategy =
|
||||
VM.getSavedProperty("java.lang.invoke.stringConcat");
|
||||
CACHE_ENABLE = Boolean.parseBoolean(
|
||||
|
@ -718,25 +716,10 @@ public final class StringConcatFactory {
|
|||
|
||||
private static String getClassName(Class<?> hostClass) throws StringConcatException {
|
||||
/*
|
||||
The generated class is in the same package as the host class as
|
||||
it's the implementation of the string concatenation for the host class.
|
||||
|
||||
When cache is enabled, we want to cache as much as we can.
|
||||
|
||||
However, there are two peculiarities:
|
||||
|
||||
a) The generated class should stay within the same package as the
|
||||
host class, to allow Unsafe.defineAnonymousClass access controls
|
||||
to work properly. JDK may choose to fail with IllegalAccessException
|
||||
when accessing a VM anonymous class with non-privileged callers,
|
||||
see JDK-8058575.
|
||||
|
||||
b) If we mark the stub with some prefix, say, derived from the package
|
||||
name because of (a), we can technically use that stub in other packages.
|
||||
But the call stack traces would be extremely puzzling to unsuspecting users
|
||||
and profiling tools: whatever stub wins the race, would be linked in all
|
||||
similar callsites.
|
||||
|
||||
Therefore, we set the class prefix to match the host class package, and use
|
||||
the prefix as the cache key too. This only affects BC_* strategies, and only when
|
||||
cache is enabled.
|
||||
*/
|
||||
|
||||
switch (STRATEGY) {
|
||||
|
@ -745,9 +728,11 @@ public final class StringConcatFactory {
|
|||
case BC_SB_SIZED_EXACT: {
|
||||
if (CACHE_ENABLE) {
|
||||
String pkgName = hostClass.getPackageName();
|
||||
return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
|
||||
return (!pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
|
||||
} else {
|
||||
return hostClass.getName().replace('.', '/') + "$$StringConcat";
|
||||
String name = hostClass.isHidden() ? hostClass.getName().replace('/', '_')
|
||||
: hostClass.getName();
|
||||
return name.replace('.', '/') + "$$StringConcat";
|
||||
}
|
||||
}
|
||||
case MH_SB_SIZED:
|
||||
|
@ -819,7 +804,7 @@ public final class StringConcatFactory {
|
|||
* chain javac would otherwise emit. This strategy uses only the public API,
|
||||
* and comes as the baseline for the current JDK behavior. On other words,
|
||||
* this strategy moves the javac generated bytecode to runtime. The
|
||||
* generated bytecode is loaded via Unsafe.defineAnonymousClass, but with
|
||||
* generated bytecode is loaded via Lookup::defineClass, but with
|
||||
* the caller class coming from the BSM -- in other words, the protection
|
||||
* guarantees are inherited from the method where invokedynamic was
|
||||
* originally called. This means, among other things, that the bytecode is
|
||||
|
@ -848,7 +833,6 @@ public final class StringConcatFactory {
|
|||
* private String API.
|
||||
*/
|
||||
private static final class BytecodeStringBuilderStrategy {
|
||||
static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
static final int CLASSFILE_VERSION = 52;
|
||||
static final String METHOD_NAME = "concat";
|
||||
|
||||
|
@ -861,7 +845,7 @@ public final class StringConcatFactory {
|
|||
|
||||
cw.visit(CLASSFILE_VERSION,
|
||||
ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC,
|
||||
className, // Unsafe.defineAnonymousClass would append an unique ID
|
||||
className,
|
||||
null,
|
||||
"java/lang/Object",
|
||||
null
|
||||
|
@ -874,6 +858,7 @@ public final class StringConcatFactory {
|
|||
null,
|
||||
null);
|
||||
|
||||
// use of @ForceInline no longer has any effect
|
||||
mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true);
|
||||
mv.visitCode();
|
||||
|
||||
|
@ -1143,11 +1128,9 @@ public final class StringConcatFactory {
|
|||
|
||||
byte[] classBytes = cw.toByteArray();
|
||||
try {
|
||||
Class<?> hostClass = lookup.lookupClass();
|
||||
Class<?> innerClass = UNSAFE.defineAnonymousClass(hostClass, classBytes, null);
|
||||
UNSAFE.ensureClassInitialized(innerClass);
|
||||
dumpIfEnabled(innerClass.getName(), classBytes);
|
||||
return Lookup.IMPL_LOOKUP.findStatic(innerClass, METHOD_NAME, args);
|
||||
Class<?> innerClass = lookup.defineHiddenClass(classBytes, true, STRONG).lookupClass();
|
||||
dumpIfEnabled(className, classBytes);
|
||||
return lookup.findStatic(innerClass, METHOD_NAME, args);
|
||||
} catch (Exception e) {
|
||||
dumpIfEnabled(className + "$$FAILED", classBytes);
|
||||
throw new StringConcatException("Exception while spinning the class", e);
|
||||
|
@ -1270,8 +1253,8 @@ public final class StringConcatFactory {
|
|||
* computation on MethodHandle combinators. The computation is built with
|
||||
* public MethodHandle APIs, resolved from a public Lookup sequence, and
|
||||
* ends up calling the public StringBuilder API. Therefore, this strategy
|
||||
* does not use any private API at all, even the Unsafe.defineAnonymousClass,
|
||||
* since everything is handled under cover by java.lang.invoke APIs.
|
||||
* does not use any private API at all since everything is handled under
|
||||
* cover by java.lang.invoke APIs.
|
||||
*
|
||||
* <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder,
|
||||
* sized exactly".</b>
|
||||
|
@ -1283,7 +1266,6 @@ public final class StringConcatFactory {
|
|||
* private String API.
|
||||
*/
|
||||
private static final class MethodHandleStringBuilderStrategy {
|
||||
|
||||
private MethodHandleStringBuilderStrategy() {
|
||||
// no instantiation
|
||||
}
|
||||
|
@ -1461,6 +1443,8 @@ public final class StringConcatFactory {
|
|||
return sum;
|
||||
}
|
||||
|
||||
private static final Lookup MHSBS_LOOKUP = lookup();
|
||||
|
||||
private static final ConcurrentMap<Integer, MethodHandle> SUMMERS;
|
||||
|
||||
// This one is deliberately non-lambdified to optimize startup time:
|
||||
|
@ -1474,9 +1458,9 @@ public final class StringConcatFactory {
|
|||
// unroll some initial sizes.
|
||||
Class<?>[] cls = new Class<?>[cnt];
|
||||
Arrays.fill(cls, int.class);
|
||||
return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls);
|
||||
return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls);
|
||||
} else {
|
||||
return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class)
|
||||
return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class)
|
||||
.asCollector(int[].class, cnt - 1);
|
||||
}
|
||||
}
|
||||
|
@ -1491,8 +1475,8 @@ public final class StringConcatFactory {
|
|||
STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class);
|
||||
BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class);
|
||||
if (DEBUG) {
|
||||
BUILDER_TO_STRING_CHECKED = lookupStatic(MethodHandles.Lookup.IMPL_LOOKUP,
|
||||
MethodHandleStringBuilderStrategy.class, "toStringChecked", String.class, StringBuilder.class);
|
||||
BUILDER_TO_STRING_CHECKED = lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class,
|
||||
"toStringChecked", String.class, StringBuilder.class);
|
||||
} else {
|
||||
BUILDER_TO_STRING_CHECKED = null;
|
||||
}
|
||||
|
@ -1516,8 +1500,6 @@ public final class StringConcatFactory {
|
|||
* that requires porting if there are private JDK changes occur.
|
||||
*/
|
||||
private static final class MethodHandleInlineCopyStrategy {
|
||||
static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
|
||||
private MethodHandleInlineCopyStrategy() {
|
||||
// no instantiation
|
||||
}
|
||||
|
@ -1736,8 +1718,9 @@ public final class StringConcatFactory {
|
|||
private static final Function<Class<?>, MethodHandle> PREPEND = new Function<>() {
|
||||
@Override
|
||||
public MethodHandle apply(Class<?> c) {
|
||||
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class,
|
||||
String.class, Wrapper.asPrimitiveType(c), String.class);
|
||||
return JLA.stringConcatHelper("prepend",
|
||||
methodType(long.class, long.class, byte[].class,
|
||||
String.class, Wrapper.asPrimitiveType(c), String.class));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1745,8 +1728,7 @@ public final class StringConcatFactory {
|
|||
private static final Function<Class<?>, MethodHandle> MIX = new Function<>() {
|
||||
@Override
|
||||
public MethodHandle apply(Class<?> c) {
|
||||
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
|
||||
Wrapper.asPrimitiveType(c));
|
||||
return JLA.stringConcatHelper("mix", methodType(long.class, long.class, Wrapper.asPrimitiveType(c)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1759,7 +1741,7 @@ public final class StringConcatFactory {
|
|||
|
||||
static {
|
||||
try {
|
||||
MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class);
|
||||
MethodHandle initCoder = JLA.stringConcatHelper("initialCoder", methodType(long.class));
|
||||
INITIAL_CODER = (long) initCoder.invoke();
|
||||
} catch (Throwable e) {
|
||||
throw new AssertionError(e);
|
||||
|
@ -1768,9 +1750,9 @@ public final class StringConcatFactory {
|
|||
PREPENDERS = new ConcurrentHashMap<>();
|
||||
MIXERS = new ConcurrentHashMap<>();
|
||||
|
||||
SIMPLE = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "simpleConcat", String.class, Object.class, Object.class);
|
||||
NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class);
|
||||
NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newArray", byte[].class, long.class);
|
||||
SIMPLE = JLA.stringConcatHelper("simpleConcat", methodType(String.class, Object.class, Object.class));
|
||||
NEW_STRING = JLA.stringConcatHelper("newString", methodType(String.class, byte[].class, long.class));
|
||||
NEW_ARRAY = JLA.stringConcatHelper( "newArray", methodType(byte[].class, long.class));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1784,7 +1766,7 @@ public final class StringConcatFactory {
|
|||
}
|
||||
|
||||
private static final MethodHandle OBJECT_INSTANCE =
|
||||
lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "stringOf", String.class, Object.class);
|
||||
JLA.stringConcatHelper("stringOf", methodType(String.class, Object.class));
|
||||
|
||||
private static class FloatStringifiers {
|
||||
private static final MethodHandle FLOAT_INSTANCE =
|
||||
|
|
|
@ -27,19 +27,24 @@ package java.lang.invoke;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An entity that has a field or method type descriptor
|
||||
*
|
||||
* @jvms 4.3.2 Field Descriptors
|
||||
* @jvms 4.3.3 Method Descriptors
|
||||
* An entity that has a type descriptor.
|
||||
*
|
||||
* @since 12
|
||||
*/
|
||||
public interface TypeDescriptor {
|
||||
/**
|
||||
* Return the type descriptor string for this instance, which must be either
|
||||
* a field type descriptor (JVMS 4.3.2) or method type descriptor (JVMS 4.3.3).
|
||||
* Returns the descriptor string for this {@code TypeDescriptor} object.
|
||||
*
|
||||
* @return the type descriptor
|
||||
* If this {@code TypeDescriptor} object can be described in nominal form,
|
||||
* then this method returns a type descriptor as specified in JVMS {@jvms 4.3}.
|
||||
* The result descriptor string can be used to produce
|
||||
* a {@linkplain java.lang.constant.ConstantDesc nominal descriptor}.
|
||||
*
|
||||
* Otherwise, the result string is not a type descriptor.
|
||||
* No {@linkplain java.lang.constant.ConstantDesc nominal descriptor}
|
||||
* can be produced from the result string.
|
||||
*
|
||||
* @return the descriptor string for this {@code TypeDescriptor} object
|
||||
* @jvms 4.3.2 Field Descriptors
|
||||
* @jvms 4.3.3 Method Descriptors
|
||||
*/
|
||||
|
@ -47,7 +52,10 @@ public interface TypeDescriptor {
|
|||
|
||||
|
||||
/**
|
||||
* An entity that has a field type descriptor
|
||||
* An entity that has a field type descriptor.
|
||||
* Field descriptors conforming to JVMS {@jvms 4.3.2} can be described
|
||||
* nominally via {@link Class#describeConstable Class::describeConstable};
|
||||
* otherwise they cannot be described nominally.
|
||||
*
|
||||
* @param <F> the class implementing {@linkplain TypeDescriptor.OfField}
|
||||
* @jvms 4.3.2 Field Descriptors
|
||||
|
@ -86,6 +94,9 @@ public interface TypeDescriptor {
|
|||
|
||||
/**
|
||||
* An entity that has a method type descriptor
|
||||
* Method descriptors conforming to JVMS {@jvms 4.3.3} can be described
|
||||
* nominally via {@link MethodType#describeConstable MethodType::describeConstable};
|
||||
* otherwise they cannot be described nominally.
|
||||
*
|
||||
* @param <F> the type representing field type descriptors
|
||||
* @param <M> the class implementing {@linkplain TypeDescriptor.OfMethod}
|
||||
|
|
|
@ -176,6 +176,12 @@ public class AccessibleObject implements AnnotatedElement {
|
|||
* to the caller and the package containing the declaring class is not open
|
||||
* to the caller's module. </p>
|
||||
*
|
||||
* <p> This method cannot be used to enable {@linkplain Field#set <em>write</em>}
|
||||
* access to a final field declared in a {@linkplain Class#isHidden() hidden class},
|
||||
* since such fields are not modifiable. The {@code accessible} flag when
|
||||
* {@code true} suppresses Java language access control checks to only
|
||||
* enable {@linkplain Field#get <em>read</em>} access to such fields.
|
||||
*
|
||||
* <p> If there is a security manager, its
|
||||
* {@code checkPermission} method is first called with a
|
||||
* {@code ReflectPermission("suppressAccessChecks")} permission.
|
||||
|
|
|
@ -721,10 +721,19 @@ class Field extends AccessibleObject implements Member {
|
|||
* the underlying field is inaccessible, the method throws an
|
||||
* {@code IllegalAccessException}.
|
||||
*
|
||||
* <p>If the underlying field is final, the method throws an
|
||||
* {@code IllegalAccessException} unless {@code setAccessible(true)}
|
||||
* has succeeded for this {@code Field} object
|
||||
* and the field is non-static. Setting a final field in this way
|
||||
* <p>If the underlying field is final, this {@code Field} object has
|
||||
* <em>write</em> access if and only if the following conditions are met:
|
||||
* <ul>
|
||||
* <li>{@link #setAccessible(boolean) setAccessible(true)} has succeeded for
|
||||
* this {@code Field} object;</li>
|
||||
* <li>the field is non-static; and</li>
|
||||
* <li>the field's declaring class is not a {@linkplain Class#isHidden()
|
||||
* hidden class}.</li>
|
||||
* </ul>
|
||||
* If any of the above checks is not met, this method throws an
|
||||
* {@code IllegalAccessException}.
|
||||
*
|
||||
* <p> Setting a final field in this way
|
||||
* is meaningful only during deserialization or reconstruction of
|
||||
* instances of classes with blank final fields, before they are
|
||||
* made available for access by other parts of a program. Use in
|
||||
|
@ -756,7 +765,8 @@ class Field extends AccessibleObject implements Member {
|
|||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
|
@ -791,7 +801,8 @@ class Field extends AccessibleObject implements Member {
|
|||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
|
@ -827,7 +838,8 @@ class Field extends AccessibleObject implements Member {
|
|||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
|
@ -863,7 +875,8 @@ class Field extends AccessibleObject implements Member {
|
|||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
|
@ -899,7 +912,8 @@ class Field extends AccessibleObject implements Member {
|
|||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
|
@ -935,7 +949,8 @@ class Field extends AccessibleObject implements Member {
|
|||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
|
@ -971,7 +986,8 @@ class Field extends AccessibleObject implements Member {
|
|||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
|
@ -1007,7 +1023,8 @@ class Field extends AccessibleObject implements Member {
|
|||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
|
@ -1043,7 +1060,8 @@ class Field extends AccessibleObject implements Member {
|
|||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
package jdk.internal.access;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Method;
|
||||
|
@ -147,6 +150,14 @@ public interface JavaLangAccess {
|
|||
*/
|
||||
Class<?> defineClass(ClassLoader cl, String name, byte[] b, ProtectionDomain pd, String source);
|
||||
|
||||
/**
|
||||
* Defines a class with the given name to a class loader with
|
||||
* the given flags and class data.
|
||||
*
|
||||
* @see java.lang.invoke.MethodHandles.Lookup#defineClass
|
||||
*/
|
||||
Class<?> defineClass(ClassLoader cl, Class<?> lookup, String name, byte[] b, ProtectionDomain pd, boolean initialize, int flags, Object classData);
|
||||
|
||||
/**
|
||||
* Returns a class loaded by the bootstrap class loader.
|
||||
*/
|
||||
|
@ -311,4 +322,21 @@ public interface JavaLangAccess {
|
|||
* @param cause set t's cause to new value
|
||||
*/
|
||||
void setCause(Throwable t, Throwable cause);
|
||||
|
||||
/**
|
||||
* Get protection domain of the given Class
|
||||
*/
|
||||
ProtectionDomain protectionDomain(Class<?> c);
|
||||
|
||||
/**
|
||||
* Get a method handle of string concat helper method
|
||||
*/
|
||||
MethodHandle stringConcatHelper(String name, MethodType methodType);
|
||||
|
||||
/*
|
||||
* Get the class data associated with the given class.
|
||||
* @param c the class
|
||||
* @see java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
|
||||
*/
|
||||
Object classData(Class<?> c);
|
||||
}
|
||||
|
|
|
@ -25,9 +25,10 @@
|
|||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
|
||||
/** Used only for the first few invocations of a Constructor;
|
||||
afterward, switches to bytecode-based implementation */
|
||||
|
||||
|
@ -49,6 +50,7 @@ class NativeConstructorAccessorImpl extends ConstructorAccessorImpl {
|
|||
// because that kind of class can't be referred to by name, hence can't
|
||||
// be found from the generated bytecode.
|
||||
if (++numInvocations > ReflectionFactory.inflationThreshold()
|
||||
&& !c.getDeclaringClass().isHidden()
|
||||
&& !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) {
|
||||
ConstructorAccessorImpl acc = (ConstructorAccessorImpl)
|
||||
new MethodAccessorGenerator().
|
||||
|
|
|
@ -25,9 +25,10 @@
|
|||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
|
||||
/** Used only for the first few invocations of a Method; afterward,
|
||||
switches to bytecode-based implementation */
|
||||
|
||||
|
@ -47,6 +48,7 @@ class NativeMethodAccessorImpl extends MethodAccessorImpl {
|
|||
// that kind of class can't be referred to by name, hence can't be
|
||||
// found from the generated bytecode.
|
||||
if (++numInvocations > ReflectionFactory.inflationThreshold()
|
||||
&& !method.getDeclaringClass().isHidden()
|
||||
&& !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
|
||||
MethodAccessorImpl acc = (MethodAccessorImpl)
|
||||
new MethodAccessorGenerator().
|
||||
|
|
|
@ -51,7 +51,7 @@ public class Reflection {
|
|||
fieldFilterMap = Map.of(
|
||||
Reflection.class, ALL_MEMBERS,
|
||||
AccessibleObject.class, ALL_MEMBERS,
|
||||
Class.class, Set.of("classLoader"),
|
||||
Class.class, Set.of("classLoader", "classData"),
|
||||
ClassLoader.class, ALL_MEMBERS,
|
||||
Constructor.class, ALL_MEMBERS,
|
||||
Field.class, ALL_MEMBERS,
|
||||
|
|
|
@ -200,7 +200,8 @@ public class ReflectionFactory {
|
|||
method = root;
|
||||
}
|
||||
|
||||
if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
|
||||
if (noInflation && !method.getDeclaringClass().isHidden()
|
||||
&& !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
|
||||
return new MethodAccessorGenerator().
|
||||
generateMethod(method.getDeclaringClass(),
|
||||
method.getName(),
|
||||
|
@ -244,7 +245,8 @@ public class ReflectionFactory {
|
|||
return new BootstrapConstructorAccessorImpl(c);
|
||||
}
|
||||
|
||||
if (noInflation && !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) {
|
||||
if (noInflation && !c.getDeclaringClass().isHidden()
|
||||
&& !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) {
|
||||
return new MethodAccessorGenerator().
|
||||
generateConstructor(c.getDeclaringClass(),
|
||||
c.getParameterTypes(),
|
||||
|
|
|
@ -35,7 +35,7 @@ class UnsafeFieldAccessorFactory {
|
|||
boolean isFinal = Modifier.isFinal(field.getModifiers());
|
||||
boolean isVolatile = Modifier.isVolatile(field.getModifiers());
|
||||
boolean isQualified = isFinal || isVolatile;
|
||||
boolean isReadOnly = isFinal && (isStatic || !override);
|
||||
boolean isReadOnly = isFinal && (isStatic || !override || field.getDeclaringClass().isHidden());
|
||||
if (isStatic) {
|
||||
// This code path does not guarantee that the field's
|
||||
// declaring class has been initialized, but it must be
|
||||
|
|
|
@ -154,11 +154,7 @@ public class BytecodeDescriptor {
|
|||
} else if (t == Object.class) {
|
||||
sb.append("Ljava/lang/Object;");
|
||||
} else {
|
||||
boolean lsemi = (!t.isArray());
|
||||
if (lsemi) sb.append('L');
|
||||
sb.append(t.getName().replace('.', '/'));
|
||||
if (lsemi) sb.append(';');
|
||||
sb.append(t.descriptorString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue