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 0239a661c4ee..68334588f1de 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 @@ -15,97 +15,265 @@ */ package com.intellij.codeInspection.bytecodeAnalysis; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.TypeConversionUtil; -import gnu.trove.TLongArrayList; -import gnu.trove.TLongHashSet; -import gnu.trove.TLongObjectHashMap; -import gnu.trove.TLongObjectIterator; import org.jetbrains.annotations.NotNull; -import org.jetbrains.org.objectweb.asm.Type; +import org.jetbrains.annotations.Nullable; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis.LOG; /** * @author lambdamix */ -public abstract class BytecodeAnalysisConverter implements ApplicationComponent { +public class BytecodeAnalysisConverter { - public static final int SHIFT = 4096; + // how many bytes are taken from class fqn digest + public static final int CLASS_HASH_SIZE = 10; + // how many bytes are taken from signature digest + public static final int SIGNATURE_HASH_SIZE = 4; + public static final int HASH_SIZE = CLASS_HASH_SIZE + SIGNATURE_HASH_SIZE; - public static BytecodeAnalysisConverter getInstance() { - return ApplicationManager.getApplication().getComponent(BytecodeAnalysisConverter.class); + public static MessageDigest getMessageDigest() throws NoSuchAlgorithmException { + return MessageDigest.getInstance("MD5"); } + /** + * Converts an equation over asm keys into equation over small hash keys. + */ @NotNull - @Override - public String getComponentName() { - return "BytecodeAnalysisConverter"; - } - - public abstract int getVersion(); - - protected abstract int enumerateString(@NotNull String s) throws IOException; - - protected abstract int enumerateCompoundKey(@NotNull int[] key) throws IOException; - - IdEquation convert(Equation equation) throws IOException { + static HEquation convert(@NotNull Equation equation, @NotNull MessageDigest md) { ProgressManager.checkCanceled(); Result rhs = equation.rhs; - IdResult result; + HResult result; if (rhs instanceof Final) { - result = new IdFinal(((Final)rhs).value); + result = new HFinal(((Final)rhs).value); } else { Pending pending = (Pending)rhs; Set> sumOrigin = pending.sum; - IntIdComponent[] components = new IntIdComponent[sumOrigin.size()]; + HComponent[] components = new HComponent[sumOrigin.size()]; int componentI = 0; for (Product prod : sumOrigin) { - long[] intProd = new long[prod.ids.size()]; + HKey[] intProd = new HKey[prod.ids.size()]; int idI = 0; for (Key id : prod.ids) { - long rawId = mkAsmKey(id); - if (rawId <= 0) { - LOG.error("raw key should be positive. rawId = " + rawId); - } - intProd[idI] = id.stable ? rawId : -rawId; + intProd[idI] = asmKey(id, md); idI++; } - IntIdComponent intIdComponent = new IntIdComponent(prod.value, intProd); + HComponent intIdComponent = new HComponent(prod.value, intProd); components[componentI] = intIdComponent; componentI++; } - result = new IdPending(components); + result = new HPending(components); } - long rawKey = mkAsmKey(equation.id); - if (rawKey <= 0) { - LOG.error("raw key should be positive. rawKey = " + rawKey); + HKey key = asmKey(equation.id, md); + return new HEquation(key, result); + } + + /** + * Converts an asm method key to a small hash key (HKey) + */ + @NotNull + public static HKey asmKey(@NotNull Key key, @NotNull MessageDigest md) { + String signature = key.method.methodName + key.method.methodDesc; + byte[] classDigest = md.digest(key.method.internalClassName.getBytes()); + byte[] sigDigest = md.digest(signature.getBytes()); + byte[] digest = new byte[HASH_SIZE]; + System.arraycopy(classDigest, 0, digest, 0, CLASS_HASH_SIZE); + System.arraycopy(sigDigest, 0, digest, CLASS_HASH_SIZE, SIGNATURE_HASH_SIZE); + return new HKey(digest, mkDirectionKey(key.direction), key.stable); + } + + /** + * Converts a Psi method to a small hash key (HKey). + * Returns null if conversion is impossible (something is not resolvable). + */ + @Nullable + public static HKey psiKey(@NotNull PsiMethod psiMethod, @NotNull Direction direction, @NotNull MessageDigest md) { + final PsiClass psiClass = PsiTreeUtil.getParentOfType(psiMethod, PsiClass.class, false); + if (psiClass == null) { + return null; + } + byte[] classDigest = psiClassDigest(psiClass, md); + if (classDigest == null) { + return null; + } + byte[] sigDigest = methodDigest(psiMethod, md); + if (sigDigest == null) { + return null; + } + byte[] digest = new byte[HASH_SIZE]; + System.arraycopy(classDigest, 0, digest, 0, CLASS_HASH_SIZE); + System.arraycopy(sigDigest, 0, digest, CLASS_HASH_SIZE, SIGNATURE_HASH_SIZE); + return new HKey(digest, mkDirectionKey(direction), true); + } + + @Nullable + private static byte[] psiClassDigest(@NotNull PsiClass psiClass, @NotNull MessageDigest md) { + String descriptor = descriptor(psiClass, 0, false); + if (descriptor == null) { + return null; + } + return md.digest(descriptor.getBytes()); + } + + @Nullable + private static byte[] methodDigest(@NotNull PsiMethod psiMethod, @NotNull MessageDigest md) { + String descriptor = descriptor(psiMethod); + if (descriptor == null) { + return null; + } + return md.digest(descriptor.getBytes()); + } + + @Nullable + private static String descriptor(@NotNull PsiMethod psiMethod) { + StringBuilder sb = new StringBuilder(); + final PsiClass psiClass = PsiTreeUtil.getParentOfType(psiMethod, PsiClass.class, false); + if (psiClass == null) { + return null; + } + PsiClass outerClass = psiClass.getContainingClass(); + boolean isInnerClassConstructor = psiMethod.isConstructor() && (outerClass != null) && !psiClass.hasModifierProperty(PsiModifier.STATIC); + PsiParameter[] parameters = psiMethod.getParameterList().getParameters(); + PsiType returnType = psiMethod.getReturnType(); + + sb.append(returnType == null ? "" : psiMethod.getName()); + sb.append('('); + + String desc; + + if (isInnerClassConstructor) { + desc = descriptor(outerClass, 0, true); + if (desc == null) { + return null; + } + sb.append(desc); + } + for (PsiParameter parameter : parameters) { + desc = descriptor(parameter.getType()); + if (desc == null) { + return null; + } + sb.append(desc); + } + sb.append(')'); + if (returnType == null) { + sb.append('V'); + } else { + desc = descriptor(returnType); + if (desc == null) { + return null; + } else { + sb.append(desc); + } + } + return sb.toString(); + } + + @Nullable + private static String descriptor(@NotNull PsiClass psiClass, int dimensions, boolean full) { + PsiFile containingFile = psiClass.getContainingFile(); + if (!(containingFile instanceof PsiClassOwner)) { + LOG.debug("containingFile was not resolved for " + psiClass.getQualifiedName()); + return null; + } + PsiClassOwner psiFile = (PsiClassOwner)containingFile; + String packageName = psiFile.getPackageName(); + String qname = psiClass.getQualifiedName(); + if (qname == null) { + return null; + } + String className; + if (packageName.length() > 0) { + className = qname.substring(packageName.length() + 1).replace('.', '$'); + } else { + className = qname.replace('.', '$'); + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < dimensions; i++) { + sb.append('['); + } + if (full) { + sb.append('L'); + } + if (packageName.length() > 0) { + sb.append(packageName.replace('.', '/')); + sb.append('/'); + } + sb.append(className); + if (full) { + sb.append(';'); + } + return sb.toString(); + } + + @Nullable + private static String descriptor(@NotNull PsiType psiType) { + int dimensions = 0; + psiType = TypeConversionUtil.erasure(psiType); + if (psiType instanceof PsiArrayType) { + PsiArrayType arrayType = (PsiArrayType)psiType; + psiType = arrayType.getDeepComponentType(); + dimensions = arrayType.getArrayDimensions(); } - long key = equation.id.stable ? rawKey : -rawKey; - return new IdEquation(key, result); + if (psiType instanceof PsiClassType) { + PsiClass psiClass = ((PsiClassType)psiType).resolve(); + if (psiClass != null) { + return descriptor(psiClass, dimensions, true); + } + else { + LOG.debug("resolve was null for " + ((PsiClassType)psiType).getClassName()); + return null; + } + } + else if (psiType instanceof PsiPrimitiveType) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < dimensions; i++) { + sb.append('['); + } + if (PsiType.VOID.equals(psiType)) { + sb.append('V'); + } + else if (PsiType.BOOLEAN.equals(psiType)) { + sb.append('Z'); + } + else if (PsiType.CHAR.equals(psiType)) { + sb.append('C'); + } + else if (PsiType.BYTE.equals(psiType)) { + sb.append('B'); + } + else if (PsiType.SHORT.equals(psiType)) { + sb.append('S'); + } + else if (PsiType.INT.equals(psiType)) { + sb.append('I'); + } + else if (PsiType.FLOAT.equals(psiType)) { + sb.append('F'); + } + else if (PsiType.LONG.equals(psiType)) { + sb.append('J'); + } + else if (PsiType.DOUBLE.equals(psiType)) { + sb.append('D'); + } + return sb.toString(); + } + return null; } - public long mkAsmKey(@NotNull Key key) throws IOException { - long baseKey = mkAsmSignatureKey(key.method); - long directionKey = mkDirectionKey(key.direction); - return baseKey * SHIFT + directionKey; - } - - private static int mkDirectionKey(Direction dir) throws IOException { + private static int mkDirectionKey(Direction dir) { if (dir instanceof Out) { return 0; } else if (dir instanceof In) { @@ -134,211 +302,54 @@ public abstract class BytecodeAnalysisConverter implements ApplicationComponent } } - // class + short signature - private int mkAsmSignatureKey(@NotNull Method method) throws IOException { - int[] sigKey = new int[2]; - sigKey[0] = mkAsmTypeKey(Type.getObjectType(method.internalClassName)); - sigKey[1] = mkAsmShortSignatureKey(method); - return enumerateCompoundKey(sigKey); - } - - private int mkAsmShortSignatureKey(@NotNull Method method) throws IOException { - Type[] argTypes = Type.getArgumentTypes(method.methodDesc); - int arity = argTypes.length; - int[] sigKey = new int[2 + arity]; - sigKey[0] = mkAsmTypeKey(Type.getReturnType(method.methodDesc)); - sigKey[1] = enumerateString(method.methodName); - for (int i = 0; i < argTypes.length; i++) { - sigKey[2 + i] = mkAsmTypeKey(argTypes[i]); - } - return enumerateCompoundKey(sigKey); - } - - private int mkAsmTypeKey(Type type) throws IOException { - String className = type.getClassName(); - int dotIndex = className.lastIndexOf('.'); - String packageName; - String simpleName; - if (dotIndex > 0) { - packageName = className.substring(0, dotIndex); - simpleName = className.substring(dotIndex + 1); - } else { - packageName = ""; - simpleName = className; - } - int[] classKey = new int[]{enumerateString(packageName), enumerateString(simpleName)}; - return enumerateCompoundKey(classKey); - } - - public long mkPsiKey(@NotNull PsiMethod psiMethod, Direction direction) throws IOException { - final PsiClass psiClass = PsiTreeUtil.getParentOfType(psiMethod, PsiClass.class, false); - if (psiClass == null) { - LOG.debug("PsiClass was null for " + psiMethod.getName()); - return -1; - } - long sigKey = mkPsiSignatureKey(psiMethod); - if (sigKey == -1) { - return -1; - } - long directionKey = mkDirectionKey(direction); - return sigKey * SHIFT + directionKey; - } - - private int mkPsiSignatureKey(@NotNull PsiMethod psiMethod) throws IOException { - final PsiClass psiClass = PsiTreeUtil.getParentOfType(psiMethod, PsiClass.class, false); - if (psiClass == null) { - LOG.debug("PsiClass was null for " + psiMethod.getName()); - return -1; - } - PsiClass outerClass = psiClass.getContainingClass(); - boolean isInnerClassConstructor = psiMethod.isConstructor() && (outerClass != null) && !psiClass.hasModifierProperty(PsiModifier.STATIC); + /** + * Given a PSI method and its primary HKey enumerate all contract keys for it. + */ + @NotNull + public static ArrayList mkInOutKeys(@NotNull PsiMethod psiMethod, @NotNull HKey primaryKey) { PsiParameter[] parameters = psiMethod.getParameterList().getParameters(); - PsiType returnType = psiMethod.getReturnType(); - - final int shift = isInnerClassConstructor ? 1 : 0; - final int arity = parameters.length + shift; - int[] shortSigKey = new int[2 + arity]; - if (returnType == null) { - shortSigKey[0] = mkPsiTypeKey(PsiType.VOID); - shortSigKey[1] = enumerateString(""); - } else { - shortSigKey[0] = mkPsiTypeKey(returnType); - shortSigKey[1] = enumerateString(psiMethod.getName()); - } - if (isInnerClassConstructor) { - shortSigKey[2] = mkPsiClassKey(outerClass, 0); - } - for (int i = 0; i < parameters.length; i++) { - PsiParameter parameter = parameters[i]; - shortSigKey[2 + i + shift] = mkPsiTypeKey(parameter.getType()); - } - for (int aShortSigKey : shortSigKey) { - if (aShortSigKey == -1) { - return -1; - } - } - - int[] sigKey = new int[2]; - int classKey = mkPsiClassKey(psiClass, 0); - if (classKey == -1) { - return -1; - } - sigKey[0] = classKey; - sigKey[1] = enumerateCompoundKey(shortSigKey); - - return enumerateCompoundKey(sigKey); - } - - public TLongArrayList mkInOutKeys(@NotNull PsiMethod psiMethod, long primaryKey) throws IOException { - PsiParameter[] parameters = psiMethod.getParameterList().getParameters(); - TLongArrayList keys = new TLongArrayList(parameters.length * 2 + 1); + ArrayList keys = new ArrayList(parameters.length * 2 + 1); for (int i = 0; i < parameters.length; i++) { PsiParameter parameter = parameters[i]; PsiType parameterType = parameter.getType(); if (parameterType instanceof PsiPrimitiveType) { if (PsiType.BOOLEAN.equals(parameterType)) { - keys.add(primaryKey + mkDirectionKey(new InOut(i, Value.False))); - keys.add(primaryKey + mkDirectionKey(new InOut(i, Value.True))); + keys.add(primaryKey.updateDirection(mkDirectionKey(new InOut(i, Value.False)))); + keys.add(primaryKey.updateDirection(mkDirectionKey(new InOut(i, Value.True)))); } } else { - keys.add(primaryKey + mkDirectionKey(new InOut(i, Value.NotNull))); - keys.add(primaryKey + mkDirectionKey(new InOut(i, Value.Null))); + keys.add(primaryKey.updateDirection(mkDirectionKey(new InOut(i, Value.NotNull)))); + keys.add(primaryKey.updateDirection(mkDirectionKey(new InOut(i, Value.Null)))); } } return keys; } - - private int mkPsiClassKey(PsiClass psiClass, int dimensions) throws IOException { - PsiFile containingFile = psiClass.getContainingFile(); - if (!(containingFile instanceof PsiClassOwner)) { - LOG.debug("containingFile was not resolved for " + psiClass.getQualifiedName()); - return -1; - } - PsiClassOwner psiFile = (PsiClassOwner)containingFile; - String packageName = psiFile.getPackageName(); - String qname = psiClass.getQualifiedName(); - if (qname == null) { - return -1; - } - String className = qname; - if (packageName.length() > 0) { - className = qname.substring(packageName.length() + 1).replace('.', '$'); - } - int[] classKey = new int[2]; - classKey[0] = enumerateString(packageName); - if (dimensions == 0) { - classKey[1] = enumerateString(className); - } else { - StringBuilder sb = new StringBuilder(className); - for (int j = 0; j < dimensions; j++) { - sb.append("[]"); - } - classKey[1] = enumerateString(sb.toString()); - } - return enumerateCompoundKey(classKey); - } - - private int mkPsiTypeKey(PsiType psiType) throws IOException { - int dimensions = 0; - psiType = TypeConversionUtil.erasure(psiType); - if (psiType instanceof PsiArrayType) { - PsiArrayType arrayType = (PsiArrayType)psiType; - psiType = arrayType.getDeepComponentType(); - dimensions = arrayType.getArrayDimensions(); - } - - if (psiType instanceof PsiClassType) { - // no resolve() -> no package/class split - PsiClass psiClass = ((PsiClassType)psiType).resolve(); - if (psiClass != null) { - return mkPsiClassKey(psiClass, dimensions); - } - else { - LOG.debug("resolve was null for " + ((PsiClassType)psiType).getClassName()); - return -1; - } - } - else if (psiType instanceof PsiPrimitiveType) { - String packageName = ""; - String className = psiType.getPresentableText(); - int[] classKey = new int[2]; - classKey[0] = enumerateString(packageName); - if (dimensions == 0) { - classKey[1] = enumerateString(className); - } else { - StringBuilder sb = new StringBuilder(className); - for (int j = 0; j < dimensions; j++) { - sb.append("[]"); - } - classKey[1] = enumerateString(sb.toString()); - } - return enumerateCompoundKey(classKey); - } - return -1; - } - - public void addMethodAnnotations(TLongObjectHashMap internalIdSolutions, Annotations annotations, long methodKey, int arity) { - + /** + * Given `solution` of all dependencies of a method with the `methodKey`, converts this solution into annotations. + * + * @param solution solution of equations + * @param annotations annotations to which corresponding solutions should be added + * @param methodKey a primary key of a method being analyzed + * @param arity arity of this method (hint for constructing @Contract annotations) + */ + public static void addMethodAnnotations(@NotNull HashMap solution, @NotNull Annotations annotations, @NotNull HKey methodKey, int arity) { List clauses = new ArrayList(); - TLongObjectIterator solutionsIterator = internalIdSolutions.iterator(); - - TLongHashSet notNulls = annotations.notNulls; - TLongObjectHashMap contracts = annotations.contracts; - for (int i = internalIdSolutions.size(); i-- > 0;) { - solutionsIterator.advance(); - long key = Math.abs(solutionsIterator.key()); - Value value = solutionsIterator.value(); + HashSet notNulls = annotations.notNulls; + HashMap contracts = annotations.contracts; + for (Map.Entry entry : solution.entrySet()) { + HKey key = entry.getKey().mkStable(); + Value value = entry.getValue(); if (value == Value.Top || value == Value.Bot) { continue; } - Direction direction = extractDirection((int)(key % SHIFT)); - if (value == Value.NotNull && direction instanceof Out && key == methodKey) { + Direction direction = extractDirection(key.dirKey); + if (value == Value.NotNull && direction instanceof Out && methodKey.equals(key)) { notNulls.add(key); } else if (direction instanceof InOut) { - long baseKey = key - (key % SHIFT); - if (baseKey == methodKey) { + HKey baseKey = key.mkBase(); + if (methodKey.equals(baseKey)) { clauses.add(contractElement(arity, (InOut)direction, value)); } } @@ -353,24 +364,28 @@ public abstract class BytecodeAnalysisConverter implements ApplicationComponent } } - public void addParameterAnnotations(TLongObjectHashMap internalIdSolutions, Annotations annotations) { - TLongObjectIterator solutionsIterator = internalIdSolutions.iterator(); - TLongHashSet notNulls = annotations.notNulls; - for (int i = internalIdSolutions.size(); i-- > 0;) { - solutionsIterator.advance(); - long key = Math.abs(solutionsIterator.key()); - Value value = solutionsIterator.value(); + /** + * Converts solutions for equations over parameters into annotations. + * + * @param solution + * @param annotations + */ + public static void addParameterAnnotations(@NotNull HashMap solution, @NotNull Annotations annotations) { + HashSet notNulls = annotations.notNulls; + for (Map.Entry entry : solution.entrySet()) { + HKey key = entry.getKey().mkStable(); + Value value = entry.getValue(); if (value == Value.Top || value == Value.Bot) { continue; } - Direction direction = extractDirection((int)(key % SHIFT)); + Direction direction = extractDirection(key.dirKey); if (value == Value.NotNull && (direction instanceof In || direction instanceof Out)) { notNulls.add(key); } } } - static String contractValueString(Value v) { + private static String contractValueString(@NotNull Value v) { switch (v) { case False: return "false"; case True: return "true"; @@ -380,7 +395,7 @@ public abstract class BytecodeAnalysisConverter implements ApplicationComponent } } - static String contractElement(int arity, InOut inOut, Value value) { + private static String contractElement(int arity, InOut inOut, Value value) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < arity; i++) { Value currentValue = Value.Top; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverterImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverterImpl.java deleted file mode 100644 index 067cc743679e..000000000000 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverterImpl.java +++ /dev/null @@ -1,206 +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.ide.util.PropertiesComponent; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.PathManager; -import com.intellij.openapi.util.ThrowableComputable; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.util.io.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.*; -import java.io.DataOutputStream; -import java.util.Arrays; - -import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis.LOG; - -/** - * @author lambdamix - */ -public class BytecodeAnalysisConverterImpl extends BytecodeAnalysisConverter { - private static final int LOGIC_VERSION = 1; - private static final String ENUMERATORS_VERSION_KEY = "BytecodeAnalysisConverter.Enumerators"; - - private File myVersionFile; - private PersistentStringEnumerator myNamesEnumerator; - private PersistentEnumeratorDelegate myCompoundKeyEnumerator; - private int version; - - @Override - public void initComponent() { - - // suffix as an indicator of version - final File keysDir = new File(PathManager.getIndexRoot(), "bytecodekeys"); - final File namesFile = new File(keysDir, "names" + LOGIC_VERSION); - final File compoundKeysFile = new File(keysDir, "compound" + LOGIC_VERSION); - myVersionFile = new File(keysDir, "version" + LOGIC_VERSION); - - version = PropertiesComponent.getInstance().getOrInitInt(ENUMERATORS_VERSION_KEY, 0); - if (ApplicationManager.getApplication().isUnitTestMode()) { - version = _readVersion(); - } - - if (!namesFile.exists() || !compoundKeysFile.exists() || !myVersionFile.exists()) { - LOG.info("No enumerators detected, re-initialization of enumerators."); - IOUtil.deleteAllFilesStartingWith(keysDir); - version++; - } - - try { - IOUtil.openCleanOrResetBroken(new ThrowableComputable() { - @Override - public Void compute() throws IOException { - myNamesEnumerator = new PersistentStringEnumerator(namesFile, true); - myCompoundKeyEnumerator = new IntArrayPersistentEnumerator(compoundKeysFile, new IntArrayKeyDescriptor()); - return null; - } - }, new Runnable() { - @Override - public void run() { - LOG.info("Error during initialization of enumerators in bytecode analysis. Re-initializing."); - IOUtil.deleteAllFilesStartingWith(keysDir); - version++; - } - }); - } - catch (IOException e) { - LOG.error("Re-initialization of enumerators in bytecode analysis failed.", e); - } - PropertiesComponent.getInstance().setValue(ENUMERATORS_VERSION_KEY, String.valueOf(version)); - _saveVersion(); - } - - @Override - public void disposeComponent() { - try { - myNamesEnumerator.close(); - myCompoundKeyEnumerator.close(); - } - catch (IOException e) { - LOG.debug(e); - } - } - - public int _readVersion() { - try { - final DataInputStream is = new DataInputStream(new FileInputStream(myVersionFile)); - try { - return is.readInt(); - } - finally { - is.close(); - } - } - catch (FileNotFoundException ignored) { - } - catch (IOException ignored) { - } - return 0; - } - - private void _saveVersion() { - try { - FileUtil.createIfDoesntExist(myVersionFile); - final DataOutputStream os = new DataOutputStream(new FileOutputStream(myVersionFile)); - try { - os.writeInt(version); - } - finally { - os.close(); - } - } - catch (IOException ignored) { - } - } - - public int getVersion() { - return version; - } - - @Override - protected int enumerateString(@NotNull String s) throws IOException { - return myNamesEnumerator.enumerate(s); - } - - @Override - protected int enumerateCompoundKey(@NotNull int[] key) throws IOException { - return myCompoundKeyEnumerator.enumerate(key); - } - - private static class IntArrayKeyDescriptor implements KeyDescriptor, DifferentSerializableBytesImplyNonEqualityPolicy { - - @Override - public void save(@NotNull DataOutput out, int[] value) throws IOException { - DataInputOutputUtil.writeINT(out, value.length); - for (int i : value) { - DataInputOutputUtil.writeINT(out, i); - } - } - - @Override - public int[] read(@NotNull DataInput in) throws IOException { - int[] value = new int[DataInputOutputUtil.readINT(in)]; - for (int i = 0; i < value.length; i++) { - value[i] = DataInputOutputUtil.readINT(in); - } - return value; - } - - @Override - public int getHashCode(int[] value) { - return Arrays.hashCode(value); - } - - @Override - public boolean isEqual(int[] val1, int[] val2) { - return Arrays.equals(val1, val2); - } - } - - private static class IntArrayPersistentEnumerator extends PersistentEnumeratorDelegate { - private final CachingEnumerator myCache; - - public IntArrayPersistentEnumerator(File compoundKeysFile, IntArrayKeyDescriptor descriptor) throws IOException { - super(compoundKeysFile, descriptor, 1024 * 4); - myCache = new CachingEnumerator(new DataEnumerator() { - @Override - public int enumerate(@Nullable int[] value) throws IOException { - return IntArrayPersistentEnumerator.super.enumerate(value); - } - - @Nullable - @Override - public int[] valueOf(int idx) throws IOException { - return IntArrayPersistentEnumerator.super.valueOf(idx); - } - }, descriptor); - } - - @Override - public int enumerate(@Nullable int[] value) throws IOException { - return myCache.enumerate(value); - } - - @Nullable - @Override - public int[] valueOf(int idx) throws IOException { - return myCache.valueOf(idx); - } - } -} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java index 297b2b694c62..d4ecd481a0ec 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java @@ -34,12 +34,11 @@ import java.io.IOException; /** * @author lambdamix */ -public class BytecodeAnalysisIndex extends FileBasedIndexExtension { - public static final ID NAME = ID.create("bytecodeAnalysis"); - private final EquationExternalizer myExternalizer = new EquationExternalizer(); - private static final DataIndexer INDEXER = - new ClassDataIndexer(BytecodeAnalysisConverter.getInstance()); - private static final SmartLongKeyDescriptor KEY_DESCRIPTOR = new SmartLongKeyDescriptor(); +public class BytecodeAnalysisIndex extends FileBasedIndexExtension { + public static final ID NAME = ID.create("bytecodeAnalysis"); + private final HEquationExternalizer myExternalizer = new HEquationExternalizer(); + private static final ClassDataIndexer INDEXER = new ClassDataIndexer(); + private static final HKeyDescriptor KEY_DESCRIPTOR = new HKeyDescriptor(); private static final int ourInternalVersion = 3; private static boolean ourEnabled = SystemProperties.getBooleanProperty("idea.enable.bytecode.contract.inference", isEnabledByDefault()); @@ -51,25 +50,25 @@ public class BytecodeAnalysisIndex extends FileBasedIndexExtension getName() { + public ID getName() { return NAME; } @NotNull @Override - public DataIndexer getIndexer() { + public DataIndexer getIndexer() { return INDEXER; } @NotNull @Override - public KeyDescriptor getKeyDescriptor() { + public KeyDescriptor getKeyDescriptor() { return KEY_DESCRIPTOR; } @NotNull @Override - public DataExternalizer getValueExternalizer() { + public DataExternalizer getValueExternalizer() { return myExternalizer; } @@ -91,111 +90,85 @@ public class BytecodeAnalysisIndex extends FileBasedIndexExtension, DifferentSerializableBytesImplyNonEqualityPolicy { + private static class HKeyDescriptor implements KeyDescriptor, DifferentSerializableBytesImplyNonEqualityPolicy { + @Override - public void save(@NotNull DataOutput out, IdEquation equation) throws IOException { - long id = equation.id; - int sign = id > 0 ? 1 : -1; - id = Math.abs(id); - int primaryId = (int)(id / BytecodeAnalysisConverter.SHIFT); - int secondaryId = (int)(id % BytecodeAnalysisConverter.SHIFT); - out.writeInt(sign * primaryId); - DataInputOutputUtil.writeINT(out, secondaryId); - IdResult rhs = equation.rhs; - if (rhs instanceof IdFinal) { - IdFinal finalResult = (IdFinal)rhs; + public void save(@NotNull DataOutput out, HKey value) throws IOException { + out.write(value.key); + DataInputOutputUtil.writeINT(out, value.dirKey); + out.writeBoolean(value.stable); + } + + @Override + public HKey read(@NotNull DataInput in) throws IOException { + byte[] bytes = new byte[BytecodeAnalysisConverter.HASH_SIZE]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = in.readByte(); + } + return new HKey(bytes, DataInputOutputUtil.readINT(in), in.readBoolean()); + } + + @Override + public int getHashCode(HKey value) { + return value.hashCode(); + } + + @Override + public boolean isEqual(HKey val1, HKey val2) { + return val1.equals(val2); + } + } + + public static class HEquationExternalizer implements DataExternalizer, DifferentSerializableBytesImplyNonEqualityPolicy { + @Override + public void save(@NotNull DataOutput out, HResult rhs) throws IOException { + if (rhs instanceof HFinal) { + HFinal finalResult = (HFinal)rhs; out.writeBoolean(true); // final flag DataInputOutputUtil.writeINT(out, finalResult.value.ordinal()); } else { - IdPending pendResult = (IdPending)rhs; + HPending pendResult = (HPending)rhs; out.writeBoolean(false); // pending flag DataInputOutputUtil.writeINT(out, pendResult.delta.length); - for (IntIdComponent component : pendResult.delta) { + for (HComponent component : pendResult.delta) { DataInputOutputUtil.writeINT(out, component.value.ordinal()); - long[] ids = component.ids; + HKey[] ids = component.ids; DataInputOutputUtil.writeINT(out, ids.length); - for (long id1 : ids) { - sign = id1 > 0 ? 1 : -1; - id = Math.abs(id1); - primaryId = (int)(id / BytecodeAnalysisConverter.SHIFT); - secondaryId = (int)(id % BytecodeAnalysisConverter.SHIFT); - out.writeInt(sign * primaryId); - DataInputOutputUtil.writeINT(out, secondaryId); + for (HKey id1 : ids) { + KEY_DESCRIPTOR.save(out, id1); } } } } @Override - public IdEquation read(@NotNull DataInput in) throws IOException { - long primaryId = in.readInt(); - int sign = primaryId > 0 ? 1 : -1; - primaryId = Math.abs(primaryId); - int secondaryId = DataInputOutputUtil.readINT(in); - long equationId = sign * (primaryId * BytecodeAnalysisConverter.SHIFT + secondaryId); + public HResult read(@NotNull DataInput in) throws IOException { boolean isFinal = in.readBoolean(); // flag if (isFinal) { int ordinal = DataInputOutputUtil.readINT(in); Value value = Value.values()[ordinal]; - return new IdEquation(equationId, new IdFinal(value)); + return new HFinal(value); } else { int sumLength = DataInputOutputUtil.readINT(in); - IntIdComponent[] components = new IntIdComponent[sumLength]; + HComponent[] components = new HComponent[sumLength]; for (int i = 0; i < sumLength; i++) { int ordinal = DataInputOutputUtil.readINT(in); Value value = Value.values()[ordinal]; int componentSize = DataInputOutputUtil.readINT(in); - long[] ids = new long[componentSize]; + HKey[] ids = new HKey[componentSize]; for (int j = 0; j < componentSize; j++) { - primaryId = in.readInt(); - sign = primaryId > 0 ? 1 : -1; - primaryId = Math.abs(primaryId); - secondaryId = DataInputOutputUtil.readINT(in); - long id = sign * (primaryId * BytecodeAnalysisConverter.SHIFT + secondaryId); - ids[j] = id; + ids[j] = KEY_DESCRIPTOR.read(in); } - components[i] = new IntIdComponent(value, ids); + components[i] = new HComponent(value, ids); } - return new IdEquation(equationId, new IdPending(components)); + return new HPending(components); } } } - - private static class SmartLongKeyDescriptor implements KeyDescriptor, DifferentSerializableBytesImplyNonEqualityPolicy { - @Override - public void save(@NotNull DataOutput out, Long value) throws IOException { - long id = value.longValue(); - int sign = id > 0 ? 1 : -1; - id = Math.abs(id); - int primaryId = (int)(id / BytecodeAnalysisConverter.SHIFT); - int secondaryId = (int)(id % BytecodeAnalysisConverter.SHIFT); - out.writeInt(primaryId * sign); - DataInputOutputUtil.writeINT(out, secondaryId); - } - - @Override - public Long read(@NotNull DataInput in) throws IOException { - long primaryId = in.readInt(); - int sign = primaryId > 0 ? 1 : -1; - primaryId = Math.abs(primaryId); - int secondaryId = DataInputOutputUtil.readINT(in); - return sign * (primaryId * BytecodeAnalysisConverter.SHIFT + secondaryId); - } - - @Override - public int getHashCode(Long value) { - return value.hashCode(); - } - - @Override - public boolean isEqual(Long val1, Long val2) { - return val1.longValue() == val2.longValue(); - } - } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java index 3f859d89edc8..e92bfcba03ec 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java @@ -27,6 +27,7 @@ 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.security.MessageDigest; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -37,29 +38,25 @@ import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalys /** * @author lambdamix */ -public class ClassDataIndexer implements DataIndexer { - final BytecodeAnalysisConverter myConverter; - - public ClassDataIndexer(BytecodeAnalysisConverter converter) { - myConverter = converter; - } +public class ClassDataIndexer implements DataIndexer { @NotNull @Override - public Map map(@NotNull FileContent inputData) { - HashMap map = new HashMap(); + public Map map(@NotNull FileContent inputData) { + HashMap map = new HashMap(); try { - ClassEquations rawEquations = processClass(new ClassReader(inputData.getContent())); + MessageDigest md = BytecodeAnalysisConverter.getMessageDigest(); + ClassEquations rawEquations = processClass(new ClassReader(inputData.getContent()), inputData.getFile().getPresentableUrl()); List> rawParameterEquations = rawEquations.parameterEquations; List> rawContractEquations = rawEquations.contractEquations; for (Equation rawParameterEquation: rawParameterEquations) { - IdEquation equation = myConverter.convert(rawParameterEquation); - map.put(equation.id, equation); + HEquation equation = BytecodeAnalysisConverter.convert(rawParameterEquation, md); + map.put(equation.key, equation.result); } for (Equation rawContractEquation: rawContractEquations) { - IdEquation equation = myConverter.convert(rawContractEquation); - map.put(equation.id, equation); + HEquation equation = BytecodeAnalysisConverter.convert(rawContractEquation, md); + map.put(equation.key, equation.result); } } catch (ProcessCanceledException e) { @@ -83,7 +80,7 @@ public class ClassDataIndexer implements DataIndexer> parameterEquations = new ArrayList>(); final List> contractEquations = new ArrayList>(); @@ -180,6 +177,10 @@ public class ClassDataIndexer implements DataIndexer(new Key(method, new In(i), stable), new Final(Value.Top))); } } if (isReferenceArg && isInterestingResult) { if (leakingParameters[i]) { - contractEquations.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.Null), resultOrigins.getValue(), stable).analyze()); - contractEquations.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.NotNull), resultOrigins.getValue(), stable).analyze()); - } else { + if (resultOrigins.getValue() != null) { + // result origins analysis was ok + contractEquations.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.Null), resultOrigins.getValue(), stable).analyze()); + contractEquations.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.NotNull), resultOrigins.getValue(), stable).analyze()); + } + else { + // result origins analysis failed, approximating to Top + contractEquations.add(new Equation(new Key(method, new InOut(i, Value.Null), stable), new Final(Value.Top))); + contractEquations.add(new Equation(new Key(method, new InOut(i, Value.NotNull), stable), new Final(Value.Top))); + } + } + else { + // parameter is not leaking, so a contract is the same as for the whole method contractEquations.add(new Equation(new Key(method, new InOut(i, Value.Null), stable), resultEquation.getValue().rhs)); contractEquations.add(new Equation(new Key(method, new InOut(i, Value.NotNull), stable), resultEquation.getValue().rhs)); } } if (isBooleanArg && isInterestingResult) { if (leakingParameters[i]) { - contractEquations.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.False), resultOrigins.getValue(), stable).analyze()); - contractEquations.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.True), resultOrigins.getValue(), stable).analyze()); - } else { + if (resultOrigins.getValue() != null) { + // result origins analysis was ok + contractEquations.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.False), resultOrigins.getValue(), stable).analyze()); + contractEquations.add(new InOutAnalysis(new RichControlFlow(graph, dfs), new InOut(i, Value.True), resultOrigins.getValue(), stable).analyze()); + } + else { + // result origins analysis failed, approximating to Top + contractEquations.add(new Equation(new Key(method, new InOut(i, Value.False), stable), new Final(Value.Top))); + contractEquations.add(new Equation(new Key(method, new InOut(i, Value.True), stable), new Final(Value.Top))); + } + } + else { + // parameter is not leaking, so a contract is the same as for the whole method contractEquations.add(new Equation(new Key(method, new InOut(i, Value.False), stable), resultEquation.getValue().rhs)); contractEquations.add(new Equation(new Key(method, new InOut(i, Value.True), stable), resultEquation.getValue().rhs)); } } } - if (isReferenceResult) { - contractEquations.add(resultEquation.getValue()); - } added = true; } else { @@ -222,6 +242,9 @@ public class ClassDataIndexer implements DataIndexer(new Key(method, new Out(), stable), new Final(Value.Top))); + } for (int i = 0; i < argumentTypes.length; i++) { Type argType = argumentTypes[i]; int argSort = argType.getSort(); @@ -240,9 +263,6 @@ public class ClassDataIndexer implements DataIndexer(new Key(method, new InOut(i, Value.True), stable), new Final(Value.Top))); } } - if (isReferenceResult) { - contractEquations.add(new Equation(new Key(method, new Out(), stable), new Final(Value.Top))); - } } } catch (ProcessCanceledException e) { @@ -251,7 +271,7 @@ public class ClassDataIndexer implements DataIndexer contractKeys(@NotNull PsiModifierListOwner owner, HKey primaryKey) { if (owner instanceof PsiMethod) { - TLongArrayList result = BytecodeAnalysisConverter.getInstance().mkInOutKeys((PsiMethod)owner, primaryKey); + ArrayList result = BytecodeAnalysisConverter.mkInOutKeys((PsiMethod)owner, primaryKey); result.add(primaryKey); return result; } - TLongArrayList result = new TLongArrayList(1); + ArrayList result = new ArrayList(1); result.add(primaryKey); return result; } - private Annotations loadAnnotations(@NotNull PsiModifierListOwner owner, long key, TLongArrayList allKeys) - throws IOException, EquationsLimitException { + private Annotations loadAnnotations(@NotNull PsiModifierListOwner owner, @NotNull HKey key, ArrayList allKeys) + throws EquationsLimitException { Annotations result = new Annotations(); if (owner instanceof PsiParameter) { final Solver solver = new Solver(new ELattice(Value.NotNull, Value.Top)); collectEquations(allKeys, solver); - TLongObjectHashMap solutions = solver.solve(); - BytecodeAnalysisConverter.getInstance().addParameterAnnotations(solutions, result); + HashMap solutions = solver.solve(); + BytecodeAnalysisConverter.addParameterAnnotations(solutions, result); } else if (owner instanceof PsiMethod) { final Solver solver = new Solver(new ELattice(Value.Bot, Value.Top)); collectEquations(allKeys, solver); - TLongObjectHashMap solutions = solver.solve(); - BytecodeAnalysisConverter.getInstance().addMethodAnnotations(solutions, result, key, - ((PsiMethod)owner).getParameterList().getParameters().length); + HashMap solutions = solver.solve(); + int arity = ((PsiMethod)owner).getParameterList().getParameters().length; + BytecodeAnalysisConverter.addMethodAnnotations(solutions, result, key, arity); } return result; } - private void collectEquations(TLongArrayList keys, Solver solver) throws EquationsLimitException { + private void collectEquations(ArrayList keys, Solver solver) throws EquationsLimitException { GlobalSearchScope librariesScope = ProjectScope.getLibrariesScope(myProject); - TLongHashSet queued = new TLongHashSet(); - LongStack queue = new LongStack(); + HashSet queued = new HashSet(); + Stack queue = new Stack(); - for (int i = 0; i < keys.size(); i++) { - long key = keys.get(i); + for (HKey key : keys) { queue.push(key); queued.add(key); // stable/unstable - queue.push(-key); - queued.add(-key); + HKey nKey = key.negate(); + queue.push(nKey); + queued.add(nKey); } FileBasedIndex index = FileBasedIndex.getInstance(); @@ -216,20 +218,20 @@ public class ProjectBytecodeAnalysis { throw new EquationsLimitException(); } ProgressManager.checkCanceled(); - List equations = index.getValues(BytecodeAnalysisIndex.NAME, queue.pop(), librariesScope); - for (IdEquation equation : equations) { - IdResult rhs = equation.rhs; - solver.addEquation(equation); - if (rhs instanceof IdPending) { - IdPending intIdPending = (IdPending)rhs; - for (IntIdComponent component : intIdPending.delta) { - for (long depKey : component.ids) { + HKey hKey = queue.pop(); + List results = index.getValues(BytecodeAnalysisIndex.NAME, hKey, librariesScope); + for (HResult result : results) { + solver.addEquation(new HEquation(hKey, result)); + if (result instanceof HPending) { + HPending pending = (HPending)result; + for (HComponent component : pending.delta) { + for (HKey depKey : component.ids) { if (!queued.contains(depKey)) { queue.push(depKey); queued.add(depKey); } // stable/unstable - long swapped = -depKey; + HKey swapped = depKey.negate(); if (!queued.contains(swapped)) { queue.push(swapped); queued.add(swapped); @@ -251,9 +253,9 @@ public class ProjectBytecodeAnalysis { class Annotations { // @NotNull keys - final TLongHashSet notNulls = new TLongHashSet(); + final HashSet notNulls = new HashSet(); // @Contracts - final TLongObjectHashMap contracts = new TLongObjectHashMap(); + final HashMap contracts = new HashMap(); } class EquationsLimitException extends Exception {} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java index 1dec1de8a606..f03452a12dad 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java @@ -15,10 +15,6 @@ */ package com.intellij.codeInspection.bytecodeAnalysis; -import com.intellij.util.containers.LongStack; -import gnu.trove.TLongHashSet; -import gnu.trove.TLongIterator; -import gnu.trove.TLongObjectHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException; @@ -48,88 +44,6 @@ final class ELattice> { } } -// component specialized for ints -final class IntIdComponent { - Value value; - final long[] ids; - - IntIdComponent(Value value, long[] ids) { - this.value = value; - this.ids = ids; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - IntIdComponent that = (IntIdComponent)o; - - if (!Arrays.equals(ids, that.ids)) return false; - if (value != that.value) return false; - - return true; - } - - @Override - public int hashCode() { - return value.ordinal() + Arrays.hashCode(ids); - } - - public boolean remove(long id) { - return IdUtils.remove(ids, id); - } - - public boolean isEmpty() { - return IdUtils.isEmpty(ids); - } - - IntIdComponent copy() { - return new IntIdComponent(value, ids.clone()); - } -} - -class IdUtils { - // removed value - static final long nullId = 0; - - static boolean contains(long[] ids, int id) { - for (long id1 : ids) { - if (id1 == id) return true; - } - - return false; - } - - static boolean isEmpty(long[] ids) { - for (long id : ids) { - if (id != nullId) return false; - } - return true; - } - - static IntIdComponent[] toArray(Collection set) { - IntIdComponent[] result = new IntIdComponent[set.size()]; - int i = 0; - for (IntIdComponent intIdComponent : set) { - result[i] = intIdComponent; - i++; - } - - return result; - } - - static boolean remove(long[] ids, long id) { - boolean removed = false; - for (int i = 0; i < ids.length; i++) { - if (ids[i] == id) { - ids[i] = nullId; - removed = true; - } - } - return removed; - } -} class ResultUtil> { private final ELattice lattice; @@ -235,94 +149,6 @@ final class Pending implements Result { } -interface IdResult {} -// this just wrapper, no need for this really -final class IdFinal implements IdResult { - final Value value; - public IdFinal(Value value) { - this.value = value; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - IdFinal that = (IdFinal)o; - - if (value != that.value) return false; - - return true; - } - - @Override - public int hashCode() { - return value.ordinal(); - } - - @Override - public String toString() { - return super.toString(); - } -} - -final class IdPending implements IdResult { - final IntIdComponent[] delta; - - IdPending(IntIdComponent[] delta) { - this.delta = delta; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof IdPending)) return false; - IdPending pending = (IdPending)o; - return Arrays.equals(delta, pending.delta); - } - - @Override - public int hashCode() { - return Arrays.hashCode(delta); - } - - IdPending copy() { - IntIdComponent[] delta1 = new IntIdComponent[delta.length]; - for (int i = 0; i < delta.length; i++) { - delta1[i] = delta[i].copy(); - } - return new IdPending(delta1); - } -} - -final class IdEquation { - final long id; - final IdResult rhs; - - IdEquation(long id, IdResult rhs) { - this.id = id; - this.rhs = rhs; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof IdEquation)) return false; - - IdEquation equation = (IdEquation)o; - - if (id != equation.id) return false; - if (!rhs.equals(equation.rhs)) return false; - - return true; - } - - @Override - public int hashCode() { - return 31 * ((int)(id ^ (id >>> 32))) + rhs.hashCode(); - } -} - final class Solution { final Id id; final Val value; @@ -352,10 +178,10 @@ final class Solver { private int size = 0; private final ELattice lattice; - private final TLongObjectHashMap dependencies = new TLongObjectHashMap(); - private final TLongObjectHashMap pending = new TLongObjectHashMap(); - private final TLongObjectHashMap solved = new TLongObjectHashMap(); - private final LongStack moving = new LongStack(); + private final HashMap> dependencies = new HashMap>(); + private final HashMap pending = new HashMap(); + private final HashMap solved = new HashMap(); + private final Stack moving = new Stack(); int getSize() { return size; @@ -365,65 +191,67 @@ final class Solver { this.lattice = lattice; } - void addEquation(IdEquation equation) { + private final HashSet queued = new HashSet(); + + void addEquation(HEquation equation) { size ++; - IdResult rhs = equation.rhs; - if (rhs instanceof IdFinal) { - solved.put(equation.id, ((IdFinal) rhs).value); - moving.push(equation.id); - } else if (rhs instanceof IdPending) { - IdPending pendResult = ((IdPending)rhs).copy(); - IdResult norm = normalize(pendResult.delta); - if (norm instanceof IdFinal) { - solved.put(equation.id, ((IdFinal) norm).value); - moving.push(equation.id); + HResult rhs = equation.result; + if (!queued.add(equation.key)) { + ProjectBytecodeAnalysis.LOG.error("duplicate"); + } + if (rhs instanceof HFinal) { + solved.put(equation.key, ((HFinal) rhs).value); + moving.push(equation.key); + } else if (rhs instanceof HPending) { + HPending pendResult = ((HPending)rhs).copy(); + HResult norm = normalize(pendResult.delta); + if (norm instanceof HFinal) { + solved.put(equation.key, ((HFinal) norm).value); + moving.push(equation.key); } else { - IdPending pendResult1 = ((IdPending)rhs).copy(); - for (IntIdComponent component : pendResult1.delta) { - for (long trigger : component.ids) { - TLongHashSet set = dependencies.get(trigger); + HPending pendResult1 = ((HPending)rhs).copy(); + for (HComponent component : pendResult1.delta) { + for (HKey trigger : component.ids) { + HashSet set = dependencies.get(trigger); if (set == null) { - set = new TLongHashSet(); + set = new HashSet(); dependencies.put(trigger, set); } - set.add(equation.id); + set.add(equation.key); } - pending.put(equation.id, pendResult1); + pending.put(equation.key, pendResult1); } } } } - TLongObjectHashMap solve() { + HashMap solve() { while (!moving.empty()) { - long id = moving.pop(); + HKey id = moving.pop(); Value value = solved.get(id); - boolean stable = id > 0; - long[] pIds = stable ? new long[]{id, -id} : new long[]{-id, id}; - Value[] pVals = stable ? new Value[]{value, value} : new Value[]{value, lattice.top}; + HKey[] pIds = id.stable ? new HKey[]{id, id.negate()} : new HKey[]{id.negate(), id}; + Value[] pVals = id.stable ? new Value[]{value, value} : new Value[]{value, lattice.top}; for (int i = 0; i < pIds.length; i++) { - long pId = pIds[i]; + HKey pId = pIds[i]; Value pVal = pVals[i]; - TLongHashSet dIds = dependencies.get(pId); + HashSet dIds = dependencies.get(pId); if (dIds == null) { continue; } - TLongIterator dIdsIterator = dIds.iterator(); - while (dIdsIterator.hasNext()) { - long dId = dIdsIterator.next(); - IdPending pend = pending.remove(dId); + for (HKey dId : dIds) { + HPending pend = pending.remove(dId); if (pend != null) { - IdResult pend1 = substitute(pend, pId, pVal); - if (pend1 instanceof IdFinal) { - IdFinal fi = (IdFinal)pend1; + HResult pend1 = substitute(pend, pId, pVal); + if (pend1 instanceof HFinal) { + HFinal fi = (HFinal)pend1; solved.put(dId, fi.value); moving.push(dId); } else { - pending.put(dId, (IdPending)pend1); + pending.put(dId, (HPending)pend1); } } } @@ -434,9 +262,9 @@ final class Solver { } // substitute id -> value into pending - IdResult substitute(IdPending pending, long id, Value value) { - IntIdComponent[] sum = pending.delta; - for (IntIdComponent intIdComponent : sum) { + HResult substitute(@NotNull HPending pending, @NotNull HKey id, @NotNull Value value) { + HComponent[] sum = pending.delta; + for (HComponent intIdComponent : sum) { if (intIdComponent.remove(id)) { intIdComponent.value = lattice.meet(intIdComponent.value, value); } @@ -444,17 +272,17 @@ final class Solver { return normalize(sum); } - IdResult normalize(IntIdComponent[] sum) { + @NotNull HResult normalize(@NotNull HComponent[] sum) { Value acc = lattice.bot; boolean computableNow = true; - for (IntIdComponent prod : sum) { + for (HComponent prod : sum) { if (prod.isEmpty() || prod.value == lattice.bot) { acc = lattice.join(acc, prod.value); } else { computableNow = false; } } - return (acc == lattice.top || computableNow) ? new IdFinal(acc) : new IdPending(sum); + return (acc == lattice.top || computableNow) ? new HFinal(acc) : new HPending(sum); } } diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIntegrationTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIntegrationTest.java index 516b140cc0b1..48187b9d350a 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIntegrationTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIntegrationTest.java @@ -38,7 +38,7 @@ import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; import com.intellij.util.AsynchConsumer; import org.jetbrains.annotations.Contract; -import java.io.IOException; +import java.security.MessageDigest; import java.util.ArrayList; import java.util.List; @@ -50,7 +50,7 @@ public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestC private InferredAnnotationsManager myInferredAnnotationsManager; private ExternalAnnotationsManager myExternalAnnotationsManager; - + private MessageDigest myMessageDigest; private List diffs = new ArrayList(); @Override @@ -62,6 +62,7 @@ public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestC myInferredAnnotationsManager = InferredAnnotationsManager.getInstance(myModule.getProject()); myExternalAnnotationsManager = ExternalAnnotationsManager.getInstance(myModule.getProject()); + myMessageDigest = BytecodeAnalysisConverter.getMessageDigest(); } private void setUpLibraries() { @@ -127,13 +128,9 @@ public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestC } private void checkMethodAnnotations(PsiMethod method) { - try { - if (ProjectBytecodeAnalysis.getKey(method) == -1) { - return; - } - } - catch (IOException e) { - fail(); + + if (ProjectBytecodeAnalysis.getKey(method, myMessageDigest) == null) { + return; } // not null-result 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 9d26fd804f68..57f9e728f86c 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisTest.java @@ -37,6 +37,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; +import java.security.MessageDigest; import java.util.HashMap; /** @@ -47,15 +48,15 @@ public class BytecodeAnalysisTest extends JavaCodeInsightFixtureTestCase { private final String myClassesProjectRelativePath = "/classes/" + Test01.class.getPackage().getName().replace('.', '/'); private JavaPsiFacade myJavaPsiFacade; private InferredAnnotationsManager myInferredAnnotationsManager; - private BytecodeAnalysisConverter myBytecodeAnalysisConverter; + private MessageDigest myMessageDigest; + @Override protected void setUp() throws Exception { super.setUp(); myJavaPsiFacade = JavaPsiFacade.getInstance(myModule.getProject()); myInferredAnnotationsManager = InferredAnnotationsManager.getInstance(myModule.getProject()); - myBytecodeAnalysisConverter = BytecodeAnalysisConverter.getInstance(); - + myMessageDigest = MessageDigest.getInstance("MD5"); setUpDataClasses(); } @@ -183,17 +184,21 @@ public class BytecodeAnalysisTest extends JavaCodeInsightFixtureTestCase { private void checkCompoundId(Method method, PsiMethod psiMethod, boolean noKey) throws IOException { Direction direction = new Out(); - long psiKey = myBytecodeAnalysisConverter.mkPsiKey(psiMethod, direction); + System.out.println(); + System.out.println(method.internalClassName); + System.out.println(method.methodName); + System.out.println(method.methodDesc); + + + HKey psiKey = BytecodeAnalysisConverter.psiKey(psiMethod, direction, myMessageDigest); if (noKey) { - assertTrue(-1 == psiKey); + assertTrue(null == psiKey); return; } else { - assertFalse(-1 == psiKey); + assertFalse(null == psiKey); } - - long asmKey = myBytecodeAnalysisConverter.mkAsmKey(new Key(method, direction, true)); - + HKey asmKey = BytecodeAnalysisConverter.asmKey(new Key(method, direction, true), myMessageDigest); Assert.assertEquals(asmKey, psiKey); } diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 20536b2a6a61..18892f183ca9 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -26,10 +26,6 @@ com.intellij.util.xml.impl.JavaDomApplicationComponent - - com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisConverter - com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisConverterImpl -