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.invoke.CallSite;
  28 import java.lang.invoke.MethodHandle;
  29 import java.lang.invoke.MethodHandles;
  30 import java.util.Arrays;
  31 import java.util.Objects;
  32 import java.util.stream.Stream;
  33 
  34 import static java.lang.constant.ConstantDescs.CD_String;
  35 import static java.lang.constant.ConstantUtils.EMPTY_CONSTANTDESC;
  36 import static java.lang.constant.ConstantUtils.validateMemberName;
  37 import static java.util.Objects.requireNonNull;
  38 import static java.util.stream.Collectors.joining;
  39 
  40 /**
  41  * A <a href="package-summary.html#nominal">nominal descriptor</a> for an
  42  * {@code invokedynamic} call site.
  43  *
  44  * <p>Concrete subtypes of {@linkplain DynamicCallSiteDesc} must be
  45  * <a href="../doc-files/ValueBased.html">value-based</a>.
  46  *
  47  * @since 12
  48  */
  49 public class DynamicCallSiteDesc {
  50 
  51     private final DirectMethodHandleDesc bootstrapMethod;
  52     private final ConstantDesc[] bootstrapArgs;
  53     private final String invocationName;
  54     private final MethodTypeDesc invocationType;
  55 
  56     /**
  57      * Creates a nominal descriptor for an {@code invokedynamic} call site.
  58      *
  59      * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
  60      *                        bootstrap method for the {@code invokedynamic}
  61      * @param invocationName The unqualified name that would appear in the {@code NameAndType}
  62      *                       operand of the {@code invokedynamic}
  63      * @param invocationType a {@link MethodTypeDesc} describing the invocation
  64      *                       type that would appear in the {@code NameAndType}
  65      *                       operand of the {@code invokedynamic}
  66      * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
  67      *                      to the bootstrap, that would appear in the
  68      *                      {@code BootstrapMethods} attribute
  69      * @throws NullPointerException if any parameter is null
  70      * @throws IllegalArgumentException if the invocation name has the incorrect
  71      * format
  72      * @jvms 4.2.2 Unqualified Names
  73      */
  74     private DynamicCallSiteDesc(DirectMethodHandleDesc bootstrapMethod,
  75                                 String invocationName,
  76                                 MethodTypeDesc invocationType,
  77                                 ConstantDesc[] bootstrapArgs) {
  78         this.invocationName = validateMemberName(requireNonNull(invocationName), true);
  79         this.invocationType = requireNonNull(invocationType);
  80         this.bootstrapMethod = requireNonNull(bootstrapMethod);
  81         this.bootstrapArgs = requireNonNull(bootstrapArgs.clone());
  82         if (invocationName.length() == 0)
  83             throw new IllegalArgumentException("Illegal invocation name: " + invocationName);
  84     }
  85 
  86     /**
  87      * Creates a nominal descriptor for an {@code invokedynamic} call site.
  88      *
  89      * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
  90      *                        bootstrap method for the {@code invokedynamic}
  91      * @param invocationName The unqualified name that would appear in the {@code NameAndType}
  92      *                       operand of the {@code invokedynamic}
  93      * @param invocationType a {@link MethodTypeDesc} describing the invocation
  94      *                       type that would appear in the {@code NameAndType}
  95      *                       operand of the {@code invokedynamic}
  96      * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
  97      *                      to the bootstrap, that would appear in the
  98      *                      {@code BootstrapMethods} attribute
  99      * @return the nominal descriptor
 100      * @throws NullPointerException if any parameter is null
 101      * @throws IllegalArgumentException if the invocation name has the incorrect
 102      * format
 103      * @jvms 4.2.2 Unqualified Names
 104      */
 105     public static DynamicCallSiteDesc of(DirectMethodHandleDesc bootstrapMethod,
 106                                          String invocationName,
 107                                          MethodTypeDesc invocationType,
 108                                          ConstantDesc... bootstrapArgs) {
 109         return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, bootstrapArgs);
 110     }
 111 
 112     /**
 113      * Creates a nominal descriptor for an {@code invokedynamic} call site whose
 114      * bootstrap method has no static arguments.
 115      *
 116      * @param bootstrapMethod The bootstrap method for the {@code invokedynamic}
 117      * @param invocationName The invocationName that would appear in the
 118      * {@code NameAndType} operand of the {@code invokedynamic}
 119      * @param invocationType The invocation invocationType that would appear
 120      * in the {@code NameAndType} operand of the {@code invokedynamic}
 121      * @return the nominal descriptor
 122      * @throws NullPointerException if any parameter is null
 123      * @throws IllegalArgumentException if the invocation name has the incorrect
 124      * format
 125      */
 126     public static DynamicCallSiteDesc of(DirectMethodHandleDesc bootstrapMethod,
 127                                          String invocationName,
 128                                          MethodTypeDesc invocationType) {
 129         return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, EMPTY_CONSTANTDESC);
 130     }
 131 
 132     /**
 133      * Creates a nominal descriptor for an {@code invokedynamic} call site whose
 134      * bootstrap method has no static arguments and for which the name parameter
 135      * is {@link ConstantDescs#DEFAULT_NAME}.
 136      *
 137      * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
 138      *                        bootstrap method for the {@code invokedynamic}
 139      * @param invocationType a {@link MethodTypeDesc} describing the invocation
 140      *                       type that would appear in the {@code NameAndType}
 141      *                       operand of the {@code invokedynamic}
 142      * @return the nominal descriptor
 143      * @throws NullPointerException if any parameter is null
 144      */
 145     public static DynamicCallSiteDesc of(DirectMethodHandleDesc bootstrapMethod,
 146                                          MethodTypeDesc invocationType) {
 147         return of(bootstrapMethod, ConstantDescs.DEFAULT_NAME, invocationType);
 148     }
 149 
 150     /**
 151      * Returns a nominal descriptor for an {@code invokedynamic} call site whose
 152      * bootstrap method, name, and invocation type are the same as this one, but
 153      * with the specified bootstrap arguments.
 154      *
 155      * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
 156      *                      to the bootstrap, that would appear in the
 157      *                      {@code BootstrapMethods} attribute
 158      * @return the nominal descriptor
 159      * @throws NullPointerException if any parameter is null
 160      */
 161     public DynamicCallSiteDesc withArgs(ConstantDesc... bootstrapArgs) {
 162         return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, bootstrapArgs);
 163     }
 164 
 165     /**
 166      * Returns a nominal descriptor for an {@code invokedynamic} call site whose
 167      * bootstrap and bootstrap arguments are the same as this one, but with the
 168      * specified invocationName and invocation invocationType
 169      *
 170      * @param invocationName The unqualified name that would appear in the {@code NameAndType}
 171      *                       operand of the {@code invokedynamic}
 172      * @param invocationType a {@link MethodTypeDesc} describing the invocation
 173      *                       type that would appear in the {@code NameAndType}
 174      *                       operand of the {@code invokedynamic}
 175      * @return the nominal descriptor
 176      * @throws NullPointerException if any parameter is null
 177      * @throws IllegalArgumentException if the invocation name has the incorrect
 178      * format
 179      * @jvms 4.2.2 Unqualified Names
 180      */
 181     public DynamicCallSiteDesc withNameAndType(String invocationName,
 182                                                MethodTypeDesc invocationType) {
 183         return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, bootstrapArgs);
 184     }
 185 
 186     /**
 187      * Returns the invocation name that would appear in the {@code NameAndType}
 188      * operand of the {@code invokedynamic}.
 189      *
 190      * @return the invocation name
 191      */
 192     public String invocationName() {
 193         return invocationName;
 194     }
 195 
 196     /**
 197      * Returns a {@link MethodTypeDesc} describing the invocation type that
 198      * would appear in the {@code NameAndType} operand of the {@code invokedynamic}.
 199      *
 200      * @return the invocation type
 201      */
 202     public MethodTypeDesc invocationType() {
 203         return invocationType;
 204     }
 205 
 206     /**
 207      * Returns a {@link MethodHandleDesc} describing the bootstrap method for
 208      * the {@code invokedynamic}.
 209      *
 210      * @return the bootstrap method for the {@code invokedynamic}
 211      */
 212     public MethodHandleDesc bootstrapMethod() { return bootstrapMethod; }
 213 
 214     /**
 215      * Returns {@link ConstantDesc}s describing the bootstrap arguments for the
 216      * {@code invokedynamic}. The returned array is always non-null. A zero
 217      * length array is returned if this {@linkplain DynamicCallSiteDesc} has no
 218      * bootstrap arguments.
 219      *
 220      * @return the bootstrap arguments for the {@code invokedynamic}
 221      */
 222     public ConstantDesc[] bootstrapArgs() { return bootstrapArgs.clone(); }
 223 
 224     /**
 225      * Reflectively invokes the bootstrap method with the specified arguments,
 226      * and return the resulting {@link CallSite}
 227      *
 228      * @param lookup The {@link MethodHandles.Lookup} used to resolve class names
 229      * @return the {@link CallSite}
 230      * @throws Throwable if any exception is thrown by the bootstrap method
 231      */
 232     public CallSite resolveCallSiteDesc(MethodHandles.Lookup lookup) throws Throwable {
 233         assert bootstrapMethod.invocationType().parameterType(1).equals(CD_String);
 234         MethodHandle bsm = (MethodHandle) bootstrapMethod.resolveConstantDesc(lookup);
 235         Object[] args = new Object[bootstrapArgs.length + 3];
 236         args[0] = lookup;
 237         args[1] = invocationName;
 238         args[2] = invocationType.resolveConstantDesc(lookup);
 239         System.arraycopy(bootstrapArgs, 0, args, 3, bootstrapArgs.length);
 240         return (CallSite) bsm.invokeWithArguments(args);
 241     }
 242 
 243     /**
 244      * Compares the specified object with this descriptor for equality.  Returns
 245      * {@code true} if and only if the specified object is also a
 246      * {@linkplain DynamicCallSiteDesc}, and both descriptors have equal
 247      * bootstrap methods, bootstrap argument lists, invocation name, and
 248      * invocation type.
 249      *
 250      * @param o the {@code DynamicCallSiteDesc} to compare to this
 251      *       {@code DynamicCallSiteDesc}
 252      * @return {@code true} if the specified {@code DynamicCallSiteDesc} is
 253      *      equal to this {@code DynamicCallSiteDesc}.
 254      */
 255     @Override
 256     public final boolean equals(Object o) {
 257         if (this == o) return true;
 258         if (o == null || getClass() != o.getClass()) return false;
 259         DynamicCallSiteDesc specifier = (DynamicCallSiteDesc) o;
 260         return Objects.equals(bootstrapMethod, specifier.bootstrapMethod) &&
 261                Arrays.equals(bootstrapArgs, specifier.bootstrapArgs) &&
 262                Objects.equals(invocationName, specifier.invocationName) &&
 263                Objects.equals(invocationType, specifier.invocationType);
 264     }
 265 
 266     @Override
 267     public final int hashCode() {
 268         int result = Objects.hash(bootstrapMethod, invocationName, invocationType);
 269         result = 31 * result + Arrays.hashCode(bootstrapArgs);
 270         return result;
 271     }
 272 
 273     /**
 274      * Returns a compact textual description of this call site description,
 275      * including the bootstrap method, the invocation name and type, and
 276      * the static bootstrap arguments.
 277      *
 278      * @return A compact textual description of this call site descriptor
 279      */
 280     @Override
 281     public String toString() {
 282         return String.format("DynamicCallSiteDesc[%s::%s(%s%s):%s]",
 283                              bootstrapMethod.owner().displayName(),
 284                              bootstrapMethod.methodName(),
 285                              invocationName.equals(ConstantDescs.DEFAULT_NAME) ? "" : invocationName + "/",
 286                              Stream.of(bootstrapArgs).map(Object::toString).collect(joining(",")),
 287                              invocationType.displayDescriptor());
 288     }
 289 }