| /* |
| * 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)); |
| } |
| } |