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 }