6060import java .util .List ;
6161import java .util .Map ;
6262import java .util .Set ;
63+ import java .util .function .Predicate ;
64+ import java .util .stream .Stream ;
6365import org .eclipse .jdt .core .compiler .CharOperation ;
6466import org .eclipse .jdt .internal .compiler .ast .ASTNode ;
6567import org .eclipse .jdt .internal .compiler .ast .LambdaExpression ;
@@ -96,6 +98,8 @@ abstract public class ReferenceBinding extends TypeBinding {
9698 int typeBits ; // additional bits characterizing this type
9799 protected MethodBinding [] singleAbstractMethod ;
98100
101+ protected static final DysfunctionalInterfaceException DYSFUNCTIONAL_INTERFACE_EXCEPTION = new DysfunctionalInterfaceException ("Not a functional interface" ); //$NON-NLS-1$
102+
99103 public static final ReferenceBinding LUB_GENERIC = new ReferenceBinding () { /* used for lub computation */
100104 { this .id = TypeIds .T_undefined ; }
101105 @ Override
@@ -2322,93 +2326,58 @@ public void detectWrapperResource() {
23222326 }
23232327}
23242328
2325- protected MethodBinding [] getInterfaceAbstractContracts (Scope scope , boolean replaceWildcards , boolean filterDefaultMethods ) throws InvalidBindingException {
2326-
2327- if (!isInterface () || !isValidBinding ()) {
2328- throw new InvalidBindingException ("Not a functional interface" ); //$NON-NLS-1$
2329- }
2330-
2331- MethodBinding [] methods = methods ();
2332- MethodBinding [] contracts = new MethodBinding [0 ];
2333- int contractsCount = 0 ;
2334- int contractsLength = 0 ;
2335-
2336- ReferenceBinding [] superInterfaces = superInterfaces ();
2337- for (ReferenceBinding superInterface : superInterfaces ) {
2338- // filterDefaultMethods=false => keep default methods needed to filter out any abstract methods they may override:
2339- MethodBinding [] superInterfaceContracts = superInterface .getInterfaceAbstractContracts (scope , replaceWildcards , false );
2340- final int superInterfaceContractsLength = superInterfaceContracts == null ? 0 : superInterfaceContracts .length ;
2341- if (superInterfaceContractsLength == 0 ) continue ;
2342- if (contractsLength < contractsCount + superInterfaceContractsLength ) {
2343- System .arraycopy (contracts , 0 , contracts = new MethodBinding [contractsLength = contractsCount + superInterfaceContractsLength ], 0 , contractsCount );
2344- }
2345- System .arraycopy (superInterfaceContracts , 0 , contracts , contractsCount , superInterfaceContractsLength );
2346- contractsCount += superInterfaceContractsLength ;
2347- }
2329+ private MethodBinding [] getFunctionalInterfaceAbstractContracts (Scope scope , boolean replaceWildcards ) throws DysfunctionalInterfaceException {
23482330
23492331 LookupEnvironment environment = scope .environment ();
2350- for (int i = 0 , length = methods == null ? 0 : methods .length ; i < length ; i ++) {
2351- final MethodBinding method = methods [i ];
2352- if (method == null || method .isStatic () || method .redeclaresPublicObjectMethod (scope ) || method .isPrivate ())
2353- continue ;
2354- if (!method .isValidBinding ())
2355- throw new InvalidBindingException ("Not a functional interface" ); //$NON-NLS-1$
2356- for (int j = 0 ; j < contractsCount ;) {
2357- if ( contracts [j ] != null && MethodVerifier .doesMethodOverride (method , contracts [j ], environment )) {
2358- contractsCount --;
2359- // abstract method from super type overridden by present interface ==> contracts[j] = null;
2360- if (j < contractsCount ) {
2361- System .arraycopy (contracts , j +1 , contracts , j , contractsCount - j );
2362- continue ;
2363- }
2364- }
2365- j ++;
2366- }
2367- if (filterDefaultMethods && method .isDefaultMethod ())
2368- continue ; // skip default method itself
2369- if (contractsCount == contractsLength ) {
2370- System .arraycopy (contracts , 0 , contracts = new MethodBinding [contractsLength += 16 ], 0 , contractsCount );
2371- }
2372- if (environment .globalOptions .isAnnotationBasedNullAnalysisEnabled ) {
2373- ImplicitNullAnnotationVerifier .ensureNullnessIsKnown (method , scope );
2374- }
2375- contracts [contractsCount ++] = method ;
2376- }
2377- // check mutual overriding of inherited methods (i.e., not from current type):
2378- for (int i = 0 ; i < contractsCount ; i ++) {
2379- MethodBinding contractI = contracts [i ];
2380- if (TypeBinding .equalsEquals (contractI .declaringClass , this ))
2381- continue ;
2382- for (int j = 0 ; j < contractsCount ; j ++) {
2383- MethodBinding contractJ = contracts [j ];
2384- if (i == j || TypeBinding .equalsEquals (contractJ .declaringClass , this ))
2385- continue ;
2386- if (contractI == contractJ || MethodVerifier .doesMethodOverride (contractI , contractJ , environment )) {
2387- contractsCount --;
2388- // abstract method from one super type overridden by other super interface ==> contracts[j] = null;
2389- if (j < contractsCount ) {
2390- System .arraycopy (contracts , j +1 , contracts , j , contractsCount - j );
2391- }
2392- j --;
2393- if (j < i )
2394- i --;
2395- continue ;
2332+ boolean isAnnotationBasedNullAnalysisEnabled = environment .globalOptions .isAnnotationBasedNullAnalysisEnabled ;
2333+ MethodBinding samCandidate = null ;
2334+
2335+ MethodBinding [] methods = collateFunctionalInterfaceContracts (scope , replaceWildcards , new HashSet <>())
2336+ .sorted ((m1 , m2 ) -> CharOperation .compareTo (m1 .selector , m2 .selector ))
2337+ .toArray (MethodBinding []::new );
2338+
2339+ for (int i = 0 , length = methods .length ; i < length ; i ++) {
2340+ MethodBinding method = methods [i ];
2341+ for (int j = i + 1 ; j < length ; j ++) {
2342+ MethodBinding otherMethod = methods [j ];
2343+ if (method .selector .length != otherMethod .selector .length || !CharOperation .equals (method .selector , otherMethod .selector ))
2344+ break ; // Skip comparing apple to oranges (input sorted by selector, so no viable overrides past this point)
2345+ if (MethodVerifier .doesMethodOverride (otherMethod , method , environment )) {
2346+ methods [i ] = method = null ;
2347+ break ; // Skip comparing rotten apple with remaining lot: is overridden already, no use checking if is overridden also by another
23962348 }
23972349 }
2398- if (filterDefaultMethods && contractI . isDefaultMethod ()) {
2399- contractsCount --;
2400- // remove default method after it has eliminated any matching abstract methods from contracts
2401- if (i < contractsCount ) {
2402- System . arraycopy ( contracts , i + 1 , contracts , i , contractsCount - i ) ;
2403- }
2404- i -- ;
2350+ if (method != null && method . isAbstract ()) {
2351+ if ( samCandidate == null )
2352+ samCandidate = method ;
2353+ else if (! CharOperation . equals ( samCandidate . selector , method . selector ) || samCandidate . parameters . length != method . parameters . length )
2354+ throw DYSFUNCTIONAL_INTERFACE_EXCEPTION ;
2355+ if ( isAnnotationBasedNullAnalysisEnabled )
2356+ ImplicitNullAnnotationVerifier . ensureNullnessIsKnown ( method , scope ) ;
24052357 }
24062358 }
2407- if (contractsCount < contractsLength ) {
2408- System .arraycopy (contracts , 0 , contracts = new MethodBinding [contractsCount ], 0 , contractsCount );
2409- }
2410- return contracts ;
2359+ return Arrays .stream (methods ).filter (m -> m != null && m .isAbstract ()).toArray (MethodBinding []::new );
24112360}
2361+
2362+ protected Stream <MethodBinding > collateFunctionalInterfaceContracts (Scope scope , boolean replaceWildcards , Set <ReferenceBinding > visitedInterfaces ) throws DysfunctionalInterfaceException {
2363+
2364+ if (!isInterface () || !isValidBinding ())
2365+ throw DYSFUNCTIONAL_INTERFACE_EXCEPTION ;
2366+
2367+ Predicate <MethodBinding > isContractual = m -> { // select valid public abstract || default methods
2368+ if (m == null || m .isStatic () || m .redeclaresPublicObjectMethod (scope ) || m .isPrivate ())
2369+ return false ;
2370+ if (!m .isValidBinding ())
2371+ throw DYSFUNCTIONAL_INTERFACE_EXCEPTION ;
2372+ return true ;
2373+ };
2374+
2375+ return Stream .concat ( //
2376+ Arrays .stream (superInterfaces ()).filter (iface ->visitedInterfaces .add (iface )).flatMap (superInterface -> superInterface .collateFunctionalInterfaceContracts (scope , replaceWildcards , visitedInterfaces )), //
2377+ Arrays .stream (methods ()).filter (isContractual )
2378+ );
2379+ }
2380+
24122381@ Override
24132382public MethodBinding getSingleAbstractMethod (Scope scope , boolean replaceWildcards ) {
24142383
@@ -2426,7 +2395,7 @@ public MethodBinding getSingleAbstractMethod(Scope scope, boolean replaceWildcar
24262395 scope .compilationUnitScope ().recordQualifiedReference (this .compoundName );
24272396 MethodBinding [] methods = null ;
24282397 try {
2429- methods = getInterfaceAbstractContracts (scope , replaceWildcards , true );
2398+ methods = getFunctionalInterfaceAbstractContracts (scope , replaceWildcards );
24302399 if (methods == null || methods .length == 0 )
24312400 return this .singleAbstractMethod [index ] = samProblemBinding ;
24322401 int contractParameterLength = 0 ;
@@ -2442,7 +2411,7 @@ public MethodBinding getSingleAbstractMethod(Scope scope, boolean replaceWildcar
24422411 return this .singleAbstractMethod [index ] = samProblemBinding ;
24432412 }
24442413 }
2445- } catch (InvalidBindingException e ) {
2414+ } catch (DysfunctionalInterfaceException e ) {
24462415 return this .singleAbstractMethod [index ] = samProblemBinding ;
24472416 }
24482417 if (methods .length == 1 )
@@ -2677,10 +2646,9 @@ public boolean isDisjointFrom(ReferenceBinding that) {
26772646 }
26782647 }
26792648}
2680- static class InvalidBindingException extends Exception {
2649+ static class DysfunctionalInterfaceException extends RuntimeException {
26812650 private static final long serialVersionUID = 1L ;
2682-
2683- InvalidBindingException (String message ) {
2651+ DysfunctionalInterfaceException (String message ) {
26842652 super (message );
26852653 }
26862654}
0 commit comments