mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
towards indexing
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
<orderEntry type="module" module-name="resources-en" />
|
||||
<orderEntry type="module" module-name="xml-psi-impl" />
|
||||
<orderEntry type="library" exported="" name="asm5" level="project" />
|
||||
<orderEntry type="module" module-name="platform-api" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
|
||||
@@ -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<Key, Value> equation) throws IOException {
|
||||
Result<Key, Value> rhs = equation.rhs;
|
||||
IntIdResult result;
|
||||
if (rhs instanceof Final) {
|
||||
result = new IntIdFinal(((Final<Key, Value>)rhs).value);
|
||||
} else {
|
||||
Pending<Key, Value> pending = (Pending<Key, Value>)rhs;
|
||||
Set<Set<Key>> deltaOrig = pending.delta;
|
||||
IntIdComponent[] components = new IntIdComponent[deltaOrig.size()];
|
||||
int componentI = 0;
|
||||
for (Set<Key> 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);
|
||||
}
|
||||
}
|
||||
@@ -65,8 +65,6 @@ public class BytecodeAnalysisHandler extends AbstractProjectComponent {
|
||||
|
||||
private final PsiManager myPsiManager;
|
||||
|
||||
private final Enumerators myEnumerators;
|
||||
|
||||
private MostlySingularMultiMap<String, AnnotationData> myAnnotations = new MostlySingularMultiMap<String, AnnotationData>();
|
||||
|
||||
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<String, AnnotationData> 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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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<Integer, Collection<IntIdEquation>> {
|
||||
public static final int KEY = 0;
|
||||
public static final ID<Integer, Collection<IntIdEquation>> NAME = ID.create("bytecodeAnalysis");
|
||||
private final EquationExternalizer myExternalizer = new EquationExternalizer();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ID<Integer, Collection<IntIdEquation>> getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DataIndexer<Integer, Collection<IntIdEquation>, FileContent> getIndexer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public KeyDescriptor<Integer> getKeyDescriptor() {
|
||||
return EnumeratorIntegerDescriptor.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DataExternalizer<Collection<IntIdEquation>> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<Integer, Collection<IntIdEquation>, FileContent> {
|
||||
final BytecodeAnalysisConverter myLowering;
|
||||
|
||||
public ClassDataIndexer(BytecodeAnalysisConverter lowering) {
|
||||
myLowering = lowering;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<Integer, Collection<IntIdEquation>> map(@NotNull FileContent inputData) {
|
||||
ArrayList<Equation<Key, Value>> rawEquations = ClassProcessing.processClass(new ClassReader(inputData.getContent()));
|
||||
Collection<IntIdEquation> idEquations = new ArrayList<IntIdEquation>(rawEquations.size());
|
||||
for (Equation<Key, Value> rawEquation : rawEquations) {
|
||||
try {
|
||||
IntIdEquation idEquation = myLowering.enumerate(rawEquation);
|
||||
idEquations.add(idEquation);
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.singletonMap(BytecodeAnalysisIndex.KEY, idEquations);
|
||||
}
|
||||
}
|
||||
@@ -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<Value> valueLattice = new ELattice<Value>(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<Equation<Key, Value>> equations = ClassProcessing.processClass(new ClassReader(file.contentsToByteArray()));
|
||||
for (Equation<Key, Value> 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<Key, Value> equation) {
|
||||
try {
|
||||
myIntIdSolver.addEquation(myLowering.enumerate(equation));
|
||||
}
|
||||
catch (IOException e) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
// nullity, contracts
|
||||
MostlySingularMultiMap<String, AnnotationData> annotations() {
|
||||
TIntObjectHashMap<Value> internalIdSolutions = myIntIdSolver.solve();
|
||||
return Util.makeAnnotations(internalIdSolutions);
|
||||
}
|
||||
}
|
||||
@@ -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<Equation<Key, Value>> processClass(final ClassReader classReader) {
|
||||
final ArrayList<Equation<Key, Value>> result = new ArrayList<Equation<Key, Value>>();
|
||||
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<Equation<Key, Value>> toAdd = new LinkedList<Equation<Key, Value>>();
|
||||
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<Key, Value> 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<Key, Value>(new Key(method, new In(i)), new Final<Key, Value>(Value.Top)));
|
||||
if (isReferenceResult || isBooleanResult) {
|
||||
addEquation(new Equation<Key, Value>(new Key(method, new InOut(i, Value.Null)), new Final<Key, Value>(Value.Top)));
|
||||
addEquation(new Equation<Key, Value>(new Key(method, new InOut(i, Value.NotNull)), new Final<Key, Value>(Value.Top)));
|
||||
}
|
||||
}
|
||||
if (Type.BOOLEAN_TYPE.equals(argType)) {
|
||||
if (isReferenceResult || isBooleanResult) {
|
||||
addEquation(new Equation<Key, Value>(new Key(method, new InOut(i, Value.False)), new Final<Key, Value>(Value.Top)));
|
||||
addEquation(new Equation<Key, Value>(new Key(method, new InOut(i, Value.True)), new Final<Key, Value>(Value.Top)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isReferenceResult) {
|
||||
addEquation(new Equation<Key, Value>(new Key(method, new Out()), new Final<Key, Value>(Value.Top)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addEquation(Equation<Key, Value> equation) {
|
||||
result.add(equation);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Value> valueLattice = new ELattice<Value>(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<Equation<Key, Value>> toAdd = new LinkedList<Equation<Key, Value>>();
|
||||
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<Key, Value> 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<Key, Value>(new Key(method, new In(i)), new Final<Key, Value>(Value.Top)));
|
||||
if (isReferenceResult || isBooleanResult) {
|
||||
addEquation(new Equation<Key, Value>(new Key(method, new InOut(i, Value.Null)), new Final<Key, Value>(Value.Top)));
|
||||
addEquation(new Equation<Key, Value>(new Key(method, new InOut(i, Value.NotNull)), new Final<Key, Value>(Value.Top)));
|
||||
}
|
||||
}
|
||||
if (Type.BOOLEAN_TYPE.equals(argType)) {
|
||||
if (isReferenceResult || isBooleanResult) {
|
||||
addEquation(new Equation<Key, Value>(new Key(method, new InOut(i, Value.False)), new Final<Key, Value>(Value.Top)));
|
||||
addEquation(new Equation<Key, Value>(new Key(method, new InOut(i, Value.True)), new Final<Key, Value>(Value.Top)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isReferenceResult) {
|
||||
addEquation(new Equation<Key, Value>(new Key(method, new Out()), new Final<Key, Value>(Value.Top)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addEquation(Equation<Key, Value> equation) {
|
||||
try {
|
||||
myIntIdSolver.addEquation(enumerate(equation));
|
||||
}
|
||||
catch (IOException e) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
// turns high-lever equation into low-level one
|
||||
IntIdEquation enumerate(Equation<Key, Value> equation) throws IOException {
|
||||
com.intellij.codeInspection.bytecodeAnalysis.Result<Key, Value> rhs = equation.rhs;
|
||||
IntIdResult result;
|
||||
if (rhs instanceof Final) {
|
||||
result = new IntIdFinal(((Final<Key, Value>)rhs).value);
|
||||
} else {
|
||||
Pending<Key, Value> pending = (Pending<Key, Value>)rhs;
|
||||
Set<Set<Key>> deltaOrig = pending.delta;
|
||||
IntIdComponent[] components = new IntIdComponent[deltaOrig.size()];
|
||||
int componentI = 0;
|
||||
for (Set<Key> 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<String, AnnotationData> annotations() {
|
||||
TIntObjectHashMap<Value> internalIdSolutions = myIntIdSolver.solve();
|
||||
return Util.makeAnnotations(internalIdSolutions, myEnumerators);
|
||||
}
|
||||
}
|
||||
@@ -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<Collection<IntIdEquation>> {
|
||||
@Override
|
||||
public void save(@NotNull DataOutput out, Collection<IntIdEquation> 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<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();
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -36,8 +36,8 @@ public class Util {
|
||||
}
|
||||
}
|
||||
|
||||
public static MostlySingularMultiMap<String, AnnotationData> makeAnnotations(TIntObjectHashMap<Value> internalIdSolutions,
|
||||
Enumerators enumerators) {
|
||||
public static MostlySingularMultiMap<String, AnnotationData> makeAnnotations(TIntObjectHashMap<Value> internalIdSolutions) {
|
||||
BytecodeAnalysisConverter lowering = BytecodeAnalysisConverter.getInstance();
|
||||
MostlySingularMultiMap<String, AnnotationData> annotations = new MostlySingularMultiMap<String, AnnotationData>();
|
||||
HashMap<String, StringBuilder> contracts = new HashMap<String, StringBuilder>();
|
||||
TIntObjectIterator<Value> 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) {
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
<component>
|
||||
<implementation-class>com.intellij.codeInsight.preview.ImageOrColorPreviewManager</implementation-class>
|
||||
</component>
|
||||
|
||||
<component>
|
||||
<implementation-class>com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisConverter</implementation-class>
|
||||
</component>
|
||||
</application-components>
|
||||
|
||||
<project-components>
|
||||
|
||||
Reference in New Issue
Block a user