1 import jdk.incubator.code.Op;
  2 import org.testng.Assert;
  3 import org.testng.annotations.DataProvider;
  4 import org.testng.annotations.Test;
  5 
  6 import java.lang.invoke.MethodHandles;
  7 import java.lang.reflect.Method;
  8 import jdk.incubator.code.Body;
  9 import jdk.incubator.code.OpTransformer;
 10 import jdk.incubator.code.bytecode.BytecodeGenerator;
 11 import jdk.incubator.code.interpreter.Interpreter;
 12 import jdk.incubator.code.op.CoreOp;
 13 import jdk.incubator.code.op.ExtendedOp;
 14 import jdk.incubator.code.type.JavaType;
 15 import jdk.incubator.code.type.MethodRef;
 16 import jdk.incubator.code.CodeReflection;
 17 import java.lang.runtime.ExactConversionsSupport;
 18 import java.util.List;
 19 import java.util.Optional;
 20 import java.util.stream.Stream;
 21 
 22 import static jdk.incubator.code.op.CoreOp.*;
 23 import static jdk.incubator.code.op.ExtendedOp.match;
 24 import static jdk.incubator.code.op.ExtendedOp.typePattern;
 25 import static jdk.incubator.code.type.FunctionType.functionType;
 26 import static jdk.incubator.code.type.PrimitiveType.*;
 27 
 28 /*
 29  * @test
 30  * @modules jdk.incubator.code
 31  * @run testng TestPrimitiveTypePatterns
 32  * @enablePreview
 33  */
 34 
 35 public class TestPrimitiveTypePatterns {
 36 
 37     static MethodRef conversionMethodRef(JavaType sourceType, JavaType targetType) {
 38         if (SHORT.equals(sourceType) || CHAR.equals(sourceType)) {
 39             sourceType = INT;
 40         }
 41         String n = "is%sTo%sExact".formatted(capitalize(sourceType.toString()), capitalize(targetType.toString()));
 42         JavaType c = JavaType.type(ExactConversionsSupport.class);
 43         return MethodRef.method(c, n, BOOLEAN, sourceType);
 44     }
 45 
 46     static String capitalize(String s) {
 47         return s.substring(0, 1).toUpperCase() + s.substring(1);
 48     }
 49 
 50     @DataProvider
 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     @Test(dataProvider = "narrowingPrimitiveAndWideningPrimitiveThatNeedCheck")
144     void testNarrowingPrimitiveAndWideningPrimitiveThatNeedCheck(JavaType sourceType, JavaType targetType, Object[] values) throws Throwable {
145 
146         var model = buildTypePatternModel(sourceType, targetType);
147         model.writeTo(System.out);
148 
149         var lmodel = model.transform(OpTransformer.LOWERING_TRANSFORMER);
150         lmodel.writeTo(System.out);
151 
152 
153         var expectedConvMethod = conversionMethodRef(sourceType, targetType);
154         var actualConvMethod = lmodel.elements()
155                 .mapMulti((ce, c) -> {
156                     if (ce instanceof InvokeOp op) {
157                         c.accept(op.invokeDescriptor());
158                     }
159                 })
160                 .findFirst().orElseThrow();
161         Assert.assertEquals(actualConvMethod, expectedConvMethod);
162 
163         var mh = BytecodeGenerator.generate(MethodHandles.lookup(), lmodel);
164 
165         for (Object v : values) {
166             Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lmodel, v), mh.invoke(v));
167         }
168     }
169 
170     @CodeReflection
171     static boolean identityPrimitive(short s) {
172         return s instanceof short _;
173     }
174 
175     @Test
176     void testIdentityPrimitive() {
177         FuncOp f = getFuncOp("identityPrimitive");
178         f.writeTo(System.out);
179 
180         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
181         lf.writeTo(System.out);
182 
183         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Short.MAX_VALUE), true);
184     }
185 
186     @CodeReflection
187     static boolean wideningNarrowingPrimitive(byte s) {
188         return s instanceof char _;
189     }
190 
191     @Test
192     void testWideningNarrowingPrimitive() {
193         FuncOp f = getFuncOp("wideningNarrowingPrimitive");
194         f.writeTo(System.out);
195 
196         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
197         lf.writeTo(System.out);
198 
199         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Byte.MAX_VALUE), true);
200         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Byte.MIN_VALUE), false);
201     }
202 
203     @CodeReflection
204     static boolean boxing(int s) {
205         return s instanceof Integer _;
206     }
207 
208     @Test
209     void testBoxing() {
210         FuncOp f = getFuncOp("boxing");
211         f.writeTo(System.out);
212 
213         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
214         lf.writeTo(System.out);
215 
216         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MAX_VALUE), true);
217         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MIN_VALUE), true);
218     }
219 
220     @CodeReflection
221     static boolean boxingWideningReference(int s) {
222         return s instanceof Number _;
223     }
224 
225     @Test
226     void testBoxingWideningReference() {
227         FuncOp f = getFuncOp("boxingWideningReference");
228         f.writeTo(System.out);
229 
230         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
231         lf.writeTo(System.out);
232 
233         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MAX_VALUE), true);
234         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MIN_VALUE), true);
235     }
236 
237     @CodeReflection
238     static boolean narrowingReferenceUnboxing(Number n) {
239         return n instanceof int _;
240     }
241 
242     @Test
243     void testNarrowingReferenceUnboxing() {
244         FuncOp f = getFuncOp("narrowingReferenceUnboxing");
245         f.writeTo(System.out);
246 
247         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
248         lf.writeTo(System.out);
249 
250         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, 1), true);
251         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, (short) 1), false);
252     }
253 
254     @CodeReflection
255     static boolean unboxing(Integer n) {
256         return n instanceof int _;
257     }
258 
259     @Test
260     void testUnboxing() {
261         FuncOp f = getFuncOp("unboxing");
262         f.writeTo(System.out);
263 
264         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
265         lf.writeTo(System.out);
266 
267         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MAX_VALUE), true);
268         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MIN_VALUE), true);
269     }
270 
271     @CodeReflection
272     static boolean unboxingWideningPrimitive(Integer n) {
273         return n instanceof long _;
274     }
275 
276     @Test
277     void testUnboxingWideningPrimitive() {
278         FuncOp f = getFuncOp("unboxingWideningPrimitive");
279         f.writeTo(System.out);
280 
281         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
282         lf.writeTo(System.out);
283 
284         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MAX_VALUE), true);
285         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MIN_VALUE), true);
286     }
287 
288     @CodeReflection
289     static boolean wideningReference(String s) {
290         return s instanceof Object _;
291     }
292 
293     @Test
294     void testWideningReference() {
295         FuncOp f = getFuncOp("wideningReference");
296         f.writeTo(System.out);
297 
298         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
299         lf.writeTo(System.out);
300 
301         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, (Object) null), false);
302         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, "str"), true);
303     }
304 
305     @CodeReflection
306     static boolean identityReference(Float f) {
307         return f instanceof Float _;
308     }
309 
310     @Test
311     void testIdentityReference() {
312         FuncOp f = getFuncOp("identityReference");
313         f.writeTo(System.out);
314 
315         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
316         lf.writeTo(System.out);
317 
318         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Float.MAX_VALUE), true);
319         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Float.MIN_VALUE), true);
320         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Float.POSITIVE_INFINITY), true);
321         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Float.NEGATIVE_INFINITY), true);
322     }
323 
324     @CodeReflection
325     static boolean narrowingReference(Number n) {
326         return n instanceof Double _;
327     }
328 
329     @Test
330     void testNarrowingReference() {
331         FuncOp f = getFuncOp("narrowingReference");
332         f.writeTo(System.out);
333 
334         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
335         lf.writeTo(System.out);
336 
337         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Float.MAX_VALUE), false);
338         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MIN_VALUE), false);
339         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Double.POSITIVE_INFINITY), true);
340         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Double.NEGATIVE_INFINITY), true);
341     }
342 
343     @CodeReflection
344     static boolean wideningPrimitive(int i) {
345         return i instanceof long _;
346     }
347 
348     @Test
349     void testWideningPrimitive() {
350         FuncOp f = getFuncOp("wideningPrimitive");
351         f.writeTo(System.out);
352 
353         FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
354         lf.writeTo(System.out);
355 
356         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MAX_VALUE), true);
357         Assert.assertEquals(Interpreter.invoke(MethodHandles.lookup(), lf, Integer.MIN_VALUE), true);
358     }
359 
360      private CoreOp.FuncOp getFuncOp(String name) {
361         Optional<Method> om = Stream.of(this.getClass().getDeclaredMethods())
362                 .filter(m -> m.getName().equals(name))
363                 .findFirst();
364 
365         Method m = om.get();
366         return Op.ofMethod(m).get();
367     }
368 
369     static FuncOp buildTypePatternModel(JavaType sourceType, JavaType targetType) {
370         // builds the model of:
371         // static boolean f(sourceType a) { return a instanceof targetType _; }
372         return func(sourceType + "_" + targetType, functionType(JavaType.BOOLEAN, sourceType)).body(fblock -> {
373 
374             var paramVal = fblock.parameters().get(0);
375 
376             var patternVar = fblock.op(var(fblock.op(constant(targetType, defaultValue(targetType)))));
377 
378             var pattern = Body.Builder.of(fblock.parentBody(), functionType(ExtendedOp.Pattern.bindingType(targetType)));
379             pattern.entryBlock().op(_yield(
380                     pattern.entryBlock().op(typePattern(targetType, null))
381             ));
382 
383             var match = Body.Builder.of(fblock.parentBody(), functionType(JavaType.VOID, targetType));
384             var binding = match.entryBlock().parameters().get(0);
385             match.entryBlock().op(varStore(patternVar, binding));
386             match.entryBlock().op(_yield());
387 
388             var result = fblock.op(match(paramVal, pattern, match));
389 
390             fblock.op(_return(result));
391         });
392     }
393 
394     static Object defaultValue(JavaType t) {
395         if (List.of(BYTE, SHORT, CHAR, INT).contains(t)) {
396             return 0;
397         } else if (LONG.equals(t)) {
398             return 0L;
399         } else if (FLOAT.equals(t)) {
400             return 0f;
401         } else if (DOUBLE.equals(t)) {
402             return 0d;
403         } else if (BOOLEAN.equals(t)) {
404             return false;
405         }
406         return null;
407     }
408 }