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 }