Optimized obtaining super methods.

This commit is contained in:
Evgeny Gerashchenko
2013-06-03 18:20:36 +04:00
parent 343a28ac2d
commit c51a70b3b8
@@ -17,9 +17,11 @@
package org.jetbrains.jet.lang.resolve.java.kotlinSignature;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.HierarchicalMethodSignature;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiSubstitutorImpl;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -38,7 +40,11 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.intellij.psi.util.TypeConversionUtil.erasure;
// This class contains heuristics for processing corner cases in propagation
class PropagationHeuristics {
@@ -122,8 +128,7 @@ class PropagationHeuristics {
@NotNull
static List<PsiMethod> getSuperMethods(@NotNull PsiMethod method) {
List<PsiMethod> superMethods = Lists.newArrayList();
for (HierarchicalMethodSignature superSignature : method.getHierarchicalMethodSignature().getSuperSignatures()) {
PsiMethod superMethod = superSignature.getMethod();
for (PsiMethod superMethod : new SuperMethodCollector(method).collect()) {
CallableMemberDescriptor.Kind kindFromFlags =
DescriptorKindUtils.flagsToKind(new PsiMethodWrapper(superMethod).getJetMethodAnnotation().kind());
if (kindFromFlags == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
@@ -141,4 +146,110 @@ class PropagationHeuristics {
private PropagationHeuristics() {
}
private static class SuperMethodCollector {
private final PsiMethod initialMethod;
private final String initialMethodName;
private final List<PsiType> initialParametersErasure;
private final List<PsiClass> visitedSuperclasses = Lists.newArrayList();
private final List<PsiMethod> collectedMethods = Lists.newArrayList();
private SuperMethodCollector(@NotNull PsiMethod initialMethod) {
this.initialMethod = initialMethod;
initialMethodName = initialMethod.getName();
PsiParameterList parameterList = initialMethod.getParameterList();
initialParametersErasure = Lists.newArrayListWithExpectedSize(parameterList.getParametersCount());
for (PsiParameter parameter : parameterList.getParameters()) {
initialParametersErasure.add(erasureNoEllipsis(parameter.getType()));
}
}
public List<PsiMethod> collect() {
if (!canHaveSuperMethod(initialMethod)) {
return Collections.emptyList();
}
PsiClass containingClass = initialMethod.getContainingClass();
assert containingClass != null : " containing class is null for " + initialMethod;
for (PsiClassType superType : containingClass.getSuperTypes()) {
collectFromSupertype(superType);
}
return collectedMethods;
}
private void collectFromSupertype(PsiClassType type) {
PsiClass klass = type.resolve();
if (klass == null) {
return;
}
if (!visitedSuperclasses.add(klass)) {
return;
}
PsiSubstitutor supertypeSubstitutor = getErasedSubstitutor(type);
for (PsiMethod methodFromSuper : klass.getMethods()) {
if (isSubMethodOf(methodFromSuper, supertypeSubstitutor)) {
collectedMethods.add(methodFromSuper);
return;
}
}
for (PsiType superType : type.getSuperTypes()) {
assert superType instanceof PsiClassType : "supertype is not a PsiClassType for " + type + ": " + superType;
collectFromSupertype((PsiClassType) superType);
}
}
private boolean isSubMethodOf(@NotNull PsiMethod methodFromSuper, @NotNull PsiSubstitutor supertypeSubstitutor) {
if (!methodFromSuper.getName().equals(initialMethodName)) {
return false;
}
PsiParameterList fromSuperParameterList = methodFromSuper.getParameterList();
if (fromSuperParameterList.getParametersCount() != initialParametersErasure.size()) {
return false;
}
for (int i = 0; i < initialParametersErasure.size(); i++) {
PsiType originalType = initialParametersErasure.get(i);
PsiType typeFromSuper = fromSuperParameterList.getParameters()[i].getType();
PsiType typeFromSuperErased = erasureNoEllipsis(supertypeSubstitutor.substitute(typeFromSuper));
if (!Comparing.equal(originalType, typeFromSuperErased)) {
return false;
}
}
return true;
}
private static PsiType erasureNoEllipsis(PsiType type) {
if (type instanceof PsiEllipsisType) {
return erasureNoEllipsis(((PsiEllipsisType) type).toArrayType());
}
return erasure(type);
}
private static PsiSubstitutor getErasedSubstitutor(PsiClassType type) {
Map<PsiTypeParameter, PsiType> unerasedMap = type.resolveGenerics().getSubstitutor().getSubstitutionMap();
Map<PsiTypeParameter, PsiType> erasedMap = Maps.newHashMapWithExpectedSize(unerasedMap.size());
for (Map.Entry<PsiTypeParameter, PsiType> entry : unerasedMap.entrySet()) {
erasedMap.put(entry.getKey(), erasure(entry.getValue()));
}
return PsiSubstitutorImpl.createSubstitutor(erasedMap);
}
private static boolean canHaveSuperMethod(PsiMethod method) {
if (method.isConstructor()) return false;
if (method.hasModifierProperty(PsiModifier.STATIC)) return false;
if (method.hasModifierProperty(PsiModifier.PRIVATE)) return false;
PsiClass containingClass = method.getContainingClass();
return containingClass != null && !CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName());
}
}
}