--- old/src/share/classes/java/lang/Class.java 2014-06-16 12:36:00.769970912 +0200 +++ new/src/share/classes/java/lang/Class.java 2014-06-16 12:36:00.585970918 +0200 @@ -2697,12 +2697,26 @@ } 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) { @@ -2710,6 +2724,9 @@ methods = Arrays.copyOf(methods, 2 * methods.length); } methods[length++] = m; + + if (m != null && m.isDefault()) + defaults++; } void addAll(Method[] ma) { @@ -2743,7 +2760,10 @@ } } - 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); @@ -2759,19 +2779,35 @@ 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 @@ -2789,9 +2825,48 @@ } } + /* 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,9 +2894,8 @@ // out concrete implementations inherited from superclasses at // the end. MethodArray inheritedMethods = new MethodArray(); - Class[] interfaces = getInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - inheritedMethods.addAllNonStatic(interfaces[i].privateGetPublicMethods()); + for (Class i : getInterfaces()) { + inheritedMethods.addInterfaceMethods(i.privateGetPublicMethods()); } if (!isInterface()) { Class c = getSuperclass(); @@ -2832,8 +2906,10 @@ // 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 @@ -2846,9 +2922,10 @@ // 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) { @@ -2923,8 +3000,21 @@ 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 @@ -2932,6 +3022,14 @@ // 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), @@ -2953,7 +3051,7 @@ Class[] interfaces = getInterfaces(); for (Class c : interfaces) if ((res = c.getMethod0(name, parameterTypes, false)) != null) - return res; + allInterfaceCandidates.add(res); // Not found return null; }