mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
minimal structures for inferred annotations in memory
This commit is contained in:
+11
-51
@@ -26,6 +26,7 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.io.*;
|
||||
import gnu.trove.TIntHashSet;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import gnu.trove.TIntObjectIterator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -325,15 +326,14 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME - how to handle errors here? - this is called after indexing is complete
|
||||
public Annotations makeAnnotations(TIntObjectHashMap<Value> internalIdSolutions) {
|
||||
final TIntObjectHashMap<AnnotationData> outs = new TIntObjectHashMap<AnnotationData>();
|
||||
final TIntObjectHashMap<AnnotationData> params = new TIntObjectHashMap<AnnotationData>();
|
||||
final TIntObjectHashMap<AnnotationData> contracts = new TIntObjectHashMap<AnnotationData>();
|
||||
public void addAnnotations(TIntObjectHashMap<Value> internalIdSolutions, Annotations annotations) {
|
||||
|
||||
TIntObjectHashMap<List<String>> contractClauses = new TIntObjectHashMap<List<String>>();
|
||||
TIntObjectIterator<Value> solutionsIterator = internalIdSolutions.iterator();
|
||||
|
||||
TIntHashSet notNulls = annotations.notNulls;
|
||||
TIntObjectHashMap<String> contracts = annotations.contracts;
|
||||
|
||||
for (int i = internalIdSolutions.size(); i-- > 0;) {
|
||||
solutionsIterator.advance();
|
||||
int key = Math.abs(solutionsIterator.key());
|
||||
@@ -351,19 +351,14 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
|
||||
|
||||
if (compoundKey != null) {
|
||||
Direction direction = extractDirection(compoundKey);
|
||||
|
||||
if (direction instanceof In && value == Value.NotNull) {
|
||||
params.put(key, new AnnotationData("org.jetbrains.annotations.NotNull", ""));
|
||||
}
|
||||
else if (direction instanceof Out && value == Value.NotNull) {
|
||||
outs.put(key, new AnnotationData("org.jetbrains.annotations.NotNull", ""));
|
||||
if (value == Value.NotNull && (direction instanceof In || direction instanceof Out)) {
|
||||
notNulls.add(key);
|
||||
}
|
||||
else if (direction instanceof InOut) {
|
||||
compoundKey[0] = 0;
|
||||
compoundKey[1] = 0;
|
||||
compoundKey[2] = 0;
|
||||
try {
|
||||
// TODO - sort (normalize) contract clauses
|
||||
int baseKey = myCompoundKeyEnumerator.enumerate(compoundKey);
|
||||
List<String> clauses = contractClauses.get(baseKey);
|
||||
if (clauses == null) {
|
||||
@@ -388,14 +383,11 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
|
||||
Collections.sort(clauses);
|
||||
|
||||
//if (!outs.contains(key)) {
|
||||
StringBuilder sb = new StringBuilder("\"");
|
||||
StringUtil.join(clauses, ";", sb);
|
||||
sb.append('"');
|
||||
contracts.put(key, new AnnotationData("org.jetbrains.annotations.Contract", sb.toString()));
|
||||
//}
|
||||
StringBuilder sb = new StringBuilder("\"");
|
||||
StringUtil.join(clauses, ";", sb);
|
||||
sb.append('"');
|
||||
contracts.put(key, sb.toString().intern());
|
||||
}
|
||||
|
||||
return new Annotations(outs, params, contracts);
|
||||
}
|
||||
|
||||
static String contractValueString(Value v) {
|
||||
@@ -425,37 +417,6 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String debugCompoundKey(@NotNull int[] key) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(key[0]);
|
||||
sb.append(", ");
|
||||
sb.append(key[1]);
|
||||
sb.append(", ");
|
||||
sb.append(key[2]);
|
||||
sb.append(", ");
|
||||
sb.append(myPackageEnumerator.valueOf(key[3]));
|
||||
sb.append(", ");
|
||||
sb.append(myNamesEnumerator.valueOf(key[4]));
|
||||
sb.append(", ");
|
||||
sb.append(myPackageEnumerator.valueOf(key[5]));
|
||||
sb.append(", ");
|
||||
sb.append(myNamesEnumerator.valueOf(key[6]));
|
||||
sb.append(", ");
|
||||
sb.append(myNamesEnumerator.valueOf(key[7]));
|
||||
sb.append(", ");
|
||||
sb.append(key[8]);
|
||||
sb.append(", ");
|
||||
|
||||
for (int i = 0; i < key[8]; i++) {
|
||||
sb.append(myPackageEnumerator.valueOf(key[9 + 2*i]));
|
||||
sb.append(", ");
|
||||
sb.append(myNamesEnumerator.valueOf(key[9 + 2*i + 1]));
|
||||
sb.append(", ");
|
||||
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
@@ -475,7 +436,6 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
|
||||
int[] value = new int[DataInputOutputUtil.readINT(in)];
|
||||
for (int i = 0; i < value.length; i++) {
|
||||
value[i] = DataInputOutputUtil.readINT(in);
|
||||
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
+92
-106
@@ -25,27 +25,22 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ModuleRootAdapter;
|
||||
import com.intellij.openapi.roots.ModuleRootEvent;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.NotNullLazyKey;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.*;
|
||||
import com.intellij.psi.search.ProjectScope;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.indexing.FileBasedIndex;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import gnu.trove.TIntHashSet;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author lambdamix
|
||||
@@ -53,7 +48,14 @@ import java.util.List;
|
||||
public class ProjectBytecodeAnalysis extends AbstractProjectComponent {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis");
|
||||
private static final List<AnnotationData> NO_DATA = new ArrayList<AnnotationData>(0);
|
||||
private static final CharTableImpl charTable = new CharTableImpl();
|
||||
private static final JavaParserUtil.ParserWrapper ANNOTATION = new JavaParserUtil.ParserWrapper() {
|
||||
@Override
|
||||
public void parse(final PsiBuilder builder) {
|
||||
JavaParser.INSTANCE.getDeclarationParser().parseAnnotation(builder);
|
||||
}
|
||||
};
|
||||
private final PsiAnnotation notNullAnnotation;
|
||||
|
||||
private final PsiManager myPsiManager;
|
||||
|
||||
@@ -77,12 +79,14 @@ public class ProjectBytecodeAnalysis extends AbstractProjectComponent {
|
||||
unloadAnnotations();
|
||||
}
|
||||
});
|
||||
notNullAnnotation = createAnnotationFromText("@org.jetbrains.annotations.NotNull");
|
||||
}
|
||||
|
||||
private void loadAnnotations() {
|
||||
Annotations parameterAnnotations = loadParameterAnnotations();
|
||||
Annotations contractAnnotations = loadContractAnnotations();
|
||||
myAnnotations = new Annotations(contractAnnotations.outs, parameterAnnotations.params, contractAnnotations.contracts);
|
||||
Annotations annotations = new Annotations();
|
||||
loadParameterAnnotations(annotations);
|
||||
loadContractAnnotations(annotations);
|
||||
myAnnotations = annotations;
|
||||
}
|
||||
|
||||
private void unloadAnnotations() {
|
||||
@@ -90,7 +94,7 @@ public class ProjectBytecodeAnalysis extends AbstractProjectComponent {
|
||||
LOG.info("unloaded");
|
||||
}
|
||||
|
||||
private Annotations loadParameterAnnotations() {
|
||||
private void loadParameterAnnotations(Annotations annotations) {
|
||||
LOG.info("initializing parameter annotations");
|
||||
final IntIdSolver solver = new IntIdSolver(new ELattice<Value>(Value.NotNull, Value.Top));
|
||||
FileBasedIndex.getInstance().processValues(
|
||||
@@ -106,10 +110,10 @@ public class ProjectBytecodeAnalysis extends AbstractProjectComponent {
|
||||
LOG.info("parameter equations are constructed");
|
||||
TIntObjectHashMap<Value> solutions = solver.solve();
|
||||
LOG.info("parameter equations are solved");
|
||||
return BytecodeAnalysisConverter.getInstance().makeAnnotations(solutions);
|
||||
BytecodeAnalysisConverter.getInstance().addAnnotations(solutions, annotations);
|
||||
}
|
||||
|
||||
private Annotations loadContractAnnotations() {
|
||||
private void loadContractAnnotations(Annotations annotations) {
|
||||
LOG.info("initializing contract annotations");
|
||||
final IntIdSolver solver = new IntIdSolver(new ELattice<Value>(Value.Bot, Value.Top));
|
||||
FileBasedIndex.getInstance().processValues(
|
||||
@@ -125,61 +129,98 @@ public class ProjectBytecodeAnalysis extends AbstractProjectComponent {
|
||||
LOG.info("contract equations are constructed");
|
||||
TIntObjectHashMap<Value> solutions = solver.solve();
|
||||
LOG.info("contract equations are solved");
|
||||
return BytecodeAnalysisConverter.getInstance().makeAnnotations(solutions);
|
||||
BytecodeAnalysisConverter.getInstance().addAnnotations(solutions, annotations);
|
||||
}
|
||||
|
||||
|
||||
// TODO: what follows was just copied/modified from BaseExternalAnnotationsManager
|
||||
@Nullable
|
||||
public PsiAnnotation findInferredAnnotation(@NotNull PsiModifierListOwner listOwner, @NotNull String annotationFQN) {
|
||||
List<AnnotationData> list = collectInferredAnnotations(listOwner);
|
||||
AnnotationData data = findByFQN(list, annotationFQN);
|
||||
if (data == null) {
|
||||
if (annotationFQN.equals("org.jetbrains.annotations.NotNull")) {
|
||||
return findNotNullAnnotation(listOwner);
|
||||
}
|
||||
else if (annotationFQN.equals("org.jetbrains.annotations.Contract")) {
|
||||
return findContractAnnotation(listOwner);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
return data.getAnnotation(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiAnnotation[] findInferredAnnotations(@NotNull PsiModifierListOwner listOwner) {
|
||||
List<AnnotationData> result = collectInferredAnnotations(listOwner);
|
||||
if (result == null || result.isEmpty()) return null;
|
||||
return ContainerUtil.map2Array(result, PsiAnnotation.EMPTY_ARRAY, new Function<AnnotationData, PsiAnnotation>() {
|
||||
@Override
|
||||
public PsiAnnotation fun(AnnotationData data) {
|
||||
return data.getAnnotation(ProjectBytecodeAnalysis.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static AnnotationData findByFQN(@NotNull List<AnnotationData> map, @NotNull final String annotationFQN) {
|
||||
return ContainerUtil.find(map, new Condition<AnnotationData>() {
|
||||
@Override
|
||||
public boolean value(AnnotationData data) {
|
||||
return data.annotationClassFqName.equals(annotationFQN);
|
||||
}
|
||||
});
|
||||
return collectInferredAnnotations(listOwner);
|
||||
}
|
||||
|
||||
// TODO the best way to synchronize?
|
||||
private synchronized List<AnnotationData> collectInferredAnnotations(PsiModifierListOwner listOwner) {
|
||||
@Nullable
|
||||
private synchronized PsiAnnotation[] collectInferredAnnotations(PsiModifierListOwner listOwner) {
|
||||
if (myAnnotations == null) {
|
||||
loadAnnotations();
|
||||
}
|
||||
try {
|
||||
int key = getKey(listOwner);
|
||||
if (key == -1) {
|
||||
return NO_DATA;
|
||||
return null;
|
||||
}
|
||||
boolean notNull = myAnnotations.notNulls.contains(key);
|
||||
String contractValue = myAnnotations.contracts.get(key);
|
||||
|
||||
if (notNull && contractValue != null) {
|
||||
return new PsiAnnotation[]{
|
||||
notNullAnnotation,
|
||||
createAnnotationFromText("@org.jetbrains.annotations.Contract(" + contractValue + ")")
|
||||
};
|
||||
}
|
||||
else if (notNull) {
|
||||
return new PsiAnnotation[]{
|
||||
notNullAnnotation
|
||||
};
|
||||
}
|
||||
else if (contractValue != null) {
|
||||
return new PsiAnnotation[]{
|
||||
createAnnotationFromText("@org.jetbrains.annotations.Contract(" + contractValue + ")")
|
||||
};
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
SmartList<AnnotationData> result = new SmartList<AnnotationData>();
|
||||
ContainerUtil.addIfNotNull(result, myAnnotations.contracts.get(key));
|
||||
ContainerUtil.addIfNotNull(result, myAnnotations.outs.get(key));
|
||||
ContainerUtil.addIfNotNull(result, myAnnotations.params.get(key));
|
||||
return result;
|
||||
}
|
||||
catch (IOException e) {
|
||||
return NO_DATA;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private synchronized PsiAnnotation findNotNullAnnotation(PsiModifierListOwner listOwner) {
|
||||
if (myAnnotations == null) {
|
||||
loadAnnotations();
|
||||
}
|
||||
try {
|
||||
int key = getKey(listOwner);
|
||||
if (key == -1) {
|
||||
return null;
|
||||
}
|
||||
return myAnnotations.notNulls.contains(key) ? notNullAnnotation : null;
|
||||
}
|
||||
catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private synchronized PsiAnnotation findContractAnnotation(PsiModifierListOwner listOwner) {
|
||||
if (myAnnotations == null) {
|
||||
loadAnnotations();
|
||||
}
|
||||
try {
|
||||
int key = getKey(listOwner);
|
||||
if (key == -1) {
|
||||
return null;
|
||||
}
|
||||
String contractValue = myAnnotations.contracts.get(key);
|
||||
return contractValue != null ? createAnnotationFromText("@org.jetbrains.annotations.Contract(" + contractValue + ")") : null;
|
||||
}
|
||||
catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,76 +241,21 @@ public class ProjectBytecodeAnalysis extends AbstractProjectComponent {
|
||||
}
|
||||
}
|
||||
|
||||
// interner for storing annotation FQN
|
||||
private final CharTableImpl charTable = new CharTableImpl();
|
||||
private static final JavaParserUtil.ParserWrapper ANNOTATION = new JavaParserUtil.ParserWrapper() {
|
||||
@Override
|
||||
public void parse(final PsiBuilder builder) {
|
||||
JavaParser.INSTANCE.getDeclarationParser().parseAnnotation(builder);
|
||||
}
|
||||
};
|
||||
@NotNull
|
||||
PsiAnnotation createAnnotationFromText(@NotNull final String text) throws IncorrectOperationException {
|
||||
// synchronize during interning in charTable
|
||||
synchronized (charTable) {
|
||||
final DummyHolder holder = DummyHolderFactory.createHolder(myPsiManager,
|
||||
new JavaDummyElement(text, ANNOTATION, LanguageLevel.HIGHEST), null,
|
||||
charTable);
|
||||
final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode());
|
||||
if (!(element instanceof PsiAnnotation)) {
|
||||
throw new IncorrectOperationException("Incorrect annotation \"" + text + "\".");
|
||||
}
|
||||
return (PsiAnnotation)element;
|
||||
return (PsiAnnotation) element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Annotations {
|
||||
final TIntObjectHashMap<AnnotationData> outs;
|
||||
final TIntObjectHashMap<AnnotationData> params;
|
||||
final TIntObjectHashMap<AnnotationData> contracts;
|
||||
|
||||
Annotations(TIntObjectHashMap<AnnotationData> outs, TIntObjectHashMap<AnnotationData> params, TIntObjectHashMap<AnnotationData> contracts) {
|
||||
this.outs = outs;
|
||||
this.params = params;
|
||||
this.contracts = contracts;
|
||||
}
|
||||
}
|
||||
|
||||
class AnnotationData {
|
||||
@NotNull final String annotationClassFqName;
|
||||
@NotNull final String annotationParameters;
|
||||
private volatile PsiAnnotation annotation;
|
||||
|
||||
AnnotationData(@NotNull String annotationClassFqName, @NotNull String annotationParameters) {
|
||||
this.annotationClassFqName = annotationClassFqName;
|
||||
this.annotationParameters = annotationParameters;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
PsiAnnotation getAnnotation(@NotNull ProjectBytecodeAnalysis context) {
|
||||
PsiAnnotation a = annotation;
|
||||
if (a == null) {
|
||||
annotation = a = context.createAnnotationFromText("@" + annotationClassFqName + (annotationParameters.isEmpty() ? "" : "("+annotationParameters+")"));
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
AnnotationData data = (AnnotationData)o;
|
||||
|
||||
return annotationClassFqName.equals(data.annotationClassFqName) && annotationParameters.equals(data.annotationParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = annotationClassFqName.hashCode();
|
||||
result = 31 * result + annotationParameters.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
// @NotNull keys
|
||||
final TIntHashSet notNulls = new TIntHashSet();
|
||||
// @Contracts
|
||||
final TIntObjectHashMap<String> contracts = new TIntObjectHashMap<String>();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user