1 /* 2 * Copyright (c) 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 26 package java.lang.reflect.code.type; 27 28 import java.lang.constant.ClassDesc; 29 import java.lang.invoke.MethodHandles.Lookup; 30 import java.lang.reflect.ParameterizedType; 31 import java.lang.reflect.Type; 32 import java.lang.reflect.code.TypeElement; 33 import java.util.ArrayList; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.Optional; 37 38 /** 39 * A class type. 40 */ 41 public final class ClassType implements TypeVarRef.Owner, JavaType { 42 // Fully qualified name 43 private final ClassDesc type; 44 45 private final List<JavaType> typeArguments; 46 47 ClassType(ClassDesc type) { 48 this(type, List.of()); 49 } 50 51 ClassType(ClassDesc type, List<JavaType> typeArguments) { 52 if (!type.isClassOrInterface()) { 53 throw new IllegalArgumentException("Invalid base type: " + type); 54 } 55 this.type = type; 56 this.typeArguments = List.copyOf(typeArguments); 57 } 58 59 @Override 60 public Type resolve(Lookup lookup) throws ReflectiveOperationException { 61 Class<?> baseType = type.resolveConstantDesc(lookup); 62 List<Type> resolvedTypeArgs = new ArrayList<>(); 63 for (JavaType typearg : typeArguments) { 64 resolvedTypeArgs.add(typearg.resolve(lookup)); 65 } 66 return resolvedTypeArgs.isEmpty() ? 67 baseType : 68 makeReflectiveParameterizedType(baseType, 69 resolvedTypeArgs.toArray(new Type[0]), baseType.getDeclaringClass()); // @@@: generic owner is erased here 70 } 71 72 // Copied code in jdk.compiler module throws UOE 73 private static ParameterizedType makeReflectiveParameterizedType(Class<?> base, Type[] typeArgs, Class<?> owner) { 74 /*__throw new UnsupportedOperationException();__*/ return sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.make(base, typeArgs, owner); 75 } 76 77 @Override 78 public ExternalizedTypeElement externalize() { 79 List<ExternalizedTypeElement> args = typeArguments.stream() 80 .map(TypeElement::externalize) 81 .toList(); 82 83 ExternalizedTypeElement td = new ExternalizedTypeElement(toClassName(), args); 84 return td; 85 } 86 87 @Override 88 public String toString() { 89 return externalize().toString(); 90 } 91 92 @Override 93 public boolean equals(Object o) { 94 if (this == o) return true; 95 if (o == null || getClass() != o.getClass()) return false; 96 97 ClassType typeDesc = (ClassType) o; 98 99 if (!type.equals(typeDesc.type)) return false; 100 return typeArguments.equals(typeDesc.typeArguments); 101 } 102 103 @Override 104 public int hashCode() { 105 int result = type.hashCode(); 106 result = 31 * result + typeArguments.hashCode(); 107 return result; 108 } 109 110 /** 111 * {@return the unboxed primitive type associated with this class type (if any)} 112 */ 113 public Optional<PrimitiveType> unbox() { 114 class LazyHolder { 115 static final Map<ClassType, PrimitiveType> wrapperToPrimitive = Map.of( 116 J_L_BYTE, BYTE, 117 J_L_SHORT, SHORT, 118 J_L_INTEGER, INT, 119 J_L_LONG, LONG, 120 J_L_FLOAT, FLOAT, 121 J_L_DOUBLE, DOUBLE, 122 J_L_CHARACTER, CHAR, 123 J_L_BOOLEAN, BOOLEAN 124 ); 125 } 126 return Optional.ofNullable(LazyHolder.wrapperToPrimitive.get(this)); 127 } 128 129 @Override 130 public JavaType erasure() { 131 return rawType(); 132 } 133 134 // Conversions 135 136 /** 137 * {@return a class type whose base type is the same as this class type, but without any 138 * type arguments} 139 */ 140 public ClassType rawType() { 141 return new ClassType(type); 142 } 143 144 /** 145 * {@return {@code true} if this class type has a non-empty type argument list} 146 * @see ClassType#typeArguments() 147 */ 148 public boolean hasTypeArguments() { 149 return !typeArguments.isEmpty(); 150 } 151 152 /** 153 * {@return the type argument list associated with this class type} 154 */ 155 public List<JavaType> typeArguments() { 156 return typeArguments; 157 } 158 159 @Override 160 public JavaType toBasicType() { 161 return JavaType.J_L_OBJECT; 162 } 163 164 /** 165 * {@return a human-readable name for this class type} 166 */ 167 public String toClassName() { 168 String pkg = type.packageName(); 169 return pkg.isEmpty() ? 170 type.displayName() : 171 String.format("%s.%s", pkg, type.displayName()); 172 } 173 174 @Override 175 public ClassDesc toNominalDescriptor() { 176 return type; 177 } 178 }