towards compound keys

This commit is contained in:
Ilya Klyuchnikov
2014-07-10 10:35:35 +02:00
committed by peter
parent 68ef299d00
commit d905c85c97
5 changed files with 239 additions and 260 deletions
@@ -19,11 +19,17 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.MostlySingularMultiMap;
import com.intellij.util.io.PersistentStringEnumerator;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TIntObjectIterator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.org.objectweb.asm.Type;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
/**
@@ -39,33 +45,6 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
PersistentStringEnumerator internalKeyEnumerator;
IntIdEquation convert(Equation<Key, Value> equation) throws IOException {
Result<Key, Value> rhs = equation.rhs;
IntIdResult result;
if (rhs instanceof Final) {
result = new IntIdFinal(((Final<Key, Value>)rhs).value);
} else {
Pending<Key, Value> pending = (Pending<Key, Value>)rhs;
Set<Set<Key>> deltaOrig = pending.delta;
IntIdComponent[] components = new IntIdComponent[deltaOrig.size()];
int componentI = 0;
for (Set<Key> keyComponent : deltaOrig) {
int[] ids = new int[keyComponent.size()];
int idI = 0;
for (Key id : keyComponent) {
ids[idI] = internalKeyEnumerator.enumerate(Util.internalKeyString(id));
idI++;
}
IntIdComponent intIdComponent = new IntIdComponent(ids);
components[componentI] = intIdComponent;
componentI++;
}
result = new IntIdPending(pending.infinum, components);
}
int key = internalKeyEnumerator.enumerate(Util.internalKeyString(equation.id));
return new IntIdEquation(key, result);
}
@Override
public void initComponent() {
try {
@@ -93,4 +72,205 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
public String getComponentName() {
return "BytecodeAnalysisConverter";
}
IntIdEquation convert(Equation<Key, Value> equation) throws IOException {
Result<Key, Value> rhs = equation.rhs;
IntIdResult result;
if (rhs instanceof Final) {
result = new IntIdFinal(((Final<Key, Value>)rhs).value);
} else {
Pending<Key, Value> pending = (Pending<Key, Value>)rhs;
Set<Set<Key>> deltaOrig = pending.delta;
IntIdComponent[] components = new IntIdComponent[deltaOrig.size()];
int componentI = 0;
for (Set<Key> keyComponent : deltaOrig) {
int[] ids = new int[keyComponent.size()];
int idI = 0;
for (Key id : keyComponent) {
// TODO refactor here
ids[idI] = internalKeyEnumerator.enumerate(internalKeyString(id));
idI++;
}
IntIdComponent intIdComponent = new IntIdComponent(ids);
components[componentI] = intIdComponent;
componentI++;
}
result = new IntIdPending(pending.infinum, components);
}
int key = internalKeyEnumerator.enumerate(internalKeyString(equation.id));
return new IntIdEquation(key, result);
}
static class InternalKey {
final String annotationKey;
final Direction dir;
InternalKey(String annotationKey, Direction dir) {
this.annotationKey = annotationKey;
this.dir = dir;
}
}
public MostlySingularMultiMap<String, AnnotationData> makeAnnotations(TIntObjectHashMap<Value> internalIdSolutions) {
MostlySingularMultiMap<String, AnnotationData> annotations = new MostlySingularMultiMap<String, AnnotationData>();
HashMap<String, StringBuilder> contracts = new HashMap<String, StringBuilder>();
TIntObjectIterator<Value> iterator = internalIdSolutions.iterator();
for (int i = internalIdSolutions.size(); i-- > 0;) {
iterator.advance();
int inKey = iterator.key();
Value value = iterator.value();
if (value == Value.Top || value == Value.Bot) {
continue;
}
InternalKey key;
try {
String s = internalKeyEnumerator.valueOf(inKey);
key = readInternalKey(s);
}
catch (IOException e) {
throw new RuntimeException(e);
}
if (key != null) {
Direction direction = key.dir;
String baseAnnKey = key.annotationKey;
if (direction instanceof In && value == Value.NotNull) {
String annKey = baseAnnKey + " " + ((In)direction).paramIndex;
// TODO - here
annotations.add(annKey, new AnnotationData("org.jetbrains.annotations.NotNull", ""));
}
else if (direction instanceof Out && value == Value.NotNull) {
// TODO - here
annotations.add(baseAnnKey, new AnnotationData("org.jetbrains.annotations.NotNull", ""));
}
// TODO - sort (normalize) contract clauses
else if (direction instanceof InOut) {
StringBuilder sb = contracts.get(baseAnnKey);
if (sb == null) {
sb = new StringBuilder("\"");
contracts.put(baseAnnKey, sb);
}
else {
sb.append(';');
}
contractElement(sb, calculateArity(baseAnnKey), (InOut)direction, value);
}
}
}
for (Map.Entry<String, StringBuilder> contract : contracts.entrySet()) {
if (!annotations.containsKey(contract.getKey())) {
annotations.add(contract.getKey(), new AnnotationData("org.jetbrains.annotations.Contract", contract.getValue().append('"').toString()));
}
}
return annotations;
}
// TODO - this is a hack for now
static int calculateArity(String annotationKey) {
return annotationKey.split(",").length;
}
static String contractValueString(Value v) {
switch (v) {
case False: return "false";
case True: return "true";
case NotNull: return "!null";
case Null: return "null";
default: return "_";
}
}
static String contractElement(StringBuilder sb, int arity, InOut inOut, Value value) {
for (int i = 0; i < arity; i++) {
Value currentValue = Value.Top;
if (i == inOut.paramIndex) {
currentValue = inOut.inValue;
}
if (i > 0) {
sb.append(',');
}
sb.append(contractValueString(currentValue));
}
sb.append("->");
sb.append(contractValueString(value));
return sb.toString();
}
public static String internalKeyString(Key key) {
return annotationKey(key.method) + ';' + direction2Key(key.direction);
}
public static String direction2Key(Direction dir) {
if (dir instanceof In) {
return "In:" + ((In)dir).paramIndex;
} else if (dir instanceof Out) {
return "Out";
} else {
InOut inOut = (InOut)dir;
return "InOut:" + inOut.paramIndex + ":" + inOut.inValue.name();
}
}
public static InternalKey readInternalKey(String s) {
String[] parts = s.split(";");
String annKey = parts[0];
String[] dirStrings = parts[1].split(":");
if ("In".equals(dirStrings[0])) {
return new InternalKey(annKey, new In(Integer.valueOf(dirStrings[1])));
} else if ("Out".equals(dirStrings[0])) {
return new InternalKey(annKey, new Out());
} else {
return new InternalKey(annKey, new InOut(Integer.valueOf(dirStrings[1]), Value.valueOf(dirStrings[2])));
}
}
public static String annotationKey(Method method) {
if ("<init>".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();
}
}
@@ -42,6 +42,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MostlySingularMultiMap;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.messages.MessageBusConnection;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -49,16 +50,16 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class BytecodeAnalysisHandler extends AbstractProjectComponent {
public class ProjectBytecodeAnalysis extends AbstractProjectComponent {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisHandler");
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis");
private static final List<AnnotationData> NO_DATA = new ArrayList<AnnotationData>(1);
private final PsiManager myPsiManager;
private MostlySingularMultiMap<String, AnnotationData> myAnnotations = null;
public BytecodeAnalysisHandler(Project project, PsiManager psiManager) {
public ProjectBytecodeAnalysis(Project project, PsiManager psiManager) {
super(project);
myPsiManager = psiManager;
@@ -77,6 +78,27 @@ public class BytecodeAnalysisHandler extends AbstractProjectComponent {
});
}
private void loadAnnotations() {
LOG.info("initializing annotations");
final IntIdSolver solver = new IntIdSolver();
FileBasedIndex.getInstance().processValues(
BytecodeAnalysisIndex.NAME, BytecodeAnalysisIndex.KEY, null, new FileBasedIndex.ValueProcessor<Collection<IntIdEquation>>() {
@Override
public boolean process(VirtualFile file, Collection<IntIdEquation> value) {
for (IntIdEquation intIdEquation : value) {
solver.addEquation(intIdEquation);
}
return true;
}
}, ProjectScope.getLibrariesScope(myProject));
LOG.info("equations are constructed");
TIntObjectHashMap<Value> solutions = solver.solve();
LOG.info("equations are solved");
myAnnotations = BytecodeAnalysisConverter.getInstance().makeAnnotations(solutions);
LOG.info("initialized " + myAnnotations.size());
}
// TODO: what follows was just copied/modified from BaseExternalAnnotationsManager
// TODO: refactor?
@Nullable
@@ -94,7 +116,6 @@ public class BytecodeAnalysisHandler extends AbstractProjectComponent {
return data.getAnnotation(this);
}
@Nullable
public PsiAnnotation[] findInferredAnnotations(@NotNull PsiModifierListOwner listOwner) {
List<AnnotationData> result = collectInferredAnnotations(listOwner);
@@ -102,7 +123,7 @@ public class BytecodeAnalysisHandler extends AbstractProjectComponent {
PsiAnnotation[] myResult = ContainerUtil.map2Array(result, PsiAnnotation.EMPTY_ARRAY, new Function<AnnotationData, PsiAnnotation>() {
@Override
public PsiAnnotation fun(AnnotationData data) {
return data.getAnnotation(BytecodeAnalysisHandler.this);
return data.getAnnotation(ProjectBytecodeAnalysis.this);
}
});
String key = getExternalName(listOwner);
@@ -128,19 +149,7 @@ public class BytecodeAnalysisHandler extends AbstractProjectComponent {
SmartList<AnnotationData> result = new SmartList<AnnotationData>();
if (myAnnotations == null) {
LOG.info("initializing annotations");
final IntIdSolver solver = new IntIdSolver();
FileBasedIndex.getInstance().processValues(BytecodeAnalysisIndex.NAME, BytecodeAnalysisIndex.KEY, null, new FileBasedIndex.ValueProcessor<Collection<IntIdEquation>>() {
@Override
public boolean process(VirtualFile file, Collection<IntIdEquation> value) {
for (IntIdEquation intIdEquation : value) {
solver.addEquation(intIdEquation);
}
return true;
}
}, ProjectScope.getLibrariesScope(myProject));
myAnnotations = Util.makeAnnotations(solver.solve());
LOG.info("initialized " + myAnnotations.size());
loadAnnotations();
}
Iterable<AnnotationData> inferred = myAnnotations.get(key);
@@ -192,7 +201,7 @@ class AnnotationData {
}
@NotNull
PsiAnnotation getAnnotation(@NotNull BytecodeAnalysisHandler context) {
PsiAnnotation getAnnotation(@NotNull ProjectBytecodeAnalysis context) {
PsiAnnotation a = annotation;
if (a == null) {
annotation = a = context.createAnnotationFromText("@" + annotationClassFqName + (annotationParameters.isEmpty() ? "" : "("+annotationParameters+")"));
@@ -1,210 +0,0 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection.bytecodeAnalysis;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.MostlySingularMultiMap;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TIntObjectIterator;
import org.jetbrains.org.objectweb.asm.Type;
import java.io.IOException;
import java.util.Map;
public class Util {
static class InternalKey {
final String annotationKey;
final Direction dir;
InternalKey(String annotationKey, Direction dir) {
this.annotationKey = annotationKey;
this.dir = dir;
}
}
public static MostlySingularMultiMap<String, AnnotationData> makeAnnotations(TIntObjectHashMap<Value> internalIdSolutions) {
BytecodeAnalysisConverter lowering = BytecodeAnalysisConverter.getInstance();
MostlySingularMultiMap<String, AnnotationData> annotations = new MostlySingularMultiMap<String, AnnotationData>();
HashMap<String, StringBuilder> contracts = new HashMap<String, StringBuilder>();
TIntObjectIterator<Value> iterator = internalIdSolutions.iterator();
for (int i = internalIdSolutions.size(); i-- > 0;) {
iterator.advance();
int inKey = iterator.key();
Value value = iterator.value();
if (value == Value.Top || value == Value.Bot) {
continue;
}
InternalKey key;
try {
String s = lowering.internalKeyEnumerator.valueOf(inKey);
key = readInternalKey(s);
}
catch (IOException e) {
throw new RuntimeException(e);
}
if (key != null) {
Direction direction = key.dir;
String baseAnnKey = key.annotationKey;
if (direction instanceof In && value == Value.NotNull) {
String annKey = baseAnnKey + " " + ((In)direction).paramIndex;
annotations.add(annKey, new AnnotationData("org.jetbrains.annotations.NotNull", ""));
}
else if (direction instanceof Out && value == Value.NotNull) {
annotations.add(baseAnnKey, new AnnotationData("org.jetbrains.annotations.NotNull", ""));
}
// TODO - sort (normalize) contract clauses
else if (direction instanceof InOut) {
StringBuilder sb = contracts.get(baseAnnKey);
if (sb == null) {
sb = new StringBuilder("\"");
contracts.put(baseAnnKey, sb);
}
else {
sb.append(';');
}
contractElement(sb, calculateArity(baseAnnKey), (InOut)direction, value);
}
}
}
for (Map.Entry<String, StringBuilder> contract : contracts.entrySet()) {
if (!annotations.containsKey(contract.getKey())) {
annotations.add(contract.getKey(), new AnnotationData("org.jetbrains.annotations.Contract", contract.getValue().append('"').toString()));
}
}
return annotations;
}
// TODO - this is a hack for now
static int calculateArity(String annotationKey) {
return annotationKey.split(",").length;
}
static String contractValueString(Value v) {
switch (v) {
case False: return "false";
case True: return "true";
case NotNull: return "!null";
case Null: return "null";
default: return "_";
}
}
static String contractElement(StringBuilder sb, int arity, InOut inOut, Value value) {
for (int i = 0; i < arity; i++) {
Value currentValue = Value.Top;
if (i == inOut.paramIndex) {
currentValue = inOut.inValue;
}
if (i > 0) {
sb.append(',');
}
sb.append(contractValueString(currentValue));
}
sb.append("->");
sb.append(contractValueString(value));
return sb.toString();
}
public static String annotationKey(Method method, Direction dir) {
String annPrefix = annotationKey(method);
if (dir instanceof In) {
return annPrefix + " " + ((In)dir).paramIndex;
} else {
return annPrefix;
}
}
public static String internalKeyString(Key key) {
return annotationKey(key.method) + ';' + direction2Key(key.direction);
}
public static String direction2Key(Direction dir) {
if (dir instanceof In) {
return "In:" + ((In)dir).paramIndex;
} else if (dir instanceof Out) {
return "Out";
} else {
InOut inOut = (InOut)dir;
return "InOut:" + inOut.paramIndex + ":" + inOut.inValue.name();
}
}
public static InternalKey readInternalKey(String s) {
String[] parts = s.split(";");
String annKey = parts[0];
String[] dirStrings = parts[1].split(":");
if ("In".equals(dirStrings[0])) {
return new InternalKey(annKey, new In(Integer.valueOf(dirStrings[1])));
} else if ("Out".equals(dirStrings[0])) {
return new InternalKey(annKey, new Out());
} else {
return new InternalKey(annKey, new InOut(Integer.valueOf(dirStrings[1]), Value.valueOf(dirStrings[2])));
}
}
public static String annotationKey(Method method) {
if ("<init>".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();
}
}
@@ -15,7 +15,7 @@
*/
package com.intellij.codeInsight;
import com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisHandler;
import com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiModifierListOwner;
import org.jetbrains.annotations.NotNull;
@@ -25,12 +25,12 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager {
@Nullable
@Override
public PsiAnnotation findInferredAnnotation(@NotNull PsiModifierListOwner listOwner, @NotNull String annotationFQN) {
return listOwner.getProject().getComponent(BytecodeAnalysisHandler.class).findInferredAnnotation(listOwner, annotationFQN);
return listOwner.getProject().getComponent(ProjectBytecodeAnalysis.class).findInferredAnnotation(listOwner, annotationFQN);
}
@Nullable
@Override
public PsiAnnotation[] findInferredAnnotations(@NotNull PsiModifierListOwner listOwner) {
return listOwner.getProject().getComponent(BytecodeAnalysisHandler.class).findInferredAnnotations(listOwner);
return listOwner.getProject().getComponent(ProjectBytecodeAnalysis.class).findInferredAnnotations(listOwner);
}
}
@@ -231,7 +231,7 @@
</component>
<component>
<implementation-class>com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisHandler</implementation-class>
<implementation-class>com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis</implementation-class>
</component>
</project-components>