diff options
Diffstat (limited to 'java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java')
-rw-r--r-- | java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java | 458 |
1 files changed, 226 insertions, 232 deletions
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java index 0239a661c4ee..84a5851e847d 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,271 +15,208 @@ */ 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<Key, Value> equation) throws IOException { + static DirectionResultPair convert(@NotNull Equation<Key, Value> equation, @NotNull MessageDigest md) { ProgressManager.checkCanceled(); Result<Key, Value> rhs = equation.rhs; - IdResult result; + HResult result; if (rhs instanceof Final) { - result = new IdFinal(((Final<Key, Value>)rhs).value); + result = new HFinal(((Final<Key, Value>)rhs).value); } else { Pending<Key, Value> pending = (Pending<Key, Value>)rhs; Set<Product<Key, Value>> sumOrigin = pending.sum; - IntIdComponent[] components = new IntIdComponent[sumOrigin.size()]; + HComponent[] components = new HComponent[sumOrigin.size()]; int componentI = 0; for (Product<Key, Value> 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); - } - - long key = equation.id.stable ? rawKey : -rawKey; - return new IdEquation(key, result); + return new DirectionResultPair(mkDirectionKey(equation.id.direction), result); } - public long mkAsmKey(@NotNull Key key) throws IOException { - long baseKey = mkAsmSignatureKey(key.method); - long directionKey = mkDirectionKey(key.direction); - return baseKey * SHIFT + directionKey; + /** + * Converts an asm method key to a small hash key (HKey) + */ + @NotNull + public static HKey asmKey(@NotNull Key key, @NotNull MessageDigest md) { + byte[] classDigest = md.digest(key.method.internalClassName.getBytes()); + md.update(key.method.methodName.getBytes()); + md.update(key.method.methodDesc.getBytes()); + byte[] sigDigest = md.digest(); + 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); } - private static int mkDirectionKey(Direction dir) throws IOException { - if (dir instanceof Out) { - return 0; - } else if (dir instanceof In) { - In in = (In)dir; - return 8 * in.paramId() + 1; - } else { - InOut inOut = (InOut)dir; - return 8 * inOut.paramId() + 2 + inOut.valueId(); + /** + * 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; } - } - - @NotNull - private static Direction extractDirection(int directionKey) { - if (directionKey == 0) { - return new Out(); + byte[] classDigest = psiClassDigest(psiClass, md); + if (classDigest == null) { + return null; } - else { - int paramId = directionKey / 8; - int subDirection = directionKey % 8; - if (subDirection == 1) { - return new In(paramId); - } - else { - return new InOut(paramId, Value.values()[subDirection - 2]); - } + 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); } - // 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]); + @Nullable + private static byte[] psiClassDigest(@NotNull PsiClass psiClass, @NotNull MessageDigest md) { + String descriptor = descriptor(psiClass, 0, false); + if (descriptor == null) { + return null; } - return enumerateCompoundKey(sigKey); + return md.digest(descriptor.getBytes()); } - 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; + @Nullable + private static byte[] methodDigest(@NotNull PsiMethod psiMethod, @NotNull MessageDigest md) { + String descriptor = descriptor(psiMethod); + if (descriptor == null) { + return null; } - int[] classKey = new int[]{enumerateString(packageName), enumerateString(simpleName)}; - return enumerateCompoundKey(classKey); + return md.digest(descriptor.getBytes()); } - 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 { + @Nullable + private static String descriptor(@NotNull PsiMethod psiMethod) { + StringBuilder sb = new StringBuilder(); final PsiClass psiClass = PsiTreeUtil.getParentOfType(psiMethod, PsiClass.class, false); if (psiClass == null) { - LOG.debug("PsiClass was null for " + psiMethod.getName()); - return -1; + 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(); - 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("<init>"); - } else { - shortSigKey[0] = mkPsiTypeKey(returnType); - shortSigKey[1] = enumerateString(psiMethod.getName()); - } + sb.append(returnType == null ? "<init>" : psiMethod.getName()); + sb.append('('); + + String desc; + 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; + desc = descriptor(outerClass, 0, true); + if (desc == null) { + return null; } + sb.append(desc); } - - int[] sigKey = new int[2]; - int classKey = mkPsiClassKey(psiClass, 0); - if (classKey == -1) { - return -1; + for (PsiParameter parameter : parameters) { + desc = descriptor(parameter.getType()); + if (desc == null) { + return null; + } + sb.append(desc); } - 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); - 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))); - } + sb.append(')'); + if (returnType == null) { + sb.append('V'); + } else { + desc = descriptor(returnType); + if (desc == null) { + return null; } else { - keys.add(primaryKey + mkDirectionKey(new InOut(i, Value.NotNull))); - keys.add(primaryKey + mkDirectionKey(new InOut(i, Value.Null))); + sb.append(desc); } } - return keys; + return sb.toString(); } - - private int mkPsiClassKey(PsiClass psiClass, int dimensions) throws IOException { + @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 -1; + return null; } PsiClassOwner psiFile = (PsiClassOwner)containingFile; String packageName = psiFile.getPackageName(); String qname = psiClass.getQualifiedName(); if (qname == null) { - return -1; + return null; } - String className = qname; + String className; 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()); + className = qname.replace('.', '$'); + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < dimensions; i++) { + sb.append('['); + } + if (full) { + sb.append('L'); } - return enumerateCompoundKey(classKey); + if (packageName.length() > 0) { + sb.append(packageName.replace('.', '/')); + sb.append('/'); + } + sb.append(className); + if (full) { + sb.append(';'); + } + return sb.toString(); } - private int mkPsiTypeKey(PsiType psiType) throws IOException { + @Nullable + private static String descriptor(@NotNull PsiType psiType) { int dimensions = 0; psiType = TypeConversionUtil.erasure(psiType); if (psiType instanceof PsiArrayType) { @@ -289,56 +226,130 @@ public abstract class BytecodeAnalysisConverter implements ApplicationComponent } if (psiType instanceof PsiClassType) { - // no resolve() -> no package/class split PsiClass psiClass = ((PsiClassType)psiType).resolve(); if (psiClass != null) { - return mkPsiClassKey(psiClass, dimensions); + return descriptor(psiClass, dimensions, true); } else { LOG.debug("resolve was null for " + ((PsiClassType)psiType).getClassName()); - return -1; + return null; } } 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()); + 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'); } - return enumerateCompoundKey(classKey); + 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 -1; + return null; } - public void addMethodAnnotations(TLongObjectHashMap<Value> internalIdSolutions, Annotations annotations, long methodKey, int arity) { + private static int mkDirectionKey(Direction dir) { + if (dir instanceof Out) { + return 0; + } else if (dir instanceof In) { + In in = (In)dir; + // nullity mask is 0/1 + return 8 * in.paramId() + 1 + in.nullityMask; + } else { + InOut inOut = (InOut)dir; + return 8 * inOut.paramId() + 3 + inOut.valueId(); + } + } - List<String> clauses = new ArrayList<String>(); - TLongObjectIterator<Value> solutionsIterator = internalIdSolutions.iterator(); + @NotNull + private static Direction extractDirection(int directionKey) { + if (directionKey == 0) { + return new Out(); + } + else { + int paramId = directionKey / 8; + int subDirection = directionKey % 8; + if (subDirection <= 2) { + return new In(paramId, subDirection - 1); + } + else { + return new InOut(paramId, Value.values()[subDirection - 3]); + } + } + } + + /** + * Given a PSI method and its primary HKey enumerate all contract keys for it. + */ + @NotNull + public static ArrayList<HKey> mkInOutKeys(@NotNull PsiMethod psiMethod, @NotNull HKey primaryKey) { + PsiParameter[] parameters = psiMethod.getParameterList().getParameters(); + ArrayList<HKey> keys = new ArrayList<HKey>(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.updateDirection(mkDirectionKey(new InOut(i, Value.False)))); + keys.add(primaryKey.updateDirection(mkDirectionKey(new InOut(i, Value.True)))); + } + } else { + keys.add(primaryKey.updateDirection(mkDirectionKey(new InOut(i, Value.NotNull)))); + keys.add(primaryKey.updateDirection(mkDirectionKey(new InOut(i, Value.Null)))); + } + } + return keys; + } - TLongHashSet notNulls = annotations.notNulls; - TLongObjectHashMap<String> contracts = annotations.contracts; - for (int i = internalIdSolutions.size(); i-- > 0;) { - solutionsIterator.advance(); - long key = Math.abs(solutionsIterator.key()); - Value value = solutionsIterator.value(); + /** + * Given `solution` of all dependencies of a method with the `methodKey`, converts this solution into annotations. + * + * @param solution solution of equations + * @param methodAnnotations 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<HKey, Value> solution, @NotNull MethodAnnotations methodAnnotations, @NotNull HKey methodKey, int arity) { + List<String> clauses = new ArrayList<String>(); + HashSet<HKey> notNulls = methodAnnotations.notNulls; + HashMap<HKey, String> contracts = methodAnnotations.contracts; + for (Map.Entry<HKey, Value> 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,7 @@ public abstract class BytecodeAnalysisConverter implements ApplicationComponent } } - public void addParameterAnnotations(TLongObjectHashMap<Value> internalIdSolutions, Annotations annotations) { - TLongObjectIterator<Value> 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(); - if (value == Value.Top || value == Value.Bot) { - continue; - } - Direction direction = extractDirection((int)(key % SHIFT)); - 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 +374,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; |