/* * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @requires !vm.graal.enabled & vm.opt.final.UseVtableBasedCHA == true * @modules java.base/jdk.internal.org.objectweb.asm * java.base/jdk.internal.misc * java.base/jdk.internal.vm.annotation * @library /test/lib / * @compile Utils.java * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions * -XX:+PrintCompilation -XX:+PrintInlining -Xlog:dependencies=debug -verbose:class -XX:CompileCommand=quiet * -XX:CompileCommand=compileonly,*::m * -XX:CompileCommand=compileonly,*::test -XX:CompileCommand=dontinline,*::test * -Xbatch -Xmixed -XX:+WhiteBoxAPI * -XX:-TieredCompilation * -XX:-StressMethodHandleLinkerInlining * compiler.cha.DefaultRootMethod * * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions * -XX:+PrintCompilation -XX:+PrintInlining -Xlog:dependencies=debug -verbose:class -XX:CompileCommand=quiet * -XX:CompileCommand=compileonly,*::m * -XX:CompileCommand=compileonly,*::test -XX:CompileCommand=dontinline,*::test * -Xbatch -Xmixed -XX:+WhiteBoxAPI * -XX:+TieredCompilation -XX:TieredStopAtLevel=1 * -XX:-StressMethodHandleLinkerInlining * compiler.cha.DefaultRootMethod */ package compiler.cha; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import static compiler.cha.Utils.*; public class DefaultRootMethod { public static void main(String[] args) { run(DefaultRoot.class); run(InheritedDefault.class); // Implementation limitation: CHA is not performed by C1 during inlining through MH linkers. if (!jdk.test.whitebox.code.Compiler.isC1Enabled()) { run(DefaultRoot.TestMH.class, DefaultRoot.class); run(InheritedDefault.TestMH.class, InheritedDefault.class); } System.out.println("TEST PASSED"); } public static class DefaultRoot extends ATest { public DefaultRoot() { super(C.class, D.class); } interface I { default Object m() { return CORRECT; } } static class C implements I { /* inherited I.m */} static class D extends C { /* inherited I.m */ } static abstract class E1 extends C { /* empty */ } static abstract class E2 extends C { public abstract Object m(); } static abstract class E3 extends C { public Object m() { return "E3.m"; } } interface I1 extends I { Object m(); } interface I2 extends I { default Object m() { return "I2.m"; } } static abstract class F1 extends C implements I1 { } static abstract class F2 extends C implements I2 { } static class G extends C { public Object m() { return CORRECT; } } @Override public Object test(C obj) throws Throwable { return obj.m(); // invokevirtual C.m() } @Override public void checkInvalidReceiver() { // nothing to do: concrete class types are enforced by the verifier } @TestCase public void test() { // 0. Trigger compilation of a megamorphic call site compile(megamorphic()); // Dn <: D.m <: C <: I.m DEFAULT assertCompiled(); // Dependency: type = unique_concrete_method, context = C, method = D.m // 1. No invalidation: abstract classes don't participate in CHA. initialize(E1.class, // ABSTRACT E1 <: C <: I.m DEFAULT E2.class, // ABSTRACT E2.m ABSTRACT <: C <: I.m DEFAULT E3.class, // ABSTRACT E3.m <: C <: I.m DEFAULT F1.class, // ABSTRACT F1 <: C <: I.m DEFAULT, I1.m ABSTRACT F2.class); // ABSTRACT F2 <: C <: I.m DEFAULT, I2.m DEFAULT assertCompiled(); // 2. Dependency invalidation: G.m <: C <: I.m DEFAULT load(G.class); assertCompiled(); // 3. Dependency invalidation: G.m <: C <: I.m DEFAULT initialize(G.class); assertNotCompiled(); // 4. Recompilation: no inlining, no dependencies compile(megamorphic()); call(new C() { public Object m() { return CORRECT; } }); // Cn.m <: C <: I.m DEFAULT call(new G() { public Object m() { return CORRECT; } }); // Gn <: G.m <: C <: I.m DEFAULT assertCompiled(); } public static class TestMH extends DefaultRoot { static final MethodHandle TEST_MH = findVirtualHelper(C.class, "m", Object.class, MethodHandles.lookup()); @Override public Object test(C obj) throws Throwable { return TEST_MH.invokeExact(obj); // invokevirtual C.m() } } } public static class InheritedDefault extends ATest { public InheritedDefault() { super(C.class, D.class); } interface I { Object m(); } interface J extends I { default Object m() { return CORRECT; } } static abstract class C implements I { /* inherits I.m ABSTRACT */} // NB! The class is marked abstract to avoid abstract_with_unique_concrete_subtype dependency static abstract class D extends C implements J { /* inherits J.m DEFAULT*/ } static abstract class E1 extends C { /* empty */ } static abstract class E2 extends C { public abstract Object m(); } static abstract class E3 extends C { public Object m() { return "E3.m"; } } interface I1 extends I { Object m(); } interface I2 extends I { default Object m() { return "I2.m"; } } static abstract class F1 extends C implements I1 { } static abstract class F2 extends C implements I2 { } interface K extends I { default Object m() { return CORRECT; } } static class G extends C implements K { /* inherits K.m DEFAULT */ } @Override public Object test(C obj) throws Throwable { return obj.m(); // invokevirtual C.m() } @Override public void checkInvalidReceiver() { // nothing to do: concrete class types are enforced by the verifier } @TestCase public void test() { // 0. Trigger compilation of a megamorphic call site compile(megamorphic()); // Dn <: D.m <: C <: I.m ABSTRACT, J.m DEFAULT assertCompiled(); // Dependency: type = unique_concrete_method, context = C, method = D.m // 1. No invalidation: abstract classes don't participate in CHA. initialize(E1.class, // ABSTRACT E1 <: C <: I.m ABSTRACT E2.class, // ABSTRACT E2.m ABSTRACT <: C <: I.m ABSTRACT E3.class, // ABSTRACT E3.m <: C <: I.m ABSTRACT F1.class, // ABSTRACT F1 <: C <: I.m ABSTRACT, I1.m ABSTRACT F2.class); // ABSTRACT F2 <: C <: I.m ABSTRACT, I2.m DEFAULT assertCompiled(); // 2. No invalidation: not yet linked classes don't participate in CHA. load(G.class); assertCompiled(); // 3. Dependency invalidation: G.m <: C <: I.m DEFAULT initialize(G.class); assertNotCompiled(); // 4. Recompilation: no inlining, no dependencies compile(megamorphic()); call(new C() { public Object m() { return CORRECT; } }); // Cn.m <: C <: I.m DEFAULT call(new G() { public Object m() { return CORRECT; } }); // Gn <: G.m <: C <: I.m DEFAULT assertCompiled(); } public static class TestMH extends InheritedDefault { static final MethodHandle TEST_MH = findVirtualHelper(C.class, "m", Object.class, MethodHandles.lookup()); @Override public Object test(C obj) throws Throwable { return TEST_MH.invokeExact(obj); // invokevirtual C.m() } } } }