1 import jdk.incubator.code.Body;
  2 import jdk.incubator.code.CodeReflection;
  3 import jdk.incubator.code.Op;
  4 import jdk.incubator.code.OpTransformer;
  5 import jdk.incubator.code.bytecode.BytecodeGenerator;
  6 import jdk.incubator.code.dialect.core.CoreOp;
  7 import jdk.incubator.code.dialect.java.JavaOp;
  8 import jdk.incubator.code.dialect.java.JavaType;
  9 import jdk.incubator.code.dialect.java.MethodRef;
 10 import jdk.incubator.code.interpreter.Interpreter;
 11 import org.junit.jupiter.api.Assertions;
 12 import org.junit.jupiter.api.Test;
 13 import org.junit.jupiter.params.ParameterizedTest;
 14 import org.junit.jupiter.params.provider.MethodSource;
 15 
 16 import java.lang.invoke.MethodHandles;
 17 import java.lang.reflect.Method;
 18 import java.lang.runtime.ExactConversionsSupport;
 19 import java.util.List;
 20 import java.util.Optional;
 21 import java.util.stream.Stream;
 22 
 23 import static jdk.incubator.code.dialect.core.CoreOp.*;
 24 import static jdk.incubator.code.dialect.core.CoreType.functionType;
 25 import static jdk.incubator.code.dialect.java.JavaOp.match;
 26 import static jdk.incubator.code.dialect.java.JavaOp.typePattern;
 27 import static jdk.incubator.code.dialect.java.PrimitiveType.*;
 28 
 29 /*
 30  * @test
 31  * @modules jdk.incubator.code
 32  * @run junit TestPrimitiveTypePatterns
 33  * @enablePreview
 34  */
 35 
 36 public class TestPrimitiveTypePatterns {
 37 
 38     static MethodRef conversionMethodRef(JavaType sourceType, JavaType targetType) {
 39         if (SHORT.equals(sourceType) || CHAR.equals(sourceType)) {
 40             sourceType = INT;
 41         }
 42         String n = "is%sTo%sExact".formatted(capitalize(sourceType.toString()), capitalize(targetType.toString()));
 43         JavaType c = JavaType.type(ExactConversionsSupport.class);
 44         return MethodRef.method(c, n, BOOLEAN, sourceType);
 45     }
 46 
 47     static String capitalize(String s) {
 48         return s.substring(0, 1).toUpperCase() + s.substring(1);
 49     }
 50 
 51     public static Object[][] narrowingPrimitiveAndWideningPrimitiveThatNeedCheck() {
 52         return new Object[][]{
 53                 {JavaType.INT, JavaType.BYTE, new Object[] {
 54                         Byte.MIN_VALUE - 1, Byte.MIN_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE + 1
 55                 }},
 56                 {JavaType.INT, JavaType.SHORT, new Object[] {
 57                         Short.MIN_VALUE - 1, Short.MIN_VALUE, Short.MAX_VALUE, Short.MAX_VALUE + 1
 58                 }},
 59                 {JavaType.INT, JavaType.CHAR, new Object[] {
 60                         Character.MIN_VALUE - 1, Character.MIN_VALUE, Character.MAX_VALUE, Character.MAX_VALUE + 1
 61                 }},
 62                 // (1<<24) + 1 : first int that's not an instanceof float
 63                 // 1<<31) - (1<<7): largest int that's an instance of float
 64                 {JavaType.INT, JavaType.FLOAT, new Object[] {
 65                         1<<24, (1<<24) + 1, (1<<31) - (1<<7), (1<<31) - (1<<7) + 1, Integer.MAX_VALUE, Integer.MIN_VALUE
 66                 }},
 67 
 68                 {JavaType.SHORT, JavaType.BYTE, new Object[]{
 69                         (short) (Byte.MIN_VALUE - 1), Byte.MIN_VALUE, Byte.MAX_VALUE, (short) (Byte.MAX_VALUE + 1)
 70                 }},
 71                 {JavaType.SHORT, JavaType.CHAR, new Object[]{
 72                         Short.MIN_VALUE, (short) -1, (short) 0, Short.MAX_VALUE
 73                 }},
 74 
 75                 {JavaType.CHAR, JavaType.BYTE, new Object[]{
 76                         (char) 0, (char) Byte.MAX_VALUE, (char) (Byte.MAX_VALUE + 1)
 77                 }},
 78                 {JavaType.CHAR, JavaType.SHORT, new Object[]{
 79                         (char) 0, (char) Short.MAX_VALUE, (char) (Short.MAX_VALUE + 1)
 80                 }},
 81 
 82                 {JavaType.LONG, JavaType.BYTE, new Object[] {
 83                         Byte.MIN_VALUE - 1, Byte.MIN_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE + 1
 84                 }},
 85                 {JavaType.LONG, JavaType.SHORT, new Object[] {
 86                         Short.MIN_VALUE - 1, Short.MIN_VALUE, Short.MAX_VALUE, Short.MAX_VALUE + 1
 87                 }},
 88                 {JavaType.LONG, JavaType.CHAR, new Object[] {
 89                         Character.MIN_VALUE - 1, Character.MIN_VALUE, Character.MAX_VALUE, Character.MAX_VALUE + 1
 90                 }},
 91                 {JavaType.LONG, JavaType.INT, new Object[] {
 92                         (long)Integer.MIN_VALUE - 1, Integer.MIN_VALUE, Integer.MAX_VALUE, (long)Integer.MAX_VALUE + 1
 93                 }},
 94                 // (1<<24) + 1 : first long that can't be represented as float
 95                 // (1L<<63) - (1L<<39) : largest long that can be represented as float
 96                 {JavaType.LONG, JavaType.FLOAT, new Object[] {
 97                         Long.MIN_VALUE, (1L<<24), (1<<24) + 1, (1L<<63) - (1L<<39), (1L<<63) - (1L<<39) + 1, Long.MAX_VALUE
 98                 }},
 99                 // (1L<<53) + 1 : first long that can't be represented as double
100                 // (1L<<63) - (1<<10) : largest long that can be represented as double
101                 {JavaType.LONG, JavaType.DOUBLE, new Object[] {
102                         Long.MIN_VALUE, 1L<<53, (1L<<53) + 1, (1L<<63) - (1<<10), (1L<<63) - (1<<10) + 1, Long.MAX_VALUE
103                 }},
104 
105                 {JavaType.FLOAT, JavaType.BYTE, new Object[] {
106                         Byte.MIN_VALUE - 1, Byte.MIN_VALUE, Byte.MAX_VALUE, Byte.MIN_VALUE + 1
107                 }},
108                 {JavaType.FLOAT, JavaType.SHORT, new Object[] {
109                         Short.MIN_VALUE - 1, Short.MIN_VALUE, Short.MAX_VALUE, Short.MAX_VALUE + 1
110                 }},
111                 {JavaType.FLOAT, JavaType.CHAR, new Object[] {
112                         Character.MIN_VALUE - 1, Character.MIN_VALUE, Character.MAX_VALUE, Character.MAX_VALUE + 1
113                 }},
114                 {JavaType.FLOAT, JavaType.INT, new Object[] {
115                         Float.MIN_VALUE, Float.NEGATIVE_INFINITY, 0f, Float.POSITIVE_INFINITY, Float.MAX_VALUE
116                 }},
117                 {JavaType.FLOAT, JavaType.LONG, new Object[] {
118                         Float.MIN_VALUE, Float.NEGATIVE_INFINITY, 0f, Float.POSITIVE_INFINITY, Float.MAX_VALUE
119                 }},
120 
121                 {JavaType.DOUBLE, JavaType.BYTE, new Object[] {
122                         Double.NEGATIVE_INFINITY, Double.MIN_VALUE, -0d, +0d, Double.MAX_VALUE, Double.POSITIVE_INFINITY, Double.NaN
123                 }},
124                 {JavaType.DOUBLE, JavaType.SHORT, new Object[] {
125                         Double.NEGATIVE_INFINITY, Double.MIN_VALUE, -0d, +0d, Double.MAX_VALUE, Double.POSITIVE_INFINITY, Double.NaN
126                 }},
127                 {JavaType.DOUBLE, JavaType.CHAR, new Object[] {
128                         Double.NEGATIVE_INFINITY, Double.MIN_VALUE, -0d, +0d, Double.MAX_VALUE, Double.POSITIVE_INFINITY, Double.NaN
129                 }},
130                 {JavaType.DOUBLE, JavaType.INT, new Object[] {
131                         Double.NEGATIVE_INFINITY, Double.MIN_VALUE, -0d, +0d, Double.MAX_VALUE, Double.POSITIVE_INFINITY, Double.NaN
132                 }},
133                 {JavaType.DOUBLE, JavaType.LONG, new Object[] {
134                         Double.NEGATIVE_INFINITY, Double.MIN_VALUE, -0d, +0d, Double.MAX_VALUE, Double.POSITIVE_INFINITY, Double.NaN
135                 }},
136                 {JavaType.DOUBLE, JavaType.FLOAT, new Object[] {
137                         Double.NEGATIVE_INFINITY, Double.MIN_VALUE, -0d, +0d, Double.MAX_VALUE, Double.POSITIVE_INFINITY, Double.NaN
138                 }}
139 
140         };
141     }
142 
143     @ParameterizedTest
144     @MethodSource("narrowingPrimitiveAndWideningPrimitiveThatNeedCheck")
145     void testNarrowingPrimitiveAndWideningPrimitiveThatNeedCheck(JavaType sourceType, JavaType targetType, Object[] values) throws Throwable {
146 
147         var model = buildTypePatternModel(sourceType, targetType);
148         System.out.println(model.toText());
149 
150         var lmodel = model.transform(OpTransformer.LOWERING_TRANSFORMER);
151         System.out.println(lmodel.toText());
152 
153 
154         var expectedConvMethod = conversionMethodRef(sourceType, targetType);
155         var actualConvMethod = lmodel.elements()
156                 .mapMulti((ce, c) -> {
157                     if (ce instanceof JavaOp.InvokeOp op) {
158                         c.accept(op.invokeDescriptor());
159                     }
160                 })
161                 .findFirst().orElseThrow();
162         Assertions.assertEquals(expectedConvMethod, actualConvMethod);
163 
164         var mh = BytecodeGenerator.generate(MethodHandles.lookup(), lmodel);
165 
166         for (Object v : values) {
167             Assertions.assertEquals(mh.invoke(v), Interpreter.invoke(MethodHandles.lookup(), lmodel, v));
168         }
169     }
170 
171     @CodeReflection
172     static boolean identityPrimitive(short s) {
173         return s instanceof short _;
174     }
175 
176     @Test
177     void testIdentityPrimitive() {
178         FuncOp f = getFuncOp("identityPrimitive");
179         System.out.println(f.toText());
180 
181         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
182         System.out.println(lf.toText());
183 
184         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Short.MAX_VALUE));
185     }
186 
187     @CodeReflection
188     static boolean wideningNarrowingPrimitive(byte s) {
189         return s instanceof char _;
190     }
191 
192     @Test
193     void testWideningNarrowingPrimitive() {
194         FuncOp f = getFuncOp("wideningNarrowingPrimitive");
195         System.out.println(f.toText());
196 
197         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
198         System.out.println(lf.toText());
199 
200         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Byte.MAX_VALUE));
201         Assertions.assertEquals(false, Interpreter.invoke(MethodHandles.lookup(), lf, Byte.MIN_VALUE));
202     }
203 
204     @CodeReflection
205     static boolean boxing(int s) {
206         return s instanceof Integer _;
207     }
208 
209     @Test
210     void testBoxing() {
211         FuncOp f = getFuncOp("boxing");
212         System.out.println(f.toText());
213 
214         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
215         System.out.println(lf.toText());
216 
217         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MAX_VALUE));
218         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MIN_VALUE));
219     }
220 
221     @CodeReflection
222     static boolean boxingWideningReference(int s) {
223         return s instanceof Number _;
224     }
225 
226     @Test
227     void testBoxingWideningReference() {
228         FuncOp f = getFuncOp("boxingWideningReference");
229         System.out.println(f.toText());
230 
231         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
232         System.out.println(lf.toText());
233 
234         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MAX_VALUE));
235         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MIN_VALUE));
236     }
237 
238     @CodeReflection
239     static boolean narrowingReferenceUnboxing(Number n) {
240         return n instanceof int _;
241     }
242 
243     @Test
244     void testNarrowingReferenceUnboxing() {
245         FuncOp f = getFuncOp("narrowingReferenceUnboxing");
246         System.out.println(f.toText());
247 
248         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
249         System.out.println(lf.toText());
250 
251         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, 1));
252         Assertions.assertEquals(false, Interpreter.invoke(MethodHandles.lookup(), lf, (short) 1));
253     }
254 
255     @CodeReflection
256     static boolean unboxing(Integer n) {
257         return n instanceof int _;
258     }
259 
260     @Test
261     void testUnboxing() {
262         FuncOp f = getFuncOp("unboxing");
263         System.out.println(f.toText());
264 
265         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
266         System.out.println(lf.toText());
267 
268         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MAX_VALUE));
269         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MIN_VALUE));
270     }
271 
272     @CodeReflection
273     static boolean unboxingWideningPrimitive(Integer n) {
274         return n instanceof long _;
275     }
276 
277     @Test
278     void testUnboxingWideningPrimitive() {
279         FuncOp f = getFuncOp("unboxingWideningPrimitive");
280         System.out.println(f.toText());
281 
282         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
283         System.out.println(lf.toText());
284 
285         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MAX_VALUE));
286         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MIN_VALUE));
287     }
288 
289     @CodeReflection
290     static boolean wideningReference(String s) {
291         return s instanceof Object _;
292     }
293 
294     @Test
295     void testWideningReference() {
296         FuncOp f = getFuncOp("wideningReference");
297         System.out.println(f.toText());
298 
299         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
300         System.out.println(lf.toText());
301 
302         Assertions.assertEquals(false, Interpreter.invoke(MethodHandles.lookup(), lf, (Object) null));
303         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, "str"));
304     }
305 
306     @CodeReflection
307     static boolean identityReference(Float f) {
308         return f instanceof Float _;
309     }
310 
311     @Test
312     void testIdentityReference() {
313         FuncOp f = getFuncOp("identityReference");
314         System.out.println(f.toText());
315 
316         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
317         System.out.println(lf.toText());
318 
319         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Float.MAX_VALUE));
320         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Float.MIN_VALUE));
321         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Float.POSITIVE_INFINITY));
322         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Float.NEGATIVE_INFINITY));
323     }
324 
325     @CodeReflection
326     static boolean narrowingReference(Number n) {
327         return n instanceof Double _;
328     }
329 
330     @Test
331     void testNarrowingReference() {
332         FuncOp f = getFuncOp("narrowingReference");
333         System.out.println(f.toText());
334 
335         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
336         System.out.println(lf.toText());
337 
338         Assertions.assertEquals(false, Interpreter.invoke(MethodHandles.lookup(), lf, Float.MAX_VALUE));
339         Assertions.assertEquals(false, Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MIN_VALUE));
340         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Double.POSITIVE_INFINITY));
341         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Double.NEGATIVE_INFINITY));
342     }
343 
344     @CodeReflection
345     static boolean wideningPrimitive(int i) {
346         return i instanceof long _;
347     }
348 
349     @Test
350     void testWideningPrimitive() {
351         FuncOp f = getFuncOp("wideningPrimitive");
352         System.out.println(f.toText());
353 
354         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
355         System.out.println(lf.toText());
356 
357         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MAX_VALUE));
358         Assertions.assertEquals(true, Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MIN_VALUE));
359     }
360 
361      private CoreOp.FuncOp getFuncOp(String name) {
362         Optional<Method> om = Stream.of(this.getClass().getDeclaredMethods())
363                 .filter(m -> m.getName().equals(name))
364                 .findFirst();
365 
366         Method m = om.get();
367         return Op.ofMethod(m).get();
368     }
369 
370     static FuncOp buildTypePatternModel(JavaType sourceType, JavaType targetType) {
371         // builds the model of:
372         // static boolean f(sourceType a) { return a instanceof targetType _; }
373         return func(sourceType + "_" + targetType, functionType(JavaType.BOOLEAN, sourceType)).body(fblock -> {
374 
375             var paramVal = fblock.parameters().get(0);
376 
377             var patternVar = fblock.op(var(fblock.op(constant(targetType, defaultValue(targetType)))));
378 
379             var pattern = Body.Builder.of(fblock.parentBody(), functionType(JavaOp.Pattern.bindingType(targetType)));
380             pattern.entryBlock().op(core_yield(
381                     pattern.entryBlock().op(typePattern(targetType, null))
382             ));
383 
384             var match = Body.Builder.of(fblock.parentBody(), functionType(JavaType.VOID, targetType));
385             var binding = match.entryBlock().parameters().get(0);
386             match.entryBlock().op(varStore(patternVar, binding));
387             match.entryBlock().op(core_yield());
388 
389             var result = fblock.op(match(paramVal, pattern, match));
390 
391             fblock.op(return_(result));
392         });
393     }
394 
395     static Object defaultValue(JavaType t) {
396         if (List.of(BYTE, SHORT, CHAR, INT).contains(t)) {
397             return 0;
398         } else if (LONG.equals(t)) {
399             return 0L;
400         } else if (FLOAT.equals(t)) {
401             return 0f;
402         } else if (DOUBLE.equals(t)) {
403             return 0d;
404         } else if (BOOLEAN.equals(t)) {
405             return false;
406         }
407         return null;
408     }
409 }