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