--- /dev/null 2018-05-14 10:15:02.574213890 -0700 +++ new/test/hotspot/jtreg/vmTestbase/vm/runtime/defmeth/DefaultVsAbstractTest.java 2018-05-21 10:49:49.433497379 -0700 @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2013, 2018, 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. + */ + +package vm.runtime.defmeth; + +import nsk.share.test.TestBase; +import vm.runtime.defmeth.shared.DefMethTest; +import vm.runtime.defmeth.shared.annotation.KnownFailure; +import vm.runtime.defmeth.shared.data.*; +import vm.runtime.defmeth.shared.data.method.param.NewInstanceParam; +import vm.runtime.defmeth.shared.builder.TestBuilder; +import static vm.runtime.defmeth.shared.ExecutionMode.*; +import static vm.runtime.defmeth.shared.data.method.body.CallMethod.Invoke.*; +import static vm.runtime.defmeth.shared.data.method.body.CallMethod.IndexbyteOp.*; + +/** + * Tests on interaction of default methods with abstract methods + * + * The rule: "the superclass always wins." + * + * In searching the superclass hierarchy, a declaration in a superclass is + * preferred to a default in an interface. This preference includes abstract + * methods in superclasses as well; the defaults are only considered when + * the entire implementation hierarchy is silent on the status of the method + * in question. + */ +public class DefaultVsAbstractTest extends DefMethTest { + + public static void main(String[] args) { + TestBase.runTest(new DefaultVsAbstractTest(), args); + } + + /* + * interface I { public int m() default { return 1; } } + * class C implements I { public abstract int m(); } + * + * TEST: new C() throws InstantiationError + */ + public void test0() { + TestBuilder b = factory.getBuilder(); + + Interface I = b.intf("I") + .defaultMethod("m", "()I").returns(1).build() + .build(); + + ConcreteClass C = b.clazz("C").implement(I) + .abstractMethod("m", "()I").build() + .build(); + + b.test() + .callSite(I, C, "m", "()I") + .throws_(InstantiationError.class) + .done() + .run(); + } + + /* + * interface I { + * public int m() default { return 1; } + * } + * class C implements I { + * public abstract int m(); + * } + * class D extends C {} + * + * TEST: I i = new D(); i.m() ==> AME + * TEST: C c = new D(); c.m() ==> AME + * TEST: D d = new D(); d.m() ==> AME + */ + @KnownFailure(modes = {INVOKE_EXACT, INVOKE_GENERIC, INVOKE_WITH_ARGS, INDY }) // Test1_I_D_m: NPE instead of AME + // Test3_D_D_m: AME => IAE => ICCE instead of AME + public void test1() { + TestBuilder b = factory.getBuilder(); + + Interface I = b.intf("I") + .defaultMethod("m", "()I").returns(1).build() + .build(); + + ConcreteClass C = b.clazz("C").implement(I) + .abstractMethod("m", "()I").build() + .build(); + + ConcreteClass D = b.clazz("D").extend(C).build(); + + b.test() + .callSite(I, D, "m", "()I") + .throws_(AbstractMethodError.class) + .done() + .test() + .callSite(C, D, "m", "()I") + .throws_(AbstractMethodError.class) + .done() + .test() + .callSite(D, D, "m", "()I") + .throws_(AbstractMethodError.class) + .done() + .run(); + } + + /* + * interface I { + * default public int m() { return 1; } + * } + * class C { + * abstract public int m(); + * } + * class D extends C implements I {} + * + * TEST: I o = new D(); o.m()I throws AME + * TEST: C o = new D(); o.m()I throws AME + * TEST: D o = new D(); o.m()I throws AME + */ + @KnownFailure(modes = { INVOKE_EXACT, INVOKE_GENERIC, INVOKE_WITH_ARGS, INDY }) // Test1_I_D_m: NPE instead of AME + // Test3_D_D_m: AME => IAE => ICCE instead of AME + public void test2() { + TestBuilder b = factory.getBuilder(); + + Interface I = b.intf("I") + .defaultMethod("m", "()I").returns(1).build() + .build(); + + ConcreteClass C = b.clazz("C") + .abstractMethod("m", "()I").build() + .build(); + + ConcreteClass D = b.clazz("D").extend(C).implement(I).build(); + + b.test() + .callSite(I, D, "m", "()I") + .throws_(AbstractMethodError.class) + .done() + .test() + .callSite(C, D, "m", "()I") + .throws_(AbstractMethodError.class) + .done() + .test() + .callSite(D, D, "m", "()I") + .throws_(AbstractMethodError.class) + .done() + .run(); + } + + /* + * interface I { + * default public int m() { return 1; } + * } + * class C { + * abstract public int m(); + * } + * class D extends C implements I { + * public int m() { return 2; } + * } + * + * TEST: I o = new D(); o.m()I == 2 + * TEST: C o = new D(); o.m()I == 2 + * TEST: D o = new D(); o.m()I == 2 + */ + public void test3() { + TestBuilder b = factory.getBuilder(); + + Interface I = b.intf("I") + .defaultMethod("m", "()I").returns(1).build() + .build(); + + ConcreteClass C = b.clazz("C") + .abstractMethod("m", "()I").build() + .build(); + + ConcreteClass D = b.clazz("D").extend(C).implement(I) + .concreteMethod("m", "()I").returns(2) + .build() + .build(); + + b.test() // I i = new D(); ... + .callSite(I, D, "m", "()I").returns(2) + .done() + .test() // C c = new D(); ... + .callSite(C, D, "m", "()I").returns(2) + .done() + .test() // D d = new C(); ... + .callSite(D, D, "m", "()I").returns(2) + .done() + .run(); + } + + /* + * interface I { + * default public int m() { return 1; } + * } + * class E { + * abstract public int m(); + * } + * class D extends E {} + * class C extends D implements I {} + * + * TEST: I o = new C(); o.m()I throws AME + * TEST: E o = new C(); o.m()I throws AME + * TEST: D o = new C(); o.m()I throws AME + * TEST: C o = new C(); o.m()I throws AME + */ + @KnownFailure(modes = { + INVOKE_EXACT, INVOKE_GENERIC, INVOKE_WITH_ARGS, INDY // Test1_I_C_m: NPE instead of AME + // Test3_D_C_m: AME => IAE => ICCE instead of AME + // Test4_C_C_m: AME => IAE => ICCE instead of AME + }) + public void test4() { + TestBuilder b = factory.getBuilder(); + + Interface I = b.intf("I") + .defaultMethod("m", "()I").returns(1).build() + .build(); + + ConcreteClass E = b.clazz("E") + .abstractMethod("m", "()I").build() + .build(); + + ConcreteClass D = b.clazz("D").extend(E).build(); + + ConcreteClass C = b.clazz("C").extend(D).implement(I).build(); + + b.test() // I i = new C(); ... + .callSite(I, C, "m", "()I") + .throws_(AbstractMethodError.class) + .done() + .test() // E e = new C(); ... + .callSite(E, C, "m", "()I") + .throws_(AbstractMethodError.class) + .done() + .test() // D d = new C(); ... + .callSite(D, C, "m", "()I") + .throws_(AbstractMethodError.class) + .done() + .test() // C c = new C(); ... + .callSite(C, C, "m", "()I") + .throws_(AbstractMethodError.class) + .done() + .run(); + } + + /* + * interface I { + * default public int m() { return 1; } + * } + * class E { + * abstract public int m(); + * } + * class D extends E { + * public int m() { return 2; } + * } + * class C extends D implements I {} + * + * TEST: I o = new C(); o.m()I == 2 + * TEST: I o = new C(); o.m()I == 2 + * TEST: I o = new C(); o.m()I == 2 + * TEST: I o = new C(); o.m()I == 2 + */ + public void test5() { + TestBuilder b = factory.getBuilder(); + + Interface I = b.intf("I") + .defaultMethod("m", "()I").returns(1).build() + .build(); + + ConcreteClass E = b.clazz("E") + .abstractMethod("m", "()I").build() + .build(); + + ConcreteClass D = b.clazz("D").extend(E) + .concreteMethod("m", "()I").returns(2).build() + .build(); + + ConcreteClass C = b.clazz("C").extend(D).implement(I).build(); + + b.test() // I i = new C(); ... + .callSite(I, C, "m", "()I") + .returns(2) + .done() + .test() // E e = new C(); ... + .callSite(I, C, "m", "()I") + .returns(2) + .done() + .test() // D d = new C(); ... + .callSite(I, C, "m", "()I") + .returns(2) + .done() + .test() // C c = new C(); ... + .callSite(I, C, "m", "()I") + .returns(2) + .done() + .run(); + } + + /* + * interface I { + * default public int m() { return 1; } + * } + * interface J { + * default public int m() { return 2; } + * } + * class E { + * abstract public int m(); + * } + * class D extends E { + * public int m() { return 3; } + * } + * class C extends D implements I, J {} + * + * TEST: I o = new C(); o.m()I == 3 + * TEST: J o = new C(); o.m()I == 3 + * TEST: E o = new C(); o.m()I == 3 + * TEST: D o = new C(); o.m()I == 3 + * TEST: J o = new C(); o.m()I == 3 + */ + public void test6() { + TestBuilder b = factory.getBuilder(); + + Interface I = b.intf("I") + .defaultMethod("m", "()I").returns(1).build() + .build(); + + Interface J = b.intf("J") + .defaultMethod("m", "()I").returns(2).build() + .build(); + + ConcreteClass E = b.clazz("E") + .abstractMethod("m", "()I").build() + .build(); + + ConcreteClass D = b.clazz("D").extend(E) + .concreteMethod("m", "()I").returns(3).build() + .build(); + + ConcreteClass C = b.clazz("C").extend(D).implement(I, J).build(); + + + b.test() // I i = new C(); ... + .callSite(I, C, "m", "()I").returns(3) + .done() + .test() // J j = new C(); ... + .callSite(J, C, "m", "()I").returns(3) + .done() + .test() // E e = new C(); ... + .callSite(E, C, "m", "()I").returns(3) + .done() + .test() // D d = new C(); ... + .callSite(D, C, "m", "()I").returns(3) + .done() + .test() // C c = new C(); ... + .callSite(J, C, "m", "()I").returns(3) + .done() + .run(); + } + + /* + * interface I { + * abstract public int m(); + * } + * + * interface J { + * default public int m() { return 1; } + * } + * + * class A implements I; + * + * class B extends A implements J; + * + * TEST: A o = new B(); o.m()I + * returns 1 for REFLECTION and INVOKE_WITH_ARGS + * ICCE for other modes + */ + public void testInvokeInterfaceClassDefaultMethod() { + TestBuilder b = factory.getBuilder(); + + Interface I = b.intf("I") + .abstractMethod("m", "()I").build() + .build(); + + Interface J = b.intf("J") + .extend(I) + .defaultMethod("m", "()I").returns(1).build() + .build(); + + ConcreteClass A = b.clazz("A").implement(I).build(); + + ConcreteClass B = b.clazz("B").extend(A).implement(J).build(); + + String exeMode = factory.getExecutionMode(); + + // the test passes in the reflection mode because there's no way to + // express invokeinterface on a class using Reflection API + // In the test generator, vm.runtime.defmeth.shared.executor.ReflectionTest, + // the invokeinterface is switched to invokevirtual. + // + // the test passes in the INVOKE_WITH_ARGS mode due to the fix for + // JDK-8032010 to conform with the removal of the following check + // during method resolution in JVMS-5.4.3.3 Method Resolution + // "If method lookup succeeds and the method is abstract, but C is not + // abstract, method resolution throws an AbstractMethodError." + if (exeMode.equals("REFLECTION") || + exeMode.equals("INVOKE_WITH_ARGS")) { + b.test().interfaceCallSite(A, B, "m", "()I") + .returns(1).done() + .run(); + } else { + // ICCE in other modes due to + // JVMS-5.4.3.4. Interface Method Resolution + // When resolving an interface method reference: + // If C is not an interface, interface method resolution throws an IncompatibleClassChangeError. + b.test().interfaceCallSite(A, B, "m", "()I") + .throws_(IncompatibleClassChangeError.class).done() + .run(); + } + } + + /* + * interface I { + * abstract public int m(); + * } + * + * interface J { + * abstract public int m(); + * } + * + * class A implements I; + * + * class B extends A implements J; + * + * TEST: A o = new B(); o.m()I + * ICCE for DIRECT mode + * AME for REFLECTION and INVOKE_WITH_ARGS modes + * IAE for other modes + */ + public void testInvokeInterfaceClassAbstractMethod() { + TestBuilder b = factory.getBuilder(); + + Interface I = b.intf("I") + .abstractMethod("m", "()I").build() + .build(); + + Interface J = b.intf("J") + .abstractMethod("m", "()I").build() + .build(); + + ConcreteClass A = b.clazz("A").implement(I).build(); + + ConcreteClass B = b.clazz("B").extend(A).implement(J).build(); + + String exeMode = factory.getExecutionMode(); + + // ICCE in direct mode due to + // JVMS-5.4.3.4. Interface Method Resolution + // When resolving an interface method reference: + // If C is not an interface, interface method resolution throws an IncompatibleClassChangeError. + Class expectedError = IncompatibleClassChangeError.class;; + + b.test().interfaceCallSite(A, B, "m", "()I") + .throws_(expectedError).done() + .run(); + + } + + /* + * interface I { + * public int m() default { return 1; } + * } + * + * interface J { + * public int m() default { return 1; } + * } + * + * class A implements I; + * + * class B extends A implements J; + * + * TEST: A o = new B(); o.m()I + * ICCE for all modes + */ + public void testInvokeInterfaceMultipleDefinedClassDefaultMethod() { + TestBuilder b = factory.getBuilder(); + + Interface I = b.intf("I") + .defaultMethod("m", "()I").returns(1).build() + .build(); + + Interface J = b.intf("J") + .defaultMethod("m", "()I").returns(1).build() + .build(); + + ConcreteClass A = b.clazz("A").implement(I).build(); + + ConcreteClass B = b.clazz("B").extend(A).implement(J).build(); + + String exeMode = factory.getExecutionMode(); + + // ICCE in direct mode due to + // JVMS-5.4.3.4. Interface Method Resolution + // When resolving an interface method reference: + // If C is not an interface, interface method resolution throws an IncompatibleClassChangeError. + Class expectedError = IncompatibleClassChangeError.class; + + b.test().interfaceCallSite(A, B, "m", "()I") + .throws_(expectedError).done() + .run(); + } + +}