Change Signature: Reduce JetChangeSignatureUsageProcessor state to prevent

memory leaks. JetMethodDescriptor is stored in UsageInfo list
This commit is contained in:
Alexey Sedunov
2015-07-24 18:37:13 +03:00
parent 4f12dcc069
commit f4ebd895ca
2 changed files with 76 additions and 38 deletions
@@ -49,7 +49,7 @@ import java.util.ArrayList
import java.util.HashMap import java.util.HashMap
import java.util.LinkedHashSet import java.util.LinkedHashSet
public class JetChangeInfo( public open class JetChangeInfo(
val methodDescriptor: JetMethodDescriptor, val methodDescriptor: JetMethodDescriptor,
private var name: String = methodDescriptor.getName(), private var name: String = methodDescriptor.getName(),
val newReturnType: JetType? = methodDescriptor.baseDescriptor.getReturnType(), val newReturnType: JetType? = methodDescriptor.baseDescriptor.getReturnType(),
@@ -83,12 +83,48 @@ import org.jetbrains.kotlin.types.TypeUtils;
import java.util.*; import java.util.*;
public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsageProcessor { public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsageProcessor {
// This is special 'PsiElement' whose purpose is to wrap JetMethodDescriptor so that it can be kept in the usage list
private static class OriginalJavaMethodDescriptorWrapper extends UsageInfo {
JetMethodDescriptor originalJavaMethodDescriptor;
public OriginalJavaMethodDescriptorWrapper(@NotNull PsiElement element) {
super(element);
}
}
private static class DummyJetChangeInfo extends JetChangeInfo {
public DummyJetChangeInfo(
@NotNull PsiElement method,
@NotNull JetMethodDescriptor methodDescriptor
) {
super(methodDescriptor,
"",
null,
"",
Visibilities.INTERNAL,
Collections.<JetParameterInfo>emptyList(),
null,
method,
Collections.<PsiElement>emptyList());
}
}
@Nullable
private static OriginalJavaMethodDescriptorWrapper getOriginalJavaMethodDescriptorWrapper(@NotNull UsageInfo[] usages) {
return KotlinPackage.firstOrNull(KotlinPackage.filterIsInstance(usages, OriginalJavaMethodDescriptorWrapper.class));
}
// It's here to prevent O(usage_count^2) performance
private boolean initializedOriginalDescriptor;
@Override @Override
public UsageInfo[] findUsages(ChangeInfo info) { public UsageInfo[] findUsages(ChangeInfo info) {
originalJavaMethodDescriptor = null; initializedOriginalDescriptor = false;
Set<UsageInfo> result = new HashSet<UsageInfo>(); Set<UsageInfo> result = new HashSet<UsageInfo>();
result.add(new OriginalJavaMethodDescriptorWrapper(info.getMethod()));
if (info instanceof JetChangeInfo) { if (info instanceof JetChangeInfo) {
findAllMethodUsages((JetChangeInfo) info, result); findAllMethodUsages((JetChangeInfo) info, result);
} }
@@ -813,8 +849,6 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
}); });
} }
private JetMethodDescriptor originalJavaMethodDescriptor;
private static boolean isJavaMethodUsage(UsageInfo usageInfo) { private static boolean isJavaMethodUsage(UsageInfo usageInfo) {
// MoveRenameUsageInfo corresponds to non-Java usage of Java method // MoveRenameUsageInfo corresponds to non-Java usage of Java method
return usageInfo instanceof JavaMethodDeferredKotlinUsage || usageInfo instanceof MoveRenameUsageInfo; return usageInfo instanceof JavaMethodDeferredKotlinUsage || usageInfo instanceof MoveRenameUsageInfo;
@@ -836,6 +870,11 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
refTarget != null && ChangeSignaturePackage.isCaller(refTarget, allUsages)); refTarget != null && ChangeSignaturePackage.isCaller(refTarget, allUsages));
} }
private static boolean canCreateReplacementUsage(UsageInfo originalUsageInfo) {
if (originalUsageInfo instanceof JavaMethodDeferredKotlinUsage) return true;
return PsiTreeUtil.getParentOfType(originalUsageInfo.getElement(), JetCallElement.class) != null;
}
private static class NullabilityPropagator { private static class NullabilityPropagator {
private final NullableNotNullManager nullManager; private final NullableNotNullManager nullManager;
private final JavaPsiFacade javaPsiFacade; private final JavaPsiFacade javaPsiFacade;
@@ -958,55 +997,54 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
} }
if (beforeMethodChange) { if (beforeMethodChange) {
boolean startedFromJava = method instanceof PsiMethod; if (!(method instanceof PsiMethod) || initializedOriginalDescriptor) return true;
if (startedFromJava && originalJavaMethodDescriptor == null) {
FunctionDescriptor methodDescriptor = ResolvePackage.getJavaMethodDescriptor((PsiMethod) method);
assert methodDescriptor != null;
originalJavaMethodDescriptor =
new JetChangeSignatureData(methodDescriptor, method, Collections.singletonList(methodDescriptor));;
// This change info is used as a placeholder before primary method update OriginalJavaMethodDescriptorWrapper descriptorWrapper = getOriginalJavaMethodDescriptorWrapper(usages);
// It gets replaced with real change info afterwards if (descriptorWrapper == null || descriptorWrapper.originalJavaMethodDescriptor != null) return true;
JetChangeInfo dummyChangeInfo =
new JetChangeInfo(originalJavaMethodDescriptor,
"",
null,
"",
Visibilities.INTERNAL,
Collections.<JetParameterInfo>emptyList(),
null,
changeInfo.getMethod(),
Collections.<PsiElement>emptyList());
for (int i = 0; i < usages.length; i++) {
UsageInfo oldUsageInfo = usages[i];
if (!isJavaMethodUsage(oldUsageInfo)) continue;
UsageInfo newUsageInfo = createReplacementUsage(oldUsageInfo, dummyChangeInfo, usages); FunctionDescriptor methodDescriptor = ResolvePackage.getJavaMethodDescriptor((PsiMethod) method);
if (newUsageInfo != null) { assert methodDescriptor != null;
usages[i] = newUsageInfo; descriptorWrapper.originalJavaMethodDescriptor =
} new JetChangeSignatureData(methodDescriptor, method, Collections.singletonList(methodDescriptor));
// This change info is used as a placeholder before primary method update
// It gets replaced with real change info afterwards
JetChangeInfo dummyChangeInfo = new DummyJetChangeInfo(changeInfo.getMethod(), descriptorWrapper.originalJavaMethodDescriptor);
for (int i = 0; i < usages.length; i++) {
UsageInfo oldUsageInfo = usages[i];
if (!isJavaMethodUsage(oldUsageInfo)) continue;
UsageInfo newUsageInfo = createReplacementUsage(oldUsageInfo, dummyChangeInfo, usages);
if (newUsageInfo != null) {
usages[i] = newUsageInfo;
} }
} }
initializedOriginalDescriptor = true;
return true; return true;
} }
PsiElement element = usageInfo.getElement(); PsiElement element = usageInfo.getElement();
if (element == null) return false; if (element == null) return false;
if (originalJavaMethodDescriptor != null) { if (usageInfo instanceof JavaMethodKotlinUsageWithDelegate) {
JetChangeInfo javaMethodChangeInfo = ChangeSignaturePackage.toJetChangeInfo(changeInfo, originalJavaMethodDescriptor); JavaMethodKotlinUsageWithDelegate usageWithDelegate = (JavaMethodKotlinUsageWithDelegate) usageInfo;
originalJavaMethodDescriptor = null; // Do not call getOriginalJavaMethodDescriptorWrapper() on each usage to avoid O(usage_count^2) performance
if (((JavaMethodKotlinUsageWithDelegate) usageInfo).getJavaMethodChangeInfo() instanceof DummyJetChangeInfo) {
OriginalJavaMethodDescriptorWrapper descriptorWrapper = getOriginalJavaMethodDescriptorWrapper(usages);
JetMethodDescriptor methodDescriptor = descriptorWrapper != null ? descriptorWrapper.originalJavaMethodDescriptor : null;
if (methodDescriptor == null) return true;
for (UsageInfo info : usages) { JetChangeInfo javaMethodChangeInfo = ChangeSignaturePackage.toJetChangeInfo(changeInfo, methodDescriptor);
if (info instanceof JavaMethodKotlinUsageWithDelegate) { for (UsageInfo info : usages) {
((JavaMethodKotlinUsageWithDelegate) info).setJavaMethodChangeInfo(javaMethodChangeInfo); if (info instanceof JavaMethodKotlinUsageWithDelegate) {
((JavaMethodKotlinUsageWithDelegate) info).setJavaMethodChangeInfo(javaMethodChangeInfo);
}
} }
} }
}
if (usageInfo instanceof JavaMethodKotlinUsageWithDelegate) { return usageWithDelegate.processUsage(usages);
return ((JavaMethodKotlinUsageWithDelegate) usageInfo).processUsage(usages);
} }
if (usageInfo instanceof MoveRenameUsageInfo && isJavaMethodUsage) { if (usageInfo instanceof MoveRenameUsageInfo && isJavaMethodUsage) {