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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 import jdk.incubator.code.dialect.java.*;
25 import jdk.incubator.code.dialect.java.impl.JavaTypeUtils;
26 import org.junit.jupiter.api.Assertions;
27 import org.junit.jupiter.api.Test;
28 import org.junit.jupiter.params.ParameterizedTest;
29 import org.junit.jupiter.params.provider.MethodSource;
30
31 import java.lang.constant.ClassDesc;
32 import java.lang.constant.ConstantDescs;
33 import java.lang.invoke.MethodHandles;
34 import java.lang.reflect.Field;
35 import java.lang.reflect.Type;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.stream.Stream;
40
41
42 /*
43 * @test
44 * @modules jdk.incubator.code/jdk.incubator.code.dialect.java.impl
45 * @run junit TestJavaType
46 */
47
48 public class TestJavaType {
49
50 public static Object[][] JavaTypes() {
51 return new Object[][]{
52 {"boolean", "Z"},
53 {"byte", "B"},
54 {"char", "C"},
55 {"short", "S"},
56 {"int", "I"},
57 {"long", "J"},
58 {"float", "F"},
59 {"double", "D"},
60 {"void", "V"},
61 {"int[]", "[I"},
62 {"int[][][][]", "[[[[I"},
63 {"java.lang.String", "Ljava/lang/String;"},
64 {"java.lang.String[][]", "[[Ljava/lang/String;"},
65 {"a.b.C$D", "La/b/C$D;"},
66 };
67 }
68
69 @ParameterizedTest
70 @MethodSource("JavaTypes")
71 public void testJavaType(String tds, String bcd) {
72 JavaType jt = typeFromFlatString(tds);
73 Assertions.assertEquals(tds, jt.toString());
74 Assertions.assertEquals(bcd, jt.toNominalDescriptor().descriptorString());
75 Assertions.assertEquals(JavaType.type(ClassDesc.ofDescriptor(bcd)), jt);
76 }
77
78 public static Object[][] classDescriptors() {
79 return new Object[][]{
80 {"java.lang.String", "java.lang.String"},
81 };
82 }
83
84 @ParameterizedTest
85 @MethodSource("classDescriptors")
86 public void classDescriptor(String tds, String bcd) {
87 ClassType jt = (ClassType) typeFromFlatString(tds);
88 Assertions.assertEquals(tds, jt.toString());
89 Assertions.assertEquals(bcd, jt.toClassName());
90 }
91
92 public static Object[][] basicJavaTypes() {
93 return new Object[][]{
94 {"boolean", "int"},
95 {"byte", "int"},
96 {"char", "int"},
97 {"short", "int"},
98 {"int", "int"},
99 {"long", "long"},
100 {"float", "float"},
101 {"double", "double"},
102 {"void", "void"},
103 {"int[]", "java.lang.Object"},
104 {"int[][][][]", "java.lang.Object"},
105 {"java.lang.String", "java.lang.Object"},
106 {"java.lang.String[][]", "java.lang.Object"},
107 {"a.b.C$D", "java.lang.Object"},
108 {"java.util.List<T>", "java.lang.Object"},
109 {"java.util.List<T>[]", "java.lang.Object"},
110 };
111 }
112
113 @ParameterizedTest
114 @MethodSource("basicJavaTypes")
115 public void testBasicJavaType(String tds, String btds) {
116 JavaType jt = typeFromFlatString(tds);
117 Assertions.assertEquals(tds, jt.toString());
118 Assertions.assertEquals(btds, jt.toBasicType().toString());
119 }
120
121
122 public static Object[][] argumentJavaTypes() {
123 return new Object[][]{
124 {"java.util.List<T>", new String[] {"T"}},
125 {"java.util.List<T>[]", new String[] {"T"}},
126 {"java.util.List<java.util.function.Supplier<T>>", new String[] {"java.util.function.Supplier<T>"}},
127 {"java.util.List<java.util.function.Supplier<T>>[][]", new String[] {"java.util.function.Supplier<T>"}},
128 {"java.util.Map<K, V>", new String[] {"K", "V"}},
129 {"ab<cd<S<T, V>, N>>", new String[] {"cd<S<T, V>, N>"}},
130 {"java.util.Consumer<java.util.Function<String, Number>>", new String[] {"java.util.Function<String, Number>"}},
131 };
132 }
133
134 @ParameterizedTest
135 @MethodSource("argumentJavaTypes")
136 public void testArgumentJavaType(String tds, String... argTypes) {
137 JavaType jt = typeFromFlatString(tds);
138 Assertions.assertEquals(tds, jt.toString());
139
140 while (jt instanceof ArrayType) {
141 jt = ((ArrayType)jt).componentType();
142 }
143 ClassType ct = (ClassType)jt;
144
145 Assertions.assertEquals(ct.typeArguments().size(), argTypes.length);
146
147 Assertions.assertEquals(Stream.of(argTypes).map(TestJavaType::typeFromFlatString).toList(), ct.typeArguments());
148 }
149
150 @ParameterizedTest
151 @MethodSource("classDescs")
152 public void testClassDescRoundTrip(ClassDesc classDesc) {
153 Assertions.assertEquals(JavaType.type(classDesc).toNominalDescriptor(), classDesc);
154 }
155
156 public static Object[][] classDescs() throws ReflectiveOperationException {
157 List<Object[]> classDescs = new ArrayList<>();
158 for (Field f : ConstantDescs.class.getDeclaredFields()) {
159 if (f.getName().startsWith("CD_")) {
160 ClassDesc cd = (ClassDesc)f.get(null);
161 classDescs.add(new Object[] { cd });
162 if (!cd.equals(ConstantDescs.CD_void)) {
163 classDescs.add(new Object[]{cd.arrayType()});
164 classDescs.add(new Object[]{cd.arrayType().arrayType()});
165 }
166 }
167 }
168 return classDescs.stream().toArray(Object[][]::new);
169 }
170
171 @ParameterizedTest
172 @MethodSource("types")
173 public void testTypeRoundTrip(Type type) throws ReflectiveOperationException {
174 JavaType javaType = JavaType.type(type);
175 Assertions.assertEquals(javaType.resolve(MethodHandles.lookup()), type);
176 Assertions.assertEquals(JavaType.JAVA_ONLY_TYPE_FACTORY.constructType(javaType.externalize()), javaType);
177 }
178
179 @ParameterizedTest
180 @MethodSource("types")
181 public void testTypeString(Type type) throws ReflectiveOperationException {
182 JavaType javaType = JavaType.type(type);
183 Assertions.assertEquals(replaceTypeVariables(javaType).toString()
184 .replaceAll("::", "\\$"), type.getTypeName()
185 );
186 }
187
188 JavaType replaceTypeVariables(JavaType type) {
189 // This type transformation replaces type variables with simple class types.
190 // This obtains a JavaType whose toString behaves the same as Type::getTypeName
191 return switch (type) {
192 case PrimitiveType p -> p;
193 case WildcardType w -> JavaType.wildcard(w.boundKind(), replaceTypeVariables(w.boundType()));
194 case ArrayType a -> JavaType.array(replaceTypeVariables(a.componentType()));
195 case ClassType c -> {
196 ClassType res = c.rawType();
197 if (c.enclosingType().isPresent()) {
198 JavaType encl = replaceTypeVariables(c.enclosingType().get());
199 String nestedName = c.toClassName().substring(encl.toNominalDescriptor().displayName().length() + 1);
200 res = JavaType.qualified(replaceTypeVariables(c.enclosingType().get()), nestedName);
201 }
202 if (c.hasTypeArguments()) {
203 res = JavaType.parameterized(res,
204 c.typeArguments().stream().map(this::replaceTypeVariables).toList());
205 }
206 yield res;
207 }
208 case TypeVariableType t -> JavaType.type(ClassDesc.of(t.name()));
209 };
210 }
211
212 public static Object[][] types() throws ReflectiveOperationException {
213 List<Object[]> types = new ArrayList<>();
214 for (Field f : TypeHolder.class.getDeclaredFields()) {
215 types.add(new Object[] { f.getGenericType() });
216 }
217 return types.stream().toArray(Object[][]::new);
218 }
219
220 static class TypeHolder<X extends Number> {
221 boolean p1;
222 char p2;
223 byte p3;
224 short p4;
225 int p5;
226 long p6;
227 float p7;
228 double p8;
229
230 boolean[] ap1;
231 char[] ap2;
232 byte[] ap3;
233 short[] ap4;
234 int[] ap5;
235 long[] ap6;
236 float[] ap7;
237 double[] ap8;
238
239 boolean[][] aap1;
240 char[][] aap2;
241 byte[][] aap3;
242 short[][] aap4;
243 int[][] aap5;
244 long[][] aap6;
245 float[][] aap7;
246 double[][] aap8;
247
248 String r1;
249 Map<String, String> r2;
250 Map<String, ? extends String> r3;
251 Map<? extends String, String> r4;
252 Map<? extends String, ? extends String> r5;
253 Map<? extends List<? extends String>, ? super List<? extends String>> r6;
254 Map<? extends List<? extends String>[], ? super List<? extends String>[]> r7;
255 List<boolean[]> r8;
256 List<char[]> r9;
257 List<byte[]> r10;
258 List<short[]> r11;
259 List<int[]> r12;
260 List<long[]> r13;
261 List<float[]> r14;
262 List<double[]> r15;
263
264 String[] ar1;
265 Map<String, String>[] ar2;
266 Map<String, ? extends String>[] ar3;
267 Map<? extends String, String>[] ar4;
268 Map<? extends String, ? extends String>[] ar5;
269 Map<? extends List<? extends String>, ? super List<? extends String>>[] ar6;
270 Map<? extends List<? extends String>[], ? super List<? extends String>[]>[] ar7;
271 List<boolean[]>[] ar8;
272 List<char[]>[] ar9;
273 List<byte[]>[] ar10;
274 List<short[]>[] ar11;
275 List<int[]>[] ar12;
276 List<long[]>[] ar13;
277 List<float[]>[] ar14;
278 List<double[]>[] ar15;
279
280 String[][] aar1;
281 Map<String, String>[][] aar2;
282 Map<String, ? extends String>[][] aar3;
283 Map<? extends String, String>[][] aar4;
284 Map<? extends String, ? extends String>[][] aar5;
285 Map<? extends List<? extends String>, ? super List<? extends String>>[][] aar6;
286 Map<? extends List<? extends String>[], ? super List<? extends String>[]>[][] aar7;
287 List<boolean[]>[][] aar8;
288 List<char[]>[][] aar9;
289 List<byte[]>[][] aar10;
290 List<short[]>[][] aar11;
291 List<int[]>[][] aar12;
292 List<long[]>[][] aar13;
293 List<float[]>[][] aar14;
294 List<double[]>[][] aar15;
295
296 X x1;
297 Map<X, X> x2;
298 Map<X, ? extends X> x3;
299 Map<? extends X, X> x4;
300 Map<? extends X, ? extends X> x5;
301 Map<? extends List<? extends X>, ? super List<? extends X>> x6;
302 Map<? extends List<? extends X>[], ? super List<? extends X>[]> x7;
303 List<X[]> x8;
304
305 X[] ax1;
306 Map<X, X>[] ax2;
307 Map<X, ? extends X>[] ax3;
308 Map<? extends X, X>[] ax4;
309 Map<? extends X, ? extends X>[] ax5;
310 Map<? extends List<? extends X>, ? super List<? extends X>>[] ax6;
311 Map<? extends List<? extends X>[], ? super List<? extends X>[]>[] ax7;
312 List<X[]>[] ax8;
313
314 X[][] aax1;
315 Map<X, X>[][] aax2;
316 Map<X, ? extends X>[][] aax3;
317 Map<? extends X, X>[][] aax4;
318 Map<? extends X, ? extends X>[][] aax5;
319 Map<? extends List<? extends X>, ? super List<? extends X>>[][] aax6;
320 Map<? extends List<? extends X>[], ? super List<? extends X>[]>[][] aax7;
321 List<X[]>[][] aax8;
322
323 static class Outer<X> {
324 class Inner<X> { }
325 }
326
327 Outer<String>.Inner<String> o1;
328 Outer<? extends String>.Inner<String> o2;
329 Outer<String>.Inner<? extends String> o3;
330 Outer<? extends String>.Inner<? extends String> o4;
331 Outer<? super String>.Inner<String> o5;
332 Outer<String>.Inner<? super String> o6;
333 Outer<? super String>.Inner<? super String> o7;
334 Outer<?>.Inner<String> o8;
335 Outer<String>.Inner<?> o9;
336 Outer<?>.Inner<?> o10;
337
338 Outer<int[]>.Inner<int[]> oa1;
339 Outer<? extends int[]>.Inner<int[]> oa2;
340 Outer<int[]>.Inner<? extends int[]> oa3;
341 Outer<? extends int[]>.Inner<? extends int[]> oa4;
342 Outer<? super int[]>.Inner<int[]> oa5;
343 Outer<int[]>.Inner<? super int[]> oa6;
344 Outer<? super int[]>.Inner<? super int[]> oa7;
345 Outer<?>.Inner<int[]> oa8;
346 Outer<int[]>.Inner<?> oa9;
347 Outer<?>.Inner<?> oa10;
348
349 Outer<String>.Inner<String>[] ao1;
350 Outer<? extends String>.Inner<String>[] ao2;
351 Outer<String>.Inner<? extends String>[] ao3;
352 Outer<? extends String>.Inner<? extends String>[] ao4;
353 Outer<? super String>.Inner<String>[] ao5;
354 Outer<String>.Inner<? super String>[] ao6;
355 Outer<? super String>.Inner<? super String>[] ao7;
356 Outer<?>.Inner<String>[] ao8;
357 Outer<String>.Inner<?>[] ao9;
358 Outer<?>.Inner<?>[] ao10;
359
360 Outer<int[]>.Inner<int[]>[] aoa1;
361 Outer<? extends int[]>.Inner<int[]>[] aoa2;
362 Outer<int[]>.Inner<? extends int[]>[] aoa3;
363 Outer<? extends int[]>.Inner<? extends int[]>[] aoa4;
364 Outer<? super int[]>.Inner<int[]>[] aoa5;
365 Outer<int[]>.Inner<? super int[]>[] aoa6;
366 Outer<? super int[]>.Inner<? super int[]>[] aoa7;
367 Outer<?>.Inner<int[]>[] aoa8;
368 Outer<int[]>.Inner<?>[] aoa9;
369 Outer<?>.Inner<?>[] aoa10;
370
371 Outer<String>.Inner<String>[][] aao1;
372 Outer<? extends String>.Inner<String>[][] aao2;
373 Outer<String>.Inner<? extends String>[][] aao3;
374 Outer<? extends String>.Inner<? extends String>[][] aao4;
375 Outer<? super String>.Inner<String>[][] aao5;
376 Outer<String>.Inner<? super String>[][] aao6;
377 Outer<? super String>.Inner<? super String>[][] aao7;
378 Outer<?>.Inner<String>[][] aao8;
379 Outer<String>.Inner<?>[][] aao9;
380 Outer<?>.Inner<?>[][] aao10;
381
382 Outer<int[]>.Inner<int[]>[][] aaoa1;
383 Outer<? extends int[]>.Inner<int[]>[][] aaoa2;
384 Outer<int[]>.Inner<? extends int[]>[][] aaoa3;
385 Outer<? extends int[]>.Inner<? extends int[]>[][] aaoa4;
386 Outer<? super int[]>.Inner<int[]>[][] aaoa5;
387 Outer<int[]>.Inner<? super int[]>[][] aaoa6;
388 Outer<? super int[]>.Inner<? super int[]>[][] aaoa7;
389 Outer<?>.Inner<int[]>[][] aaoa8;
390 Outer<int[]>.Inner<?>[][] aaoa9;
391 Outer<?>.Inner<?>[][] aaoa10;
392 }
393
394 private static JavaType typeFromFlatString(String desc) {
395 return JavaTypeUtils.toJavaType(JavaTypeUtils.parseExternalTypeString(desc));
396 }
397
398 static class InnerTypes {
399
400 class Member {
401 class One {
402 class Two {
403 class Three { }
404 }
405 }
406 }
407
408 static class Nested { }
409
410 void m() {
411 class Local_I_M { }
412 }
413
414 static void s_m() {
415 class Local_S_M { }
416 }
417
418 InnerTypes() {
419 class Local_C { }
420 }
421 }
422
423 @Test
424 public void testInnerTypes() throws ReflectiveOperationException {
425 var innertypes = JavaType.type(InnerTypes.class);
426 var member = (ClassType)JavaType.type(InnerTypes.Member.class);
427 Assertions.assertEquals(innertypes, member.enclosingType().get());
428
429 var memberOne = (ClassType)JavaType.type(InnerTypes.Member.One.class);
430 Assertions.assertEquals(member, memberOne.enclosingType().get());
431 Assertions.assertEquals(InnerTypes.Member.One.class.getName(), memberOne.toClassName());
432
433 var memberTwo = (ClassType)JavaType.type(InnerTypes.Member.One.Two.class);
434 Assertions.assertEquals(memberOne, memberTwo.enclosingType().get());
435 Assertions.assertEquals(InnerTypes.Member.One.Two.class.getName(), memberTwo.toClassName());
436
437 var memberThree = (ClassType)JavaType.type(InnerTypes.Member.One.Two.Three.class);
438 Assertions.assertEquals(memberTwo, memberThree.enclosingType().get());
439 Assertions.assertEquals(InnerTypes.Member.One.Two.Three.class.getName(), memberThree.toClassName());
440
441 var nested = (ClassType)JavaType.type(InnerTypes.Nested.class);
442 Assertions.assertTrue(nested.enclosingType().isEmpty());
443
444 var local_s_m = (ClassType)JavaType.type(Class.forName("TestJavaType$InnerTypes$1Local_S_M"));
445 Assertions.assertTrue(local_s_m.enclosingType().isEmpty());
446
447 var local_i_m = (ClassType)JavaType.type(Class.forName("TestJavaType$InnerTypes$1Local_I_M"));
448 Assertions.assertEquals(innertypes, local_i_m.enclosingType().get());
449
450 var local_c = (ClassType)JavaType.type(Class.forName("TestJavaType$InnerTypes$1Local_C"));
451 Assertions.assertEquals(innertypes, local_c.enclosingType().get());
452 }
453 }