From 6445980a62f7ddea2603c5bcb1b697a4edce960f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20Borggr=C3=A9n-Franck?= Date: Tue, 10 Jun 2014 14:40:20 +0200 Subject: [PATCH] 8029674: (reflect) getMethods returns default methods that are not members of the class Filter out methods that have a more specific default method from result Reviewed-by: darcy, dlsmith --- jdk/src/share/classes/java/lang/Class.java | 129 +++- .../FilterNotMostSpecific.java | 691 ++++++++++++++++++ 2 files changed, 805 insertions(+), 15 deletions(-) create mode 100644 jdk/test/java/lang/reflect/DefaultMethodMembers/FilterNotMostSpecific.java diff --git a/jdk/src/share/classes/java/lang/Class.java b/jdk/src/share/classes/java/lang/Class.java index 32695c11c2a..71daa390286 100644 --- a/jdk/src/share/classes/java/lang/Class.java +++ b/jdk/src/share/classes/java/lang/Class.java @@ -2696,12 +2696,26 @@ public final class Class implements java.io.Serializable, } static class MethodArray { + // Don't add or remove methods except by add() or remove() calls. private Method[] methods; private int length; + private int defaults; MethodArray() { - methods = new Method[20]; + this(20); + } + + MethodArray(int initialSize) { + if (initialSize < 2) + throw new IllegalArgumentException("Size should be 2 or more"); + + methods = new Method[initialSize]; length = 0; + defaults = 0; + } + + boolean hasDefaults() { + return defaults != 0; } void add(Method m) { @@ -2709,6 +2723,9 @@ public final class Class implements java.io.Serializable, methods = Arrays.copyOf(methods, 2 * methods.length); } methods[length++] = m; + + if (m != null && m.isDefault()) + defaults++; } void addAll(Method[] ma) { @@ -2742,7 +2759,10 @@ public final class Class implements java.io.Serializable, } } - void addAllNonStatic(Method[] methods) { + /* Add Methods declared in an interface to this MethodArray. + * Static methods declared in interfaces are not inherited. + */ + void addInterfaceMethods(Method[] methods) { for (Method candidate : methods) { if (!Modifier.isStatic(candidate.getModifiers())) { add(candidate); @@ -2758,19 +2778,35 @@ public final class Class implements java.io.Serializable, return methods[i]; } - void removeByNameAndSignature(Method toRemove) { + Method getFirst() { + for (Method m : methods) + if (m != null) + return m; + return null; + } + + void removeByNameAndDescriptor(Method toRemove) { for (int i = 0; i < length; i++) { Method m = methods[i]; - if (m != null && - m.getReturnType() == toRemove.getReturnType() && - m.getName() == toRemove.getName() && - arrayContentsEq(m.getParameterTypes(), - toRemove.getParameterTypes())) { - methods[i] = null; + if (m != null && matchesNameAndDescriptor(m, toRemove)) { + remove(i); } } } + private void remove(int i) { + if (methods[i] != null && methods[i].isDefault()) + defaults--; + methods[i] = null; + } + + private boolean matchesNameAndDescriptor(Method m1, Method m2) { + return m1.getReturnType() == m2.getReturnType() && + m1.getName() == m2.getName() && // name is guaranteed to be interned + arrayContentsEq(m1.getParameterTypes(), + m2.getParameterTypes()); + } + void compactAndTrim() { int newPos = 0; // Get rid of null slots @@ -2788,9 +2824,48 @@ public final class Class implements java.io.Serializable, } } + /* Removes all Methods from this MethodArray that have a more specific + * default Method in this MethodArray. + * + * Users of MethodArray are responsible for pruning Methods that have + * a more specific concrete Method. + */ + void removeLessSpecifics() { + if (!hasDefaults()) + return; + + for (int i = 0; i < length; i++) { + Method m = get(i); + if (m == null || !m.isDefault()) + continue; + + for (int j = 0; j < length; j++) { + if (i == j) + continue; + + Method candidate = get(j); + if (candidate == null) + continue; + + if (!matchesNameAndDescriptor(m, candidate)) + continue; + + if (hasMoreSpecificClass(m, candidate)) + remove(j); + } + } + } + Method[] getArray() { return methods; } + + // Returns true if m1 is more specific than m2 + static boolean hasMoreSpecificClass(Method m1, Method m2) { + Class m1Class = m1.getDeclaringClass(); + Class m2Class = m2.getDeclaringClass(); + return m1Class != m2Class && m2Class.isAssignableFrom(m1Class); + } } @@ -2819,7 +2894,7 @@ public final class Class implements java.io.Serializable, // the end. MethodArray inheritedMethods = new MethodArray(); for (Class i : getInterfaces()) { - inheritedMethods.addAllNonStatic(i.privateGetPublicMethods()); + inheritedMethods.addInterfaceMethods(i.privateGetPublicMethods()); } if (!isInterface()) { Class c = getSuperclass(); @@ -2830,8 +2905,10 @@ public final class Class implements java.io.Serializable, // interface methods for (int i = 0; i < supers.length(); i++) { Method m = supers.get(i); - if (m != null && !Modifier.isAbstract(m.getModifiers())) { - inheritedMethods.removeByNameAndSignature(m); + if (m != null && + !Modifier.isAbstract(m.getModifiers()) && + !m.isDefault()) { + inheritedMethods.removeByNameAndDescriptor(m); } } // Insert superclass's inherited methods before @@ -2844,9 +2921,10 @@ public final class Class implements java.io.Serializable, // Filter out all local methods from inherited ones for (int i = 0; i < methods.length(); i++) { Method m = methods.get(i); - inheritedMethods.removeByNameAndSignature(m); + inheritedMethods.removeByNameAndDescriptor(m); } methods.addAllIfNotPresent(inheritedMethods); + methods.removeLessSpecifics(); methods.compactAndTrim(); res = methods.getArray(); if (rd != null) { @@ -2919,8 +2997,21 @@ public final class Class implements java.io.Serializable, return (res == null ? res : getReflectionFactory().copyMethod(res)); } - private Method getMethod0(String name, Class[] parameterTypes, boolean includeStaticMethods) { + MethodArray interfaceCandidates = new MethodArray(2); + Method res = privateGetMethodRecursive(name, parameterTypes, includeStaticMethods, interfaceCandidates); + if (res != null) + return res; + + // Not found on class or superclass directly + interfaceCandidates.removeLessSpecifics(); + return interfaceCandidates.getFirst(); // may be null + } + + private Method privateGetMethodRecursive(String name, + Class[] parameterTypes, + boolean includeStaticMethods, + MethodArray allInterfaceCandidates) { // Note: the intent is that the search algorithm this routine // uses be equivalent to the ordering imposed by // privateGetPublicMethods(). It fetches only the declared @@ -2928,6 +3019,14 @@ public final class Class implements java.io.Serializable, // number of Method objects which have to be created for the // common case where the method being requested is declared in // the class which is being queried. + // + // Due to default methods, unless a method is found on a superclass, + // methods declared in any superinterface needs to be considered. + // Collect all candidates declared in superinterfaces in {@code + // allInterfaceCandidates} and select the most specific if no match on + // a superclass is found. + + // Must _not_ return root methods Method res; // Search declared public methods if ((res = searchMethods(privateGetDeclaredMethods(true), @@ -2949,7 +3048,7 @@ public final class Class implements java.io.Serializable, Class[] interfaces = getInterfaces(); for (Class c : interfaces) if ((res = c.getMethod0(name, parameterTypes, false)) != null) - return res; + allInterfaceCandidates.add(res); // Not found return null; } diff --git a/jdk/test/java/lang/reflect/DefaultMethodMembers/FilterNotMostSpecific.java b/jdk/test/java/lang/reflect/DefaultMethodMembers/FilterNotMostSpecific.java new file mode 100644 index 00000000000..4a3d89836db --- /dev/null +++ b/jdk/test/java/lang/reflect/DefaultMethodMembers/FilterNotMostSpecific.java @@ -0,0 +1,691 @@ +/* + * Copyright (c) 2014, 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 + * @bug 8029674 + * @summary Verify that the right interface methods are returned by + * Class.getMethod() and Class.getMethods() + * @run testng FilterNotMostSpecific + */ + +import java.lang.reflect.*; +import java.lang.annotation.*; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +public class FilterNotMostSpecific { + + @Test(dataProvider="getCases") + public void testGetMethod(Class iface) { + boolean match = false; + MethodDesc[] expectedMethods = iface.getAnnotationsByType(MethodDesc.class); + + for (MethodDesc expected : expectedMethods) { + if (expected.isGetMethodReturn()) { + try { + Method m = iface.getMethod(expected.name()); + if (!assertMatch(expected, m)) + fail(failMsg(expected, m, iface)); + else + match = true; + } catch (NoSuchMethodException e) { + fail("expected: " + toMethodString(expected), e); + } + } + } + assert(match); + } + + @Test(dataProvider="getCases") + public void testGetMethods(Class iface) { + List foundMethods = filterObjectMethods(iface.getMethods()); + MethodDesc[] expectedMethods = iface.getAnnotationsByType(MethodDesc.class); + Set used = new HashSet<>(); + + for (MethodDesc expected : expectedMethods) { + boolean found = false; + + for (Method m : foundMethods) { + if (used.contains(m)) + continue; + + if(expected.name().equals(m.getName()) && + expected.declaringClass() ==m.getDeclaringClass()) { + + found = true; + assertMatch(expected, m); + used.add(m); + break; + } + } + if (! found) + fail("On: "+ iface +"\nDid not find " + toMethodString(expected) + " among " + foundMethods); + } + assertEquals(foundMethods.size(), expectedMethods.length, + "\non: " + iface + + "\nexpected: " + toMethodStrings(expectedMethods) + + "\nfound: " + foundMethods + "\n"); + } + + private boolean assertMatch(MethodDesc expected, Method m) { + if (!expected.name().equals(m.getName())) + return false; + if (expected.declaringClass() != m.getDeclaringClass()) + return false; + + if (expected.kind() == MethodKind.ABSTRACT) + assertTrue(Modifier.isAbstract(m.getModifiers()), m + " should be ABSTRACT"); + else if (expected.kind() == MethodKind.CONCRETE) + assertTrue(!Modifier.isAbstract(m.getModifiers()) && !m.isDefault(), m + " should be CONCRETE"); + else if (expected.kind() == MethodKind.DEFAULT) + assertTrue(m.isDefault(), m + " should be DEFAULT"); + + return true; + } + + private String failMsg(MethodDesc expected, Method m, Class iface) { + return "\nOn interface: " + iface + + "\nexpected: " + toMethodString(expected) + + "\nfound: " + m; + } + + private static List filterObjectMethods(Method[] in) { + return Arrays.stream(in). + filter(m -> (m.getDeclaringClass() != java.lang.Object.class)). + collect(Collectors.toList()); + } + + private String toMethodString(MethodDesc m) { + return m.declaringClass().getSimpleName().toString() + "." + + m.name() + "()"; + } + + private List toMethodStrings(MethodDesc[] m) { + return Arrays.stream(m). + map(this::toMethodString) + .collect(Collectors.toList()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Repeatable(MethodDescs.class) + public @interface MethodDesc { + String name(); + Class declaringClass(); + MethodKind kind() default MethodKind.ABSTRACT; + boolean isGetMethodReturn() default false; + } + + @Retention(RetentionPolicy.RUNTIME) + public @interface MethodDescs { + MethodDesc[] value(); + } + + public static enum MethodKind { + ABSTRACT, + CONCRETE, + DEFAULT, + } + // base interfaces + interface I { void nonDefault(); } + interface J extends I { void nonDefault(); } + + interface Jprim extends I {} + interface Jbis extends Jprim { void nonDefault(); } + + // interesting cases + + @MethodDesc(name="nonDefault", declaringClass=Jbis.class, + isGetMethodReturn=true) + interface P1 extends Jbis {} + + @MethodDesc(name="nonDefault", declaringClass=Jbis.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=I.class) + interface P2 extends Jbis, Jprim {} + + @MethodDesc(name="nonDefault", declaringClass=Jbis.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=I.class) + interface P3 extends Jbis, Jprim, I {} + + @MethodDesc(name="nonDefault", declaringClass=I.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=J.class) + interface P4 extends I, J {} + + @MethodDesc(name="nonDefault", declaringClass=J.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=I.class) + interface P5 extends J, I {} + + @MethodDesc(name="nonDefault", declaringClass=J.class, + isGetMethodReturn=true) + interface K1 extends J {} + + @MethodDesc(name="nonDefault", declaringClass=K1M.class, + isGetMethodReturn=true) + interface K1M extends J { void nonDefault(); } + + @MethodDesc(name="nonDefault", declaringClass=I.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=J.class) + interface K2 extends I, J {} + + @MethodDesc(name="nonDefault", declaringClass=J.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=I.class) + interface K2O extends J, I {} + + @MethodDesc(name="nonDefault", declaringClass=K2M.class, + isGetMethodReturn=true) + interface K2M extends J, I { void nonDefault(); } + + // base interfaces default methods + interface L { default void isDefault() {} void nonDefault(); } + interface M extends L { default void isDefault() {} void nonDefault(); } + + // test cases default methods + + @MethodDesc(name="nonDefault", declaringClass=M.class, + isGetMethodReturn=true) + @MethodDesc(name="isDefault", declaringClass=M.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + interface N1 extends M {} + + @MethodDesc(name="isDefault", declaringClass=N1D.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=M.class, + isGetMethodReturn=true) + interface N1D extends M { default void isDefault() {}} + + @MethodDesc(name="nonDefault", declaringClass=N1N.class, + isGetMethodReturn=true) + @MethodDesc(name="isDefault", declaringClass=M.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + interface N1N extends M { void nonDefault(); } + + @MethodDesc(name="isDefault", declaringClass=N1DN.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=N1DN.class, + isGetMethodReturn=true) + interface N1DN extends M { default void isDefault() {} void nonDefault(); } + + @MethodDesc(name="isDefault", declaringClass=M.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=L.class) + @MethodDesc(name="nonDefault", declaringClass=M.class, + isGetMethodReturn=true) + interface N2 extends M, L {} + + @MethodDesc(name="isDefault", declaringClass=M.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=L.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=M.class) + interface N22 extends L, M {} + + @MethodDesc(name="isDefault", declaringClass=N2D.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=L.class) + @MethodDesc(name="nonDefault", declaringClass=M.class, + isGetMethodReturn=true) + interface N2D extends M, L { default void isDefault() {}} + + @MethodDesc(name="isDefault", declaringClass=M.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=N2N.class, + isGetMethodReturn=true) + interface N2N extends M, L { void nonDefault(); } + + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class, + isGetMethodReturn=true) + interface N2DN extends M, L { default void isDefault() {} void nonDefault(); } + + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=L.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=M.class) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class) + interface O1 extends L, M, N2DN {} + + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=L.class) + @MethodDesc(name="nonDefault", declaringClass=M.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class) + interface O2 extends M, N2DN, L {} + + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=L.class) + @MethodDesc(name="nonDefault", declaringClass=M.class) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class, + isGetMethodReturn=true) + interface O3 extends N2DN, L, M {} + + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=L.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=M.class) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class) + abstract class C1 implements L, M, N2DN {} + + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=L.class) + @MethodDesc(name="nonDefault", declaringClass=M.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class) + abstract class C2 implements M, N2DN, L {} + + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=L.class) + @MethodDesc(name="nonDefault", declaringClass=M.class) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class, + isGetMethodReturn=true) + abstract class C3 implements N2DN, L, M {} + + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=C4.class, + kind=MethodKind.CONCRETE, isGetMethodReturn=true) + class C4 implements L, M, N2DN { public void nonDefault() {} } + + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=C5.class, + kind=MethodKind.CONCRETE, isGetMethodReturn=true) + class C5 implements M, N2DN, L { public void nonDefault() {} } + + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=C6.class, + kind=MethodKind.CONCRETE, isGetMethodReturn=true) + class C6 implements N2DN, L, M { public void nonDefault() {} } + + // reabstraction + + @MethodDesc(name="isDefault", declaringClass=R1.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=L.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=M.class) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class) + interface R1 extends L, M, N2DN { void isDefault(); } + + @MethodDesc(name="isDefault", declaringClass=R2.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=L.class) + @MethodDesc(name="nonDefault", declaringClass=M.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class) + interface R2 extends M, N2DN, L { void isDefault(); } + + @MethodDesc(name="isDefault", declaringClass=R3.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=L.class) + @MethodDesc(name="nonDefault", declaringClass=M.class) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class, + isGetMethodReturn=true) + interface R3 extends N2DN, L, M { void isDefault(); } + + // this one is strange but logical, getMethod finds N2DN first, which is + // default but not the most specific + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="isDefault", declaringClass=R1.class) + @MethodDesc(name="nonDefault", declaringClass=L.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=M.class) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class) + interface R4 extends L, M, N2DN, R1 {} + + // this one is strange but logical, getMethod finds N2DN first, which is + // default but not the most specific + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="isDefault", declaringClass=R2.class) + @MethodDesc(name="nonDefault", declaringClass=L.class) + @MethodDesc(name="nonDefault", declaringClass=M.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class) + interface R5 extends M, N2DN, R2, L {} + + // this one is strange but logical, getMethod finds N2DN first, which is + // default but not the most specific + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="isDefault", declaringClass=R3.class) + @MethodDesc(name="nonDefault", declaringClass=L.class) + @MethodDesc(name="nonDefault", declaringClass=M.class) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class, + isGetMethodReturn=true) + interface R6 extends N2DN, R3, L, M {} + + // the following three finds the "right" one + @MethodDesc(name="isDefault", declaringClass=R1.class, + isGetMethodReturn=true) + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT) + @MethodDesc(name="nonDefault", declaringClass=L.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=M.class) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class) + interface R7 extends L, M, R1, N2DN {} + + @MethodDesc(name="isDefault", declaringClass=R2.class, + isGetMethodReturn=true) + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT) + @MethodDesc(name="nonDefault", declaringClass=L.class) + @MethodDesc(name="nonDefault", declaringClass=M.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class) + interface R8 extends M, R2, N2DN, L {} + + @MethodDesc(name="isDefault", declaringClass=R3.class, + isGetMethodReturn=true) + @MethodDesc(name="isDefault", declaringClass=N2DN.class, + kind=MethodKind.DEFAULT) + @MethodDesc(name="nonDefault", declaringClass=L.class) + @MethodDesc(name="nonDefault", declaringClass=M.class) + @MethodDesc(name="nonDefault", declaringClass=N2DN.class, + isGetMethodReturn=true) + interface R9 extends R3, N2DN, L, M {} + + // More reabstraction + interface Z1 { void z(); } + interface Z2 extends Z1 { default void z() {} } + + @MethodDesc(name="z", declaringClass=Z2.class, + isGetMethodReturn=true, kind=MethodKind.DEFAULT) + interface Z31 extends Z1, Z2 {} + + @MethodDesc(name="z", declaringClass=Z2.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + interface Z32 extends Z2, Z1 {} + + interface Z3 extends Z2, Z1 { void z(); } + + @MethodDesc(name="z", declaringClass=Z2.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="z", declaringClass=Z3.class) + interface Z41 extends Z1, Z2, Z3 { } + + @MethodDesc(name="z", declaringClass=Z2.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="z", declaringClass=Z3.class) + interface Z42 extends Z2, Z3, Z1 { } + + @MethodDesc(name="z", declaringClass=Z3.class, + isGetMethodReturn=true) + @MethodDesc(name="z", declaringClass=Z2.class, + kind=MethodKind.DEFAULT) + interface Z43 extends Z3, Z1, Z2 { } + + @MethodDesc(name="z", declaringClass=Z2.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="z", declaringClass=Z3.class) + abstract class ZC41 implements Z1, Z2, Z3 { } + + @MethodDesc(name="z", declaringClass=Z2.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="z", declaringClass=Z3.class) + abstract class ZC42 implements Z2, Z3, Z1 { } + + @MethodDesc(name="z", declaringClass=Z3.class, + isGetMethodReturn=true) + @MethodDesc(name="z", declaringClass=Z2.class, + kind=MethodKind.DEFAULT) + abstract class ZC43 implements Z3, Z1, Z2 { } + + // More reabstraction + concretization + interface X1 { default void x() {} } + interface X2 extends X1 { void x(); } + + @MethodDesc(name="x", declaringClass=X1.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + @MethodDesc(name="x", declaringClass=X2.class) + interface X31 extends X1, X2 {} + + @MethodDesc(name="x", declaringClass=X2.class, + isGetMethodReturn=true) + @MethodDesc(name="x", declaringClass=X1.class, + kind=MethodKind.DEFAULT) + interface X32 extends X2, X1 {} + + @MethodDesc(name="x", declaringClass=X3.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + interface X3 extends X2, X1 { default void x() {} } + + // order shouldn't matter here + @MethodDesc(name="x", declaringClass=X3.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + interface X41 extends X1, X2, X3 { } + + @MethodDesc(name="x", declaringClass=X3.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + interface X42 extends X2, X3, X1 { } + + @MethodDesc(name="x", declaringClass=X3.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + interface X43 extends X3, X1, X2 { } + + // order shouldn't matter here + @MethodDesc(name="x", declaringClass=X3.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + abstract class XC41 implements X1, X2, X3 { } + + @MethodDesc(name="x", declaringClass=X3.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + abstract class XC42 implements X2, X3, X1 { } + + @MethodDesc(name="x", declaringClass=X3.class, + kind=MethodKind.DEFAULT, isGetMethodReturn=true) + abstract class XC43 implements X3, X1, X2 { } + + interface K extends I, J { void nonDefault(); } + + @MethodDesc(name="nonDefault", declaringClass=I.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=J.class) + @MethodDesc(name="nonDefault", declaringClass=K.class) + abstract class ZZ1 implements I, J, K {} + + @MethodDesc(name="nonDefault", declaringClass=I.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=J.class) + @MethodDesc(name="nonDefault", declaringClass=K.class) + abstract class ZZ2 extends ZZ1 implements K, I, J {} + + @MethodDesc(name="nonDefault", declaringClass=I.class, + isGetMethodReturn=true) + @MethodDesc(name="nonDefault", declaringClass=J.class) + @MethodDesc(name="nonDefault", declaringClass=K.class) + abstract class ZZ3 extends ZZ2 implements J, K, I {} + + // bridges + interface B1A { Object m(); } + interface B1B extends B1A { Map m(); } + + @MethodDesc(name="m", declaringClass=B1C.class, + isGetMethodReturn=true) + @MethodDesc(name="m", declaringClass=B1C.class, + kind=MethodKind.DEFAULT) + @MethodDesc(name="m", declaringClass=B1C.class, + kind=MethodKind.DEFAULT) + interface B1C extends B1B { HashMap m(); } + + @MethodDesc(name="m", declaringClass=B2.class, + isGetMethodReturn=true) + @MethodDesc(name="m", declaringClass=B2.class, + kind=MethodKind.DEFAULT) + @MethodDesc(name="m", declaringClass=B2.class, + kind=MethodKind.DEFAULT) + interface B2 extends B1C { HashMap m(); } + + @MethodDesc(name="m", declaringClass=B2.class, //HahsMap + isGetMethodReturn=true) + @MethodDesc(name="m", declaringClass=B2.class, //Map + kind=MethodKind.DEFAULT) + @MethodDesc(name="m", declaringClass=B2.class, //Object + kind=MethodKind.DEFAULT) + interface B3A extends B2, B1A {} + + // this one is funny since HashMap isn't a bridge thus not a default + @MethodDesc(name="m", declaringClass=B2.class, //HashMap + isGetMethodReturn=true) + @MethodDesc(name="m", declaringClass=B2.class, //Map + kind=MethodKind.DEFAULT) + @MethodDesc(name="m", declaringClass=B2.class, //Object + kind=MethodKind.DEFAULT) + @MethodDesc(name="m", declaringClass=B1C.class) //HashMap + interface B3B extends B2, B1C {} + + // same name different params type + interface A1 { void m(); void m(int i); void m(int i, int j); } + interface A2A extends A1 { void m(); void m(int i); void m(int i, int j); } + interface A2B extends A1 { void m(); void m(int i); default void m(int i, int j) {} } + + @MethodDesc(name="m", declaringClass=A1.class, + isGetMethodReturn=true) + @MethodDesc(name="m", declaringClass=A1.class) + @MethodDesc(name="m", declaringClass=A1.class) + @MethodDesc(name="m", declaringClass=A2A.class) + @MethodDesc(name="m", declaringClass=A2A.class) + @MethodDesc(name="m", declaringClass=A2A.class) + interface A3A extends A1, A2A {} + + @MethodDesc(name="m", declaringClass=A1.class, + isGetMethodReturn=true) + @MethodDesc(name="m", declaringClass=A1.class) + @MethodDesc(name="m", declaringClass=A2B.class) + @MethodDesc(name="m", declaringClass=A2B.class) + @MethodDesc(name="m", declaringClass=A2B.class, + kind=MethodKind.DEFAULT) + interface A3B extends A1, A2B {} + + @DataProvider + public Object[][] getCases() { return CASES; } + public static final Class[][] CASES = { + { K1.class }, + { K1M.class }, + { K2.class }, + { K2O.class }, + { K2M.class }, + + { N1.class }, + { N1D.class }, + { N1N.class }, + { N1DN.class }, + + { N2.class }, + { N22.class }, + { N2D.class }, + { N2N.class }, + { N2DN.class }, + + { P1.class }, + { P2.class }, + { P3.class }, + { P4.class }, + { P5.class }, + + { O1.class }, + { O2.class }, + { O3.class }, + + { C1.class }, + { C2.class }, + { C3.class }, + + { C4.class }, + { C5.class }, + { C6.class }, + + { R1.class }, + { R2.class }, + { R3.class }, + + { R4.class }, + { R5.class }, + { R6.class }, + + { R7.class }, + { R8.class }, + { R9.class }, + + { Z31.class }, + { Z32.class }, + + { Z41.class }, + { Z42.class }, + { Z43.class }, + + { ZC41.class }, + { ZC42.class }, + { ZC43.class }, + + { ZZ1.class }, + { ZZ2.class }, + { ZZ3.class }, + + { X3.class }, + { X31.class }, + { X32.class }, + + { X41.class }, + { X42.class }, + { X43.class }, + + { XC41.class }, + { XC42.class }, + { XC43.class }, + + { B1C.class }, + { B2.class }, + { B3A.class }, + { B3B.class }, + + { A3A.class }, + { A3B.class }, + }; +}