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 +