/*
 * Copyright (c) 2018, 2019, 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 nominal descriptor for a dynamic constant (one described in the constant pool with Constant_Dynamic_info.)

Concrete subtypes of DynamicConstantDesc must be value-based.

Type parameters:
  • <T> – the type of the dynamic constant
Since:12
/** * 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.
Params:
  • bootstrapMethod – a DirectMethodHandleDesc describing the bootstrap method for the constant
  • constantName – The unqualified name that would appear in the NameAndType operand of the LDC for this constant
  • constantType – a ClassDesc describing the type that would appear in the NameAndType operand of the LDC for this constant
  • bootstrapArgs – ConstantDescs describing the static arguments to the bootstrap, that would appear in the BootstrapMethods attribute
Throws:
@jvms4.2.2 Unqualified Names
/** * 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.

Classes whose Constable.describeConstable() method produce a DynamicConstantDesc with a well-known bootstrap including Class (for instances describing primitive types), Enum, and VarHandle.

Bytecode-reading APIs that process the constant pool and wish to expose entries as ConstantDesc to their callers should generally use this method in preference to ofNamed(DirectMethodHandleDesc, String, ClassDesc, ConstantDesc...) because this may result in a more specific type that can be provided to callers.

Params:
  • bootstrapMethod – a DirectMethodHandleDesc describing the bootstrap method for the constant
  • constantName – The unqualified name that would appear in the NameAndType operand of the LDC for this constant
  • constantType – a ClassDesc describing the type that would appear in the NameAndType operand of the LDC for this constant
  • bootstrapArgs – ConstantDescs describing the static arguments to the bootstrap, that would appear in the BootstrapMethods attribute
Type parameters:
  • <T> – the type of the dynamic constant
Throws:
Returns:the nominal descriptor
@jvms4.2.2 Unqualified Names
/** * 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.
Params:
  • bootstrapMethod – a DirectMethodHandleDesc describing the bootstrap method for the constant
  • constantName – The unqualified name that would appear in the NameAndType operand of the LDC for this constant
  • constantType – a ClassDesc describing the type that would appear in the NameAndType operand of the LDC for this constant
  • bootstrapArgs – ConstantDescs describing the static arguments to the bootstrap, that would appear in the BootstrapMethods attribute
Type parameters:
  • <T> – the type of the dynamic constant
Throws:
Returns:the nominal descriptor
@jvms4.2.2 Unqualified Names
/** * 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 ConstantDescs.DEFAULT_NAME, and whose type parameter is always the same as the bootstrap method return type.
Params:
  • bootstrapMethod – a DirectMethodHandleDesc describing the bootstrap method for the constant
  • bootstrapArgs – ConstantDescs describing the static arguments to the bootstrap, that would appear in the BootstrapMethods attribute
Type parameters:
  • <T> – the type of the dynamic constant
Throws:
Returns:the nominal descriptor
@jvms4.2.2 Unqualified Names
/** * 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 ConstantDescs.DEFAULT_NAME, and whose type parameter is always the same as the bootstrap method return type.
Params:
Type parameters:
  • <T> – the type of the dynamic constant
Throws:
Returns:the nominal descriptor
/** * 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 NameAndType operand of the LDC for this constant.
Returns:the constant name
/** * 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 ClassDesc describing the type that would appear in the NameAndType operand of the LDC for this constant.
Returns:the constant type
/** * 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 MethodHandleDesc describing the bootstrap method for this constant.
Returns:the bootstrap method
/** * 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.
Returns:the bootstrap arguments
/** * 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 List.
Returns:a List of the bootstrap arguments
/** * 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 true if and only if the specified object is also a DynamicConstantDesc, and both descriptors have equal bootstrap methods, bootstrap argument lists, constant name, and constant type.
Params:
  • o – the DynamicConstantDesc to compare to this DynamicConstantDesc
Returns:true if the specified DynamicConstantDesc is equals to this DynamicConstantDesc.
/** * 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.
Returns:A compact textual description of this call site descriptor
/** * 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); } } }