1 /*
   2  * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package java.lang.constant;
  26 
  27 import java.lang.Enum.EnumDesc;
  28 import java.lang.invoke.MethodHandle;
  29 import java.lang.invoke.MethodHandles;
  30 import java.lang.invoke.VarHandle;
  31 import java.lang.invoke.VarHandle.VarHandleDesc;
  32 import java.util.Arrays;
  33 import java.util.List;
  34 import java.util.Map;
  35 import java.util.Objects;
  36 import java.util.Optional;
  37 import java.util.function.Function;
  38 import java.util.stream.Stream;
  39 
  40 import jdk.internal.lang.annotation.Foldable;
  41 
  42 import static java.lang.constant.ConstantDescs.CD_Class;
  43 import static java.lang.constant.ConstantDescs.CD_VarHandle;
  44 import static java.lang.constant.ConstantDescs.DEFAULT_NAME;
  45 import static java.lang.constant.ConstantUtils.EMPTY_CONSTANTDESC;
  46 import static java.lang.constant.ConstantUtils.validateMemberName;
  47 import static java.util.Objects.requireNonNull;
  48 import static java.util.stream.Collectors.joining;
  49 
  50 /**
  51  * A <a href="package-summary.html#nominal">nominal descriptor</a> for a
  52  * dynamic constant (one described in the constant pool with
  53  * {@code Constant_Dynamic_info}.)
  54  *
  55  * <p>Concrete subtypes of {@linkplain DynamicConstantDesc} must be
  56  * <a href="../doc-files/ValueBased.html">value-based</a>.
  57  *
  58  * @param <T> the type of the dynamic constant
  59  *
  60  * @since 12
  61  */
  62 public abstract class DynamicConstantDesc<T>
  63         implements ConstantDesc, Constable {
  64 
  65     private final DirectMethodHandleDesc bootstrapMethod;
  66     private final ConstantDesc[] bootstrapArgs;
  67     private final String constantName;
  68     private final ClassDesc constantType;
  69 
  70     private static final Map<MethodHandleDesc, Function<DynamicConstantDesc<?>, ConstantDesc>> canonicalMap
  71             = Map.ofEntries(Map.entry(ConstantDescs.BSM_PRIMITIVE_CLASS, DynamicConstantDesc::canonicalizePrimitiveClass),
  72                             Map.entry(ConstantDescs.BSM_ENUM_CONSTANT, DynamicConstantDesc::canonicalizeEnum),
  73                             Map.entry(ConstantDescs.BSM_NULL_CONSTANT, DynamicConstantDesc::canonicalizeNull),
  74                             Map.entry(ConstantDescs.BSM_VARHANDLE_STATIC_FIELD, DynamicConstantDesc::canonicalizeStaticFieldVarHandle),
  75                             Map.entry(ConstantDescs.BSM_VARHANDLE_FIELD, DynamicConstantDesc::canonicalizeFieldVarHandle),
  76                             Map.entry(ConstantDescs.BSM_VARHANDLE_ARRAY, DynamicConstantDesc::canonicalizeArrayVarHandle)
  77     );
  78 
  79     /**
  80      * Creates a nominal descriptor for a dynamic constant.
  81      *
  82      * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
  83      *                        bootstrap method for the constant
  84      * @param constantName The unqualified name that would appear in the {@code NameAndType}
  85      *                     operand of the {@code LDC} for this constant
  86      * @param constantType a {@link ClassDesc} describing the type
  87      *                     that would appear in the {@code NameAndType} operand
  88      *                     of the {@code LDC} for this constant
  89      * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
  90      *                      to the bootstrap, that would appear in the
  91      *                      {@code BootstrapMethods} attribute
  92      * @throws NullPointerException if any argument is null
  93      * @throws IllegalArgumentException if the {@code name} has the incorrect
  94      * format
  95      * @jvms 4.2.2 Unqualified Names
  96      */
  97     protected DynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod,
  98                                   String constantName,
  99                                   ClassDesc constantType,
 100                                   ConstantDesc... bootstrapArgs) {
 101         this.bootstrapMethod = requireNonNull(bootstrapMethod);
 102         this.constantName = validateMemberName(requireNonNull(constantName), true);
 103         this.constantType = requireNonNull(constantType);
 104         this.bootstrapArgs = requireNonNull(bootstrapArgs).clone();
 105 
 106         if (constantName.length() == 0)
 107             throw new IllegalArgumentException("Illegal invocation name: " + constantName);
 108     }
 109 
 110     /**
 111      * Returns a nominal descriptor for a dynamic constant, transforming it into
 112      * a more specific type if the constant bootstrap is a well-known one and a
 113      * more specific nominal descriptor type (e.g., ClassDesc) is available.
 114      *
 115      * <p>Classes whose {@link Constable#describeConstable()} method produce
 116      * a {@linkplain DynamicConstantDesc} with a well-known bootstrap including
 117      * {@link Class} (for instances describing primitive types), {@link Enum},
 118      * and {@link VarHandle}.
 119      *
 120      * <p>Bytecode-reading APIs that process the constant pool and wish to expose
 121      * entries as {@link ConstantDesc} to their callers should generally use this
 122      * method in preference to {@link #ofNamed(DirectMethodHandleDesc, String, ClassDesc, ConstantDesc...)}
 123      * because this may result in a more specific type that can be provided to
 124      * callers.
 125      *
 126      * @param <T> the type of the dynamic constant
 127      * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
 128      *                        bootstrap method for the constant
 129      * @param constantName The unqualified name that would appear in the {@code NameAndType}
 130      *                     operand of the {@code LDC} for this constant
 131      * @param constantType a {@link ClassDesc} describing the type
 132      *                     that would appear in the {@code NameAndType} operand
 133      *                     of the {@code LDC} for this constant
 134      * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
 135      *                      to the bootstrap, that would appear in the
 136      *                      {@code BootstrapMethods} attribute
 137      * @return the nominal descriptor
 138      * @throws NullPointerException if any argument is null
 139      * @throws IllegalArgumentException if the {@code name} has the incorrect
 140      * format
 141      * @jvms 4.2.2 Unqualified Names
 142      */
 143     @Foldable
 144     public static<T> ConstantDesc ofCanonical(DirectMethodHandleDesc bootstrapMethod,
 145                                               String constantName,
 146                                               ClassDesc constantType,
 147                                               ConstantDesc[] bootstrapArgs) {
 148         return DynamicConstantDesc.<T>ofNamed(bootstrapMethod, constantName, constantType, bootstrapArgs)
 149                 .tryCanonicalize();
 150     }
 151 
 152     /**
 153      * Returns a nominal descriptor for a dynamic constant.
 154      *
 155      * @param <T> the type of the dynamic constant
 156      * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
 157      *                        bootstrap method for the constant
 158      * @param constantName The unqualified name that would appear in the {@code NameAndType}
 159      *                     operand of the {@code LDC} for this constant
 160      * @param constantType a {@link ClassDesc} describing the type
 161      *                     that would appear in the {@code NameAndType} operand
 162      *                     of the {@code LDC} for this constant
 163      * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
 164      *                      to the bootstrap, that would appear in the
 165      *                      {@code BootstrapMethods} attribute
 166      * @return the nominal descriptor
 167      * @throws NullPointerException if any argument is null
 168      * @throws IllegalArgumentException if the {@code name} has the incorrect
 169      * format
 170      * @jvms 4.2.2 Unqualified Names
 171      */
 172     @Foldable
 173     public static<T> DynamicConstantDesc<T> ofNamed(DirectMethodHandleDesc bootstrapMethod,
 174                                                     String constantName,
 175                                                     ClassDesc constantType,
 176                                                     ConstantDesc... bootstrapArgs) {
 177         return new AnonymousDynamicConstantDesc<>(bootstrapMethod, constantName, constantType, bootstrapArgs);
 178     }
 179 
 180     /**
 181      * Returns a nominal descriptor for a dynamic constant whose name parameter
 182      * is {@link ConstantDescs#DEFAULT_NAME}, and whose type parameter is always
 183      * the same as the bootstrap method return type.
 184      *
 185      * @param <T> the type of the dynamic constant
 186      * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
 187      *                        bootstrap method for the constant
 188      * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
 189      *                      to the bootstrap, that would appear in the
 190      *                      {@code BootstrapMethods} attribute
 191      * @return the nominal descriptor
 192      * @throws NullPointerException if any argument is null
 193      * @jvms 4.2.2 Unqualified Names
 194      */
 195     @Foldable
 196     public static<T> DynamicConstantDesc<T> of(DirectMethodHandleDesc bootstrapMethod,
 197                                                ConstantDesc... bootstrapArgs) {
 198         return ofNamed(bootstrapMethod, DEFAULT_NAME, bootstrapMethod.invocationType().returnType(), bootstrapArgs);
 199     }
 200 
 201     /**
 202      * Returns a nominal descriptor for a dynamic constant whose bootstrap has
 203      * no static arguments, whose name parameter is {@link ConstantDescs#DEFAULT_NAME},
 204      * and whose type parameter is always the same as the bootstrap method return type.
 205      *
 206      * @param <T> the type of the dynamic constant
 207      * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
 208      *                        bootstrap method for the constant
 209      * @return the nominal descriptor
 210      * @throws NullPointerException if any argument is null
 211      */
 212     @Foldable
 213     public static<T> DynamicConstantDesc<T> of(DirectMethodHandleDesc bootstrapMethod) {
 214         return of(bootstrapMethod, EMPTY_CONSTANTDESC);
 215     }
 216 
 217     /**
 218      * Returns the name that would appear in the {@code NameAndType} operand
 219      * of the {@code LDC} for this constant.
 220      *
 221      * @return the constant name
 222      */
 223     @Foldable
 224     public String constantName() {
 225         return constantName;
 226     }
 227 
 228     /**
 229      * Returns a {@link ClassDesc} describing the type that would appear in the
 230      * {@code NameAndType} operand of the {@code LDC} for this constant.
 231      *
 232      * @return the constant type
 233      */
 234     @Foldable
 235     public ClassDesc constantType() {
 236         return constantType;
 237     }
 238 
 239     /**
 240      * Returns a {@link MethodHandleDesc} describing the bootstrap method for
 241      * this constant.
 242      *
 243      * @return the bootstrap method
 244      */
 245     @Foldable
 246     public DirectMethodHandleDesc bootstrapMethod() {
 247         return bootstrapMethod;
 248     }
 249 
 250     /**
 251      * Returns the bootstrap arguments for this constant.
 252      *
 253      * @return the bootstrap arguments
 254      */
 255     public ConstantDesc[] bootstrapArgs() {
 256         return bootstrapArgs.clone();
 257     }
 258 
 259     /**
 260      * Returns the bootstrap arguments for this constant as an immutable {@link List}.
 261      *
 262      * @return a {@link List} of the bootstrap arguments
 263      */
 264     public List<ConstantDesc> bootstrapArgsList() {
 265         return List.of(bootstrapArgs);
 266     }
 267 
 268     @SuppressWarnings("unchecked")
 269     public T resolveConstantDesc(MethodHandles.Lookup lookup) throws ReflectiveOperationException {
 270         try {
 271             MethodHandle bsm = (MethodHandle) bootstrapMethod.resolveConstantDesc(lookup);
 272             if (bsm.type().parameterCount() < 2 ||
 273                 !MethodHandles.Lookup.class.isAssignableFrom(bsm.type().parameterType(0))) {
 274                 throw new BootstrapMethodError(
 275                         "Invalid bootstrap method declared for resolving a dynamic constant: " + bootstrapMethod);
 276             }
 277             Object[] bsmArgs = new Object[3 + bootstrapArgs.length];
 278             bsmArgs[0] = lookup;
 279             bsmArgs[1] = constantName;
 280             bsmArgs[2] = constantType.resolveConstantDesc(lookup);
 281             for (int i = 0; i < bootstrapArgs.length; i++)
 282                 bsmArgs[3 + i] = bootstrapArgs[i].resolveConstantDesc(lookup);
 283 
 284             return (T) bsm.invokeWithArguments(bsmArgs);
 285         } catch (Error e) {
 286             throw e;
 287         } catch (Throwable t) {
 288             throw new BootstrapMethodError(t);
 289         }
 290     }
 291 
 292     private ConstantDesc tryCanonicalize() {
 293         Function<DynamicConstantDesc<?>, ConstantDesc> f = canonicalMap.get(bootstrapMethod);
 294         if (f != null) {
 295             try {
 296                 return f.apply(this);
 297             }
 298             catch (Throwable t) {
 299                 return this;
 300             }
 301         }
 302         return this;
 303     }
 304 
 305     private static ConstantDesc canonicalizeNull(DynamicConstantDesc<?> desc) {
 306         if (desc.bootstrapArgs.length != 0)
 307             return desc;
 308         return ConstantDescs.NULL;
 309     }
 310 
 311     private static ConstantDesc canonicalizeEnum(DynamicConstantDesc<?> desc) {
 312         if (desc.bootstrapArgs.length != 0
 313             || desc.constantName == null)
 314             return desc;
 315         return EnumDesc.of(desc.constantType, desc.constantName);
 316     }
 317 
 318     private static ConstantDesc canonicalizePrimitiveClass(DynamicConstantDesc<?> desc) {
 319         if (desc.bootstrapArgs.length != 0
 320             || !desc.constantType().equals(CD_Class)
 321             || desc.constantName == null)
 322             return desc;
 323         return ClassDesc.ofDescriptor(desc.constantName);
 324     }
 325 
 326     private static ConstantDesc canonicalizeStaticFieldVarHandle(DynamicConstantDesc<?> desc) {
 327         if (desc.bootstrapArgs.length != 2
 328                 || !desc.constantType().equals(CD_VarHandle))
 329             return desc;
 330         return VarHandleDesc.ofStaticField((ClassDesc) desc.bootstrapArgs[0],
 331                                      desc.constantName,
 332                                      (ClassDesc) desc.bootstrapArgs[1]);
 333     }
 334 
 335     private static ConstantDesc canonicalizeFieldVarHandle(DynamicConstantDesc<?> desc) {
 336         if (desc.bootstrapArgs.length != 2
 337             || !desc.constantType().equals(CD_VarHandle))
 338             return desc;
 339         return VarHandleDesc.ofField((ClassDesc) desc.bootstrapArgs[0],
 340                                      desc.constantName,
 341                                      (ClassDesc) desc.bootstrapArgs[1]);
 342     }
 343 
 344     private static ConstantDesc canonicalizeArrayVarHandle(DynamicConstantDesc<?> desc) {
 345         if (desc.bootstrapArgs.length != 1
 346             || !desc.constantType().equals(CD_VarHandle))
 347             return desc;
 348         return VarHandleDesc.ofArray((ClassDesc) desc.bootstrapArgs[0]);
 349     }
 350 
 351     // @@@ To eventually support in canonicalization: DCR with BSM=MHR_METHODHANDLEDESC_ASTYPE becomes AsTypeMHDesc
 352 
 353     /**
 354      * Compares the specified object with this descriptor for equality.  Returns
 355      * {@code true} if and only if the specified object is also a
 356      * {@linkplain DynamicConstantDesc}, and both descriptors have equal
 357      * bootstrap methods, bootstrap argument lists, constant name, and
 358      * constant type.
 359      *
 360      * @param o the {@code DynamicConstantDesc} to compare to this
 361      *       {@code DynamicConstantDesc}
 362      * @return {@code true} if the specified {@code DynamicConstantDesc} is
 363      *      equals to this {@code DynamicConstantDesc}.
 364      *
 365      */
 366     @Override
 367     public final boolean equals(Object o) {
 368         if (this == o) return true;
 369         if (!(o instanceof DynamicConstantDesc)) return false;
 370         DynamicConstantDesc<?> desc = (DynamicConstantDesc<?>) o;
 371         return Objects.equals(bootstrapMethod, desc.bootstrapMethod) &&
 372                Arrays.equals(bootstrapArgs, desc.bootstrapArgs) &&
 373                Objects.equals(constantName, desc.constantName) &&
 374                Objects.equals(constantType, desc.constantType);
 375     }
 376 
 377     @Override
 378     public final int hashCode() {
 379         int result = Objects.hash(bootstrapMethod, constantName, constantType);
 380         result = 31 * result + Arrays.hashCode(bootstrapArgs);
 381         return result;
 382     }
 383 
 384     /**
 385      * Returns a compact textual description of this constant description,
 386      * including the bootstrap method, the constant name and type, and
 387      * the static bootstrap arguments.
 388      *
 389      * @return A compact textual description of this call site descriptor
 390      */
 391     @Override
 392     public String toString() {
 393         return String.format("DynamicConstantDesc[%s::%s(%s%s)%s]",
 394                              bootstrapMethod.owner().displayName(),
 395                              bootstrapMethod.methodName(),
 396                              constantName.equals(ConstantDescs.DEFAULT_NAME) ? "" : constantName + "/",
 397                              Stream.of(bootstrapArgs).map(Object::toString).collect(joining(",")),
 398                              constantType.displayName());
 399     }
 400 
 401     private static class AnonymousDynamicConstantDesc<T> extends DynamicConstantDesc<T> {
 402         AnonymousDynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod, String constantName, ClassDesc constantType, ConstantDesc... bootstrapArgs) {
 403             super(bootstrapMethod, constantName, constantType, bootstrapArgs);
 404         }
 405 
 406         @Override
 407         public Optional<? extends ConstantDesc> describeConstable() {
 408             return Optional.empty();
 409         }
 410     }
 411 }