1 /* 2 * Copyright (c) 2018, 2021, 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.Enum.EnumDesc; 28 import java.lang.invoke.MethodHandle; 29 import java.lang.invoke.MethodHandles; 30 import java.lang.invoke.VarHandle; 31 import java.lang.invoke.VarHandle.VarHandleDesc; 32 import java.util.Arrays; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Objects; 36 import java.util.function.Function; 37 import java.util.stream.Stream; 38 39 import static java.lang.constant.ConstantDescs.CD_Class; 40 import static java.lang.constant.ConstantDescs.CD_VarHandle; 41 import static java.lang.constant.ConstantDescs.DEFAULT_NAME; 42 import static java.lang.constant.ConstantUtils.EMPTY_CONSTANTDESC; 43 import static java.lang.constant.ConstantUtils.validateMemberName; 44 import static java.util.Objects.requireNonNull; 45 import static java.util.stream.Collectors.joining; 46 47 /** 48 * A <a href="package-summary.html#nominal">nominal descriptor</a> for a 49 * dynamic constant (one described in the constant pool with 50 * {@code Constant_Dynamic_info}.) 51 * 52 * <p>Concrete subtypes of {@linkplain DynamicConstantDesc} should be immutable 53 * and their behavior should not rely on object identity. 54 * 55 * @param <T> the type of the dynamic constant 56 * 57 * @since 12 58 */ 59 public abstract non-sealed class DynamicConstantDesc<T> 60 implements ConstantDesc { 61 62 private final DirectMethodHandleDesc bootstrapMethod; 63 private final ConstantDesc[] bootstrapArgs; 64 private final String constantName; 65 private final ClassDesc constantType; 66 67 /** 68 * Creates a nominal descriptor for a dynamic constant. 69 * 70 * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the 71 * bootstrap method for the constant 72 * @param constantName The unqualified name that would appear in the {@code NameAndType} 73 * operand of the {@code LDC} for this constant 74 * @param constantType a {@link ClassDesc} describing the type 75 * that would appear in the {@code NameAndType} operand 76 * of the {@code LDC} for this constant 77 * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments 78 * to the bootstrap, that would appear in the 79 * {@code BootstrapMethods} attribute 80 * @throws NullPointerException if any argument is null 81 * @throws IllegalArgumentException if the {@code name} has the incorrect 82 * format 83 * @jvms 4.2.2 Unqualified Names 84 */ DynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod, String constantName, ClassDesc constantType, ConstantDesc... bootstrapArgs)85 protected DynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod, 86 String constantName, 87 ClassDesc constantType, 88 ConstantDesc... bootstrapArgs) { 89 this.bootstrapMethod = requireNonNull(bootstrapMethod); 90 this.constantName = validateMemberName(requireNonNull(constantName), true); 91 this.constantType = requireNonNull(constantType); 92 this.bootstrapArgs = requireNonNull(bootstrapArgs).clone(); 93 94 if (constantName.length() == 0) 95 throw new IllegalArgumentException("Illegal invocation name: " + constantName); 96 } 97 98 /** 99 * Returns a nominal descriptor for a dynamic constant, transforming it into 100 * a more specific type if the constant bootstrap is a well-known one and a 101 * more specific nominal descriptor type (e.g., ClassDesc) is available. 102 * 103 * <p>Classes whose {@link Constable#describeConstable()} method produce 104 * a {@linkplain DynamicConstantDesc} with a well-known bootstrap including 105 * {@link Class} (for instances describing primitive types), {@link Enum}, 106 * and {@link VarHandle}. 107 * 108 * <p>Bytecode-reading APIs that process the constant pool and wish to expose 109 * entries as {@link ConstantDesc} to their callers should generally use this 110 * method in preference to {@link #ofNamed(DirectMethodHandleDesc, String, ClassDesc, ConstantDesc...)} 111 * because this may result in a more specific type that can be provided to 112 * callers. 113 * 114 * @param <T> the type of the dynamic constant 115 * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the 116 * bootstrap method for the constant 117 * @param constantName The unqualified name that would appear in the {@code NameAndType} 118 * operand of the {@code LDC} for this constant 119 * @param constantType a {@link ClassDesc} describing the type 120 * that would appear in the {@code NameAndType} operand 121 * of the {@code LDC} for this constant 122 * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments 123 * to the bootstrap, that would appear in the 124 * {@code BootstrapMethods} attribute 125 * @return the nominal descriptor 126 * @throws NullPointerException if any argument is null 127 * @throws IllegalArgumentException if the {@code name} has the incorrect 128 * format 129 * @jvms 4.2.2 Unqualified Names 130 */ 131 // Do not call this method from the static initialization of java.lang.constant.ConstantDescs 132 // since that can lead to potential deadlock during multi-threaded concurrent execution ofCanonical(DirectMethodHandleDesc bootstrapMethod, String constantName, ClassDesc constantType, ConstantDesc[] bootstrapArgs)133 public static<T> ConstantDesc ofCanonical(DirectMethodHandleDesc bootstrapMethod, 134 String constantName, 135 ClassDesc constantType, 136 ConstantDesc[] bootstrapArgs) { 137 return DynamicConstantDesc.<T>ofNamed(bootstrapMethod, constantName, constantType, bootstrapArgs) 138 .tryCanonicalize(); 139 } 140 141 /** 142 * Returns a nominal descriptor for a dynamic constant. 143 * 144 * @param <T> the type of the dynamic constant 145 * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the 146 * bootstrap method for the constant 147 * @param constantName The unqualified name that would appear in the {@code NameAndType} 148 * operand of the {@code LDC} for this constant 149 * @param constantType a {@link ClassDesc} describing the type 150 * that would appear in the {@code NameAndType} operand 151 * of the {@code LDC} for this constant 152 * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments 153 * to the bootstrap, that would appear in the 154 * {@code BootstrapMethods} attribute 155 * @return the nominal descriptor 156 * @throws NullPointerException if any argument is null 157 * @throws IllegalArgumentException if the {@code name} has the incorrect 158 * format 159 * @jvms 4.2.2 Unqualified Names 160 */ 161 ofNamed(DirectMethodHandleDesc bootstrapMethod, String constantName, ClassDesc constantType, ConstantDesc... bootstrapArgs)162 public static<T> DynamicConstantDesc<T> ofNamed(DirectMethodHandleDesc bootstrapMethod, 163 String constantName, 164 ClassDesc constantType, 165 ConstantDesc... bootstrapArgs) { 166 return new AnonymousDynamicConstantDesc<>(bootstrapMethod, constantName, constantType, bootstrapArgs); 167 } 168 169 /** 170 * Returns a nominal descriptor for a dynamic constant whose name parameter 171 * is {@link ConstantDescs#DEFAULT_NAME}, and whose type parameter is always 172 * the same as the bootstrap method return type. 173 * 174 * @param <T> the type of the dynamic constant 175 * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the 176 * bootstrap method for the constant 177 * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments 178 * to the bootstrap, that would appear in the 179 * {@code BootstrapMethods} attribute 180 * @return the nominal descriptor 181 * @throws NullPointerException if any argument is null 182 * @jvms 4.2.2 Unqualified Names 183 */ of(DirectMethodHandleDesc bootstrapMethod, ConstantDesc... bootstrapArgs)184 public static<T> DynamicConstantDesc<T> of(DirectMethodHandleDesc bootstrapMethod, 185 ConstantDesc... bootstrapArgs) { 186 return ofNamed(bootstrapMethod, DEFAULT_NAME, bootstrapMethod.invocationType().returnType(), bootstrapArgs); 187 } 188 189 /** 190 * Returns a nominal descriptor for a dynamic constant whose bootstrap has 191 * no static arguments, whose name parameter is {@link ConstantDescs#DEFAULT_NAME}, 192 * and whose type parameter is always the same as the bootstrap method return type. 193 * 194 * @param <T> the type of the dynamic constant 195 * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the 196 * bootstrap method for the constant 197 * @return the nominal descriptor 198 * @throws NullPointerException if any argument is null 199 */ of(DirectMethodHandleDesc bootstrapMethod)200 public static<T> DynamicConstantDesc<T> of(DirectMethodHandleDesc bootstrapMethod) { 201 return of(bootstrapMethod, EMPTY_CONSTANTDESC); 202 } 203 204 /** 205 * Returns the name that would appear in the {@code NameAndType} operand 206 * of the {@code LDC} for this constant. 207 * 208 * @return the constant name 209 */ constantName()210 public String constantName() { 211 return constantName; 212 } 213 214 /** 215 * Returns a {@link ClassDesc} describing the type that would appear in the 216 * {@code NameAndType} operand of the {@code LDC} for this constant. 217 * 218 * @return the constant type 219 */ constantType()220 public ClassDesc constantType() { 221 return constantType; 222 } 223 224 /** 225 * Returns a {@link MethodHandleDesc} describing the bootstrap method for 226 * this constant. 227 * 228 * @return the bootstrap method 229 */ bootstrapMethod()230 public DirectMethodHandleDesc bootstrapMethod() { 231 return bootstrapMethod; 232 } 233 234 /** 235 * Returns the bootstrap arguments for this constant. 236 * 237 * @return the bootstrap arguments 238 */ bootstrapArgs()239 public ConstantDesc[] bootstrapArgs() { 240 return bootstrapArgs.clone(); 241 } 242 243 /** 244 * Returns the bootstrap arguments for this constant as an immutable {@link List}. 245 * 246 * @return a {@link List} of the bootstrap arguments 247 */ bootstrapArgsList()248 public List<ConstantDesc> bootstrapArgsList() { 249 return List.of(bootstrapArgs); 250 } 251 252 @SuppressWarnings("unchecked") resolveConstantDesc(MethodHandles.Lookup lookup)253 public T resolveConstantDesc(MethodHandles.Lookup lookup) throws ReflectiveOperationException { 254 try { 255 MethodHandle bsm = (MethodHandle) bootstrapMethod.resolveConstantDesc(lookup); 256 if (bsm.type().parameterCount() < 2 || 257 !MethodHandles.Lookup.class.isAssignableFrom(bsm.type().parameterType(0))) { 258 throw new BootstrapMethodError( 259 "Invalid bootstrap method declared for resolving a dynamic constant: " + bootstrapMethod); 260 } 261 Object[] bsmArgs = new Object[3 + bootstrapArgs.length]; 262 bsmArgs[0] = lookup; 263 bsmArgs[1] = constantName; 264 bsmArgs[2] = constantType.resolveConstantDesc(lookup); 265 for (int i = 0; i < bootstrapArgs.length; i++) 266 bsmArgs[3 + i] = bootstrapArgs[i].resolveConstantDesc(lookup); 267 268 return (T) bsm.invokeWithArguments(bsmArgs); 269 } catch (Error e) { 270 throw e; 271 } catch (Throwable t) { 272 throw new BootstrapMethodError(t); 273 } 274 } 275 tryCanonicalize()276 private ConstantDesc tryCanonicalize() { 277 Function<DynamicConstantDesc<?>, ConstantDesc> f = CanonicalMapHolder.CANONICAL_MAP.get(bootstrapMethod); 278 if (f != null) { 279 try { 280 return f.apply(this); 281 } 282 catch (Throwable t) { 283 return this; 284 } 285 } 286 return this; 287 } 288 canonicalizeNull(DynamicConstantDesc<?> desc)289 private static ConstantDesc canonicalizeNull(DynamicConstantDesc<?> desc) { 290 if (desc.bootstrapArgs.length != 0) 291 return desc; 292 return ConstantDescs.NULL; 293 } 294 canonicalizeEnum(DynamicConstantDesc<?> desc)295 private static ConstantDesc canonicalizeEnum(DynamicConstantDesc<?> desc) { 296 if (desc.bootstrapArgs.length != 0 297 || desc.constantName == null) 298 return desc; 299 return EnumDesc.of(desc.constantType, desc.constantName); 300 } 301 canonicalizePrimitiveClass(DynamicConstantDesc<?> desc)302 private static ConstantDesc canonicalizePrimitiveClass(DynamicConstantDesc<?> desc) { 303 if (desc.bootstrapArgs.length != 0 304 || !desc.constantType().equals(CD_Class) 305 || desc.constantName == null) 306 return desc; 307 return ClassDesc.ofDescriptor(desc.constantName); 308 } 309 canonicalizeStaticFieldVarHandle(DynamicConstantDesc<?> desc)310 private static ConstantDesc canonicalizeStaticFieldVarHandle(DynamicConstantDesc<?> desc) { 311 if (desc.bootstrapArgs.length != 2 312 || !desc.constantType().equals(CD_VarHandle)) 313 return desc; 314 return VarHandleDesc.ofStaticField((ClassDesc) desc.bootstrapArgs[0], 315 desc.constantName, 316 (ClassDesc) desc.bootstrapArgs[1]); 317 } 318 canonicalizeFieldVarHandle(DynamicConstantDesc<?> desc)319 private static ConstantDesc canonicalizeFieldVarHandle(DynamicConstantDesc<?> desc) { 320 if (desc.bootstrapArgs.length != 2 321 || !desc.constantType().equals(CD_VarHandle)) 322 return desc; 323 return VarHandleDesc.ofField((ClassDesc) desc.bootstrapArgs[0], 324 desc.constantName, 325 (ClassDesc) desc.bootstrapArgs[1]); 326 } 327 canonicalizeArrayVarHandle(DynamicConstantDesc<?> desc)328 private static ConstantDesc canonicalizeArrayVarHandle(DynamicConstantDesc<?> desc) { 329 if (desc.bootstrapArgs.length != 1 330 || !desc.constantType().equals(CD_VarHandle)) 331 return desc; 332 return VarHandleDesc.ofArray((ClassDesc) desc.bootstrapArgs[0]); 333 } 334 335 // @@@ To eventually support in canonicalization: DCR with BSM=MHR_METHODHANDLEDESC_ASTYPE becomes AsTypeMHDesc 336 337 /** 338 * Compares the specified object with this descriptor for equality. Returns 339 * {@code true} if and only if the specified object is also a 340 * {@linkplain DynamicConstantDesc}, and both descriptors have equal 341 * bootstrap methods, bootstrap argument lists, constant name, and 342 * constant type. 343 * 344 * @param o the {@code DynamicConstantDesc} to compare to this 345 * {@code DynamicConstantDesc} 346 * @return {@code true} if the specified {@code DynamicConstantDesc} 347 * is equal to this {@code DynamicConstantDesc}. 348 * 349 */ 350 @Override equals(Object o)351 public final boolean equals(Object o) { 352 if (this == o) return true; 353 return (o instanceof DynamicConstantDesc<?> desc) 354 && Objects.equals(bootstrapMethod, desc.bootstrapMethod) 355 && Arrays.equals(bootstrapArgs, desc.bootstrapArgs) 356 && Objects.equals(constantName, desc.constantName) 357 && Objects.equals(constantType, desc.constantType); 358 } 359 360 @Override hashCode()361 public final int hashCode() { 362 int result = Objects.hash(bootstrapMethod, constantName, constantType); 363 result = 31 * result + Arrays.hashCode(bootstrapArgs); 364 return result; 365 } 366 367 /** 368 * Returns a compact textual description of this constant description, 369 * including the bootstrap method, the constant name and type, and 370 * the static bootstrap arguments. 371 * 372 * @return A compact textual description of this call site descriptor 373 */ 374 @Override toString()375 public String toString() { 376 return String.format("DynamicConstantDesc[%s::%s(%s%s)%s]", 377 bootstrapMethod.owner().displayName(), 378 bootstrapMethod.methodName(), 379 constantName.equals(ConstantDescs.DEFAULT_NAME) ? "" : constantName + "/", 380 Stream.of(bootstrapArgs).map(Object::toString).collect(joining(",")), 381 constantType.displayName()); 382 } 383 384 private static class AnonymousDynamicConstantDesc<T> extends DynamicConstantDesc<T> { AnonymousDynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod, String constantName, ClassDesc constantType, ConstantDesc... bootstrapArgs)385 AnonymousDynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod, String constantName, ClassDesc constantType, ConstantDesc... bootstrapArgs) { 386 super(bootstrapMethod, constantName, constantType, bootstrapArgs); 387 } 388 } 389 390 private static final class CanonicalMapHolder { 391 static final Map<MethodHandleDesc, Function<DynamicConstantDesc<?>, ConstantDesc>> CANONICAL_MAP = 392 Map.ofEntries( 393 Map.entry(ConstantDescs.BSM_PRIMITIVE_CLASS, DynamicConstantDesc::canonicalizePrimitiveClass), 394 Map.entry(ConstantDescs.BSM_ENUM_CONSTANT, DynamicConstantDesc::canonicalizeEnum), 395 Map.entry(ConstantDescs.BSM_NULL_CONSTANT, DynamicConstantDesc::canonicalizeNull), 396 Map.entry(ConstantDescs.BSM_VARHANDLE_STATIC_FIELD, DynamicConstantDesc::canonicalizeStaticFieldVarHandle), 397 Map.entry(ConstantDescs.BSM_VARHANDLE_FIELD, DynamicConstantDesc::canonicalizeFieldVarHandle), 398 Map.entry(ConstantDescs.BSM_VARHANDLE_ARRAY, DynamicConstantDesc::canonicalizeArrayVarHandle)); 399 } 400 } 401