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 }