8210031: implementation for JVM Constants API

Co-authored-by: Brian Goetz <brian.goetz@oracle.com>
Reviewed-by: jrose, mcimadamore, darcy, mchung, rriggs, dholmes, forax
This commit is contained in:
Vicente Romero 2018-12-09 12:36:24 -05:00
parent b80d335354
commit 9846588b31
72 changed files with 6719 additions and 103 deletions

View file

@ -26,6 +26,8 @@
package java.lang;
import java.lang.annotation.Annotation;
import java.lang.constant.ClassDesc;
import java.lang.invoke.TypeDescriptor;
import java.lang.module.ModuleReader;
import java.lang.ref.SoftReference;
import java.io.IOException;
@ -46,6 +48,7 @@ import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.constant.Constable;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
@ -58,6 +61,7 @@ import java.util.LinkedHashSet;
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;
@ -72,6 +76,7 @@ import jdk.internal.reflect.ConstantPool;
import jdk.internal.reflect.Reflection;
import jdk.internal.reflect.ReflectionFactory;
import jdk.internal.vm.annotation.ForceInline;
import sun.invoke.util.Wrapper;
import sun.reflect.generics.factory.CoreReflectionFactory;
import sun.reflect.generics.factory.GenericsFactory;
import sun.reflect.generics.repository.ClassRepository;
@ -154,7 +159,9 @@ import sun.reflect.misc.ReflectUtil;
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
AnnotatedElement,
TypeDescriptor.OfField<Class<?>>,
Constable {
private static final int ANNOTATION= 0x00002000;
private static final int ENUM = 0x00004000;
private static final int SYNTHETIC = 0x00001000;
@ -4027,4 +4034,68 @@ public final class Class<T> implements java.io.Serializable,
}
return members;
}
/**
* Returns the type descriptor string for this class.
* <p>
* Note that 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
* @jvms 4.3.2 Field Descriptors
* @since 12
*/
@Override
public String descriptorString() {
if (isPrimitive())
return Wrapper.forPrimitiveType(this).basicTypeString();
else if (isArray()) {
return "[" + componentType.descriptorString();
}
else {
return "L" + getName().replace('.', '/') + ";";
}
}
/**
* Returns the component type of this {@code Class}, if it describes
* an array type, or {@code null} otherwise.
*
* @implSpec
* Equivalent to {@link Class#getComponentType()}.
*
* @return a {@code Class} describing the component type, or {@code null}
* if this {@code Class} does not describe an array type
* @since 12
*/
@Override
public Class<?> componentType() {
return isArray() ? componentType : null;
}
/**
* Returns a {@code Class} for an array type whose component type
* is described by this {@linkplain Class}.
*
* @return a {@code Class} describing the array type
* @since 12
*/
@Override
public Class<?> arrayType() {
return Array.newInstance(this, 0).getClass();
}
/**
* 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
*/
@Override
public Optional<ClassDesc> describeConstable() {
return Optional.of(ClassDesc.ofDescriptor(descriptorString()));
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,6 +25,11 @@
package java.lang;
import java.lang.invoke.MethodHandles;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDesc;
import java.util.Optional;
import jdk.internal.math.FloatingDecimal;
import jdk.internal.math.DoubleConsts;
import jdk.internal.HotSpotIntrinsicCandidate;
@ -46,7 +51,8 @@ import jdk.internal.HotSpotIntrinsicCandidate;
* @author Joseph D. Darcy
* @since 1.0
*/
public final class Double extends Number implements Comparable<Double> {
public final class Double extends Number
implements Comparable<Double>, Constable, ConstantDesc {
/**
* A constant holding the positive infinity of type
* {@code double}. It is equal to the value returned by
@ -1070,6 +1076,31 @@ public final class Double extends Number implements Comparable<Double> {
return Math.min(a, b);
}
/**
* Returns a nominal descriptor for this instance, which is the instance
* itself.
*
* @return an {@link Optional} describing the {@linkplain Double} instance
* @since 12
*/
@Override
public Optional<Double> describeConstable() {
return Optional.of(this);
}
/**
* Resolves this instance as a {@link ConstantDesc}, the result of which is
* the instance itself.
*
* @param lookup ignored
* @return the {@linkplain Double} instance
* @since 12
*/
@Override
public Double resolveConstantDesc(MethodHandles.Lookup lookup) {
return this;
}
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -9172774392245257468L;
}

View file

@ -25,11 +25,19 @@
package java.lang;
import java.io.Serializable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.constant.ClassDesc;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc;
import java.lang.invoke.MethodHandles;
import java.util.Optional;
import static java.util.Objects.requireNonNull;
/**
* This is the common base class of all Java language enumeration types.
@ -56,7 +64,7 @@ import java.io.ObjectStreamException;
@SuppressWarnings("serial") // No serialVersionUID needed due to
// special-casing of enum types.
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
implements Constable, Comparable<E>, Serializable {
/**
* The name of this enum constant, as declared in the enum declaration.
* Most programmers should use the {@link #toString} method rather than
@ -203,6 +211,21 @@ public abstract class Enum<E extends Enum<E>>
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}
/**
* Returns an enum descriptor {@code EnumDesc} 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
*/
@Override
public final Optional<EnumDesc<E>> describeConstable() {
return getDeclaringClass()
.describeConstable()
.map(c -> EnumDesc.of(c, name));
}
/**
* Returns the enum constant of the specified enum type with the
* specified name. The name must match exactly an identifier used
@ -258,4 +281,56 @@ public abstract class Enum<E extends Enum<E>>
private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for an
* {@code enum} constant.
*
* @param <E> the type of the enum constant
*
* @since 12
*/
public static final class EnumDesc<E extends Enum<E>>
extends DynamicConstantDesc<E> {
/**
* Constructs a nominal descriptor for the specified {@code enum} class and name.
*
* @param constantType a {@link ClassDesc} describing the {@code enum} class
* @param constantName the unqualified name of the enum constant
* @throws NullPointerException if any argument is null
* @jvms 4.2.2 Unqualified Names
*/
private EnumDesc(ClassDesc constantType, String constantName) {
super(ConstantDescs.BSM_ENUM_CONSTANT, requireNonNull(constantName), requireNonNull(constantType));
}
/**
* Returns a nominal descriptor for the specified {@code enum} class and name
*
* @param <E> the type of the enum constant
* @param enumClass a {@link ClassDesc} describing the {@code enum} class
* @param constantName the unqualified name of the enum constant
* @return the nominal descriptor
* @throws NullPointerException if any argument is null
* @jvms 4.2.2 Unqualified Names
* @since 12
*/
public static<E extends Enum<E>> EnumDesc<E> of(ClassDesc enumClass,
String constantName) {
return new EnumDesc<>(enumClass, constantName);
}
@Override
@SuppressWarnings("unchecked")
public E resolveConstantDesc(MethodHandles.Lookup lookup)
throws ReflectiveOperationException {
return Enum.valueOf((Class<E>) constantType().resolveConstantDesc(lookup), constantName());
}
@Override
public String toString() {
return String.format("EnumDesc[%s.%s]", constantType().displayName(), constantName());
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,6 +25,11 @@
package java.lang;
import java.lang.invoke.MethodHandles;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDesc;
import java.util.Optional;
import jdk.internal.math.FloatingDecimal;
import jdk.internal.HotSpotIntrinsicCandidate;
@ -45,7 +50,8 @@ import jdk.internal.HotSpotIntrinsicCandidate;
* @author Joseph D. Darcy
* @since 1.0
*/
public final class Float extends Number implements Comparable<Float> {
public final class Float extends Number
implements Comparable<Float>, Constable, ConstantDesc {
/**
* A constant holding the positive infinity of type
* {@code float}. It is equal to the value returned by
@ -982,6 +988,31 @@ public final class Float extends Number implements Comparable<Float> {
return Math.min(a, b);
}
/**
* Returns a nominal descriptor for this instance, which is the instance
* itself.
*
* @return an {@link Optional} describing the {@linkplain Float} instance
* @since 12
*/
@Override
public Optional<Float> describeConstable() {
return Optional.of(this);
}
/**
* Resolves this instance as a {@link ConstantDesc}, the result of which is
* the instance itself.
*
* @param lookup ignored
* @return the {@linkplain Float} instance
* @since 12
*/
@Override
public Float resolveConstantDesc(MethodHandles.Lookup lookup) {
return this;
}
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -2671257302660747028L;
}

View file

@ -26,7 +26,12 @@
package java.lang;
import java.lang.annotation.Native;
import java.lang.invoke.MethodHandles;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDesc;
import java.util.Objects;
import java.util.Optional;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.misc.VM;
@ -56,7 +61,8 @@ import static java.lang.String.UTF16;
* @author Joseph D. Darcy
* @since 1.0
*/
public final class Integer extends Number implements Comparable<Integer> {
public final class Integer extends Number
implements Comparable<Integer>, Constable, ConstantDesc {
/**
* A constant holding the minimum value an {@code int} can
* have, -2<sup>31</sup>.
@ -1831,6 +1837,31 @@ public final class Integer extends Number implements Comparable<Integer> {
return Math.min(a, b);
}
/**
* Returns a nominal descriptor for this instance, which is the instance
* itself.
*
* @return an {@link Optional} describing the {@linkplain Integer} instance
* @since 12
*/
@Override
public Optional<Integer> describeConstable() {
return Optional.of(this);
}
/**
* Resolves this instance as a {@link ConstantDesc}, the result of which is
* the instance itself.
*
* @param lookup ignored
* @return the {@linkplain Integer} instance
* @since 12
*/
@Override
public Integer resolveConstantDesc(MethodHandles.Lookup lookup) {
return this;
}
/** use serialVersionUID from JDK 1.0.2 for interoperability */
@Native private static final long serialVersionUID = 1360826667806852920L;
}

View file

@ -26,8 +26,13 @@
package java.lang;
import java.lang.annotation.Native;
import java.lang.invoke.MethodHandles;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDesc;
import java.math.*;
import java.util.Objects;
import java.util.Optional;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.misc.VM;
@ -57,7 +62,8 @@ import static java.lang.String.UTF16;
* @author Joseph D. Darcy
* @since 1.0
*/
public final class Long extends Number implements Comparable<Long> {
public final class Long extends Number
implements Comparable<Long>, Constable, ConstantDesc {
/**
* A constant holding the minimum value a {@code long} can
* have, -2<sup>63</sup>.
@ -1960,6 +1966,31 @@ public final class Long extends Number implements Comparable<Long> {
return Math.min(a, b);
}
/**
* Returns a nominal descriptor for this instance, which is the instance
* itself.
*
* @return an {@link Optional} describing the {@linkplain Long} instance
* @since 12
*/
@Override
public Optional<Long> describeConstable() {
return Optional.of(this);
}
/**
* Resolves this instance as a {@link ConstantDesc}, the result of which is
* the instance itself.
*
* @param lookup ignored
* @return the {@linkplain Long} instance
* @since 12
*/
@Override
public Long resolveConstantDesc(MethodHandles.Lookup lookup) {
return this;
}
/** use serialVersionUID from JDK 1.0.2 for interoperability */
@Native private static final long serialVersionUID = 4290774380558885855L;
}

View file

@ -28,6 +28,9 @@ package java.lang;
import java.io.ObjectStreamField;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Native;
import java.lang.invoke.MethodHandles;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDesc;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
@ -35,6 +38,7 @@ import java.util.Comparator;
import java.util.Formatter;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.Spliterator;
import java.util.StringJoiner;
import java.util.function.Function;
@ -127,7 +131,8 @@ import static java.util.function.Predicate.not;
*/
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
/**
* The value is used for character storage.
@ -3538,4 +3543,30 @@ public final class String
throw new IllegalArgumentException(
format("Not a valid Unicode code point: 0x%X", codePoint));
}
/**
* Returns a nominal descriptor for this instance, which is the instance
* itself.
*
* @return an {@link Optional} describing the {@linkplain String} instance
* @since 12
*/
@Override
public Optional<String> describeConstable() {
return Optional.of(this);
}
/**
* Resolves this instance as a {@link ConstantDesc}, the result of which is
* the instance itself.
*
* @param lookup ignored
* @return the {@linkplain String} instance
* @since 12
*/
@Override
public String resolveConstantDesc(MethodHandles.Lookup lookup) {
return this;
}
}

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import static java.lang.constant.ConstantDescs.BSM_INVOKE;
import static java.lang.constant.ConstantDescs.CD_MethodHandle;
import static java.util.Objects.requireNonNull;
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for a
* {@link MethodHandle} constant that performs a {@link MethodHandle#asType(MethodType)}
* adaptation on another {@link MethodHandle}.
*/
final class AsTypeMethodHandleDesc extends DynamicConstantDesc<MethodHandle>
implements MethodHandleDesc {
private final MethodHandleDesc underlying;
private final MethodTypeDesc type;
AsTypeMethodHandleDesc(MethodHandleDesc underlying, MethodTypeDesc type) {
super(BSM_INVOKE, ConstantDescs.DEFAULT_NAME, CD_MethodHandle,
ConstantDescs.MHD_METHODHANDLE_ASTYPE, underlying, type);
this.underlying = requireNonNull(underlying);
this.type = requireNonNull(type);
}
@Override
public MethodTypeDesc invocationType() {
return type;
}
@Override
public MethodHandle resolveConstantDesc(MethodHandles.Lookup lookup)
throws ReflectiveOperationException {
MethodHandle handle = (MethodHandle) underlying.resolveConstantDesc(lookup);
MethodType methodType = (MethodType) type.resolveConstantDesc(lookup);
return handle.asType(methodType);
}
@Override
public String toString() {
return String.format("%s.asType%s", underlying.toString(), type.displayDescriptor());
}
}

View file

@ -0,0 +1,299 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.lang.invoke.TypeDescriptor;
import java.util.stream.Stream;
import sun.invoke.util.Wrapper;
import static java.lang.constant.ConstantUtils.binaryToInternal;
import static java.lang.constant.ConstantUtils.dropLastChar;
import static java.lang.constant.ConstantUtils.internalToBinary;
import static java.lang.constant.ConstantUtils.validateMemberName;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for a
* {@link Class} constant.
*
* <p>For common system types, including all the primitive types, there are
* predefined {@linkplain ClassDesc} constants in {@link ConstantDescs}.
* (The {@code java.lang.constant} APIs consider {@code void} to be a primitive type.)
* To create a {@linkplain ClassDesc} for a class or interface type, use {@link #of} or
* {@link #ofDescriptor(String)}; to create a {@linkplain ClassDesc} for an array
* type, use {@link #ofDescriptor(String)}, or first obtain a
* {@linkplain ClassDesc} for the component type and then call the {@link #arrayType()}
* or {@link #arrayType(int)} methods.
*
* @apiNote In the future, if the Java language permits, {@linkplain ClassDesc}
* may become a {@code sealed} interface, which would prohibit subclassing except
* by explicitly permitted types. Non-platform classes should not implement
* {@linkplain ClassDesc} directly.
*
* @see ConstantDescs
*
* @since 12
*/
public interface ClassDesc
extends ConstantDesc,
TypeDescriptor.OfField<ClassDesc> {
/**
* Returns a {@linkplain ClassDesc} for a class or interface type,
* given the name of the class or interface, such as {@code "java.lang.String"}.
* (To create a descriptor for an array type, either use {@link #ofDescriptor(String)}
* or {@link #arrayType()}; to create a descriptor for a primitive type, use
* {@link #ofDescriptor(String)} or use the predefined constants in
* {@link ConstantDescs}).
*
* @param name the fully qualified (dot-separated) binary class name
* @return a {@linkplain ClassDesc} describing the desired class
* @throws NullPointerException if any argument is {@code null}
* @throws IllegalArgumentException if the name string is not in the
* correct format
*/
static ClassDesc of(String name) {
ConstantUtils.validateBinaryClassName(requireNonNull(name));
return ClassDesc.ofDescriptor("L" + binaryToInternal(name) + ";");
}
/**
* Returns a {@linkplain ClassDesc} for a class or interface type,
* given a package name and the unqualified (simple) name for the
* class or interface.
*
* @param packageName the package name (dot-separated); if the package
* name is the empty string, the class is considered to
* be in the unnamed package
* @param className the unqualified (simple) class name
* @return a {@linkplain ClassDesc} describing the desired class
* @throws NullPointerException if any argument is {@code null}
* @throws IllegalArgumentException if the package name or class name are
* not in the correct format
*/
static ClassDesc of(String packageName, String className) {
ConstantUtils.validateBinaryClassName(requireNonNull(packageName));
validateMemberName(requireNonNull(className));
return ofDescriptor(String.format("L%s%s%s;",
binaryToInternal(packageName),
(packageName.length() > 0 ? "/" : ""),
className));
}
/**
* Returns a {@linkplain ClassDesc} given a descriptor string for a class,
* interface, array, or primitive type.
*
* @apiNote
*
* A field type descriptor string for a non-array type is either
* a one-letter code corresponding to a primitive type
* ({@code J,I,C,S,B,D,F,Z,V}), or the letter {@code L}, followed
* by the fully qualified binary name of a class, followed by {@code ;}.
* A field type descriptor for an array type is the character {@code [}
* followed by the field descriptor for the component type. Examples of
* valid type descriptor strings include {@code Ljava/lang/String;}, {@code I},
* {@code [I}, {@code V}, {@code [Ljava/lang/String;}, etc.
* for more detail.
*
* @param descriptor a field descriptor string
* @return a {@linkplain ClassDesc} describing the desired class
* @throws NullPointerException if any argument is {@code null}
* @throws IllegalArgumentException if the name string is not in the
* correct format
* @jvms 4.3.2 Field Descriptors
*/
static ClassDesc ofDescriptor(String descriptor) {
requireNonNull(descriptor);
return (descriptor.length() == 1)
? new PrimitiveClassDescImpl(descriptor)
: new ReferenceClassDescImpl(descriptor);
}
/**
* Returns a {@linkplain ClassDesc} for an array type whose component type
* is described by this {@linkplain ClassDesc}.
*
* @return a {@linkplain ClassDesc} describing the array type
*/
default ClassDesc arrayType() {
return arrayType(1);
}
/**
* Returns a {@linkplain ClassDesc} for an array type of the specified rank,
* whose component type is described by this {@linkplain ClassDesc}.
*
* @param rank the rank of the array
* @return a {@linkplain ClassDesc} describing the array type
* @throws IllegalArgumentException if the rank is zero or negative
*/
default ClassDesc arrayType(int rank) {
if (rank <= 0)
throw new IllegalArgumentException("rank: " + rank);
return ClassDesc.ofDescriptor("[".repeat(rank) + descriptorString());
}
/**
* Returns a {@linkplain ClassDesc} for a nested class of the class or
* interface type described by this {@linkplain ClassDesc}.
*
* @param nestedName the unqualified name of the nested class
* @return a {@linkplain ClassDesc} describing the nested class
* @throws NullPointerException if any argument is {@code null}
* @throws IllegalStateException if this {@linkplain ClassDesc} does not
* describe a class or interface type
* @throws IllegalArgumentException if the nested class name is invalid
*/
default ClassDesc nested(String nestedName) {
validateMemberName(nestedName);
if (!isClassOrInterface())
throw new IllegalStateException("Outer class is not a class or interface type");
return ClassDesc.ofDescriptor(String.format("%s$%s;", dropLastChar(descriptorString()), nestedName));
}
/**
* Returns a {@linkplain ClassDesc} for a nested class of the class or
* interface type described by this {@linkplain ClassDesc}.
*
* @param firstNestedName the unqualified name of the first level of nested class
* @param moreNestedNames the unqualified name(s) of the remaining levels of
* nested class
* @return a {@linkplain ClassDesc} describing the nested class
* @throws NullPointerException if any argument is {@code null}
* @throws IllegalStateException if this {@linkplain ClassDesc} does not
* describe a class or interface type
* @throws IllegalArgumentException if the nested class name is invalid
*/
default ClassDesc nested(String firstNestedName, String... moreNestedNames) {
if (!isClassOrInterface())
throw new IllegalStateException("Outer class is not a class or interface type");
return moreNestedNames.length == 0
? nested(firstNestedName)
: nested(firstNestedName + Stream.of(moreNestedNames).collect(joining("$", "$", "")));
}
/**
* Returns whether this {@linkplain ClassDesc} describes an array type.
*
* @return whether this {@linkplain ClassDesc} describes an array type
*/
default boolean isArray() {
return descriptorString().startsWith("[");
}
/**
* Returns whether this {@linkplain ClassDesc} describes a primitive type.
*
* @return whether this {@linkplain ClassDesc} describes a primitive type
*/
default boolean isPrimitive() {
return descriptorString().length() == 1;
}
/**
* Returns whether this {@linkplain ClassDesc} describes a class or interface type.
*
* @return whether this {@linkplain ClassDesc} describes a class or interface type
*/
default boolean isClassOrInterface() {
return descriptorString().startsWith("L");
}
/**
* Returns the component type of this {@linkplain ClassDesc}, if it describes
* an array type, or {@code null} otherwise.
*
* @return a {@linkplain ClassDesc} describing the component type, or {@code null}
* if this descriptor does not describe an array type
*/
default ClassDesc componentType() {
return isArray() ? ClassDesc.ofDescriptor(descriptorString().substring(1)) : null;
}
/**
* Returns the package name of this {@linkplain ClassDesc}, if it describes
* a class or interface type.
*
* @return the package name, or the empty string if the class is in the
* default package, or this {@linkplain ClassDesc} does not describe a class or interface type
*/
default String packageName() {
if (!isClassOrInterface())
return "";
String className = internalToBinary(ConstantUtils.dropFirstAndLastChar(descriptorString()));
int index = className.lastIndexOf('.');
return (index == -1) ? "" : className.substring(0, index);
}
/**
* Returns a human-readable name for the type described by this descriptor.
*
* @implSpec
* <p>The default implementation returns the simple name
* (e.g., {@code int}) for primitive types, the unqualified class name
* for class or interface types, or the display name of the component type
* suffixed with the appropriate number of {@code []} pairs for array types.
*
* @return the human-readable name
*/
default String displayName() {
if (isPrimitive())
return Wrapper.forBasicType(descriptorString().charAt(0)).primitiveSimpleName();
else if (isClassOrInterface()) {
return descriptorString().substring(Math.max(1, descriptorString().lastIndexOf('/') + 1),
descriptorString().length() - 1);
}
else if (isArray()) {
int depth = ConstantUtils.arrayDepth(descriptorString());
ClassDesc c = this;
for (int i=0; i<depth; i++)
c = c.componentType();
return c.displayName() + "[]".repeat(depth);
}
else
throw new IllegalStateException(descriptorString());
}
/**
* Returns a field type descriptor string for this type
*
* @return the descriptor string
* @jvms 4.3.2 Field Descriptors
*/
String descriptorString();
/**
* Compare the specified object with this descriptor for equality. Returns
* {@code true} if and only if the specified object is also a
* {@linkplain ClassDesc} and both describe the same type.
*
* @param o the other object
* @return whether this descriptor is equal to the other object
*/
boolean equals(Object o);
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.util.Optional;
/**
* Represents a type which is <em>constable</em>. A constable type is one whose
* values are constants that can be represented in the constant pool of a Java
* classfile as described in JVMS 4.4, and whose instances can describe themselves
* nominally as a {@link ConstantDesc}.
*
* <p>Some constable types have a native representation in the constant pool:
* {@link String}, {@link Integer}, {@link Long}, {@link Float},
* {@link Double}, {@link Class}, {@link MethodType}, and {@link MethodHandle}.
* The types {@link String}, {@link Integer}, {@link Long}, {@link Float},
* and {@link Double} serve as their own nominal descriptors; {@link Class},
* {@link MethodType}, and {@link MethodHandle} have corresponding nominal
* descriptors {@link ClassDesc}, {@link MethodTypeDesc}, and {@link MethodHandleDesc}.
*
* <p>Other reference types can be constable if their instances can describe
* themselves in nominal form as a {@link ConstantDesc}. Examples in the Java SE
* Platform API are types that support Java language features such as {@link Enum},
* and runtime support classes such as {@link VarHandle}. These are typically
* described with a {@link DynamicConstantDesc}, which describes dynamically
* generated constants (JVMS 4.4.10).
*
* <p>The nominal form of an instance of a constable type is obtained via
* {@link #describeConstable()}. A {@linkplain Constable} need
* not be able to (or may choose not to) describe all its instances in the form of
* a {@link ConstantDesc}; this method returns an {@link Optional} that can be
* empty to indicate that a nominal descriptor could not be created for an instance.
* (For example, {@link MethodHandle} will produce nominal descriptors for direct
* method handles, but not necessarily those produced by method handle
* combinators.)
* @jvms 4.4 The Constant Pool
* @jvms 4.4.10 The CONSTANT_InvokeDynamic_info Structure
*
* @since 12
*/
public interface Constable {
/**
* Returns a nominal descriptor for this instance, if one can be
* constructed, or an empty {@link Optional} if one cannot be constructed.
*
* @return An {@link Optional} containing the resulting nominal descriptor,
* or an empty {@link Optional} if one cannot be constructed.
*/
Optional<? extends ConstantDesc> describeConstable();
}

View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.lang.Enum.EnumDesc;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle.VarHandleDesc;
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for a loadable
* constant value, as defined in JVMS 4.4. Such a descriptor can be resolved via
* {@link ConstantDesc#resolveConstantDesc(MethodHandles.Lookup)} to yield the
* constant value itself.
*
* <p>Class names in a nominal descriptor, like class names in the constant pool
* of a classfile, must be interpreted with respect to a particular class
* loader, which is not part of the nominal descriptor.
*
* <p>Static constants that are expressible natively in the constant pool ({@link String},
* {@link Integer}, {@link Long}, {@link Float}, and {@link Double}) implement
* {@link ConstantDesc}, and serve as nominal descriptors for themselves.
* Native linkable constants ({@link Class}, {@link MethodType}, and
* {@link MethodHandle}) have counterpart {@linkplain ConstantDesc} types:
* {@link ClassDesc}, {@link MethodTypeDesc}, and {@link MethodHandleDesc}.
* Other constants are represented by subtypes of {@link DynamicConstantDesc}.
*
* <p>APIs that perform generation or parsing of bytecode are encouraged to use
* {@linkplain ConstantDesc} to describe the operand of an {@code ldc} instruction
* (including dynamic constants), the static bootstrap arguments of
* dynamic constants and {@code invokedynamic} instructions, and other
* bytecodes or classfile structures that make use of the constant pool.
*
* <p>Constants describing various common constants (such as {@link ClassDesc}
* instances for platform types) can be found in {@link ConstantDescs}.
*
* <p>Implementations of {@linkplain ConstantDesc} must be
* <a href="../doc-files/ValueBased.html">value-based</a> classes.
*
* <p>Non-platform classes should not implement {@linkplain ConstantDesc} directly.
* Instead, they should extend {@link DynamicConstantDesc} (as {@link EnumDesc}
* and {@link VarHandleDesc} do.)
*
* <p>Nominal descriptors should be compared using the
* {@link Object#equals(Object)} method. There is no guarantee that any
* particular entity will always be represented by the same descriptor instance.
*
* @apiNote In the future, if the Java language permits, {@linkplain ConstantDesc}
* may become a {@code sealed} interface, which would prohibit subclassing except by
* explicitly permitted types. Clients can assume that the following
* set of subtypes is exhaustive: {@link String}, {@link Integer},
* {@link Long}, {@link Float}, {@link Double}, {@link ClassDesc},
* {@link MethodTypeDesc}, {@link MethodHandleDesc}, and
* {@link DynamicConstantDesc}; this list may be extended to reflect future
* changes to the constant pool format as defined in JVMS 4.4.
*
* @see Constable
* @see ConstantDescs
*
* @jvms 4.4 The Constant Pool
*
* @since 12
*/
public interface ConstantDesc {
/**
* Resolves this descriptor reflectively, emulating the resolution behavior
* of JVMS 5.4.3 and the access control behavior of JVMS 5.4.4. The resolution
* and access control context is provided by the {@link MethodHandles.Lookup}
* parameter. No caching of the resulting value is performed.
*
* @param lookup The {@link MethodHandles.Lookup} to provide name resolution
* and access control context
* @return the resolved constant value
* @throws ReflectiveOperationException if a class, method, or field
* could not be reflectively resolved in the course of resolution
* @throws LinkageError if a linkage error occurs
* @jvms 5.4.3 Resolution
* @jvms 5.4.4 Access Control
*/
Object resolveConstantDesc(MethodHandles.Lookup lookup) throws ReflectiveOperationException;
}

View file

@ -0,0 +1,298 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.lang.Enum.EnumDesc;
import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantBootstraps;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.lang.invoke.VarHandle.VarHandleDesc;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static java.lang.constant.DirectMethodHandleDesc.*;
import static java.lang.constant.DirectMethodHandleDesc.Kind.STATIC;
/**
* Predefined values of <a href="package-summary.html#nominal">nominal descriptor</a>
* for common constants, including descriptors for primitive class types and
* other common platform types, and descriptors for method handles for standard
* bootstrap methods.
*
* @see ConstantDesc
*
* @since 12
*/
public final class ConstantDescs {
// No instances
private ConstantDescs() { }
/** Invocation name to use when no name is needed, such as the name of a
* constructor, or the invocation name of a dynamic constant or dynamic
* callsite when the bootstrap is known to ignore the invocation name.
*/
public static final String DEFAULT_NAME = "_";
// Don't change the order of these declarations!
/** {@link ClassDesc} representing {@link Object} */
public static final ClassDesc CD_Object = ClassDesc.of("java.lang.Object");
/** {@link ClassDesc} representing {@link String} */
public static final ClassDesc CD_String = ClassDesc.of("java.lang.String");
/** {@link ClassDesc} representing {@link Class} */
public static final ClassDesc CD_Class = ClassDesc.of("java.lang.Class");
/** {@link ClassDesc} representing {@link Number} */
public static final ClassDesc CD_Number = ClassDesc.of("java.lang.Number");
/** {@link ClassDesc} representing {@link Integer} */
public static final ClassDesc CD_Integer = ClassDesc.of("java.lang.Integer");
/** {@link ClassDesc} representing {@link Long} */
public static final ClassDesc CD_Long = ClassDesc.of("java.lang.Long");
/** {@link ClassDesc} representing {@link Float} */
public static final ClassDesc CD_Float = ClassDesc.of("java.lang.Float");
/** {@link ClassDesc} representing {@link Double} */
public static final ClassDesc CD_Double = ClassDesc.of("java.lang.Double");
/** {@link ClassDesc} representing {@link Short} */
public static final ClassDesc CD_Short = ClassDesc.of("java.lang.Short");
/** {@link ClassDesc} representing {@link Byte} */
public static final ClassDesc CD_Byte = ClassDesc.of("java.lang.Byte");
/** {@link ClassDesc} representing {@link Character} */
public static final ClassDesc CD_Character = ClassDesc.of("java.lang.Character");
/** {@link ClassDesc} representing {@link Boolean} */
public static final ClassDesc CD_Boolean = ClassDesc.of("java.lang.Boolean");
/** {@link ClassDesc} representing {@link Void} */
public static final ClassDesc CD_Void = ClassDesc.of("java.lang.Void");
/** {@link ClassDesc} representing {@link Throwable} */
public static final ClassDesc CD_Throwable = ClassDesc.of("java.lang.Throwable");
/** {@link ClassDesc} representing {@link Exception} */
public static final ClassDesc CD_Exception = ClassDesc.of("java.lang.Exception");
/** {@link ClassDesc} representing {@link Enum} */
public static final ClassDesc CD_Enum = ClassDesc.of("java.lang.Enum");
/** {@link ClassDesc} representing {@link VarHandle} */
public static final ClassDesc CD_VarHandle = ClassDesc.of("java.lang.invoke.VarHandle");
/** {@link ClassDesc} representing {@link MethodHandles} */
public static final ClassDesc CD_MethodHandles = ClassDesc.of("java.lang.invoke.MethodHandles");
/** {@link ClassDesc} representing {@link MethodHandles.Lookup} */
public static final ClassDesc CD_MethodHandles_Lookup = CD_MethodHandles.nested("Lookup");
/** {@link ClassDesc} representing {@link MethodHandle} */
public static final ClassDesc CD_MethodHandle = ClassDesc.of("java.lang.invoke.MethodHandle");
/** {@link ClassDesc} representing {@link MethodType} */
public static final ClassDesc CD_MethodType = ClassDesc.of("java.lang.invoke.MethodType");
/** {@link ClassDesc} representing {@link CallSite} */
public static final ClassDesc CD_CallSite = ClassDesc.of("java.lang.invoke.CallSite");
/** {@link ClassDesc} representing {@link Collection} */
public static final ClassDesc CD_Collection = ClassDesc.of("java.util.Collection");
/** {@link ClassDesc} representing {@link List} */
public static final ClassDesc CD_List = ClassDesc.of("java.util.List");
/** {@link ClassDesc} representing {@link Set} */
public static final ClassDesc CD_Set = ClassDesc.of("java.util.Set");
/** {@link ClassDesc} representing {@link Map} */
public static final ClassDesc CD_Map = ClassDesc.of("java.util.Map");
/** {@link ClassDesc} representing {@link ConstantDesc} */
public static final ClassDesc CD_ConstantDesc = ClassDesc.of("java.lang.constant.ConstantDesc");
/** {@link ClassDesc} representing {@link ClassDesc} */
public static final ClassDesc CD_ClassDesc = ClassDesc.of("java.lang.constant.ClassDesc");
/** {@link ClassDesc} representing {@link EnumDesc} */
public static final ClassDesc CD_EnumDesc = CD_Enum.nested("EnumDesc");
/** {@link ClassDesc} representing {@link MethodTypeDesc} */
public static final ClassDesc CD_MethodTypeDesc = ClassDesc.of("java.lang.constant.MethodTypeDesc");
/** {@link ClassDesc} representing {@link MethodHandleDesc} */
public static final ClassDesc CD_MethodHandleDesc = ClassDesc.of("java.lang.constant.MethodHandleDesc");
/** {@link ClassDesc} representing {@link DirectMethodHandleDesc} */
public static final ClassDesc CD_DirectMethodHandleDesc = ClassDesc.of("java.lang.constant.DirectMethodHandleDesc");
/** {@link ClassDesc} representing {@link VarHandleDesc} */
public static final ClassDesc CD_VarHandleDesc = CD_VarHandle.nested("VarHandleDesc");
/** {@link ClassDesc} representing {@link DirectMethodHandleDesc.Kind} */
public static final ClassDesc CD_MethodHandleDesc_Kind = CD_DirectMethodHandleDesc.nested("Kind");
/** {@link ClassDesc} representing {@link DynamicConstantDesc} */
public static final ClassDesc CD_DynamicConstantDesc = ClassDesc.of("java.lang.constant.DynamicConstantDesc");
/** {@link ClassDesc} representing {@link DynamicCallSiteDesc} */
public static final ClassDesc CD_DynamicCallSiteDesc = ClassDesc.of("java.lang.constant.DynamicCallSiteDesc");
/** {@link ClassDesc} representing {@link ConstantBootstraps} */
public static final ClassDesc CD_ConstantBootstraps = ClassDesc.of("java.lang.invoke.ConstantBootstraps");
private static final ClassDesc[] INDY_BOOTSTRAP_ARGS = {
ConstantDescs.CD_MethodHandles_Lookup,
ConstantDescs.CD_String,
ConstantDescs.CD_MethodType};
private static final ClassDesc[] CONDY_BOOTSTRAP_ARGS = {
ConstantDescs.CD_MethodHandles_Lookup,
ConstantDescs.CD_String,
ConstantDescs.CD_Class};
/** {@link MethodHandleDesc} representing {@link ConstantBootstraps#primitiveClass(Lookup, String, Class) ConstantBootstraps.primitiveClass} */
public static final DirectMethodHandleDesc BSM_PRIMITIVE_CLASS
= ofConstantBootstrap(CD_ConstantBootstraps, "primitiveClass",
CD_Class);
/** {@link MethodHandleDesc} representing {@link ConstantBootstraps#enumConstant(Lookup, String, Class) ConstantBootstraps.enumConstant} */
public static final DirectMethodHandleDesc BSM_ENUM_CONSTANT
= ofConstantBootstrap(CD_ConstantBootstraps, "enumConstant",
CD_Enum);
/** {@link MethodHandleDesc} representing {@link ConstantBootstraps#nullConstant(Lookup, String, Class) ConstantBootstraps.nullConstant} */
public static final DirectMethodHandleDesc BSM_NULL_CONSTANT
= ofConstantBootstrap(CD_ConstantBootstraps, "nullConstant",
ConstantDescs.CD_Object);
/** {@link MethodHandleDesc} representing {@link ConstantBootstraps#fieldVarHandle(Lookup, String, Class, Class, Class) ConstantBootstraps.fieldVarHandle} */
public static final DirectMethodHandleDesc BSM_VARHANDLE_FIELD
= ofConstantBootstrap(CD_ConstantBootstraps, "fieldVarHandle",
CD_VarHandle, CD_Class, CD_Class);
/** {@link MethodHandleDesc} representing {@link ConstantBootstraps#staticFieldVarHandle(Lookup, String, Class, Class, Class) ConstantBootstraps.staticVarHandle} */
public static final DirectMethodHandleDesc BSM_VARHANDLE_STATIC_FIELD
= ofConstantBootstrap(CD_ConstantBootstraps, "staticFieldVarHandle",
CD_VarHandle, CD_Class, CD_Class);
/** {@link MethodHandleDesc} representing {@link ConstantBootstraps#arrayVarHandle(Lookup, String, Class, Class) ConstantBootstraps.arrayVarHandle} */
public static final DirectMethodHandleDesc BSM_VARHANDLE_ARRAY
= ofConstantBootstrap(CD_ConstantBootstraps, "arrayVarHandle",
CD_VarHandle, CD_Class);
/** {@link MethodHandleDesc} representing {@link ConstantBootstraps#invoke(Lookup, String, Class, MethodHandle, Object...) ConstantBootstraps.invoke} */
public static final DirectMethodHandleDesc BSM_INVOKE
= ofConstantBootstrap(CD_ConstantBootstraps, "invoke",
CD_Object, CD_MethodHandle, CD_Object.arrayType());
/** {@link ClassDesc} representing the primitive type {@code int} */
public static final ClassDesc CD_int = ClassDesc.ofDescriptor("I");
/** {@link ClassDesc} representing the primitive type {@code long} */
public static final ClassDesc CD_long = ClassDesc.ofDescriptor("J");
/** {@link ClassDesc} representing the primitive type {@code float} */
public static final ClassDesc CD_float = ClassDesc.ofDescriptor("F");
/** {@link ClassDesc} representing the primitive type {@code double} */
public static final ClassDesc CD_double = ClassDesc.ofDescriptor("D");
/** {@link ClassDesc} representing the primitive type {@code short} */
public static final ClassDesc CD_short = ClassDesc.ofDescriptor("S");
/** {@link ClassDesc} representing the primitive type {@code byte} */
public static final ClassDesc CD_byte = ClassDesc.ofDescriptor("B");
/** {@link ClassDesc} representing the primitive type {@code char} */
public static final ClassDesc CD_char = ClassDesc.ofDescriptor("C");
/** {@link ClassDesc} representing the primitive type {@code boolean} */
public static final ClassDesc CD_boolean = ClassDesc.ofDescriptor("Z");
/** {@link ClassDesc} representing the primitive type {@code void} */
public static final ClassDesc CD_void = ClassDesc.ofDescriptor("V");
/** Nominal descriptor representing the constant {@code null} */
public static final ConstantDesc NULL
= DynamicConstantDesc.ofNamed(ConstantDescs.BSM_NULL_CONSTANT,
DEFAULT_NAME, ConstantDescs.CD_Object);
static final DirectMethodHandleDesc MHD_METHODHANDLE_ASTYPE
= MethodHandleDesc.ofMethod(Kind.VIRTUAL, CD_MethodHandle, "asType",
MethodTypeDesc.of(CD_MethodHandle, CD_MethodType));
/**
* Returns a {@link MethodHandleDesc} corresponding to a bootstrap method for
* an {@code invokedynamic} callsite, which is a static method whose leading
* parameter types are {@code Lookup}, {@code String}, and {@code MethodType}.
*
* @param owner the class declaring the method
* @param name the unqualified name of the method
* @param returnType the return type of the method
* @param paramTypes the types of the static bootstrap arguments, if any
* @return the {@link MethodHandleDesc}
* @throws NullPointerException if any of the arguments are null
* @jvms 4.2.2 Unqualified Names
*/
public static DirectMethodHandleDesc ofCallsiteBootstrap(ClassDesc owner,
String name,
ClassDesc returnType,
ClassDesc... paramTypes) {
return MethodHandleDesc.ofMethod(STATIC, owner, name, MethodTypeDesc.of(returnType, paramTypes)
.insertParameterTypes(0, INDY_BOOTSTRAP_ARGS));
}
/**
* Returns a {@link MethodHandleDesc} corresponding to a bootstrap method for a
* dynamic constant, which is a static method whose leading arguments are
* {@code Lookup}, {@code String}, and {@code Class}.
*
* @param owner the class declaring the method
* @param name the unqualified name of the method
* @param returnType the return type of the method
* @param paramTypes the types of the static bootstrap arguments, if any
* @return the {@link MethodHandleDesc}
* @throws NullPointerException if any of the arguments are null
* @jvms 4.2.2 Unqualified Names
*/
public static DirectMethodHandleDesc ofConstantBootstrap(ClassDesc owner,
String name,
ClassDesc returnType,
ClassDesc... paramTypes) {
return MethodHandleDesc.ofMethod(STATIC, owner, name, MethodTypeDesc.of(returnType, paramTypes)
.insertParameterTypes(0, CONDY_BOOTSTRAP_ARGS));
}
}

View file

@ -0,0 +1,175 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import static java.util.Objects.requireNonNull;
/**
* Helper methods for the implementation of {@code java.lang.constant}.
*/
class ConstantUtils {
/** an empty constant descriptor */
public static final ConstantDesc[] EMPTY_CONSTANTDESC = new ConstantDesc[0];
static final Constable[] EMPTY_CONSTABLE = new Constable[0];
private static final Set<String> pointyNames = Set.of("<init>", "<clinit>");
/**
* Validates the correctness of a binary class name. In particular checks for the presence of
* invalid characters in the name.
*
* @param name the class name
* @return the class name passed if valid
* @throws IllegalArgumentException if the class name is invalid
*/
static String validateBinaryClassName(String name) {
for (int i=0; i<name.length(); i++) {
char ch = name.charAt(i);
if (ch == ';' || ch == '[' || ch == '/')
throw new IllegalArgumentException("Invalid class name: " + name);
}
return name;
}
/**
* Validates a member name
*
* @param name the name of the member
* @return the name passed if valid
* @throws IllegalArgumentException if the member name is invalid
*/
public static String validateMemberName(String name) {
requireNonNull(name);
if (name.length() == 0)
throw new IllegalArgumentException("zero-length member name");
for (int i=0; i<name.length(); i++) {
char ch = name.charAt(i);
if (ch == '.' || ch == ';' || ch == '[' || ch == '/')
throw new IllegalArgumentException("Invalid member name: " + name);
if (ch == '<' || ch == '>') {
if (!pointyNames.contains(name))
throw new IllegalArgumentException("Invalid member name: " + name);
}
}
return name;
}
static void validateClassOrInterface(ClassDesc classDesc) {
if (!classDesc.isClassOrInterface())
throw new IllegalArgumentException("not a class or interface type: " + classDesc);
}
static int arrayDepth(String descriptorString) {
int depth = 0;
while (descriptorString.charAt(depth) == '[')
depth++;
return depth;
}
static String binaryToInternal(String name) {
return name.replace('.', '/');
}
static String internalToBinary(String name) {
return name.replace('/', '.');
}
static String dropLastChar(String s) {
return s.substring(0, s.length() - 1);
}
static String dropFirstAndLastChar(String s) {
return s.substring(1, s.length() - 1);
}
/**
* Parses a method descriptor string, and return a list of field descriptor
* strings, return type first, then parameter types
*
* @param descriptor the descriptor string
* @return the list of types
* @throws IllegalArgumentException if the descriptor string is not valid
*/
static List<String> parseMethodDescriptor(String descriptor) {
int cur = 0, end = descriptor.length();
ArrayList<String> ptypes = new ArrayList<>();
if (cur >= end || descriptor.charAt(cur) != '(')
throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
++cur; // skip '('
while (cur < end && descriptor.charAt(cur) != ')') {
int len = matchSig(descriptor, cur, end);
if (len == 0 || descriptor.charAt(cur) == 'V')
throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
ptypes.add(descriptor.substring(cur, cur + len));
cur += len;
}
if (cur >= end)
throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
++cur; // skip ')'
int rLen = matchSig(descriptor, cur, end);
if (rLen == 0 || cur + rLen != end)
throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
ptypes.add(0, descriptor.substring(cur, cur + rLen));
return ptypes;
}
/**
* Validates that the characters at [start, end) within the provided string
* describe a valid field type descriptor.
*
* @param str the descriptor string
* @param start the starting index into the string
* @param end the ending index within the string
* @return the length of the descriptor, or 0 if it is not a descriptor
* @throws IllegalArgumentException if the descriptor string is not valid
*/
static int matchSig(String str, int start, int end) {
if (start >= end || start >= str.length() || end > str.length())
return 0;
char c = str.charAt(start);
if (c == 'L') {
int endc = str.indexOf(';', start);
int badc = str.indexOf('.', start);
if (badc >= 0 && badc < endc)
return 0;
badc = str.indexOf('[', start);
if (badc >= 0 && badc < endc)
return 0;
return (endc < 0) ? 0 : endc - start + 1;
} else if (c == '[') {
int t = matchSig(str, start+1, end);
return (t > 0) ? t + 1 : 0;
} else {
return ("IJCSBFDZV".indexOf(c) >= 0) ? 1 : 0;
}
}
}

View file

@ -0,0 +1,261 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandleInfo;
import java.util.OptionalInt;
import java.util.stream.Stream;
import jdk.internal.vm.annotation.Stable;
import static java.lang.invoke.MethodHandleInfo.REF_getField;
import static java.lang.invoke.MethodHandleInfo.REF_getStatic;
import static java.lang.invoke.MethodHandleInfo.REF_invokeInterface;
import static java.lang.invoke.MethodHandleInfo.REF_invokeSpecial;
import static java.lang.invoke.MethodHandleInfo.REF_invokeStatic;
import static java.lang.invoke.MethodHandleInfo.REF_invokeVirtual;
import static java.lang.invoke.MethodHandleInfo.REF_newInvokeSpecial;
import static java.lang.invoke.MethodHandleInfo.REF_putField;
import static java.lang.invoke.MethodHandleInfo.REF_putStatic;
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for a direct
* {@link MethodHandle}. A {@linkplain DirectMethodHandleDesc} corresponds to
* a {@code Constant_MethodHandle_info} entry in the constant pool of a classfile.
*
* @apiNote In the future, if the Java language permits, {@linkplain DirectMethodHandleDesc}
* may become a {@code sealed} interface, which would prohibit subclassing except
* by explicitly permitted types. Non-platform classes should not implement
* {@linkplain DirectMethodHandleDesc} directly.
*
* @since 12
*/
public interface DirectMethodHandleDesc extends MethodHandleDesc {
/**
* Kinds of method handles that can be described with {@linkplain DirectMethodHandleDesc}.
*
* @since 12
*/
enum Kind {
/** A method handle for a method invoked as with {@code invokestatic} */
STATIC(REF_invokeStatic),
/** A method handle for a method invoked as with {@code invokestatic} */
INTERFACE_STATIC(REF_invokeStatic, true),
/** A method handle for a method invoked as with {@code invokevirtual} */
VIRTUAL(REF_invokeVirtual),
/** A method handle for a method invoked as with {@code invokeinterface} */
INTERFACE_VIRTUAL(REF_invokeInterface, true),
/** A method handle for a method invoked as with {@code invokespecial} */
SPECIAL(REF_invokeSpecial),
/** A method handle for an interface method invoked as with {@code invokespecial} */
INTERFACE_SPECIAL(REF_invokeSpecial, true),
/** A method handle for a constructor */
CONSTRUCTOR(REF_newInvokeSpecial),
/** A method handle for a read accessor for an instance field */
GETTER(REF_getField),
/** A method handle for a write accessor for an instance field */
SETTER(REF_putField),
/** A method handle for a read accessor for a static field */
STATIC_GETTER(REF_getStatic),
/** A method handle for a write accessor for a static field */
STATIC_SETTER(REF_putStatic);
/** The corresponding {@code refKind} value for this kind of method handle,
* as defined by {@link MethodHandleInfo}
*/
public final int refKind;
/** Is this an interface
*/
public final boolean isInterface;
Kind(int refKind) {
this(refKind, false);
}
Kind(int refKind, boolean isInterface) { this.refKind = refKind; this.isInterface = isInterface; }
/**
* Returns the enumeration member with the given {@code refKind} field.
* Behaves as if {@code valueOf(refKind, false)}. As a special case,
* if {@code refKind} is {@code REF_invokeInterface} (9) then the
* {@code isInterface} field will be true.
*
* @param refKind refKind of desired member
* @return the matching enumeration member
* @throws IllegalArgumentException if there is no such member
*/
public static Kind valueOf(int refKind) {
return valueOf(refKind, false);
}
/**
* Returns the enumeration member with the given the {@code refKind} and
* {@code isInterface} arguments.
* For most values of {@code refKind} there is an exact match regardless of the value of {@code isInterface}.
* These are:
* <UL>
* <LI>{@code REF_invokeVirtual} which matches to {@code VIRTUAL}
* <LI>{@code REF_invokeInterface} which matches to {@code INTERFACE_VIRTUAL}
* <LI>{@code REF_newInvokeSpecial} which matches to {@code CONSTRUCTOR}
* <LI>{@code REF_getField} which matches to {@code GETTER}
* <LI>{@code REF_putField} which matches to {@code SETTER}
* <LI>{@code REF_getStatic} which matches to {@code STATIC_GETTER}
* <LI>{@code REF_putStatic} which matches to {@code STATIC_SETTER}
* </UL>
* As for the rest, the returned kind will depend on the value (false or true accordingly) of {@code isInterface}:
* <UL>
* <LI>{@code REF_invokeStatic} which matches to {@code STATIC} or {@code INTERFACE_STATIC}
* <LI>{@code REF_invokeSpecial} which matches to {@code SPECIAL} or {@code INTERFACE_SPECIAL}
* </UL>
* @param refKind refKind of desired member
* @param isInterface whether desired member is for interface methods
* @return the matching enumeration member
* @throws IllegalArgumentException if there is no such member
*/
public static Kind valueOf(int refKind, boolean isInterface) {
int i = tableIndex(refKind, isInterface);
if (i >= 0 && i < TABLE.length) {
Kind kind = TABLE[i];
if (kind == null) {
throw new IllegalArgumentException(String.format("refKind=%d", refKind));
}
if (kind.refKind == refKind && kind.isInterface == isInterface) {
return kind;
}
}
throw new IllegalArgumentException(String.format("refKind=%d", refKind));
}
private static int tableIndex(int refKind, boolean isInterface) {
if (refKind < 0) return refKind;
return (refKind * 2) + (isInterface ? 1 : 0);
}
private static final @Stable Kind[] TABLE;
static {
// Pack the static table.
int max = 0;
for (Kind k : values())
max = Math.max(max, tableIndex(k.refKind, true));
TABLE = new Kind[max+1];
for (Kind kind : values()) {
int i = tableIndex(kind.refKind, kind.isInterface);
if (i >= TABLE.length || TABLE[i] != null)
throw new AssertionError("TABLE entry for " + kind);
TABLE[i] = kind;
}
// Pack in some aliases also.
int ii = tableIndex(REF_invokeInterface, false);
if (TABLE[ii] != null)
throw new AssertionError("TABLE entry for (invokeInterface, false) used by " + TABLE[ii]);
TABLE[ii] = INTERFACE_VIRTUAL;
for (Kind kind : values()) {
if (!kind.isInterface) {
// Add extra cache entry to alias the isInterface case.
// For example, (REF_getStatic, X) will produce STATIC_GETTER
// for either truth value of X.
int i = tableIndex(kind.refKind, true);
if (TABLE[i] == null) {
// There is not a specific Kind for interfaces
if (kind == VIRTUAL) kind = INTERFACE_VIRTUAL;
if (TABLE[i] == null) TABLE[i] = kind;
}
}
}
}
/**
* Does this {@code Kind} correspond to a virtual method invocation?
*
* @return if this {@code Kind} corresponds to a virtual method invocation
*/
boolean isVirtualMethod() {
switch (this) {
case VIRTUAL:
case SPECIAL:
case INTERFACE_VIRTUAL:
case INTERFACE_SPECIAL:
return true;
default:
return false;
}
}
}
/**
* Returns the {@code kind} of the method handle described by this nominal
* descriptor.
*
* @return the {@link Kind}
*/
Kind kind();
/**
* Returns the {@code refKind} of the method handle described by this nominal
* reference, as defined by {@link MethodHandleInfo}.
*
* @return the reference kind
*/
int refKind();
/**
* Indicates if the method is declared by an interface
*
* @return true if the method is declared by an interface
*/
boolean isOwnerInterface();
/**
* Returns a {@link ClassDesc} describing the class declaring the
* method or field described by this nominal descriptor.
*
* @return the class declaring the method or field
*/
ClassDesc owner();
/**
* Returns the name of the method or field described by this nominal descriptor.
* For constructors, returns the reserved name {@code "<init>"}.
*
* @return the name of the method or field
*/
String methodName();
/**
* Returns the lookup descriptor of the method handle described by this descriptor,
* after adjusting for the invocation mode. This will correspond to either
* a method type descriptor string (for methods and constructors), or a field
* descriptor string (for field access method handles). The lookup descriptor
* string is in the same format as accepted by {@link MethodHandleDesc#of(Kind, ClassDesc, String, String)}.
*
* @return the lookup descriptor string
*/
String lookupDescriptor();
}

View file

@ -0,0 +1,220 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Objects;
import static java.lang.constant.ConstantDescs.CD_void;
import static java.lang.constant.ConstantUtils.validateClassOrInterface;
import static java.lang.constant.ConstantUtils.validateMemberName;
import static java.lang.constant.DirectMethodHandleDesc.Kind.CONSTRUCTOR;
import static java.util.Objects.requireNonNull;
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for a direct
* {@link MethodHandle}. A {@linkplain DirectMethodHandleDescImpl} corresponds to
* a {@code Constant_MethodHandle_info} entry in the constant pool of a classfile.
*/
final class DirectMethodHandleDescImpl implements DirectMethodHandleDesc {
private final Kind kind;
private final ClassDesc owner;
private final String name;
private final MethodTypeDesc invocationType;
/**
* Constructs a {@linkplain DirectMethodHandleDescImpl} for a method or field
* from a kind, owner, name, and type
*
* @param kind the kind of the method handle
* @param owner the declaring class or interface for the method
* @param name the unqualified name of the method (ignored if {@code kind} is {@code CONSTRUCTOR})
* @param type the lookup type of the method
* @throws NullPointerException if any non-ignored argument is null
* @throws IllegalArgumentException if {@code kind} describes a field accessor,
* and {@code type} is not consistent with that kind of field accessor, or if
* {@code kind} describes a constructor, and the return type of {@code type}
* is not {@code void}
* @jvms 4.2.2 Unqualified Names
*/
DirectMethodHandleDescImpl(Kind kind, ClassDesc owner, String name, MethodTypeDesc type) {
if (kind == CONSTRUCTOR)
name = "<init>";
requireNonNull(kind);
validateClassOrInterface(requireNonNull(owner));
validateMemberName(requireNonNull(name));
requireNonNull(type);
switch (kind) {
case CONSTRUCTOR: validateConstructor(type); break;
case GETTER: validateFieldType(type, false, true); break;
case SETTER: validateFieldType(type, true, true); break;
case STATIC_GETTER: validateFieldType(type, false, false); break;
case STATIC_SETTER: validateFieldType(type, true, false); break;
}
this.kind = kind;
this.owner = owner;
this.name = name;
if (kind.isVirtualMethod())
this.invocationType = type.insertParameterTypes(0, owner);
else if (kind == CONSTRUCTOR)
this.invocationType = type.changeReturnType(owner);
else
this.invocationType = type;
}
private static void validateFieldType(MethodTypeDesc type, boolean isSetter, boolean isVirtual) {
boolean isVoid = type.returnType().descriptorString().equals("V");
int expectedParams = (isSetter ? 1 : 0) + (isVirtual ? 1 : 0);
if (isVoid != isSetter
|| type.parameterCount() != expectedParams
|| (isVirtual && type.parameterType(0).isPrimitive())) {
String expectedType = String.format("(%s%s)%s", (isVirtual ? "R" : ""),
(isSetter ? "T" : ""), (isSetter ? "V" : "T"));
throw new IllegalArgumentException(String.format("Expected type of %s for getter, found %s", expectedType, type));
}
}
private static void validateConstructor(MethodTypeDesc type) {
if (!type.returnType().descriptorString().equals("V")) {
throw new IllegalArgumentException(String.format("Expected type of (T*)V for constructor, found %s", type));
}
}
@Override
public Kind kind() { return kind; }
@Override
public int refKind() { return kind.refKind; }
@Override
public boolean isOwnerInterface() { return kind.isInterface; }
@Override
public ClassDesc owner() {
return owner;
}
@Override
public String methodName() {
return name;
}
@Override
public MethodTypeDesc invocationType() {
return invocationType;
}
@Override
public String lookupDescriptor() {
switch (kind) {
case VIRTUAL:
case SPECIAL:
case INTERFACE_VIRTUAL:
case INTERFACE_SPECIAL:
return invocationType.dropParameterTypes(0, 1).descriptorString();
case STATIC:
case INTERFACE_STATIC:
return invocationType.descriptorString();
case CONSTRUCTOR:
return invocationType.changeReturnType(CD_void).descriptorString();
case GETTER:
case STATIC_GETTER:
return invocationType.returnType().descriptorString();
case SETTER:
return invocationType.parameterType(1).descriptorString();
case STATIC_SETTER:
return invocationType.parameterType(0).descriptorString();
default:
throw new IllegalStateException(kind.toString());
}
}
public MethodHandle resolveConstantDesc(MethodHandles.Lookup lookup)
throws ReflectiveOperationException {
Class<?> resolvedOwner = (Class<?>) owner.resolveConstantDesc(lookup);
MethodType invocationType = (MethodType) this.invocationType().resolveConstantDesc(lookup);
switch (kind) {
case STATIC:
case INTERFACE_STATIC:
return lookup.findStatic(resolvedOwner, name, invocationType);
case INTERFACE_VIRTUAL:
case VIRTUAL:
return lookup.findVirtual(resolvedOwner, name, invocationType.dropParameterTypes(0, 1));
case SPECIAL:
case INTERFACE_SPECIAL:
return lookup.findSpecial(resolvedOwner, name, invocationType.dropParameterTypes(0, 1),
lookup.lookupClass());
case CONSTRUCTOR:
return lookup.findConstructor(resolvedOwner, invocationType.changeReturnType(void.class));
case GETTER:
return lookup.findGetter(resolvedOwner, name, invocationType.returnType());
case STATIC_GETTER:
return lookup.findStaticGetter(resolvedOwner, name, invocationType.returnType());
case SETTER:
return lookup.findSetter(resolvedOwner, name, invocationType.parameterType(1));
case STATIC_SETTER:
return lookup.findStaticSetter(resolvedOwner, name, invocationType.parameterType(0));
default:
throw new IllegalStateException(kind.name());
}
}
/**
* Returns {@code true} if this {@linkplain DirectMethodHandleDescImpl} is
* equal to another {@linkplain DirectMethodHandleDescImpl}. Equality is
* determined by the two descriptors having equal kind, owner, name, and type
* descriptor.
* @param o a {@code DirectMethodHandleDescImpl} to compare to this
* {@code DirectMethodHandleDescImpl}
* @return {@code true} if the specified {@code DirectMethodHandleDescImpl} is
* equals to this {@code DirectMethodHandleDescImpl}.
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DirectMethodHandleDescImpl desc = (DirectMethodHandleDescImpl) o;
return kind == desc.kind &&
Objects.equals(owner, desc.owner) &&
Objects.equals(name, desc.name) &&
Objects.equals(invocationType, desc.invocationType);
}
@Override
public int hashCode() {
return Objects.hash(kind, owner, name, invocationType);
}
@Override
public String toString() {
return String.format("MethodHandleDesc[%s/%s::%s%s]", kind, owner.displayName(), name, invocationType.displayDescriptor());
}
}

View file

@ -0,0 +1,289 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Stream;
import static java.lang.constant.ConstantDescs.CD_String;
import static java.lang.constant.ConstantUtils.EMPTY_CONSTANTDESC;
import static java.lang.constant.ConstantUtils.validateMemberName;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for an
* {@code invokedynamic} call site.
*
* <p>Concrete subtypes of {@linkplain DynamicCallSiteDesc} must be
* <a href="../doc-files/ValueBased.html">value-based</a>.
*
* @since 12
*/
public class DynamicCallSiteDesc {
private final DirectMethodHandleDesc bootstrapMethod;
private final ConstantDesc[] bootstrapArgs;
private final String invocationName;
private final MethodTypeDesc invocationType;
/**
* Creates a nominal descriptor for an {@code invokedynamic} call site.
*
* @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
* bootstrap method for the {@code invokedynamic}
* @param invocationName The unqualified name that would appear in the {@code NameAndType}
* operand of the {@code invokedynamic}
* @param invocationType a {@link MethodTypeDesc} describing the invocation
* type that would appear in the {@code NameAndType}
* operand of the {@code invokedynamic}
* @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
* to the bootstrap, that would appear in the
* {@code BootstrapMethods} attribute
* @throws NullPointerException if any parameter is null
* @throws IllegalArgumentException if the invocation name has the incorrect
* format
* @jvms 4.2.2 Unqualified Names
*/
private DynamicCallSiteDesc(DirectMethodHandleDesc bootstrapMethod,
String invocationName,
MethodTypeDesc invocationType,
ConstantDesc[] bootstrapArgs) {
this.invocationName = validateMemberName(requireNonNull(invocationName));
this.invocationType = requireNonNull(invocationType);
this.bootstrapMethod = requireNonNull(bootstrapMethod);
this.bootstrapArgs = requireNonNull(bootstrapArgs.clone());
if (invocationName.length() == 0)
throw new IllegalArgumentException("Illegal invocation name: " + invocationName);
}
/**
* Creates a nominal descriptor for an {@code invokedynamic} call site.
*
* @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
* bootstrap method for the {@code invokedynamic}
* @param invocationName The unqualified name that would appear in the {@code NameAndType}
* operand of the {@code invokedynamic}
* @param invocationType a {@link MethodTypeDesc} describing the invocation
* type that would appear in the {@code NameAndType}
* operand of the {@code invokedynamic}
* @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
* to the bootstrap, that would appear in the
* {@code BootstrapMethods} attribute
* @return the nominal descriptor
* @throws NullPointerException if any parameter is null
* @throws IllegalArgumentException if the invocation name has the incorrect
* format
* @jvms 4.2.2 Unqualified Names
*/
public static DynamicCallSiteDesc of(DirectMethodHandleDesc bootstrapMethod,
String invocationName,
MethodTypeDesc invocationType,
ConstantDesc... bootstrapArgs) {
return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, bootstrapArgs);
}
/**
* Creates a nominal descriptor for an {@code invokedynamic} call site whose
* bootstrap method has no static arguments.
*
* @param bootstrapMethod The bootstrap method for the {@code invokedynamic}
* @param invocationName The invocationName that would appear in the
* {@code NameAndType} operand of the {@code invokedynamic}
* @param invocationType The invocation invocationType that would appear
* in the {@code NameAndType} operand of the {@code invokedynamic}
* @return the nominal descriptor
* @throws NullPointerException if any parameter is null
* @throws IllegalArgumentException if the invocation name has the incorrect
* format
*/
public static DynamicCallSiteDesc of(DirectMethodHandleDesc bootstrapMethod,
String invocationName,
MethodTypeDesc invocationType) {
return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, EMPTY_CONSTANTDESC);
}
/**
* Creates a nominal descriptor for an {@code invokedynamic} call site whose
* bootstrap method has no static arguments and for which the name parameter
* is {@link ConstantDescs#DEFAULT_NAME}.
*
* @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
* bootstrap method for the {@code invokedynamic}
* @param invocationType a {@link MethodTypeDesc} describing the invocation
* type that would appear in the {@code NameAndType}
* operand of the {@code invokedynamic}
* @return the nominal descriptor
* @throws NullPointerException if any parameter is null
*/
public static DynamicCallSiteDesc of(DirectMethodHandleDesc bootstrapMethod,
MethodTypeDesc invocationType) {
return of(bootstrapMethod, ConstantDescs.DEFAULT_NAME, invocationType);
}
/**
* Returns a nominal descriptor for an {@code invokedynamic} call site whose
* bootstrap method, name, and invocation type are the same as this one, but
* with the specified bootstrap arguments.
*
* @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
* to the bootstrap, that would appear in the
* {@code BootstrapMethods} attribute
* @return the nominal descriptor
* @throws NullPointerException if any parameter is null
*/
public DynamicCallSiteDesc withArgs(ConstantDesc... bootstrapArgs) {
return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, bootstrapArgs);
}
/**
* Returns a nominal descriptor for an {@code invokedynamic} call site whose
* bootstrap and bootstrap arguments are the same as this one, but with the
* specified invocationName and invocation invocationType
*
* @param invocationName The unqualified name that would appear in the {@code NameAndType}
* operand of the {@code invokedynamic}
* @param invocationType a {@link MethodTypeDesc} describing the invocation
* type that would appear in the {@code NameAndType}
* operand of the {@code invokedynamic}
* @return the nominal descriptor
* @throws NullPointerException if any parameter is null
* @throws IllegalArgumentException if the invocation name has the incorrect
* format
* @jvms 4.2.2 Unqualified Names
*/
public DynamicCallSiteDesc withNameAndType(String invocationName,
MethodTypeDesc invocationType) {
return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, bootstrapArgs);
}
/**
* Returns the invocation name that would appear in the {@code NameAndType}
* operand of the {@code invokedynamic}.
*
* @return the invocation name
*/
public String invocationName() {
return invocationName;
}
/**
* Returns a {@link MethodTypeDesc} describing the invocation type that
* would appear in the {@code NameAndType} operand of the {@code invokedynamic}.
*
* @return the invocation type
*/
public MethodTypeDesc invocationType() {
return invocationType;
}
/**
* Returns a {@link MethodHandleDesc} describing the bootstrap method for
* the {@code invokedynamic}.
*
* @return the bootstrap method for the {@code invokedynamic}
*/
public MethodHandleDesc bootstrapMethod() { return bootstrapMethod; }
/**
* Returns {@link ConstantDesc}s describing the bootstrap arguments for the
* {@code invokedynamic}. The returned array is always non-null. A zero
* length array is returned if this {@linkplain DynamicCallSiteDesc} has no
* bootstrap arguments.
*
* @return the bootstrap arguments for the {@code invokedynamic}
*/
public ConstantDesc[] bootstrapArgs() { return bootstrapArgs.clone(); }
/**
* Reflectively invokes the bootstrap method with the specified arguments,
* and return the resulting {@link CallSite}
*
* @param lookup The {@link MethodHandles.Lookup} used to resolve class names
* @return the {@link CallSite}
* @throws Throwable if any exception is thrown by the bootstrap method
*/
public CallSite resolveCallSiteDesc(MethodHandles.Lookup lookup) throws Throwable {
assert bootstrapMethod.invocationType().parameterType(1).equals(CD_String);
MethodHandle bsm = (MethodHandle) bootstrapMethod.resolveConstantDesc(lookup);
Object[] args = new Object[bootstrapArgs.length + 3];
args[0] = lookup;
args[1] = invocationName;
args[2] = invocationType.resolveConstantDesc(lookup);
System.arraycopy(bootstrapArgs, 0, args, 3, bootstrapArgs.length);
return (CallSite) bsm.invokeWithArguments(args);
}
/**
* Compares the specified object with this descriptor for equality. Returns
* {@code true} if and only if the specified object is also a
* {@linkplain DynamicCallSiteDesc}, and both descriptors have equal
* bootstrap methods, bootstrap argument lists, invocation name, and
* invocation type.
*
* @param o the {@code DynamicCallSiteDesc} to compare to this
* {@code DynamicCallSiteDesc}
* @return {@code true} if the specified {@code DynamicCallSiteDesc} is
* equals to this {@code DynamicCallSiteDesc}.
*/
@Override
public final boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DynamicCallSiteDesc specifier = (DynamicCallSiteDesc) o;
return Objects.equals(bootstrapMethod, specifier.bootstrapMethod) &&
Arrays.equals(bootstrapArgs, specifier.bootstrapArgs) &&
Objects.equals(invocationName, specifier.invocationName) &&
Objects.equals(invocationType, specifier.invocationType);
}
@Override
public final int hashCode() {
int result = Objects.hash(bootstrapMethod, invocationName, invocationType);
result = 31 * result + Arrays.hashCode(bootstrapArgs);
return result;
}
/**
* Returns a compact textual description of this call site description,
* including the bootstrap method, the invocation name and type, and
* the static bootstrap arguments.
*
* @return A compact textual description of this call site descriptor
*/
@Override
public String toString() {
return String.format("DynamicCallSiteDesc[%s::%s(%s%s):%s]",
bootstrapMethod.owner().displayName(),
bootstrapMethod.methodName(),
invocationName.equals(ConstantDescs.DEFAULT_NAME) ? "" : invocationName + "/",
Stream.of(bootstrapArgs).map(Object::toString).collect(joining(",")),
invocationType.displayDescriptor());
}
}

View file

@ -0,0 +1,397 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.lang.Enum.EnumDesc;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.invoke.VarHandle.VarHandleDesc;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;
import static java.lang.constant.ConstantDescs.CD_Class;
import static java.lang.constant.ConstantDescs.CD_VarHandle;
import static java.lang.constant.ConstantDescs.DEFAULT_NAME;
import static java.lang.constant.ConstantUtils.EMPTY_CONSTANTDESC;
import static java.lang.constant.ConstantUtils.validateMemberName;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for a
* dynamic constant (one described in the constant pool with
* {@code Constant_Dynamic_info}.)
*
* <p>Concrete subtypes of {@linkplain DynamicConstantDesc} must be
* <a href="../doc-files/ValueBased.html">value-based</a>.
*
* @param <T> the type of the dynamic constant
*
* @since 12
*/
public abstract class DynamicConstantDesc<T>
implements ConstantDesc {
private final DirectMethodHandleDesc bootstrapMethod;
private final ConstantDesc[] bootstrapArgs;
private final String constantName;
private final ClassDesc constantType;
private static final Map<MethodHandleDesc, Function<DynamicConstantDesc<?>, ConstantDesc>> canonicalMap
= Map.ofEntries(Map.entry(ConstantDescs.BSM_PRIMITIVE_CLASS, DynamicConstantDesc::canonicalizePrimitiveClass),
Map.entry(ConstantDescs.BSM_ENUM_CONSTANT, DynamicConstantDesc::canonicalizeEnum),
Map.entry(ConstantDescs.BSM_NULL_CONSTANT, DynamicConstantDesc::canonicalizeNull),
Map.entry(ConstantDescs.BSM_VARHANDLE_STATIC_FIELD, DynamicConstantDesc::canonicalizeStaticFieldVarHandle),
Map.entry(ConstantDescs.BSM_VARHANDLE_FIELD, DynamicConstantDesc::canonicalizeFieldVarHandle),
Map.entry(ConstantDescs.BSM_VARHANDLE_ARRAY, DynamicConstantDesc::canonicalizeArrayVarHandle)
);
/**
* Creates a nominal descriptor for a dynamic constant.
*
* @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
* bootstrap method for the constant
* @param constantName The unqualified name that would appear in the {@code NameAndType}
* operand of the {@code LDC} for this constant
* @param constantType a {@link ClassDesc} describing the type
* that would appear in the {@code NameAndType} operand
* of the {@code LDC} for this constant
* @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
* to the bootstrap, that would appear in the
* {@code BootstrapMethods} attribute
* @throws NullPointerException if any argument is null
* @throws IllegalArgumentException if the {@code name} has the incorrect
* format
* @jvms 4.2.2 Unqualified Names
*/
protected DynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod,
String constantName,
ClassDesc constantType,
ConstantDesc... bootstrapArgs) {
this.bootstrapMethod = requireNonNull(bootstrapMethod);
this.constantName = validateMemberName(requireNonNull(constantName));
this.constantType = requireNonNull(constantType);
this.bootstrapArgs = requireNonNull(bootstrapArgs).clone();
if (constantName.length() == 0)
throw new IllegalArgumentException("Illegal invocation name: " + constantName);
}
/**
* Returns a nominal descriptor for a dynamic constant, transforming it into
* a more specific type if the constant bootstrap is a well-known one and a
* more specific nominal descriptor type (e.g., ClassDesc) is available.
*
* <p>Classes whose {@link Constable#describeConstable()} method produce
* a {@linkplain DynamicConstantDesc} with a well-known bootstrap including
* {@link Class} (for instances describing primitive types), {@link Enum},
* and {@link VarHandle}.
*
* <p>Bytecode-reading APIs that process the constant pool and wish to expose
* entries as {@link ConstantDesc} to their callers should generally use this
* method in preference to {@link #ofNamed(DirectMethodHandleDesc, String, ClassDesc, ConstantDesc...)}
* because this may result in a more specific type that can be provided to
* callers.
*
* @param <T> the type of the dynamic constant
* @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
* bootstrap method for the constant
* @param constantName The unqualified name that would appear in the {@code NameAndType}
* operand of the {@code LDC} for this constant
* @param constantType a {@link ClassDesc} describing the type
* that would appear in the {@code NameAndType} operand
* of the {@code LDC} for this constant
* @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
* to the bootstrap, that would appear in the
* {@code BootstrapMethods} attribute
* @return the nominal descriptor
* @throws NullPointerException if any argument is null
* @throws IllegalArgumentException if the {@code name} has the incorrect
* format
* @jvms 4.2.2 Unqualified Names
*/
public static<T> ConstantDesc ofCanonical(DirectMethodHandleDesc bootstrapMethod,
String constantName,
ClassDesc constantType,
ConstantDesc[] bootstrapArgs) {
return DynamicConstantDesc.<T>ofNamed(bootstrapMethod, constantName, constantType, bootstrapArgs)
.tryCanonicalize();
}
/**
* Returns a nominal descriptor for a dynamic constant.
*
* @param <T> the type of the dynamic constant
* @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
* bootstrap method for the constant
* @param constantName The unqualified name that would appear in the {@code NameAndType}
* operand of the {@code LDC} for this constant
* @param constantType a {@link ClassDesc} describing the type
* that would appear in the {@code NameAndType} operand
* of the {@code LDC} for this constant
* @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
* to the bootstrap, that would appear in the
* {@code BootstrapMethods} attribute
* @return the nominal descriptor
* @throws NullPointerException if any argument is null
* @throws IllegalArgumentException if the {@code name} has the incorrect
* format
* @jvms 4.2.2 Unqualified Names
*/
public static<T> DynamicConstantDesc<T> ofNamed(DirectMethodHandleDesc bootstrapMethod,
String constantName,
ClassDesc constantType,
ConstantDesc... bootstrapArgs) {
return new AnonymousDynamicConstantDesc<>(bootstrapMethod, constantName, constantType, bootstrapArgs);
}
/**
* Returns a nominal descriptor for a dynamic constant whose name parameter
* is {@link ConstantDescs#DEFAULT_NAME}, and whose type parameter is always
* the same as the bootstrap method return type.
*
* @param <T> the type of the dynamic constant
* @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
* bootstrap method for the constant
* @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
* to the bootstrap, that would appear in the
* {@code BootstrapMethods} attribute
* @return the nominal descriptor
* @throws NullPointerException if any argument is null
* @jvms 4.2.2 Unqualified Names
*/
public static<T> DynamicConstantDesc<T> of(DirectMethodHandleDesc bootstrapMethod,
ConstantDesc... bootstrapArgs) {
return ofNamed(bootstrapMethod, DEFAULT_NAME, bootstrapMethod.invocationType().returnType(), bootstrapArgs);
}
/**
* Returns a nominal descriptor for a dynamic constant whose bootstrap has
* no static arguments, whose name parameter is {@link ConstantDescs#DEFAULT_NAME},
* and whose type parameter is always the same as the bootstrap method return type.
*
* @param <T> the type of the dynamic constant
* @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
* bootstrap method for the constant
* @return the nominal descriptor
* @throws NullPointerException if any argument is null
*/
public static<T> DynamicConstantDesc<T> of(DirectMethodHandleDesc bootstrapMethod) {
return of(bootstrapMethod, EMPTY_CONSTANTDESC);
}
/**
* Returns the name that would appear in the {@code NameAndType} operand
* of the {@code LDC} for this constant.
*
* @return the constant name
*/
public String constantName() {
return constantName;
}
/**
* Returns a {@link ClassDesc} describing the type that would appear in the
* {@code NameAndType} operand of the {@code LDC} for this constant.
*
* @return the constant type
*/
public ClassDesc constantType() {
return constantType;
}
/**
* Returns a {@link MethodHandleDesc} describing the bootstrap method for
* this constant.
*
* @return the bootstrap method
*/
public DirectMethodHandleDesc bootstrapMethod() {
return bootstrapMethod;
}
/**
* Returns the bootstrap arguments for this constant.
*
* @return the bootstrap arguments
*/
public ConstantDesc[] bootstrapArgs() {
return bootstrapArgs.clone();
}
/**
* Returns the bootstrap arguments for this constant as an immutable {@link List}.
*
* @return a {@link List} of the bootstrap arguments
*/
public List<ConstantDesc> bootstrapArgsList() {
return List.of(bootstrapArgs);
}
@SuppressWarnings("unchecked")
public T resolveConstantDesc(MethodHandles.Lookup lookup) throws ReflectiveOperationException {
try {
MethodHandle bsm = (MethodHandle) bootstrapMethod.resolveConstantDesc(lookup);
if (bsm.type().parameterCount() < 2 ||
!MethodHandles.Lookup.class.isAssignableFrom(bsm.type().parameterType(0))) {
throw new BootstrapMethodError(
"Invalid bootstrap method declared for resolving a dynamic constant: " + bootstrapMethod);
}
Object[] bsmArgs = new Object[3 + bootstrapArgs.length];
bsmArgs[0] = lookup;
bsmArgs[1] = constantName;
bsmArgs[2] = constantType.resolveConstantDesc(lookup);
for (int i = 0; i < bootstrapArgs.length; i++)
bsmArgs[3 + i] = bootstrapArgs[i].resolveConstantDesc(lookup);
return (T) bsm.invokeWithArguments(bsmArgs);
} catch (Error e) {
throw e;
} catch (Throwable t) {
throw new BootstrapMethodError(t);
}
}
private ConstantDesc tryCanonicalize() {
Function<DynamicConstantDesc<?>, ConstantDesc> f = canonicalMap.get(bootstrapMethod);
if (f != null) {
try {
return f.apply(this);
}
catch (Throwable t) {
return this;
}
}
return this;
}
private static ConstantDesc canonicalizeNull(DynamicConstantDesc<?> desc) {
if (desc.bootstrapArgs.length != 0)
return desc;
return ConstantDescs.NULL;
}
private static ConstantDesc canonicalizeEnum(DynamicConstantDesc<?> desc) {
if (desc.bootstrapArgs.length != 0
|| desc.constantName == null)
return desc;
return EnumDesc.of(desc.constantType, desc.constantName);
}
private static ConstantDesc canonicalizePrimitiveClass(DynamicConstantDesc<?> desc) {
if (desc.bootstrapArgs.length != 0
|| !desc.constantType().equals(CD_Class)
|| desc.constantName == null)
return desc;
return ClassDesc.ofDescriptor(desc.constantName);
}
private static ConstantDesc canonicalizeStaticFieldVarHandle(DynamicConstantDesc<?> desc) {
if (desc.bootstrapArgs.length != 2
|| !desc.constantType().equals(CD_VarHandle))
return desc;
return VarHandleDesc.ofStaticField((ClassDesc) desc.bootstrapArgs[0],
desc.constantName,
(ClassDesc) desc.bootstrapArgs[1]);
}
private static ConstantDesc canonicalizeFieldVarHandle(DynamicConstantDesc<?> desc) {
if (desc.bootstrapArgs.length != 2
|| !desc.constantType().equals(CD_VarHandle))
return desc;
return VarHandleDesc.ofField((ClassDesc) desc.bootstrapArgs[0],
desc.constantName,
(ClassDesc) desc.bootstrapArgs[1]);
}
private static ConstantDesc canonicalizeArrayVarHandle(DynamicConstantDesc<?> desc) {
if (desc.bootstrapArgs.length != 1
|| !desc.constantType().equals(CD_VarHandle))
return desc;
return VarHandleDesc.ofArray((ClassDesc) desc.bootstrapArgs[0]);
}
// @@@ To eventually support in canonicalization: DCR with BSM=MHR_METHODHANDLEDESC_ASTYPE becomes AsTypeMHDesc
/**
* Compares the specified object with this descriptor for equality. Returns
* {@code true} if and only if the specified object is also a
* {@linkplain DynamicConstantDesc}, and both descriptors have equal
* bootstrap methods, bootstrap argument lists, constant name, and
* constant type.
*
* @param o the {@code DynamicConstantDesc} to compare to this
* {@code DynamicConstantDesc}
* @return {@code true} if the specified {@code DynamicConstantDesc} is
* equals to this {@code DynamicConstantDesc}.
*
*/
@Override
public final boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof DynamicConstantDesc)) return false;
DynamicConstantDesc<?> desc = (DynamicConstantDesc<?>) o;
return Objects.equals(bootstrapMethod, desc.bootstrapMethod) &&
Arrays.equals(bootstrapArgs, desc.bootstrapArgs) &&
Objects.equals(constantName, desc.constantName) &&
Objects.equals(constantType, desc.constantType);
}
@Override
public final int hashCode() {
int result = Objects.hash(bootstrapMethod, constantName, constantType);
result = 31 * result + Arrays.hashCode(bootstrapArgs);
return result;
}
/**
* Returns a compact textual description of this constant description,
* including the bootstrap method, the constant name and type, and
* the static bootstrap arguments.
*
* @return A compact textual description of this call site descriptor
*/
@Override
public String toString() {
return String.format("DynamicConstantDesc[%s::%s(%s%s)%s]",
bootstrapMethod.owner().displayName(),
bootstrapMethod.methodName(),
constantName.equals(ConstantDescs.DEFAULT_NAME) ? "" : constantName + "/",
Stream.of(bootstrapArgs).map(Object::toString).collect(joining(",")),
constantType.displayName());
}
private static class AnonymousDynamicConstantDesc<T> extends DynamicConstantDesc<T> {
AnonymousDynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod, String constantName, ClassDesc constantType, ConstantDesc... bootstrapArgs) {
super(bootstrapMethod, constantName, constantType, bootstrapArgs);
}
}
}

View file

@ -0,0 +1,219 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import static java.lang.constant.ConstantDescs.CD_void;
import static java.lang.constant.DirectMethodHandleDesc.Kind.CONSTRUCTOR;
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for a
* {@link MethodHandle} constant.
*
* @apiNote In the future, if the Java language permits, {@linkplain MethodHandleDesc}
* may become a {@code sealed} interface, which would prohibit subclassing except
* by explicitly permitted types. Non-platform classes should not implement
* {@linkplain MethodHandleDesc} directly.
*
* @since 12
*/
public interface MethodHandleDesc
extends ConstantDesc {
/**
* Creates a {@linkplain MethodHandleDesc} corresponding to an invocation of a
* declared method, invocation of a constructor, or access to a field.
*
* <p>The lookup descriptor string has the same format as for the various
* variants of {@code CONSTANT_MethodHandle_info} and for the lookup
* methods on {@link MethodHandles.Lookup}. For a method or constructor
* invocation, it is interpreted as a method type descriptor; for field
* access, it is interpreted as a field descriptor. If {@code kind} is
* {@code CONSTRUCTOR}, the {@code name} parameter is ignored and the return
* type of the lookup descriptor must be {@code void}. If {@code kind}
* corresponds to a virtual method invocation, the lookup type includes the
* method parameters but not the receiver type.
*
* @param kind The kind of method handle to be described
* @param owner a {@link ClassDesc} describing the class containing the
* method, constructor, or field
* @param name the unqualified name of the method or field (ignored if
* {@code kind} is {@code CONSTRUCTOR})
* @param lookupDescriptor a method descriptor string the lookup type,
* if the request is for a method invocation, or
* describing the invocation type, if the request is
* for a field or constructor
* @return the {@linkplain MethodHandleDesc}
* @throws NullPointerException if any of the non-ignored arguments are null
* @jvms 4.4.8 The CONSTANT_MethodHandle_info Structure
* @jvms 4.2.2 Unqualified Names
* @jvms 4.3.2 Field Descriptors
* @jvms 4.3.3 Method Descriptors
*/
static DirectMethodHandleDesc of(DirectMethodHandleDesc.Kind kind,
ClassDesc owner,
String name,
String lookupDescriptor) {
switch (kind) {
case GETTER:
case SETTER:
case STATIC_GETTER:
case STATIC_SETTER:
return ofField(kind, owner, name, ClassDesc.ofDescriptor(lookupDescriptor));
default:
return new DirectMethodHandleDescImpl(kind, owner, name, MethodTypeDesc.ofDescriptor(lookupDescriptor));
}
}
/**
* Creates a {@linkplain MethodHandleDesc} corresponding to an invocation of a
* declared method or constructor.
*
* <p>The lookup descriptor string has the same format as for the lookup
* methods on {@link MethodHandles.Lookup}. If {@code kind} is
* {@code CONSTRUCTOR}, the name is ignored and the return type of the lookup
* type must be {@code void}. If {@code kind} corresponds to a virtual method
* invocation, the lookup type includes the method parameters but not the
* receiver type.
*
* @param kind The kind of method handle to be described; must be one of
* {@code SPECIAL, VIRTUAL, STATIC, INTERFACE_SPECIAL,
* INTERFACE_VIRTUAL, INTERFACE_STATIC, CONSTRUCTOR}
* @param owner a {@link ClassDesc} describing the class containing the
* method or constructor
* @param name the unqualified name of the method (ignored if {@code kind}
* is {@code CONSTRUCTOR})
* @param lookupMethodType a {@link MethodTypeDesc} describing the lookup type
* @return the {@linkplain MethodHandleDesc}
* @throws NullPointerException if any non-ignored arguments are null
* @throws IllegalArgumentException if the {@code name} has the incorrect
* format, or the kind is invalid
* @jvms 4.2.2 Unqualified Names
*/
static DirectMethodHandleDesc ofMethod(DirectMethodHandleDesc.Kind kind,
ClassDesc owner,
String name,
MethodTypeDesc lookupMethodType) {
switch (kind) {
case GETTER:
case SETTER:
case STATIC_GETTER:
case STATIC_SETTER:
throw new IllegalArgumentException(kind.toString());
case VIRTUAL:
case SPECIAL:
case INTERFACE_VIRTUAL:
case INTERFACE_SPECIAL:
case INTERFACE_STATIC:
case STATIC:
case CONSTRUCTOR:
return new DirectMethodHandleDescImpl(kind, owner, name, lookupMethodType);
default:
throw new IllegalArgumentException(kind.toString());
}
}
/**
* Creates a {@linkplain MethodHandleDesc} corresponding to a method handle
* that accesses a field.
*
* @param kind the kind of the method handle to be described; must be one of {@code GETTER},
* {@code SETTER}, {@code STATIC_GETTER}, or {@code STATIC_SETTER}
* @param owner a {@link ClassDesc} describing the class containing the field
* @param fieldName the unqualified name of the field
* @param fieldType a {@link ClassDesc} describing the type of the field
* @return the {@linkplain MethodHandleDesc}
* @throws NullPointerException if any of the arguments are null
* @throws IllegalArgumentException if the {@code kind} is not one of the
* valid values or if the field name is not valid
* @jvms 4.2.2 Unqualified Names
*/
static DirectMethodHandleDesc ofField(DirectMethodHandleDesc.Kind kind,
ClassDesc owner,
String fieldName,
ClassDesc fieldType) {
MethodTypeDesc mtr;
switch (kind) {
case GETTER: mtr = MethodTypeDesc.of(fieldType, owner); break;
case SETTER: mtr = MethodTypeDesc.of(CD_void, owner, fieldType); break;
case STATIC_GETTER: mtr = MethodTypeDesc.of(fieldType); break;
case STATIC_SETTER: mtr = MethodTypeDesc.of(CD_void, fieldType); break;
default:
throw new IllegalArgumentException(kind.toString());
}
return new DirectMethodHandleDescImpl(kind, owner, fieldName, mtr);
}
/**
* Returns a {@linkplain MethodHandleDesc} corresponding to invocation of a constructor
*
* @param owner a {@link ClassDesc} describing the class containing the
* constructor
* @param paramTypes {@link ClassDesc}s describing the parameter types of
* the constructor
* @return the {@linkplain MethodHandleDesc}
* @throws NullPointerException if any of the arguments are null
*/
static DirectMethodHandleDesc ofConstructor(ClassDesc owner,
ClassDesc... paramTypes) {
return MethodHandleDesc.ofMethod(CONSTRUCTOR, owner, ConstantDescs.DEFAULT_NAME,
MethodTypeDesc.of(CD_void, paramTypes));
}
/**
* Returns a {@linkplain MethodHandleDesc} that describes this method handle
* adapted to a different type, as if by {@link MethodHandle#asType(MethodType)}.
*
* @param type a {@link MethodHandleDesc} describing the new method type
* @return a {@linkplain MethodHandleDesc} for the adapted method handle
*/
default MethodHandleDesc asType(MethodTypeDesc type) {
return (invocationType().equals(type)) ? this : new AsTypeMethodHandleDesc(this, type);
}
/**
* Returns a {@link MethodTypeDesc} describing the invocation type of the
* method handle described by this nominal descriptor. The invocation type
* describes the full set of stack values that are consumed by the invocation
* (including the receiver, if any).
*
* @return a {@linkplain MethodHandleDesc} describing the method handle type
*/
MethodTypeDesc invocationType();
/**
* Compares the specified object with this descriptor for equality. Returns
* {@code true} if and only if the specified object is also a
* {@linkplain MethodHandleDesc}, and both encode the same nominal description
* of a method handle.
*
* @param o the other object
* @return whether this descriptor is equal to the other object
*/
boolean equals(Object o);
}

View file

@ -0,0 +1,201 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.lang.invoke.MethodType;
import java.lang.invoke.TypeDescriptor;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for a
* {@linkplain MethodType} constant.
*
* @apiNote In the future, if the Java language permits, {@linkplain MethodTypeDesc}
* may become a {@code sealed} interface, which would prohibit subclassing except
* by explicitly permitted types. Non-platform classes should not implement
* {@linkplain MethodTypeDesc} directly.
*
* @since 12
*/
public interface MethodTypeDesc
extends ConstantDesc,
TypeDescriptor.OfMethod<ClassDesc, MethodTypeDesc> {
/**
* Creates a {@linkplain MethodTypeDesc} given a method descriptor string.
*
* @param descriptor a method descriptor string
* @return a {@linkplain MethodTypeDesc} describing the desired method type
* @throws NullPointerException if any argument is {@code null}
* @throws IllegalArgumentException if the descriptor string is not a valid
* method descriptor
* @jvms 4.3.3 Method Descriptors
*/
static MethodTypeDesc ofDescriptor(String descriptor) {
return MethodTypeDescImpl.ofDescriptor(descriptor);
}
/**
* Returns a {@linkplain MethodTypeDesc} given the return type and parameter
* types.
*
* @param returnDesc a {@linkplain ClassDesc} describing the return type
* @param paramDescs {@linkplain ClassDesc}s describing the argument types
* @return a {@linkplain MethodTypeDesc} describing the desired method type
* @throws NullPointerException if any argument is {@code null}
*/
static MethodTypeDesc of(ClassDesc returnDesc, ClassDesc... paramDescs) {
return new MethodTypeDescImpl(returnDesc, paramDescs);
}
/**
* Gets the return type of the method type described by this {@linkplain MethodTypeDesc}.
*
* @return a {@link ClassDesc} describing the return type of the method type
*/
ClassDesc returnType();
/**
* Returns the number of parameters of the method type described by
* this {@linkplain MethodTypeDesc}.
* @return the number of parameters
*/
int parameterCount();
/**
* Returns the parameter type of the {@code index}'th parameter of the method type
* described by this {@linkplain MethodTypeDesc}.
*
* @param index the index of the parameter to retrieve
* @return a {@link ClassDesc} describing the desired parameter type
* @throws IndexOutOfBoundsException if the index is outside the half-open
* range {[0, parameterCount())}
*/
ClassDesc parameterType(int index);
/**
* Returns the parameter types as an immutable {@link List}.
*
* @return a {@link List} of {@link ClassDesc} describing the parameter types
*/
List<ClassDesc> parameterList();
/**
* Returns the parameter types as an array.
*
* @return an array of {@link ClassDesc} describing the parameter types
*/
ClassDesc[] parameterArray();
/**
* Returns a {@linkplain MethodTypeDesc} that is identical to
* this one, except with the specified return type.
*
* @param returnType a {@link ClassDesc} describing the new return type
* @return a {@linkplain MethodTypeDesc} describing the desired method type
* @throws NullPointerException if any argument is {@code null}
*/
MethodTypeDesc changeReturnType(ClassDesc returnType);
/**
* Returns a {@linkplain MethodTypeDesc} that is identical to this one,
* except that a single parameter type has been changed to the specified type.
*
* @param index the index of the parameter to change
* @param paramType a {@link ClassDesc} describing the new parameter type
* @return a {@linkplain MethodTypeDesc} describing the desired method type
* @throws NullPointerException if any argument is {@code null}
* @throws IndexOutOfBoundsException if the index is outside the half-open
* range {[0, parameterCount)}
*/
MethodTypeDesc changeParameterType(int index, ClassDesc paramType);
/**
* Returns a {@linkplain MethodTypeDesc} that is identical to this one,
* except that a range of parameter types have been removed.
*
* @param start the index of the first parameter to remove
* @param end the index after the last parameter to remove
* @return a {@linkplain MethodTypeDesc} describing the desired method type
* @throws IndexOutOfBoundsException if {@code start} is outside the half-open
* range {[0, parameterCount)}, or {@code end} is outside the closed range
* {@code [0, parameterCount]}
*/
MethodTypeDesc dropParameterTypes(int start, int end);
/**
* Returns a {@linkplain MethodTypeDesc} that is identical to this one,
* except that a range of additional parameter types have been inserted.
*
* @param pos the index at which to insert the first inserted parameter
* @param paramTypes {@link ClassDesc}s describing the new parameter types
* to insert
* @return a {@linkplain MethodTypeDesc} describing the desired method type
* @throws NullPointerException if any argument is {@code null}
* @throws IndexOutOfBoundsException if {@code pos} is outside the closed
* range {[0, parameterCount]}
*/
MethodTypeDesc insertParameterTypes(int pos, ClassDesc... paramTypes);
/**
* Returns the method type descriptor string.
*
* @return the method type descriptor string
* @jvms 4.3.3 Method Descriptors
*/
default String descriptorString() {
return String.format("(%s)%s",
Stream.of(parameterArray())
.map(ClassDesc::descriptorString)
.collect(Collectors.joining()),
returnType().descriptorString());
}
/**
* Returns a human-readable descriptor for this method type, using the
* canonical names for parameter and return types.
*
* @return the human-readable descriptor for this method type
*/
default String displayDescriptor() {
return String.format("(%s)%s",
Stream.of(parameterArray())
.map(ClassDesc::displayName)
.collect(Collectors.joining(",")),
returnType().displayName());
}
/**
* Compares the specified object with this descriptor for equality. Returns
* {@code true} if and only if the specified object is also a
* {@linkplain MethodTypeDesc} both have the same arity, their return types
* are equal, and each pair of corresponding parameter types are equal.
*
* @param o the other object
* @return whether this descriptor is equal to the other object
*/
boolean equals(Object o);
}

View file

@ -0,0 +1,173 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import java.util.List;
import static java.util.Objects.requireNonNull;
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for a
* {@link MethodType}. A {@linkplain MethodTypeDescImpl} corresponds to a
* {@code Constant_MethodType_info} entry in the constant pool of a classfile.
*/
final class MethodTypeDescImpl implements MethodTypeDesc {
private final ClassDesc returnType;
private final ClassDesc[] argTypes;
/**
* Constructs a {@linkplain MethodTypeDesc} with the specified return type
* and parameter types
*
* @param returnType a {@link ClassDesc} describing the return type
* @param argTypes {@link ClassDesc}s describing the parameter types
*/
MethodTypeDescImpl(ClassDesc returnType, ClassDesc[] argTypes) {
this.returnType = requireNonNull(returnType);
this.argTypes = requireNonNull(argTypes);
for (ClassDesc cr : argTypes)
if (cr.isPrimitive() && cr.descriptorString().equals("V"))
throw new IllegalArgumentException("Void parameters not permitted");
}
/**
* Creates a {@linkplain MethodTypeDescImpl} given a method descriptor string.
*
* @param descriptor the method descriptor string
* @return a {@linkplain MethodTypeDescImpl} describing the desired method type
* @throws IllegalArgumentException if the descriptor string is not a valid
* method descriptor
* @jvms 4.3.3 Method Descriptors
*/
static MethodTypeDescImpl ofDescriptor(String descriptor) {
requireNonNull(descriptor);
List<String> types = ConstantUtils.parseMethodDescriptor(descriptor);
ClassDesc[] paramTypes = types.stream().skip(1).map(ClassDesc::ofDescriptor).toArray(ClassDesc[]::new);
return new MethodTypeDescImpl(ClassDesc.ofDescriptor(types.get(0)), paramTypes);
}
@Override
public ClassDesc returnType() {
return returnType;
}
@Override
public int parameterCount() {
return argTypes.length;
}
@Override
public ClassDesc parameterType(int index) {
return argTypes[index];
}
@Override
public List<ClassDesc> parameterList() {
return List.of(argTypes);
}
@Override
public ClassDesc[] parameterArray() {
return argTypes.clone();
}
@Override
public MethodTypeDesc changeReturnType(ClassDesc returnType) {
return MethodTypeDesc.of(returnType, argTypes);
}
@Override
public MethodTypeDesc changeParameterType(int index, ClassDesc paramType) {
ClassDesc[] newArgs = argTypes.clone();
newArgs[index] = paramType;
return MethodTypeDesc.of(returnType, newArgs);
}
@Override
public MethodTypeDesc dropParameterTypes(int start, int end) {
if (start < 0 || start >= argTypes.length || end < 0 || end > argTypes.length)
throw new IndexOutOfBoundsException();
else if (start > end)
throw new IllegalArgumentException(String.format("Range (%d, %d) not valid for size %d", start, end, argTypes.length));
ClassDesc[] newArgs = new ClassDesc[argTypes.length - (end - start)];
System.arraycopy(argTypes, 0, newArgs, 0, start);
System.arraycopy(argTypes, end, newArgs, start, argTypes.length - end);
return MethodTypeDesc.of(returnType, newArgs);
}
@Override
public MethodTypeDesc insertParameterTypes(int pos, ClassDesc... paramTypes) {
if (pos < 0 || pos > argTypes.length)
throw new IndexOutOfBoundsException(pos);
ClassDesc[] newArgs = new ClassDesc[argTypes.length + paramTypes.length];
System.arraycopy(argTypes, 0, newArgs, 0, pos);
System.arraycopy(paramTypes, 0, newArgs, pos, paramTypes.length);
System.arraycopy(argTypes, pos, newArgs, pos+paramTypes.length, argTypes.length - pos);
return MethodTypeDesc.of(returnType, newArgs);
}
@Override
public MethodType resolveConstantDesc(MethodHandles.Lookup lookup) {
return MethodType.fromMethodDescriptorString(descriptorString(), lookup.lookupClass().getClassLoader());
}
/**
* Returns {@code true} if this {@linkplain MethodTypeDescImpl} is
* equal to another {@linkplain MethodTypeDescImpl}. Equality is
* determined by the two descriptors having equal return types and argument
* types.
*
* @param o the {@code MethodTypeDescImpl} to compare to this
* {@code MethodTypeDescImpl}
* @return {@code true} if the specified {@code MethodTypeDescImpl} is
* equals to this {@code MethodTypeDescImpl}.
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MethodTypeDescImpl constant = (MethodTypeDescImpl) o;
return returnType.equals(constant.returnType)
&& Arrays.equals(argTypes, constant.argTypes);
}
@Override
public int hashCode() {
int result = returnType.hashCode();
result = 31 * result + Arrays.hashCode(argTypes);
return result;
}
@Override
public String toString() {
return String.format("MethodTypeDesc[%s]", displayDescriptor());
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.lang.invoke.MethodHandles;
import sun.invoke.util.Wrapper;
import static java.util.Objects.requireNonNull;
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for the class
* constant corresponding to a primitive type (e.g., {@code int.class}).
*/
final class PrimitiveClassDescImpl
extends DynamicConstantDesc<Class<?>> implements ClassDesc {
private final String descriptor;
/**
* Creates a {@linkplain ClassDesc} given a descriptor string for a primitive
* type.
*
* @param descriptor the descriptor string, which must be a one-character
* string corresponding to one of the nine base types
* @throws IllegalArgumentException if the descriptor string does not
* describe a valid primitive type
* @jvms 4.3 Descriptors
*/
PrimitiveClassDescImpl(String descriptor) {
super(ConstantDescs.BSM_PRIMITIVE_CLASS, requireNonNull(descriptor), ConstantDescs.CD_Class);
if (descriptor.length() != 1
|| "VIJCSBFDZ".indexOf(descriptor.charAt(0)) < 0)
throw new IllegalArgumentException(String.format("not a valid primitive type descriptor: %s", descriptor));
this.descriptor = descriptor;
}
@Override
public String descriptorString() {
return descriptor;
}
@Override
public Class<?> resolveConstantDesc(MethodHandles.Lookup lookup) {
return Wrapper.forBasicType(descriptorString().charAt(0)).primitiveType();
}
@Override
public String toString() {
return String.format("PrimitiveClassDesc[%s]", displayName());
}
}

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.constant;
import java.lang.invoke.MethodHandles;
import static java.lang.constant.ConstantUtils.dropFirstAndLastChar;
import static java.lang.constant.ConstantUtils.internalToBinary;
import static java.util.Objects.requireNonNull;
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for a class,
* interface, or array type. A {@linkplain ReferenceClassDescImpl} corresponds to a
* {@code Constant_Class_info} entry in the constant pool of a classfile.
*/
final class ReferenceClassDescImpl implements ClassDesc {
private final String descriptor;
/**
* Creates a {@linkplain ClassDesc} from a descriptor string for a class or
* interface type
*
* @param descriptor a field descriptor string for a class or interface type
* @throws IllegalArgumentException if the descriptor string is not a valid
* field descriptor string, or does not describe a class or interface type
* @jvms 4.3.2 Field Descriptors
*/
ReferenceClassDescImpl(String descriptor) {
requireNonNull(descriptor);
int len = ConstantUtils.matchSig(descriptor, 0, descriptor.length());
if (len == 0 || len == 1
|| len != descriptor.length())
throw new IllegalArgumentException(String.format("not a valid reference type descriptor: %s", descriptor));
this.descriptor = descriptor;
}
@Override
public String descriptorString() {
return descriptor;
}
@Override
public Class<?> resolveConstantDesc(MethodHandles.Lookup lookup)
throws ReflectiveOperationException {
ClassDesc c = this;
int depth = ConstantUtils.arrayDepth(descriptorString());
for (int i=0; i<depth; i++)
c = c.componentType();
if (c.isPrimitive())
return lookup.findClass(descriptorString());
else {
Class<?> clazz = lookup.findClass(internalToBinary(dropFirstAndLastChar(c.descriptorString())));
for (int i = 0; i < depth; i++)
clazz = clazz.arrayType();
return clazz;
}
}
/**
* Returns {@code true} if this {@linkplain ReferenceClassDescImpl} is
* equal to another {@linkplain ReferenceClassDescImpl}. Equality is
* determined by the two class descriptors having equal class descriptor
* strings.
*
* @param o the {@code ClassDesc} to compare to this
* {@code ClassDesc}
* @return {@code true} if the specified {@code ClassDesc} is
* equals to this {@code ClassDesc}.
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ClassDesc constant = (ClassDesc) o;
return descriptor.equals(constant.descriptorString());
}
@Override
public int hashCode() {
return descriptor.hashCode();
}
@Override
public String toString() {
return String.format("ClassDesc[%s]", displayName());
}
}

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Classes and interfaces to represent <em>nominal descriptors</em> for run-time
* entities such as classes or method handles, and classfile entities such as
* constant pool entries or {@code invokedynamic} call sites. These classes
* are suitable for use in bytecode reading and writing APIs, {@code invokedynamic}
* bootstraps, bytecode intrinsic APIs, and compile-time or link-time program
* analysis tools.
*
* <p>Every API that reads and writes bytecode instructions needs to model the
* operands to these instructions and other classfile structures (such as entries
* in the bootstrap methods table or stack maps, which frequently reference
* entries in the classfile constant pool.) Such entries can denote values of
* fundamental types, such as strings or integers; parts of a program, such as
* classes or method handles; or values of arbitrary user-defined types. The
* {@link java.lang.constant.ConstantDesc} hierarchy provides a representation of
* constant pool entries in nominal form that is convenient for APIs to model
* operands of bytecode instructions.
*
* <h2><a id="nominal"></a>Nominal Descriptors</h2>
*
* <p>A {@link java.lang.constant.ConstantDesc} is a description of a constant
* value. Such a description is the <em>nominal form</em> of the constant value;
* it is not the value itself, but rather a "recipe" for describing the value,
* storing the value in a constant pool entry, or reconstituting the value given
* a class loading context. Every {@link java.lang.constant.ConstantDesc}
* knows how to <em>resolve</em> itself -- compute the value that it describes --
* via {@link java.lang.constant.ConstantDesc#resolveConstantDesc(java.lang.invoke.MethodHandles.Lookup)}.
* This allows an API which accepts {@link java.lang.constant.ConstantDesc}
* objects to evaluate them reflectively, provided that the classes and methods
* referenced in their nominal description are present and accessible.
*
* <p>The subtypes of {@link java.lang.constant.ConstantDesc} describe various kinds
* of constant values. For each type of loadable constant pool entry defined in JVMS 4.4,
* there is a corresponding subtype of {@link java.lang.constant.ConstantDesc}:
* {@link java.lang.constant.ClassDesc}, {@link java.lang.constant.MethodTypeDesc},
* {@link java.lang.constant.DirectMethodHandleDesc}, {@link java.lang.String},
* {@link java.lang.Integer}, {@link java.lang.Long}, {@link java.lang.Float},
* {@link java.lang.Double}, and {@link java.lang.constant.DynamicConstantDesc}. These classes
* provide type-specific accessor methods to extract the nominal information for
* that kind of constant. When a bytecode-writing API encounters a {@link java.lang.constant.ConstantDesc},
* it should examine it to see which of these types it is, cast it, extract
* its nominal information, and generate the corresponding entry to the constant pool.
* When a bytecode-reading API encounters a constant pool entry, it can
* convert it to the appropriate type of nominal descriptor. For dynamic
* constants, bytecode-reading APIs may wish to use the factory
* {@link java.lang.constant.DynamicConstantDesc#ofCanonical(DirectMethodHandleDesc, java.lang.String, ClassDesc, ConstantDesc[])},
* which will inspect the bootstrap and, for well-known bootstraps, return
* a more specific subtype of {@link java.lang.constant.DynamicConstantDesc}, such as
* {@link java.lang.Enum.EnumDesc}.
*
* <p>Another way to obtain the nominal description of a value is to ask the value
* itself. A {@link java.lang.constant.Constable} is a type whose values
* can describe themselves in nominal form as a {@link java.lang.constant.ConstantDesc}.
* Fundamental types such as {@link java.lang.String} and {@link java.lang.Class}
* implement {@link java.lang.constant.Constable}, as can user-defined
* classes. Entities that generate classfiles (such as compilers) can introspect
* over constable objects to obtain a more efficient way to represent their values
* in classfiles.
*
* <p>This package also includes {@link java.lang.constant.DynamicCallSiteDesc},
* which represents a (non-loadable) {@code Constant_InvokeDynamic_info} constant
* pool entry. It describes the bootstrap method, invocation name and type,
* and bootstrap arguments associated with an {@code invokedynamic} instruction.
* It is also suitable for describing {@code invokedynamic} call sites in bytecode
* reading and writing APIs.
*
* @jvms 4.4 The Constant Pool
*
* @since 12
*/
package java.lang.constant;

View file

@ -28,10 +28,19 @@ package java.lang.invoke;
import jdk.internal.HotSpotIntrinsicCandidate;
import java.lang.constant.ClassDesc;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDesc;
import java.lang.constant.DirectMethodHandleDesc;
import java.lang.constant.MethodHandleDesc;
import java.lang.constant.MethodTypeDesc;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import static java.lang.invoke.MethodHandleInfo.*;
import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
/**
* A method handle is a typed, directly executable reference to an underlying method,
@ -428,7 +437,7 @@ mh.invokeExact(System.out, "Hello, world.");
* @author John Rose, JSR 292 EG
* @since 1.7
*/
public abstract class MethodHandle {
public abstract class MethodHandle implements Constable {
/**
* Internal marker interface which distinguishes (to the Java compiler)
@ -1511,6 +1520,60 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
return bindArgumentL(0, x);
}
/**
* Return 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
*/
@Override
public Optional<MethodHandleDesc> describeConstable() {
MethodHandleInfo info;
ClassDesc owner;
String name;
MethodTypeDesc type;
boolean isInterface;
try {
info = IMPL_LOOKUP.revealDirect(this);
isInterface = info.getDeclaringClass().isInterface();
owner = info.getDeclaringClass().describeConstable().orElseThrow();
type = info.getMethodType().describeConstable().orElseThrow();
name = info.getName();
}
catch (Exception e) {
return Optional.empty();
}
switch (info.getReferenceKind()) {
case REF_getField:
return Optional.of(MethodHandleDesc.ofField(DirectMethodHandleDesc.Kind.GETTER, owner, name, type.returnType()));
case REF_putField:
return Optional.of(MethodHandleDesc.ofField(DirectMethodHandleDesc.Kind.SETTER, owner, name, type.parameterType(0)));
case REF_getStatic:
return Optional.of(MethodHandleDesc.ofField(DirectMethodHandleDesc.Kind.STATIC_GETTER, owner, name, type.returnType()));
case REF_putStatic:
return Optional.of(MethodHandleDesc.ofField(DirectMethodHandleDesc.Kind.STATIC_SETTER, owner, name, type.parameterType(0)));
case REF_invokeVirtual:
return Optional.of(MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.VIRTUAL, owner, name, type));
case REF_invokeStatic:
return isInterface ?
Optional.of(MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, owner, name, type)) :
Optional.of(MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.STATIC, owner, name, type));
case REF_invokeSpecial:
return isInterface ?
Optional.of(MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_SPECIAL, owner, name, type)) :
Optional.of(MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.SPECIAL, owner, name, type));
case REF_invokeInterface:
return Optional.of(MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_VIRTUAL, owner, name, type));
case REF_newInvokeSpecial:
return Optional.of(MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.CONSTRUCTOR, owner, name, type));
default:
return Optional.empty();
}
}
/**
* Returns a string representation of the method handle,
* starting with the string {@code "MethodHandle"} and

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,21 +25,30 @@
package java.lang.invoke;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.Wrapper;
import java.lang.ref.WeakReference;
import java.lang.constant.ClassDesc;
import java.lang.constant.Constable;
import java.lang.constant.MethodTypeDesc;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Stream;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.BytecodeDescriptor;
import static java.lang.invoke.MethodHandleStatics.*;
import sun.invoke.util.VerifyType;
import sun.invoke.util.Wrapper;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
/**
* A method type represents the arguments and return type accepted and
@ -91,7 +100,10 @@ import sun.invoke.util.VerifyType;
* @since 1.7
*/
public final
class MethodType implements java.io.Serializable {
class MethodType
implements Constable,
TypeDescriptor.OfMethod<Class<?>, MethodType>,
java.io.Serializable {
private static final long serialVersionUID = 292L; // {rtype, {ptype...}}
// The rtype and ptypes fields define the structural identity of the method type:
@ -1175,10 +1187,43 @@ class MethodType implements java.io.Serializable {
return desc;
}
/**
* Return a field type descriptor string for this type
*
* @return the descriptor string
* @jvms 4.3.2 Field Descriptors
* @since 12
*/
@Override
public String descriptorString() {
return toMethodDescriptorString();
}
/*non-public*/ static String toFieldDescriptorString(Class<?> cls) {
return BytecodeDescriptor.unparse(cls);
}
/**
* Return 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
*/
@Override
public Optional<MethodTypeDesc> describeConstable() {
try {
return Optional.of(MethodTypeDesc.of(returnType().describeConstable().orElseThrow(),
Stream.of(parameterArray())
.map(p -> p.describeConstable().orElseThrow())
.toArray(ClassDesc[]::new)));
}
catch (NoSuchElementException e) {
return Optional.empty();
}
}
/// Serialization.
/**

View file

@ -0,0 +1,187 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
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
*
* @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).
*
* @return the type descriptor
* @jvms 4.3.2 Field Descriptors
* @jvms 4.3.3 Method Descriptors
*/
String descriptorString();
/**
* An entity that has a field type descriptor
*
* @param <F> the class implementing {@linkplain TypeDescriptor.OfField}
* @jvms 4.3.2 Field Descriptors
* @since 12
*/
interface OfField<F extends TypeDescriptor.OfField<F>> extends TypeDescriptor {
/**
* Does this field descriptor describe an array type?
* @return whether this field descriptor describes an array type
*/
boolean isArray();
/**
* Does this field descriptor describe a primitive type?
* @return whether this field descriptor describes a primitive type
*/
boolean isPrimitive();
/**
* If this field descriptor describes an array type, return
* a descriptor for its component type, otherwise return {@code null}.
* @return the component type, or {@code null} if this field descriptor does
* not describe an array type
*/
F componentType();
/**
* Return a descriptor for the array type whose component type is described by this
* descriptor
* @return the descriptor for the array type
*/
F arrayType();
}
/**
* An entity that has a method type descriptor
*
* @param <F> the type representing field type descriptors
* @param <M> the class implementing {@linkplain TypeDescriptor.OfMethod}
* @jvms 4.3.2 Field Descriptors
* @jvms 4.3.3 Method Descriptors
* @since 12
*/
interface OfMethod<F extends TypeDescriptor.OfField<F>, M extends TypeDescriptor.OfMethod<F, M>>
extends TypeDescriptor {
/**
* Return the number of parameters in the method type
* @return the number of parameters
*/
int parameterCount();
/**
* Return a field descriptor describing the requested parameter of the method type
* described by this descriptor
* @param i the index of the parameter
* @return a field descriptor for the requested parameter type
* @throws IndexOutOfBoundsException if the index is outside the half-open
* range {[0, parameterCount)}
*/
F parameterType(int i);
/**
* Return a field descriptor describing the return type of the method type described
* by this descriptor
* @return a field descriptor for the return type
*/
F returnType();
/**
* Return an array of field descriptors for the parameter types of the method type
* described by this descriptor
* @return field descriptors for the parameter types
*/
F[] parameterArray();
/**
* Return an immutable list of field descriptors for the parameter types of the method type
* described by this descriptor
* @return field descriptors for the parameter types
*/
List<F> parameterList();
/**
* Return a method descriptor that is identical to this one, except that the return
* type has been changed to the specified type
*
* @param newReturn a field descriptor for the new return type
* @throws NullPointerException if any argument is {@code null}
* @return the new method descriptor
*/
M changeReturnType(F newReturn);
/**
* Return a method descriptor that is identical to this one,
* except that a single parameter type has been changed to the specified type.
*
* @param index the index of the parameter to change
* @param paramType a field descriptor describing the new parameter type
* @return the new method descriptor
* @throws NullPointerException if any argument is {@code null}
* @throws IndexOutOfBoundsException if the index is outside the half-open
* range {[0, parameterCount)}
*/
M changeParameterType(int index, F paramType);
/**
* Return a method descriptor that is identical to this one,
* except that a range of parameter types have been removed.
*
* @param start the index of the first parameter to remove
* @param end the index after the last parameter to remove
* @return the new method descriptor
*
* @throws IndexOutOfBoundsException if {@code start} is outside the half-open
* range {@code [0, parameterCount)}, or {@code end} is outside the closed range
* {@code [0, parameterCount]}, or if {@code start > end}
*/
M dropParameterTypes(int start, int end);
/**
* Return a method descriptor that is identical to this one,
* except that a range of additional parameter types have been inserted.
*
* @param pos the index at which to insert the first inserted parameter
* @param paramTypes field descriptors describing the new parameter types
* to insert
* @return the new method descriptor
* @throws NullPointerException if any argument is {@code null}
* @throws IndexOutOfBoundsException if {@code pos} is outside the closed
* range {[0, parameterCount]}
*/
@SuppressWarnings("unchecked")
M insertParameterTypes(int pos, F... paramTypes);
}
}

View file

@ -25,19 +25,26 @@
package java.lang.invoke;
import java.lang.constant.ClassDesc;
import java.lang.constant.Constable;
import java.lang.constant.ConstantDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DirectMethodHandleDesc;
import java.lang.constant.DynamicConstantDesc;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Stable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
import static java.lang.invoke.MethodHandleStatics.newInternalError;
/**
* A VarHandle is a dynamically strongly typed reference to a variable, or to a
@ -437,7 +444,7 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
* @see MethodType
* @since 9
*/
public abstract class VarHandle {
public abstract class VarHandle implements Constable {
final VarForm vform;
VarHandle(VarForm vform) {
@ -1857,6 +1864,32 @@ public abstract class VarHandle {
}
}
@Override
public final boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VarHandle that = (VarHandle) o;
return accessModeType(AccessMode.GET).equals(that.accessModeType(AccessMode.GET)) &&
internalEquals(that);
}
abstract boolean internalEquals(VarHandle vh);
@Override
public final int hashCode() {
return 31 * accessModeType(AccessMode.GET).hashCode() + internalHashCode();
}
abstract int internalHashCode();
@Override
public final String toString() {
return String.format("VarHandle[varType=%s, coord=%s]",
varType().getName(),
coordinateTypes());
}
/**
* Returns the variable type of variables referenced by this VarHandle.
*
@ -1951,6 +1984,20 @@ public abstract class VarHandle {
}
}
/**
* Return 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
*/
@Override
public Optional<VarHandleDesc> describeConstable() {
// partial function for field and array only
return Optional.empty();
}
@Stable
TypesAndInvokers typesAndInvokers;
@ -2082,4 +2129,163 @@ public abstract class VarHandle {
public static void storeStoreFence() {
UNSAFE.storeStoreFence();
}
/**
* A <a href="package-summary.html#nominal">nominal descriptor</a> for a
* {@link VarHandle} constant.
*
* @since 12
*/
public static final class VarHandleDesc extends DynamicConstantDesc<VarHandle> {
/**
* Kinds of variable handle descs
*/
private enum Kind {
FIELD(ConstantDescs.BSM_VARHANDLE_FIELD),
STATIC_FIELD(ConstantDescs.BSM_VARHANDLE_STATIC_FIELD),
ARRAY(ConstantDescs.BSM_VARHANDLE_ARRAY);
final DirectMethodHandleDesc bootstrapMethod;
Kind(DirectMethodHandleDesc bootstrapMethod) {
this.bootstrapMethod = bootstrapMethod;
}
ConstantDesc[] toBSMArgs(ClassDesc declaringClass, ClassDesc varType) {
switch (this) {
case FIELD:
case STATIC_FIELD:
return new ConstantDesc[] {declaringClass, varType };
case ARRAY:
return new ConstantDesc[] {declaringClass };
default:
throw new InternalError("Cannot reach here");
}
}
}
private final Kind kind;
private final ClassDesc declaringClass;
private final ClassDesc varType;
/**
* Construct a {@linkplain VarHandleDesc} given a kind, name, and declaring
* class.
*
* @param kind the kind of of the var handle
* @param name the unqualified name of the field, for field var handles; otherwise ignored
* @param declaringClass a {@link ClassDesc} describing the declaring class,
* for field var handles
* @param varType a {@link ClassDesc} describing the type of the variable
* @throws NullPointerException if any required argument is null
* @jvms 4.2.2 Unqualified Names
*/
private VarHandleDesc(Kind kind, String name, ClassDesc declaringClass, ClassDesc varType) {
super(kind.bootstrapMethod, name,
ConstantDescs.CD_VarHandle,
kind.toBSMArgs(declaringClass, varType));
this.kind = kind;
this.declaringClass = declaringClass;
this.varType = varType;
}
/**
* Returns a {@linkplain VarHandleDesc} corresponding to a {@link VarHandle}
* for an instance field.
*
* @param name the unqualifed name of the field
* @param declaringClass a {@link ClassDesc} describing the declaring class,
* for field var handles
* @param fieldType a {@link ClassDesc} describing the type of the field
* @return the {@linkplain VarHandleDesc}
* @throws NullPointerException if any of the arguments are null
* @jvms 4.2.2 Unqualified Names
*/
public static VarHandleDesc ofField(ClassDesc declaringClass, String name, ClassDesc fieldType) {
Objects.requireNonNull(declaringClass);
Objects.requireNonNull(name);
Objects.requireNonNull(fieldType);
return new VarHandleDesc(Kind.FIELD, name, declaringClass, fieldType);
}
/**
* Returns a {@linkplain VarHandleDesc} corresponding to a {@link VarHandle}
* for a static field.
*
* @param name the unqualified name of the field
* @param declaringClass a {@link ClassDesc} describing the declaring class,
* for field var handles
* @param fieldType a {@link ClassDesc} describing the type of the field
* @return the {@linkplain VarHandleDesc}
* @throws NullPointerException if any of the arguments are null
* @jvms 4.2.2 Unqualified Names
*/
public static VarHandleDesc ofStaticField(ClassDesc declaringClass, String name, ClassDesc fieldType) {
Objects.requireNonNull(declaringClass);
Objects.requireNonNull(name);
Objects.requireNonNull(fieldType);
return new VarHandleDesc(Kind.STATIC_FIELD, name, declaringClass, fieldType);
}
/**
* Returns a {@linkplain VarHandleDesc} corresponding to a {@link VarHandle}
* for for an array type.
*
* @param arrayClass a {@link ClassDesc} describing the type of the array
* @return the {@linkplain VarHandleDesc}
* @throws NullPointerException if any of the arguments are null
*/
public static VarHandleDesc ofArray(ClassDesc arrayClass) {
Objects.requireNonNull(arrayClass);
if (!arrayClass.isArray())
throw new IllegalArgumentException("Array class argument not an array: " + arrayClass);
return new VarHandleDesc(Kind.ARRAY, ConstantDescs.DEFAULT_NAME, arrayClass, arrayClass.componentType());
}
/**
* Returns a {@link ClassDesc} describing the type of the variable described
* by this descriptor.
*
* @return the variable type
*/
public ClassDesc varType() {
return varType;
}
@Override
public VarHandle resolveConstantDesc(MethodHandles.Lookup lookup)
throws ReflectiveOperationException {
switch (kind) {
case FIELD:
return lookup.findVarHandle((Class<?>) declaringClass.resolveConstantDesc(lookup),
constantName(),
(Class<?>) varType.resolveConstantDesc(lookup));
case STATIC_FIELD:
return lookup.findStaticVarHandle((Class<?>) declaringClass.resolveConstantDesc(lookup),
constantName(),
(Class<?>) varType.resolveConstantDesc(lookup));
case ARRAY:
return MethodHandles.arrayElementVarHandle((Class<?>) declaringClass.resolveConstantDesc(lookup));
default:
throw new InternalError("Cannot reach here");
}
}
@Override
public String toString() {
switch (kind) {
case FIELD:
case STATIC_FIELD:
return String.format("VarHandleDesc[%s%s.%s:%s]",
(kind == Kind.STATIC_FIELD) ? "static " : "",
declaringClass.displayName(), constantName(), varType.displayName());
case ARRAY:
return String.format("VarHandleDesc[%s[]]", declaringClass.displayName());
default:
throw new InternalError("Cannot reach here");
}
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,6 +25,9 @@
package java.lang.invoke;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
final class VarHandles {
@ -144,6 +147,38 @@ final class VarHandles {
}
}
// Required by instance field handles
static Field getFieldFromReceiverAndOffset(Class<?> receiverType,
long offset,
Class<?> fieldType) {
for (Field f : receiverType.getDeclaredFields()) {
if (Modifier.isStatic(f.getModifiers())) continue;
if (offset == UNSAFE.objectFieldOffset(f)) {
assert f.getType() == fieldType;
return f;
}
}
throw new InternalError("Field not found at offset");
}
// Required by instance static field handles
static Field getStaticFieldFromBaseAndOffset(Object base,
long offset,
Class<?> fieldType) {
// @@@ This is a little fragile assuming the base is the class
Class<?> receiverType = (Class<?>) base;
for (Field f : receiverType.getDeclaredFields()) {
if (!Modifier.isStatic(f.getModifiers())) continue;
if (offset == UNSAFE.staticFieldOffset(f)) {
assert f.getType() == fieldType;
return f;
}
}
throw new InternalError("Static field not found at offset");
}
static VarHandle makeArrayElementHandle(Class<?> arrayClass) {
if (!arrayClass.isArray())
throw new IllegalArgumentException("not an array: " + arrayClass);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -27,7 +27,9 @@ package java.lang.invoke;
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
import java.lang.invoke.VarHandle.VarHandleDesc;
import java.util.Objects;
import java.util.Optional;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
@ -61,6 +63,30 @@ final class VarHandle$Type$s {
return accessMode.at.accessModeType(receiverType, {#if[Object]?fieldType:$type$.class});
}
@Override
final boolean internalEquals(VarHandle vh) {
FieldInstanceReadOnly that = (FieldInstanceReadOnly) vh;
return fieldOffset == that.fieldOffset;
}
@Override
final int internalHashCode() {
return Long.hashCode(fieldOffset);
}
@Override
public Optional<VarHandleDesc> describeConstable() {
var receiverTypeRef = receiverType.describeConstable();
var fieldTypeRef = {#if[Object]?fieldType:$type$.class}.describeConstable();
if (!receiverTypeRef.isPresent() || !fieldTypeRef.isPresent())
return Optional.empty();
// Reflect on this VarHandle to extract the field name
String name = VarHandles.getFieldFromReceiverAndOffset(
receiverType, fieldOffset, {#if[Object]?fieldType:$type$.class}).getName();
return Optional.of(VarHandleDesc.ofField(receiverTypeRef.get(), name, fieldTypeRef.get()));
}
@ForceInline
static $type$ get(FieldInstanceReadOnly handle, Object holder) {
return UNSAFE.get$Type$(Objects.requireNonNull(handle.receiverType.cast(holder)),
@ -323,6 +349,32 @@ final class VarHandle$Type$s {
#end[Object]
}
@Override
final boolean internalEquals(VarHandle vh) {
FieldStaticReadOnly that = (FieldStaticReadOnly) vh;
return base == that.base && fieldOffset == that.fieldOffset;
}
@Override
final int internalHashCode() {
return 31 * Long.hashCode(fieldOffset) + base.hashCode();
}
@Override
public Optional<VarHandleDesc> describeConstable() {
var fieldTypeRef = {#if[Object]?fieldType:$type$.class}.describeConstable();
if (!fieldTypeRef.isPresent())
return Optional.empty();
// Reflect on this VarHandle to extract the field name
var staticField = VarHandles.getStaticFieldFromBaseAndOffset(
base, fieldOffset, {#if[Object]?fieldType:$type$.class});
var receiverTypeRef = staticField.getDeclaringClass().describeConstable();
if (!receiverTypeRef.isPresent())
return Optional.empty();
return Optional.of(VarHandleDesc.ofStaticField(receiverTypeRef.get(), staticField.getName(), fieldTypeRef.get()));
}
@Override
final MethodType accessModeTypeUncached(AccessMode accessMode) {
return accessMode.at.accessModeType(null, {#if[Object]?fieldType:$type$.class});
@ -587,6 +639,29 @@ final class VarHandle$Type$s {
#end[Object]
}
@Override
final boolean internalEquals(VarHandle vh) {
// Equality of access mode types of AccessMode.GET is sufficient for
// equality checks
return true;
}
@Override
final int internalHashCode() {
// The hash code of the access mode types of AccessMode.GET is
// sufficient for hash code generation
return 0;
}
@Override
public Optional<VarHandleDesc> describeConstable() {
var arrayTypeRef = {#if[Object]?arrayType:$type$[].class}.describeConstable();
if (!arrayTypeRef.isPresent())
return Optional.empty();
return Optional.of(VarHandleDesc.ofArray(arrayTypeRef.get()));
}
@Override
final MethodType accessModeTypeUncached(AccessMode accessMode) {
return accessMode.at.accessModeType({#if[Object]?arrayType:$type$[].class}, {#if[Object]?arrayType.getComponentType():$type$.class}, int.class);

View file

@ -67,6 +67,17 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
super(form);
this.be = be;
}
@Override
final boolean internalEquals(VarHandle vh) {
ByteArrayViewVarHandle that = (ByteArrayViewVarHandle) vh;
return be == that.be;
}
@Override
final int internalHashCode() {
return Boolean.hashCode(be);
}
}
static final class ArrayHandle extends ByteArrayViewVarHandle {