From 0ea01efcd06d69b7a60ab1d4c5a441bca43cf010 Mon Sep 17 00:00:00 2001 From: Ilya Klyuchnikov Date: Thu, 19 Jun 2014 23:15:58 +0400 Subject: [PATCH] minimal structures for inferred annotations in memory --- .../BytecodeAnalysisConverter.java | 62 +----- .../ProjectBytecodeAnalysis.java | 198 ++++++++---------- 2 files changed, 103 insertions(+), 157 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java index 84cfc56f2ea6..76478cd4c27a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java @@ -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 internalIdSolutions) { - final TIntObjectHashMap outs = new TIntObjectHashMap(); - final TIntObjectHashMap params = new TIntObjectHashMap(); - final TIntObjectHashMap contracts = new TIntObjectHashMap(); + public void addAnnotations(TIntObjectHashMap internalIdSolutions, Annotations annotations) { TIntObjectHashMap> contractClauses = new TIntObjectHashMap>(); TIntObjectIterator solutionsIterator = internalIdSolutions.iterator(); + TIntHashSet notNulls = annotations.notNulls; + TIntObjectHashMap 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 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; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java index 964c534c322e..53e1fa74965e 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java @@ -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 NO_DATA = new ArrayList(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.NotNull, Value.Top)); FileBasedIndex.getInstance().processValues( @@ -106,10 +110,10 @@ public class ProjectBytecodeAnalysis extends AbstractProjectComponent { LOG.info("parameter equations are constructed"); TIntObjectHashMap 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.Bot, Value.Top)); FileBasedIndex.getInstance().processValues( @@ -125,61 +129,98 @@ public class ProjectBytecodeAnalysis extends AbstractProjectComponent { LOG.info("contract equations are constructed"); TIntObjectHashMap 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 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 result = collectInferredAnnotations(listOwner); - if (result == null || result.isEmpty()) return null; - return ContainerUtil.map2Array(result, PsiAnnotation.EMPTY_ARRAY, new Function() { - @Override - public PsiAnnotation fun(AnnotationData data) { - return data.getAnnotation(ProjectBytecodeAnalysis.this); - } - }); - } - - @Nullable - private static AnnotationData findByFQN(@NotNull List map, @NotNull final String annotationFQN) { - return ContainerUtil.find(map, new Condition() { - @Override - public boolean value(AnnotationData data) { - return data.annotationClassFqName.equals(annotationFQN); - } - }); + return collectInferredAnnotations(listOwner); } // TODO the best way to synchronize? - private synchronized List 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 result = new SmartList(); - 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 outs; - final TIntObjectHashMap params; - final TIntObjectHashMap contracts; - - Annotations(TIntObjectHashMap outs, TIntObjectHashMap params, TIntObjectHashMap 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 contracts = new TIntObjectHashMap(); }