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 /* 25 * @test 26 * @requires !vm.graal.enabled 27 * @modules java.base/jdk.internal.org.objectweb.asm 28 * java.base/jdk.internal.misc 29 * java.base/jdk.internal.vm.annotation 30 * @library /test/lib / 31 * @build sun.hotspot.WhiteBox 32 * @run driver ClassFileInstaller sun.hotspot.WhiteBox 33 * sun.hotspot.WhiteBox$WhiteBoxPermission 34 * 35 * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions 36 * -XX:+PrintCompilation -XX:+PrintInlining -XX:+TraceDependencies -verbose:class -XX:CompileCommand=quiet 37 * -XX:CompileCommand=compileonly,*::test -XX:CompileCommand=compileonly,*::m -XX:CompileCommand=dontinline,*::test 38 * -Xbatch -XX:+WhiteBoxAPI -Xmixed 39 * -XX:-TieredCompilation 40 * compiler.cha.StrengthReduceInterfaceCall 41 * 42 * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions 43 * -XX:+PrintCompilation -XX:+PrintInlining -XX:+TraceDependencies -verbose:class -XX:CompileCommand=quiet 44 * -XX:CompileCommand=compileonly,*::test -XX:CompileCommand=compileonly,*::m -XX:CompileCommand=dontinline,*::test 45 * -Xbatch -XX:+WhiteBoxAPI -Xmixed 46 * -XX:+TieredCompilation -XX:TieredStopAtLevel=1 47 * compiler.cha.StrengthReduceInterfaceCall 48 */ 49 package compiler.cha; 50 51 import jdk.internal.misc.Unsafe; 52 import jdk.internal.org.objectweb.asm.ClassWriter; 53 import jdk.internal.org.objectweb.asm.MethodVisitor; 54 import jdk.internal.vm.annotation.DontInline; 55 import sun.hotspot.WhiteBox; 56 57 import java.io.IOException; 58 import java.lang.annotation.Retention; 59 import java.lang.annotation.RetentionPolicy; 60 import java.lang.invoke.MethodHandle; 61 import java.lang.invoke.MethodHandles; 62 import java.lang.reflect.Method; 63 import java.util.HashMap; 64 import java.util.concurrent.Callable; 65 66 import static jdk.test.lib.Asserts.*; 67 import static jdk.internal.org.objectweb.asm.ClassWriter.*; 68 import static jdk.internal.org.objectweb.asm.Opcodes.*; 69 70 public class StrengthReduceInterfaceCall { 71 public static void main(String[] args) { 72 run(ObjectToString.class); 73 run(ObjectHashCode.class); 74 run(TwoLevelHierarchyLinear.class); 75 run(ThreeLevelHierarchyLinear.class); 76 run(ThreeLevelHierarchyAbstractVsDefault.class); 77 run(ThreeLevelDefaultHierarchy.class); 78 run(ThreeLevelDefaultHierarchy1.class); 79 } 80 81 public static class ObjectToString extends ATest<ObjectToString.I> { 82 public ObjectToString() { super(I.class, C.class); } 83 84 interface J { String toString(); } 85 interface I extends J {} 86 87 static class C implements I {} 88 89 interface K1 extends I {} 90 interface K2 extends I { String toString(); } // K2.tS() ABSTRACT 91 // interface K3 extends I { default String toString() { return "K3"; } // K2.tS() DEFAULT 92 93 static class D implements I { public String toString() { return "D"; }} 94 95 static class DJ1 implements J {} 96 static class DJ2 implements J { public String toString() { return "DJ2"; }} 97 98 @Override 99 public Object test(I i) { return ObjectToStringHelper.test(i); /* invokeinterface I.toString() */ } 100 101 @TestCase 102 public void testMono() { 103 // 0. Trigger compilation of a monomorphic call site 104 compile(monomophic()); // C1 <: C <: intf I <: intf J <: Object.toString() 105 assertCompiled(); 106 107 // Dependency: none 108 109 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 110 assertCompiled(); 111 112 call(new C() { public String toString() { return "Cn"; }}); // Cn.tS <: C.tS <: intf I 113 assertCompiled(); 114 } 115 116 @TestCase 117 public void testBi() { 118 // 0. Trigger compilation of a bimorphic call site 119 compile(bimorphic()); // C1 <: C <: intf I <: intf J <: Object.toString() 120 assertCompiled(); 121 122 // Dependency: none 123 124 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 125 assertCompiled(); 126 127 call(new C() { public String toString() { return "Cn"; }}); // Cn.tS <: C.tS <: intf I 128 assertCompiled(); 129 } 130 131 @TestCase 132 public void testMega() { 133 // 0. Trigger compilation of a megamorphic call site 134 compile(megamorphic()); // C1,C2,C3 <: C <: intf I <: intf J <: Object.toString() 135 assertCompiled(); 136 137 // Dependency: none 138 // compiler.cha.StrengthReduceInterfaceCall$ObjectToString::test (5 bytes) 139 // @ 1 compiler.cha.StrengthReduceInterfaceCall$ObjectToStringHelper::test (7 bytes) inline (hot) 140 // @ 1 java.lang.Object::toString (36 bytes) virtual call 141 142 // No dependency - no invalidation 143 repeat(100, () -> call(new C(){})); // Cn <: C <: intf I 144 assertCompiled(); 145 146 initialize(K1.class, // intf K1 <: intf I <: intf J 147 K2.class, // intf K2.tS ABSTRACT <: intf I <: intf J 148 DJ1.class, // DJ1 <: intf J 149 DJ2.class); // DJ2.tS <: intf J 150 assertCompiled(); 151 152 initialize(D.class); // D.tS <: intf I <: intf J 153 assertCompiled(); 154 155 call(new C() { public String toString() { return "Cn"; }}); // Cn.tS <: C.tS <: intf I 156 assertCompiled(); 157 } 158 159 @Override 160 public void checkInvalidReceiver() { 161 shouldThrow(IncompatibleClassChangeError.class, () -> { 162 I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated 163 test(o); 164 }); 165 assertCompiled(); 166 167 shouldThrow(IncompatibleClassChangeError.class, () -> { 168 I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface 169 test(j); 170 }); 171 assertCompiled(); 172 } 173 } 174 175 public static class ObjectHashCode extends ATest<ObjectHashCode.I> { 176 public ObjectHashCode() { super(I.class, C.class); } 177 178 interface J {} 179 interface I extends J {} 180 181 static class C implements I {} 182 183 interface K1 extends I {} 184 interface K2 extends I { int hashCode(); } // K2.hC() ABSTRACT 185 // interface K3 extends I { default int hashCode() { return CORRECT; } // K2.hC() DEFAULT 186 187 static class D implements I { public int hashCode() { return super.hashCode(); }} 188 189 static class DJ1 implements J {} 190 static class DJ2 implements J { public int hashCode() { return super.hashCode(); }} 191 192 @Override 193 public Object test(I i) { 194 return ObjectHashCodeHelper.test(i); /* invokeinterface I.hashCode() */ 195 } 196 197 @TestCase 198 public void testMono() { 199 // 0. Trigger compilation of a monomorphic call site 200 compile(monomophic()); // C1 <: C <: intf I <: intf J <: Object.hashCode() 201 assertCompiled(); 202 203 // Dependency: none 204 205 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 206 assertCompiled(); 207 208 call(new C() { public int hashCode() { return super.hashCode(); }}); // Cn.hC <: C.hC <: intf I 209 assertCompiled(); 210 } 211 212 @TestCase 213 public void testBi() { 214 // 0. Trigger compilation of a bimorphic call site 215 compile(bimorphic()); // C1 <: C <: intf I <: intf J <: Object.toString() 216 assertCompiled(); 217 218 // Dependency: none 219 220 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 221 assertCompiled(); 222 223 call(new C() { public int hashCode() { return super.hashCode(); }}); // Cn.hC <: C.hC <: intf I 224 assertCompiled(); 225 } 226 227 @TestCase 228 public void testMega() { 229 // 0. Trigger compilation of a megamorphic call site 230 compile(megamorphic()); // C1,C2,C3 <: C <: intf I <: intf J <: Object.hashCode() 231 assertCompiled(); 232 233 // Dependency: none 234 235 // No dependency - no invalidation 236 repeat(100, () -> call(new C(){})); // Cn <: C <: intf I 237 assertCompiled(); 238 239 initialize(K1.class, // intf K1 <: intf I <: intf J 240 K2.class, // intf K2.hC ABSTRACT <: intf I <: intf J 241 DJ1.class, // DJ1 <: intf J 242 DJ2.class); // DJ2.hC <: intf J 243 assertCompiled(); 244 245 initialize(D.class); // D.hC <: intf I <: intf J 246 assertCompiled(); 247 248 call(new C() { public int hashCode() { return super.hashCode(); }}); // Cn.hC <: C.hC <: intf I 249 assertCompiled(); 250 } 251 252 @Override 253 public void checkInvalidReceiver() { 254 shouldThrow(IncompatibleClassChangeError.class, () -> { 255 I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated 256 test(o); 257 }); 258 assertCompiled(); 259 260 shouldThrow(IncompatibleClassChangeError.class, () -> { 261 I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface 262 test(j); 263 }); 264 assertCompiled(); 265 } 266 } 267 268 public static class TwoLevelHierarchyLinear extends ATest<TwoLevelHierarchyLinear.I> { 269 public TwoLevelHierarchyLinear() { super(I.class, C.class); } 270 271 interface J { default Object m() { return WRONG; } } 272 273 interface I extends J { Object m(); } 274 static class C implements I { public Object m() { return CORRECT; }} 275 276 interface K1 extends I {} 277 interface K2 extends I { Object m(); } 278 interface K3 extends I { default Object m() { return WRONG; }} 279 280 static class D implements I { public Object m() { return WRONG; }} 281 282 static class DJ1 implements J {} 283 static class DJ2 implements J { public Object m() { return WRONG; }} 284 285 @DontInline 286 public Object test(I i) { 287 return i.m(); 288 } 289 290 @TestCase 291 public void testMega1() { 292 // 0. Trigger compilation of a megamorphic call site 293 compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I.m ABSTRACT <: intf J.m ABSTRACT 294 assertCompiled(); 295 296 // Dependency: type = unique_concrete_method, context = I, method = C.m 297 298 checkInvalidReceiver(); // ensure proper type check is preserved 299 300 // 1. No deoptimization/invalidation on not-yet-seen receiver 301 repeat(100, () -> call(new C(){})); // Cn <: C.m <: intf I.m ABSTRACT <: intf J.m DEFAULT 302 assertCompiled(); 303 304 // 2. No dependency invalidation on class loading of unrelated classes: different context 305 initialize(K1.class, // intf K1 <: intf I.m ABSTRACT <: intf J.m DEFAULT 306 K2.class, // intf K2.m ABSTRACT <: intf I.m ABSTRACT <: intf J.m DEFAULT 307 DJ1.class, // DJ1 <: intf J.m DEFAULT 308 DJ2.class); // DJ2.m <: intf J.m DEFAULT 309 assertCompiled(); 310 311 // 3. Dependency invalidation on D <: I 312 initialize(D.class); // D.m <: intf I.m ABSTRACT <: intf J.m DEFAULT 313 assertNotCompiled(); 314 315 // 4. Recompilation: no inlining, no dependencies 316 compile(megamorphic()); 317 call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I.m ABSTRACT <: intf J.m DEFAULT 318 assertCompiled(); 319 320 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 321 } 322 323 @TestCase 324 public void testMega2() { 325 // 0. Trigger compilation of a megamorphic call site 326 compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I.m ABSTRACT <: intf J.m DEFAULT 327 assertCompiled(); 328 329 // Dependency: type = unique_concrete_method, context = I, method = C.m 330 331 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 332 333 // 1. Dependency invalidation 334 initialize(K3.class); // intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT 335 assertNotCompiled(); 336 337 // 2. Recompilation: still inlines 338 // FIXME: no default method support in CHA yet 339 compile(megamorphic()); 340 call(new K3() { public Object m() { return CORRECT; }}); // K3n.m <: intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m ABSTRACT 341 assertNotCompiled(); 342 343 // 3. Recompilation: no inlining, no dependencies 344 compile(megamorphic()); 345 call(new K3() { public Object m() { return CORRECT; }}); // Kn.m <: intf K3.m DEFAULT <: intf I.m ABSTRACT <: intf J.m DEFAULT 346 assertCompiled(); 347 348 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 349 } 350 351 @Override 352 public void checkInvalidReceiver() { 353 shouldThrow(IncompatibleClassChangeError.class, () -> { 354 I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated 355 test(o); 356 }); 357 assertCompiled(); 358 359 shouldThrow(IncompatibleClassChangeError.class, () -> { 360 I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface 361 test(j); 362 }); 363 assertCompiled(); 364 } 365 } 366 367 public static class ThreeLevelHierarchyLinear extends ATest<ThreeLevelHierarchyLinear.I> { 368 public ThreeLevelHierarchyLinear() { super(I.class, C.class); } 369 370 interface J { Object m(); } 371 interface I extends J {} 372 373 interface K1 extends I {} 374 interface K2 extends I { Object m(); } 375 interface K3 extends I { default Object m() { return WRONG; }} 376 377 static class C implements I { public Object m() { return CORRECT; }} 378 379 static class DI implements I { public Object m() { return WRONG; }} 380 static class DJ implements J { public Object m() { return WRONG; }} 381 382 @DontInline 383 public Object test(I i) { 384 return i.m(); // I <: J.m ABSTRACT 385 } 386 387 @TestCase 388 public void testMega1() { 389 // 0. Trigger compilation of a megamorphic call site 390 compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT 391 assertCompiled(); 392 393 // Dependency: type = unique_concrete_method, context = I, method = C.m 394 395 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 396 397 // 1. No deoptimization/invalidation on not-yet-seen receiver 398 repeat(100, () -> call(new C(){})); // Cn <: C.m <: intf I 399 assertCompiled(); // No deopt on not-yet-seen receiver 400 401 // 2. No dependency invalidation: different context 402 initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT 403 K1.class, // intf K1 <: intf I <: intf J.m ABSTRACT 404 K2.class); // intf K2.m ABSTRACT <: intf I <: intf J.m ABSTRACT 405 assertCompiled(); 406 407 // 3. Dependency invalidation: DI.m <: I 408 initialize(DI.class); // DI.m <: intf I <: intf J.m ABSTRACT 409 assertNotCompiled(); 410 411 // 4. Recompilation w/o a dependency 412 compile(megamorphic()); 413 call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I <: intf J.m ABSTRACT 414 assertCompiled(); // no dependency 415 416 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 417 } 418 419 @TestCase 420 public void testMega2() { 421 compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT 422 assertCompiled(); 423 424 // Dependency: type = unique_concrete_method, context = I, method = C.m 425 426 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 427 428 // Dependency invalidation 429 initialize(K3.class); // intf K3.m DEFAULT <: intf I; 430 assertNotCompiled(); // FIXME: default methods in sub-interfaces shouldn't be taken into account by CHA 431 432 // Recompilation with a dependency 433 compile(megamorphic()); 434 assertCompiled(); 435 436 // Dependency: type = unique_concrete_method, context = I, method = C.m 437 438 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 439 440 call(new K3() { public Object m() { return CORRECT; }}); // Kn.m <: K3.m DEFAULT <: intf I <: intf J.m ABSTRACT 441 assertNotCompiled(); 442 443 // Recompilation w/o a dependency 444 compile(megamorphic()); 445 // Dependency: none 446 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 447 call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I <: intf J.m ABSTRACT 448 assertCompiled(); 449 } 450 451 @Override 452 public void checkInvalidReceiver() { 453 shouldThrow(IncompatibleClassChangeError.class, () -> { 454 I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated 455 test(o); 456 }); 457 assertCompiled(); 458 459 shouldThrow(IncompatibleClassChangeError.class, () -> { 460 I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() { public Object m() { return WRONG; }}); // super interface 461 test(j); 462 }); 463 assertCompiled(); 464 } 465 } 466 467 public static class ThreeLevelHierarchyAbstractVsDefault extends ATest<ThreeLevelHierarchyAbstractVsDefault.I> { 468 public ThreeLevelHierarchyAbstractVsDefault() { super(I.class, C.class); } 469 470 interface J1 { default Object m() { return WRONG; } } // intf J1.m DEFAULT 471 interface J2 extends J1 { Object m(); } // intf J2.m ABSTRACT <: intf J1 472 interface I extends J1, J2 {} // intf I.m OVERPASS <: intf J1,J2 473 474 static class C implements I { public Object m() { return CORRECT; }} 475 476 @DontInline 477 public Object test(I i) { 478 return i.m(); // intf I.m OVERPASS 479 } 480 481 static class DI implements I { public Object m() { return WRONG; }} 482 483 static class DJ11 implements J1 {} 484 static class DJ12 implements J1 { public Object m() { return WRONG; }} 485 486 static class DJ2 implements J2 { public Object m() { return WRONG; }} 487 488 interface K11 extends J1 {} 489 interface K12 extends J1 { Object m(); } 490 interface K13 extends J1 { default Object m() { return WRONG; }} 491 interface K21 extends J2 {} 492 interface K22 extends J2 { Object m(); } 493 interface K23 extends J2 { default Object m() { return WRONG; }} 494 495 496 public void testMega1() { 497 // 0. Trigger compilation of megamorphic call site 498 compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I.m OVERPASS <: intf J2.m ABSTRACT <: intf J1.m DEFAULT 499 assertCompiled(); 500 501 // Dependency: type = unique_concrete_method, context = I, method = C.m 502 503 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 504 505 // 1. No deopt/invalidation on not-yet-seen receiver 506 repeat(100, () -> call(new C(){})); // Cn <: C.m <: intf I.m OVERPASS <: intf J2.m ABSTRACT <: intf J1.m DEFAULT 507 assertCompiled(); 508 509 // 2. No dependency invalidation: different context 510 initialize(K11.class, K12.class, K13.class, 511 K21.class, K22.class, K23.class); 512 513 // 3. Dependency invalidation: Cn.m <: C <: I 514 call(new C() { public Object m() { return CORRECT; }}); // Cn.m <: C.m <: intf I.m OVERPASS <: intf J2.m ABSTRACT <: intf J1.m DEFAULT 515 assertNotCompiled(); 516 517 // 4. Recompilation w/o a dependency 518 compile(megamorphic()); 519 call(new C() { public Object m() { return CORRECT; }}); 520 assertCompiled(); // no inlining 521 522 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 523 } 524 525 public void testMega2() { 526 // 0. Trigger compilation of a megamorphic call site 527 compile(megamorphic()); 528 assertCompiled(); 529 530 // Dependency: type = unique_concrete_method, context = I, method = C.m 531 532 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 533 534 // 1. No dependency invalidation: different context 535 initialize(DJ11.class, 536 DJ12.class, 537 DJ2.class); 538 assertCompiled(); 539 540 // 2. Dependency invalidation: DI.m <: I 541 initialize(DI.class); 542 assertNotCompiled(); 543 544 // 3. Recompilation w/o a dependency 545 compile(megamorphic()); 546 call(new C() { public Object m() { return CORRECT; }}); 547 assertCompiled(); // no inlining 548 549 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 550 } 551 552 @Override 553 public void checkInvalidReceiver() { 554 shouldThrow(IncompatibleClassChangeError.class, () -> { 555 I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated 556 test(o); 557 }); 558 assertCompiled(); 559 560 shouldThrow(IncompatibleClassChangeError.class, () -> { 561 I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J1() {}); // super interface 562 test(j); 563 }); 564 assertCompiled(); 565 566 shouldThrow(IncompatibleClassChangeError.class, () -> { 567 I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J2() { public Object m() { return WRONG; }}); // super interface 568 test(j); 569 }); 570 assertCompiled(); 571 } 572 } 573 574 public static class ThreeLevelDefaultHierarchy extends ATest<ThreeLevelDefaultHierarchy.I> { 575 public ThreeLevelDefaultHierarchy() { super(I.class, C.class); } 576 577 interface J { default Object m() { return WRONG; }} 578 interface I extends J {} 579 580 static class C implements I { public Object m() { return CORRECT; }} 581 582 interface K1 extends I {} 583 interface K2 extends I { Object m(); } 584 interface K3 extends I { default Object m() { return WRONG; }} 585 586 static class DI implements I { public Object m() { return WRONG; }} 587 static class DJ implements J { public Object m() { return WRONG; }} 588 589 @DontInline 590 public Object test(I i) { 591 return i.m(); // no inlining since J.m is a default method 592 } 593 594 @TestCase 595 public void testMega() { 596 // 0. Trigger compilation of a megamorphic call site 597 compile(megamorphic()); // C1,C2,C3 <: C.m <: intf I <: intf J.m ABSTRACT 598 assertCompiled(); 599 600 // Dependency: none 601 602 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 603 604 // 1. No deoptimization/invalidation on not-yet-seen receiver 605 repeat(100, () -> call(new C() {})); 606 assertCompiled(); 607 608 // 2. No dependency and no inlining 609 initialize(DJ.class, // DJ.m <: intf J.m ABSTRACT 610 DI.class, // DI.m <: intf I <: intf J.m ABSTRACT 611 K1.class, // intf K1 <: intf I <: intf J.m ABSTRACT 612 K2.class); // intf K2.m ABSTRACT <: intf I <: intf J.m ABSTRACT 613 assertCompiled(); 614 } 615 616 @Override 617 public void checkInvalidReceiver() { 618 shouldThrow(IncompatibleClassChangeError.class, () -> { 619 I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated 620 test(o); 621 }); 622 assertCompiled(); 623 624 shouldThrow(IncompatibleClassChangeError.class, () -> { 625 I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J() {}); // super interface 626 test(j); 627 }); 628 assertCompiled(); 629 } 630 } 631 632 public static class ThreeLevelDefaultHierarchy1 extends ATest<ThreeLevelDefaultHierarchy1.I> { 633 public ThreeLevelDefaultHierarchy1() { super(I.class, C.class); } 634 635 interface J1 { Object m();} 636 interface J2 extends J1 { default Object m() { return WRONG; } } 637 interface I extends J1, J2 {} 638 639 static class C implements I { public Object m() { return CORRECT; }} 640 641 interface K1 extends I {} 642 interface K2 extends I { Object m(); } 643 interface K3 extends I { default Object m() { return WRONG; }} 644 645 static class DI implements I { public Object m() { return WRONG; }} 646 static class DJ1 implements J1 { public Object m() { return WRONG; }} 647 static class DJ2 implements J2 { public Object m() { return WRONG; }} 648 649 @DontInline 650 public Object test(I i) { 651 return i.m(); // no inlining since J.m is a default method 652 } 653 654 @TestCase 655 public void testMega() { 656 // 0. Trigger compilation of a megamorphic call site 657 compile(megamorphic()); 658 assertCompiled(); 659 660 // Dependency: none 661 662 checkInvalidReceiver(); // ensure proper type check on receiver is preserved 663 664 // 1. No deoptimization/invalidation on not-yet-seen receiver 665 repeat(100, () -> call(new C() {})); 666 assertCompiled(); 667 668 // 2. No dependency, no inlining 669 // CHA doesn't support default methods yet. 670 initialize(DJ1.class, 671 DJ2.class, 672 DI.class, 673 K1.class, 674 K2.class, 675 K3.class); 676 assertCompiled(); 677 } 678 679 @Override 680 public void checkInvalidReceiver() { 681 shouldThrow(IncompatibleClassChangeError.class, () -> { 682 I o = (I) unsafeCastMH(I.class).invokeExact(new Object()); // unrelated 683 test(o); 684 }); 685 assertCompiled(); 686 687 shouldThrow(IncompatibleClassChangeError.class, () -> { 688 I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J1() { public Object m() { return WRONG; } }); // super interface 689 test(j); 690 }); 691 assertCompiled(); 692 693 shouldThrow(IncompatibleClassChangeError.class, () -> { 694 I j = (I) unsafeCastMH(I.class).invokeExact((Object)new J2() {}); // super interface 695 test(j); 696 }); 697 assertCompiled(); 698 } 699 } 700 701 /* =========================================================== */ 702 703 interface Action { 704 int run(); 705 } 706 707 public static final Unsafe U = Unsafe.getUnsafe(); 708 709 interface Test<T> { 710 boolean isCompiled(); 711 void assertNotCompiled(); 712 void assertCompiled(); 713 714 void call(T o); 715 T receiver(int id); 716 717 default Runnable monomophic() { 718 return () -> { 719 call(receiver(0)); // 100% 720 }; 721 } 722 723 default Runnable bimorphic() { 724 return () -> { 725 call(receiver(0)); // 50% 726 call(receiver(1)); // 50% 727 }; 728 } 729 730 default Runnable polymorphic() { 731 return () -> { 732 for (int i = 0; i < 23; i++) { 733 call(receiver(0)); // 92% 734 } 735 call(receiver(1)); // 4% 736 call(receiver(2)); // 4% 737 }; 738 } 739 740 default Runnable megamorphic() { 741 return () -> { 742 call(receiver(0)); // 33% 743 call(receiver(1)); // 33% 744 call(receiver(2)); // 33% 745 }; 746 } 747 748 default void compile(Runnable r) { 749 assertNotCompiled(); 750 while(!isCompiled()) { 751 r.run(); 752 } 753 assertCompiled(); 754 } 755 756 default void initialize(Class<?>... cs) { 757 for (Class<?> c : cs) { 758 U.ensureClassInitialized(c); 759 } 760 } 761 762 default void repeat(int cnt, Runnable r) { 763 for (int i = 0; i < cnt; i++) { 764 r.run(); 765 } 766 } 767 } 768 769 public static abstract class ATest<T> implements Test<T> { 770 public static final WhiteBox WB = WhiteBox.getWhiteBox(); 771 772 public static final Object CORRECT = new Object(); 773 public static final Object WRONG = new Object(); 774 775 final Method TEST; 776 private final Class<T> declared; 777 private final Class<?> receiver; 778 779 private final HashMap<Integer, T> receivers = new HashMap<>(); 780 781 public ATest(Class<T> declared, Class<?> receiver) { 782 this.declared = declared; 783 this.receiver = receiver; 784 TEST = compute(() -> this.getClass().getDeclaredMethod("test", declared)); 785 } 786 787 @DontInline 788 public abstract Object test(T i); 789 790 public abstract void checkInvalidReceiver(); 791 792 public T receiver(int id) { 793 return receivers.computeIfAbsent(id, (i -> { 794 try { 795 MyClassLoader cl = (MyClassLoader) receiver.getClassLoader(); 796 Class<?> sub = cl.subclass(receiver, i); 797 return (T)sub.getDeclaredConstructor().newInstance(); 798 } catch (Exception e) { 799 throw new Error(e); 800 } 801 })); 802 } 803 804 @Override 805 public boolean isCompiled() { return WB.isMethodCompiled(TEST); } 806 807 @Override 808 public void assertNotCompiled() { assertFalse(isCompiled()); } 809 810 @Override 811 public void assertCompiled() { assertTrue(isCompiled()); } 812 813 @Override 814 public void call(T i) { 815 assertTrue(test(i) != WRONG); 816 } 817 } 818 819 @Retention(value = RetentionPolicy.RUNTIME) 820 public @interface TestCase {} 821 822 static void run(Class<?> test) { 823 try { 824 for (Method m : test.getDeclaredMethods()) { 825 if (m.isAnnotationPresent(TestCase.class)) { 826 System.out.println(m.toString()); 827 ClassLoader cl = new MyClassLoader(test); 828 Class<?> c = cl.loadClass(test.getName()); 829 c.getMethod(m.getName()).invoke(c.getDeclaredConstructor().newInstance()); 830 } 831 } 832 } catch (Exception e) { 833 throw new Error(e); 834 } 835 } 836 837 static class ObjectToStringHelper { 838 static Object test(Object o) { 839 throw new Error("not used"); 840 } 841 } 842 static class ObjectHashCodeHelper { 843 static int test(Object o) { 844 throw new Error("not used"); 845 } 846 } 847 848 static final class MyClassLoader extends ClassLoader { 849 private final Class<?> test; 850 851 MyClassLoader(Class<?> test) { 852 this.test = test; 853 } 854 855 static String intl(String s) { 856 return s.replace('.', '/'); 857 } 858 859 Class<?> subclass(Class<?> c, int id) { 860 String name = c.getName() + id; 861 Class<?> sub = findLoadedClass(name); 862 if (sub == null) { 863 ClassWriter cw = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES); 864 cw.visit(52, ACC_PUBLIC | ACC_SUPER, intl(name), null, intl(c.getName()), null); 865 866 { // Default constructor: <init>()V 867 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 868 mv.visitCode(); 869 mv.visitVarInsn(ALOAD, 0); 870 mv.visitMethodInsn(INVOKESPECIAL, intl(c.getName()), "<init>", "()V", false); 871 mv.visitInsn(RETURN); 872 mv.visitMaxs(0, 0); 873 mv.visitEnd(); 874 } 875 876 byte[] classFile = cw.toByteArray(); 877 return defineClass(name, classFile, 0, classFile.length); 878 } 879 return sub; 880 } 881 882 protected Class<?> loadClass(String name, boolean resolve) 883 throws ClassNotFoundException 884 { 885 // First, check if the class has already been loaded 886 Class<?> c = findLoadedClass(name); 887 if (c == null) { 888 try { 889 c = getParent().loadClass(name); 890 if (name.endsWith("ObjectToStringHelper")) { 891 ClassWriter cw = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES); 892 cw.visit(52, ACC_PUBLIC | ACC_SUPER, intl(name), null, "java/lang/Object", null); 893 894 { 895 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "test", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null); 896 mv.visitCode(); 897 mv.visitVarInsn(ALOAD, 0); 898 mv.visitMethodInsn(INVOKEINTERFACE, intl(test.getName()) + "$I", "toString", "()Ljava/lang/String;", true); 899 mv.visitInsn(ARETURN); 900 mv.visitMaxs(0, 0); 901 mv.visitEnd(); 902 } 903 904 byte[] classFile = cw.toByteArray(); 905 return defineClass(name, classFile, 0, classFile.length); 906 } else if (name.endsWith("ObjectHashCodeHelper")) { 907 ClassWriter cw = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES); 908 cw.visit(52, ACC_PUBLIC | ACC_SUPER, intl(name), null, "java/lang/Object", null); 909 910 { 911 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "test", "(Ljava/lang/Object;)I", null, null); 912 mv.visitCode(); 913 mv.visitVarInsn(ALOAD, 0); 914 mv.visitMethodInsn(INVOKEINTERFACE, intl(test.getName()) + "$I", "hashCode", "()I", true); 915 mv.visitInsn(IRETURN); 916 mv.visitMaxs(0, 0); 917 mv.visitEnd(); 918 } 919 920 byte[] classFile = cw.toByteArray(); 921 return defineClass(name, classFile, 0, classFile.length); 922 } else if (c == test || name.startsWith(test.getName())) { 923 try { 924 String path = name.replace('.', '/') + ".class"; 925 byte[] classFile = getParent().getResourceAsStream(path).readAllBytes(); 926 return defineClass(name, classFile, 0, classFile.length); 927 } catch (IOException e) { 928 throw new Error(e); 929 } 930 } 931 } catch (ClassNotFoundException e) { 932 // ClassNotFoundException thrown if class not found 933 // from the non-null parent class loader 934 } 935 936 if (c == null) { 937 // If still not found, then invoke findClass in order 938 // to find the class. 939 c = findClass(name); 940 } 941 } 942 if (resolve) { 943 resolveClass(c); 944 } 945 return c; 946 } 947 } 948 949 public interface RunnableWithException { 950 void run() throws Throwable; 951 } 952 953 public static void shouldThrow(Class<? extends Throwable> expectedException, RunnableWithException r) { 954 try { 955 r.run(); 956 throw new AssertionError("Exception not thrown: " + expectedException.getName()); 957 } catch(Throwable e) { 958 if (expectedException == e.getClass()) { 959 // success: proper exception is thrown 960 } else { 961 throw new Error(expectedException.getName() + " is expected", e); 962 } 963 } 964 } 965 966 public static MethodHandle unsafeCastMH(Class<?> cls) { 967 try { 968 MethodHandle mh = MethodHandles.identity(Object.class); 969 return MethodHandles.explicitCastArguments(mh, mh.type().changeReturnType(cls)); 970 } catch (Throwable e) { 971 throw new Error(e); 972 } 973 } 974 975 static <T> T compute(Callable<T> c) { 976 try { 977 return c.call(); 978 } catch (Exception e) { 979 throw new Error(e); 980 } 981 } 982 }