1 /* 2 * Copyright (c) 2018, 2023, 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.MethodHandles; 28 import java.lang.invoke.TypeDescriptor; 29 import java.util.stream.Stream; 30 31 import sun.invoke.util.Wrapper; 32 33 import static java.lang.constant.ConstantUtils.binaryToInternal; 34 import static java.lang.constant.ConstantUtils.dropLastChar; 35 import static java.lang.constant.ConstantUtils.internalToBinary; 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 a 42 * {@link Class} constant. 43 * 44 * <p>For common system types, including all the primitive types, there are 45 * predefined {@linkplain ClassDesc} constants in {@link ConstantDescs}. 46 * (The {@code java.lang.constant} APIs consider {@code void} to be a primitive type.) 47 * To create a {@linkplain ClassDesc} for a class or interface type, use {@link #of} or 48 * {@link #ofDescriptor(String)}; to create a {@linkplain ClassDesc} for an array 49 * type, use {@link #ofDescriptor(String)}, or first obtain a 50 * {@linkplain ClassDesc} for the component type and then call the {@link #arrayType()} 51 * or {@link #arrayType(int)} methods. 52 * 53 * @see ConstantDescs 54 * 55 * @since 12 56 */ 57 public sealed interface ClassDesc 58 extends ConstantDesc, 59 TypeDescriptor.OfField<ClassDesc> 60 permits PrimitiveClassDescImpl, 61 ReferenceClassDescImpl { 62 63 /** 64 * Returns a {@linkplain ClassDesc} for a class or interface type, 65 * given the name of the class or interface, such as {@code "java.lang.String"}. 66 * (To create a descriptor for an array type, either use {@link #ofDescriptor(String)} 67 * or {@link #arrayType()}; to create a descriptor for a primitive type, use 68 * {@link #ofDescriptor(String)} or use the predefined constants in 69 * {@link ConstantDescs}). 70 * 71 * @param name the fully qualified (dot-separated) binary class name 72 * @return a {@linkplain ClassDesc} describing the desired class 73 * @throws NullPointerException if the argument is {@code null} 74 * @throws IllegalArgumentException if the name string is not in the 75 * correct format 76 * @see ClassDesc#ofDescriptor(String) 77 * @see ClassDesc#ofInternalName(String) 78 */ of(String name)79 static ClassDesc of(String name) { 80 ConstantUtils.validateBinaryClassName(requireNonNull(name)); 81 return ClassDesc.ofDescriptor("L" + binaryToInternal(name) + ";"); 82 } 83 84 // BEGIN Android-removed: Not used in Android. 85 /* 86 /** 87 * Returns a {@linkplain ClassDesc} for a class or interface type, 88 * given the name of the class or interface in internal form, 89 * such as {@code "java/lang/String"}. 90 * 91 * @apiNote 92 * To create a descriptor for an array type, either use {@link #ofDescriptor(String)} 93 * or {@link #arrayType()}; to create a descriptor for a primitive type, use 94 * {@link #ofDescriptor(String)} or use the predefined constants in 95 * {@link ConstantDescs}. 96 * 97 * @param name the fully qualified class name, in internal (slash-separated) form 98 * @return a {@linkplain ClassDesc} describing the desired class 99 * @throws NullPointerException if the argument is {@code null} 100 * @throws IllegalArgumentException if the name string is not in the 101 * correct format 102 * @jvms 4.2.1 Binary Class and Interface Names 103 * @see ClassDesc#of(String) 104 * @see ClassDesc#ofDescriptor(String) 105 * @since 20 106 * / 107 static ClassDesc ofInternalName(String name) { 108 ConstantUtils.validateInternalClassName(requireNonNull(name)); 109 return ClassDesc.ofDescriptor("L" + name + ";"); 110 } 111 */ 112 // END Android-removed: Not used in Android. 113 114 /** 115 * Returns a {@linkplain ClassDesc} for a class or interface type, 116 * given a package name and the unqualified (simple) name for the 117 * class or interface. 118 * 119 * @param packageName the package name (dot-separated); if the package 120 * name is the empty string, the class is considered to 121 * be in the unnamed package 122 * @param className the unqualified (simple) class name 123 * @return a {@linkplain ClassDesc} describing the desired class 124 * @throws NullPointerException if any argument is {@code null} 125 * @throws IllegalArgumentException if the package name or class name are 126 * not in the correct format 127 */ of(String packageName, String className)128 static ClassDesc of(String packageName, String className) { 129 ConstantUtils.validateBinaryClassName(requireNonNull(packageName)); 130 if (packageName.isEmpty()) { 131 return of(className); 132 } 133 validateMemberName(requireNonNull(className), false); 134 return ofDescriptor("L" + binaryToInternal(packageName) + 135 "/" + className + ";"); 136 } 137 138 /** 139 * Returns a {@linkplain ClassDesc} given a descriptor string for a class, 140 * interface, array, or primitive type. 141 * 142 * @apiNote 143 * 144 * A field type descriptor string for a non-array type is either 145 * a one-letter code corresponding to a primitive type 146 * ({@code "J", "I", "C", "S", "B", "D", "F", "Z", "V"}), or the letter {@code "L"}, followed 147 * by the fully qualified binary name of a class, followed by {@code ";"}. 148 * A field type descriptor for an array type is the character {@code "["} 149 * followed by the field descriptor for the component type. Examples of 150 * valid type descriptor strings include {@code "Ljava/lang/String;"}, {@code "I"}, 151 * {@code "[I"}, {@code "V"}, {@code "[Ljava/lang/String;"}, etc. 152 * See JVMS {@jvms 4.3.2 }("Field Descriptors") for more detail. 153 * 154 * @param descriptor a field descriptor string 155 * @return a {@linkplain ClassDesc} describing the desired class 156 * @throws NullPointerException if the argument is {@code null} 157 * @throws IllegalArgumentException if the descriptor string is not in the 158 * correct format 159 * @jvms 4.3.2 Field Descriptors 160 * @jvms 4.4.1 The CONSTANT_Class_info Structure 161 * @see ClassDesc#of(String) 162 * @see ClassDesc#ofInternalName(String) 163 */ ofDescriptor(String descriptor)164 static ClassDesc ofDescriptor(String descriptor) { 165 requireNonNull(descriptor); 166 if (descriptor.isEmpty()) { 167 throw new IllegalArgumentException( 168 "not a valid reference type descriptor: " + descriptor); 169 } 170 int depth = ConstantUtils.arrayDepth(descriptor); 171 if (depth > ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) { 172 throw new IllegalArgumentException( 173 "Cannot create an array type descriptor with more than " + 174 ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions"); 175 } 176 return (descriptor.length() == 1) 177 ? new PrimitiveClassDescImpl(descriptor) 178 : new ReferenceClassDescImpl(descriptor); 179 } 180 181 /** 182 * Returns a {@linkplain ClassDesc} for an array type whose component type 183 * is described by this {@linkplain ClassDesc}. 184 * 185 * @return a {@linkplain ClassDesc} describing the array type 186 * @throws IllegalStateException if the resulting {@linkplain 187 * ClassDesc} would have an array rank of greater than 255 188 * @jvms 4.4.1 The CONSTANT_Class_info Structure 189 */ arrayType()190 default ClassDesc arrayType() { 191 int depth = ConstantUtils.arrayDepth(descriptorString()); 192 if (depth >= ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) { 193 throw new IllegalStateException( 194 "Cannot create an array type descriptor with more than " + 195 ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions"); 196 } 197 return arrayType(1); 198 } 199 200 /** 201 * Returns a {@linkplain ClassDesc} for an array type of the specified rank, 202 * whose component type is described by this {@linkplain ClassDesc}. 203 * 204 * @param rank the rank of the array 205 * @return a {@linkplain ClassDesc} describing the array type 206 * @throws IllegalArgumentException if the rank is less than or 207 * equal to zero or if the rank of the resulting array type is 208 * greater than 255 209 * @jvms 4.4.1 The CONSTANT_Class_info Structure 210 */ arrayType(int rank)211 default ClassDesc arrayType(int rank) { 212 int netRank; 213 if (rank <= 0) { 214 throw new IllegalArgumentException("rank " + rank + " is not a positive value"); 215 } 216 try { 217 int currentDepth = ConstantUtils.arrayDepth(descriptorString()); 218 netRank = Math.addExact(currentDepth, rank); 219 if (netRank > ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) { 220 throw new IllegalArgumentException("rank: " + netRank + 221 " exceeds maximum supported dimension of " + 222 ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS); 223 } 224 } catch (ArithmeticException ae) { 225 throw new IllegalArgumentException("Integer overflow in rank computation"); 226 } 227 return ClassDesc.ofDescriptor("[".repeat(rank) + descriptorString()); 228 } 229 230 /** 231 * Returns a {@linkplain ClassDesc} for a nested class of the class or 232 * interface type described by this {@linkplain ClassDesc}. 233 * 234 * @apiNote 235 * 236 * Example: If descriptor {@code d} describes the class {@code java.util.Map}, a 237 * descriptor for the class {@code java.util.Map.Entry} could be obtained 238 * by {@code d.nested("Entry")}. 239 * 240 * @param nestedName the unqualified name of the nested class 241 * @return a {@linkplain ClassDesc} describing the nested class 242 * @throws NullPointerException if the argument is {@code null} 243 * @throws IllegalStateException if this {@linkplain ClassDesc} does not 244 * describe a class or interface type 245 * @throws IllegalArgumentException if the nested class name is invalid 246 */ nested(String nestedName)247 default ClassDesc nested(String nestedName) { 248 validateMemberName(nestedName, false); 249 if (!isClassOrInterface()) 250 throw new IllegalStateException("Outer class is not a class or interface type"); 251 return ClassDesc.ofDescriptor(dropLastChar(descriptorString()) + "$" + nestedName + ";"); 252 } 253 254 /** 255 * Returns a {@linkplain ClassDesc} for a nested class of the class or 256 * interface type described by this {@linkplain ClassDesc}. 257 * 258 * @param firstNestedName the unqualified name of the first level of nested class 259 * @param moreNestedNames the unqualified name(s) of the remaining levels of 260 * nested class 261 * @return a {@linkplain ClassDesc} describing the nested class 262 * @throws NullPointerException if any argument or its contents is {@code null} 263 * @throws IllegalStateException if this {@linkplain ClassDesc} does not 264 * describe a class or interface type 265 * @throws IllegalArgumentException if the nested class name is invalid 266 */ nested(String firstNestedName, String... moreNestedNames)267 default ClassDesc nested(String firstNestedName, String... moreNestedNames) { 268 if (!isClassOrInterface()) 269 throw new IllegalStateException("Outer class is not a class or interface type"); 270 validateMemberName(firstNestedName, false); 271 requireNonNull(moreNestedNames); 272 for (String addNestedNames : moreNestedNames) { 273 validateMemberName(addNestedNames, false); 274 } 275 return moreNestedNames.length == 0 276 ? nested(firstNestedName) 277 : nested(firstNestedName + Stream.of(moreNestedNames).collect(joining("$", "$", ""))); 278 } 279 280 /** 281 * Returns whether this {@linkplain ClassDesc} describes an array type. 282 * 283 * @return whether this {@linkplain ClassDesc} describes an array type 284 */ isArray()285 default boolean isArray() { 286 return descriptorString().startsWith("["); 287 } 288 289 /** 290 * Returns whether this {@linkplain ClassDesc} describes a primitive type. 291 * 292 * @return whether this {@linkplain ClassDesc} describes a primitive type 293 */ isPrimitive()294 default boolean isPrimitive() { 295 return descriptorString().length() == 1; 296 } 297 298 /** 299 * Returns whether this {@linkplain ClassDesc} describes a class or interface type. 300 * 301 * @return whether this {@linkplain ClassDesc} describes a class or interface type 302 */ isClassOrInterface()303 default boolean isClassOrInterface() { 304 return descriptorString().startsWith("L"); 305 } 306 307 /** 308 * Returns the component type of this {@linkplain ClassDesc}, if it describes 309 * an array type, or {@code null} otherwise. 310 * 311 * @return a {@linkplain ClassDesc} describing the component type, or {@code null} 312 * if this descriptor does not describe an array type 313 */ componentType()314 default ClassDesc componentType() { 315 return isArray() ? ClassDesc.ofDescriptor(descriptorString().substring(1)) : null; 316 } 317 318 /** 319 * Returns the package name of this {@linkplain ClassDesc}, if it describes 320 * a class or interface type. 321 * 322 * @return the package name, or the empty string if the class is in the 323 * default package, or this {@linkplain ClassDesc} does not describe a class or interface type 324 */ packageName()325 default String packageName() { 326 if (!isClassOrInterface()) 327 return ""; 328 String className = internalToBinary(ConstantUtils.dropFirstAndLastChar(descriptorString())); 329 int index = className.lastIndexOf('.'); 330 return (index == -1) ? "" : className.substring(0, index); 331 } 332 333 /** 334 * Returns a human-readable name for the type described by this descriptor. 335 * 336 * @implSpec 337 * <p>The default implementation returns the simple name 338 * (e.g., {@code int}) for primitive types, the unqualified class name 339 * for class or interface types, or the display name of the component type 340 * suffixed with the appropriate number of {@code []} pairs for array types. 341 * 342 * @return the human-readable name 343 */ displayName()344 default String displayName() { 345 if (isPrimitive()) 346 return Wrapper.forBasicType(descriptorString().charAt(0)).primitiveSimpleName(); 347 else if (isClassOrInterface()) { 348 return descriptorString().substring(Math.max(1, descriptorString().lastIndexOf('/') + 1), 349 descriptorString().length() - 1); 350 } 351 else if (isArray()) { 352 int depth = ConstantUtils.arrayDepth(descriptorString()); 353 ClassDesc c = this; 354 for (int i=0; i<depth; i++) 355 c = c.componentType(); 356 return c.displayName() + "[]".repeat(depth); 357 } 358 else 359 throw new IllegalStateException(descriptorString()); 360 } 361 362 /** 363 * Returns a field type descriptor string for this type 364 * 365 * @return the descriptor string 366 * @jvms 4.3.2 Field Descriptors 367 */ descriptorString()368 String descriptorString(); 369 370 @Override resolveConstantDesc(MethodHandles.Lookup lookup)371 Class<?> resolveConstantDesc(MethodHandles.Lookup lookup) throws ReflectiveOperationException; 372 373 /** 374 * Compare the specified object with this descriptor for equality. Returns 375 * {@code true} if and only if the specified object is also a 376 * {@linkplain ClassDesc} and both describe the same type. 377 * 378 * @param o the other object 379 * @return whether this descriptor is equal to the other object 380 */ equals(Object o)381 boolean equals(Object o); 382 } 383