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