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:
Mandy Chung 2020-04-21 06:55:38 -07:00
parent 642041adbc
commit 7cc1371059
198 changed files with 9526 additions and 1575 deletions

View file

@ -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&trade; 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&trade; 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&trade; 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&trade; Virtual Machine Specification</cite>,
* <cite>The Java&trade; 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&trade; 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&trade; 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();
}

View file

@ -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()))

View file

@ -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);
}
}
}

View file

@ -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();
}
});
}
}

View file

@ -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:

View file

@ -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) {

View file

@ -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

View file

@ -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();

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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 {

View file

@ -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() {

View file

@ -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 =

View file

@ -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}

View file

@ -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.

View file

@ -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),

View file

@ -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);
}

View file

@ -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().

View file

@ -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().

View file

@ -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,

View file

@ -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(),

View file

@ -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

View file

@ -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());
}
}
}