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