1 /* 2 * Copyright (c) 2018, 2020, 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} should be immutable 45 * and their behavior should not rely on object identity. 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 or its contents are {@code 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 for (int i = 0; i < this.bootstrapArgs.length; i++) { 83 requireNonNull(this.bootstrapArgs[i]); 84 } 85 if (invocationName.length() == 0) 86 throw new IllegalArgumentException("Illegal invocation name: " + invocationName); 87 } 88 89 /** 90 * Creates a nominal descriptor for an {@code invokedynamic} call site. 91 * 92 * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the 93 * bootstrap method for the {@code invokedynamic} 94 * @param invocationName The unqualified name that would appear in the {@code NameAndType} 95 * operand of the {@code invokedynamic} 96 * @param invocationType a {@link MethodTypeDesc} describing the invocation 97 * type that would appear in the {@code NameAndType} 98 * operand of the {@code invokedynamic} 99 * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments 100 * to the bootstrap, that would appear in the 101 * {@code BootstrapMethods} attribute 102 * @return the nominal descriptor 103 * @throws NullPointerException if any parameter or its contents are {@code null} 104 * @throws IllegalArgumentException if the invocation name has the incorrect 105 * format 106 * @jvms 4.2.2 Unqualified Names 107 */ of(DirectMethodHandleDesc bootstrapMethod, String invocationName, MethodTypeDesc invocationType, ConstantDesc... bootstrapArgs)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 */ of(DirectMethodHandleDesc bootstrapMethod, String invocationName, MethodTypeDesc invocationType)129 public static DynamicCallSiteDesc of(DirectMethodHandleDesc bootstrapMethod, 130 String invocationName, 131 MethodTypeDesc invocationType) { 132 return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, EMPTY_CONSTANTDESC); 133 } 134 135 /** 136 * Creates a nominal descriptor for an {@code invokedynamic} call site whose 137 * bootstrap method has no static arguments and for which the name parameter 138 * is {@link ConstantDescs#DEFAULT_NAME}. 139 * 140 * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the 141 * bootstrap method for the {@code invokedynamic} 142 * @param invocationType a {@link MethodTypeDesc} describing the invocation 143 * type that would appear in the {@code NameAndType} 144 * operand of the {@code invokedynamic} 145 * @return the nominal descriptor 146 * @throws NullPointerException if any parameter is null 147 */ of(DirectMethodHandleDesc bootstrapMethod, MethodTypeDesc invocationType)148 public static DynamicCallSiteDesc of(DirectMethodHandleDesc bootstrapMethod, 149 MethodTypeDesc invocationType) { 150 return of(bootstrapMethod, ConstantDescs.DEFAULT_NAME, invocationType); 151 } 152 153 /** 154 * Returns a nominal descriptor for an {@code invokedynamic} call site whose 155 * bootstrap method, name, and invocation type are the same as this one, but 156 * with the specified bootstrap arguments. 157 * 158 * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments 159 * to the bootstrap, that would appear in the 160 * {@code BootstrapMethods} attribute 161 * @return the nominal descriptor 162 * @throws NullPointerException if the argument or its contents are {@code null} 163 */ withArgs(ConstantDesc... bootstrapArgs)164 public DynamicCallSiteDesc withArgs(ConstantDesc... bootstrapArgs) { 165 return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, bootstrapArgs); 166 } 167 168 /** 169 * Returns a nominal descriptor for an {@code invokedynamic} call site whose 170 * bootstrap and bootstrap arguments are the same as this one, but with the 171 * specified invocationName and invocation invocationType 172 * 173 * @param invocationName The unqualified name that would appear in the {@code NameAndType} 174 * operand of the {@code invokedynamic} 175 * @param invocationType a {@link MethodTypeDesc} describing the invocation 176 * type that would appear in the {@code NameAndType} 177 * operand of the {@code invokedynamic} 178 * @return the nominal descriptor 179 * @throws NullPointerException if any parameter is null 180 * @throws IllegalArgumentException if the invocation name has the incorrect 181 * format 182 * @jvms 4.2.2 Unqualified Names 183 */ withNameAndType(String invocationName, MethodTypeDesc invocationType)184 public DynamicCallSiteDesc withNameAndType(String invocationName, 185 MethodTypeDesc invocationType) { 186 return new DynamicCallSiteDesc(bootstrapMethod, invocationName, invocationType, bootstrapArgs); 187 } 188 189 /** 190 * Returns the invocation name that would appear in the {@code NameAndType} 191 * operand of the {@code invokedynamic}. 192 * 193 * @return the invocation name 194 */ invocationName()195 public String invocationName() { 196 return invocationName; 197 } 198 199 /** 200 * Returns a {@link MethodTypeDesc} describing the invocation type that 201 * would appear in the {@code NameAndType} operand of the {@code invokedynamic}. 202 * 203 * @return the invocation type 204 */ invocationType()205 public MethodTypeDesc invocationType() { 206 return invocationType; 207 } 208 209 /** 210 * Returns a {@link MethodHandleDesc} describing the bootstrap method for 211 * the {@code invokedynamic}. 212 * 213 * @return the bootstrap method for the {@code invokedynamic} 214 */ bootstrapMethod()215 public MethodHandleDesc bootstrapMethod() { return bootstrapMethod; } 216 217 /** 218 * Returns {@link ConstantDesc}s describing the bootstrap arguments for the 219 * {@code invokedynamic}. The returned array is always non-null. A zero 220 * length array is returned if this {@linkplain DynamicCallSiteDesc} has no 221 * bootstrap arguments. 222 * 223 * @return the bootstrap arguments for the {@code invokedynamic} 224 */ bootstrapArgs()225 public ConstantDesc[] bootstrapArgs() { return bootstrapArgs.clone(); } 226 227 /** 228 * Reflectively invokes the bootstrap method with the specified arguments, 229 * and return the resulting {@link CallSite} 230 * 231 * @param lookup The {@link MethodHandles.Lookup} used to resolve class names 232 * @return the {@link CallSite} 233 * @throws Throwable if any exception is thrown by the bootstrap method 234 */ resolveCallSiteDesc(MethodHandles.Lookup lookup)235 public CallSite resolveCallSiteDesc(MethodHandles.Lookup lookup) throws Throwable { 236 assert bootstrapMethod.invocationType().parameterType(1).equals(CD_String); 237 MethodHandle bsm = (MethodHandle) bootstrapMethod.resolveConstantDesc(lookup); 238 Object[] args = new Object[bootstrapArgs.length + 3]; 239 args[0] = lookup; 240 args[1] = invocationName; 241 args[2] = invocationType.resolveConstantDesc(lookup); 242 System.arraycopy(bootstrapArgs, 0, args, 3, bootstrapArgs.length); 243 return (CallSite) bsm.invokeWithArguments(args); 244 } 245 246 /** 247 * Compares the specified object with this descriptor for equality. Returns 248 * {@code true} if and only if the specified object is also a 249 * {@linkplain DynamicCallSiteDesc}, and both descriptors have equal 250 * bootstrap methods, bootstrap argument lists, invocation name, and 251 * invocation type. 252 * 253 * @param o the {@code DynamicCallSiteDesc} to compare to this 254 * {@code DynamicCallSiteDesc} 255 * @return {@code true} if the specified {@code DynamicCallSiteDesc} 256 * is equal to this {@code DynamicCallSiteDesc}. 257 */ 258 @Override equals(Object o)259 public final boolean equals(Object o) { 260 if (this == o) return true; 261 if (o == null || getClass() != o.getClass()) return false; 262 DynamicCallSiteDesc specifier = (DynamicCallSiteDesc) o; 263 return Objects.equals(bootstrapMethod, specifier.bootstrapMethod) && 264 Arrays.equals(bootstrapArgs, specifier.bootstrapArgs) && 265 Objects.equals(invocationName, specifier.invocationName) && 266 Objects.equals(invocationType, specifier.invocationType); 267 } 268 269 @Override hashCode()270 public final int hashCode() { 271 int result = Objects.hash(bootstrapMethod, invocationName, invocationType); 272 result = 31 * result + Arrays.hashCode(bootstrapArgs); 273 return result; 274 } 275 276 /** 277 * Returns a compact textual description of this call site description, 278 * including the bootstrap method, the invocation name and type, and 279 * the static bootstrap arguments. 280 * 281 * @return A compact textual description of this call site descriptor 282 */ 283 @Override toString()284 public String toString() { 285 return String.format("DynamicCallSiteDesc[%s::%s(%s%s):%s]", 286 bootstrapMethod.owner().displayName(), 287 bootstrapMethod.methodName(), 288 invocationName.equals(ConstantDescs.DEFAULT_NAME) ? "" : invocationName + "/", 289 Stream.of(bootstrapArgs).map(Object::toString).collect(joining(",")), 290 invocationType.displayDescriptor()); 291 } 292 } 293