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