summaryrefslogtreecommitdiff
path: root/java/java-analysis-impl
diff options
context:
space:
mode:
Diffstat (limited to 'java/java-analysis-impl')
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java26
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java17
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java8
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java14
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java5
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java359
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverterImpl.java206
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java166
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java19
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java2
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java19
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java260
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java152
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java21
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java14
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java10
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/xml/DeprecatedClassUsageInspection.java8
17 files changed, 759 insertions, 547 deletions
diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java
index 7f8d01382b09..c23ebdd00855 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java
@@ -1292,5 +1292,31 @@ public class GenericsHighlightUtil {
}
}
}
+
+ public static HighlightInfo checkInferredIntersections(PsiSubstitutor substitutor, TextRange ref) {
+ for (Map.Entry<PsiTypeParameter, PsiType> typeEntry : substitutor.getSubstitutionMap().entrySet()) {
+ final PsiType type = typeEntry.getValue();
+ if (type instanceof PsiIntersectionType) {
+ final PsiType[] conjuncts = ((PsiIntersectionType)type).getConjuncts();
+ for (int i = 0; i < conjuncts.length; i++) {
+ PsiClass conjunct = PsiUtil.resolveClassInClassTypeOnly(conjuncts[i]);
+ if (conjunct != null && !conjunct.isInterface()) {
+ for (int i1 = i + 1; i1 < conjuncts.length; i1++) {
+ PsiClass oppositeConjunct = PsiUtil.resolveClassInClassTypeOnly(conjuncts[i1]);
+ if (oppositeConjunct != null && !oppositeConjunct.isInterface()) {
+ if (!conjunct.isInheritor(oppositeConjunct, true) && !oppositeConjunct.isInheritor(conjunct, true)) {
+ return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
+ .descriptionAndTooltip("Type parameter " + typeEntry.getKey().getName() + " has incompatible upper bounds: " +
+ conjunct.getName() + " and " + oppositeConjunct.getName())
+ .range(ref).create();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java
index 602e3fea2cc2..a4ea7087af5a 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java
@@ -696,14 +696,17 @@ public class HighlightControlFlowUtil {
return true;
}
- if (ControlFlowUtil.isVariableDefinitelyAssigned(variable, controlFlow)) {
- final Collection<ControlFlowUtil.VariableInfo> initializedTwice = ControlFlowUtil.getInitializedTwice(controlFlow);
- effectivelyFinal = !initializedTwice.contains(new ControlFlowUtil.VariableInfo(variable, null));
- if (effectivelyFinal) {
- effectivelyFinal = notAccessedForWriting(variable, new LocalSearchScope(scope));
+ final List<PsiReferenceExpression> readBeforeWriteLocals = ControlFlowUtil.getReadBeforeWriteLocals(controlFlow);
+ for (PsiReferenceExpression expression : readBeforeWriteLocals) {
+ if (expression.resolve() == variable) {
+ return PsiUtil.isAccessedForReading(expression);
}
- } else {
- effectivelyFinal = false;
+ }
+
+ final Collection<ControlFlowUtil.VariableInfo> initializedTwice = ControlFlowUtil.getInitializedTwice(controlFlow);
+ effectivelyFinal = !initializedTwice.contains(new ControlFlowUtil.VariableInfo(variable, null));
+ if (effectivelyFinal) {
+ effectivelyFinal = notAccessedForWriting(variable, new LocalSearchScope(scope));
}
}
return effectivelyFinal;
diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java
index b567ce41325a..118c98efc9d2 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java
@@ -344,8 +344,12 @@ public class HighlightMethodUtil {
if (resolved instanceof PsiMethod && resolveResult.isValidResult()) {
TextRange fixRange = getFixRange(methodCall);
highlightInfo = HighlightUtil.checkUnhandledExceptions(methodCall, fixRange);
- if (highlightInfo == null && !LambdaUtil.isValidQualifier4InterfaceStaticMethodCall((PsiMethod)resolved, methodCall.getMethodExpression(), resolveResult.getCurrentFileResolveScope(), languageLevel)) {
- highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip("Static method may be invoked on containing interface class only").range(fixRange).create();
+ if (highlightInfo == null) {
+ if (!LambdaUtil.isValidQualifier4InterfaceStaticMethodCall((PsiMethod)resolved, methodCall.getMethodExpression(), resolveResult.getCurrentFileResolveScope(), languageLevel)) {
+ highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip("Static method may be invoked on containing interface class only").range(fixRange).create();
+ } else {
+ highlightInfo = GenericsHighlightUtil.checkInferredIntersections(substitutor, fixRange);
+ }
}
}
else {
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java
index c1f127d3a1df..621ce3fce2af 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java
@@ -141,7 +141,19 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
.inferTypeArguments(method.getTypeParameters(), parameters, expressions,
((MethodCandidateInfo)result).getSiteSubstitutor(), callExpr.getParent(),
DefaultParameterTypeInferencePolicy.INSTANCE);
- return substitutor.substitute(parameters[i].getType());
+ PsiType paramType;
+ if (i < parameters.length) {
+ paramType = parameters[i].getType();
+ }
+ else {
+ paramType = parameters[parameters.length - 1].getType();
+ if (!(paramType instanceof PsiEllipsisType)) {
+ return null;
+ }
+ paramType = ((PsiEllipsisType)paramType).getComponentType();
+ }
+
+ return substitutor.substitute(paramType);
}
}
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java
index 44e493c63f0a..47ef448c7484 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java
@@ -208,7 +208,8 @@ class MakeResult<Res> implements PendingAction<Res> {
}
abstract class Analysis<Res> {
- private static final int STEPS_LIMIT = 30000;
+ public static final int STEPS_LIMIT = 30000;
+ public static final int EQUATION_SIZE_LIMIT = 30;
final RichControlFlow richControlFlow;
final Direction direction;
final ControlFlowGraph controlFlow;
@@ -225,7 +226,7 @@ abstract class Analysis<Res> {
Res earlyResult = null;
abstract Res identity();
- abstract Res combineResults(Res delta, List<Res> subResults);
+ abstract Res combineResults(Res delta, List<Res> subResults) throws AnalyzerException;
abstract boolean isEarlyResult(Res res);
abstract Equation<Key, Value> mkEquation(Res result);
abstract void processState(State state) throws AnalyzerException;
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java
index f29dd7f6cf0c..0239a661c4ee 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java
@@ -15,112 +15,68 @@
*/
package com.intellij.codeInspection.bytecodeAnalysis;
-import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.progress.ProgressManager;
-import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.TypeConversionUtil;
-import com.intellij.util.io.*;
-import gnu.trove.TIntHashSet;
-import gnu.trove.TIntObjectHashMap;
-import gnu.trove.TIntObjectIterator;
+import gnu.trove.TLongArrayList;
+import gnu.trove.TLongHashSet;
+import gnu.trove.TLongObjectHashMap;
+import gnu.trove.TLongObjectIterator;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.jetbrains.org.objectweb.asm.Type;
-import java.io.DataInput;
-import java.io.DataOutput;
-import java.io.File;
import java.io.IOException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis.LOG;
/**
* @author lambdamix
*/
-public class BytecodeAnalysisConverter implements ApplicationComponent {
+public abstract class BytecodeAnalysisConverter implements ApplicationComponent {
- private static final String VERSION = "BytecodeAnalysisConverter.Enumerators";
+ public static final int SHIFT = 4096;
public static BytecodeAnalysisConverter getInstance() {
return ApplicationManager.getApplication().getComponent(BytecodeAnalysisConverter.class);
}
- private PersistentStringEnumerator myNamesEnumerator;
- private PersistentEnumeratorDelegate<int[]> myCompoundKeyEnumerator;
- private int version;
-
- @Override
- public void initComponent() {
- version = PropertiesComponent.getInstance().getOrInitInt(VERSION, 0);
- final File keysDir = new File(PathManager.getIndexRoot(), "bytecodekeys");
- final File namesFile = new File(keysDir, "names");
- final File compoundKeysFile = new File(keysDir, "compound");
-
- try {
- IOUtil.openCleanOrResetBroken(new ThrowableComputable<Void, IOException>() {
- @Override
- public Void compute() throws IOException {
- myNamesEnumerator = new PersistentStringEnumerator(namesFile, true);
- myCompoundKeyEnumerator = new IntArrayPersistentEnumerator(compoundKeysFile, new IntArrayKeyDescriptor());
- return null;
- }
- }, new Runnable() {
- @Override
- public void run() {
- LOG.info("Error during initialization of enumerators in bytecode analysis. Re-initializing.");
- IOUtil.deleteAllFilesStartingWith(keysDir);
- version ++;
- }
- });
- }
- catch (IOException e) {
- LOG.error("Re-initialization of enumerators in bytecode analysis failed.", e);
- }
- // TODO: is it enough for rebuilding indices?
- PropertiesComponent.getInstance().setValue(VERSION, String.valueOf(version));
- }
-
- @Override
- public void disposeComponent() {
- try {
- myNamesEnumerator.close();
- myCompoundKeyEnumerator.close();
- }
- catch (IOException e) {
- LOG.debug(e);
- }
- }
-
@NotNull
@Override
public String getComponentName() {
return "BytecodeAnalysisConverter";
}
- IntIdEquation convert(Equation<Key, Value> equation) throws IOException {
+ public abstract int getVersion();
+
+ protected abstract int enumerateString(@NotNull String s) throws IOException;
+
+ protected abstract int enumerateCompoundKey(@NotNull int[] key) throws IOException;
+
+ IdEquation convert(Equation<Key, Value> equation) throws IOException {
ProgressManager.checkCanceled();
Result<Key, Value> rhs = equation.rhs;
- IntIdResult result;
+ IdResult result;
if (rhs instanceof Final) {
- result = new IntIdFinal(((Final<Key, Value>)rhs).value);
+ result = new IdFinal(((Final<Key, Value>)rhs).value);
} else {
Pending<Key, Value> pending = (Pending<Key, Value>)rhs;
Set<Product<Key, Value>> sumOrigin = pending.sum;
IntIdComponent[] components = new IntIdComponent[sumOrigin.size()];
int componentI = 0;
for (Product<Key, Value> prod : sumOrigin) {
- int[] intProd = new int[prod.ids.size()];
+ long[] intProd = new long[prod.ids.size()];
int idI = 0;
for (Key id : prod.ids) {
- int rawId = mkAsmKey(id);
+ long rawId = mkAsmKey(id);
if (rawId <= 0) {
LOG.error("raw key should be positive. rawId = " + rawId);
}
@@ -131,24 +87,51 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
components[componentI] = intIdComponent;
componentI++;
}
- result = new IntIdPending(components);
+ result = new IdPending(components);
}
- int rawKey = mkAsmKey(equation.id);
+ long rawKey = mkAsmKey(equation.id);
if (rawKey <= 0) {
LOG.error("raw key should be positive. rawKey = " + rawKey);
}
- int key = equation.id.stable ? rawKey : -rawKey;
- return new IntIdEquation(key, result);
+ long key = equation.id.stable ? rawKey : -rawKey;
+ return new IdEquation(key, result);
}
- public int mkAsmKey(@NotNull Key key) throws IOException {
- return myCompoundKeyEnumerator.enumerate(new int[]{mkDirectionKey(key.direction), mkAsmSignatureKey(key.method)});
+ public long mkAsmKey(@NotNull Key key) throws IOException {
+ long baseKey = mkAsmSignatureKey(key.method);
+ long directionKey = mkDirectionKey(key.direction);
+ return baseKey * SHIFT + directionKey;
}
- private int mkDirectionKey(Direction dir) throws IOException {
- return myCompoundKeyEnumerator.enumerate(new int[]{dir.directionId(), dir.paramId(), dir.valueId()});
+ private static int mkDirectionKey(Direction dir) throws IOException {
+ if (dir instanceof Out) {
+ return 0;
+ } else if (dir instanceof In) {
+ In in = (In)dir;
+ return 8 * in.paramId() + 1;
+ } else {
+ InOut inOut = (InOut)dir;
+ return 8 * inOut.paramId() + 2 + inOut.valueId();
+ }
+ }
+
+ @NotNull
+ private static Direction extractDirection(int directionKey) {
+ if (directionKey == 0) {
+ return new Out();
+ }
+ else {
+ int paramId = directionKey / 8;
+ int subDirection = directionKey % 8;
+ if (subDirection == 1) {
+ return new In(paramId);
+ }
+ else {
+ return new InOut(paramId, Value.values()[subDirection - 2]);
+ }
+ }
}
// class + short signature
@@ -156,33 +139,19 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
int[] sigKey = new int[2];
sigKey[0] = mkAsmTypeKey(Type.getObjectType(method.internalClassName));
sigKey[1] = mkAsmShortSignatureKey(method);
- return myCompoundKeyEnumerator.enumerate(sigKey);
+ return enumerateCompoundKey(sigKey);
}
private int mkAsmShortSignatureKey(@NotNull Method method) throws IOException {
Type[] argTypes = Type.getArgumentTypes(method.methodDesc);
int arity = argTypes.length;
- int[] sigKey = new int[3 + arity];
+ int[] sigKey = new int[2 + arity];
sigKey[0] = mkAsmTypeKey(Type.getReturnType(method.methodDesc));
- sigKey[1] = myNamesEnumerator.enumerate(method.methodName);
- sigKey[2] = argTypes.length;
+ sigKey[1] = enumerateString(method.methodName);
for (int i = 0; i < argTypes.length; i++) {
- sigKey[3 + i] = mkAsmTypeKey(argTypes[i]);
+ sigKey[2 + i] = mkAsmTypeKey(argTypes[i]);
}
- return myCompoundKeyEnumerator.enumerate(sigKey);
- }
-
- @Nullable
- private static Direction extractDirection(int[] directionKey) {
- switch (directionKey[0]) {
- case Direction.OUT_DIRECTION:
- return new Out();
- case Direction.IN_DIRECTION:
- return new In(directionKey[1]);
- case Direction.INOUT_DIRECTION:
- return new InOut(directionKey[1], Value.values()[directionKey[2]]);
- }
- return null;
+ return enumerateCompoundKey(sigKey);
}
private int mkAsmTypeKey(Type type) throws IOException {
@@ -197,22 +166,22 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
packageName = "";
simpleName = className;
}
- int[] classKey = new int[]{myNamesEnumerator.enumerate(packageName), myNamesEnumerator.enumerate(simpleName)};
- return myCompoundKeyEnumerator.enumerate(classKey);
+ int[] classKey = new int[]{enumerateString(packageName), enumerateString(simpleName)};
+ return enumerateCompoundKey(classKey);
}
- public int mkPsiKey(@NotNull PsiMethod psiMethod, Direction direction) throws IOException {
+ public long mkPsiKey(@NotNull PsiMethod psiMethod, Direction direction) throws IOException {
final PsiClass psiClass = PsiTreeUtil.getParentOfType(psiMethod, PsiClass.class, false);
if (psiClass == null) {
LOG.debug("PsiClass was null for " + psiMethod.getName());
return -1;
}
- int sigKey = mkPsiSignatureKey(psiMethod);
+ long sigKey = mkPsiSignatureKey(psiMethod);
if (sigKey == -1) {
return -1;
}
- return myCompoundKeyEnumerator.enumerate(new int[]{mkDirectionKey(direction), sigKey});
-
+ long directionKey = mkDirectionKey(direction);
+ return sigKey * SHIFT + directionKey;
}
private int mkPsiSignatureKey(@NotNull PsiMethod psiMethod) throws IOException {
@@ -228,21 +197,20 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
final int shift = isInnerClassConstructor ? 1 : 0;
final int arity = parameters.length + shift;
- int[] shortSigKey = new int[3 + arity];
+ int[] shortSigKey = new int[2 + arity];
if (returnType == null) {
shortSigKey[0] = mkPsiTypeKey(PsiType.VOID);
- shortSigKey[1] = myNamesEnumerator.enumerate("<init>");
+ shortSigKey[1] = enumerateString("<init>");
} else {
shortSigKey[0] = mkPsiTypeKey(returnType);
- shortSigKey[1] = myNamesEnumerator.enumerate(psiMethod.getName());
+ shortSigKey[1] = enumerateString(psiMethod.getName());
}
- shortSigKey[2] = arity;
if (isInnerClassConstructor) {
- shortSigKey[3] = mkPsiClassKey(outerClass, 0);
+ shortSigKey[2] = mkPsiClassKey(outerClass, 0);
}
for (int i = 0; i < parameters.length; i++) {
PsiParameter parameter = parameters[i];
- shortSigKey[3 + i + shift] = mkPsiTypeKey(parameter.getType());
+ shortSigKey[2 + i + shift] = mkPsiTypeKey(parameter.getType());
}
for (int aShortSigKey : shortSigKey) {
if (aShortSigKey == -1) {
@@ -256,9 +224,28 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
return -1;
}
sigKey[0] = classKey;
- sigKey[1] = myCompoundKeyEnumerator.enumerate(shortSigKey);
+ sigKey[1] = enumerateCompoundKey(shortSigKey);
- return myCompoundKeyEnumerator.enumerate(sigKey);
+ return enumerateCompoundKey(sigKey);
+ }
+
+ public TLongArrayList mkInOutKeys(@NotNull PsiMethod psiMethod, long primaryKey) throws IOException {
+ PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
+ TLongArrayList keys = new TLongArrayList(parameters.length * 2 + 1);
+ for (int i = 0; i < parameters.length; i++) {
+ PsiParameter parameter = parameters[i];
+ PsiType parameterType = parameter.getType();
+ if (parameterType instanceof PsiPrimitiveType) {
+ if (PsiType.BOOLEAN.equals(parameterType)) {
+ keys.add(primaryKey + mkDirectionKey(new InOut(i, Value.False)));
+ keys.add(primaryKey + mkDirectionKey(new InOut(i, Value.True)));
+ }
+ } else {
+ keys.add(primaryKey + mkDirectionKey(new InOut(i, Value.NotNull)));
+ keys.add(primaryKey + mkDirectionKey(new InOut(i, Value.Null)));
+ }
+ }
+ return keys;
}
@@ -279,17 +266,17 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
className = qname.substring(packageName.length() + 1).replace('.', '$');
}
int[] classKey = new int[2];
- classKey[0] = myNamesEnumerator.enumerate(packageName);
+ classKey[0] = enumerateString(packageName);
if (dimensions == 0) {
- classKey[1] = myNamesEnumerator.enumerate(className);
+ classKey[1] = enumerateString(className);
} else {
StringBuilder sb = new StringBuilder(className);
for (int j = 0; j < dimensions; j++) {
sb.append("[]");
}
- classKey[1] = myNamesEnumerator.enumerate(sb.toString());
+ classKey[1] = enumerateString(sb.toString());
}
- return myCompoundKeyEnumerator.enumerate(classKey);
+ return enumerateCompoundKey(classKey);
}
private int mkPsiTypeKey(PsiType psiType) throws IOException {
@@ -316,77 +303,69 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
String packageName = "";
String className = psiType.getPresentableText();
int[] classKey = new int[2];
- classKey[0] = myNamesEnumerator.enumerate(packageName);
+ classKey[0] = enumerateString(packageName);
if (dimensions == 0) {
- classKey[1] = myNamesEnumerator.enumerate(className);
+ classKey[1] = enumerateString(className);
} else {
StringBuilder sb = new StringBuilder(className);
for (int j = 0; j < dimensions; j++) {
sb.append("[]");
}
- classKey[1] = myNamesEnumerator.enumerate(sb.toString());
+ classKey[1] = enumerateString(sb.toString());
}
- return myCompoundKeyEnumerator.enumerate(classKey);
+ return enumerateCompoundKey(classKey);
}
return -1;
}
- public void addAnnotations(TIntObjectHashMap<Value> internalIdSolutions, Annotations annotations) {
-
- TIntObjectHashMap<List<String>> contractClauses = new TIntObjectHashMap<List<String>>();
- TIntObjectIterator<Value> solutionsIterator = internalIdSolutions.iterator();
+ public void addMethodAnnotations(TLongObjectHashMap<Value> internalIdSolutions, Annotations annotations, long methodKey, int arity) {
- TIntHashSet notNulls = annotations.notNulls;
- TIntObjectHashMap<String> contracts = annotations.contracts;
+ List<String> clauses = new ArrayList<String>();
+ TLongObjectIterator<Value> solutionsIterator = internalIdSolutions.iterator();
+ TLongHashSet notNulls = annotations.notNulls;
+ TLongObjectHashMap<String> contracts = annotations.contracts;
for (int i = internalIdSolutions.size(); i-- > 0;) {
solutionsIterator.advance();
- int key = Math.abs(solutionsIterator.key());
+ long key = Math.abs(solutionsIterator.key());
Value value = solutionsIterator.value();
if (value == Value.Top || value == Value.Bot) {
continue;
}
- try {
- int[] compoundKey = myCompoundKeyEnumerator.valueOf(key);
- Direction direction = extractDirection(myCompoundKeyEnumerator.valueOf(compoundKey[0]));
- if (value == Value.NotNull && (direction instanceof In || direction instanceof Out)) {
- notNulls.add(key);
- }
- else if (direction instanceof InOut) {
- compoundKey = new int[]{mkDirectionKey(new Out()), compoundKey[1]};
- try {
- int baseKey = myCompoundKeyEnumerator.enumerate(compoundKey);
- List<String> clauses = contractClauses.get(baseKey);
- if (clauses == null) {
- clauses = new ArrayList<String>();
- contractClauses.put(baseKey, clauses);
- }
- int[] sig = myCompoundKeyEnumerator.valueOf(compoundKey[1]);
- int[] shortSig = myCompoundKeyEnumerator.valueOf(sig[1]);
- int arity = shortSig[2];
- clauses.add(contractElement(arity, (InOut)direction, value));
- }
- catch (IOException e) {
- LOG.debug(e);
- }
- }
+ Direction direction = extractDirection((int)(key % SHIFT));
+ if (value == Value.NotNull && direction instanceof Out && key == methodKey) {
+ notNulls.add(key);
}
- catch (IOException e) {
- LOG.debug(e);
+ else if (direction instanceof InOut) {
+ long baseKey = key - (key % SHIFT);
+ if (baseKey == methodKey) {
+ clauses.add(contractElement(arity, (InOut)direction, value));
+ }
}
}
- TIntObjectIterator<List<String>> buildersIterator = contractClauses.iterator();
- for (int i = contractClauses.size(); i-- > 0;) {
- buildersIterator.advance();
- int key = buildersIterator.key();
- if (!notNulls.contains(key)) {
- List<String> clauses = buildersIterator.value();
- Collections.sort(clauses);
- StringBuilder sb = new StringBuilder("\"");
- StringUtil.join(clauses, ";", sb);
- sb.append('"');
- contracts.put(key, sb.toString().intern());
+ if (!notNulls.contains(methodKey) && !clauses.isEmpty()) {
+ Collections.sort(clauses);
+ StringBuilder sb = new StringBuilder("\"");
+ StringUtil.join(clauses, ";", sb);
+ sb.append('"');
+ contracts.put(methodKey, sb.toString().intern());
+ }
+ }
+
+ public void addParameterAnnotations(TLongObjectHashMap<Value> internalIdSolutions, Annotations annotations) {
+ TLongObjectIterator<Value> solutionsIterator = internalIdSolutions.iterator();
+ TLongHashSet notNulls = annotations.notNulls;
+ for (int i = internalIdSolutions.size(); i-- > 0;) {
+ solutionsIterator.advance();
+ long key = Math.abs(solutionsIterator.key());
+ Value value = solutionsIterator.value();
+ if (value == Value.Top || value == Value.Bot) {
+ continue;
+ }
+ Direction direction = extractDirection((int)(key % SHIFT));
+ if (value == Value.NotNull && (direction instanceof In || direction instanceof Out)) {
+ notNulls.add(key);
}
}
}
@@ -418,68 +397,4 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
return sb.toString();
}
- public int getVersion() {
- return version;
- }
-
- private static class IntArrayKeyDescriptor implements KeyDescriptor<int[]> {
-
- @Override
- public void save(@NotNull DataOutput out, int[] value) throws IOException {
- DataInputOutputUtil.writeINT(out, value.length);
- for (int i : value) {
- DataInputOutputUtil.writeINT(out, i);
- }
- }
-
- @Override
- public int[] read(@NotNull DataInput in) throws IOException {
- int[] value = new int[DataInputOutputUtil.readINT(in)];
- for (int i = 0; i < value.length; i++) {
- value[i] = DataInputOutputUtil.readINT(in);
- }
- return value;
- }
-
- @Override
- public int getHashCode(int[] value) {
- return Arrays.hashCode(value);
- }
-
- @Override
- public boolean isEqual(int[] val1, int[] val2) {
- return Arrays.equals(val1, val2);
- }
- }
-
- private static class IntArrayPersistentEnumerator extends PersistentEnumeratorDelegate<int[]> {
- private final CachingEnumerator<int[]> myCache;
-
- public IntArrayPersistentEnumerator(File compoundKeysFile, IntArrayKeyDescriptor descriptor) throws IOException {
- super(compoundKeysFile, descriptor, 1024 * 4);
- myCache = new CachingEnumerator<int[]>(new DataEnumerator<int[]>() {
- @Override
- public int enumerate(@Nullable int[] value) throws IOException {
- return IntArrayPersistentEnumerator.super.enumerate(value);
- }
-
- @Nullable
- @Override
- public int[] valueOf(int idx) throws IOException {
- return IntArrayPersistentEnumerator.super.valueOf(idx);
- }
- }, descriptor);
- }
-
- @Override
- public int enumerate(@Nullable int[] value) throws IOException {
- return myCache.enumerate(value);
- }
-
- @Nullable
- @Override
- public int[] valueOf(int idx) throws IOException {
- return myCache.valueOf(idx);
- }
- }
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverterImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverterImpl.java
new file mode 100644
index 000000000000..067cc743679e
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverterImpl.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.codeInspection.bytecodeAnalysis;
+
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.util.ThrowableComputable;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.util.io.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.*;
+import java.io.DataOutputStream;
+import java.util.Arrays;
+
+import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis.LOG;
+
+/**
+ * @author lambdamix
+ */
+public class BytecodeAnalysisConverterImpl extends BytecodeAnalysisConverter {
+ private static final int LOGIC_VERSION = 1;
+ private static final String ENUMERATORS_VERSION_KEY = "BytecodeAnalysisConverter.Enumerators";
+
+ private File myVersionFile;
+ private PersistentStringEnumerator myNamesEnumerator;
+ private PersistentEnumeratorDelegate<int[]> myCompoundKeyEnumerator;
+ private int version;
+
+ @Override
+ public void initComponent() {
+
+ // suffix as an indicator of version
+ final File keysDir = new File(PathManager.getIndexRoot(), "bytecodekeys");
+ final File namesFile = new File(keysDir, "names" + LOGIC_VERSION);
+ final File compoundKeysFile = new File(keysDir, "compound" + LOGIC_VERSION);
+ myVersionFile = new File(keysDir, "version" + LOGIC_VERSION);
+
+ version = PropertiesComponent.getInstance().getOrInitInt(ENUMERATORS_VERSION_KEY, 0);
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ version = _readVersion();
+ }
+
+ if (!namesFile.exists() || !compoundKeysFile.exists() || !myVersionFile.exists()) {
+ LOG.info("No enumerators detected, re-initialization of enumerators.");
+ IOUtil.deleteAllFilesStartingWith(keysDir);
+ version++;
+ }
+
+ try {
+ IOUtil.openCleanOrResetBroken(new ThrowableComputable<Void, IOException>() {
+ @Override
+ public Void compute() throws IOException {
+ myNamesEnumerator = new PersistentStringEnumerator(namesFile, true);
+ myCompoundKeyEnumerator = new IntArrayPersistentEnumerator(compoundKeysFile, new IntArrayKeyDescriptor());
+ return null;
+ }
+ }, new Runnable() {
+ @Override
+ public void run() {
+ LOG.info("Error during initialization of enumerators in bytecode analysis. Re-initializing.");
+ IOUtil.deleteAllFilesStartingWith(keysDir);
+ version++;
+ }
+ });
+ }
+ catch (IOException e) {
+ LOG.error("Re-initialization of enumerators in bytecode analysis failed.", e);
+ }
+ PropertiesComponent.getInstance().setValue(ENUMERATORS_VERSION_KEY, String.valueOf(version));
+ _saveVersion();
+ }
+
+ @Override
+ public void disposeComponent() {
+ try {
+ myNamesEnumerator.close();
+ myCompoundKeyEnumerator.close();
+ }
+ catch (IOException e) {
+ LOG.debug(e);
+ }
+ }
+
+ public int _readVersion() {
+ try {
+ final DataInputStream is = new DataInputStream(new FileInputStream(myVersionFile));
+ try {
+ return is.readInt();
+ }
+ finally {
+ is.close();
+ }
+ }
+ catch (FileNotFoundException ignored) {
+ }
+ catch (IOException ignored) {
+ }
+ return 0;
+ }
+
+ private void _saveVersion() {
+ try {
+ FileUtil.createIfDoesntExist(myVersionFile);
+ final DataOutputStream os = new DataOutputStream(new FileOutputStream(myVersionFile));
+ try {
+ os.writeInt(version);
+ }
+ finally {
+ os.close();
+ }
+ }
+ catch (IOException ignored) {
+ }
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ @Override
+ protected int enumerateString(@NotNull String s) throws IOException {
+ return myNamesEnumerator.enumerate(s);
+ }
+
+ @Override
+ protected int enumerateCompoundKey(@NotNull int[] key) throws IOException {
+ return myCompoundKeyEnumerator.enumerate(key);
+ }
+
+ private static class IntArrayKeyDescriptor implements KeyDescriptor<int[]>, DifferentSerializableBytesImplyNonEqualityPolicy {
+
+ @Override
+ public void save(@NotNull DataOutput out, int[] value) throws IOException {
+ DataInputOutputUtil.writeINT(out, value.length);
+ for (int i : value) {
+ DataInputOutputUtil.writeINT(out, i);
+ }
+ }
+
+ @Override
+ public int[] read(@NotNull DataInput in) throws IOException {
+ int[] value = new int[DataInputOutputUtil.readINT(in)];
+ for (int i = 0; i < value.length; i++) {
+ value[i] = DataInputOutputUtil.readINT(in);
+ }
+ return value;
+ }
+
+ @Override
+ public int getHashCode(int[] value) {
+ return Arrays.hashCode(value);
+ }
+
+ @Override
+ public boolean isEqual(int[] val1, int[] val2) {
+ return Arrays.equals(val1, val2);
+ }
+ }
+
+ private static class IntArrayPersistentEnumerator extends PersistentEnumeratorDelegate<int[]> {
+ private final CachingEnumerator<int[]> myCache;
+
+ public IntArrayPersistentEnumerator(File compoundKeysFile, IntArrayKeyDescriptor descriptor) throws IOException {
+ super(compoundKeysFile, descriptor, 1024 * 4);
+ myCache = new CachingEnumerator<int[]>(new DataEnumerator<int[]>() {
+ @Override
+ public int enumerate(@Nullable int[] value) throws IOException {
+ return IntArrayPersistentEnumerator.super.enumerate(value);
+ }
+
+ @Nullable
+ @Override
+ public int[] valueOf(int idx) throws IOException {
+ return IntArrayPersistentEnumerator.super.valueOf(idx);
+ }
+ }, descriptor);
+ }
+
+ @Override
+ public int enumerate(@Nullable int[] value) throws IOException {
+ return myCache.enumerate(value);
+ }
+
+ @Nullable
+ @Override
+ public int[] valueOf(int idx) throws IOException {
+ return myCache.valueOf(idx);
+ }
+ }
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java
index 6a4b32783c95..297b2b694c62 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java
@@ -19,31 +19,29 @@ import com.intellij.ide.highlighter.JavaClassFileType;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.VirtualFileWithId;
import com.intellij.util.SystemProperties;
import com.intellij.util.indexing.*;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.DataInputOutputUtil;
-import com.intellij.util.io.EnumeratorIntegerDescriptor;
+import com.intellij.util.io.DifferentSerializableBytesImplyNonEqualityPolicy;
import com.intellij.util.io.KeyDescriptor;
import org.jetbrains.annotations.NotNull;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
/**
* @author lambdamix
*/
-public class BytecodeAnalysisIndex extends FileBasedIndexExtension<Integer, Collection<IntIdEquation>> {
- public static final ID<Integer, Collection<IntIdEquation>> NAME = ID.create("bytecodeAnalysis");
+public class BytecodeAnalysisIndex extends FileBasedIndexExtension<Long, IdEquation> {
+ public static final ID<Long, IdEquation> NAME = ID.create("bytecodeAnalysis");
private final EquationExternalizer myExternalizer = new EquationExternalizer();
- private static final DataIndexer<Integer, Collection<IntIdEquation>, FileContent> INDEXER =
+ private static final DataIndexer<Long, IdEquation, FileContent> INDEXER =
new ClassDataIndexer(BytecodeAnalysisConverter.getInstance());
+ private static final SmartLongKeyDescriptor KEY_DESCRIPTOR = new SmartLongKeyDescriptor();
- private static final int ourInternalVersion = 2;
+ private static final int ourInternalVersion = 3;
private static boolean ourEnabled = SystemProperties.getBooleanProperty("idea.enable.bytecode.contract.inference", isEnabledByDefault());
private static boolean isEnabledByDefault() {
@@ -51,31 +49,27 @@ public class BytecodeAnalysisIndex extends FileBasedIndexExtension<Integer, Coll
return application.isInternal() || application.isUnitTestMode();
}
- public static int indexKey(VirtualFile file, boolean parameters) {
- return (file instanceof VirtualFileWithId ? ((VirtualFileWithId)file).getId() * 2 : -2) + (parameters ? 1 : 0);
- }
-
@NotNull
@Override
- public ID<Integer, Collection<IntIdEquation>> getName() {
+ public ID<Long, IdEquation> getName() {
return NAME;
}
@NotNull
@Override
- public DataIndexer<Integer, Collection<IntIdEquation>, FileContent> getIndexer() {
+ public DataIndexer<Long, IdEquation, FileContent> getIndexer() {
return INDEXER;
}
@NotNull
@Override
- public KeyDescriptor<Integer> getKeyDescriptor() {
- return EnumeratorIntegerDescriptor.INSTANCE;
+ public KeyDescriptor<Long> getKeyDescriptor() {
+ return KEY_DESCRIPTOR;
}
@NotNull
@Override
- public DataExternalizer<Collection<IntIdEquation>> getValueExternalizer() {
+ public DataExternalizer<IdEquation> getValueExternalizer() {
return myExternalizer;
}
@@ -100,68 +94,108 @@ public class BytecodeAnalysisIndex extends FileBasedIndexExtension<Integer, Coll
return ourInternalVersion + BytecodeAnalysisConverter.getInstance().getVersion() + (ourEnabled ? 0xFF : 0);
}
- public static class EquationExternalizer implements DataExternalizer<Collection<IntIdEquation>> {
+ public static class EquationExternalizer implements DataExternalizer<IdEquation>, DifferentSerializableBytesImplyNonEqualityPolicy {
@Override
- public void save(@NotNull DataOutput out, Collection<IntIdEquation> equations) throws IOException {
- DataInputOutputUtil.writeINT(out, equations.size());
-
- for (IntIdEquation equation : equations) {
- out.writeInt(equation.id);
- IntIdResult rhs = equation.rhs;
- if (rhs instanceof IntIdFinal) {
- IntIdFinal finalResult = (IntIdFinal)rhs;
- out.writeBoolean(true); // final flag
- DataInputOutputUtil.writeINT(out, finalResult.value.ordinal());
- } else {
- IntIdPending pendResult = (IntIdPending)rhs;
- out.writeBoolean(false); // pending flag
- DataInputOutputUtil.writeINT(out, pendResult.delta.length);
-
- for (IntIdComponent component : pendResult.delta) {
- DataInputOutputUtil.writeINT(out, component.value.ordinal());
- int[] ids = component.ids;
- DataInputOutputUtil.writeINT(out, ids.length);
- for (int id : ids) {
- out.writeInt(id);
- }
+ public void save(@NotNull DataOutput out, IdEquation equation) throws IOException {
+ long id = equation.id;
+ int sign = id > 0 ? 1 : -1;
+ id = Math.abs(id);
+ int primaryId = (int)(id / BytecodeAnalysisConverter.SHIFT);
+ int secondaryId = (int)(id % BytecodeAnalysisConverter.SHIFT);
+ out.writeInt(sign * primaryId);
+ DataInputOutputUtil.writeINT(out, secondaryId);
+ IdResult rhs = equation.rhs;
+ if (rhs instanceof IdFinal) {
+ IdFinal finalResult = (IdFinal)rhs;
+ out.writeBoolean(true); // final flag
+ DataInputOutputUtil.writeINT(out, finalResult.value.ordinal());
+ } else {
+ IdPending pendResult = (IdPending)rhs;
+ out.writeBoolean(false); // pending flag
+ DataInputOutputUtil.writeINT(out, pendResult.delta.length);
+
+ for (IntIdComponent component : pendResult.delta) {
+ DataInputOutputUtil.writeINT(out, component.value.ordinal());
+ long[] ids = component.ids;
+ DataInputOutputUtil.writeINT(out, ids.length);
+ for (long id1 : ids) {
+ sign = id1 > 0 ? 1 : -1;
+ id = Math.abs(id1);
+ primaryId = (int)(id / BytecodeAnalysisConverter.SHIFT);
+ secondaryId = (int)(id % BytecodeAnalysisConverter.SHIFT);
+ out.writeInt(sign * primaryId);
+ DataInputOutputUtil.writeINT(out, secondaryId);
}
}
}
}
@Override
- public Collection<IntIdEquation> read(@NotNull DataInput in) throws IOException {
-
- int size = DataInputOutputUtil.readINT(in);
- ArrayList<IntIdEquation> result = new ArrayList<IntIdEquation>(size);
-
- for (int x = 0; x < size; x++) {
- int equationId = in.readInt();
- boolean isFinal = in.readBoolean(); // flag
- if (isFinal) {
+ public IdEquation read(@NotNull DataInput in) throws IOException {
+ long primaryId = in.readInt();
+ int sign = primaryId > 0 ? 1 : -1;
+ primaryId = Math.abs(primaryId);
+ int secondaryId = DataInputOutputUtil.readINT(in);
+ long equationId = sign * (primaryId * BytecodeAnalysisConverter.SHIFT + secondaryId);
+ boolean isFinal = in.readBoolean(); // flag
+ if (isFinal) {
+ int ordinal = DataInputOutputUtil.readINT(in);
+ Value value = Value.values()[ordinal];
+ return new IdEquation(equationId, new IdFinal(value));
+ } else {
+
+ int sumLength = DataInputOutputUtil.readINT(in);
+ IntIdComponent[] components = new IntIdComponent[sumLength];
+
+ for (int i = 0; i < sumLength; i++) {
int ordinal = DataInputOutputUtil.readINT(in);
Value value = Value.values()[ordinal];
- result.add(new IntIdEquation(equationId, new IntIdFinal(value)));
- } else {
-
- int sumLength = DataInputOutputUtil.readINT(in);
- IntIdComponent[] components = new IntIdComponent[sumLength];
-
- for (int i = 0; i < sumLength; i++) {
- int ordinal = DataInputOutputUtil.readINT(in);
- Value value = Value.values()[ordinal];
- int componentSize = DataInputOutputUtil.readINT(in);
- int[] ids = new int[componentSize];
- for (int j = 0; j < componentSize; j++) {
- ids[j] = in.readInt();
- }
- components[i] = new IntIdComponent(value, ids);
+ int componentSize = DataInputOutputUtil.readINT(in);
+ long[] ids = new long[componentSize];
+ for (int j = 0; j < componentSize; j++) {
+ primaryId = in.readInt();
+ sign = primaryId > 0 ? 1 : -1;
+ primaryId = Math.abs(primaryId);
+ secondaryId = DataInputOutputUtil.readINT(in);
+ long id = sign * (primaryId * BytecodeAnalysisConverter.SHIFT + secondaryId);
+ ids[j] = id;
}
- result.add(new IntIdEquation(equationId, new IntIdPending(components)));
+ components[i] = new IntIdComponent(value, ids);
}
+ return new IdEquation(equationId, new IdPending(components));
}
+ }
+ }
+
+ private static class SmartLongKeyDescriptor implements KeyDescriptor<Long>, DifferentSerializableBytesImplyNonEqualityPolicy {
+ @Override
+ public void save(@NotNull DataOutput out, Long value) throws IOException {
+ long id = value.longValue();
+ int sign = id > 0 ? 1 : -1;
+ id = Math.abs(id);
+ int primaryId = (int)(id / BytecodeAnalysisConverter.SHIFT);
+ int secondaryId = (int)(id % BytecodeAnalysisConverter.SHIFT);
+ out.writeInt(primaryId * sign);
+ DataInputOutputUtil.writeINT(out, secondaryId);
+ }
+
+ @Override
+ public Long read(@NotNull DataInput in) throws IOException {
+ long primaryId = in.readInt();
+ int sign = primaryId > 0 ? 1 : -1;
+ primaryId = Math.abs(primaryId);
+ int secondaryId = DataInputOutputUtil.readINT(in);
+ return sign * (primaryId * BytecodeAnalysisConverter.SHIFT + secondaryId);
+ }
- return result;
+ @Override
+ public int getHashCode(Long value) {
+ return value.hashCode();
+ }
+
+ @Override
+ public boolean isEqual(Long val1, Long val2) {
+ return val1.longValue() == val2.longValue();
}
}
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java
index 5e74a8b5dbb3..9e656b98a7fb 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java
@@ -33,7 +33,7 @@ import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalys
/**
* @author lambdamix
*/
-public class ClassDataIndexer implements DataIndexer<Integer, Collection<IntIdEquation>, FileContent> {
+public class ClassDataIndexer implements DataIndexer<Long, IdEquation, FileContent> {
final BytecodeAnalysisConverter myConverter;
public ClassDataIndexer(BytecodeAnalysisConverter converter) {
@@ -42,25 +42,20 @@ public class ClassDataIndexer implements DataIndexer<Integer, Collection<IntIdEq
@NotNull
@Override
- public Map<Integer, Collection<IntIdEquation>> map(@NotNull FileContent inputData) {
- HashMap<Integer, Collection<IntIdEquation>> map = new HashMap<Integer, Collection<IntIdEquation>>(2);
+ public Map<Long, IdEquation> map(@NotNull FileContent inputData) {
+ HashMap<Long, IdEquation> map = new HashMap<Long, IdEquation>();
try {
ClassEquations rawEquations = processClass(new ClassReader(inputData.getContent()));
List<Equation<Key, Value>> rawParameterEquations = rawEquations.parameterEquations;
List<Equation<Key, Value>> rawContractEquations = rawEquations.contractEquations;
- Collection<IntIdEquation> idParameterEquations = new ArrayList<IntIdEquation>(rawParameterEquations.size());
- Collection<IntIdEquation> idContractEquations = new ArrayList<IntIdEquation>(rawContractEquations.size());
-
- map.put(BytecodeAnalysisIndex.indexKey(inputData.getFile(), true), idParameterEquations);
- map.put(BytecodeAnalysisIndex.indexKey(inputData.getFile(), false), idContractEquations);
-
-
for (Equation<Key, Value> rawParameterEquation: rawParameterEquations) {
- idParameterEquations.add(myConverter.convert(rawParameterEquation));
+ IdEquation equation = myConverter.convert(rawParameterEquation);
+ map.put(equation.id, equation);
}
for (Equation<Key, Value> rawContractEquation: rawContractEquations) {
- idContractEquations.add(myConverter.convert(rawContractEquation));
+ IdEquation equation = myConverter.convert(rawContractEquation);
+ map.put(equation.id, equation);
}
}
catch (ProcessCanceledException e) {
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java
index c837b127b74b..7b6c52e75a63 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java
@@ -49,7 +49,7 @@ class InOutAnalysis extends Analysis<Result<Key, Value>> {
}
@Override
- Result<Key, Value> combineResults(Result<Key, Value> delta, List<Result<Key, Value>> subResults) {
+ Result<Key, Value> combineResults(Result<Key, Value> delta, List<Result<Key, Value>> subResults) throws AnalyzerException {
Result<Key, Value> result = null;
for (Result<Key, Value> subResult : subResults) {
if (result == null) {
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java
index 08c52c4d49b0..625eb8eed977 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java
@@ -78,8 +78,9 @@ abstract class PResults {
};
static final class ConditionalNPE implements PResult {
final Set<Set<Key>> sop;
- public ConditionalNPE(Set<Set<Key>> sop) {
+ public ConditionalNPE(Set<Set<Key>> sop) throws AnalyzerException {
this.sop = sop;
+ checkLimit(sop);
}
public ConditionalNPE(Key key) {
@@ -88,9 +89,19 @@ abstract class PResults {
prod.add(key);
sop.add(prod);
}
+
+ static void checkLimit(Set<Set<Key>> sop) throws AnalyzerException {
+ int size = 0;
+ for (Set<Key> keys : sop) {
+ size += keys.size();
+ }
+ if (size > Analysis.EQUATION_SIZE_LIMIT) {
+ throw new AnalyzerException(null, "Equation size is too big");
+ }
+ }
}
- static PResult join(PResult r1, PResult r2) {
+ static PResult join(PResult r1, PResult r2) throws AnalyzerException {
if (Identity == r1) return r2;
if (Identity == r2) return r1;
if (Return == r1) return Return;
@@ -102,7 +113,7 @@ abstract class PResults {
return new ConditionalNPE(join(cnpe1.sop, cnpe2.sop));
}
- static PResult meet(PResult r1, PResult r2) {
+ static PResult meet(PResult r1, PResult r2) throws AnalyzerException {
if (Identity == r1) return r2;
if (Return == r1) return r2;
if (Return == r2) return r1;
@@ -130,7 +141,7 @@ class NonNullInAnalysis extends Analysis<PResult> {
}
@Override
- PResult combineResults(PResult delta, List<PResult> subResults) {
+ PResult combineResults(PResult delta, List<PResult> subResults) throws AnalyzerException {
PResult subResult = Identity;
for (PResult sr : subResults) {
subResult = join(subResult, sr);
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java
index 86b9dd101fd9..23d9d5a9e1c5 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java
@@ -15,34 +15,31 @@
*/
package com.intellij.codeInspection.bytecodeAnalysis;
-import com.intellij.ProjectTopics;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.ContentIterator;
-import com.intellij.openapi.roots.ModuleRootAdapter;
-import com.intellij.openapi.roots.ModuleRootEvent;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.ModificationTracker;
-import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.ProjectScope;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
+import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.containers.LongStack;
import com.intellij.util.indexing.FileBasedIndex;
-import com.intellij.util.messages.MessageBusConnection;
-import gnu.trove.TIntHashSet;
-import gnu.trove.TIntObjectHashMap;
+import gnu.trove.TLongArrayList;
+import gnu.trove.TLongHashSet;
+import gnu.trove.TLongObjectHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
-import java.util.Collection;
+import java.util.List;
/**
* @author lambdamix
@@ -50,93 +47,15 @@ import java.util.Collection;
public class ProjectBytecodeAnalysis {
public static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.bytecodeAnalysis");
public static final Key<Boolean> INFERRED_ANNOTATION = Key.create("INFERRED_ANNOTATION");
+ public static final int EQUATIONS_LIMIT = 1000;
private final Project myProject;
- private volatile Annotations myAnnotations = null;
-
public static ProjectBytecodeAnalysis getInstance(@NotNull Project project) {
return ServiceManager.getService(project, ProjectBytecodeAnalysis.class);
}
public ProjectBytecodeAnalysis(Project project) {
myProject = project;
- final MessageBusConnection connection = myProject.getMessageBus().connect();
- connection.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
- @Override
- public void rootsChanged(ModuleRootEvent event) {
- unloadAnnotations();
- }
- });
- }
-
- private void loadAnnotations() {
- Annotations annotations = new Annotations();
- loadParameterAnnotations(annotations);
- loadContractAnnotations(annotations);
- myAnnotations = annotations;
- LOG.debug("NotNull annotations: " + myAnnotations.notNulls.size());
- LOG.debug("Contract annotations: " + myAnnotations.contracts.size());
- }
-
- private void unloadAnnotations() {
- myAnnotations = null;
- LOG.debug("unloaded");
- }
-
- private void loadParameterAnnotations(Annotations annotations) {
- LOG.debug("initializing parameter annotations");
- final IntIdSolver solver = new IntIdSolver(new ELattice<Value>(Value.NotNull, Value.Top));
-
- processValues(true, new FileBasedIndex.ValueProcessor<Collection<IntIdEquation>>() {
- @Override
- public boolean process(VirtualFile file, Collection<IntIdEquation> value) {
- for (IntIdEquation intIdEquation : value) {
- solver.addEquation(intIdEquation);
- }
- return true;
- }
- });
-
- LOG.debug("parameter equations are constructed");
- LOG.debug("equations: " + solver.getSize());
- TIntObjectHashMap<Value> solutions = solver.solve();
- LOG.debug("parameter equations are solved");
- BytecodeAnalysisConverter.getInstance().addAnnotations(solutions, annotations);
- }
-
- private void processValues(final boolean parameters, final FileBasedIndex.ValueProcessor<Collection<IntIdEquation>> processor) {
- final GlobalSearchScope libScope = ProjectScope.getLibrariesScope(myProject);
- final FileBasedIndex index = FileBasedIndex.getInstance();
- index.iterateIndexableFiles(new ContentIterator() {
- @Override
- public boolean processFile(VirtualFile fileOrDir) {
- ProgressManager.checkCanceled();
- if (!fileOrDir.isDirectory() && libScope.contains(fileOrDir)) {
- index.processValues(BytecodeAnalysisIndex.NAME, BytecodeAnalysisIndex.indexKey(fileOrDir, parameters),
- fileOrDir, processor, GlobalSearchScope.fileScope(myProject, fileOrDir));
- }
- return false;
- }
- }, myProject, null);
- }
-
- private void loadContractAnnotations(Annotations annotations) {
- LOG.debug("initializing contract annotations");
- final IntIdSolver solver = new IntIdSolver(new ELattice<Value>(Value.Bot, Value.Top));
- processValues(false, new FileBasedIndex.ValueProcessor<Collection<IntIdEquation>>() {
- @Override
- public boolean process(VirtualFile file, Collection<IntIdEquation> value) {
- for (IntIdEquation intIdEquation : value) {
- solver.addEquation(intIdEquation);
- }
- return true;
- }
- });
- LOG.debug("contract equations are constructed");
- LOG.debug("equations: " + solver.getSize());
- TIntObjectHashMap<Value> solutions = solver.solve();
- LOG.debug("contract equations are solved");
- BytecodeAnalysisConverter.getInstance().addAnnotations(solutions, annotations);
}
@Nullable
@@ -144,11 +63,14 @@ public class ProjectBytecodeAnalysis {
if (!(listOwner instanceof PsiCompiledElement)) {
return null;
}
- if (annotationFQN.equals("org.jetbrains.annotations.NotNull")) {
- return findNotNullAnnotation(listOwner);
- }
- else if (annotationFQN.equals("org.jetbrains.annotations.Contract")) {
- return findContractAnnotation(listOwner);
+ if (annotationFQN.equals(AnnotationUtil.NOT_NULL) || annotationFQN.equals(ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT)) {
+ PsiAnnotation[] annotations = findInferredAnnotations(listOwner);
+ for (PsiAnnotation annotation : annotations) {
+ if (annotationFQN.equals(annotation.getQualifiedName())) {
+ return annotation;
+ }
+ }
+ return null;
}
else {
return null;
@@ -156,26 +78,30 @@ public class ProjectBytecodeAnalysis {
}
@NotNull
- public PsiAnnotation[] findInferredAnnotations(@NotNull PsiModifierListOwner listOwner) {
+ public PsiAnnotation[] findInferredAnnotations(@NotNull final PsiModifierListOwner listOwner) {
if (!(listOwner instanceof PsiCompiledElement)) {
return PsiAnnotation.EMPTY_ARRAY;
}
- return collectInferredAnnotations(listOwner);
+ return CachedValuesManager.getCachedValue(listOwner, new CachedValueProvider<PsiAnnotation[]>() {
+ @Nullable
+ @Override
+ public Result<PsiAnnotation[]> compute() {
+ return Result.create(collectInferredAnnotations(listOwner), listOwner);
+ }
+ });
}
- // TODO the best way to synchronize?
@NotNull
- private synchronized PsiAnnotation[] collectInferredAnnotations(PsiModifierListOwner listOwner) {
- if (myAnnotations == null) {
- loadAnnotations();
- }
+ private PsiAnnotation[] collectInferredAnnotations(PsiModifierListOwner listOwner) {
try {
- int key = getKey(listOwner);
- if (key == -1) {
+ long ownerKey = getKey(listOwner);
+ if (ownerKey == -1) {
return PsiAnnotation.EMPTY_ARRAY;
}
- boolean notNull = myAnnotations.notNulls.contains(key);
- String contractValue = myAnnotations.contracts.get(key);
+ TLongArrayList allKeys = contractKeys(listOwner, ownerKey);
+ Annotations annotations = loadAnnotations(listOwner, ownerKey, allKeys);
+ boolean notNull = annotations.notNulls.contains(ownerKey);
+ String contractValue = annotations.contracts.get(ownerKey);
if (notNull && contractValue != null) {
return new PsiAnnotation[]{
@@ -201,6 +127,11 @@ public class ProjectBytecodeAnalysis {
LOG.debug(e);
return PsiAnnotation.EMPTY_ARRAY;
}
+ catch (EquationsLimitException e) {
+ String externalName = PsiFormatUtil.getExternalName(listOwner, false, Integer.MAX_VALUE);
+ LOG.info("Too many equations for " + externalName);
+ return PsiAnnotation.EMPTY_ARRAY;
+ }
}
private PsiAnnotation getNotNullAnnotation() {
@@ -213,54 +144,15 @@ public class ProjectBytecodeAnalysis {
});
}
- @Nullable
- private synchronized PsiAnnotation findNotNullAnnotation(PsiModifierListOwner listOwner) {
- if (myAnnotations == null) {
- loadAnnotations();
- }
- try {
- int key = getKey(listOwner);
- if (key == -1) {
- return null;
- }
- return myAnnotations.notNulls.contains(key) ? getNotNullAnnotation() : null;
- }
- catch (IOException e) {
- LOG.debug(e);
- return null;
- }
- }
-
- @Nullable
- private synchronized PsiAnnotation findContractAnnotation(PsiModifierListOwner listOwner) {
- if (myAnnotations == null) {
- loadAnnotations();
- }
- try {
- int key = getKey(listOwner);
- if (key == -1) {
- return null;
- }
- String contractValue = myAnnotations.contracts.get(key);
- return contractValue != null ? createContractAnnotation(contractValue) : null;
- }
- catch (IOException e) {
- LOG.debug(e);
- return null;
- }
- }
-
public PsiAnnotation createContractAnnotation(String contractValue) {
return createAnnotationFromText("@org.jetbrains.annotations.Contract(" + contractValue + ")");
}
- public static int getKey(@NotNull PsiModifierListOwner owner) throws IOException {
+ public static long getKey(@NotNull PsiModifierListOwner owner) throws IOException {
LOG.assertTrue(owner instanceof PsiCompiledElement, owner);
-
if (owner instanceof PsiMethod) {
return BytecodeAnalysisConverter.getInstance().mkPsiKey((PsiMethod)owner, new Out());
}
-
if (owner instanceof PsiParameter) {
PsiElement parent = owner.getParent();
if (parent instanceof PsiParameterList) {
@@ -275,6 +167,80 @@ public class ProjectBytecodeAnalysis {
return -1;
}
+ public static TLongArrayList contractKeys(@NotNull PsiModifierListOwner owner, long primaryKey) throws IOException {
+ if (owner instanceof PsiMethod) {
+ TLongArrayList result = BytecodeAnalysisConverter.getInstance().mkInOutKeys((PsiMethod)owner, primaryKey);
+ result.add(primaryKey);
+ return result;
+ }
+ TLongArrayList result = new TLongArrayList(1);
+ result.add(primaryKey);
+ return result;
+ }
+
+ private Annotations loadAnnotations(@NotNull PsiModifierListOwner owner, long key, TLongArrayList allKeys)
+ throws IOException, EquationsLimitException {
+ Annotations result = new Annotations();
+ if (owner instanceof PsiParameter) {
+ final Solver solver = new Solver(new ELattice<Value>(Value.NotNull, Value.Top));
+ collectEquations(allKeys, solver);
+ TLongObjectHashMap<Value> solutions = solver.solve();
+ BytecodeAnalysisConverter.getInstance().addParameterAnnotations(solutions, result);
+ } else if (owner instanceof PsiMethod) {
+ final Solver solver = new Solver(new ELattice<Value>(Value.Bot, Value.Top));
+ collectEquations(allKeys, solver);
+ TLongObjectHashMap<Value> solutions = solver.solve();
+ BytecodeAnalysisConverter.getInstance().addMethodAnnotations(solutions, result, key,
+ ((PsiMethod)owner).getParameterList().getParameters().length);
+ }
+ return result;
+ }
+
+ private void collectEquations(TLongArrayList keys, Solver solver) throws EquationsLimitException {
+ GlobalSearchScope librariesScope = ProjectScope.getLibrariesScope(myProject);
+ TLongHashSet queued = new TLongHashSet();
+ LongStack queue = new LongStack();
+
+ for (int i = 0; i < keys.size(); i++) {
+ long key = keys.get(i);
+ queue.push(key);
+ queued.add(key);
+ // stable/unstable
+ queue.push(-key);
+ queued.add(-key);
+ }
+
+ FileBasedIndex index = FileBasedIndex.getInstance();
+ while (!queue.empty()) {
+ if (queued.size() > EQUATIONS_LIMIT) {
+ throw new EquationsLimitException();
+ }
+ ProgressManager.checkCanceled();
+ List<IdEquation> equations = index.getValues(BytecodeAnalysisIndex.NAME, queue.pop(), librariesScope);
+ for (IdEquation equation : equations) {
+ IdResult rhs = equation.rhs;
+ solver.addEquation(equation);
+ if (rhs instanceof IdPending) {
+ IdPending intIdPending = (IdPending)rhs;
+ for (IntIdComponent component : intIdPending.delta) {
+ for (long depKey : component.ids) {
+ if (!queued.contains(depKey)) {
+ queue.push(depKey);
+ queued.add(depKey);
+ }
+ // stable/unstable
+ long swapped = -depKey;
+ if (!queued.contains(swapped)) {
+ queue.push(swapped);
+ queued.add(swapped);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
@NotNull
private PsiAnnotation createAnnotationFromText(@NotNull final String text) throws IncorrectOperationException {
PsiAnnotation annotation = JavaPsiFacade.getElementFactory(myProject).createAnnotationFromText(text, null);
@@ -285,7 +251,9 @@ public class ProjectBytecodeAnalysis {
class Annotations {
// @NotNull keys
- final TIntHashSet notNulls = new TIntHashSet();
+ final TLongHashSet notNulls = new TLongHashSet();
// @Contracts
- final TIntObjectHashMap<String> contracts = new TIntObjectHashMap<String>();
+ final TLongObjectHashMap<String> contracts = new TLongObjectHashMap<String>();
}
+
+class EquationsLimitException extends Exception {}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java
index 47c97790d102..1dec1de8a606 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java
@@ -15,10 +15,12 @@
*/
package com.intellij.codeInspection.bytecodeAnalysis;
-import com.intellij.util.containers.IntStack;
-import com.intellij.util.containers.IntToIntSetMap;
-import gnu.trove.TIntObjectHashMap;
+import com.intellij.util.containers.LongStack;
+import gnu.trove.TLongHashSet;
+import gnu.trove.TLongIterator;
+import gnu.trove.TLongObjectHashMap;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
import java.util.*;
@@ -49,9 +51,9 @@ final class ELattice<T extends Enum<T>> {
// component specialized for ints
final class IntIdComponent {
Value value;
- final int[] ids;
+ final long[] ids;
- IntIdComponent(Value value, int[] ids) {
+ IntIdComponent(Value value, long[] ids) {
this.value = value;
this.ids = ids;
}
@@ -74,7 +76,7 @@ final class IntIdComponent {
return value.ordinal() + Arrays.hashCode(ids);
}
- public boolean remove(int id) {
+ public boolean remove(long id) {
return IdUtils.remove(ids, id);
}
@@ -89,18 +91,18 @@ final class IntIdComponent {
class IdUtils {
// removed value
- static final int nullId = 0;
+ static final long nullId = 0;
- static boolean contains(int[] ids, int id) {
- for (int id1 : ids) {
+ static boolean contains(long[] ids, int id) {
+ for (long id1 : ids) {
if (id1 == id) return true;
}
return false;
}
- static boolean isEmpty(int[] ids) {
- for (int id : ids) {
+ static boolean isEmpty(long[] ids) {
+ for (long id : ids) {
if (id != nullId) return false;
}
return true;
@@ -117,7 +119,7 @@ class IdUtils {
return result;
}
- static boolean remove(int[] ids, int id) {
+ static boolean remove(long[] ids, long id) {
boolean removed = false;
for (int i = 0; i < ids.length; i++) {
if (ids[i] == id) {
@@ -137,7 +139,7 @@ class ResultUtil<Id, T extends Enum<T>> {
top = lattice.top;
}
- Result<Id, T> join(Result<Id, T> r1, Result<Id, T> r2) {
+ Result<Id, T> join(Result<Id, T> r1, Result<Id, T> r2) throws AnalyzerException {
if (r1 instanceof Final && ((Final) r1).value == top) {
return r1;
}
@@ -166,8 +168,19 @@ class ResultUtil<Id, T extends Enum<T>> {
Set<Product<Id, T>> sum = new HashSet<Product<Id, T>>();
sum.addAll(pending1.sum);
sum.addAll(pending2.sum);
+ checkLimit(sum);
return new Pending<Id, T>(sum);
}
+
+ private void checkLimit(Set<Product<Id, T>> sum) throws AnalyzerException {
+ int size = 0;
+ for (Product<Id, T> prod : sum) {
+ size += prod.ids.size();
+ }
+ if (size > Analysis.EQUATION_SIZE_LIMIT) {
+ throw new AnalyzerException(null, "Equation size is too big");
+ }
+ }
}
final class Product<K, V> {
@@ -222,11 +235,11 @@ final class Pending<Id, T> implements Result<Id, T> {
}
-interface IntIdResult {}
+interface IdResult {}
// this just wrapper, no need for this really
-final class IntIdFinal implements IntIdResult {
+final class IdFinal implements IdResult {
final Value value;
- public IntIdFinal(Value value) {
+ public IdFinal(Value value) {
this.value = value;
}
@@ -235,7 +248,7 @@ final class IntIdFinal implements IntIdResult {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- IntIdFinal that = (IntIdFinal)o;
+ IdFinal that = (IdFinal)o;
if (value != that.value) return false;
@@ -253,19 +266,19 @@ final class IntIdFinal implements IntIdResult {
}
}
-final class IntIdPending implements IntIdResult {
+final class IdPending implements IdResult {
final IntIdComponent[] delta;
- IntIdPending(IntIdComponent[] delta) {
+ IdPending(IntIdComponent[] delta) {
this.delta = delta;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (!(o instanceof IntIdPending)) return false;
- IntIdPending pending = (IntIdPending)o;
- return !Arrays.equals(delta, pending.delta);
+ if (!(o instanceof IdPending)) return false;
+ IdPending pending = (IdPending)o;
+ return Arrays.equals(delta, pending.delta);
}
@Override
@@ -273,20 +286,20 @@ final class IntIdPending implements IntIdResult {
return Arrays.hashCode(delta);
}
- IntIdPending copy() {
+ IdPending copy() {
IntIdComponent[] delta1 = new IntIdComponent[delta.length];
for (int i = 0; i < delta.length; i++) {
delta1[i] = delta[i].copy();
}
- return new IntIdPending(delta1);
+ return new IdPending(delta1);
}
}
-final class IntIdEquation {
- final int id;
- final IntIdResult rhs;
+final class IdEquation {
+ final long id;
+ final IdResult rhs;
- IntIdEquation(int id, IntIdResult rhs) {
+ IdEquation(long id, IdResult rhs) {
this.id = id;
this.rhs = rhs;
}
@@ -294,9 +307,9 @@ final class IntIdEquation {
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (!(o instanceof IntIdEquation)) return false;
+ if (!(o instanceof IdEquation)) return false;
- IntIdEquation equation = (IntIdEquation)o;
+ IdEquation equation = (IdEquation)o;
if (id != equation.id) return false;
if (!rhs.equals(equation.rhs)) return false;
@@ -306,9 +319,7 @@ final class IntIdEquation {
@Override
public int hashCode() {
- int result = id;
- result = 31 * result + rhs.hashCode();
- return result;
+ return 31 * ((int)(id ^ (id >>> 32))) + rhs.hashCode();
}
}
@@ -337,41 +348,46 @@ final class Equation<Id, T> {
}
}
-final class IntIdSolver {
+final class Solver {
private int size = 0;
private final ELattice<Value> lattice;
- private final IntToIntSetMap dependencies = new IntToIntSetMap(10000, 0.5f);
- private final TIntObjectHashMap<IntIdPending> pending = new TIntObjectHashMap<IntIdPending>();
- private final TIntObjectHashMap<Value> solved = new TIntObjectHashMap<Value>();
- private final IntStack moving = new IntStack();
+ private final TLongObjectHashMap<TLongHashSet> dependencies = new TLongObjectHashMap<TLongHashSet>();
+ private final TLongObjectHashMap<IdPending> pending = new TLongObjectHashMap<IdPending>();
+ private final TLongObjectHashMap<Value> solved = new TLongObjectHashMap<Value>();
+ private final LongStack moving = new LongStack();
int getSize() {
return size;
}
- IntIdSolver(ELattice<Value> lattice) {
+ Solver(ELattice<Value> lattice) {
this.lattice = lattice;
}
- void addEquation(IntIdEquation equation) {
+ void addEquation(IdEquation equation) {
size ++;
- IntIdResult rhs = equation.rhs;
- if (rhs instanceof IntIdFinal) {
- solved.put(equation.id, ((IntIdFinal) rhs).value);
+ IdResult rhs = equation.rhs;
+ if (rhs instanceof IdFinal) {
+ solved.put(equation.id, ((IdFinal) rhs).value);
moving.push(equation.id);
- } else if (rhs instanceof IntIdPending) {
- IntIdPending pendResult = ((IntIdPending)rhs).copy();
- IntIdResult norm = normalize(pendResult.delta);
- if (norm instanceof IntIdFinal) {
- solved.put(equation.id, ((IntIdFinal) norm).value);
+ } else if (rhs instanceof IdPending) {
+ IdPending pendResult = ((IdPending)rhs).copy();
+ IdResult norm = normalize(pendResult.delta);
+ if (norm instanceof IdFinal) {
+ solved.put(equation.id, ((IdFinal) norm).value);
moving.push(equation.id);
}
else {
- IntIdPending pendResult1 = ((IntIdPending)rhs).copy();
+ IdPending pendResult1 = ((IdPending)rhs).copy();
for (IntIdComponent component : pendResult1.delta) {
- for (int trigger : component.ids) {
- dependencies.addOccurence(trigger, equation.id);
+ for (long trigger : component.ids) {
+ TLongHashSet set = dependencies.get(trigger);
+ if (set == null) {
+ set = new TLongHashSet();
+ dependencies.put(trigger, set);
+ }
+ set.add(equation.id);
}
pending.put(equation.id, pendResult1);
}
@@ -379,31 +395,35 @@ final class IntIdSolver {
}
}
- TIntObjectHashMap<Value> solve() {
+ TLongObjectHashMap<Value> solve() {
while (!moving.empty()) {
- int id = moving.pop();
+ long id = moving.pop();
Value value = solved.get(id);
boolean stable = id > 0;
- int[] pIds = stable ? new int[]{id, -id} : new int[]{-id, id};
+ long[] pIds = stable ? new long[]{id, -id} : new long[]{-id, id};
Value[] pVals = stable ? new Value[]{value, value} : new Value[]{value, lattice.top};
for (int i = 0; i < pIds.length; i++) {
- int pId = pIds[i];
+ long pId = pIds[i];
Value pVal = pVals[i];
- // todo - remove
- int[] dIds = dependencies.get(pId);
- for (int dId : dIds) {
- IntIdPending pend = pending.remove(dId);
+ TLongHashSet dIds = dependencies.get(pId);
+ if (dIds == null) {
+ continue;
+ }
+ TLongIterator dIdsIterator = dIds.iterator();
+ while (dIdsIterator.hasNext()) {
+ long dId = dIdsIterator.next();
+ IdPending pend = pending.remove(dId);
if (pend != null) {
- IntIdResult pend1 = substitute(pend, pId, pVal);
- if (pend1 instanceof IntIdFinal) {
- IntIdFinal fi = (IntIdFinal)pend1;
+ IdResult pend1 = substitute(pend, pId, pVal);
+ if (pend1 instanceof IdFinal) {
+ IdFinal fi = (IdFinal)pend1;
solved.put(dId, fi.value);
moving.push(dId);
}
else {
- pending.put(dId, (IntIdPending)pend1);
+ pending.put(dId, (IdPending)pend1);
}
}
}
@@ -414,7 +434,7 @@ final class IntIdSolver {
}
// substitute id -> value into pending
- IntIdResult substitute(IntIdPending pending, int id, Value value) {
+ IdResult substitute(IdPending pending, long id, Value value) {
IntIdComponent[] sum = pending.delta;
for (IntIdComponent intIdComponent : sum) {
if (intIdComponent.remove(id)) {
@@ -424,7 +444,7 @@ final class IntIdSolver {
return normalize(sum);
}
- IntIdResult normalize(IntIdComponent[] sum) {
+ IdResult normalize(IntIdComponent[] sum) {
Value acc = lattice.bot;
boolean computableNow = true;
for (IntIdComponent prod : sum) {
@@ -434,7 +454,7 @@ final class IntIdSolver {
computableNow = false;
}
}
- return (acc == lattice.top || computableNow) ? new IntIdFinal(acc) : new IntIdPending(sum);
+ return (acc == lattice.top || computableNow) ? new IdFinal(acc) : new IdPending(sum);
}
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java
index a1c908837ad0..bd207e58a0fe 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java
@@ -71,7 +71,23 @@ class ContractInferenceInterpreter {
if (statements.length == 1) {
if (statements[0] instanceof PsiReturnStatement) {
List<MethodContract> result = handleDelegation(((PsiReturnStatement)statements[0]).getReturnValue(), false);
- if (result != null) return result;
+ if (result != null) {
+ PsiTypeElement typeElement = myMethod.getReturnTypeElement();
+ final boolean returningObject = typeElement == null || !(typeElement.getType() instanceof PsiClassType);
+ return ContainerUtil.findAll(result, new Condition<MethodContract>() {
+ @Override
+ public boolean value(MethodContract contract) {
+ if ((contract.returnValue == NULL_VALUE || contract.returnValue == NOT_NULL_VALUE) && returningObject) {
+ return false;
+ }
+ if ((contract.returnValue == TRUE_VALUE || contract.returnValue == FALSE_VALUE) && !returningObject) {
+ return false;
+ }
+
+ return true;
+ }
+ });
+ }
}
else if (statements[0] instanceof PsiExpressionStatement && ((PsiExpressionStatement)statements[0]).getExpression() instanceof PsiMethodCallExpression) {
List<MethodContract> result = handleDelegation(((PsiExpressionStatement)statements[0]).getExpression(), false);
@@ -132,6 +148,9 @@ class ContractInferenceInterpreter {
}
else {
answer = withConstraint(answer, paramIndex, argConstraint);
+ if (answer == null) {
+ return null;
+ }
}
}
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java
index 691c2f00d985..1167210f917e 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java
@@ -89,10 +89,16 @@ public class MethodContract {
throw new ParseException("A contract clause must be in form arg1, ..., argN -> return-value");
}
- String[] argStrings = clause.substring(0, arrowIndex).split(",");
- ValueConstraint[] args = new ValueConstraint[argStrings.length];
- for (int i = 0; i < args.length; i++) {
- args[i] = parseConstraint(argStrings[i]);
+ String beforeArrow = clause.substring(0, arrowIndex);
+ ValueConstraint[] args;
+ if (StringUtil.isNotEmpty(beforeArrow)) {
+ String[] argStrings = beforeArrow.split(",");
+ args = new ValueConstraint[argStrings.length];
+ for (int i = 0; i < args.length; i++) {
+ args[i] = parseConstraint(argStrings[i]);
+ }
+ } else {
+ args = new ValueConstraint[0];
}
result.add(new MethodContract(args, parseConstraint(clause.substring(arrowIndex + arrow.length()))));
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java
index 013e24cbe07a..36449128b494 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java
@@ -500,21 +500,19 @@ public class StandardInstructionVisitor extends InstructionVisitor {
PsiType varType = var.getVariableType();
if (!(varType instanceof PsiPrimitiveType)) return null;
+
+ if (varType == PsiType.FLOAT || varType == PsiType.DOUBLE) return null;
double minValue = varType == PsiType.BYTE ? Byte.MIN_VALUE :
varType == PsiType.SHORT ? Short.MIN_VALUE :
varType == PsiType.INT ? Integer.MIN_VALUE :
varType == PsiType.CHAR ? Character.MIN_VALUE :
- varType == PsiType.LONG ? Long.MIN_VALUE :
- varType == PsiType.FLOAT ? Float.MIN_VALUE :
- Double.MIN_VALUE;
+ Long.MIN_VALUE;
double maxValue = varType == PsiType.BYTE ? Byte.MAX_VALUE :
varType == PsiType.SHORT ? Short.MAX_VALUE :
varType == PsiType.INT ? Integer.MAX_VALUE :
varType == PsiType.CHAR ? Character.MAX_VALUE :
- varType == PsiType.LONG ? Long.MAX_VALUE :
- varType == PsiType.FLOAT ? Float.MAX_VALUE :
- Double.MAX_VALUE;
+ Long.MAX_VALUE;
return checkComparisonWithKnownRange(instruction, runner, memState, opSign, comparedWith, minValue, maxValue);
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/xml/DeprecatedClassUsageInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/xml/DeprecatedClassUsageInspection.java
index 0a914c0d4477..8c9e4a72dfef 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/xml/DeprecatedClassUsageInspection.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/xml/DeprecatedClassUsageInspection.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.codeInspection.XmlSuppressableInspectionTool;
import com.intellij.codeInspection.deprecation.DeprecationInspection;
import com.intellij.psi.*;
-import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
@@ -46,11 +45,6 @@ public class DeprecatedClassUsageInspection extends XmlSuppressableInspectionToo
}
@Override
- public void visitXmlAttribute(XmlAttribute attribute) {
- checkReferences(attribute, holder);
- }
-
- @Override
public void visitXmlAttributeValue(XmlAttributeValue value) {
checkReferences(value, holder);
}