1 /*
  2  * Copyright (c) 2018, 2024, 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 jdk.internal.constant;
 26 
 27 import java.lang.constant.ClassDesc;
 28 import java.lang.invoke.MethodHandles;
 29 
 30 import static jdk.internal.constant.ConstantUtils.*;
 31 
 32 /**
 33  * A <a href="package-summary.html#nominal">nominal descriptor</a> for a class,
 34  * interface, or array type.  A {@linkplain ReferenceClassDescImpl} corresponds to a
 35  * {@code Constant_Class_info} entry in the constant pool of a classfile.
 36  */
 37 public final class ReferenceClassDescImpl implements ClassDesc {
 38     private final String descriptor;
 39 
 40     private ReferenceClassDescImpl(String descriptor) {
 41         this.descriptor = descriptor;
 42     }
 43 
 44     /**
 45      * Creates a {@linkplain ClassDesc} from a descriptor string for a class or
 46      * interface type or an array type.
 47      *
 48      * @param descriptor a field descriptor string for a class or interface type
 49      * @throws IllegalArgumentException if the descriptor string is not a valid
 50      * field descriptor string, or does not describe a class or interface type
 51      * @jvms 4.3.2 Field Descriptors
 52      */
 53     public static ReferenceClassDescImpl of(String descriptor) {
 54         int dLen = descriptor.length();
 55         int len = ConstantUtils.skipOverFieldSignature(descriptor, 0, dLen, false);
 56         if (len <= 1 || len != dLen)
 57             throw new IllegalArgumentException(String.format("not a valid reference type descriptor: %s", descriptor));
 58         return new ReferenceClassDescImpl(descriptor);
 59     }
 60 
 61     /**
 62      * Creates a {@linkplain ClassDesc} from a pre-validated descriptor string
 63      * for a class or interface type or an array type.
 64      *
 65      * @param descriptor a field descriptor string for a class or interface type
 66      * @jvms 4.3.2 Field Descriptors
 67      */
 68     public static ReferenceClassDescImpl ofValidated(String descriptor) {
 69         assert ConstantUtils.skipOverFieldSignature(descriptor, 0, descriptor.length(), false)
 70                 == descriptor.length() : descriptor;
 71         return new ReferenceClassDescImpl(descriptor);
 72     }
 73 
 74     @Override
 75     public String descriptorString() {
 76         return descriptor;
 77     }
 78 
 79     @Override
 80     public Class<?> resolveConstantDesc(MethodHandles.Lookup lookup)
 81             throws ReflectiveOperationException {
 82         if (isArray()) {
 83             if (isPrimitiveArray()) {
 84                 return lookup.findClass(descriptor);
 85             }
 86             // Class.forName is slow on class or interface arrays
 87             int depth = ConstantUtils.arrayDepth(descriptor);
 88             Class<?> clazz = lookup.findClass(internalToBinary(descriptor.substring(depth + 1, descriptor.length() - 1)));
 89             for (int i = 0; i < depth; i++)
 90                 clazz = clazz.arrayType();
 91             return clazz;
 92         }
 93         return lookup.findClass(internalToBinary(dropFirstAndLastChar(descriptor)));
 94     }
 95 
 96     /**
 97      * Whether the descriptor is one of a primitive array, given this is
 98      * already a valid reference type descriptor.
 99      */
100     private boolean isPrimitiveArray() {
101         // All L-type descriptors must end with a semicolon; same for reference
102         // arrays, leaving primitive arrays the only ones without a final semicolon
103         return descriptor.charAt(descriptor.length() - 1) != ';';
104     }
105 
106     /**
107      * Returns {@code true} if this {@linkplain ReferenceClassDescImpl} is
108      * equal to another {@linkplain ReferenceClassDescImpl}.  Equality is
109      * determined by the two class descriptors having equal class descriptor
110      * strings.
111      *
112      * @param o the {@code ClassDesc} to compare to this
113      *       {@code ClassDesc}
114      * @return {@code true} if the specified {@code ClassDesc}
115      *      is equal to this {@code ClassDesc}.
116      */
117     @Override
118     public boolean equals(Object o) {
119         if (this == o) return true;
120         if (o instanceof ReferenceClassDescImpl constant) {
121             return descriptor.equals(constant.descriptor);
122         }
123         return false;
124     }
125 
126     @Override
127     public int hashCode() {
128         return descriptor.hashCode();
129     }
130 
131     @Override
132     public String toString() {
133         return String.format("ClassDesc[%s]", displayName());
134     }
135 }