DfaExpressionFactory: do not eagerly resolve qualifier for special field

Fixes performance degradation on long method chains
SpecialField API polishing
This commit is contained in:
Tagir Valeev
2018-03-30 13:30:44 +07:00
parent ea96b276bb
commit 80d114f1ee
5 changed files with 25 additions and 28 deletions
@@ -1123,13 +1123,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (constValue != null) {
return constValue;
}
DfaVariableValue qualifier = var.getQualifier();
if (qualifier != null) {
DfaValue value = SpecialField.tryCreateValue(qualifier, var.getPsiVariable());
if (value != null) {
return value;
}
}
return var;
}
@@ -7,7 +7,10 @@ import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.MethodUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
@@ -22,10 +25,10 @@ import static com.intellij.codeInspection.dataFlow.MethodContract.ValueConstrain
public enum SpecialField implements DfaVariableSource {
ARRAY_LENGTH(null, "length", true, LongRangeSet.indexRange()) {
@Override
public boolean isMyAccessor(PsiModifierListOwner accessor) {
return accessor instanceof PsiField && "length".equals(((PsiField)accessor).getName()) &&
boolean isMyAccessor(PsiMember accessor) {
return accessor instanceof PsiField && "length".equals(accessor.getName()) &&
JavaPsiFacade.getElementFactory(accessor.getProject()).getArrayClass(PsiUtil.getLanguageLevel(accessor)) ==
((PsiField)accessor).getContainingClass();
accessor.getContainingClass();
}
@Override
@@ -98,21 +101,20 @@ public enum SpecialField implements DfaVariableSource {
* @param accessor accessor to test to test
* @return true if supplied accessor can be used to read this special field
*/
public boolean isMyAccessor(PsiModifierListOwner accessor) {
boolean isMyAccessor(PsiMember accessor) {
return accessor instanceof PsiMethod && MethodUtils.methodMatches((PsiMethod)accessor, myClassName, null, myMethodName);
}
public static DfaValue tryCreateValue(DfaValue qualifier, PsiElement element) {
if (qualifier == null) return null;
DfaValueFactory factory = qualifier.getFactory();
if (factory == null) return null;
if (!(element instanceof PsiVariable) && !(element instanceof PsiMethod)) return null;
for (SpecialField field : values()) {
if (field.isMyAccessor((PsiModifierListOwner)element)) {
return field.createValue(factory, qualifier);
}
}
return null;
/**
* Finds a special field which corresponds to given accessor (method or field)
* @param accessor accessor to find a special field for
* @return found special field or null if accessor cannot be used to access a special field
*/
@Contract("null -> null")
@Nullable
public static SpecialField findSpecialField(PsiElement accessor) {
if (!(accessor instanceof PsiMember)) return null;
return StreamEx.of(values()).findFirst(sf -> sf.isMyAccessor((PsiMember)accessor)).orElse(null);
}
/**
@@ -503,9 +503,9 @@ public class StandardInstructionVisitor extends InstructionVisitor {
if (methodType == MethodCallInstruction.MethodType.METHOD_REFERENCE_CALL && qualifierValue instanceof DfaVariableValue) {
PsiMethod method = instruction.getTargetMethod();
DfaValue value = SpecialField.tryCreateValue(qualifierValue, method);
if (value != null) {
return value;
SpecialField field = SpecialField.findSpecialField(method);
if (field != null) {
return field.createValue(factory, qualifierValue);
}
DfaVariableSource source = DfaExpressionFactory.getAccessedVariableOrGetter(method);
if (source != null) {
@@ -199,8 +199,7 @@ public class MethodCallInstruction extends Instruction {
private boolean isPureCall() {
if (myTargetMethod == null) return false;
return ControlFlowAnalyzer.isPure(myTargetMethod) ||
Arrays.stream(SpecialField.values()).anyMatch(sf -> sf.isMyAccessor(myTargetMethod));
return ControlFlowAnalyzer.isPure(myTargetMethod) || SpecialField.findSpecialField(myTargetMethod) != null;
}
@Nullable
@@ -197,8 +197,11 @@ public class DfaExpressionFactory {
private DfaValue createFromSpecialField(PsiReferenceExpression refExpr) {
PsiElement target = refExpr.resolve();
if (!(target instanceof PsiModifierListOwner)) return null;
SpecialField sf = SpecialField.findSpecialField(target);
if (sf == null) return null;
DfaVariableValue qualifier = getQualifierVariable(refExpr.getQualifierExpression());
return SpecialField.tryCreateValue(qualifier, target);
if (qualifier == null) return null;
return sf.createValue(myFactory, qualifier);
}
@Contract("null -> null")