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 }