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