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