RFR: 8062389, 8029459, 8061950: Class.getMethod() is inconsistent with Class.getMethods() results + more

Peter Levart peter.levart at gmail.com
Tue Oct 4 13:40:30 UTC 2016


Hi,

I have a fix for conformance (P2) bug (8062389 
<https://bugs.openjdk.java.net/browse/JDK-8062389>) that also fixes a 
conformance (P3) bug (8029459 
<https://bugs.openjdk.java.net/browse/JDK-8029459>) and a performance 
issue (8061950 <https://bugs.openjdk.java.net/browse/JDK-8061950>):

http://cr.openjdk.java.net/~plevart/jdk9-dev/Class.getMethods.new/webrev.04/

As Daniel Smith writes in 8029459 
<https://bugs.openjdk.java.net/browse/JDK-8029459>, the following 
situation is as expected:

interface I { void m(); }
interface J extends I { void m(); }
interface K extends J {}
K.class.getMethods() = [ J.m ]

But the following has a different result although it should probably 
have the same:

interface I { void m(); }
interface J extends I { void m(); }
interface K extends I, J {}
K.class.getMethods() = [ I.m, J.m ]

He then suggests the following algorithm:

An implementation of getMethods consistent with JLS 8 would include the 
following (see Lambda Spec, Part H, 9.4.1 and 8.4.8):
1) The class's/interface's declared (public) methods
2) The getMethods() of the superclass (if this is a class), minus any 
that have a match in (1)
3) The getMethods() of each superinterface, minus any that have a match 
in (1) or a _concrete_ match in (2) or a match from a more-specific 
class/interface in (2) or (3)

But even that is not sufficient for the following situation:

interface E { void m(); }
interface F extends E { void m(); }
abstract class G implements E {}
abstract class H extends G implements F {}
H.class.getMethods() = [ E.m, F.m ]

The desired result of H.class.getMethods() = [ F.m ]

So a more precise definition is required which is implemented in the fix.

The getMethods() implementation partitions the union of the following 
methods:

1) The class's/interface's declared public methods
2) The getMethods() of the superclass (if this is a class)
3) The non-static getMethods() of each direct superinterface

...into equivalence classes (sets) of methods with same signature 
(return type, name, parameter types). Within each such set, only the 
"most specific" methods are kept and others are discarded. The union of 
the kept methods is the result.

The "more specific" is defined as a partial order within a set of 
methods of same signature:

Method A is more specific than method B if:
- A is declared by a class and B is declared by an interface; or
- A is declared by the same type as or a subtype of B's declaring type 
and both are either declared by classes or both by interfaces (clearly 
if A and B are declared by the same type and have the same signature, 
they are the same method)

If A and B are distinct elements from the set of methods with same 
signature, then either:
- A is more specific than B; or
- B is more specific than A; or
- A and B are incomparable

A sub-set of "most specific" methods are the methods from the set where 
for each such method M, there is no method N != M such that N is "more 
specific" than M.

There can be more than one "most specific" method for a particular 
signature as they can be inherited from multiple unrelated interfaces, but:
- javac prevents compilation when among multiply inherited methods with 
same signature there is at least one default method, so in practice, 
getMethods() will only return multiple methods with same signature if 
they are abstract interface methods. With one exception: bridge methods 
that are created by javac for co-variant overrides are default methods 
in interfaces. For example:

     interface I { Object m(); }
     interface J1 extends I { Map m(); }
     interface J2 extends I { HashMap m(); }
     interface K extends J1, J2 {}

K.class.getMethods() = [ default Object j1.m, abstract Map j1.m, default 
Object j2.m, abstract HashMap j2.m ]

But this is an extreme case where one can still expect multiple 
non-abstract methods with same signature, but different declaring type, 
returned from getMethods().

In order to also fix 8062389 
<https://bugs.openjdk.java.net/browse/JDK-8062389>, getMethod() and 
getMethods() share the same consolidation logic that results in a set of 
"most specific" methods. The partitioning in getMethods() is partially 
performed by collecting Methods into a HashMap where the keys are (name, 
parameter types) tuples and values are linked lists of Method objects 
with possibly varying return and declaring types. The consolidation, 
i.e. keeping only the set of most specific methods as new methods are 
encountered, is performed only among methods in the list that share same 
return type and also removes duplicates. getMethod() uses only one such 
list, consolidates methods that match given name and parameter types and 
returns the 1st method from the resulting list that has the most 
specific return type.

That's it for algorithms used. As partitioning partially happens using 
HashMap with (name, parameter types) keys, lists of methods that form 
values are typically kept short with most of them containing only a 
single method, so this fix also fixes performance issue 8061950 
<https://bugs.openjdk.java.net/browse/JDK-8061950>.

The patch also contains some optimizations around redundant copying of 
arrays and reflective objects.

The FilterNotMostSpecific jtreg test has been updated to accommodate for 
changed behavior. Both of the above extreme cases have been added to the 
test.

So, comments, please.

Regards, Peter



More information about the core-libs-dev mailing list