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.
  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     @ParameterizedTest
189     @MethodSource("typesXtypes")
190     public void testTypeEquals(Type type1, Type type2) throws ReflectiveOperationException {
191         JavaType javaType1 = JavaType.type(type1);
192         JavaType javaType2 = JavaType.type(type2);
193         String typeString1 = replaceTypeVariables(javaType1).toString()
194                 .replaceAll("::", "\\$");
195         String typeString2 = replaceTypeVariables(javaType2).toString()
196                 .replaceAll("::", "\\$");
197         boolean equals = typeString1.equals(typeString2);
198         Assertions.assertEquals(equals, javaType1.equals(javaType2));
199         Assertions.assertEquals(equals, javaType2.equals(javaType1));
200         if (equals) {
201             Assertions.assertEquals(javaType1.hashCode(), javaType2.hashCode());
202         }
203     }
204 
205     JavaType replaceTypeVariables(JavaType type) {
206         // This type transformation replaces type variables with simple class types.
207         // This obtains a JavaType whose toString behaves the same as Type::getTypeName
208         return switch (type) {
209             case PrimitiveType p -> p;
210             case WildcardType w -> JavaType.wildcard(w.boundKind(), replaceTypeVariables(w.boundType()));
211             case ArrayType a -> JavaType.array(replaceTypeVariables(a.componentType()));
212             case ClassType c -> {
213                 ClassType res = c.rawType();
214                 if (c.enclosingType().isPresent()) {
215                     JavaType encl = replaceTypeVariables(c.enclosingType().get());
216                     String nestedName = c.toClassName().substring(encl.toNominalDescriptor().displayName().length() + 1);
217                     res = JavaType.qualified(replaceTypeVariables(c.enclosingType().get()), nestedName);
218                 }
219                 if (c.hasTypeArguments()) {
220                     res = JavaType.parameterized(res,
221                             c.typeArguments().stream().map(this::replaceTypeVariables).toList());
222                 }
223                 yield res;
224             }
225             case TypeVariableType t -> JavaType.type(ClassDesc.of(t.name()));
226         };
227     }
228 
229     public static Object[][] types() throws ReflectiveOperationException {
230         List<Object[]> types = new ArrayList<>();
231         for (Field f : TypeHolder.class.getDeclaredFields()) {
232             types.add(new Object[] { f.getGenericType() });
233         }
234         return types.stream().toArray(Object[][]::new);
235     }
236 
237     static class TypeHolder<X extends Number> {
238         boolean p1;
239         char p2;
240         byte p3;
241         short p4;
242         int p5;
243         long p6;
244         float p7;
245         double p8;
246 
247         boolean[] ap1;
248         char[] ap2;
249         byte[] ap3;
250         short[] ap4;
251         int[] ap5;
252         long[] ap6;
253         float[] ap7;
254         double[] ap8;
255 
256         boolean[][] aap1;
257         char[][] aap2;
258         byte[][] aap3;
259         short[][] aap4;
260         int[][] aap5;
261         long[][] aap6;
262         float[][] aap7;
263         double[][] aap8;
264 
265         String r1;
266         Map<String, String> r2;
267         Map<String, ?  extends String> r3;
268         Map<? extends String, String> r4;
269         Map<? extends String, ?  extends String> r5;
270         Map<? extends List<? extends String>, ? super List<? extends String>> r6;
271         Map<? extends List<? extends String>[], ? super List<? extends String>[]> r7;
272         List<boolean[]> r8;
273         List<char[]> r9;
274         List<byte[]> r10;
275         List<short[]> r11;
276         List<int[]> r12;
277         List<long[]> r13;
278         List<float[]> r14;
279         List<double[]> r15;
280 
281         String[] ar1;
282         Map<String, String>[] ar2;
283         Map<String, ?  extends String>[] ar3;
284         Map<? extends String, String>[] ar4;
285         Map<? extends String, ?  extends String>[] ar5;
286         Map<? extends List<? extends String>, ? super List<? extends String>>[] ar6;
287         Map<? extends List<? extends String>[], ? super List<? extends String>[]>[] ar7;
288         List<boolean[]>[] ar8;
289         List<char[]>[] ar9;
290         List<byte[]>[] ar10;
291         List<short[]>[] ar11;
292         List<int[]>[] ar12;
293         List<long[]>[] ar13;
294         List<float[]>[] ar14;
295         List<double[]>[] ar15;
296 
297         String[][] aar1;
298         Map<String, String>[][] aar2;
299         Map<String, ?  extends String>[][] aar3;
300         Map<? extends String, String>[][] aar4;
301         Map<? extends String, ?  extends String>[][] aar5;
302         Map<? extends List<? extends String>, ? super List<? extends String>>[][] aar6;
303         Map<? extends List<? extends String>[], ? super List<? extends String>[]>[][] aar7;
304         List<boolean[]>[][] aar8;
305         List<char[]>[][] aar9;
306         List<byte[]>[][] aar10;
307         List<short[]>[][] aar11;
308         List<int[]>[][] aar12;
309         List<long[]>[][] aar13;
310         List<float[]>[][] aar14;
311         List<double[]>[][] aar15;
312 
313         X x1;
314         Map<X, X> x2;
315         Map<X, ?  extends X> x3;
316         Map<? extends X, X> x4;
317         Map<? extends X, ?  extends X> x5;
318         Map<? extends List<? extends X>, ? super List<? extends X>> x6;
319         Map<? extends List<? extends X>[], ? super List<? extends X>[]> x7;
320         List<X[]> x8;
321 
322         X[] ax1;
323         Map<X, X>[] ax2;
324         Map<X, ?  extends X>[] ax3;
325         Map<? extends X, X>[] ax4;
326         Map<? extends X, ?  extends X>[] ax5;
327         Map<? extends List<? extends X>, ? super List<? extends X>>[] ax6;
328         Map<? extends List<? extends X>[], ? super List<? extends X>[]>[] ax7;
329         List<X[]>[] ax8;
330 
331         X[][] aax1;
332         Map<X, X>[][] aax2;
333         Map<X, ?  extends X>[][] aax3;
334         Map<? extends X, X>[][] aax4;
335         Map<? extends X, ?  extends X>[][] aax5;
336         Map<? extends List<? extends X>, ? super List<? extends X>>[][] aax6;
337         Map<? extends List<? extends X>[], ? super List<? extends X>[]>[][] aax7;
338         List<X[]>[][] aax8;
339 
340         static class Outer<X> {
341             class Inner<X> { }
342         }
343 
344         Outer<String>.Inner<String> o1;
345         Outer<? extends String>.Inner<String> o2;
346         Outer<String>.Inner<? extends String> o3;
347         Outer<? extends String>.Inner<? extends String> o4;
348         Outer<? super String>.Inner<String> o5;
349         Outer<String>.Inner<? super String> o6;
350         Outer<? super String>.Inner<? super String> o7;
351         Outer<?>.Inner<String> o8;
352         Outer<String>.Inner<?> o9;
353         Outer<?>.Inner<?> o10;
354 
355         Outer<int[]>.Inner<int[]> oa1;
356         Outer<? extends int[]>.Inner<int[]> oa2;
357         Outer<int[]>.Inner<? extends int[]> oa3;
358         Outer<? extends int[]>.Inner<? extends int[]> oa4;
359         Outer<? super int[]>.Inner<int[]> oa5;
360         Outer<int[]>.Inner<? super int[]> oa6;
361         Outer<? super int[]>.Inner<? super int[]> oa7;
362         Outer<?>.Inner<int[]> oa8;
363         Outer<int[]>.Inner<?> oa9;
364         Outer<?>.Inner<?> oa10;
365 
366         Outer<String>.Inner<String>[] ao1;
367         Outer<? extends String>.Inner<String>[] ao2;
368         Outer<String>.Inner<? extends String>[] ao3;
369         Outer<? extends String>.Inner<? extends String>[] ao4;
370         Outer<? super String>.Inner<String>[] ao5;
371         Outer<String>.Inner<? super String>[] ao6;
372         Outer<? super String>.Inner<? super String>[] ao7;
373         Outer<?>.Inner<String>[] ao8;
374         Outer<String>.Inner<?>[] ao9;
375         Outer<?>.Inner<?>[] ao10;
376 
377         Outer<int[]>.Inner<int[]>[] aoa1;
378         Outer<? extends int[]>.Inner<int[]>[] aoa2;
379         Outer<int[]>.Inner<? extends int[]>[] aoa3;
380         Outer<? extends int[]>.Inner<? extends int[]>[] aoa4;
381         Outer<? super int[]>.Inner<int[]>[] aoa5;
382         Outer<int[]>.Inner<? super int[]>[] aoa6;
383         Outer<? super int[]>.Inner<? super int[]>[] aoa7;
384         Outer<?>.Inner<int[]>[] aoa8;
385         Outer<int[]>.Inner<?>[] aoa9;
386         Outer<?>.Inner<?>[] aoa10;
387 
388         Outer<String>.Inner<String>[][] aao1;
389         Outer<? extends String>.Inner<String>[][] aao2;
390         Outer<String>.Inner<? extends String>[][] aao3;
391         Outer<? extends String>.Inner<? extends String>[][] aao4;
392         Outer<? super String>.Inner<String>[][] aao5;
393         Outer<String>.Inner<? super String>[][] aao6;
394         Outer<? super String>.Inner<? super String>[][] aao7;
395         Outer<?>.Inner<String>[][] aao8;
396         Outer<String>.Inner<?>[][] aao9;
397         Outer<?>.Inner<?>[][] aao10;
398 
399         Outer<int[]>.Inner<int[]>[][] aaoa1;
400         Outer<? extends int[]>.Inner<int[]>[][] aaoa2;
401         Outer<int[]>.Inner<? extends int[]>[][] aaoa3;
402         Outer<? extends int[]>.Inner<? extends int[]>[][] aaoa4;
403         Outer<? super int[]>.Inner<int[]>[][] aaoa5;
404         Outer<int[]>.Inner<? super int[]>[][] aaoa6;
405         Outer<? super int[]>.Inner<? super int[]>[][] aaoa7;
406         Outer<?>.Inner<int[]>[][] aaoa8;
407         Outer<int[]>.Inner<?>[][] aaoa9;
408         Outer<?>.Inner<?>[][] aaoa10;
409     }
410 
411     private static JavaType typeFromFlatString(String desc) {
412         return JavaTypeUtils.toJavaType(JavaTypeUtils.parseExternalTypeString(desc));
413     }
414 
415     static class InnerTypes {
416 
417         class Member {
418             class One {
419                 class Two {
420                     class Three { }
421                 }
422             }
423         }
424 
425         static class Nested { }
426 
427         void m() {
428             class Local_I_M { }
429         }
430 
431         static void s_m() {
432             class Local_S_M { }
433         }
434 
435         InnerTypes() {
436             class Local_C { }
437         }
438     }
439 
440     @Test
441     public void testInnerTypes() throws ReflectiveOperationException {
442         var innertypes = JavaType.type(InnerTypes.class);
443         var member = (ClassType)JavaType.type(InnerTypes.Member.class);
444         Assertions.assertEquals(innertypes, member.enclosingType().get());
445 
446         var memberOne = (ClassType)JavaType.type(InnerTypes.Member.One.class);
447         Assertions.assertEquals(member, memberOne.enclosingType().get());
448         Assertions.assertEquals(InnerTypes.Member.One.class.getName(), memberOne.toClassName());
449 
450         var memberTwo = (ClassType)JavaType.type(InnerTypes.Member.One.Two.class);
451         Assertions.assertEquals(memberOne, memberTwo.enclosingType().get());
452         Assertions.assertEquals(InnerTypes.Member.One.Two.class.getName(), memberTwo.toClassName());
453 
454         var memberThree = (ClassType)JavaType.type(InnerTypes.Member.One.Two.Three.class);
455         Assertions.assertEquals(memberTwo, memberThree.enclosingType().get());
456         Assertions.assertEquals(InnerTypes.Member.One.Two.Three.class.getName(), memberThree.toClassName());
457 
458         var nested = (ClassType)JavaType.type(InnerTypes.Nested.class);
459         Assertions.assertTrue(nested.enclosingType().isEmpty());
460 
461         var local_s_m = (ClassType)JavaType.type(Class.forName("TestJavaType$InnerTypes$1Local_S_M"));
462         Assertions.assertTrue(local_s_m.enclosingType().isEmpty());
463 
464         var local_i_m = (ClassType)JavaType.type(Class.forName("TestJavaType$InnerTypes$1Local_I_M"));
465         Assertions.assertEquals(innertypes, local_i_m.enclosingType().get());
466 
467         var local_c = (ClassType)JavaType.type(Class.forName("TestJavaType$InnerTypes$1Local_C"));
468         Assertions.assertEquals(innertypes, local_c.enclosingType().get());
469     }
470 
471     public static Object[][] typesXtypes() throws ReflectiveOperationException {
472         List<Object[]> types = new ArrayList<>();
473         for (Field f1 : TypeHolder.class.getDeclaredFields()) {
474             for (Field f2 : TypeHolder.class.getDeclaredFields()) {
475                 types.add(new Object[]{f1.getGenericType(), f2.getGenericType()});
476             }
477         }
478         return types.stream().toArray(Object[][]::new);
479     }
480 }