1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package compiler.valhalla.valuetypes;
  25 
  26 import java.lang.invoke.*;
  27 import java.lang.reflect.Method;
  28 
  29 import jdk.test.lib.Asserts;
  30 
  31 /*
  32  * @test
  33  * @summary Test correct handling of nullable value types.
  34  * @library /testlibrary /test/lib /compiler/whitebox /
  35  * @requires os.simpleArch == "x64"
  36  * @compile -XDallowWithFieldOperator TestNullableValueTypes.java
  37  * @run driver ClassFileInstaller sun.hotspot.WhiteBox jdk.test.lib.Platform
  38  * @run main/othervm/timeout=120 -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
  39  *                               -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:+EnableValhalla
  40  *                               compiler.valhalla.valuetypes.ValueTypeTest
  41  *                               compiler.valhalla.valuetypes.TestNullableValueTypes
  42  */
  43 public class TestNullableValueTypes extends ValueTypeTest {
  44     // Extra VM parameters for some test scenarios. See ValueTypeTest.getVMParameters()
  45     @Override
  46     public String[] getExtraVMParameters(int scenario) {
  47         switch (scenario) {
  48         case 1: return new String[] {"-XX:-UseOptoBiasInlining"};
  49         case 2: return new String[] {"-XX:-UseBiasedLocking"};
  50         case 3: return new String[] {"-XX:-MonomorphicArrayCheck", "-XX:-UseBiasedLocking", "-XX:+ValueArrayFlatten"};
  51         case 4: return new String[] {"-XX:-MonomorphicArrayCheck"};
  52         }
  53         return null;
  54     }
  55 
  56     public static void main(String[] args) throws Throwable {
  57         TestNullableValueTypes test = new TestNullableValueTypes();
  58         test.run(args, MyValue1.class, MyValue2.class, MyValue2Inline.class, Test17Value.class, Test21Value.class);
  59     }
  60 
  61     static {
  62         try {
  63             Class<?> clazz = TestNullableValueTypes.class;
  64             ClassLoader loader = clazz.getClassLoader();
  65             MethodHandles.Lookup lookup = MethodHandles.lookup();
  66 
  67             MethodType test18_mt = MethodType.methodType(void.class, MyValue1.class.asBoxType());
  68             test18_mh1 = lookup.findStatic(clazz, "test18_target1", test18_mt);
  69             test18_mh2 = lookup.findStatic(clazz, "test18_target2", test18_mt);
  70 
  71             MethodType test19_mt = MethodType.methodType(void.class, MyValue1.class.asBoxType());
  72             test19_mh1 = lookup.findStatic(clazz, "test19_target1", test19_mt);
  73             test19_mh2 = lookup.findStatic(clazz, "test19_target2", test19_mt);
  74         } catch (NoSuchMethodException | IllegalAccessException e) {
  75             e.printStackTrace();
  76             throw new RuntimeException("Method handle lookup failed");
  77         }
  78     }
  79 
  80     private static final MyValue1 testValue1 = MyValue1.createWithFieldsInline(rI, rL);
  81     private static final MyValue1[] testValue1Array = new MyValue1[] {testValue1,
  82                                                                       testValue1,
  83                                                                       testValue1};
  84 
  85     MyValue1.box nullField;
  86     MyValue1.val valueField1 = testValue1;
  87 
  88     @Test
  89     public long test1(MyValue1.box vt) {
  90         long result = 0;
  91         try {
  92             result = vt.hash();
  93             throw new RuntimeException("NullPointerException expected");
  94         } catch (NullPointerException e) {
  95             // Expected
  96         }
  97         return result;
  98     }
  99 
 100     @DontCompile
 101     public void test1_verifier(boolean warmup) throws Throwable {
 102         long result = test1(null);
 103         Asserts.assertEquals(result, 0L);
 104     }
 105 
 106     @Test
 107     public long test2(MyValue1.box vt) {
 108         long result = 0;
 109         try {
 110             result = vt.hashInterpreted();
 111             throw new RuntimeException("NullPointerException expected");
 112         } catch (NullPointerException e) {
 113             // Expected
 114         }
 115         return result;
 116     }
 117 
 118     @DontCompile
 119     public void test2_verifier(boolean warmup) {
 120         long result = test2(nullField);
 121         Asserts.assertEquals(result, 0L);
 122     }
 123 
 124     @Test
 125     public long test3() {
 126         long result = 0;
 127         try {
 128             if ((Object)nullField != null) {
 129                 throw new RuntimeException("nullField should be null");
 130             }
 131             result = nullField.hash();
 132             throw new RuntimeException("NullPointerException expected");
 133         } catch (NullPointerException e) {
 134             // Expected
 135         }
 136         return result;
 137     }
 138 
 139     @DontCompile
 140     public void test3_verifier(boolean warmup) {
 141         long result = test3();
 142         Asserts.assertEquals(result, 0L);
 143     }
 144 
 145     @Test
 146     public void test4() {
 147         try {
 148             valueField1 = nullField;
 149             throw new RuntimeException("NullPointerException expected");
 150         } catch (NullPointerException e) {
 151             // Expected
 152         }
 153     }
 154 
 155     @DontCompile
 156     public void test4_verifier(boolean warmup) {
 157         test4();
 158     }
 159 
 160     @Test
 161     public MyValue1.box test5(MyValue1.box vt) {
 162         try {
 163             Object o = vt;
 164             vt = (MyValue1)o;
 165             throw new RuntimeException("NullPointerException expected");
 166         } catch (NullPointerException e) {
 167             // Expected
 168         }
 169 
 170         // Should not throw
 171         vt = test5_dontinline(vt);
 172         vt = test5_inline(vt);
 173         return vt;
 174     }
 175 
 176     @DontCompile
 177     public void test5_verifier(boolean warmup) {
 178         MyValue1.box vt = test5(nullField);
 179         Asserts.assertEquals((Object)vt, null);
 180     }
 181 
 182     @DontInline
 183     public MyValue1.box test5_dontinline(MyValue1.box vt) {
 184         return vt;
 185     }
 186 
 187     @ForceInline
 188     public MyValue1.box test5_inline(MyValue1.box vt) {
 189         return vt;
 190     }
 191 
 192     @Test
 193     public MyValue1 test6(Object obj) {
 194         MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL);
 195         try {
 196             vt = (MyValue1)obj;
 197             throw new RuntimeException("NullPointerException expected");
 198         } catch (NullPointerException e) {
 199             // Expected
 200         }
 201         return vt;
 202     }
 203 
 204     @DontCompile
 205     public void test6_verifier(boolean warmup) {
 206         MyValue1 vt = test6(null);
 207         Asserts.assertEquals(vt.hash(), testValue1.hash());
 208     }
 209 
 210     @ForceInline
 211     public MyValue1.box getNullInline() {
 212         return null;
 213     }
 214 
 215     @DontInline
 216     public MyValue1.box getNullDontInline() {
 217         return null;
 218     }
 219 
 220     @Test
 221     public void test7() throws Throwable {
 222         nullField = getNullInline();     // Should not throw
 223         nullField = getNullDontInline(); // Should not throw
 224         try {
 225             valueField1 = getNullInline();
 226             throw new RuntimeException("NullPointerException expected");
 227         } catch (NullPointerException e) {
 228             // Expected
 229         }
 230         try {
 231             valueField1 = getNullDontInline();
 232             throw new RuntimeException("NullPointerException expected");
 233         } catch (NullPointerException e) {
 234             // Expected
 235         }
 236     }
 237 
 238     @DontCompile
 239     public void test7_verifier(boolean warmup) throws Throwable {
 240         test7();
 241     }
 242 
 243     @Test
 244     public void test8() throws Throwable {
 245         try {
 246             valueField1 = nullField;
 247             throw new RuntimeException("NullPointerException expected");
 248         } catch (NullPointerException e) {
 249             // Expected
 250         }
 251     }
 252 
 253     @DontCompile
 254     public void test8_verifier(boolean warmup) throws Throwable {
 255         test8();
 256     }
 257 
 258     // merge of 2 values, one being null
 259     @Test
 260     public void test9(boolean flag1) {
 261         MyValue1 v;
 262         if (flag1) {
 263             v = valueField1;
 264         } else {
 265             v = nullField;
 266         }
 267         valueField1 = v;
 268     }
 269 
 270     @DontCompile
 271     public void test9_verifier(boolean warmup) {
 272         test9(true);
 273         try {
 274             test9(false);
 275             throw new RuntimeException("NullPointerException expected");
 276         } catch (NullPointerException e) {
 277             // Expected
 278         }
 279     }
 280 
 281     // null constant
 282     @Test
 283     public void test10(boolean flag) throws Throwable {
 284         MyValue1.box val = flag ? valueField1 : null;
 285         valueField1 = val;
 286     }
 287 
 288     @DontCompile
 289     public void test10_verifier(boolean warmup) throws Throwable {
 290         test10(true);
 291         try {
 292             test10(false);
 293             throw new RuntimeException("NullPointerException expected");
 294         } catch (NullPointerException e) {
 295             // Expected
 296         }
 297     }
 298 
 299     // null constant
 300     @Test
 301     public void test11(boolean flag) throws Throwable {
 302         MyValue1.box val = flag ? null : valueField1;
 303         valueField1 = val;
 304     }
 305 
 306     @DontCompile
 307     public void test11_verifier(boolean warmup) throws Throwable {
 308         test11(false);
 309         try {
 310             test11(true);
 311             throw new RuntimeException("NullPointerException expected");
 312         } catch (NullPointerException e) {
 313             // Expected
 314         }
 315     }
 316 
 317     // null return
 318     int test12_cnt;
 319 
 320     @DontInline
 321     public MyValue1.box test12_helper() {
 322         test12_cnt++;
 323         return nullField;
 324     }
 325 
 326     @Test
 327     public void test12() {
 328         valueField1 = test12_helper();
 329     }
 330 
 331     @DontCompile
 332     public void test12_verifier(boolean warmup) {
 333         try {
 334             test12_cnt = 0;
 335             test12();
 336             throw new RuntimeException("NullPointerException expected");
 337         } catch (NullPointerException e) {
 338             // Expected
 339         }
 340         if (test12_cnt != 1) {
 341             throw new RuntimeException("call executed twice");
 342         }
 343     }
 344 
 345     // null return at virtual call
 346     class A {
 347         public MyValue1.box test13_helper() {
 348             return nullField;
 349         }
 350     }
 351 
 352     class B extends A {
 353         public MyValue1.val test13_helper() {
 354             return nullField;
 355         }
 356     }
 357 
 358     class C extends A {
 359         public MyValue1.box test13_helper() {
 360             return nullField;
 361         }
 362     }
 363 
 364     class D extends B {
 365         public MyValue1.box test13_helper() {
 366             return nullField;
 367         }
 368     }
 369 
 370     @Test
 371     public void test13(A a) {
 372         valueField1 = a.test13_helper();
 373     }
 374 
 375     @DontCompile
 376     public void test13_verifier(boolean warmup) {
 377         A b = new B();
 378         A c = new C();
 379         A d = new D();
 380         try {
 381             test13(b);
 382             throw new RuntimeException("NullPointerException expected");
 383         } catch (NullPointerException e) {
 384             // Expected
 385         }
 386         try {
 387             test13(c);
 388             throw new RuntimeException("NullPointerException expected");
 389         } catch (NullPointerException e) {
 390             // Expected
 391         }
 392         try {
 393             test13(d);
 394             throw new RuntimeException("NullPointerException expected");
 395         } catch (NullPointerException e) {
 396             // Expected
 397         }
 398     }
 399 
 400     // Test writing null to a (flattened) value type array
 401     @ForceInline
 402     public void test14_inline(Object[] oa, Object o, int index) {
 403         oa[index] = o;
 404     }
 405 
 406     @Test()
 407     public void test14(MyValue1[] va, int index) {
 408         test14_inline(va, nullField, index);
 409     }
 410 
 411     @DontCompile
 412     public void test14_verifier(boolean warmup) {
 413         int index = Math.abs(rI) % 3;
 414         try {
 415             test14(testValue1Array, index);
 416             throw new RuntimeException("No NPE thrown");
 417         } catch (NullPointerException e) {
 418             // Expected
 419         }
 420         Asserts.assertEQ(testValue1Array[index].hash(), testValue1.hash());
 421     }
 422 
 423     @DontInline
 424     MyValue1.box getNullField1() {
 425         return nullField;
 426     }
 427 
 428     @DontInline
 429     MyValue1.val getNullField2() {
 430         return nullField;
 431     }
 432 
 433     @Test()
 434     public void test15() {
 435         nullField = getNullField1(); // should not throw
 436         try {
 437             valueField1 = getNullField1();
 438             throw new RuntimeException("NullPointerException expected");
 439         } catch (NullPointerException e) {
 440             // Expected
 441         }
 442         try {
 443             valueField1 = getNullField2();
 444             throw new RuntimeException("NullPointerException expected");
 445         } catch (NullPointerException e) {
 446             // Expected
 447         }
 448     }
 449 
 450     @DontCompile
 451     public void test15_verifier(boolean warmup) {
 452         test15();
 453     }
 454 
 455     @DontInline
 456     public boolean test16_dontinline(MyValue1.box vt) {
 457         return (Object)vt == null;
 458     }
 459 
 460     // Test c2c call passing null for a value type
 461     @Test
 462     @Warmup(10000) // Warmup to make sure 'test17_dontinline' is compiled
 463     public boolean test16(Object arg) throws Exception {
 464         Method test16method = getClass().getMethod("test16_dontinline", MyValue1.class.asBoxType());
 465         return (boolean)test16method.invoke(this, arg);
 466     }
 467 
 468     @DontCompile
 469     public void test16_verifier(boolean warmup) throws Exception {
 470         boolean res = test16(null);
 471         Asserts.assertTrue(res);
 472     }
 473 
 474     // Test scalarization of default value type with non-flattenable field
 475     value final class Test17Value {
 476         public final MyValue1.box valueField;
 477 
 478         public Test17Value() {
 479             valueField = MyValue1.createDefaultDontInline();
 480         }
 481 
 482         @ForceInline
 483         public Test17Value setValueField(MyValue1 valueField) {
 484             return __WithField(this.valueField, valueField);
 485         }
 486     }
 487 
 488     @Test()
 489     public Test17Value test17(boolean b) {
 490         Test17Value vt1 = Test17Value.default;
 491         if ((Object)vt1.valueField != null) {
 492             throw new RuntimeException("Should be null");
 493         }
 494         Test17Value vt2 = vt1.setValueField(testValue1);
 495         return b ? vt1 : vt2;
 496     }
 497 
 498     @DontCompile
 499     public void test17_verifier(boolean warmup) {
 500         test17(true);
 501         test17(false);
 502     }
 503 
 504     static final MethodHandle test18_mh1;
 505     static final MethodHandle test18_mh2;
 506 
 507     static MyValue1.box nullValue;
 508 
 509     @DontInline
 510     static void test18_target1(MyValue1.box vt) {
 511         nullValue = vt;
 512     }
 513 
 514     @ForceInline
 515     static void test18_target2(MyValue1.box vt) {
 516         nullValue = vt;
 517     }
 518 
 519     // Test passing null for a value type
 520     @Test
 521     @Warmup(11000) // Make sure lambda forms get compiled
 522     public void test18() throws Throwable {
 523         test18_mh1.invokeExact(nullValue);
 524         test18_mh2.invokeExact(nullValue);
 525     }
 526 
 527     @DontCompile
 528     public void test18_verifier(boolean warmup) {
 529         try {
 530             test18();
 531         } catch (Throwable t) {
 532             throw new RuntimeException("test18 failed", t);
 533         }
 534     }
 535 
 536     static MethodHandle test19_mh1;
 537     static MethodHandle test19_mh2;
 538 
 539     @DontInline
 540     static void test19_target1(MyValue1.box vt) {
 541         nullValue = vt;
 542     }
 543 
 544     @ForceInline
 545     static void test19_target2(MyValue1.box vt) {
 546         nullValue = vt;
 547     }
 548 
 549     // Same as test12 but with non-final mh
 550     @Test
 551     @Warmup(11000) // Make sure lambda forms get compiled
 552     public void test19() throws Throwable {
 553         test19_mh1.invokeExact(nullValue);
 554         test19_mh2.invokeExact(nullValue);
 555     }
 556 
 557     @DontCompile
 558     public void test19_verifier(boolean warmup) {
 559         try {
 560             test19();
 561         } catch (Throwable t) {
 562             throw new RuntimeException("test19 failed", t);
 563         }
 564     }
 565 
 566     // Same as test12/13 but with constant null
 567     @Test
 568     @Warmup(11000) // Make sure lambda forms get compiled
 569     public void test20(MethodHandle mh) throws Throwable {
 570         mh.invoke(null);
 571     }
 572 
 573     @DontCompile
 574     public void test20_verifier(boolean warmup) {
 575         try {
 576             test20(test18_mh1);
 577             test20(test18_mh2);
 578             test20(test19_mh1);
 579             test20(test19_mh2);
 580         } catch (Throwable t) {
 581             throw new RuntimeException("test20 failed", t);
 582         }
 583     }
 584 
 585     // Test writing null to a flattenable/non-flattenable value type field in a value type
 586     value final class Test21Value {
 587         final MyValue1.box valueField1;
 588         final MyValue1.val valueField2;
 589         final MyValue1.box alwaysNull;
 590 
 591         private Test21Value() {
 592             valueField1 = testValue1;
 593             valueField2 = testValue1;
 594             alwaysNull  = testValue1;
 595         }
 596 
 597         @ForceInline
 598         public Test21Value test1() {
 599             return __WithField(this.valueField1, alwaysNull); // Should not throw NPE
 600         }
 601 
 602         @ForceInline
 603         public Test21Value test2() {
 604             return __WithField(this.valueField2, alwaysNull); // Should throw NPE
 605         }
 606     }
 607 
 608     @Test
 609     public Test21Value test21(Test21Value vt) {
 610         vt = vt.test1();
 611         try {
 612             vt = vt.test2();
 613             throw new RuntimeException("NullPointerException expected");
 614         } catch (NullPointerException e) {
 615             // Expected
 616         }
 617         return vt;
 618     }
 619 
 620     @DontCompile
 621     public void test21_verifier(boolean warmup) {
 622         test21(Test21Value.default);
 623     }
 624 
 625     @DontInline
 626     public MyValue1.val test22_helper() {
 627         return nullField;
 628     }
 629 
 630     @Test
 631     public void test22() {
 632         valueField1 = test22_helper();
 633     }
 634 
 635     @DontCompile
 636     public void test22_verifier(boolean warmup) {
 637         try {
 638             test22();
 639             throw new RuntimeException("NullPointerException expected");
 640         } catch (NullPointerException e) {
 641             // Expected
 642         }
 643     }
 644 
 645     @Test
 646     public void test23(MyValue1[] arr, MyValue1.box b) {
 647         arr[0] = b;
 648     }
 649 
 650     @DontCompile
 651     public void test23_verifier(boolean warmup) {
 652         MyValue1[] arr = new MyValue1[2];
 653         MyValue1.box b = null;
 654         try {
 655             test23(arr, b);
 656             throw new RuntimeException("NullPointerException expected");
 657         } catch (NullPointerException e) {
 658             // Expected
 659         }
 660     }
 661 
 662     static MyValue1.box nullBox;
 663 
 664     @Test
 665     public MyValue1 test24() {
 666         return nullBox;
 667     }
 668 
 669     @DontCompile
 670     public void test24_verifier(boolean warmup) {
 671         try {
 672             test24();
 673             throw new RuntimeException("NullPointerException expected");
 674         } catch (NullPointerException e) {
 675             // Expected
 676         }
 677     }
 678 }