Skip to content

Commit 2bfece2

Browse files
committed
Improve Performance of getInterfaceAbstractContracts for interfaces with large inheritance hierarchies and Method counts
1 parent 727f3b4 commit 2bfece2

1 file changed

Lines changed: 57 additions & 67 deletions

File tree

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java

Lines changed: 57 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,7 @@
5252
*******************************************************************************/
5353
package org.eclipse.jdt.internal.compiler.lookup;
5454

55-
import java.util.ArrayList;
56-
import java.util.Arrays;
57-
import java.util.Comparator;
58-
import java.util.HashMap;
59-
import java.util.HashSet;
60-
import java.util.List;
61-
import java.util.Map;
62-
import java.util.Set;
55+
import java.util.*;
6356
import org.eclipse.jdt.core.compiler.CharOperation;
6457
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
6558
import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
@@ -2332,91 +2325,88 @@ public void detectWrapperResource() {
23322325
}
23332326

23342327
protected MethodBinding [] getInterfaceAbstractContracts(Scope scope, boolean replaceWildcards, boolean filterDefaultMethods) throws InvalidBindingException {
2335-
23362328
if (!isInterface() || !isValidBinding()) {
23372329
throw new InvalidBindingException("Not a functional interface"); //$NON-NLS-1$
23382330
}
23392331

2340-
MethodBinding [] methods = methods();
2341-
MethodBinding [] contracts = new MethodBinding[0];
2342-
int contractsCount = 0;
2343-
int contractsLength = 0;
2332+
MethodBinding [] methods = methods(); // All methods defined by THIS interface
2333+
Map<String, List<MethodBinding>> contractsBySelector = new LinkedHashMap<>();
23442334

2335+
// Fill List of contracts by name with all methods of super interfaces
23452336
ReferenceBinding [] superInterfaces = superInterfaces();
23462337
for (ReferenceBinding superInterface : superInterfaces) {
23472338
// filterDefaultMethods=false => keep default methods needed to filter out any abstract methods they may override:
23482339
MethodBinding [] superInterfaceContracts = superInterface.getInterfaceAbstractContracts(scope, replaceWildcards, false);
23492340
final int superInterfaceContractsLength = superInterfaceContracts == null ? 0 : superInterfaceContracts.length;
23502341
if (superInterfaceContractsLength == 0) continue;
2351-
if (contractsLength < contractsCount + superInterfaceContractsLength) {
2352-
System.arraycopy(contracts, 0, contracts = new MethodBinding[contractsLength = contractsCount + superInterfaceContractsLength], 0, contractsCount);
2342+
for (MethodBinding superInterfaceContract : superInterfaceContracts) {
2343+
if (superInterfaceContract == null) {
2344+
continue;
2345+
}
2346+
contractsBySelector.computeIfAbsent(String.valueOf(superInterfaceContract.selector), key -> new ArrayList<>()).add(superInterfaceContract);
23532347
}
2354-
System.arraycopy(superInterfaceContracts, 0, contracts, contractsCount, superInterfaceContractsLength);
2355-
contractsCount += superInterfaceContractsLength;
23562348
}
23572349

23582350
LookupEnvironment environment = scope.environment();
2359-
for (int i = 0, length = methods == null ? 0 : methods.length; i < length; i++) {
2360-
final MethodBinding method = methods[i];
2361-
if (method == null || method.isStatic() || method.redeclaresPublicObjectMethod(scope) || method.isPrivate())
2362-
continue;
2363-
if (!method.isValidBinding())
2364-
throw new InvalidBindingException("Not a functional interface"); //$NON-NLS-1$
2365-
for (int j = 0; j < contractsCount;) {
2366-
if ( contracts[j] != null && MethodVerifier.doesMethodOverride(method, contracts[j], environment)) {
2367-
contractsCount--;
2368-
// abstract method from super type overridden by present interface ==> contracts[j] = null;
2369-
if (j < contractsCount) {
2370-
System.arraycopy(contracts, j+1, contracts, j, contractsCount - j);
2371-
continue;
2351+
if (methods != null && methods.length > 0) {
2352+
for (final MethodBinding method : methods) {
2353+
if (method == null || method.isStatic() || method.redeclaresPublicObjectMethod(scope) || method.isPrivate())
2354+
continue;
2355+
if (!method.isValidBinding())
2356+
throw new InvalidBindingException("Not a functional interface"); //$NON-NLS-1$
2357+
List<MethodBinding> superInterfaceContractsOfSelector = contractsBySelector.computeIfAbsent(String.valueOf(method.selector), key -> new ArrayList<>());
2358+
Iterator<MethodBinding> iterator = superInterfaceContractsOfSelector.iterator();
2359+
2360+
// Does 'method' override any super interface method?
2361+
while (iterator.hasNext()) {
2362+
MethodBinding superInterfaceContract = iterator.next();
2363+
2364+
if (MethodVerifier.doesMethodOverride(method, superInterfaceContract, environment)) {
2365+
iterator.remove();
23722366
}
23732367
}
2374-
j++;
2375-
}
2376-
if (filterDefaultMethods && method.isDefaultMethod())
2377-
continue; // skip default method itself
2378-
if (contractsCount == contractsLength) {
2379-
System.arraycopy(contracts, 0, contracts = new MethodBinding[contractsLength += 16], 0, contractsCount);
2380-
}
2381-
if(environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
2382-
ImplicitNullAnnotationVerifier.ensureNullnessIsKnown(method, scope);
2368+
if (filterDefaultMethods && method.isDefaultMethod())
2369+
continue; // skip default method itself
2370+
if(environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
2371+
ImplicitNullAnnotationVerifier.ensureNullnessIsKnown(method, scope);
2372+
}
2373+
superInterfaceContractsOfSelector.add(method);
23832374
}
2384-
contracts[contractsCount++] = method;
23852375
}
2376+
23862377
// check mutual overriding of inherited methods (i.e., not from current type):
2387-
for (int i = 0; i < contractsCount; i++) {
2388-
MethodBinding contractI = contracts[i];
2389-
if (TypeBinding.equalsEquals(contractI.declaringClass, this))
2390-
continue;
2391-
for (int j = 0; j < contractsCount; j++) {
2392-
MethodBinding contractJ = contracts[j];
2393-
if (i == j || TypeBinding.equalsEquals(contractJ.declaringClass, this))
2378+
for (List<MethodBinding> contractList : contractsBySelector.values()) {
2379+
for (int i = 0; i < contractList.size(); ++i) {
2380+
MethodBinding contractI = contractList.get(i);
2381+
2382+
if (TypeBinding.equalsEquals(contractI.declaringClass, this)) {
23942383
continue;
2395-
if (contractI == contractJ || MethodVerifier.doesMethodOverride(contractI, contractJ, environment)) {
2396-
contractsCount--;
2397-
// abstract method from one super type overridden by other super interface ==> contracts[j] = null;
2398-
if (j < contractsCount) {
2399-
System.arraycopy(contracts, j+1, contracts, j, contractsCount - j);
2384+
}
2385+
2386+
for (int j = 0; j < contractList.size(); ++j) {
2387+
MethodBinding contractJ = contractList.get(j);
2388+
2389+
if (i == j || TypeBinding.equalsEquals(contractJ.declaringClass, this)) {
2390+
continue;
2391+
}
2392+
2393+
if (contractI == contractJ || MethodVerifier.doesMethodOverride(contractI, contractJ, environment)) {
2394+
contractList.remove(j);
2395+
2396+
--j;
2397+
if (j < i) {
2398+
--i;
2399+
}
24002400
}
2401-
j--;
2402-
if (j < i)
2403-
i--;
2404-
continue;
24052401
}
2406-
}
2407-
if (filterDefaultMethods && contractI.isDefaultMethod()) {
2408-
contractsCount--;
2409-
// remove default method after it has eliminated any matching abstract methods from contracts
2410-
if (i < contractsCount) {
2411-
System.arraycopy(contracts, i+1, contracts, i, contractsCount - i);
2402+
2403+
if (filterDefaultMethods && contractI.isDefaultMethod()) {
2404+
contractList.remove(i);
2405+
--i;
24122406
}
2413-
i--;
24142407
}
24152408
}
2416-
if (contractsCount < contractsLength) {
2417-
System.arraycopy(contracts, 0, contracts = new MethodBinding[contractsCount], 0, contractsCount);
2418-
}
2419-
return contracts;
2409+
return contractsBySelector.values().stream().flatMap(Collection::stream).toArray(MethodBinding[]::new);
24202410
}
24212411
@Override
24222412
public MethodBinding getSingleAbstractMethod(Scope scope, boolean replaceWildcards) {

0 commit comments

Comments
 (0)