401 lines
18 KiB
Java
401 lines
18 KiB
Java
![]() |
/*
|
||
|
* Copyright (c) 2018, 2021, 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} should be immutable
|
||
|
* and their behavior should not rely on object identity.
|
||
|
*
|
||
|
* @param <T> the type of the dynamic constant
|
||
|
*
|
||
|
* @since 12
|
||
|
*/
|
||
|
public abstract non-sealed class DynamicConstantDesc<T>
|
||
|
implements ConstantDesc {
|
||
|
|
||
|
private final DirectMethodHandleDesc bootstrapMethod;
|
||
|
private final ConstantDesc[] bootstrapArgs;
|
||
|
private final String constantName;
|
||
|
private final ClassDesc constantType;
|
||
|
|
||
|
/**
|
||
|
* 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), true);
|
||
|
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
|
||
|
*/
|
||
|
// Do not call this method from the static initialization of java.lang.constant.ConstantDescs
|
||
|
// since that can lead to potential deadlock during multi-threaded concurrent execution
|
||
|
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 = CanonicalMapHolder.CANONICAL_MAP.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 equal to this {@code DynamicConstantDesc}.
|
||
|
*
|
||
|
*/
|
||
|
@Override
|
||
|
public final boolean equals(Object o) {
|
||
|
if (this == o) return true;
|
||
|
return (o instanceof DynamicConstantDesc<?> desc)
|
||
|
&& 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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static final class CanonicalMapHolder {
|
||
|
static final Map<MethodHandleDesc, Function<DynamicConstantDesc<?>, ConstantDesc>> CANONICAL_MAP =
|
||
|
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));
|
||
|
}
|
||
|
}
|