From 383752b4a061e756c1e09cd8d9a54ebf6bf0c7fc Mon Sep 17 00:00:00 2001 From: Ilya Klyuchnikov Date: Wed, 11 Jun 2014 13:01:24 +0400 Subject: [PATCH] compound indices in action --- .../BytecodeAnalysisConverter.java | 277 +++++++----------- .../ProjectBytecodeAnalysis.java | 90 +++--- .../BytecodeAnalysisTest.java | 45 ++- .../bytecodeAnalysis/data/Test02.java | 54 ---- .../bytecodeAnalysis/data/Test03.java | 54 ---- .../data/TestConverterData.java | 81 +++++ 6 files changed, 264 insertions(+), 337 deletions(-) delete mode 100644 java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/data/Test02.java delete mode 100644 java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/data/Test03.java create mode 100644 java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/data/TestConverterData.java 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 e980cc4aeb8d..55a4f02273bd 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 @@ -22,8 +22,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.TypeConversionUtil; -import com.intellij.util.containers.HashMap; -import com.intellij.util.containers.MostlySingularMultiMap; import com.intellij.util.io.DataInputOutputUtil; import com.intellij.util.io.KeyDescriptor; import com.intellij.util.io.PersistentEnumeratorDelegate; @@ -39,7 +37,6 @@ import java.io.DataOutput; import java.io.File; import java.io.IOException; import java.util.Arrays; -import java.util.Map; import java.util.Set; /** @@ -53,7 +50,6 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { return ApplicationManager.getApplication().getComponent(BytecodeAnalysisConverter.class); } - private PersistentStringEnumerator myInternalKeyEnumerator; private PersistentStringEnumerator myPackageEnumerator; private PersistentStringEnumerator myNamesEnumerator; private PersistentEnumeratorDelegate myCompoundKeyEnumerator; @@ -62,11 +58,9 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { public void initComponent() { try { File keysDir = new File(PathManager.getIndexRoot(), "bytecodeKeys"); - final File internalKeysFile = new File(keysDir, "faba.internalIds"); - final File packageKeysFile = new File(keysDir, "faba.packages"); - final File namesFile = new File(keysDir, "faba.names1"); - final File compoundKeysFile = new File(keysDir, "faba.keys"); - myInternalKeyEnumerator = new PersistentStringEnumerator(internalKeysFile); + final File packageKeysFile = new File(keysDir, "packages"); + final File namesFile = new File(keysDir, "names"); + final File compoundKeysFile = new File(keysDir, "compound"); myPackageEnumerator = new PersistentStringEnumerator(packageKeysFile); myNamesEnumerator = new PersistentStringEnumerator(namesFile); myCompoundKeyEnumerator = new PersistentEnumeratorDelegate(compoundKeysFile, new IntArrayKeyDescriptor(), 1024 * 4); @@ -79,7 +73,6 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { @Override public void disposeComponent() { try { - myInternalKeyEnumerator.close(); myPackageEnumerator.close(); myNamesEnumerator.close(); myCompoundKeyEnumerator.close(); @@ -109,8 +102,8 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { int[] ids = new int[keyComponent.size()]; int idI = 0; for (Key id : keyComponent) { - // TODO refactor here - ids[idI] = myInternalKeyEnumerator.enumerate(internalKeyString(id)); + int[] compoundKey = mkCompoundKey(id); + ids[idI] = myCompoundKeyEnumerator.enumerate(compoundKey); idI++; } IntIdComponent intIdComponent = new IntIdComponent(ids); @@ -119,26 +112,17 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { } result = new IntIdPending(pending.infinum, components); } - int key = myInternalKeyEnumerator.enumerate(internalKeyString(equation.id)); + + int key = myCompoundKeyEnumerator.enumerate(mkCompoundKey(equation.id)); return new IntIdEquation(key, result); } - public static class InternalKey { - final String annotationKey; - final Direction dir; - - InternalKey(String annotationKey, Direction dir) { - this.annotationKey = annotationKey; - this.dir = dir; - } - } - @NotNull public int[] mkCompoundKey(@NotNull Key key) throws IOException { Direction direction = key.direction; Method method = key.method; - Type ownerType = Type.getType(method.internalClassName); + Type ownerType = Type.getObjectType(method.internalClassName); Type[] argTypes = Type.getArgumentTypes(method.methodDesc); Type returnType = Type.getReturnType(method.methodDesc); @@ -159,10 +143,23 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { return compoundKey; } + @Nullable + private static Direction extractDirection(int[] compoundKey) { + switch (compoundKey[0]) { + case Direction.OUT_DIRECTION: + return new Out(); + case Direction.IN_DIRECTION: + return new In(compoundKey[1]); + case Direction.INOUT_DIRECTION: + return new InOut(compoundKey[1], Value.values()[compoundKey[2]]); + } + return null; + } + + private void writeType(int[] compoundKey, int i, Type type) throws IOException { String className = type.getClassName(); int dotIndex = className.lastIndexOf('.'); - String packageName; String simpleName; if (dotIndex > 0) { @@ -172,44 +169,12 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { packageName = ""; simpleName = className; } - compoundKey[i] = myPackageEnumerator.enumerate(packageName); compoundKey[i + 1] = myNamesEnumerator.enumerate(simpleName); myPackageEnumerator.valueOf(compoundKey[i]); myNamesEnumerator.valueOf(compoundKey[i + 1]); } - public String showCompoundKey(@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(); - } - @Nullable public int[] mkCompoundKey(@NotNull PsiMethod psiMethod, Direction direction) throws IOException { @@ -256,6 +221,16 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { return compoundKey; } + public int mkKey(@NotNull PsiMethod psiMethod, Direction direction) throws IOException { + int[] compoundKey = mkCompoundKey(psiMethod, direction); + if (compoundKey == null) { + return -1; + } + else { + return myCompoundKeyEnumerator.enumerate(compoundKey); + } + } + private void writeClass(int[] compoundKey, int i, PsiClass psiClass, int dimensions) throws IOException { String packageName = ""; @@ -290,6 +265,7 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { } if (psiType instanceof PsiClassType) { + // no resolve() -> no package/class split PsiClass psiClass = ((PsiClassType)psiType).resolve(); if (psiClass != null) { writeClass(compoundKey, i, psiClass, dimensions); @@ -303,71 +279,88 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { String packageName = ""; String className = psiType.getPresentableText(); compoundKey[i] = myPackageEnumerator.enumerate(packageName); - compoundKey[i + 1] = myNamesEnumerator.enumerate(className); + if (dimensions == 0) { + compoundKey[i + 1] = myNamesEnumerator.enumerate(className); + } else { + StringBuilder sb = new StringBuilder(className); + for (int j = 0; j < dimensions; j++) { + sb.append("[]"); + } + compoundKey[i + 1] = myNamesEnumerator.enumerate(sb.toString()); + } return true; } return false; } - public MostlySingularMultiMap makeAnnotations(TIntObjectHashMap internalIdSolutions) { - MostlySingularMultiMap annotations = new MostlySingularMultiMap(); - HashMap contracts = new HashMap(); - TIntObjectIterator iterator = internalIdSolutions.iterator(); + public Annotations makeAnnotations(TIntObjectHashMap internalIdSolutions) { + final TIntObjectHashMap outs = new TIntObjectHashMap(); + final TIntObjectHashMap params = new TIntObjectHashMap(); + final TIntObjectHashMap contracts = new TIntObjectHashMap(); + + TIntObjectHashMap contractBuilders = new TIntObjectHashMap(); + TIntObjectIterator solutionsIterator = internalIdSolutions.iterator(); + for (int i = internalIdSolutions.size(); i-- > 0;) { - iterator.advance(); - int inKey = iterator.key(); - Value value = iterator.value(); + solutionsIterator.advance(); + int key = solutionsIterator.key(); + Value value = solutionsIterator.value(); if (value == Value.Top || value == Value.Bot) { continue; } - InternalKey key; + int[] compoundKey = null; try { - String s = myInternalKeyEnumerator.valueOf(inKey); - key = readInternalKey(s); + compoundKey = myCompoundKeyEnumerator.valueOf(key); } catch (IOException e) { - throw new RuntimeException(e); + // TODO: question: how to react? } - if (key != null) { - Direction direction = key.dir; - String baseAnnKey = key.annotationKey; + if (compoundKey != null) { + Direction direction = extractDirection(compoundKey); if (direction instanceof In && value == Value.NotNull) { - String annKey = baseAnnKey + " " + ((In)direction).paramIndex; - // TODO - here - annotations.add(annKey, new AnnotationData("org.jetbrains.annotations.NotNull", "")); + params.put(key, 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", "")); + outs.put(key, 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); + compoundKey[0] = 0; + compoundKey[1] = 0; + compoundKey[2] = 0; + try { + // TODO - sort (normalize) contract clauses + int baseKey = myCompoundKeyEnumerator.enumerate(compoundKey); + StringBuilder sb = contractBuilders.get(baseKey); + if (sb == null) { + sb = new StringBuilder("\""); + contractBuilders.put(baseKey, sb); + } + else { + sb.append(';'); + } + int arity = compoundKey[8]; + contractElement(sb, arity, (InOut)direction, value); } - else { - sb.append(';'); + catch (IOException e) { + // TODO: question: how to react? } - 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())); + TIntObjectIterator buildersIterator = contractBuilders.iterator(); + for (int i = contractBuilders.size(); i-- > 0;) { + buildersIterator.advance(); + int key = buildersIterator.key(); + StringBuilder value = buildersIterator.value(); + if (!outs.contains(key)) { + contracts.put(key, new AnnotationData("org.jetbrains.annotations.Contract", value.append('"').toString())); } } - return annotations; - } - // TODO - this is a hack for now - static int calculateArity(String annotationKey) { - return annotationKey.split(",").length; + return new Annotations(outs, params, contracts); } static String contractValueString(Value v) { @@ -396,79 +389,34 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { return sb.toString(); } - public static String internalKeyString(Key key) { - return annotationKey(key.method) + ';' + direction2Key(key.direction); - } + 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(", "); - 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(); } @@ -503,5 +451,4 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { } } - } 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 747790bd88a4..566b2f7a5a77 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 @@ -28,24 +28,20 @@ import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; -import com.intellij.psi.PsiAnnotation; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiManager; -import com.intellij.psi.PsiModifierListOwner; +import com.intellij.psi.*; import com.intellij.psi.impl.source.*; import com.intellij.psi.search.ProjectScope; -import com.intellij.psi.util.PsiFormatUtil; 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.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; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -53,28 +49,25 @@ 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(1); + private static final List NO_DATA = new ArrayList(0); private final PsiManager myPsiManager; - private MostlySingularMultiMap myAnnotations = null; + private Annotations myAnnotations = null; public ProjectBytecodeAnalysis(Project project, PsiManager psiManager) { super(project); myPsiManager = psiManager; + // TODO: question: what is a proper way to handle indices changes? StartupManager.getInstance(project).registerPostStartupActivity(new Runnable() { @Override - public void run() { - //doIndex(); - } + public void run() {} }); final MessageBusConnection connection = myProject.getMessageBus().connect(); connection.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() { @Override - public void rootsChanged(ModuleRootEvent event) { - //doIndex(); - } + public void rootsChanged(ModuleRootEvent event) {} }); } @@ -95,24 +88,18 @@ public class ProjectBytecodeAnalysis extends AbstractProjectComponent { TIntObjectHashMap solutions = solver.solve(); LOG.info("equations are solved"); myAnnotations = BytecodeAnalysisConverter.getInstance().makeAnnotations(solutions); - LOG.info("initialized " + myAnnotations.size()); + LOG.info("initialized "); } // TODO: what follows was just copied/modified from BaseExternalAnnotationsManager - // TODO: refactor? @Nullable public PsiAnnotation findInferredAnnotation(@NotNull PsiModifierListOwner listOwner, @NotNull String annotationFQN) { - String key = getExternalName(listOwner); - if (key == null) { - return null; - } List list = collectInferredAnnotations(listOwner); AnnotationData data = findByFQN(list, annotationFQN); if (data == null) { return null; } - LOG.info("annotation: " + key + " " + data); return data.getAnnotation(this); } @@ -120,15 +107,12 @@ public class ProjectBytecodeAnalysis extends AbstractProjectComponent { public PsiAnnotation[] findInferredAnnotations(@NotNull PsiModifierListOwner listOwner) { List result = collectInferredAnnotations(listOwner); if (result == null || result.isEmpty()) return null; - PsiAnnotation[] myResult = ContainerUtil.map2Array(result, PsiAnnotation.EMPTY_ARRAY, new Function() { + return ContainerUtil.map2Array(result, PsiAnnotation.EMPTY_ARRAY, new Function() { @Override public PsiAnnotation fun(AnnotationData data) { return data.getAnnotation(ProjectBytecodeAnalysis.this); } }); - String key = getExternalName(listOwner); - LOG.info("annotations: " + key + " " + result); - return myResult; } @Nullable @@ -142,28 +126,40 @@ public class ProjectBytecodeAnalysis extends AbstractProjectComponent { } private synchronized List collectInferredAnnotations(PsiModifierListOwner listOwner) { - String key = getExternalName(listOwner); - if (key == null) { - return NO_DATA; - } - SmartList result = new SmartList(); - if (myAnnotations == null) { loadAnnotations(); } - - Iterable inferred = myAnnotations.get(key); - ContainerUtil.addAll(result, inferred); - return result; + try { + int key = getKey(listOwner); + if (key == -1) { + return NO_DATA; + } + 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; + } } - @Nullable - protected static String getExternalName(@NotNull PsiModifierListOwner listOwner) { - String rawExternalName = PsiFormatUtil.getRawExternalName(listOwner); - if (rawExternalName != null) { - rawExternalName = rawExternalName.replace("...)", "[])"); + protected static int getKey(@NotNull PsiModifierListOwner owner) throws IOException { + if (owner instanceof PsiMethod) { + return BytecodeAnalysisConverter.getInstance().mkKey((PsiMethod)owner, new Out()); + } + else if (owner instanceof PsiParameter) { + final PsiElement declarationScope = ((PsiParameter)owner).getDeclarationScope(); + if (!(declarationScope instanceof PsiMethod)) { + return -1; + } + final PsiMethod psiMethod = (PsiMethod)declarationScope; + final int index = psiMethod.getParameterList().getParameterIndex((PsiParameter)owner); + return BytecodeAnalysisConverter.getInstance().mkKey(psiMethod, new In(index)); + } else { + return -1; } - return rawExternalName; } // interner for storing annotation FQN @@ -190,6 +186,18 @@ public class ProjectBytecodeAnalysis extends AbstractProjectComponent { } } +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; diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisTest.java index 0825018f0682..4e4cb2f994f9 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisTest.java @@ -18,9 +18,9 @@ package com.intellij.codeInspection.bytecodeAnalysis; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.InferredAnnotationsManager; import com.intellij.codeInspection.bytecodeAnalysis.data.Test01; -import com.intellij.codeInspection.bytecodeAnalysis.data.Test02; -import com.intellij.codeInspection.bytecodeAnalysis.data.Test03; +import com.intellij.codeInspection.bytecodeAnalysis.data.TestConverterData; import com.intellij.codeInspection.bytecodeAnalysis.data.TestAnnotation; +import com.intellij.openapi.application.ex.PathManagerEx; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -29,6 +29,7 @@ import com.intellij.psi.search.GlobalSearchScope; import com.intellij.testFramework.PsiTestUtil; import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; import com.intellij.util.ArrayUtil; +import org.apache.commons.lang.SystemUtils; import org.jetbrains.annotations.Contract; import org.jetbrains.org.objectweb.asm.Type; import org.junit.Assert; @@ -57,6 +58,7 @@ public class BytecodeAnalysisTest extends JavaCodeInsightFixtureTestCase { myBytecodeAnalysisConverter = BytecodeAnalysisConverter.getInstance(); setUpDataClasses(); + setUpVelocityLibrary(); } @Override @@ -65,32 +67,31 @@ public class BytecodeAnalysisTest extends JavaCodeInsightFixtureTestCase { super.tearDown(); } - // TODO - test it, possible solution - via external annotation manager?? - /* + // TODO: real integration test (possible solution - via external annotation manager??) public void testVelocityJar() { - VirtualFile lib = LocalFileSystem.getInstance().refreshAndFindFileByPath(PathManagerEx.getTestDataPath() + "/../../../lib"); - assertNotNull(lib); - PsiTestUtil.addLibrary(myModule, "velocity", lib.getPath(), new String[]{"/velocity.jar!/"}, new String[]{}); PsiClass psiClass = myJavaPsiFacade.findClass(SystemUtils.class.getName(), GlobalSearchScope.moduleWithLibrariesScope(myModule)); assertNotNull(psiClass); PsiMethod getJavaHomeMethod = psiClass.findMethodsByName("getJavaHome", false)[0]; assertNotNull(InferredAnnotationsManager.getInstance(myModule.getProject()).findInferredAnnotation(getJavaHomeMethod, AnnotationUtil.NOT_NULL)); } - */ + + private void setUpVelocityLibrary() { + VirtualFile lib = LocalFileSystem.getInstance().refreshAndFindFileByPath(PathManagerEx.getTestDataPath() + "/../../../lib"); + assertNotNull(lib); + PsiTestUtil.addLibrary(myModule, "velocity", lib.getPath(), new String[]{"/velocity.jar!/"}, new String[]{}); + } public void testInference() throws IOException { checkAnnotations(Test01.class); } - public void testCompoundKeys() throws IOException { + public void testConverter() throws IOException { checkCompoundIds(Test01.class); - checkCompoundIds(Test02.class); - checkCompoundIds(Test02.Inner1.class); - checkCompoundIds(Test02.Inner2.class); - checkCompoundIds(Test03.class); - checkCompoundIds(Test03.Inner1.class); - checkCompoundIds(Test03.Inner2.class); + checkCompoundIds(TestConverterData.class); + checkCompoundIds(TestConverterData.StaticNestedClass.class); + checkCompoundIds(TestConverterData.InnerClass.class); + checkCompoundIds(TestConverterData.GenericStaticNestedClass.class); checkCompoundIds(TestAnnotation.class); } @@ -107,8 +108,7 @@ public class BytecodeAnalysisTest extends JavaCodeInsightFixtureTestCase { Annotation[] parameterAnnotations = annotations[i]; PsiParameter psiParameter = psiMethod.getParameterList().getParameters()[i]; PsiAnnotation inferredAnnotation = myInferredAnnotationsManager.findInferredAnnotation(psiParameter, AnnotationUtil.NOT_NULL); - for (int j = 0; j < parameterAnnotations.length; j++) { - Annotation parameterAnnotation = parameterAnnotations[j]; + for (Annotation parameterAnnotation : parameterAnnotations) { if (parameterAnnotation.annotationType() == ExpectNotNull.class) { assertNotNull(inferredAnnotation); continue params; @@ -128,7 +128,7 @@ public class BytecodeAnalysisTest extends JavaCodeInsightFixtureTestCase { assertEquals(expectedContract == null, actualContractAnnotation == null); - if (expectedAnnotation != null) { + if (expectedContract != null) { String expectedContractValue = expectedContract.value(); String actualContractValue = AnnotationUtil.getStringAttributeValue(actualContractAnnotation, null); assertEquals(expectedContractValue, actualContractValue); @@ -144,14 +144,14 @@ public class BytecodeAnalysisTest extends JavaCodeInsightFixtureTestCase { for (java.lang.reflect.Method javaMethod : javaClass.getDeclaredMethods()) { System.out.println(javaMethod.getName()); - Method method = new Method(Type.getDescriptor(javaClass), javaMethod.getName(), Type.getMethodDescriptor(javaMethod)); + Method method = new Method(Type.getType(javaClass).getInternalName(), javaMethod.getName(), Type.getMethodDescriptor(javaMethod)); boolean noKey = javaMethod.getAnnotation(ExpectNoPsiKey.class) != null; PsiMethod psiMethod = psiClass.findMethodsByName(javaMethod.getName(), false)[0]; checkCompoundId(method, psiMethod, noKey); } for (Constructor constructor : javaClass.getDeclaredConstructors()) { - Method method = new Method(Type.getDescriptor(javaClass), "", Type.getConstructorDescriptor(constructor)); + Method method = new Method(Type.getType(javaClass).getInternalName(), "", Type.getConstructorDescriptor(constructor)); boolean noKey = constructor.getAnnotation(ExpectNoPsiKey.class) != null; PsiMethod[] constructors = psiClass.getConstructors(); PsiMethod psiMethod = constructors[0]; @@ -175,8 +175,8 @@ public class BytecodeAnalysisTest extends JavaCodeInsightFixtureTestCase { System.out.println(Arrays.toString(asmKey)); System.out.println(Arrays.toString(psiKey)); - System.out.println(myBytecodeAnalysisConverter.showCompoundKey(asmKey)); - System.out.println(myBytecodeAnalysisConverter.showCompoundKey(psiKey)); + System.out.println(myBytecodeAnalysisConverter.debugCompoundKey(asmKey)); + System.out.println(myBytecodeAnalysisConverter.debugCompoundKey(psiKey)); Assert.assertArrayEquals(asmKey, psiKey); } @@ -187,7 +187,6 @@ public class BytecodeAnalysisTest extends JavaCodeInsightFixtureTestCase { FileUtil.copyDir(classesDir, destDir); VirtualFile vFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(destDir); assertNotNull(vFile); - PsiTestUtil.addLibrary(myModule, "dataClasses", vFile.getPath(), new String[]{""}, ArrayUtil.EMPTY_STRING_ARRAY); } diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/data/Test02.java b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/data/Test02.java deleted file mode 100644 index 50d8d2711803..000000000000 --- a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/data/Test02.java +++ /dev/null @@ -1,54 +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.data; - -import com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisConverter; -import com.intellij.codeInspection.bytecodeAnalysis.ExpectNoPsiKey; - -/** - * @author lambdamix - */ -public class Test02 { - - public static class Inner1 { - public Inner1(Object o) { - - } - public Inner1[] Inner1test01(Inner1[] tests, Inner1... ellipsis) { - return tests; - } - } - - public class Inner2 { - public Inner2(Object o) { - - } - public Inner2[] Inner2test01(Inner2[] tests, Inner2... ellipsis) { - return tests; - } - } - - public Test02(int x) {} - - @ExpectNoPsiKey - public BytecodeAnalysisConverter.InternalKey test01(BytecodeAnalysisConverter.InternalKey converter) { - return converter; - } - - public Test02[] test02(Test02[] tests) { - return tests; - } -} diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/data/Test03.java b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/data/Test03.java deleted file mode 100644 index f2d0967fc385..000000000000 --- a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/data/Test03.java +++ /dev/null @@ -1,54 +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.data; - -import com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisConverter; -import com.intellij.codeInspection.bytecodeAnalysis.ExpectNoPsiKey; - -/** - * @author lambdamix - */ -public class Test03 { - - public static class Inner1 { - public Inner1(Object o) { - - } - public Inner1[] Inner1test01(Inner1[] tests, Inner1... ellipsis) { - return tests; - } - } - - public class Inner2 { - public Inner2(A o) { - - } - public A[] Inner2test01(A[] tests, A... ellipsis) { - return tests; - } - } - - public Test03(@TestAnnotation int x) {} - - @ExpectNoPsiKey - public BytecodeAnalysisConverter.InternalKey test01(BytecodeAnalysisConverter.InternalKey converter) { - return converter; - } - - public Test03[] test02(Test03[] tests) { - return tests; - } -} diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/data/TestConverterData.java b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/data/TestConverterData.java new file mode 100644 index 000000000000..254db418e55c --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/data/TestConverterData.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.data; + +import com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisConverter; +import com.intellij.codeInspection.bytecodeAnalysis.ExpectNoPsiKey; + +/** + * @author lambdamix + */ +public class TestConverterData { + + public static class StaticNestedClass { + public StaticNestedClass(Object o) { + + } + public StaticNestedClass[] test01(StaticNestedClass[] ns, StaticNestedClass... ellipsis) { + return ns; + } + } + + public class InnerClass { + // a reference to outer class should be inserted when translating PSI -> ASM + public InnerClass(Object o) {} + + public InnerClass[] Inner2test01(InnerClass[] tests, InnerClass... ellipsis) { + return tests; + } + } + + public static class GenericStaticNestedClass { + public GenericStaticNestedClass(A a) { + + } + public GenericStaticNestedClass[] test01(GenericStaticNestedClass[] ns, GenericStaticNestedClass... ellipsis) { + return ns; + } + + public GenericStaticNestedClass[] test02(GenericStaticNestedClass[] ns, GenericStaticNestedClass... ellipsis) { + return ns; + } + + public class GenericInnerClass { + public GenericInnerClass(B b) {} + + public GenericStaticNestedClass test01(GenericInnerClass c) { + return GenericStaticNestedClass.this; + } + } + } + + public TestConverterData(int x) {} + + // BytecodeAnalysisConverter class is not in the project path, so translation from PSI is impossible + @ExpectNoPsiKey + public BytecodeAnalysisConverter test01(BytecodeAnalysisConverter converter) { + return converter; + } + + @TestAnnotation + public TestConverterData[] test02(@TestAnnotation TestConverterData[] tests) throws Exception { + return tests; + } + + public boolean[] test03(boolean[] b) { + return b; + } +}