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 }