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 */ DynamicCallSiteDesc(DirectMethodHandleDesc bootstrapMethod, String invocationName, MethodTypeDesc invocationType, ConstantDesc[] bootstrapArgs)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 */ of(DirectMethodHandleDesc bootstrapMethod, String invocationName, MethodTypeDesc invocationType, ConstantDesc... bootstrapArgs)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 */ of(DirectMethodHandleDesc bootstrapMethod, String invocationName, MethodTypeDesc invocationType)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 */ of(DirectMethodHandleDesc bootstrapMethod, MethodTypeDesc invocationType)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 */ withArgs(ConstantDesc... bootstrapArgs)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 */ withNameAndType(String invocationName, MethodTypeDesc invocationType)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 */ invocationName()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 */ invocationType()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 */ bootstrapMethod()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 */ bootstrapArgs()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 */ resolveCallSiteDesc(MethodHandles.Lookup lookup)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 * equals to this {@code DynamicCallSiteDesc}. 254 */ 255 @Override equals(Object o)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 hashCode()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 toString()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 } 290