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 eb7261d25f45..bc99541630ad 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 @@ -19,11 +19,17 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.util.containers.HashMap; +import com.intellij.util.containers.MostlySingularMultiMap; import com.intellij.util.io.PersistentStringEnumerator; +import gnu.trove.TIntObjectHashMap; +import gnu.trove.TIntObjectIterator; import org.jetbrains.annotations.NotNull; +import org.jetbrains.org.objectweb.asm.Type; import java.io.File; import java.io.IOException; +import java.util.Map; import java.util.Set; /** @@ -39,33 +45,6 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { PersistentStringEnumerator internalKeyEnumerator; - IntIdEquation convert(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); - } - @Override public void initComponent() { try { @@ -93,4 +72,205 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { public String getComponentName() { return "BytecodeAnalysisConverter"; } + + IntIdEquation convert(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) { + // TODO refactor here + ids[idI] = internalKeyEnumerator.enumerate(internalKeyString(id)); + idI++; + } + IntIdComponent intIdComponent = new IntIdComponent(ids); + components[componentI] = intIdComponent; + componentI++; + } + result = new IntIdPending(pending.infinum, components); + } + int key = internalKeyEnumerator.enumerate(internalKeyString(equation.id)); + return new IntIdEquation(key, result); + } + + static class InternalKey { + final String annotationKey; + final Direction dir; + + InternalKey(String annotationKey, Direction dir) { + this.annotationKey = annotationKey; + this.dir = dir; + } + } + + public MostlySingularMultiMap makeAnnotations(TIntObjectHashMap internalIdSolutions) { + MostlySingularMultiMap annotations = new MostlySingularMultiMap(); + HashMap contracts = new HashMap(); + TIntObjectIterator iterator = internalIdSolutions.iterator(); + for (int i = internalIdSolutions.size(); i-- > 0;) { + iterator.advance(); + int inKey = iterator.key(); + Value value = iterator.value(); + if (value == Value.Top || value == Value.Bot) { + continue; + } + InternalKey key; + try { + String s = internalKeyEnumerator.valueOf(inKey); + key = readInternalKey(s); + } + catch (IOException e) { + throw new RuntimeException(e); + } + + if (key != null) { + Direction direction = key.dir; + String baseAnnKey = key.annotationKey; + + if (direction instanceof In && value == Value.NotNull) { + String annKey = baseAnnKey + " " + ((In)direction).paramIndex; + // TODO - here + annotations.add(annKey, new AnnotationData("org.jetbrains.annotations.NotNull", "")); + } + else if (direction instanceof Out && value == Value.NotNull) { + // TODO - here + annotations.add(baseAnnKey, new AnnotationData("org.jetbrains.annotations.NotNull", "")); + } + // TODO - sort (normalize) contract clauses + else if (direction instanceof InOut) { + StringBuilder sb = contracts.get(baseAnnKey); + if (sb == null) { + sb = new StringBuilder("\""); + contracts.put(baseAnnKey, sb); + } + else { + sb.append(';'); + } + contractElement(sb, calculateArity(baseAnnKey), (InOut)direction, value); + } + } + } + + for (Map.Entry contract : contracts.entrySet()) { + if (!annotations.containsKey(contract.getKey())) { + annotations.add(contract.getKey(), new AnnotationData("org.jetbrains.annotations.Contract", contract.getValue().append('"').toString())); + } + } + return annotations; + } + + // TODO - this is a hack for now + static int calculateArity(String annotationKey) { + return annotationKey.split(",").length; + } + + static String contractValueString(Value v) { + switch (v) { + case False: return "false"; + case True: return "true"; + case NotNull: return "!null"; + case Null: return "null"; + default: return "_"; + } + } + + static String contractElement(StringBuilder sb, int arity, InOut inOut, Value value) { + for (int i = 0; i < arity; i++) { + Value currentValue = Value.Top; + if (i == inOut.paramIndex) { + currentValue = inOut.inValue; + } + if (i > 0) { + sb.append(','); + } + sb.append(contractValueString(currentValue)); + } + sb.append("->"); + sb.append(contractValueString(value)); + return sb.toString(); + } + + public static String internalKeyString(Key key) { + return annotationKey(key.method) + ';' + direction2Key(key.direction); + } + + public static String direction2Key(Direction dir) { + if (dir instanceof In) { + return "In:" + ((In)dir).paramIndex; + } else if (dir instanceof Out) { + return "Out"; + } else { + InOut inOut = (InOut)dir; + return "InOut:" + inOut.paramIndex + ":" + inOut.inValue.name(); + } + } + + public static InternalKey readInternalKey(String s) { + String[] parts = s.split(";"); + String annKey = parts[0]; + String[] dirStrings = parts[1].split(":"); + if ("In".equals(dirStrings[0])) { + return new InternalKey(annKey, new In(Integer.valueOf(dirStrings[1]))); + } else if ("Out".equals(dirStrings[0])) { + return new InternalKey(annKey, new Out()); + } else { + return new InternalKey(annKey, new InOut(Integer.valueOf(dirStrings[1]), Value.valueOf(dirStrings[2]))); + } + } + + public static String annotationKey(Method method) { + if ("".equals(method.methodName)) { + return canonical(method.internalClassName) + " " + + simpleName(method.internalClassName) + + parameters(method); + } else { + return canonical(method.internalClassName) + " " + + returnType(method) + " " + + method.methodName + + parameters(method); + } + } + + private static String returnType(Method method) { + return canonical(Type.getReturnType(method.methodDesc).getClassName()); + } + + public static String canonical(String internalName) { + return internalName.replace('/', '.').replace('$', '.'); + } + + private static String simpleName(String internalName) { + String cn = canonical(internalName); + int lastDotIndex = cn.lastIndexOf('.'); + if (lastDotIndex == -1) { + return cn; + } else { + return cn.substring(lastDotIndex + 1); + } + } + + private static String parameters(Method method) { + Type[] argTypes = Type.getArgumentTypes(method.methodDesc); + StringBuilder sb = new StringBuilder("("); + boolean notFirst = false; + for (Type argType : argTypes) { + if (notFirst) { + sb.append(", "); + } + else { + notFirst = true; + } + sb.append(canonical(argType.getClassName())); + } + sb.append(')'); + return sb.toString(); + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisHandler.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java similarity index 89% rename from java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisHandler.java rename to java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java index 679c190b5b1f..747790bd88a4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisHandler.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java @@ -42,6 +42,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MostlySingularMultiMap; import com.intellij.util.indexing.FileBasedIndex; import com.intellij.util.messages.MessageBusConnection; +import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -49,16 +50,16 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -public class BytecodeAnalysisHandler extends AbstractProjectComponent { +public class ProjectBytecodeAnalysis extends AbstractProjectComponent { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisHandler"); + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis"); private static final List NO_DATA = new ArrayList(1); private final PsiManager myPsiManager; private MostlySingularMultiMap myAnnotations = null; - public BytecodeAnalysisHandler(Project project, PsiManager psiManager) { + public ProjectBytecodeAnalysis(Project project, PsiManager psiManager) { super(project); myPsiManager = psiManager; @@ -77,6 +78,27 @@ public class BytecodeAnalysisHandler extends AbstractProjectComponent { }); } + private void loadAnnotations() { + LOG.info("initializing annotations"); + final IntIdSolver solver = new IntIdSolver(); + FileBasedIndex.getInstance().processValues( + BytecodeAnalysisIndex.NAME, BytecodeAnalysisIndex.KEY, null, new FileBasedIndex.ValueProcessor>() { + @Override + public boolean process(VirtualFile file, Collection value) { + for (IntIdEquation intIdEquation : value) { + solver.addEquation(intIdEquation); + } + return true; + } + }, ProjectScope.getLibrariesScope(myProject)); + LOG.info("equations are constructed"); + TIntObjectHashMap solutions = solver.solve(); + LOG.info("equations are solved"); + myAnnotations = BytecodeAnalysisConverter.getInstance().makeAnnotations(solutions); + LOG.info("initialized " + myAnnotations.size()); + } + + // TODO: what follows was just copied/modified from BaseExternalAnnotationsManager // TODO: refactor? @Nullable @@ -94,7 +116,6 @@ public class BytecodeAnalysisHandler extends AbstractProjectComponent { return data.getAnnotation(this); } - @Nullable public PsiAnnotation[] findInferredAnnotations(@NotNull PsiModifierListOwner listOwner) { List result = collectInferredAnnotations(listOwner); @@ -102,7 +123,7 @@ public class BytecodeAnalysisHandler extends AbstractProjectComponent { PsiAnnotation[] myResult = ContainerUtil.map2Array(result, PsiAnnotation.EMPTY_ARRAY, new Function() { @Override public PsiAnnotation fun(AnnotationData data) { - return data.getAnnotation(BytecodeAnalysisHandler.this); + return data.getAnnotation(ProjectBytecodeAnalysis.this); } }); String key = getExternalName(listOwner); @@ -128,19 +149,7 @@ public class BytecodeAnalysisHandler extends AbstractProjectComponent { SmartList result = new SmartList(); if (myAnnotations == null) { - LOG.info("initializing annotations"); - final IntIdSolver solver = new IntIdSolver(); - FileBasedIndex.getInstance().processValues(BytecodeAnalysisIndex.NAME, BytecodeAnalysisIndex.KEY, null, new FileBasedIndex.ValueProcessor>() { - @Override - public boolean process(VirtualFile file, Collection value) { - for (IntIdEquation intIdEquation : value) { - solver.addEquation(intIdEquation); - } - return true; - } - }, ProjectScope.getLibrariesScope(myProject)); - myAnnotations = Util.makeAnnotations(solver.solve()); - LOG.info("initialized " + myAnnotations.size()); + loadAnnotations(); } Iterable inferred = myAnnotations.get(key); @@ -192,7 +201,7 @@ class AnnotationData { } @NotNull - PsiAnnotation getAnnotation(@NotNull BytecodeAnalysisHandler context) { + PsiAnnotation getAnnotation(@NotNull ProjectBytecodeAnalysis context) { PsiAnnotation a = annotation; if (a == null) { annotation = a = context.createAnnotationFromText("@" + annotationClassFqName + (annotationParameters.isEmpty() ? "" : "("+annotationParameters+")")); 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 deleted file mode 100644 index e55871e18b47..000000000000 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Util.java +++ /dev/null @@ -1,210 +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.util.containers.HashMap; -import com.intellij.util.containers.MostlySingularMultiMap; -import gnu.trove.TIntObjectHashMap; -import gnu.trove.TIntObjectIterator; -import org.jetbrains.org.objectweb.asm.Type; - -import java.io.IOException; -import java.util.Map; - -public class Util { - - static class InternalKey { - final String annotationKey; - final Direction dir; - - InternalKey(String annotationKey, Direction dir) { - this.annotationKey = annotationKey; - this.dir = dir; - } - } - - public static MostlySingularMultiMap makeAnnotations(TIntObjectHashMap internalIdSolutions) { - BytecodeAnalysisConverter lowering = BytecodeAnalysisConverter.getInstance(); - MostlySingularMultiMap annotations = new MostlySingularMultiMap(); - HashMap contracts = new HashMap(); - TIntObjectIterator iterator = internalIdSolutions.iterator(); - for (int i = internalIdSolutions.size(); i-- > 0;) { - iterator.advance(); - int inKey = iterator.key(); - Value value = iterator.value(); - if (value == Value.Top || value == Value.Bot) { - continue; - } - InternalKey key; - try { - String s = lowering.internalKeyEnumerator.valueOf(inKey); - key = readInternalKey(s); - } - catch (IOException e) { - throw new RuntimeException(e); - } - - if (key != null) { - Direction direction = key.dir; - String baseAnnKey = key.annotationKey; - - if (direction instanceof In && value == Value.NotNull) { - String annKey = baseAnnKey + " " + ((In)direction).paramIndex; - annotations.add(annKey, new AnnotationData("org.jetbrains.annotations.NotNull", "")); - } - else if (direction instanceof Out && value == Value.NotNull) { - annotations.add(baseAnnKey, new AnnotationData("org.jetbrains.annotations.NotNull", "")); - } - // TODO - sort (normalize) contract clauses - else if (direction instanceof InOut) { - StringBuilder sb = contracts.get(baseAnnKey); - if (sb == null) { - sb = new StringBuilder("\""); - contracts.put(baseAnnKey, sb); - } - else { - sb.append(';'); - } - contractElement(sb, calculateArity(baseAnnKey), (InOut)direction, value); - } - } - } - - for (Map.Entry contract : contracts.entrySet()) { - if (!annotations.containsKey(contract.getKey())) { - annotations.add(contract.getKey(), new AnnotationData("org.jetbrains.annotations.Contract", contract.getValue().append('"').toString())); - } - } - return annotations; - } - - // TODO - this is a hack for now - static int calculateArity(String annotationKey) { - return annotationKey.split(",").length; - } - - static String contractValueString(Value v) { - switch (v) { - case False: return "false"; - case True: return "true"; - case NotNull: return "!null"; - case Null: return "null"; - default: return "_"; - } - } - - static String contractElement(StringBuilder sb, int arity, InOut inOut, Value value) { - for (int i = 0; i < arity; i++) { - Value currentValue = Value.Top; - if (i == inOut.paramIndex) { - currentValue = inOut.inValue; - } - if (i > 0) { - sb.append(','); - } - sb.append(contractValueString(currentValue)); - } - sb.append("->"); - sb.append(contractValueString(value)); - return sb.toString(); - } - - public static String annotationKey(Method method, Direction dir) { - String annPrefix = annotationKey(method); - if (dir instanceof In) { - return annPrefix + " " + ((In)dir).paramIndex; - } else { - return annPrefix; - } - } - - public static String internalKeyString(Key key) { - return annotationKey(key.method) + ';' + direction2Key(key.direction); - } - - public static String direction2Key(Direction dir) { - if (dir instanceof In) { - return "In:" + ((In)dir).paramIndex; - } else if (dir instanceof Out) { - return "Out"; - } else { - InOut inOut = (InOut)dir; - return "InOut:" + inOut.paramIndex + ":" + inOut.inValue.name(); - } - } - - public static InternalKey readInternalKey(String s) { - String[] parts = s.split(";"); - String annKey = parts[0]; - String[] dirStrings = parts[1].split(":"); - if ("In".equals(dirStrings[0])) { - return new InternalKey(annKey, new In(Integer.valueOf(dirStrings[1]))); - } else if ("Out".equals(dirStrings[0])) { - return new InternalKey(annKey, new Out()); - } else { - return new InternalKey(annKey, new InOut(Integer.valueOf(dirStrings[1]), Value.valueOf(dirStrings[2]))); - } - } - - public static String annotationKey(Method method) { - if ("".equals(method.methodName)) { - return canonical(method.internalClassName) + " " + - simpleName(method.internalClassName) + - parameters(method); - } else { - return canonical(method.internalClassName) + " " + - returnType(method) + " " + - method.methodName + - parameters(method); - } - } - - private static String returnType(Method method) { - return canonical(Type.getReturnType(method.methodDesc).getClassName()); - } - - public static String canonical(String internalName) { - return internalName.replace('/', '.').replace('$', '.'); - } - - private static String simpleName(String internalName) { - String cn = canonical(internalName); - int lastDotIndex = cn.lastIndexOf('.'); - if (lastDotIndex == -1) { - return cn; - } else { - return cn.substring(lastDotIndex + 1); - } - } - - private static String parameters(Method method) { - Type[] argTypes = Type.getArgumentTypes(method.methodDesc); - StringBuilder sb = new StringBuilder("("); - boolean notFirst = false; - for (Type argType : argTypes) { - if (notFirst) { - sb.append(", "); - } - else { - notFirst = true; - } - sb.append(canonical(argType.getClassName())); - } - sb.append(')'); - return sb.toString(); - } - -} diff --git a/java/java-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java b/java/java-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java index 69e2f889ab26..92f161f16fb2 100644 --- a/java/java-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/InferredAnnotationsManagerImpl.java @@ -15,7 +15,7 @@ */ package com.intellij.codeInsight; -import com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisHandler; +import com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis; import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiModifierListOwner; import org.jetbrains.annotations.NotNull; @@ -25,12 +25,12 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager { @Nullable @Override public PsiAnnotation findInferredAnnotation(@NotNull PsiModifierListOwner listOwner, @NotNull String annotationFQN) { - return listOwner.getProject().getComponent(BytecodeAnalysisHandler.class).findInferredAnnotation(listOwner, annotationFQN); + return listOwner.getProject().getComponent(ProjectBytecodeAnalysis.class).findInferredAnnotation(listOwner, annotationFQN); } @Nullable @Override public PsiAnnotation[] findInferredAnnotations(@NotNull PsiModifierListOwner listOwner) { - return listOwner.getProject().getComponent(BytecodeAnalysisHandler.class).findInferredAnnotations(listOwner); + return listOwner.getProject().getComponent(ProjectBytecodeAnalysis.class).findInferredAnnotations(listOwner); } } diff --git a/platform/platform-resources/src/componentSets/Lang.xml b/platform/platform-resources/src/componentSets/Lang.xml index c253aed8e6e2..dc5edaa3b5c8 100644 --- a/platform/platform-resources/src/componentSets/Lang.xml +++ b/platform/platform-resources/src/componentSets/Lang.xml @@ -231,7 +231,7 @@ - com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisHandler + com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis