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 jdk.internal.vm.annotation.Stable; 28 29 import java.lang.Enum.EnumDesc; 30 import java.lang.invoke.MethodHandle; 31 import java.lang.invoke.MethodHandles; 32 import java.lang.invoke.MethodHandles.Lookup; 33 import java.lang.invoke.VarHandle; 34 import java.lang.invoke.VarHandle.VarHandleDesc; 35 import java.lang.reflect.InvocationTargetException; 36 import java.security.AccessController; 37 import java.security.PrivilegedAction; 38 import java.util.Arrays; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.Objects; 42 import java.util.Optional; 43 import java.util.function.Function; 44 import java.util.stream.Stream; 45 46 import static java.lang.constant.ConstantDescs.CR_Class; 47 import static java.lang.constant.ConstantDescs.CR_VarHandle; 48 import static java.lang.constant.ConstantDescs.DEFAULT_NAME; 49 import static java.lang.constant.ConstantUtils.*; 50 import static java.lang.invoke.MethodHandles.lookup; 51 import static java.util.Objects.requireNonNull; 52 import static java.util.stream.Collectors.joining; 53 54 /** 55 * A <a href="package-summary.html#nominal">nominal descriptor</a> for a 56 * dynamic constant (one described in the constant pool with 57 * {@code Constant_Dynamic_info}.) 58 * 59 * <p>Concrete subtypes of {@linkplain DynamicConstantDesc} must be 60 * <a href="../doc-files/ValueBased.html">value-based</a>. 61 * 62 * @param <T> the type of the dynamic constant 63 */ 64 public abstract class DynamicConstantDesc<T> 65 implements ConstantDesc<T>, Constable<ConstantDesc<T>> { 66 67 private final DirectMethodHandleDesc bootstrapMethod; 68 private final ConstantDesc<?>[] bootstrapArgs; 69 private final String constantName; 70 private final ClassDesc constantType; 71 72 private static final Map<MethodHandleDesc, Function<DynamicConstantDesc<?>, ConstantDesc<?>>> canonicalMap 73 = Map.ofEntries(Map.entry(ConstantDescs.BSM_PRIMITIVE_CLASS, DynamicConstantDesc::canonicalizePrimitiveClass), 74 Map.entry(ConstantDescs.BSM_ENUM_CONSTANT, DynamicConstantDesc::canonicalizeEnum), 75 Map.entry(ConstantDescs.BSM_NULL_CONSTANT, DynamicConstantDesc::canonicalizeNull), 76 Map.entry(ConstantDescs.BSM_VARHANDLE_STATIC_FIELD, DynamicConstantDesc::canonicalizeStaticFieldVarHandle), 77 Map.entry(ConstantDescs.BSM_VARHANDLE_FIELD, DynamicConstantDesc::canonicalizeFieldVarHandle), 78 Map.entry(ConstantDescs.BSM_VARHANDLE_ARRAY, DynamicConstantDesc::canonicalizeArrayVarHandle) 79 ); 80 81 /** 82 * Create a nominal descriptor for a dynamic constant. 83 * 84 * @param bootstrapMethod a {@link DirectMethodHandleDescImpl} describing the 85 * bootstrap method for the constant 86 * @param constantName The name that would appear in the {@code NameAndType} 87 * operand of the {@code LDC} for this constant, as per 88 * JVMS 4.2.2 89 * @param constantType a {@link DirectMethodHandleDescImpl} describing the type 90 * that would appear in the {@code NameAndType} operand 91 * of the {@code LDC} for this constant 92 * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments 93 * to the bootstrap, that would appear in the 94 * {@code BootstrapMethods} attribute 95 * @throws NullPointerException if any argument is null 96 * @throws IllegalArgumentException if the {@code name} has the incorrect 97 * format 98 * @jvms 4.2.2 Unqualified Names 99 */ 100 protected DynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod, 101 String constantName, 102 ClassDesc constantType, 103 ConstantDesc<?>... bootstrapArgs) { 104 this.bootstrapMethod = validateBootstrapMethod(requireNonNull(bootstrapMethod), constantName); 105 this.constantName = validateMemberName(requireNonNull(constantName)); 106 this.constantType = requireNonNull(constantType); 107 this.bootstrapArgs = requireNonNull(bootstrapArgs).clone(); 108 109 if (constantName.length() == 0) 110 throw new IllegalArgumentException("Illegal invocation name: " + constantName); 111 } 112 113 /** 114 * Return a nominal descriptor for a dynamic constant, transforming it into 115 * a more specific type if the constant bootstrap is a well-known one and a 116 * more specific nominal descriptor type (e.g., ClassDesc) is available. 117 * 118 * <p>Classes whose {@link Constable#describeConstable()} method produces 119 * a {@linkplain DynamicConstantDesc} with a well-known bootstrap including 120 * {@link Class} (for instances describing primitive types), {@link Enum}, 121 * and {@link VarHandle}. 122 * 123 * <p>Bytecode-reading APIs that process the constant pool and wish to expose 124 * entries as {@link ConstantDesc} to their callers should generally use this 125 * method in preference to {@link #ofNamed(DirectMethodHandleDesc, String, ClassDesc, ConstantDesc...)} 126 * because this may result in a more specific type that can be provided to 127 * callers. 128 * 129 * @param <T> the type of the dynamic constant 130 * @param bootstrapMethod a {@link DirectMethodHandleDescImpl} describing the 131 * bootstrap method for the constant 132 * @param constantName The name that would appear in the {@code NameAndType} 133 * operand of the {@code LDC} for this constant, as per 134 * JVMS 4.2.2 135 * @param constantType a {@link DirectMethodHandleDescImpl} describing the type 136 * that would appear in the {@code NameAndType} operand 137 * of the {@code LDC} for this constant 138 * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments 139 * to the bootstrap, that would appear in the 140 * {@code BootstrapMethods} attribute 141 * @return the nominal descriptor 142 * @throws NullPointerException if any argument is null 143 * @throws IllegalArgumentException if the {@code name} has the incorrect 144 * format 145 * @jvms 4.2.2 Unqualified Names 146 */ 147 public static<T> ConstantDesc<T> ofCanonical(DirectMethodHandleDesc bootstrapMethod, 148 String constantName, 149 ClassDesc constantType, 150 ConstantDesc<?>[] bootstrapArgs) { 151 return DynamicConstantDesc.<T>ofNamed(bootstrapMethod, constantName, constantType, bootstrapArgs) 152 .tryCanonicalize(); 153 } 154 155 /** 156 * Return a nominal descriptor for a dynamic constant. 157 * 158 * @param <T> the type of the dynamic constant 159 * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the 160 * bootstrap method for the constant 161 * @param constantName The name that would appear in the {@code NameAndType} 162 * operand of the {@code LDC} for this constant, as per 163 * JVMS 4.2.2 164 * @param constantType a {@link ClassDesc} describing the type 165 * that would appear in the {@code NameAndType} operand 166 * of the {@code LDC} for this constant 167 * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments 168 * to the bootstrap, that would appear in the 169 * {@code BootstrapMethods} attribute 170 * @return the nominal descriptor 171 * @throws NullPointerException if any argument is null 172 * @throws IllegalArgumentException if the {@code name} has the incorrect 173 * format 174 * @jvms 4.2.2 Unqualified Names 175 */ 176 177 public static<T> DynamicConstantDesc<T> ofNamed(DirectMethodHandleDesc bootstrapMethod, 178 String constantName, 179 ClassDesc constantType, 180 ConstantDesc<?>... bootstrapArgs) { 181 return new AnonymousDynamicConstantDesc<>(bootstrapMethod, constantName, constantType, bootstrapArgs); 182 } 183 184 /** 185 * Return a nominal descriptor for a dynamic constant. 186 * 187 * @param <T> the type of the dynamic constant 188 * @param bootstrapMethod a {@link DirectMethodHandleDescImpl} describing the 189 * bootstrap method for the constant 190 * @param constantName The name that would appear in the {@code NameAndType} 191 * operand of the {@code LDC} for this constant, as per 192 * JVMS 4.2.2 193 * @param constantTypeDescriptor a field descriptor string for the type 194 * that would appear in the {@code NameAndType} operand 195 * of the {@code LDC} for this constant 196 * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments 197 * to the bootstrap, that would appear in the 198 * {@code BootstrapMethods} attribute 199 * @return the nominal descriptor 200 * @throws NullPointerException if any argument is null 201 * @throws IllegalArgumentException if the {@code name} has the incorrect 202 * format 203 * @jvms 4.2.2 Unqualified Names 204 */ 205 206 public static<T> DynamicConstantDesc<T> ofNamed(DirectMethodHandleDesc bootstrapMethod, 207 String constantName, 208 String constantTypeDescriptor, 209 ConstantDesc<?>... bootstrapArgs) { 210 return ofNamed(bootstrapMethod, constantName, ClassDesc.ofDescriptor(constantTypeDescriptor), bootstrapArgs); 211 } 212 213 /** 214 * Return a nominal descriptor for a dynamic constant whose name parameter 215 * is {@link ConstantDescs#DEFAULT_NAME}, and whose type parameter is always 216 * the same as the bootstrap method return type. 217 * 218 * @param <T> the type of the dynamic constant 219 * @param bootstrapMethod a {@link DirectMethodHandleDescImpl} describing the 220 * bootstrap method for the constant 221 * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments 222 * to the bootstrap, that would appear in the 223 * {@code BootstrapMethods} attribute 224 * @return the nominal descriptor 225 * @throws NullPointerException if any argument is null 226 * @throws IllegalArgumentException if the {@code name} has the incorrect 227 * format 228 * @jvms 4.2.2 Unqualified Names 229 */ 230 public static<T> DynamicConstantDesc<T> of(DirectMethodHandleDesc bootstrapMethod, 231 ConstantDesc<?>... bootstrapArgs) { 232 return ofNamed(bootstrapMethod, DEFAULT_NAME, bootstrapMethod.methodType().returnType(), bootstrapArgs); 233 } 234 235 /** 236 * Return a nominal descriptor for a dynamic constant whose name parameter 237 * is {@link ConstantDescs#INVOKE_NAME}, and whose type parameter is always 238 * the same as the bootstrap method return type. 239 * 240 * @param <T> the type of the dynamic constant 241 * @param bootstrapMethod a {@link DirectMethodHandleDescImpl} describing the 242 * bootstrap method for the constant 243 * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments 244 * to the bootstrap, that would appear in the 245 * {@code BootstrapMethods} attribute 246 * @return the nominal descriptor 247 * @throws NullPointerException if any argument is null 248 * @throws IllegalArgumentException if the {@code name} has the incorrect 249 * format 250 * @jvms 4.2.2 Unqualified Names 251 */ 252 public static<T> DynamicConstantDesc<T> ofInvoke(DirectMethodHandleDesc bootstrapMethod, 253 ConstantDesc<?>... bootstrapArgs) { 254 String constantName = ConstantDescs.INVOKE_NAME; 255 validateExpressionModeBootstrapMethod(bootstrapMethod, constantName); 256 return ofNamed(bootstrapMethod, constantName, bootstrapMethod.methodType().returnType(), bootstrapArgs); 257 } 258 259 /** 260 * Return a nominal descriptor for a dynamic constant whose name parameter 261 * is {@link ConstantDescs#SYMBOLIC_NAME}, and whose type parameter is always 262 * the same as the bootstrap method return type. The effect of resolving 263 * this dynamic constant will be to invoke the bootstrap method, without 264 * metadata, and without resolving any static argument whose corresponding 265 * bootstrap method parameter type is either {@code Object}, {@code ConstantDesc}, 266 * or a subtype of {@code ConstantDesc}. Other bootstrap method parameter 267 * types, such as {@code String} or {@code MethodHandle}, receive resolved 268 * static arguments, as usual. 269 * 270 * @param <T> the type of the dynamic constant 271 * @param bootstrapMethod a {@link DirectMethodHandleDescImpl} describing the 272 * bootstrap method which will produce for the constant 273 * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments 274 * to the bootstrap, that would appear in the 275 * {@code BootstrapMethods} attribute 276 * @return the nominal descriptor 277 * @throws NullPointerException if any argument is null 278 * @throws IllegalArgumentException if the {@code name} has the incorrect 279 * format 280 * @jvms 4.2.2 Unqualified Names 281 */ 282 public static<T> DynamicConstantDesc<T> ofSymbolicExpression(DirectMethodHandleDesc bootstrapMethod, 283 ConstantDesc<?>... bootstrapArgs) { 284 String constantName = ConstantDescs.SYMBOLIC_NAME; 285 validateExpressionModeBootstrapMethod(bootstrapMethod, constantName); 286 return ofNamed(bootstrapMethod, constantName, bootstrapMethod.methodType().returnType(), bootstrapArgs); 287 } 288 289 /** 290 * Return a nominal descriptor for a dynamic constant whose name parameter 291 * is {@link ConstantDescs#SYMBOLIC_NAME}, whose type parameter is 292 * {@code ConstantDesc}, and whose bootstrap method is 293 * {@link ConstantBootstraps#constantDesc}. The effect of resolving 294 * this dynamic constant will be to return a {@code ConstantDesc} 295 * that represents the sole static argument, without resolving it. 296 * 297 * @param <T> the type of the constant to be described 298 * @param constantDesc the constant to be described 299 * @return the nominal descriptor, which when resolved produces the constant, as a {@code ConstantDesc} 300 * @throws NullPointerException if any argument is null 301 * @throws IllegalArgumentException if the {@code name} has the incorrect 302 * format 303 * @jvms 4.2.2 Unqualified Names 304 */ 305 public static<T extends ConstantDesc<?>> DynamicConstantDesc<T> ofSymbolic(T constantDesc) { 306 return ofSymbolicExpression(ConstantDescs.MHR_CONSTANTDESC_IDENTITY, constantDesc); 307 } 308 309 /** 310 * Return a nominal descriptor for a dynamic constant whose bootstrap has 311 * no static arguments, whose name parameter is {@link ConstantDescs#DEFAULT_NAME}, 312 * and whose type parameter is always the same as the bootstrap method return type. 313 * 314 * @param <T> the type of the dynamic constant 315 * @param bootstrapMethod a {@link DirectMethodHandleDescImpl} describing the 316 * bootstrap method for the constant 317 * @return the nominal descriptor 318 * @throws NullPointerException if any argument is null 319 * @throws IllegalArgumentException if the {@code name} has the incorrect 320 * format 321 */ 322 public static<T> DynamicConstantDesc<T> of(DirectMethodHandleDesc bootstrapMethod) { 323 return of(bootstrapMethod, EMPTY_CONSTANTDESC); 324 } 325 326 /** 327 * Returns The name that would appear in the {@code NameAndType} operand 328 * of the {@code LDC} for this constant 329 * 330 * @return the constant name 331 */ 332 public String constantName() { 333 return constantName; 334 } 335 336 /** 337 * Returns a {@link ClassDesc} describing the type that would appear in the 338 * {@code NameAndType} operand of the {@code LDC} for this constant. 339 * 340 * @return the constant type 341 */ 342 public ClassDesc constantType() { 343 return constantType; 344 } 345 346 /** 347 * Returns a {@link MethodHandleDesc} describing the bootstrap method for 348 * this constant 349 * 350 * @return the bootstrap method 351 */ 352 public DirectMethodHandleDesc bootstrapMethod() { 353 return bootstrapMethod; 354 } 355 356 /** 357 * Returns the bootstrap arguments for this constant 358 * @return the bootstrap arguments 359 */ 360 public ConstantDesc<?>[] bootstrapArgs() { 361 return bootstrapArgs.clone(); 362 } 363 364 /** 365 * Returns the bootstrap arguments for this constant as a {@link List} 366 * 367 * @return a {@link List} of the bootstrap arguments, described as {@link ConstantDesc} 368 */ 369 public List<ConstantDesc<?>> bootstrapArgsList() { 370 return List.of(bootstrapArgs); 371 } 372 373 public T resolveConstantDesc(Lookup lookup) throws ReflectiveOperationException { 374 try { 375 Object rawResult = MH_resolveDynamicConstant().invokeExact(this, lookup); 376 @SuppressWarnings("unchecked") 377 T result = (T) rawResult; 378 return result; 379 } catch (ReflectiveOperationException|RuntimeException ex) { 380 throw ex; 381 } catch (Throwable ex) { 382 throw new InvocationTargetException(ex); 383 } 384 } 385 386 private static @Stable MethodHandle MH_resolveDynamicConstant; 387 private static MethodHandle MH_resolveDynamicConstant() { 388 MethodHandle mh = MH_resolveDynamicConstant; 389 if (mh != null) return mh; 390 // tunnel through access controls to get to BSCI::ofConstantDesc 391 mh = AccessController.doPrivileged(new PrivilegedAction<MethodHandle>() { 392 @Override 393 public MethodHandle run() { 394 try { 395 var bsci = Class.forName("java.lang.invoke.BootstrapMethodInvoker"); 396 var meth = bsci.getDeclaredMethod("resolveDynamicConstant", 397 DynamicConstantDesc.class, Lookup.class); 398 meth.setAccessible(true); 399 return lookup().unreflect(meth); 400 } catch (ReflectiveOperationException ex) { 401 throw new InternalError(ex); 402 } 403 } 404 }); 405 return mh; 406 } 407 408 @Override 409 public Optional<? extends ConstantDesc<ConstantDesc<T>>> describeConstable() { 410 return Optional.of(DynamicConstantDesc.ofSymbolic(this)); 411 } 412 413 private ConstantDesc<T> tryCanonicalize() { 414 Function<DynamicConstantDesc<?>, ConstantDesc<?>> f = canonicalMap.get(bootstrapMethod); 415 if (f != null) { 416 try { 417 @SuppressWarnings("unchecked") 418 ConstantDesc<T> converted = (ConstantDesc<T>) f.apply(this); 419 return converted; 420 } 421 catch (Throwable t) { 422 return this; 423 } 424 } 425 return this; 426 } 427 428 private static ConstantDesc<?> canonicalizeNull(DynamicConstantDesc<?> desc) { 429 if (desc.bootstrapArgs.length != 0) 430 return desc; 431 return ConstantDescs.NULL; 432 } 433 434 private static ConstantDesc<?> canonicalizeEnum(DynamicConstantDesc<?> desc) { 435 if (desc.bootstrapArgs.length != 0 436 || desc.constantName == null) 437 return desc; 438 return EnumDesc.of(desc.constantType, desc.constantName); 439 } 440 441 private static ConstantDesc<?> canonicalizePrimitiveClass(DynamicConstantDesc<?> desc) { 442 if (desc.bootstrapArgs.length != 0 443 || !desc.constantType().equals(CR_Class) 444 || desc.constantName == null) 445 return desc; 446 return ClassDesc.ofDescriptor(desc.constantName); 447 } 448 449 private static ConstantDesc<?> canonicalizeStaticFieldVarHandle(DynamicConstantDesc<?> desc) { 450 if (desc.bootstrapArgs.length != 3 451 || !desc.constantType().equals(CR_VarHandle)) 452 return desc; 453 return VarHandleDesc.ofStaticField((ClassDesc) desc.bootstrapArgs[0], 454 (String) desc.bootstrapArgs[1], 455 (ClassDesc) desc.bootstrapArgs[2]); 456 } 457 458 private static ConstantDesc<?> canonicalizeFieldVarHandle(DynamicConstantDesc<?> desc) { 459 if (desc.bootstrapArgs.length != 3 460 || !desc.constantType().equals(CR_VarHandle)) 461 return desc; 462 return VarHandleDesc.ofField((ClassDesc) desc.bootstrapArgs[0], 463 (String) desc.bootstrapArgs[1], 464 (ClassDesc) desc.bootstrapArgs[2]); 465 } 466 467 private static ConstantDesc<?> canonicalizeArrayVarHandle(DynamicConstantDesc<?> desc) { 468 if (desc.bootstrapArgs.length != 1 469 || !desc.constantType().equals(CR_VarHandle)) 470 return desc; 471 return VarHandleDesc.ofArray((ClassDesc) desc.bootstrapArgs[0]); 472 } 473 474 // @@@ To eventually support in canonicalization: DCR with BSM=MHR_METHODHANDLEDESC_ASTYPE becomes AsTypeMHDesc 475 476 @Override 477 public final boolean equals(Object o) { 478 if (this == o) return true; 479 if (!(o instanceof DynamicConstantDesc)) return false; 480 DynamicConstantDesc<?> desc = (DynamicConstantDesc<?>) o; 481 return Objects.equals(bootstrapMethod, desc.bootstrapMethod) && 482 Arrays.equals(bootstrapArgs, desc.bootstrapArgs) && 483 Objects.equals(constantName, desc.constantName) && 484 Objects.equals(constantType, desc.constantType); 485 } 486 487 @Override 488 public final int hashCode() { 489 int result = Objects.hash(bootstrapMethod, constantName, constantType); 490 result = 31 * result + Arrays.hashCode(bootstrapArgs); 491 return result; 492 } 493 494 @Override 495 public String toString() { 496 return String.format("DynamicConstantDesc[%s::%s(%s%s)%s]", 497 bootstrapMethod.owner().displayName(), 498 bootstrapMethod.methodName(), 499 constantName.equals(ConstantDescs.DEFAULT_NAME) ? "" : constantName + "/", 500 Stream.of(bootstrapArgs).map(Object::toString).collect(joining(",")), 501 constantType.displayName()); 502 } 503 504 private static class AnonymousDynamicConstantDesc<T> extends DynamicConstantDesc<T> { 505 AnonymousDynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod, String constantName, ClassDesc constantType, ConstantDesc<?>... bootstrapArgs) { 506 super(bootstrapMethod, constantName, constantType, bootstrapArgs); 507 } 508 } 509 }