mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
inference of @Nullable results for simple methods
This commit is contained in:
+14
-5
@@ -274,16 +274,21 @@ public class BytecodeAnalysisConverter {
|
||||
}
|
||||
|
||||
|
||||
private static int mkDirectionKey(Direction dir) {
|
||||
static int mkDirectionKey(Direction dir) {
|
||||
if (dir == Out) {
|
||||
return 0;
|
||||
} else if (dir instanceof In) {
|
||||
}
|
||||
else if (dir == NullableOut) {
|
||||
return 1;
|
||||
}
|
||||
else if (dir instanceof In) {
|
||||
In in = (In)dir;
|
||||
// nullity mask is 0/1
|
||||
return 8 * in.paramId() + 1 + in.nullityMask;
|
||||
} else {
|
||||
return 2 + 8 * in.paramId() + in.nullityMask;
|
||||
}
|
||||
else {
|
||||
InOut inOut = (InOut)dir;
|
||||
return 8 * inOut.paramId() + 3 + inOut.valueId();
|
||||
return 4 + 8 * inOut.paramId() + inOut.valueId();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +297,11 @@ public class BytecodeAnalysisConverter {
|
||||
if (directionKey == 0) {
|
||||
return Out;
|
||||
}
|
||||
else if (directionKey == 1) {
|
||||
return NullableOut;
|
||||
}
|
||||
else {
|
||||
directionKey--;
|
||||
int paramId = directionKey / 8;
|
||||
int subDirection = directionKey % 8;
|
||||
if (subDirection <= 2) {
|
||||
|
||||
+5
-3
@@ -181,7 +181,7 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
|
||||
final boolean stable,
|
||||
boolean jsr) throws AnalyzerException {
|
||||
|
||||
List<Equation<Key, Value>> result = new ArrayList<Equation<Key, Value>>(argumentTypes.length * 4 + 1);
|
||||
List<Equation<Key, Value>> result = new ArrayList<Equation<Key, Value>>(argumentTypes.length * 4 + 2);
|
||||
boolean maybeLeakingParameter = isInterestingResult;
|
||||
for (Type argType : argumentTypes) {
|
||||
if (ASMUtils.isReferenceType(argType) || (isReferenceResult && ASMUtils.isBooleanType(argType))) {
|
||||
@@ -313,11 +313,12 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
|
||||
boolean isReferenceResult,
|
||||
boolean isBooleanResult,
|
||||
boolean stable) throws AnalyzerException {
|
||||
List<Equation<Key, Value>> result = new ArrayList<Equation<Key, Value>>(argumentTypes.length * 4 + 1);
|
||||
List<Equation<Key, Value>> result = new ArrayList<Equation<Key, Value>>(argumentTypes.length * 4 + 2);
|
||||
CombinedAnalysis analyzer = new CombinedAnalysis(method, graph);
|
||||
analyzer.analyze();
|
||||
if (isReferenceResult) {
|
||||
result.add(analyzer.outContractEquation(stable));
|
||||
result.add(analyzer.nullableResultEquation(stable));
|
||||
}
|
||||
for (int i = 0; i < argumentTypes.length; i++) {
|
||||
Type argType = argumentTypes[i];
|
||||
@@ -343,9 +344,10 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
|
||||
boolean isReferenceResult,
|
||||
boolean isInterestingResult,
|
||||
boolean stable) {
|
||||
List<Equation<Key, Value>> result = new ArrayList<Equation<Key, Value>>(argumentTypes.length * 3 + 1);
|
||||
List<Equation<Key, Value>> result = new ArrayList<Equation<Key, Value>>(argumentTypes.length * 4 + 2);
|
||||
if (isReferenceResult) {
|
||||
result.add(new Equation<Key, Value>(new Key(method, Out, stable), FINAL_TOP));
|
||||
result.add(new Equation<Key, Value>(new Key(method, NullableOut, stable), FINAL_BOT));
|
||||
}
|
||||
for (int i = 0; i < argumentTypes.length; i++) {
|
||||
Type argType = argumentTypes[i];
|
||||
|
||||
+33
-7
@@ -80,17 +80,19 @@ interface CombinedData {
|
||||
}
|
||||
|
||||
final class TrackableCallValue extends BasicValue implements Trackable {
|
||||
final Method method;
|
||||
final boolean stableCall;
|
||||
final List<? extends BasicValue> args;
|
||||
private final int originInsnIndex;
|
||||
final Method method;
|
||||
final List<? extends BasicValue> args;
|
||||
final boolean stableCall;
|
||||
final boolean thisCall;
|
||||
|
||||
TrackableCallValue(int originInsnIndex, Type tp, Method method, boolean stableCall, List<? extends BasicValue> args) {
|
||||
TrackableCallValue(int originInsnIndex, Type tp, Method method, List<? extends BasicValue> args, boolean stableCall, boolean thisCall) {
|
||||
super(tp);
|
||||
this.originInsnIndex = originInsnIndex;
|
||||
this.method = method;
|
||||
this.stableCall = stableCall;
|
||||
this.args = args;
|
||||
this.stableCall = stableCall;
|
||||
this.thisCall = thisCall;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -313,6 +315,28 @@ final class CombinedAnalysis {
|
||||
return new Equation<Key, Value>(key, result);
|
||||
}
|
||||
|
||||
final Equation<Key, Value> nullableResultEquation(boolean stable) {
|
||||
final Key key = new Key(method, NullableOut, stable);
|
||||
final Result<Key, Value> result;
|
||||
if (exception ||
|
||||
returnValue instanceof Trackable && interpreter.dereferencedValues[((Trackable)returnValue).getOriginInsnIndex()]) {
|
||||
result = new Final<Key, Value>(Value.Bot);
|
||||
}
|
||||
else if (returnValue instanceof TrackableCallValue) {
|
||||
TrackableCallValue call = (TrackableCallValue)returnValue;
|
||||
Key callKey = new Key(call.method, NullableOut, call.stableCall || call.thisCall);
|
||||
Set<Key> keys = new SingletonSet<Key>(callKey);
|
||||
result = new Pending<Key, Value>(new SingletonSet<Product<Key, Value>>(new Product<Key, Value>(Value.Null, keys)));
|
||||
}
|
||||
else if (returnValue instanceof TrackableNullValue) {
|
||||
result = new Final<Key, Value>(Value.Null);
|
||||
}
|
||||
else {
|
||||
result = new Final<Key, Value>(Value.Bot);
|
||||
}
|
||||
return new Equation<Key, Value>(key, result);
|
||||
}
|
||||
|
||||
final Frame<BasicValue> createStartFrame() {
|
||||
Frame<BasicValue> frame = new Frame<BasicValue>(methodNode.maxLocals, methodNode.maxStack);
|
||||
Type returnType = Type.getReturnType(methodNode.desc);
|
||||
@@ -545,10 +569,12 @@ final class CombinedInterpreter extends BasicInterpreter {
|
||||
}
|
||||
}
|
||||
}
|
||||
BasicValue receiver = null;
|
||||
if (shift == 1) {
|
||||
values.remove(0);
|
||||
receiver = values.remove(0);
|
||||
}
|
||||
return new TrackableCallValue(origin, retType, method, stable, values);
|
||||
boolean thisCall = (opCode == INVOKEINTERFACE || opCode == INVOKEVIRTUAL) && receiver == ThisValue;
|
||||
return new TrackableCallValue(origin, retType, method, values, stable, thisCall);
|
||||
case MULTIANEWARRAY:
|
||||
return new NotNullValue(super.naryOperation(insn, values).getType());
|
||||
default:
|
||||
|
||||
+31
-5
@@ -103,6 +103,7 @@ public class ProjectBytecodeAnalysis {
|
||||
ArrayList<HKey> allKeys = contractKeys((PsiMethod)listOwner, primaryKey);
|
||||
MethodAnnotations methodAnnotations = loadMethodAnnotations((PsiMethod)listOwner, primaryKey, allKeys);
|
||||
boolean notNull = methodAnnotations.notNulls.contains(primaryKey);
|
||||
boolean nullable = methodAnnotations.nullables.contains(primaryKey);
|
||||
String contractValue = methodAnnotations.contracts.get(primaryKey);
|
||||
if (notNull && contractValue != null) {
|
||||
return new PsiAnnotation[]{
|
||||
@@ -110,11 +111,22 @@ public class ProjectBytecodeAnalysis {
|
||||
createAnnotationFromText("@" + ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT + "(" + contractValue + ")")
|
||||
};
|
||||
}
|
||||
if (nullable && contractValue != null) {
|
||||
return new PsiAnnotation[]{
|
||||
getNullableAnnotation(),
|
||||
createAnnotationFromText("@" + ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT + "(" + contractValue + ")")
|
||||
};
|
||||
}
|
||||
else if (notNull) {
|
||||
return new PsiAnnotation[]{
|
||||
getNotNullAnnotation()
|
||||
};
|
||||
}
|
||||
else if (nullable) {
|
||||
return new PsiAnnotation[]{
|
||||
getNullableAnnotation()
|
||||
};
|
||||
}
|
||||
else if (contractValue != null) {
|
||||
return new PsiAnnotation[]{
|
||||
createAnnotationFromText("@" + ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT + "(" + contractValue + ")")
|
||||
@@ -198,14 +210,14 @@ public class ProjectBytecodeAnalysis {
|
||||
private ParameterAnnotations loadParameterAnnotations(@NotNull HKey notNullKey)
|
||||
throws EquationsLimitException {
|
||||
|
||||
final Solver notNullSolver = new Solver(new ELattice<Value>(Value.NotNull, Value.Top));
|
||||
final Solver notNullSolver = new Solver(new ELattice<Value>(Value.NotNull, Value.Top), Value.Top);
|
||||
collectEquations(Collections.singletonList(notNullKey), notNullSolver);
|
||||
|
||||
HashMap<HKey, Value> notNullSolutions = notNullSolver.solve();
|
||||
boolean notNull =
|
||||
(Value.NotNull == notNullSolutions.get(notNullKey)) || (Value.NotNull == notNullSolutions.get(notNullKey.mkUnstable()));
|
||||
|
||||
final Solver nullableSolver = new Solver(new ELattice<Value>(Value.Null, Value.Top));
|
||||
final Solver nullableSolver = new Solver(new ELattice<Value>(Value.Null, Value.Top), Value.Top);
|
||||
final HKey nullableKey = new HKey(notNullKey.key, notNullKey.dirKey + 1, true);
|
||||
collectEquations(Collections.singletonList(nullableKey), nullableSolver);
|
||||
HashMap<HKey, Value> nullableSolutions = nullableSolver.solve();
|
||||
@@ -217,11 +229,23 @@ public class ProjectBytecodeAnalysis {
|
||||
private MethodAnnotations loadMethodAnnotations(@NotNull PsiMethod owner, @NotNull HKey key, ArrayList<HKey> allKeys)
|
||||
throws EquationsLimitException {
|
||||
MethodAnnotations result = new MethodAnnotations();
|
||||
final Solver solver = new Solver(new ELattice<Value>(Value.Bot, Value.Top));
|
||||
collectEquations(allKeys, solver);
|
||||
HashMap<HKey, Value> solutions = solver.solve();
|
||||
|
||||
final Solver outSolver = new Solver(new ELattice<Value>(Value.Bot, Value.Top), Value.Top);
|
||||
collectEquations(allKeys, outSolver);
|
||||
HashMap<HKey, Value> solutions = outSolver.solve();
|
||||
int arity = owner.getParameterList().getParameters().length;
|
||||
BytecodeAnalysisConverter.addMethodAnnotations(solutions, result, key, arity);
|
||||
|
||||
|
||||
final Solver nullableMethodSolver = new Solver(new ELattice<Value>(Value.Bot, Value.Null), Value.Bot);
|
||||
HKey nullableKey = key.updateDirection(BytecodeAnalysisConverter.mkDirectionKey(NullableOut));
|
||||
collectEquations(Collections.singletonList(nullableKey), nullableMethodSolver);
|
||||
|
||||
HashMap<HKey, Value> nullableSolutions = nullableMethodSolver.solve();
|
||||
if (nullableSolutions.get(nullableKey) == Value.Null) {
|
||||
result.nullables.add(key);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -288,6 +312,8 @@ public class ProjectBytecodeAnalysis {
|
||||
class MethodAnnotations {
|
||||
// @NotNull keys
|
||||
final HashSet<HKey> notNulls = new HashSet<HKey>();
|
||||
// @Nullable keys
|
||||
final HashSet<HKey> nullables = new HashSet<HKey>();
|
||||
// @Contracts
|
||||
final HashMap<HKey, String> contracts = new HashMap<HKey, String>();
|
||||
}
|
||||
|
||||
+4
-2
@@ -265,9 +265,11 @@ final class Solver {
|
||||
|
||||
private final HResultUtil resultUtil;
|
||||
private final HashMap<CoreHKey, HEquation> equations = new HashMap<CoreHKey, HEquation>();
|
||||
private final Value unstableValue;
|
||||
|
||||
Solver(ELattice<Value> lattice) {
|
||||
Solver(ELattice<Value> lattice, Value unstableValue) {
|
||||
this.lattice = lattice;
|
||||
this.unstableValue = unstableValue;
|
||||
resultUtil = new HResultUtil(lattice);
|
||||
}
|
||||
|
||||
@@ -324,7 +326,7 @@ final class Solver {
|
||||
Value value = solved.get(id);
|
||||
|
||||
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};
|
||||
Value[] pVals = id.stable ? new Value[]{value, value} : new Value[]{value, unstableValue};
|
||||
|
||||
for (int i = 0; i < pIds.length; i++) {
|
||||
HKey pId = pIds[i];
|
||||
|
||||
Reference in New Issue
Block a user