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