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 
 29 import static java.lang.constant.ConstantUtils.*;
 30 import static java.util.Objects.requireNonNull;
 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 final class ReferenceClassDescImpl implements ClassDesc {
 38     private final String descriptor;
 39 
 40     /**
 41      * Creates a {@linkplain ClassDesc} from a descriptor string for a class or
 42      * interface type or an array type.
 43      *
 44      * @param descriptor a field descriptor string for a class or interface type
 45      * @throws IllegalArgumentException if the descriptor string is not a valid
 46      * field descriptor string, or does not describe a class or interface type
 47      * @jvms 4.3.2 Field Descriptors
 48      */
 49     ReferenceClassDescImpl(String descriptor) {
 50         requireNonNull(descriptor);
 51         int len = ConstantUtils.skipOverFieldSignature(descriptor, 0, descriptor.length(), false);
 52         if (len == 0 || len == 1
 53             || len != descriptor.length())
 54             throw new IllegalArgumentException(String.format("not a valid reference type descriptor: %s", descriptor));
 55         this.descriptor = descriptor;
 56     }
 57 
 58     @Override
 59     public String descriptorString() {
 60         return descriptor;
 61     }
 62 
 63     @Override
 64     public Class<?> resolveConstantDesc(MethodHandles.Lookup lookup)
 65             throws ReflectiveOperationException {
 66         if (isArray()) {
 67             if (isPrimitiveArray()) {
 68                 return lookup.findClass(descriptor);
 69             }
 70             // Class.forName is slow on class or interface arrays
 71             int depth = ConstantUtils.arrayDepth(descriptor);
 72             Class<?> clazz = lookup.findClass(internalToBinary(descriptor.substring(depth + 1, descriptor.length() - 1)));
 73             for (int i = 0; i < depth; i++)
 74                 clazz = clazz.arrayType();
 75             return clazz;
 76         }
 77         return lookup.findClass(internalToBinary(dropFirstAndLastChar(descriptor)));
 78     }
 79 
 80     /**
 81      * Whether the descriptor is one of a primitive array, given this is
 82      * already a valid reference type descriptor.
 83      */
 84     private boolean isPrimitiveArray() {
 85         // All L-type descriptors must end with a semicolon; same for reference
 86         // arrays, leaving primitive arrays the only ones without a final semicolon
 87         return descriptor.charAt(descriptor.length() - 1) != ';';
 88     }
 89 
 90     /**
 91      * Returns {@code true} if this {@linkplain ReferenceClassDescImpl} is
 92      * equal to another {@linkplain ReferenceClassDescImpl}.  Equality is
 93      * determined by the two class descriptors having equal class descriptor
 94      * strings.
 95      *
 96      * @param o the {@code ClassDesc} to compare to this
 97      *       {@code ClassDesc}
 98      * @return {@code true} if the specified {@code ClassDesc}
 99      *      is equal to this {@code ClassDesc}.
100      */
101     @Override
102     public boolean equals(Object o) {
103         if (this == o) return true;
104         if (o == null || getClass() != o.getClass()) return false;
105 
106         ClassDesc constant = (ClassDesc) o;
107         return descriptor.equals(constant.descriptorString());
108     }
109 
110     @Override
111     public int hashCode() {
112         return descriptor.hashCode();
113     }
114 
115     @Override
116     public String toString() {
117         return String.format("ClassDesc[%s]", displayName());
118     }
119 }