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                 ClassDescImpl {
 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 ClassDescImpl(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 }