| /* |
| * 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.annotation.NotApplicableFor; |
| import vm.runtime.defmeth.shared.data.*; |
| import static vm.runtime.defmeth.shared.data.method.body.CallMethod.Invoke.*; |
| import static vm.runtime.defmeth.shared.data.method.body.CallMethod.IndexbyteOp.*; |
| import vm.runtime.defmeth.shared.builder.TestBuilder; |
| import static vm.runtime.defmeth.shared.ExecutionMode.*; |
| |
| /** |
| * Tests on conflicting defaults. |
| * |
| * It is allowable to inherit a default through multiple paths (such as |
| * through a diamond-shaped interface hierarchy), but the resolution procedure |
| * is looking for a unique, most specific default-providing interface. |
| * |
| * If one default shadows another (where a subinterface provides a different |
| * default for an extension method declared in a superinterface), then the less |
| * specific interface is pruned from consideration no matter where it appears |
| * in the inheritance hierarchy. If two or more extended interfaces provide |
| * default implementations, and one is not a superinterface of the other, then |
| * neither is used and a linkage exception is thrown indicating conflicting |
| * default implementations. |
| */ |
| public class ConflictingDefaultsTest extends DefMethTest { |
| public static void main(String[] args) { |
| TestBase.runTest(new ConflictingDefaultsTest(), args); |
| } |
| |
| /* |
| * Conflict |
| * |
| * interface I { int m() default { return 1; } } |
| * interface J { int m() default { return 2; } } |
| * class C implements I, J {} |
| * |
| * TEST: C c = new C(); c.m() ==> ICCE |
| */ |
| public void testConflict() { |
| 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 C = b.clazz("C").implement(I,J).build(); |
| |
| b.test().callSite(C, C, "m","()I") |
| .throws_(IncompatibleClassChangeError.class) |
| .done() |
| |
| .run(); |
| } |
| |
| /* |
| * Maximally-specific Default (0.6.3 spec change) |
| * |
| * interface I { int m(); } |
| * interface J { int m() default { return 2; } } |
| * class C implements I, J {} |
| * |
| * TEST: C c = new C(); c.m() return 2 |
| */ |
| public void testMaximallySpecificDefault() { |
| TestBuilder b = factory.getBuilder(); |
| |
| Interface I = b.intf("I") |
| .abstractMethod("m", "()I").build() |
| .build(); |
| |
| Interface J = b.intf("J") |
| .defaultMethod("m", "()I").returns(2).build() |
| .build(); |
| |
| ConcreteClass C = b.clazz("C").implement(I,J).build(); |
| |
| b.test().callSite(C, C, "m","()I") |
| .returns(2) |
| .done() |
| |
| .run(); |
| } |
| |
| /* |
| * Reabstract |
| * |
| * interface I { int m() default { return 1; } } |
| * interface J extends I { int m(); } |
| * class C implements J {} |
| * |
| * TEST: C c = new C(); c.m() ==> AME |
| */ |
| public void testReabstract() { |
| TestBuilder b = factory.getBuilder(); |
| |
| Interface I = b.intf("I") |
| .defaultMethod("m", "()I").returns(1).build() |
| .build(); |
| |
| Interface J = b.intf("J").extend(I) |
| .abstractMethod("m", "()I").build() |
| .build(); |
| |
| ConcreteClass C = b.clazz("C").implement(J).build(); |
| |
| b.test().callSite(C, C, "m","()I") |
| .throws_(AbstractMethodError.class) |
| .done() |
| |
| .run(); |
| } |
| |
| /* |
| * Reabstract2 |
| * |
| * interface I { int m() default { return 1; } } |
| * interface J extends I { int m(); } |
| * class C implements J {} |
| * class D extends C { callSuper C.m} |
| * |
| * TEST: C c = new C(); c.m() ==> AME |
| * TEST: J j = new C(); j.m() ==> AME |
| * TEST: I i = new C(); i.m() ==> AME |
| * TEST: D d = new D(); d.m() ==> callSuper C.m ==> AME |
| */ |
| public void testReabstract2() { |
| TestBuilder b = factory.getBuilder(); |
| |
| Interface I = b.intf("I") |
| .defaultMethod("m", "()I").returns(1).build() |
| .build(); |
| |
| Interface J = b.intf("J").extend(I) |
| .abstractMethod("m", "()I").build() |
| .build(); |
| |
| ConcreteClass C = b.clazz("C").implement(J).build(); |
| ConcreteClass D = b.clazz("D").extend(C) |
| .concreteMethod("m", "()I").callSuper(C, "m", "()I").build() |
| .build(); |
| |
| b.test().callSite(C, C, "m","()I") |
| .throws_(AbstractMethodError.class) |
| .done() |
| .test().callSite(J, C, "m","()I") |
| .throws_(AbstractMethodError.class) |
| .done() |
| .test().callSite(I, C, "m","()I") |
| .throws_(AbstractMethodError.class) |
| .done() |
| .test().callSite(D, D, "m","()I") |
| .throws_(AbstractMethodError.class) |
| .done() |
| |
| .run(); |
| } |
| |
| /* |
| * ReabstractConflictingDefaults |
| * |
| * interface I { int m() default { return 1; } } |
| * interface J { int m() default { return 2; } } |
| * interface K extends I,J { int m(); } |
| * class A implements I,J {} |
| * class C extends A implements K {} |
| * |
| * TEST: A c = new C(); c.m() ==> AME |
| */ |
| public void testReabstractConflictingDefaults() { |
| 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(); |
| |
| Interface K = b.intf("K").extend(I,J) |
| .abstractMethod("m", "()I").build() |
| .build(); |
| |
| ConcreteClass A = b.clazz("A").implement(I,J).build(); |
| ConcreteClass C = b.clazz("C").extend(A).implement(K).build(); |
| |
| b.test().callSite(A, C, "m","()I") |
| .throws_(AbstractMethodError.class) |
| .done() |
| |
| .run(); |
| } |
| |
| |
| /* |
| * ReabstractConflictingDefaultsInvokeInterface |
| * |
| * interface I { int m() default { return 1; } } |
| * interface J { int m() default { return 2; } } |
| * interface K extends I,J { int m(); } |
| * interface L extends K { } |
| * class A implements I,J {} |
| * class C extends A implements K {} |
| * class D extends C implements L {} |
| * |
| * TEST: I i = new A(); i.m() ==> ICCE |
| * TEST: K k = new C(); k.m() ==> AME |
| * TEST: L l = new D(); l.m() ==> AME |
| */ |
| public void testReabstractConflictingDefaultsInvokeInterface() { |
| 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(); |
| |
| Interface K = b.intf("K").extend(I,J) |
| .abstractMethod("m", "()I").build() |
| .build(); |
| |
| Interface L = b.intf("L").extend(K) |
| .build(); |
| |
| ConcreteClass A = b.clazz("A").implement(I,J).build(); |
| ConcreteClass C = b.clazz("C").extend(A).implement(K).build(); |
| ConcreteClass D = b.clazz("D").extend(C).implement(L).build(); |
| |
| b.test().callSite(I, A, "m","()I") |
| .throws_(IncompatibleClassChangeError.class) |
| .done() |
| .test().callSite(K, C, "m","()I") |
| .throws_(AbstractMethodError.class) |
| .done() |
| .test().callSite(L, D, "m","()I") |
| .throws_(AbstractMethodError.class) |
| .done() |
| |
| .run(); |
| } |
| |
| /* |
| * ReabstractConflictingDefaultsSuper |
| * |
| * interface I { int m() default { return 1; } } |
| * interface J { int m() default { return 2; } } |
| * interface K extends I,J { int m(); } |
| * interface L extends K { } |
| * class A implements I,J {} |
| * class C extends A implements K {} |
| * class D extends C implements L {int m() {callSuper A.m } |
| * |
| * TEST: I i = new A(); i.m() ==> ICCE |
| * TEST: K k = new C(); CallSuper k.m() ==> AME |
| * TEST: L l = new D(); l.m() ==> AME |
| */ |
| public void testReabstractConflictingDefaultsSuper() { |
| 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(); |
| |
| Interface K = b.intf("K").extend(I,J) |
| .abstractMethod("m", "()I").build() |
| .build(); |
| |
| Interface L = b.intf("L").extend(K) |
| .build(); |
| |
| ConcreteClass A = b.clazz("A").implement(I,J).build(); |
| ConcreteClass C = b.clazz("C").extend(A).implement(K).build(); |
| ConcreteClass D = b.clazz("D").extend(C).implement(L) |
| .concreteMethod("m", "()I").callSuper(A, "m", "()I").build() |
| .build(); |
| |
| b.test().callSite(I, A, "m","()I") |
| .throws_(IncompatibleClassChangeError.class) |
| .done() |
| .test().callSite(L, D, "m","()I") |
| .throws_(AbstractMethodError.class) |
| .done() |
| |
| .run(); |
| } |
| |
| /* |
| * testReabstractResolveMethod00705m2 |
| * |
| * Test case for JDK-8027804: JCK resolveMethod test fails expecting AME |
| * |
| * This test is an extension of the JCK test resolveMethod00705m2 |
| * with additional invoke* bytecodes specified for testing purposes. |
| * |
| * interface I { int m() default { return 1; } } |
| * interface J implements I { int m(); } |
| * class A implements J,I {} |
| * class C extends A {} |
| * |
| * TEST: A a = new C(); a.m() ==> AME (invokevirtual) |
| * C c = new C(); c.m() ==> AME (invokevirtual) |
| * J j = new C(); j.m() ==> AME (invokeinterface) |
| * I i = new C(); i.m() ==> AME (invokeinterface) |
| * c.test_Cmethod_ISMR(); c.super.m() ==> AME (invokespecial MR) |
| * a.test_Amethod_ISIMR(); j.super.m() ==> AME (invokespecial IMR) |
| * |
| * For ver > 49, error will be AME |
| * For ver = 49, error will be VE |
| */ |
| |
| @NotApplicableFor(modes = { REDEFINITION }) // Can't redefine a class that gets error during loading |
| public void testReabstractResolveMethod00705m2() { |
| TestBuilder b = factory.getBuilder(); |
| |
| Interface I = b.intf("I") |
| .defaultMethod("m", "()I").returns(1).build() |
| .build(); |
| |
| Interface J = b.intf("J").extend(I) |
| .abstractMethod("m", "()I").build() |
| .build(); |
| |
| ConcreteClass A = b.clazz("A").implement(J,I) |
| .concreteMethod("test_Amethod_ISIMR", "()V") |
| .invoke(SPECIAL, b.clazzByName("J"), b.clazzByName("A"), |
| "m", "()I", INTERFACEMETHODREF) |
| .build() |
| .build(); |
| |
| ConcreteClass C = b.clazz("C").extend(A) |
| .concreteMethod("test_Cmethod_ISMR", "()V") |
| .invoke(SPECIAL, b.clazzByName("C"), b.clazzByName("C"), |
| "m", "()I", CALLSITE) |
| .build() |
| .build(); |
| |
| Class expectedError1, expectedError2; |
| if (factory.getVer() >=52) { |
| expectedError1 = expectedError2 = AbstractMethodError.class; |
| } else { |
| expectedError1 = expectedError2 = VerifyError.class; |
| } |
| |
| b.test().callSite(A, C, "m", "()I") |
| .throws_(expectedError2) |
| .done() |
| .test().callSite(C, C, "m", "()I") |
| .throws_(expectedError2) |
| .done() |
| .test().callSite(J, C, "m", "()I") |
| .throws_(expectedError1) |
| .done() |
| .test().callSite(I, C, "m", "()I") |
| .throws_(expectedError1) |
| .done() |
| .test().callSite(C, C, "test_Cmethod_ISMR", "()V") |
| .throws_(expectedError2) |
| .done() |
| .test().callSite(A, C, "test_Amethod_ISIMR", "()V") |
| .throws_(expectedError2) |
| .done() |
| |
| .run(); |
| } |
| |
| /* |
| * Shadow |
| * |
| * interface I { int m() default { return 1; } } |
| * interface J extends I { int m() default { return 2; } } |
| * class C implements J {} |
| * |
| * TEST: [I|J|C] c = new C(); c.m() == 2; |
| */ |
| public void testShadow() { |
| TestBuilder b = factory.getBuilder(); |
| |
| Interface I = b.intf("I") |
| .defaultMethod("m", "()I").returns(1).build() |
| .build(); |
| |
| Interface J = b.intf("J").extend(I) |
| .defaultMethod("m", "()I").returns(2).build() |
| .build(); |
| |
| ConcreteClass C = b.clazz("C").implement(J).build(); |
| |
| b.test().callSite(I, C, "m","()I") |
| .returns(2) |
| .done() |
| .test() |
| .callSite(J, C, "m","()I") |
| .returns(2) |
| .done() |
| .test() |
| .callSite(C, C, "m","()I") |
| .returns(2) |
| .done() |
| |
| .run(); |
| } |
| |
| /* |
| * Disqualified |
| * |
| * interface I { int m() default { return 1; } } |
| * interface J extends I { int m() default { return 2; } } |
| * class C implements I, J {} |
| * |
| * TEST: [I|J|C] c = new C(); c.m() == 2; |
| */ |
| public void testDisqualified() { |
| TestBuilder b = factory.getBuilder(); |
| |
| Interface I = b.intf("I") |
| .defaultMethod("m", "()I").returns(1).build() |
| .build(); |
| |
| Interface J = b.intf("J").extend(I) |
| .defaultMethod("m", "()I").returns(2).build() |
| .build(); |
| |
| ConcreteClass C = b.clazz("C").implement(I,J).build(); |
| |
| b.test() |
| .callSite(I, C, "m","()I") |
| .returns(2) |
| .done() |
| .test() |
| .callSite(J, C, "m","()I") |
| .returns(2) |
| .done() |
| .test() |
| .callSite(C, C, "m","()I") |
| .returns(2) |
| .done() |
| |
| .run(); |
| } |
| |
| /* |
| * Mixed arity |
| * |
| * interface I { int m() default { return 1; } } |
| * interface J { int m(int i) default { return 2; } } |
| * class C implements I, J {} |
| * |
| * TEST: I i = new C(); i.m() == 1; i.m(0) ==> NSME |
| * TEST: J j = new C(); j.m() ==> NSME; j.m(0) == 2 |
| * TEST: C c = new C(); c.m() == 1; c.m(0) == 2 |
| */ |
| @KnownFailure(modes = { INVOKE_EXACT, INVOKE_GENERIC, INDY }) // IncompatibleClassChangeError instead of NoSuchMethodError |
| public void testMixedArity1() { |
| TestBuilder b = factory.getBuilder(); |
| |
| Interface I = b.intf("I") |
| .defaultMethod("m", "()I").returns(1).build() |
| .build(); |
| |
| Interface J = b.intf("J") |
| .defaultMethod("m", "(I)I").returns(2).build() |
| .build(); |
| |
| ConcreteClass C = b.clazz("C").implement(I,J).build(); |
| |
| // I i = new C(); ... |
| b.test() |
| .callSite(I, C, "m","()I") |
| .returns(1) |
| .done() |
| .test() |
| .callSite(I, C, "m","(I)I") |
| .params(0) |
| .throws_(NoSuchMethodError.class) |
| .done() |
| |
| // J j = new C(); ... |
| .test() |
| .callSite(J, C, "m","()I") |
| .throws_(NoSuchMethodError.class) |
| .done() |
| .test() |
| .callSite(J, C, "m","(I)I") |
| .params(0) |
| .returns(2) |
| .done() |
| |
| // C c = new C(); ... |
| .test() |
| .callSite(C, C, "m","()I") |
| .returns(1) |
| .done() |
| .test() |
| .callSite(C, C, "m","(I)I") |
| .params(0) |
| .returns(2) |
| .done() |
| |
| .run(); |
| } |
| |
| /* |
| * Mixed arity |
| * |
| * interface I { int m() default { return 1; } } |
| * interface J { int m() default { return 2; } } |
| * class C implements I, J { int m(int i) { return 3; }} |
| * |
| * TEST: I i = new C(); i.m() ==> ICCE |
| * TEST: J j = new C(); j.m() ==> ICCE |
| * TEST: C c = new C(); c.m() ==> ICCE; c.m(0) == 3 |
| */ |
| public void testMixedArity2() { |
| 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 C = b.clazz("C").implement(I,J) |
| .concreteMethod("m", "(I)I").returns(3).build() |
| .build(); |
| |
| // I i = new C(); ... |
| b.test() |
| .callSite(I, C, "m","()I") |
| .throws_(IncompatibleClassChangeError.class) |
| .done() |
| |
| // J j = new C(); ... |
| .test() |
| .callSite(J, C, "m","()I") |
| .throws_(IncompatibleClassChangeError.class) |
| .done() |
| |
| // C c = new C(); ... |
| .test() |
| .callSite(C, C, "m","()I") |
| .throws_(IncompatibleClassChangeError.class) |
| .done() |
| .test() |
| .callSite(C, C, "m","(I)I") |
| .params(0) |
| .returns(3) |
| .done() |
| |
| .run(); |
| } |
| } |