| /* | |
| * Copyright (c) 2018, 2024, 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.util.Objects.requireNonNull; | |
| import static java.util.stream.Collectors.joining; | |
| import static jdk.internal.constant.ConstantUtils.EMPTY_CONSTANTDESC; | |
| import static jdk.internal.constant.ConstantUtils.validateMemberName; | |
| /** | |
| * 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(constantName, true); | |
| this.constantType = requireNonNull(constantType); | |
| this.bootstrapArgs = bootstrapArgs.length == 0 ? EMPTY_CONSTANTDESC : bootstrapArgs.clone(); | |
| } | |
| /** | |
| * 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 = 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)); | |
| } | |
| } |