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