• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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