diff --git a/java/java-analysis-impl/java-analysis-impl.iml b/java/java-analysis-impl/java-analysis-impl.iml
index 95c129baadfb..28987be6d459 100644
--- a/java/java-analysis-impl/java-analysis-impl.iml
+++ b/java/java-analysis-impl/java-analysis-impl.iml
@@ -18,6 +18,7 @@
+
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
new file mode 100644
index 000000000000..195687908601
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java
@@ -0,0 +1,74 @@
+/*
+ * 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.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.components.ApplicationComponent;
+import com.intellij.util.io.PersistentStringEnumerator;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * @author lambdamix
+ */
+public class BytecodeAnalysisConverter extends ApplicationComponent.Adapter {
+
+ public static BytecodeAnalysisConverter getInstance() {
+ return ApplicationManager.getApplication().getComponent(BytecodeAnalysisConverter.class);
+ }
+
+ PersistentStringEnumerator internalKeyEnumerator;
+ private final File myFile = new File(PathManager.getIndexRoot(), "faba.internalIds");
+
+ public BytecodeAnalysisConverter() {
+ try {
+ internalKeyEnumerator = new PersistentStringEnumerator(myFile);
+ }
+ catch (IOException e) {
+ //e.printStackTrace();
+ }
+ }
+
+ IntIdEquation enumerate(Equation equation) throws IOException {
+ Result rhs = equation.rhs;
+ IntIdResult result;
+ if (rhs instanceof Final) {
+ result = new IntIdFinal(((Final)rhs).value);
+ } else {
+ Pending pending = (Pending)rhs;
+ Set> deltaOrig = pending.delta;
+ IntIdComponent[] components = new IntIdComponent[deltaOrig.size()];
+ int componentI = 0;
+ for (Set keyComponent : deltaOrig) {
+ int[] ids = new int[keyComponent.size()];
+ int idI = 0;
+ for (Key id : keyComponent) {
+ ids[idI] = internalKeyEnumerator.enumerate(Util.internalKeyString(id));
+ idI++;
+ }
+ IntIdComponent intIdComponent = new IntIdComponent(ids);
+ components[componentI] = intIdComponent;
+ componentI++;
+ }
+ result = new IntIdPending(pending.infinum, components);
+ }
+ int key = internalKeyEnumerator.enumerate(Util.internalKeyString(equation.id));
+ return new IntIdEquation(key, result);
+ }
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisHandler.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisHandler.java
index 922697091278..26bf2127a56f 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisHandler.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisHandler.java
@@ -65,8 +65,6 @@ public class BytecodeAnalysisHandler extends AbstractProjectComponent {
private final PsiManager myPsiManager;
- private final Enumerators myEnumerators;
-
private MostlySingularMultiMap myAnnotations = new MostlySingularMultiMap();
public BytecodeAnalysisHandler(Project project, PsiManager psiManager) {
@@ -86,15 +84,6 @@ public class BytecodeAnalysisHandler extends AbstractProjectComponent {
doIndex();
}
});
- Enumerators enumerators;
- try {
- enumerators = new Enumerators(getEnumerator(project, "internalKeys"), getEnumerator(project, "annotationKeys"));
- }
- catch (IOException e) {
- LOG.error("Cannot initialize enumerators", e);
- enumerators = null;
- }
- myEnumerators = enumerators;
}
void setAnnotations(MostlySingularMultiMap annotations) {
@@ -194,9 +183,7 @@ public class BytecodeAnalysisHandler extends AbstractProjectComponent {
}
private void doIndex() {
- if (myEnumerators != null) {
- DumbService.getInstance(myProject).queueTask(new BytecodeAnalysisTask(myProject, myEnumerators));
- }
+ DumbService.getInstance(myProject).queueTask(new BytecodeAnalysisTask(myProject));
}
}
@@ -245,29 +232,13 @@ class AnnotationData {
}
}
-// TODO - this should application-level
-class Enumerators {
- @NotNull
- final PersistentStringEnumerator internalKeyEnumerator;
- @NotNull
- final PersistentStringEnumerator annotationKeyEnumerator;
-
- Enumerators(@NotNull PersistentStringEnumerator internalKeyEnumerator,
- @NotNull PersistentStringEnumerator annotationKeyEnumerator) {
- this.internalKeyEnumerator = internalKeyEnumerator;
- this.annotationKeyEnumerator = annotationKeyEnumerator;
- }
-}
-
class BytecodeAnalysisTask extends DumbModeTask {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisTask");
private final Project myProject;
private long myClassFilesCount = 0;
- private final Enumerators myEnumerators;
- BytecodeAnalysisTask(Project project, Enumerators enumerators) {
+ BytecodeAnalysisTask(Project project) {
myProject = project;
- myEnumerators = enumerators;
}
private VirtualFileVisitor> myClassFilesCounter = new VirtualFileVisitor() {
@@ -299,11 +270,11 @@ class BytecodeAnalysisTask extends DumbModeTask {
}
LOG.info("Found " + myClassFilesCount + " classes to Index");
BytecodeAnalysisHandler handler = myProject.getComponent(BytecodeAnalysisHandler.class);
- ClassProcessor myClassProcessor = new ClassProcessor(indicator, myClassFilesCount, myEnumerators);
+ ClassFileProcessor myClassFileProcessor = new ClassFileProcessor(indicator, myClassFilesCount);
for (VirtualFile classRoot : classRoots) {
- VfsUtilCore.visitChildrenRecursively(classRoot, myClassProcessor);
+ VfsUtilCore.visitChildrenRecursively(classRoot, myClassFileProcessor);
}
- handler.setAnnotations(myClassProcessor.annotations());
+ handler.setAnnotations(myClassFileProcessor.annotations());
}
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
new file mode 100644
index 000000000000..b2e67303de7b
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java
@@ -0,0 +1,74 @@
+/*
+ * 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.openapi.fileTypes.StdFileTypes;
+import com.intellij.util.indexing.*;
+import com.intellij.util.io.DataExternalizer;
+import com.intellij.util.io.EnumeratorIntegerDescriptor;
+import com.intellij.util.io.KeyDescriptor;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+/**
+ * @author lambdamix
+ */
+public class BytecodeAnalysisIndex extends FileBasedIndexExtension> {
+ public static final int KEY = 0;
+ public static final ID> NAME = ID.create("bytecodeAnalysis");
+ private final EquationExternalizer myExternalizer = new EquationExternalizer();
+
+ @NotNull
+ @Override
+ public ID> getName() {
+ return NAME;
+ }
+
+ @NotNull
+ @Override
+ public DataIndexer, FileContent> getIndexer() {
+ return null;
+ }
+
+ @NotNull
+ @Override
+ public KeyDescriptor getKeyDescriptor() {
+ return EnumeratorIntegerDescriptor.INSTANCE;
+ }
+
+ @NotNull
+ @Override
+ public DataExternalizer> getValueExternalizer() {
+ return myExternalizer;
+ }
+
+ @NotNull
+ @Override
+ public FileBasedIndex.InputFilter getInputFilter() {
+ return new DefaultFileTypeSpecificInputFilter(StdFileTypes.CLASS);
+ }
+
+ @Override
+ public boolean dependsOnFileContent() {
+ return true;
+ }
+
+ @Override
+ public int getVersion() {
+ return 0;
+ }
+}
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
new file mode 100644
index 000000000000..0eb6817ed47a
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java
@@ -0,0 +1,56 @@
+/*
+ * 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.util.indexing.DataIndexer;
+import com.intellij.util.indexing.FileContent;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.org.objectweb.asm.ClassReader;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @author lambdamix
+ */
+public class ClassDataIndexer implements DataIndexer, FileContent> {
+ final BytecodeAnalysisConverter myLowering;
+
+ public ClassDataIndexer(BytecodeAnalysisConverter lowering) {
+ myLowering = lowering;
+ }
+
+ @NotNull
+ @Override
+ public Map> map(@NotNull FileContent inputData) {
+ ArrayList> rawEquations = ClassProcessing.processClass(new ClassReader(inputData.getContent()));
+ Collection idEquations = new ArrayList(rawEquations.size());
+ for (Equation rawEquation : rawEquations) {
+ try {
+ IntIdEquation idEquation = myLowering.enumerate(rawEquation);
+ idEquations.add(idEquation);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return Collections.singletonMap(BytecodeAnalysisIndex.KEY, idEquations);
+ }
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassFileProcessor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassFileProcessor.java
new file mode 100644
index 000000000000..d34655b45059
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassFileProcessor.java
@@ -0,0 +1,81 @@
+/*
+ * 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.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileVisitor;
+import com.intellij.util.containers.MostlySingularMultiMap;
+import gnu.trove.TIntObjectHashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.org.objectweb.asm.ClassReader;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class ClassFileProcessor extends VirtualFileVisitor {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.bytecodeAnalysis.ClassProcessor");
+
+ final static ELattice valueLattice = new ELattice(Value.Bot, Value.Top);
+ final IntIdSolver myIntIdSolver;
+ final BytecodeAnalysisConverter myLowering;
+
+ @NotNull
+ final ProgressIndicator myProgressIndicator;
+ private final long totalClassFiles;
+ long processed = 0;
+
+ public ClassFileProcessor(@NotNull ProgressIndicator indicator, long totalClassFiles) {
+ this.myProgressIndicator = indicator;
+ this.totalClassFiles = totalClassFiles;
+ myLowering = BytecodeAnalysisConverter.getInstance();
+ myIntIdSolver = new IntIdSolver(valueLattice);
+ }
+
+ @Override
+ public boolean visitFile(@NotNull VirtualFile file) {
+ if (!file.isDirectory() && "class".equals(file.getExtension())) {
+ try {
+ ArrayList> equations = ClassProcessing.processClass(new ClassReader(file.contentsToByteArray()));
+ for (Equation equation : equations) {
+ addEquation(equation);
+ }
+ }
+ catch (IOException e) {
+ LOG.debug("Error when processing " + file.getPresentableUrl(), e);
+ }
+ myProgressIndicator.setText2(file.getPresentableUrl());
+ myProgressIndicator.setFraction(((double) processed++) / totalClassFiles);
+ }
+ return true;
+ }
+
+ void addEquation(Equation equation) {
+ try {
+ myIntIdSolver.addEquation(myLowering.enumerate(equation));
+ }
+ catch (IOException e) {
+ // TODO
+ }
+ }
+
+ // nullity, contracts
+ MostlySingularMultiMap annotations() {
+ TIntObjectHashMap internalIdSolutions = myIntIdSolver.solve();
+ return Util.makeAnnotations(internalIdSolutions);
+ }
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassProcessing.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassProcessing.java
new file mode 100644
index 000000000000..4c1fba2d3fc2
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassProcessing.java
@@ -0,0 +1,135 @@
+/*
+ * 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.openapi.diagnostic.Logger;
+import org.jetbrains.org.objectweb.asm.*;
+import org.jetbrains.org.objectweb.asm.tree.MethodNode;
+import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author lambdamix
+ */
+public class ClassProcessing {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.bytecodeAnalysis.ClassProcessing");
+
+ public static ArrayList> processClass(final ClassReader classReader) {
+ final ArrayList> result = new ArrayList>();
+ classReader.accept(new ClassVisitor(Opcodes.ASM5) {
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+ final MethodNode node = new MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions);
+ return new MethodVisitor(Opcodes.ASM5, node) {
+ @Override
+ public void visitEnd() {
+ super.visitEnd();
+ processMethod(classReader.getClassName(), node);
+ }
+ };
+ }
+
+ void processMethod(String className, MethodNode methodNode) {
+ Method method = new Method(className, methodNode.name, methodNode.desc);
+
+ ControlFlowGraph graph = cfg.buildControlFlowGraph(className, methodNode);
+ boolean added = false;
+ Type[] argumentTypes = Type.getArgumentTypes(methodNode.desc);
+ Type resultType = Type.getReturnType(methodNode.desc);
+ int resultSort = resultType.getSort();
+
+ boolean isReferenceResult = resultSort == Type.OBJECT || resultSort == Type.ARRAY;
+ boolean isBooleanResult = Type.BOOLEAN_TYPE == resultType;
+
+ if (graph.transitions.length > 0) {
+ DFSTree dfs = cfg.buildDFSTree(graph.transitions);
+ boolean reducible = dfs.back.isEmpty() || cfg.reducible(graph, dfs);
+ if (reducible) {
+ List> toAdd = new LinkedList>();
+ try {
+ for (int i = 0; i < argumentTypes.length; i++) {
+ Type argType = argumentTypes[i];
+ int argSort = argType.getSort();
+ boolean isReferenceArg = argSort == Type.OBJECT || argSort == Type.ARRAY;
+ boolean isBooleanArg = Type.BOOLEAN_TYPE.equals(argType);
+ if (isReferenceArg) {
+ toAdd.add(new NonNullInAnalysis(new RichControlFlow(graph, dfs), new In(i)).analyze());
+ }
+ if (isReferenceResult || isBooleanResult) {
+ if (isReferenceArg) {
+ toAdd.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.Null)).analyze());
+ toAdd.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.NotNull)).analyze());
+ }
+ if (isBooleanArg) {
+ toAdd.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.False)).analyze());
+ toAdd.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.True)).analyze());
+ }
+ }
+ }
+ if (isReferenceResult) {
+ toAdd.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new Out()).analyze());
+ }
+ added = true;
+ for (Equation equation : toAdd) {
+ addEquation(equation);
+ }
+ } catch (AnalyzerException e) {
+ throw new RuntimeException();
+ }
+ } else {
+ LOG.debug("CFG for " + method + " is not reducible");
+ }
+ }
+
+ if (!added) {
+ method = new Method(className, methodNode.name, methodNode.desc);
+ for (int i = 0; i < argumentTypes.length; i++) {
+ Type argType = argumentTypes[i];
+ int argSort = argType.getSort();
+ boolean isReferenceArg = argSort == Type.OBJECT || argSort == Type.ARRAY;
+
+ if (isReferenceArg) {
+ addEquation(new Equation(new Key(method, new In(i)), new Final(Value.Top)));
+ if (isReferenceResult || isBooleanResult) {
+ addEquation(new Equation(new Key(method, new InOut(i, Value.Null)), new Final(Value.Top)));
+ addEquation(new Equation(new Key(method, new InOut(i, Value.NotNull)), new Final(Value.Top)));
+ }
+ }
+ if (Type.BOOLEAN_TYPE.equals(argType)) {
+ if (isReferenceResult || isBooleanResult) {
+ addEquation(new Equation(new Key(method, new InOut(i, Value.False)), new Final(Value.Top)));
+ addEquation(new Equation(new Key(method, new InOut(i, Value.True)), new Final(Value.Top)));
+ }
+ }
+ }
+ if (isReferenceResult) {
+ addEquation(new Equation(new Key(method, new Out()), new Final(Value.Top)));
+ }
+ }
+ }
+
+ void addEquation(Equation equation) {
+ result.add(equation);
+ }
+ }, 0);
+
+ return result;
+ }
+
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassProcessor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassProcessor.java
deleted file mode 100644
index 48975758e703..000000000000
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassProcessor.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * 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.openapi.diagnostic.Logger;
-import com.intellij.openapi.progress.ProgressIndicator;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.openapi.vfs.VirtualFileVisitor;
-import com.intellij.util.containers.MostlySingularMultiMap;
-import gnu.trove.TIntObjectHashMap;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.org.objectweb.asm.*;
-import org.jetbrains.org.objectweb.asm.tree.MethodNode;
-import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
-
-import java.io.IOException;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-public class ClassProcessor extends VirtualFileVisitor {
- private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.bytecodeAnalysis.ClassProcessor");
-
- final static ELattice valueLattice = new ELattice(Value.Bot, Value.Top);
- final IntIdSolver myIntIdSolver;
-
- @NotNull
- final ProgressIndicator myProgressIndicator;
- private final long totalClassFiles;
- private final Enumerators myEnumerators;
- long processed = 0;
-
- public ClassProcessor(@NotNull ProgressIndicator indicator, long totalClassFiles, Enumerators enumerators) {
- this.myProgressIndicator = indicator;
- this.totalClassFiles = totalClassFiles;
- this.myEnumerators = enumerators;
- myIntIdSolver = new IntIdSolver(valueLattice);
- }
-
- @Override
- public boolean visitFile(@NotNull VirtualFile file) {
- if (!file.isDirectory() && "class".equals(file.getExtension())) {
- try {
- processClass(new ClassReader(file.contentsToByteArray()));
- }
- catch (IOException e) {
- LOG.debug("Error when processing " + file.getPresentableUrl(), e);
- }
- myProgressIndicator.setText2(file.getPresentableUrl());
- myProgressIndicator.setFraction(((double) processed++) / totalClassFiles);
- }
- return true;
- }
-
- public void processClass(final ClassReader classReader) {
- classReader.accept(new ClassVisitor(Opcodes.ASM5) {
- @Override
- public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
- final MethodNode node = new MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions);
- return new MethodVisitor(Opcodes.ASM5, node) {
- @Override
- public void visitEnd() {
- super.visitEnd();
- processMethod(classReader.getClassName(), node);
- }
- };
- }
- }, 0);
- }
-
- void processMethod(String className, MethodNode methodNode) {
- Method method = new Method(className, methodNode.name, methodNode.desc);
-
- ControlFlowGraph graph = cfg.buildControlFlowGraph(className, methodNode);
- boolean added = false;
- Type[] argumentTypes = Type.getArgumentTypes(methodNode.desc);
- Type resultType = Type.getReturnType(methodNode.desc);
- int resultSort = resultType.getSort();
-
- boolean isReferenceResult = resultSort == Type.OBJECT || resultSort == Type.ARRAY;
- boolean isBooleanResult = Type.BOOLEAN_TYPE == resultType;
-
- if (graph.transitions.length > 0) {
- DFSTree dfs = cfg.buildDFSTree(graph.transitions);
- boolean reducible = dfs.back.isEmpty() || cfg.reducible(graph, dfs);
- if (reducible) {
- List> toAdd = new LinkedList>();
- try {
- for (int i = 0; i < argumentTypes.length; i++) {
- Type argType = argumentTypes[i];
- int argSort = argType.getSort();
- boolean isReferenceArg = argSort == Type.OBJECT || argSort == Type.ARRAY;
- boolean isBooleanArg = Type.BOOLEAN_TYPE.equals(argType);
- if (isReferenceArg) {
- toAdd.add(new NonNullInAnalysis(new RichControlFlow(graph, dfs), new In(i)).analyze());
- }
- if (isReferenceResult || isBooleanResult) {
- if (isReferenceArg) {
- toAdd.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.Null)).analyze());
- toAdd.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.NotNull)).analyze());
- }
- if (isBooleanArg) {
- toAdd.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.False)).analyze());
- toAdd.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.True)).analyze());
- }
- }
- }
- if (isReferenceResult) {
- toAdd.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new Out()).analyze());
- }
- added = true;
- for (Equation equation : toAdd) {
- addEquation(equation);
- }
- } catch (AnalyzerException e) {
- throw new RuntimeException();
- }
- } else {
- LOG.debug("CFG for " + method + " is not reducible");
- }
- }
-
- if (!added) {
- method = new Method(className, methodNode.name, methodNode.desc);
- for (int i = 0; i < argumentTypes.length; i++) {
- Type argType = argumentTypes[i];
- int argSort = argType.getSort();
- boolean isReferenceArg = argSort == Type.OBJECT || argSort == Type.ARRAY;
-
- if (isReferenceArg) {
- addEquation(new Equation(new Key(method, new In(i)), new Final(Value.Top)));
- if (isReferenceResult || isBooleanResult) {
- addEquation(new Equation(new Key(method, new InOut(i, Value.Null)), new Final(Value.Top)));
- addEquation(new Equation(new Key(method, new InOut(i, Value.NotNull)), new Final(Value.Top)));
- }
- }
- if (Type.BOOLEAN_TYPE.equals(argType)) {
- if (isReferenceResult || isBooleanResult) {
- addEquation(new Equation(new Key(method, new InOut(i, Value.False)), new Final(Value.Top)));
- addEquation(new Equation(new Key(method, new InOut(i, Value.True)), new Final(Value.Top)));
- }
- }
- }
- if (isReferenceResult) {
- addEquation(new Equation(new Key(method, new Out()), new Final(Value.Top)));
- }
- }
- }
-
- void addEquation(Equation equation) {
- try {
- myIntIdSolver.addEquation(enumerate(equation));
- }
- catch (IOException e) {
- // TODO
- }
- }
-
- // turns high-lever equation into low-level one
- IntIdEquation enumerate(Equation equation) throws IOException {
- com.intellij.codeInspection.bytecodeAnalysis.Result rhs = equation.rhs;
- IntIdResult result;
- if (rhs instanceof Final) {
- result = new IntIdFinal(((Final)rhs).value);
- } else {
- Pending pending = (Pending)rhs;
- Set> deltaOrig = pending.delta;
- IntIdComponent[] components = new IntIdComponent[deltaOrig.size()];
- int componentI = 0;
- for (Set keyComponent : deltaOrig) {
- int[] ids = new int[keyComponent.size()];
- int idI = 0;
- for (Key id : keyComponent) {
- ids[idI] = myEnumerators.internalKeyEnumerator.enumerate(Util.internalKeyString(id));
- idI++;
- }
- IntIdComponent intIdComponent = new IntIdComponent(ids);
- components[componentI] = intIdComponent;
- componentI++;
- }
- result = new IntIdPending(pending.infinum, components);
- }
- int key = myEnumerators.internalKeyEnumerator.enumerate(Util.internalKeyString(equation.id));
- return new IntIdEquation(key, result);
- }
-
- // nullity, contracts
- MostlySingularMultiMap annotations() {
- TIntObjectHashMap internalIdSolutions = myIntIdSolver.solve();
- return Util.makeAnnotations(internalIdSolutions, myEnumerators);
- }
-}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/EquationExternalizer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/EquationExternalizer.java
new file mode 100644
index 000000000000..5bbd985e3d6d
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/EquationExternalizer.java
@@ -0,0 +1,89 @@
+/*
+ * 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.util.io.DataExternalizer;
+import com.intellij.util.io.DataInputOutputUtil;
+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 EquationExternalizer implements DataExternalizer> {
+ @Override
+ public void save(@NotNull DataOutput out, Collection equations) throws IOException {
+ DataInputOutputUtil.writeINT(out, equations.size());
+ for (IntIdEquation equation : equations) {
+ out.write(equation.id);
+ IntIdResult rhs = equation.rhs;
+ if (rhs instanceof IntIdFinal) {
+ IntIdFinal finalResult = (IntIdFinal)rhs;
+ out.writeBoolean(true);
+ DataInputOutputUtil.writeINT(out, finalResult.value.ordinal());
+ } else {
+ IntIdPending pendResult = (IntIdPending)rhs;
+ out.writeBoolean(false);
+ DataInputOutputUtil.writeINT(out, pendResult.infinum.ordinal());
+ out.writeInt(pendResult.delta.length);
+ for (IntIdComponent component : pendResult.delta) {
+ int[] ids = component.ids;
+ DataInputOutputUtil.writeINT(out, ids.length);
+ for (int id : ids) {
+ out.writeInt(id);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public Collection read(@NotNull DataInput in) throws IOException {
+ int size = DataInputOutputUtil.readINT(in);
+ ArrayList result = new ArrayList(size);
+
+ for (int x = 0; x < size; x++) {
+ int equationId = in.readInt();
+ boolean isFinal = in.readBoolean();
+ if (isFinal) {
+ int ordinal = DataInputOutputUtil.readINT(in);
+ Value value = Value.values()[ordinal];
+ result.add(new IntIdEquation(equationId, new IntIdFinal(value)));
+ } else {
+ int ordinal = DataInputOutputUtil.readINT(in);
+ Value value = Value.values()[ordinal];
+ int componentsNumber = DataInputOutputUtil.readINT(in);
+ IntIdComponent[] components = new IntIdComponent[componentsNumber];
+ for (int i = 0; i < componentsNumber; i++) {
+ 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(ids);
+ }
+ result.add(new IntIdEquation(equationId, new IntIdPending(value, components)));
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Util.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Util.java
index bd52c019cca4..e55871e18b47 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Util.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Util.java
@@ -36,8 +36,8 @@ public class Util {
}
}
- public static MostlySingularMultiMap makeAnnotations(TIntObjectHashMap internalIdSolutions,
- Enumerators enumerators) {
+ public static MostlySingularMultiMap makeAnnotations(TIntObjectHashMap internalIdSolutions) {
+ BytecodeAnalysisConverter lowering = BytecodeAnalysisConverter.getInstance();
MostlySingularMultiMap annotations = new MostlySingularMultiMap();
HashMap contracts = new HashMap();
TIntObjectIterator iterator = internalIdSolutions.iterator();
@@ -50,7 +50,7 @@ public class Util {
}
InternalKey key;
try {
- String s = enumerators.internalKeyEnumerator.valueOf(inKey);
+ String s = lowering.internalKeyEnumerator.valueOf(inKey);
key = readInternalKey(s);
}
catch (IOException e) {
diff --git a/platform/platform-resources/src/componentSets/Lang.xml b/platform/platform-resources/src/componentSets/Lang.xml
index 456b12a4080e..c253aed8e6e2 100644
--- a/platform/platform-resources/src/componentSets/Lang.xml
+++ b/platform/platform-resources/src/componentSets/Lang.xml
@@ -37,6 +37,10 @@
com.intellij.codeInsight.preview.ImageOrColorPreviewManager
+
+
+ com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisConverter
+