mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
DFA: initialize small primitive arrays; unroll small counting loops
This commit is contained in:
@@ -210,7 +210,7 @@ public class CFGBuilder {
|
||||
* @return this builder
|
||||
*/
|
||||
private CFGBuilder compare(IElementType relation) {
|
||||
myAnalyzer.addInstruction(new BinopInstruction(relation, null, myAnalyzer.getContext().getProject()));
|
||||
myAnalyzer.addInstruction(new BinopInstruction(relation, null, PsiType.BOOLEAN));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -556,7 +556,7 @@ public class CFGBuilder {
|
||||
if (qualifier == null) return false;
|
||||
PsiType type = qualifier.getOperand().getType();
|
||||
push(getFactory().createTypeValue(type, Nullness.NOT_NULL));
|
||||
myAnalyzer.addInstruction(new InstanceofInstruction(methodRef, methodRef.getProject(), null, type));
|
||||
myAnalyzer.addInstruction(new InstanceofInstruction(methodRef, null, type));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+72
-25
@@ -47,6 +47,7 @@ import static com.intellij.psi.CommonClassNames.*;
|
||||
public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer");
|
||||
public static final String ORG_JETBRAINS_ANNOTATIONS_CONTRACT = Contract.class.getName();
|
||||
private static final int MAX_UNROLL_SIZE = 3;
|
||||
private final PsiElement myCodeFragment;
|
||||
private final boolean myIgnoreAssertions;
|
||||
private final boolean myInlining;
|
||||
@@ -202,7 +203,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
lExpr.accept(this);
|
||||
addInstruction(new DupInstruction());
|
||||
rExpr.accept(this);
|
||||
addInstruction(new BinopInstruction(JavaTokenType.PLUS, null, myProject));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.PLUS, null, type));
|
||||
}
|
||||
else if (isAssignmentDivision(op) && type != null && PsiType.LONG.isAssignableFrom(type)) {
|
||||
lExpr.accept(this);
|
||||
@@ -228,7 +229,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
generateBoxingUnboxingInstructionFor(lExpr,exprType);
|
||||
rExpr.accept(this);
|
||||
generateBoxingUnboxingInstructionFor(rExpr, exprType);
|
||||
addInstruction(new BinopInstruction(null, null, myProject));
|
||||
addInstruction(new BinopInstruction(null, null, exprType));
|
||||
}
|
||||
|
||||
@Override public void visitAssertStatement(PsiAssertStatement statement) {
|
||||
@@ -483,7 +484,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
if (length != null) {
|
||||
addInstruction(new PushInstruction(length.createValue(myFactory, qualifier), null));
|
||||
addInstruction(new PushInstruction(myFactory.getInt(0), null));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, iteratedValue, myProject));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, iteratedValue, PsiType.BOOLEAN));
|
||||
addInstruction(new ConditionalGotoInstruction(loopEndOffset, false, null));
|
||||
hasSizeCheck = true;
|
||||
}
|
||||
@@ -555,13 +556,13 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
body.accept(this);
|
||||
}
|
||||
|
||||
PsiStatement update = statement.getUpdate();
|
||||
if (update != null) {
|
||||
update.accept(this);
|
||||
if (!addCountingLoopBound(statement)) {
|
||||
PsiStatement update = statement.getUpdate();
|
||||
if (update != null) {
|
||||
update.accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
addCountingLoopBound(statement);
|
||||
|
||||
ControlFlow.ControlFlowOffset offset = initialization != null
|
||||
? getEndOffset(initialization)
|
||||
: getStartOffset(statement);
|
||||
@@ -575,6 +576,15 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Long asLong(PsiExpression expression) {
|
||||
Object value = ExpressionUtils.computeConstantExpression(expression);
|
||||
if(value instanceof Integer || value instanceof Long) {
|
||||
return ((Number)value).longValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add known-to-be-true condition inside counting loop, effectively converting
|
||||
* {@code for(int i=origin; i<bound; i++)} to
|
||||
@@ -585,18 +595,18 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
*
|
||||
* @param statement counting loop candidate.
|
||||
*/
|
||||
private void addCountingLoopBound(PsiForStatement statement) {
|
||||
private boolean addCountingLoopBound(PsiForStatement statement) {
|
||||
CountingLoop loop = CountingLoop.from(statement);
|
||||
if (loop == null) return;
|
||||
if (loop == null) return false;
|
||||
PsiLocalVariable counter = loop.getCounter();
|
||||
Long start = asLong(loop.getInitializer());
|
||||
Long end = asLong(loop.getBound());
|
||||
if (loop.isIncluding() && !(PsiType.LONG.equals(counter.getType()) && PsiType.INT.equals(loop.getBound().getType()))) {
|
||||
Object bound = ExpressionUtils.computeConstantExpression(loop.getBound());
|
||||
// could be for(int i=0; i<=Integer.MAX_VALUE; i++) which will overflow: conservatively skip this
|
||||
if (!(bound instanceof Number)) return;
|
||||
if (bound.equals(Long.MAX_VALUE) || bound.equals(Integer.MAX_VALUE)) return;
|
||||
if (end == null || end == Long.MAX_VALUE || end == Integer.MAX_VALUE) return false;
|
||||
}
|
||||
PsiExpression initializer = loop.getInitializer();
|
||||
if (!PsiType.INT.equals(initializer.getType()) && !PsiType.LONG.equals(initializer.getType())) return;
|
||||
if (!PsiType.INT.equals(initializer.getType()) && !PsiType.LONG.equals(initializer.getType())) return false;
|
||||
DfaValue origin = null;
|
||||
Object initialValue = ExpressionUtils.computeConstantExpression(initializer);
|
||||
if (initialValue instanceof Number) {
|
||||
@@ -609,11 +619,23 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
origin = myFactory.getVarFactory().createVariableValue(initialVariable, false);
|
||||
}
|
||||
}
|
||||
if (origin == null || VariableAccessUtils.variableIsAssigned(counter, statement.getBody())) return;
|
||||
addInstruction(new PushInstruction(myFactory.getVarFactory().createVariableValue(counter, false), null));
|
||||
if (origin == null) return false;
|
||||
long diff = start == null || end == null ? -1 : end - start;
|
||||
DfaVariableValue loopVar = myFactory.getVarFactory().createVariableValue(counter, false);
|
||||
addInstruction(new PushInstruction(loopVar, null, true));
|
||||
if(diff >= 0 && diff <= MAX_UNROLL_SIZE) {
|
||||
// Unroll small loops
|
||||
addInstruction(new PushInstruction(loopVar, null));
|
||||
addInstruction(new PushInstruction(myFactory.getConstFactory().createFromValue(1, PsiType.INT, null), null));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.PLUS, null, loopVar.getVariableType()));
|
||||
} else {
|
||||
pushUnknown();
|
||||
}
|
||||
addInstruction(new AssignInstruction(null, null));
|
||||
addInstruction(new PushInstruction(origin, null));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.LE, null, myProject));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.LE, null, PsiType.BOOLEAN));
|
||||
addInstruction(new ConditionalGotoInstruction(getEndOffset(statement), false, null));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public void visitIfStatement(PsiIfStatement statement) {
|
||||
@@ -793,7 +815,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
|
||||
addInstruction(new PushInstruction(myFactory.createValue(caseExpression), caseExpression));
|
||||
caseValue.accept(this);
|
||||
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, myProject));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, PsiType.BOOLEAN));
|
||||
}
|
||||
else {
|
||||
pushUnknown();
|
||||
@@ -1211,13 +1233,13 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
right.accept(this);
|
||||
generateBoxingUnboxingInstructionFor(right, type);
|
||||
checkZeroDivisor();
|
||||
addInstruction(new BinopInstruction(expression.getOperationTokenType(), expression.isPhysical() ? expression : null, myProject));
|
||||
addInstruction(new BinopInstruction(expression.getOperationTokenType(), expression.isPhysical() ? expression : null, type));
|
||||
}
|
||||
|
||||
private void checkZeroDivisor() {
|
||||
addInstruction(new DupInstruction());
|
||||
addInstruction(new PushInstruction(myFactory.getConstFactory().createFromValue(0, PsiType.LONG, null), null));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.NE, null, myProject));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.NE, null, PsiType.BOOLEAN));
|
||||
ConditionalGotoInstruction ifNonZero = new ConditionalGotoInstruction(null, false, null);
|
||||
addInstruction(ifNonZero);
|
||||
throwException(JavaPsiFacade.getElementFactory(myProject).createTypeByFQClassName(ArithmeticException.class.getName()), null);
|
||||
@@ -1236,7 +1258,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
PsiType rType = rExpr.getType();
|
||||
|
||||
acceptBinaryRightOperand(op, type, lExpr, lType, rExpr, rType);
|
||||
addInstruction(new BinopInstruction(op, expression.isPhysical() ? expression : null, myProject));
|
||||
addInstruction(new BinopInstruction(op, expression.isPhysical() ? expression : null, type));
|
||||
|
||||
lExpr = rExpr;
|
||||
lType = rType;
|
||||
@@ -1332,7 +1354,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
operand.accept(this);
|
||||
generateBoxingUnboxingInstructionFor(operand, exprType);
|
||||
PsiElement psiAnchor = i == operands.length - 1 && expression.isPhysical() ? expression : null;
|
||||
addInstruction(new BinopInstruction(JavaTokenType.NE, psiAnchor, myProject));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.NE, psiAnchor, exprType));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1475,7 +1497,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
type = ((PsiClassType)type).rawType();
|
||||
}
|
||||
addInstruction(new PushInstruction(myFactory.createTypeValue(type, Nullness.NOT_NULL), null));
|
||||
addInstruction(new InstanceofInstruction(expression, myProject, operand, type));
|
||||
addInstruction(new InstanceofInstruction(expression, operand, type));
|
||||
}
|
||||
else {
|
||||
pushUnknown();
|
||||
@@ -1572,7 +1594,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
// if a contract resulted in 'fail', handle it
|
||||
addInstruction(new DupInstruction());
|
||||
addInstruction(new PushInstruction(myFactory.getConstFactory().getContractFail(), null));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, myProject));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, PsiType.BOOLEAN));
|
||||
ConditionalGotoInstruction ifNotFail = new ConditionalGotoInstruction(null, true, null);
|
||||
addInstruction(ifNotFail);
|
||||
addInstruction(new EmptyStackInstruction());
|
||||
@@ -1683,6 +1705,8 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
addInstruction(new AssignInstruction(null, length));
|
||||
addInstruction(new PopInstruction());
|
||||
// stack: ... var
|
||||
|
||||
initializeSmallArray((PsiArrayType)type, var, dimensions);
|
||||
}
|
||||
else {
|
||||
pushUnknown(); // qualifier
|
||||
@@ -1703,6 +1727,29 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
finishElement(expression);
|
||||
}
|
||||
|
||||
private void initializeSmallArray(PsiArrayType type, DfaVariableValue var, PsiExpression[] dimensions) {
|
||||
if (dimensions.length != 1) return;
|
||||
PsiType componentType = type.getComponentType();
|
||||
// Ignore objects as they may produce false NPE warnings due to non-perfect loop handling
|
||||
if (!(componentType instanceof PsiPrimitiveType)) return;
|
||||
Object val = ExpressionUtils.computeConstantExpression(dimensions[0]);
|
||||
if (val instanceof Integer) {
|
||||
int lengthValue = (Integer)val;
|
||||
if (lengthValue > 0 && lengthValue <= MAX_UNROLL_SIZE) {
|
||||
for (int i = 0; i < lengthValue; i++) {
|
||||
DfaValue value = getFactory().getExpressionFactory().getArrayElementValue(var, i);
|
||||
addInstruction(new PushInstruction(value, null, true));
|
||||
}
|
||||
addInstruction(new PushInstruction(getFactory().getConstFactory().createDefault(componentType), null));
|
||||
for (int i = lengthValue - 1; i >= 0; i--) {
|
||||
DfaValue value = getFactory().getExpressionFactory().getArrayElementValue(var, i);
|
||||
addInstruction(new AssignInstruction(null, value));
|
||||
}
|
||||
addInstruction(new PopInstruction());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiMethod pushConstructorArguments(PsiConstructorCall call) {
|
||||
PsiExpressionList args = call.getArgumentList();
|
||||
@@ -1778,7 +1825,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
else if (expression.getOperationTokenType() == JavaTokenType.MINUS && (PsiType.INT.equals(type) || PsiType.LONG.equals(type))) {
|
||||
addInstruction(new PushInstruction(myFactory.getConstFactory().createDefault(type), null));
|
||||
addInstruction(new SwapInstruction());
|
||||
addInstruction(new BinopInstruction(expression.getOperationTokenType(), expression, myProject));
|
||||
addInstruction(new BinopInstruction(expression.getOperationTokenType(), expression, type));
|
||||
}
|
||||
else {
|
||||
addInstruction(new PopInstruction());
|
||||
|
||||
+4
-4
@@ -28,6 +28,7 @@ import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.MethodUtils;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import gnu.trove.THashSet;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -603,8 +604,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
}
|
||||
}
|
||||
DfaValue result = null;
|
||||
PsiElement expr = instruction.getPsiAnchor();
|
||||
PsiType type = expr instanceof PsiExpression ? ((PsiExpression)expr).getType() : null;
|
||||
PsiType type = instruction.getResultType();
|
||||
if (PsiType.INT.equals(type) || PsiType.LONG.equals(type)) {
|
||||
LongRangeSet left = memState.getValueFact(dfaLeft, DfaFactType.RANGE);
|
||||
LongRangeSet right = memState.getValueFact(dfaRight, DfaFactType.RANGE);
|
||||
@@ -616,8 +616,8 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
if (JavaTokenType.PLUS == opSign && !(type instanceof PsiPrimitiveType)) {
|
||||
result = instruction.getNonNullStringValue(runner.getFactory());
|
||||
if (JavaTokenType.PLUS == opSign && TypeUtils.isJavaLangString(type)) {
|
||||
result = runner.getFactory().createTypeValue(type, Nullness.NOT_NULL);
|
||||
}
|
||||
else if (instruction instanceof InstanceofInstruction) {
|
||||
handleInstanceof((InstanceofInstruction)instruction, dfaRight, dfaLeft);
|
||||
|
||||
+10
-16
@@ -16,18 +16,14 @@
|
||||
|
||||
package com.intellij.codeInspection.dataFlow.instructions;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiClassType;
|
||||
import com.intellij.codeInspection.dataFlow.DataFlowRunner;
|
||||
import com.intellij.codeInspection.dataFlow.DfaInstructionState;
|
||||
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.psi.JavaTokenType.*;
|
||||
@@ -36,11 +32,11 @@ public class BinopInstruction extends BranchingInstruction {
|
||||
private static final TokenSet ourSignificantOperations =
|
||||
TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS, MINUS, AND, PERC, DIV, GTGT, GTGTGT);
|
||||
private final IElementType myOperationSign;
|
||||
private final Project myProject;
|
||||
private final @Nullable PsiType myResultType;
|
||||
|
||||
public BinopInstruction(IElementType opSign, @Nullable PsiElement psiAnchor, @NotNull Project project) {
|
||||
public BinopInstruction(IElementType opSign, @Nullable PsiElement psiAnchor, @Nullable PsiType resultType) {
|
||||
super(psiAnchor);
|
||||
myProject = project;
|
||||
myResultType = resultType;
|
||||
myOperationSign = ourSignificantOperations.contains(opSign) ? opSign : null;
|
||||
}
|
||||
|
||||
@@ -49,11 +45,9 @@ public class BinopInstruction extends BranchingInstruction {
|
||||
return visitor.visitBinop(this, runner, stateBefore);
|
||||
}
|
||||
|
||||
public DfaValue getNonNullStringValue(final DfaValueFactory factory) {
|
||||
PsiElement anchor = getPsiAnchor();
|
||||
Project project = myProject;
|
||||
PsiClassType string = PsiType.getJavaLangString(PsiManager.getInstance(project), anchor == null ? GlobalSearchScope.allScope(project) : anchor.getResolveScope());
|
||||
return factory.createTypeValue(string, Nullness.NOT_NULL);
|
||||
@Nullable
|
||||
public PsiType getResultType() {
|
||||
return myResultType;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
+2
-3
@@ -19,7 +19,6 @@ import com.intellij.codeInspection.dataFlow.DataFlowRunner;
|
||||
import com.intellij.codeInspection.dataFlow.DfaInstructionState;
|
||||
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
@@ -34,8 +33,8 @@ public class InstanceofInstruction extends BinopInstruction {
|
||||
@Nullable private final PsiExpression myLeft;
|
||||
@NotNull private final PsiType myCastType;
|
||||
|
||||
public InstanceofInstruction(PsiElement psiAnchor, @NotNull Project project, @Nullable PsiExpression left, @NotNull PsiType castType) {
|
||||
super(JavaTokenType.INSTANCEOF_KEYWORD, psiAnchor, project);
|
||||
public InstanceofInstruction(PsiElement psiAnchor, @Nullable PsiExpression left, @NotNull PsiType castType) {
|
||||
super(JavaTokenType.INSTANCEOF_KEYWORD, psiAnchor, PsiType.BOOLEAN);
|
||||
myLeft = left;
|
||||
myCastType = castType;
|
||||
}
|
||||
|
||||
@@ -167,4 +167,31 @@ class AdvancedArrayAccess {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
|
||||
void testNonInitializedShort() {
|
||||
int[] x = new int[2];
|
||||
if(<warning descr="Condition 'x[0] == 0' is always 'true'">x[0] == 0</warning>) {
|
||||
System.out.println("Always");
|
||||
}
|
||||
}
|
||||
|
||||
void testFor2() {
|
||||
String[] x = new String[2];
|
||||
for (int i = 0; i < 2; i++) {
|
||||
x[i] = String.valueOf(i);
|
||||
}
|
||||
for (int i = 0; i < 2; i++) {
|
||||
System.out.println(x[i].trim());
|
||||
}
|
||||
}
|
||||
|
||||
void testFor3() {
|
||||
int[] x = new int[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
x[i] = i;
|
||||
}
|
||||
if(<warning descr="Condition 'x[2] == 2' is always 'true'">x[2] == 2</warning>) {
|
||||
System.out.println("Always");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user