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