mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 21:11:28 +07:00
BytecodeAnalysis: infer failing contracts; infer contracts for input boolean arguments; squash inferred contracts; refactoring
This commit is contained in:
@@ -30,9 +30,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.intellij.codeInspection.bytecodeAnalysis.Direction.In;
|
||||
import static com.intellij.codeInspection.bytecodeAnalysis.Direction.InOut;
|
||||
|
||||
class AbstractValues {
|
||||
static final class ParamValue extends BasicValue {
|
||||
ParamValue(Type tp) {
|
||||
@@ -79,6 +76,15 @@ class AbstractValues {
|
||||
}
|
||||
}
|
||||
|
||||
static final class NthParamValue extends BasicValue {
|
||||
final int n;
|
||||
|
||||
public NthParamValue(Type type, int n) {
|
||||
super(type);
|
||||
this.n = n;
|
||||
}
|
||||
}
|
||||
|
||||
static final BasicValue CLASS_VALUE = new NotNullValue(Type.getObjectType("java/lang/Class"));
|
||||
static final BasicValue METHOD_VALUE = new NotNullValue(Type.getObjectType("java/lang/invoke/MethodType"));
|
||||
static final BasicValue STRING_VALUE = new NotNullValue(Type.getObjectType("java/lang/String"));
|
||||
@@ -272,12 +278,11 @@ abstract class Analysis<Res> {
|
||||
}
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
BasicValue value;
|
||||
if (direction instanceof InOut && ((InOut)direction).paramIndex == i ||
|
||||
direction instanceof In && ((In)direction).paramIndex == i) {
|
||||
if (direction instanceof Direction.ParamIdBasedDirection && ((Direction.ParamIdBasedDirection)direction).paramIndex == i) {
|
||||
value = new AbstractValues.ParamValue(args[i]);
|
||||
}
|
||||
else {
|
||||
value = new BasicValue(args[i]);
|
||||
value = new AbstractValues.NthParamValue(args[i], i);
|
||||
}
|
||||
frame.setLocal(local++, value);
|
||||
if (args[i].getSize() == 2) {
|
||||
|
||||
@@ -15,13 +15,15 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.bytecodeAnalysis;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.MethodContract.ValueConstraint;
|
||||
import com.intellij.codeInspection.dataFlow.StandardMethodContract;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.util.ThreadLocalCachedValue;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -114,7 +116,7 @@ public class BytecodeAnalysisConverter {
|
||||
}
|
||||
hResult = new HEffects(hEffects);
|
||||
}
|
||||
return new DirectionResultPair(mkDirectionKey(equation.id.direction), hResult);
|
||||
return new DirectionResultPair(equation.id.direction.asInt(), hResult);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +131,7 @@ public class BytecodeAnalysisConverter {
|
||||
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, key.negated);
|
||||
return new HKey(digest, key.direction.asInt(), key.stable, key.negated);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,7 +155,7 @@ public class BytecodeAnalysisConverter {
|
||||
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, false);
|
||||
return new HKey(digest, direction.asInt(), true, false);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -272,7 +274,7 @@ public class BytecodeAnalysisConverter {
|
||||
return descriptor(psiClass, dimensions, true);
|
||||
}
|
||||
else {
|
||||
LOG.debug("resolve was null for " + ((PsiClassType)psiType).getClassName());
|
||||
LOG.debug("resolve was null for " + psiType.getCanonicalText());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -314,79 +316,6 @@ public class BytecodeAnalysisConverter {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts Direction object to int.
|
||||
*
|
||||
* 0 - Out
|
||||
* 1 - NullableOut
|
||||
* 2 - Pure
|
||||
*
|
||||
* 3 - 0-th NOT_NULL
|
||||
* 4 - 0-th NULLABLE
|
||||
* ...
|
||||
*
|
||||
* 11 - 1-st NOT_NULL
|
||||
* 12 - 1-st NULLABLE
|
||||
*
|
||||
* @param dir direction of analysis
|
||||
* @return unique int for direction
|
||||
*/
|
||||
static int mkDirectionKey(Direction dir) {
|
||||
if (dir == Out) {
|
||||
return 0;
|
||||
}
|
||||
else if (dir == NullableOut) {
|
||||
return 1;
|
||||
}
|
||||
else if (dir == Pure) {
|
||||
return 2;
|
||||
}
|
||||
else if (dir instanceof In) {
|
||||
In in = (In)dir;
|
||||
// nullity mask is 0/1
|
||||
return 3 + 8 * in.paramId() + in.nullityMask;
|
||||
}
|
||||
else {
|
||||
// valueId is [1-5]
|
||||
InOut inOut = (InOut)dir;
|
||||
return 3 + 8 * inOut.paramId() + 2 + inOut.valueId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts int to Direction object.
|
||||
*
|
||||
* @param directionKey int representation of direction
|
||||
* @return Direction object
|
||||
* @see #mkDirectionKey(Direction)
|
||||
*/
|
||||
@NotNull
|
||||
static Direction extractDirection(int directionKey) {
|
||||
if (directionKey == 0) {
|
||||
return Out;
|
||||
}
|
||||
else if (directionKey == 1) {
|
||||
return NullableOut;
|
||||
}
|
||||
else if (directionKey == 2) {
|
||||
return Pure;
|
||||
}
|
||||
else {
|
||||
int paramKey = directionKey - 3;
|
||||
int paramId = paramKey / 8;
|
||||
// shifting first 3 values - now we have key [0 - 7]
|
||||
int subDirectionId = paramKey % 8;
|
||||
// 0 - 1 - @NotNull, @Nullable, parameter
|
||||
if (subDirectionId <= 1) {
|
||||
return new In(paramId, subDirectionId);
|
||||
}
|
||||
else {
|
||||
int valueId = subDirectionId - 2;
|
||||
return new InOut(paramId, Value.values()[valueId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a PSI method and its primary HKey enumerate all contract keys for it.
|
||||
*
|
||||
@@ -401,8 +330,15 @@ public class BytecodeAnalysisConverter {
|
||||
keys.add(primaryKey);
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
if (!(parameters[i].getType() instanceof PsiPrimitiveType)) {
|
||||
keys.add(primaryKey.updateDirection(mkDirectionKey(new InOut(i, Value.NotNull))));
|
||||
keys.add(primaryKey.updateDirection(mkDirectionKey(new InOut(i, Value.Null))));
|
||||
keys.add(primaryKey.withDirection(new InOut(i, Value.NotNull)));
|
||||
keys.add(primaryKey.withDirection(new InOut(i, Value.Null)));
|
||||
keys.add(primaryKey.withDirection(new InThrow(i, Value.NotNull)));
|
||||
keys.add(primaryKey.withDirection(new InThrow(i, Value.Null)));
|
||||
} else if (PsiType.BOOLEAN.equals(parameters[i].getType())) {
|
||||
keys.add(primaryKey.withDirection(new InOut(i, Value.True)));
|
||||
keys.add(primaryKey.withDirection(new InOut(i, Value.False)));
|
||||
keys.add(primaryKey.withDirection(new InThrow(i, Value.True)));
|
||||
keys.add(primaryKey.withDirection(new InThrow(i, Value.False)));
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
@@ -417,7 +353,7 @@ public class BytecodeAnalysisConverter {
|
||||
* @param arity arity of this method (hint for constructing @Contract annotations)
|
||||
*/
|
||||
public static void addMethodAnnotations(@NotNull Map<HKey, Value> solution, @NotNull MethodAnnotations methodAnnotations, @NotNull HKey methodKey, int arity) {
|
||||
List<String> contractClauses = new ArrayList<>(arity * 2);
|
||||
List<StandardMethodContract> contractClauses = new ArrayList<>();
|
||||
Set<HKey> notNulls = methodAnnotations.notNulls;
|
||||
Set<HKey> pures = methodAnnotations.pures;
|
||||
Map<HKey, String> contracts = methodAnnotations.contractsValues;
|
||||
@@ -425,11 +361,11 @@ public class BytecodeAnalysisConverter {
|
||||
for (Map.Entry<HKey, Value> entry : solution.entrySet()) {
|
||||
// NB: keys from Psi are always stable, so we need to stabilize keys from equations
|
||||
Value value = entry.getValue();
|
||||
if (value == Value.Top || value == Value.Bot) {
|
||||
if (value == Value.Top || value == Value.Bot || (value == Value.Fail && !pures.contains(methodKey))) {
|
||||
continue;
|
||||
}
|
||||
HKey key = entry.getKey().mkStable();
|
||||
Direction direction = extractDirection(key.dirKey);
|
||||
Direction direction = key.getDirection();
|
||||
HKey baseKey = key.mkBase();
|
||||
if (!methodKey.equals(baseKey)) {
|
||||
continue;
|
||||
@@ -440,22 +376,70 @@ public class BytecodeAnalysisConverter {
|
||||
else if (value == Value.Pure && direction == Pure) {
|
||||
pures.add(methodKey);
|
||||
}
|
||||
else if (direction instanceof InOut) {
|
||||
contractClauses.add(contractElement(arity, (InOut)direction, value));
|
||||
else if (direction instanceof ParamValueBasedDirection) {
|
||||
contractClauses.add(contractElement(arity, (ParamValueBasedDirection)direction, value));
|
||||
}
|
||||
}
|
||||
|
||||
// no contract clauses for @NotNull methods
|
||||
if (!notNulls.contains(methodKey) && !contractClauses.isEmpty()) {
|
||||
// no contract clauses for @NotNull methods
|
||||
Collections.sort(contractClauses);
|
||||
StringBuilder sb = new StringBuilder("\"");
|
||||
StringUtil.join(contractClauses, ";", sb);
|
||||
sb.append('"');
|
||||
contracts.put(methodKey, sb.toString().intern());
|
||||
Map<Boolean, List<StandardMethodContract>> partition =
|
||||
StreamEx.of(contractClauses).partitioningBy(c -> c.getReturnValue() == ValueConstraint.THROW_EXCEPTION);
|
||||
List<StandardMethodContract> failingContracts = squashContracts(partition.get(true));
|
||||
List<StandardMethodContract> nonFailingContracts = squashContracts(partition.get(false));
|
||||
// Sometimes "null,_->!null;!null,_->!null" contracts are inferred for some reason
|
||||
// They are squashed to "_,_->!null" which is better expressed as @NotNull annotation
|
||||
if(nonFailingContracts.size() == 1) {
|
||||
StandardMethodContract contract = nonFailingContracts.get(0);
|
||||
if(contract.getReturnValue() == ValueConstraint.NOT_NULL_VALUE && contract.isTrivial()) {
|
||||
nonFailingContracts = Collections.emptyList();
|
||||
notNulls.add(methodKey);
|
||||
}
|
||||
}
|
||||
// Failing contracts go first
|
||||
String result = StreamEx.of(failingContracts, nonFailingContracts)
|
||||
.flatMap(list -> list.stream()
|
||||
.map(Object::toString)
|
||||
.map(str -> str.replace(" ", "")) // for compatibility with existing tests
|
||||
.sorted())
|
||||
.joining(";");
|
||||
if(!result.isEmpty()) {
|
||||
contracts.put(methodKey, '"'+result+'"');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<StandardMethodContract> squashContracts(List<StandardMethodContract> contractClauses) {
|
||||
// If there's a pair of contracts yielding the same value like "null,_->true", "!null,_->true"
|
||||
// then trivial contract should be used like "_,_->true"
|
||||
StandardMethodContract soleContract = StreamEx.ofPairs(contractClauses, (c1, c2) -> {
|
||||
if (c1.getReturnValue() != c2.getReturnValue()) return null;
|
||||
int idx = -1;
|
||||
for (int i = 0; i < c1.arguments.length; i++) {
|
||||
ValueConstraint left = c1.arguments[i];
|
||||
ValueConstraint right = c2.arguments[i];
|
||||
if(left == ValueConstraint.ANY_VALUE && right == ValueConstraint.ANY_VALUE) continue;
|
||||
if(idx >= 0) return null;
|
||||
if(left == ValueConstraint.NOT_NULL_VALUE && right == ValueConstraint.NULL_VALUE ||
|
||||
left == ValueConstraint.NULL_VALUE && right == ValueConstraint.NOT_NULL_VALUE ||
|
||||
left == ValueConstraint.TRUE_VALUE && right == ValueConstraint.FALSE_VALUE ||
|
||||
left == ValueConstraint.FALSE_VALUE && right == ValueConstraint.TRUE_VALUE) {
|
||||
idx = i;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return c1;
|
||||
}).nonNull().findFirst().orElse(null);
|
||||
if(soleContract != null) {
|
||||
Arrays.fill(soleContract.arguments, ValueConstraint.ANY_VALUE);
|
||||
contractClauses = Collections.singletonList(soleContract);
|
||||
}
|
||||
return contractClauses;
|
||||
}
|
||||
|
||||
public static void addEffectAnnotations(Map<HKey, Set<HEffectQuantum>> puritySolutions,
|
||||
MethodAnnotations result,
|
||||
HKey methodKey,
|
||||
@@ -474,31 +458,11 @@ public class BytecodeAnalysisConverter {
|
||||
}
|
||||
}
|
||||
|
||||
private static String contractValueString(@NotNull Value v) {
|
||||
switch (v) {
|
||||
case False: return "false";
|
||||
case True: return "true";
|
||||
case NotNull: return "!null";
|
||||
case Null: return "null";
|
||||
default: return "_";
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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();
|
||||
private static StandardMethodContract contractElement(int arity, ParamValueBasedDirection inOut, Value value) {
|
||||
final ValueConstraint[] constraints = new ValueConstraint[arity];
|
||||
Arrays.fill(constraints, ValueConstraint.ANY_VALUE);
|
||||
constraints[inOut.paramIndex] = inOut.inValue.toValueConstraint();
|
||||
return new StandardMethodContract(constraints, value.toValueConstraint());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public class BytecodeAnalysisIndex extends ScalarIndexExtension<Bytes> {
|
||||
private static final ID<Bytes, Void> NAME = ID.create("bytecodeAnalysis");
|
||||
private static final HKeyDescriptor KEY_DESCRIPTOR = new HKeyDescriptor();
|
||||
private static final VirtualFileGist<Map<Bytes, HEquations>> ourGist = GistManager.getInstance().newVirtualFileGist(
|
||||
"BytecodeAnalysisIndex", 2, new HEquationsExternalizer(), new ClassDataIndexer());
|
||||
"BytecodeAnalysisIndex", 3, new HEquationsExternalizer(), new ClassDataIndexer());
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -256,7 +256,7 @@ public class BytecodeAnalysisIndex extends ScalarIndexExtension<Bytes> {
|
||||
ArrayList<DirectionResultPair> results = new ArrayList<>(size);
|
||||
for (int k = 0; k < size; k++) {
|
||||
int directionKey = DataInputOutputUtil.readINT(in);
|
||||
Direction direction = BytecodeAnalysisConverter.extractDirection(directionKey);
|
||||
Direction direction = Direction.fromInt(directionKey);
|
||||
if (direction == Direction.Pure) {
|
||||
Set<HEffectQuantum> effects = new HashSet<>();
|
||||
int effectsSize = DataInputOutputUtil.readINT(in);
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.gist.VirtualFileGist;
|
||||
import one.util.streamex.EntryStream;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.org.objectweb.asm.*;
|
||||
@@ -29,10 +30,10 @@ 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;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*;
|
||||
import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis.LOG;
|
||||
@@ -48,6 +49,7 @@ import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalys
|
||||
public class ClassDataIndexer implements VirtualFileGist.GistCalculator<Map<Bytes, HEquations>> {
|
||||
|
||||
public static final Final FINAL_TOP = new Final(Value.Top);
|
||||
public static final Final FINAL_FAIL = new Final(Value.Fail);
|
||||
public static final Final FINAL_BOT = new Final(Value.Bot);
|
||||
public static final Final FINAL_NOT_NULL = new Final(Value.NotNull);
|
||||
public static final Final FINAL_NULL = new Final(Value.Null);
|
||||
@@ -136,11 +138,6 @@ public class ClassDataIndexer implements VirtualFileGist.GistCalculator<Map<Byte
|
||||
List<Equation> equations = new ArrayList<>(argumentTypes.length * 4 + 3);
|
||||
equations.add(PurityAnalysis.analyze(method, methodNode, stable));
|
||||
|
||||
if (argumentTypes.length == 0 && !isInterestingResult) {
|
||||
// no need to continue analysis
|
||||
return equations;
|
||||
}
|
||||
|
||||
try {
|
||||
final ControlFlowGraph graph = ControlFlowGraph.build(className, methodNode, jsr);
|
||||
if (graph.transitions.length > 0) {
|
||||
@@ -158,14 +155,14 @@ public class ClassDataIndexer implements VirtualFileGist.GistCalculator<Map<Byte
|
||||
RichControlFlow richControlFlow = new RichControlFlow(graph, dfs);
|
||||
if (richControlFlow.reducible()) {
|
||||
NegationAnalysis negated = tryNegation(method, argumentTypes, graph, isBooleanResult, dfs, jsr);
|
||||
processBranchingMethod(method, methodNode, richControlFlow, argumentTypes, isReferenceResult, isBooleanResult, stable, jsr, equations, negated);
|
||||
processBranchingMethod(method, methodNode, richControlFlow, argumentTypes, resultType, stable, jsr, equations, negated);
|
||||
return equations;
|
||||
}
|
||||
LOG.debug(method + ": CFG is not reducible");
|
||||
}
|
||||
// simple
|
||||
else {
|
||||
processNonBranchingMethod(method, argumentTypes, graph, isReferenceResult, isBooleanResult, stable, equations);
|
||||
processNonBranchingMethod(method, argumentTypes, graph, resultType, stable, equations);
|
||||
return equations;
|
||||
}
|
||||
}
|
||||
@@ -279,33 +276,22 @@ public class ClassDataIndexer implements VirtualFileGist.GistCalculator<Map<Byte
|
||||
final MethodNode methodNode,
|
||||
final RichControlFlow richControlFlow,
|
||||
Type[] argumentTypes,
|
||||
boolean isReferenceResult,
|
||||
boolean isBooleanResult,
|
||||
Type resultType,
|
||||
final boolean stable,
|
||||
boolean jsr,
|
||||
List<Equation> result,
|
||||
NegationAnalysis negatedAnalysis) throws AnalyzerException {
|
||||
final boolean isReferenceResult = ASMUtils.isReferenceType(resultType);
|
||||
final boolean isBooleanResult = ASMUtils.isBooleanType(resultType);
|
||||
boolean isInterestingResult = isBooleanResult || isReferenceResult;
|
||||
boolean maybeLeakingParameter = isInterestingResult;
|
||||
for (Type argType : argumentTypes) {
|
||||
if (ASMUtils.isReferenceType(argType)) {
|
||||
maybeLeakingParameter = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final LeakingParameters leakingParametersAndFrames =
|
||||
maybeLeakingParameter ? leakingParametersAndFrames(method, methodNode, argumentTypes, jsr) : null;
|
||||
final LeakingParameters leakingParametersAndFrames = leakingParametersAndFrames(method, methodNode, argumentTypes, jsr);
|
||||
|
||||
boolean[] leakingParameters =
|
||||
leakingParametersAndFrames != null ? leakingParametersAndFrames.parameters : null;
|
||||
boolean[] leakingNullableParameters =
|
||||
leakingParametersAndFrames != null ? leakingParametersAndFrames.nullableParameters : null;
|
||||
boolean[] leakingParameters = leakingParametersAndFrames.parameters;
|
||||
boolean[] leakingNullableParameters = leakingParametersAndFrames.nullableParameters;
|
||||
|
||||
final boolean[] origins =
|
||||
isInterestingResult ?
|
||||
OriginsAnalysis.resultOrigins(leakingParametersAndFrames.frames, methodNode.instructions, richControlFlow.controlFlow) :
|
||||
null;
|
||||
OriginsAnalysis.resultOrigins(leakingParametersAndFrames.frames, methodNode.instructions, richControlFlow.controlFlow);
|
||||
|
||||
Equation outEquation =
|
||||
isInterestingResult ?
|
||||
@@ -316,6 +302,18 @@ public class ClassDataIndexer implements VirtualFileGist.GistCalculator<Map<Byte
|
||||
result.add(outEquation);
|
||||
result.add(new Equation(new Key(method, NullableOut, stable), NullableMethodAnalysis.analyze(methodNode, origins, jsr)));
|
||||
}
|
||||
final boolean shouldInferNonTrivialFailingContracts;
|
||||
final Equation throwEquation;
|
||||
if(methodNode.name.equals("<init>")) {
|
||||
// Do not infer failing contracts for constructors
|
||||
shouldInferNonTrivialFailingContracts = false;
|
||||
throwEquation = new Equation(new Key(method, Throw, stable), FINAL_TOP);
|
||||
} else {
|
||||
final InThrowAnalysis inThrowAnalysis = new InThrowAnalysis(richControlFlow, Throw, origins, stable, sharedPendingStates);
|
||||
throwEquation = inThrowAnalysis.analyze();
|
||||
result.add(throwEquation);
|
||||
shouldInferNonTrivialFailingContracts = !inThrowAnalysis.myHasNonTrivialReturn;
|
||||
}
|
||||
|
||||
boolean withCycle = !richControlFlow.dfsTree.back.isEmpty();
|
||||
if (argumentTypes.length > 50 && withCycle) {
|
||||
@@ -323,6 +321,31 @@ public class ClassDataIndexer implements VirtualFileGist.GistCalculator<Map<Byte
|
||||
return;
|
||||
}
|
||||
|
||||
final IntFunction<Function<Value, Stream<Equation>>> inOuts =
|
||||
index -> val -> {
|
||||
if (isBooleanResult && negatedAnalysis != null) {
|
||||
return Stream.of(negatedAnalysis.contractEquation(index, val, stable));
|
||||
}
|
||||
Stream.Builder<Equation> builder = Stream.builder();
|
||||
try {
|
||||
if (isInterestingResult) {
|
||||
builder.add(new InOutAnalysis(richControlFlow, new InOut(index, val), origins, stable, sharedPendingStates).analyze());
|
||||
}
|
||||
if (shouldInferNonTrivialFailingContracts) {
|
||||
InThrow direction = new InThrow(index, val);
|
||||
if (throwEquation.rhs.equals(FINAL_FAIL)) {
|
||||
builder.add(new Equation(new Key(method, direction, stable), FINAL_FAIL));
|
||||
}
|
||||
else {
|
||||
builder.add(new InThrowAnalysis(richControlFlow, direction, origins, stable, sharedPendingStates).analyze());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (AnalyzerException e) {
|
||||
throw new RuntimeException("Analyzer error", e);
|
||||
}
|
||||
return builder.build();
|
||||
};
|
||||
// arguments and contract clauses
|
||||
for (int i = 0; i < argumentTypes.length; i++) {
|
||||
boolean notNullParam = false;
|
||||
@@ -355,61 +378,46 @@ public class ClassDataIndexer implements VirtualFileGist.GistCalculator<Map<Byte
|
||||
}
|
||||
|
||||
if (isInterestingResult) {
|
||||
if (leakingParameters[i]) {
|
||||
if (notNullParam) {
|
||||
// @NotNull, so "null->fail"
|
||||
result.add(new Equation(new Key(method, new InOut(i, Value.Null), stable), FINAL_BOT));
|
||||
}
|
||||
else {
|
||||
// may be null on some branch, running "null->..." analysis
|
||||
if (isBooleanResult && negatedAnalysis != null) {
|
||||
result.add(negatedAnalysis.contractEquation(i, Value.Null, stable));
|
||||
}
|
||||
else {
|
||||
result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.Null), origins, stable, sharedPendingStates).analyze());
|
||||
}
|
||||
}
|
||||
if (isBooleanResult && negatedAnalysis != null) {
|
||||
result.add(negatedAnalysis.contractEquation(i, Value.NotNull, stable));
|
||||
}
|
||||
else {
|
||||
result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.NotNull), origins, stable, sharedPendingStates).analyze());
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!leakingParameters[i]) {
|
||||
// parameter is not leaking, so a contract is the same as for the whole method
|
||||
result.add(new Equation(new Key(method, new InOut(i, Value.Null), stable), outEquation.rhs));
|
||||
result.add(new Equation(new Key(method, new InOut(i, Value.NotNull), stable), outEquation.rhs));
|
||||
continue;
|
||||
}
|
||||
if (notNullParam) {
|
||||
// @NotNull, like "null->fail"
|
||||
result.add(new Equation(new Key(method, new InOut(i, Value.Null), stable), FINAL_BOT));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
Value.typeValues(argumentTypes[i]).flatMap(inOuts.apply(i)).forEach(result::add);
|
||||
}
|
||||
}
|
||||
|
||||
private void processNonBranchingMethod(Method method,
|
||||
Type[] argumentTypes,
|
||||
ControlFlowGraph graph,
|
||||
boolean isReferenceResult,
|
||||
boolean isBooleanResult,
|
||||
Type returnType,
|
||||
boolean stable,
|
||||
List<Equation> result) throws AnalyzerException {
|
||||
CombinedAnalysis analyzer = new CombinedAnalysis(method, graph);
|
||||
analyzer.analyze();
|
||||
if (isReferenceResult) {
|
||||
result.add(analyzer.outContractEquation(stable));
|
||||
ContainerUtil.addIfNotNull(result, analyzer.outContractEquation(stable));
|
||||
ContainerUtil.addIfNotNull(result, analyzer.failEquation(stable));
|
||||
if (ASMUtils.isReferenceType(returnType)) {
|
||||
result.add(analyzer.nullableResultEquation(stable));
|
||||
}
|
||||
for (int i = 0; i < argumentTypes.length; i++) {
|
||||
Type argType = argumentTypes[i];
|
||||
EntryStream.of(argumentTypes).forKeyValue((i, argType) -> {
|
||||
if (ASMUtils.isReferenceType(argType)) {
|
||||
result.add(analyzer.notNullParamEquation(i, stable));
|
||||
result.add(analyzer.nullableParamEquation(i, stable));
|
||||
if (isReferenceResult || isBooleanResult) {
|
||||
result.add(analyzer.contractEquation(i, Value.Null, stable));
|
||||
result.add(analyzer.contractEquation(i, Value.NotNull, stable));
|
||||
}
|
||||
}
|
||||
}
|
||||
Value.typeValues(argType)
|
||||
.flatMap(val -> Stream.of(analyzer.contractEquation(i, val, stable), analyzer.failEquation(i, val, stable)))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(result::add);
|
||||
});
|
||||
}
|
||||
|
||||
private List<Equation> topEquations(Method method,
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.codeInspection.bytecodeAnalysis.asm.ControlFlowGraph;
|
||||
import com.intellij.util.SingletonSet;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.org.objectweb.asm.Handle;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
import org.jetbrains.org.objectweb.asm.tree.*;
|
||||
@@ -28,9 +29,11 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.BasicInterpreter;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.intellij.codeInspection.bytecodeAnalysis.AbstractValues.*;
|
||||
import static com.intellij.codeInspection.bytecodeAnalysis.CombinedData.*;
|
||||
@@ -99,14 +102,20 @@ interface CombinedData {
|
||||
public int getOriginInsnIndex() {
|
||||
return originInsnIndex;
|
||||
}
|
||||
}
|
||||
|
||||
final class NthParamValue extends BasicValue {
|
||||
final int n;
|
||||
|
||||
public NthParamValue(Type type, int n) {
|
||||
super(type);
|
||||
this.n = n;
|
||||
@NotNull
|
||||
Set<Key> getKeysForParameter(int idx, ParamValueBasedDirection direction) {
|
||||
Set<Key> keys = new HashSet<>();
|
||||
for (int argI = 0; argI < this.args.size(); argI++) {
|
||||
BasicValue arg = this.args.get(argI);
|
||||
if (arg instanceof NthParamValue) {
|
||||
NthParamValue npv = (NthParamValue)arg;
|
||||
if (npv.n == idx) {
|
||||
keys.add(new Key(this.method, direction.withIndex(argI), this.stableCall));
|
||||
}
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,8 +246,10 @@ final class CombinedAnalysis {
|
||||
return new Equation(key, result);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
final Equation contractEquation(int i, Value inValue, boolean stable) {
|
||||
final Key key = new Key(method, new InOut(i, inValue), stable);
|
||||
final InOut direction = new InOut(i, inValue);
|
||||
final Key key = new Key(method, direction, stable);
|
||||
final Result result;
|
||||
if (exception || (inValue == Value.Null && interpreter.dereferencedParams[i])) {
|
||||
result = new Final(Value.Bot);
|
||||
@@ -260,31 +271,63 @@ final class CombinedAnalysis {
|
||||
}
|
||||
else if (returnValue instanceof TrackableCallValue) {
|
||||
TrackableCallValue call = (TrackableCallValue)returnValue;
|
||||
HashSet<Key> keys = new HashSet<>();
|
||||
for (int argI = 0; argI < call.args.size(); argI++) {
|
||||
BasicValue arg = call.args.get(argI);
|
||||
if (arg instanceof NthParamValue) {
|
||||
NthParamValue npv = (NthParamValue)arg;
|
||||
if (npv.n == i) {
|
||||
keys.add(new Key(call.method, new InOut(argI, inValue), call.stableCall));
|
||||
}
|
||||
}
|
||||
}
|
||||
Set<Key> keys = call.getKeysForParameter(i, direction);
|
||||
if (ASMUtils.isReferenceType(call.getType())) {
|
||||
keys.add(new Key(call.method, Out, call.stableCall));
|
||||
}
|
||||
if (keys.isEmpty()) {
|
||||
result = new Final(Value.Top);
|
||||
return null;
|
||||
} else {
|
||||
result = new Pending(new SingletonSet<>(new Product(Value.Top, keys)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = new Final(Value.Top);
|
||||
return null;
|
||||
}
|
||||
return new Equation(key, result);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
final Equation failEquation(boolean stable) {
|
||||
final Key key = new Key(method, Throw, stable);
|
||||
final Result result;
|
||||
if (exception) {
|
||||
result = new Final(Value.Fail);
|
||||
}
|
||||
else if (!interpreter.calls.isEmpty()) {
|
||||
Set<Key> keys =
|
||||
interpreter.calls.stream().map(call -> new Key(call.method, Throw, call.stableCall)).collect(Collectors.toSet());
|
||||
result = new Pending(new SingletonSet<>(new Product(Value.Top, keys)));
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
return new Equation(key, result);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
final Equation failEquation(int i, Value inValue, boolean stable) {
|
||||
final InThrow direction = new InThrow(i, inValue);
|
||||
final Key key = new Key(method, direction, stable);
|
||||
final Result result;
|
||||
if (exception) {
|
||||
result = new Final(Value.Fail);
|
||||
}
|
||||
else if (!interpreter.calls.isEmpty()) {
|
||||
Set<Key> keys = new HashSet<>();
|
||||
for (TrackableCallValue call : interpreter.calls) {
|
||||
keys.addAll(call.getKeysForParameter(i, direction));
|
||||
keys.add(new Key(call.method, Throw, call.stableCall));
|
||||
}
|
||||
result = new Pending(new SingletonSet<>(new Product(Value.Top, keys)));
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
return new Equation(key, result);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
final Equation outContractEquation(boolean stable) {
|
||||
final Key key = new Key(method, Out, stable);
|
||||
final Result result;
|
||||
@@ -310,7 +353,7 @@ final class CombinedAnalysis {
|
||||
result = new Pending(new SingletonSet<>(new Product(Value.Top, keys)));
|
||||
}
|
||||
else {
|
||||
result = new Final(Value.Top);
|
||||
return null;
|
||||
}
|
||||
return new Equation(key, result);
|
||||
}
|
||||
@@ -376,6 +419,9 @@ final class CombinedInterpreter extends BasicInterpreter {
|
||||
// Trackable values that were dereferenced during execution of a method
|
||||
// Values are are identified by `origin` index
|
||||
final boolean[] dereferencedValues;
|
||||
|
||||
final List<TrackableCallValue> calls = new ArrayList<>();
|
||||
|
||||
private final InsnList insns;
|
||||
|
||||
CombinedInterpreter(InsnList insns, int arity) {
|
||||
@@ -536,7 +582,9 @@ final class CombinedInterpreter extends BasicInterpreter {
|
||||
case INVOKEINTERFACE: {
|
||||
MethodInsnNode mNode = (MethodInsnNode)insn;
|
||||
Method method = new Method(mNode.owner, mNode.name, mNode.desc);
|
||||
return methodCall(opCode, origin, method, values);
|
||||
TrackableCallValue value = methodCall(opCode, origin, method, values);
|
||||
calls.add(value);
|
||||
return value;
|
||||
}
|
||||
case INVOKEDYNAMIC: {
|
||||
LambdaIndy lambda = LambdaIndy.from((InvokeDynamicInsnNode)insn);
|
||||
@@ -554,7 +602,7 @@ final class CombinedInterpreter extends BasicInterpreter {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private BasicValue methodCall(int opCode, int origin, Method method, List<? extends BasicValue> values) {
|
||||
private TrackableCallValue methodCall(int opCode, int origin, Method method, List<? extends BasicValue> values) {
|
||||
Type retType = Type.getReturnType(method.methodDesc);
|
||||
boolean stable = opCode == INVOKESTATIC || opCode == INVOKESPECIAL;
|
||||
boolean thisCall = false;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.bytecodeAnalysis;
|
||||
|
||||
import com.intellij.codeInspection.bytecodeAnalysis.Direction.ParamValueBasedDirection;
|
||||
import com.intellij.codeInspection.bytecodeAnalysis.asm.ASMUtils;
|
||||
import com.intellij.codeInspection.bytecodeAnalysis.asm.ControlFlowGraph.Edge;
|
||||
import com.intellij.codeInspection.bytecodeAnalysis.asm.RichControlFlow;
|
||||
@@ -34,28 +35,27 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.intellij.codeInspection.bytecodeAnalysis.AbstractValues.*;
|
||||
import static com.intellij.codeInspection.bytecodeAnalysis.Direction.InOut;
|
||||
import static com.intellij.codeInspection.bytecodeAnalysis.Direction.Out;
|
||||
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
class InOutAnalysis extends Analysis<Result> {
|
||||
abstract class ContractAnalysis extends Analysis<Result> {
|
||||
|
||||
static final ResultUtil resultUtil =
|
||||
new ResultUtil(new ELattice<>(Value.Bot, Value.Top));
|
||||
|
||||
final private State[] pending;
|
||||
private final InOutInterpreter interpreter;
|
||||
private final Value inValue;
|
||||
final InOutInterpreter interpreter;
|
||||
final Value inValue;
|
||||
private final int generalizeShift;
|
||||
private Result internalResult;
|
||||
Result internalResult;
|
||||
private int id;
|
||||
private int pendingTop;
|
||||
|
||||
protected InOutAnalysis(RichControlFlow richControlFlow, Direction direction, boolean[] resultOrigins, boolean stable, State[] pending) {
|
||||
protected ContractAnalysis(RichControlFlow richControlFlow, Direction direction, boolean[] resultOrigins, boolean stable, State[] pending) {
|
||||
super(richControlFlow, direction, stable);
|
||||
this.pending = pending;
|
||||
interpreter = new InOutInterpreter(direction, richControlFlow.controlFlow.methodNode.instructions, resultOrigins);
|
||||
inValue = direction instanceof InOut ? ((InOut)direction).inValue : null;
|
||||
inValue = direction instanceof ParamValueBasedDirection ? ((ParamValueBasedDirection)direction).inValue : null;
|
||||
generalizeShift = (methodNode.access & ACC_STATIC) == 0 ? 1 : 0;
|
||||
internalResult = new Final(Value.Bot);
|
||||
}
|
||||
@@ -131,52 +131,8 @@ class InOutAnalysis extends Analysis<Result> {
|
||||
|
||||
addComputed(insnIndex, state);
|
||||
|
||||
if (interpreter.deReferenced) {
|
||||
return;
|
||||
}
|
||||
|
||||
int opcode = insnNode.getOpcode();
|
||||
switch (opcode) {
|
||||
case ARETURN:
|
||||
case IRETURN:
|
||||
case LRETURN:
|
||||
case FRETURN:
|
||||
case DRETURN:
|
||||
case RETURN:
|
||||
BasicValue stackTop = popValue(frame);
|
||||
Result subResult;
|
||||
if (FalseValue == stackTop) {
|
||||
subResult = new Final(Value.False);
|
||||
}
|
||||
else if (TrueValue == stackTop) {
|
||||
subResult = new Final(Value.True);
|
||||
}
|
||||
else if (NullValue == stackTop) {
|
||||
subResult = new Final(Value.Null);
|
||||
}
|
||||
else if (stackTop instanceof NotNullValue) {
|
||||
subResult = new Final(Value.NotNull);
|
||||
}
|
||||
else if (stackTop instanceof ParamValue) {
|
||||
subResult = new Final(inValue);
|
||||
}
|
||||
else if (stackTop instanceof CallResultValue) {
|
||||
Set<Key> keys = ((CallResultValue) stackTop).inters;
|
||||
subResult = new Pending(Collections.singleton(new Product(Value.Top, keys)));
|
||||
}
|
||||
else {
|
||||
earlyResult = new Final(Value.Top);
|
||||
return;
|
||||
}
|
||||
internalResult = resultUtil.join(internalResult, subResult);
|
||||
if (internalResult instanceof Final && ((Final)internalResult).value == Value.Top) {
|
||||
earlyResult = internalResult;
|
||||
}
|
||||
return;
|
||||
case ATHROW:
|
||||
return;
|
||||
default:
|
||||
}
|
||||
if (handleReturn(frame, opcode)) return;
|
||||
|
||||
if (opcode == IFNONNULL && popValue(frame) instanceof ParamValue) {
|
||||
int nextInsnIndex = inValue == Value.Null ? insnIndex + 1 : methodNode.instructions.indexOf(((JumpInsnNode)insnNode).label);
|
||||
@@ -192,6 +148,20 @@ class InOutAnalysis extends Analysis<Result> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (opcode == IFEQ && popValue(frame) instanceof ParamValue) {
|
||||
int nextInsnIndex = inValue == Value.True ? insnIndex + 1 : methodNode.instructions.indexOf(((JumpInsnNode)insnNode).label);
|
||||
State nextState = new State(++id, new Conf(nextInsnIndex, nextFrame), nextHistory, true, false);
|
||||
pendingPush(nextState);
|
||||
return;
|
||||
}
|
||||
|
||||
if (opcode == IFNE && popValue(frame) instanceof ParamValue) {
|
||||
int nextInsnIndex = inValue == Value.False ? insnIndex + 1 : methodNode.instructions.indexOf(((JumpInsnNode)insnNode).label);
|
||||
State nextState = new State(++id, new Conf(nextInsnIndex, nextFrame), nextHistory, true, false);
|
||||
pendingPush(nextState);
|
||||
return;
|
||||
}
|
||||
|
||||
if (opcode == IFEQ && popValue(frame) == InstanceOfCheckValue && inValue == Value.Null) {
|
||||
int nextInsnIndex = methodNode.instructions.indexOf(((JumpInsnNode)insnNode).label);
|
||||
State nextState = new State(++id, new Conf(nextInsnIndex, nextFrame), nextHistory, true, false);
|
||||
@@ -218,6 +188,8 @@ class InOutAnalysis extends Analysis<Result> {
|
||||
}
|
||||
}
|
||||
|
||||
abstract boolean handleReturn(Frame<BasicValue> frame, int opcode) throws AnalyzerException;
|
||||
|
||||
private void pendingPush(State st) throws AnalyzerException {
|
||||
if (pendingTop >= STEPS_LIMIT) {
|
||||
throw new AnalyzerException(null, "limit is reached in method " + method);
|
||||
@@ -268,8 +240,117 @@ class InOutAnalysis extends Analysis<Result> {
|
||||
}
|
||||
}
|
||||
|
||||
class InOutAnalysis extends ContractAnalysis {
|
||||
|
||||
protected InOutAnalysis(RichControlFlow richControlFlow,
|
||||
Direction direction,
|
||||
boolean[] resultOrigins,
|
||||
boolean stable,
|
||||
State[] pending) {
|
||||
super(richControlFlow, direction, resultOrigins, stable, pending);
|
||||
}
|
||||
|
||||
boolean handleReturn(Frame<BasicValue> frame, int opcode) throws AnalyzerException {
|
||||
if (interpreter.deReferenced) {
|
||||
return true;
|
||||
}
|
||||
switch (opcode) {
|
||||
case ARETURN:
|
||||
case IRETURN:
|
||||
case LRETURN:
|
||||
case FRETURN:
|
||||
case DRETURN:
|
||||
case RETURN:
|
||||
BasicValue stackTop = popValue(frame);
|
||||
Result subResult;
|
||||
if (FalseValue == stackTop) {
|
||||
subResult = new Final(Value.False);
|
||||
}
|
||||
else if (TrueValue == stackTop) {
|
||||
subResult = new Final(Value.True);
|
||||
}
|
||||
else if (NullValue == stackTop) {
|
||||
subResult = new Final(Value.Null);
|
||||
}
|
||||
else if (stackTop instanceof NotNullValue) {
|
||||
subResult = new Final(Value.NotNull);
|
||||
}
|
||||
else if (stackTop instanceof ParamValue) {
|
||||
subResult = new Final(inValue);
|
||||
}
|
||||
else if (stackTop instanceof CallResultValue) {
|
||||
Set<Key> keys = ((CallResultValue) stackTop).inters;
|
||||
subResult = new Pending(Collections.singleton(new Product(Value.Top, keys)));
|
||||
}
|
||||
else {
|
||||
earlyResult = new Final(Value.Top);
|
||||
return true;
|
||||
}
|
||||
internalResult = resultUtil.join(internalResult, subResult);
|
||||
if (internalResult instanceof Final && ((Final)internalResult).value == Value.Top) {
|
||||
earlyResult = internalResult;
|
||||
}
|
||||
return true;
|
||||
case ATHROW:
|
||||
return true;
|
||||
default:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class InThrowAnalysis extends ContractAnalysis {
|
||||
private BasicValue myReturnValue;
|
||||
boolean myHasNonTrivialReturn;
|
||||
|
||||
protected InThrowAnalysis(RichControlFlow richControlFlow,
|
||||
Direction direction,
|
||||
boolean[] resultOrigins,
|
||||
boolean stable,
|
||||
State[] pending) {
|
||||
super(richControlFlow, direction, resultOrigins, stable, pending);
|
||||
}
|
||||
|
||||
boolean handleReturn(Frame<BasicValue> frame, int opcode) throws AnalyzerException {
|
||||
Result subResult;
|
||||
if (interpreter.deReferenced) {
|
||||
subResult = new Final(Value.Top);
|
||||
} else {
|
||||
switch (opcode) {
|
||||
case ARETURN:
|
||||
case IRETURN:
|
||||
case LRETURN:
|
||||
case FRETURN:
|
||||
case DRETURN:
|
||||
BasicValue value = frame.pop();
|
||||
if(!(value instanceof NthParamValue) && value != NullValue && value != TrueValue && value != FalseValue ||
|
||||
myReturnValue != null && !myReturnValue.equals(value)) {
|
||||
myHasNonTrivialReturn = true;
|
||||
} else {
|
||||
myReturnValue = value;
|
||||
}
|
||||
subResult = new Final(Value.Top);
|
||||
break;
|
||||
case RETURN:
|
||||
subResult = new Final(Value.Top);
|
||||
break;
|
||||
case ATHROW:
|
||||
subResult = new Final(Value.Fail);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
internalResult = resultUtil.join(internalResult, subResult);
|
||||
if (internalResult instanceof Final && ((Final)internalResult).value == Value.Top && myHasNonTrivialReturn) {
|
||||
earlyResult = internalResult;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class InOutInterpreter extends BasicInterpreter {
|
||||
final Direction direction;
|
||||
final ParamValueBasedDirection direction;
|
||||
final InsnList insns;
|
||||
final boolean[] resultOrigins;
|
||||
final boolean nullAnalysis;
|
||||
@@ -277,10 +358,15 @@ class InOutInterpreter extends BasicInterpreter {
|
||||
boolean deReferenced;
|
||||
|
||||
InOutInterpreter(Direction direction, InsnList insns, boolean[] resultOrigins) {
|
||||
this.direction = direction;
|
||||
this.insns = insns;
|
||||
this.resultOrigins = resultOrigins;
|
||||
nullAnalysis = (direction instanceof InOut) && (((InOut)direction).inValue) == Value.Null;
|
||||
if(direction instanceof ParamValueBasedDirection) {
|
||||
this.direction = (ParamValueBasedDirection)direction;
|
||||
this.nullAnalysis = this.direction.inValue == Value.Null;
|
||||
} else {
|
||||
this.direction = null;
|
||||
this.nullAnalysis = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -420,12 +506,11 @@ class InOutInterpreter extends BasicInterpreter {
|
||||
Type retType = Type.getReturnType(mNode.desc);
|
||||
boolean isRefRetType = retType.getSort() == Type.OBJECT || retType.getSort() == Type.ARRAY;
|
||||
if (!Type.VOID_TYPE.equals(retType)) {
|
||||
if (direction instanceof InOut) {
|
||||
InOut inOut = (InOut)direction;
|
||||
if (direction != null) {
|
||||
HashSet<Key> keys = new HashSet<>();
|
||||
for (int i = shift; i < values.size(); i++) {
|
||||
if (values.get(i) instanceof ParamValue) {
|
||||
keys.add(new Key(method, new InOut(i - shift, inOut.inValue), stable));
|
||||
keys.add(new Key(method, direction.withIndex(i - shift), stable));
|
||||
}
|
||||
}
|
||||
if (isRefRetType) {
|
||||
|
||||
@@ -15,10 +15,107 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.bytecodeAnalysis;
|
||||
|
||||
public interface Direction {
|
||||
final class In implements Direction {
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Direction {
|
||||
public static final Direction Out = explicitDirection("Out");
|
||||
public static final Direction NullableOut = explicitDirection("NullableOut");
|
||||
public static final Direction Pure = explicitDirection("Pure");
|
||||
public static final Direction Throw = explicitDirection("Throw");
|
||||
|
||||
private static final List<Direction> ourConcreteDirections = Arrays.asList(Out, NullableOut, Pure, Throw);
|
||||
private static final int CONCRETE_DIRECTIONS_OFFSET = ourConcreteDirections.size();
|
||||
private static final int IN_OUT_OFFSET = 2; // nullity mask is 0/1
|
||||
private static final int IN_THROW_OFFSET = 2 + Value.values().length;
|
||||
private static final int DIRECTIONS_PER_PARAM_ID = IN_THROW_OFFSET + Value.values().length;
|
||||
|
||||
/**
|
||||
* Converts int to Direction object.
|
||||
*
|
||||
* @param directionKey int representation of direction
|
||||
* @return Direction object
|
||||
* @see #asInt()
|
||||
*/
|
||||
@NotNull
|
||||
static Direction fromInt(int directionKey) {
|
||||
if(directionKey < CONCRETE_DIRECTIONS_OFFSET) {
|
||||
return ourConcreteDirections.get(directionKey);
|
||||
}
|
||||
int paramKey = directionKey - CONCRETE_DIRECTIONS_OFFSET;
|
||||
int paramId = paramKey / DIRECTIONS_PER_PARAM_ID;
|
||||
int subDirectionId = paramKey % DIRECTIONS_PER_PARAM_ID;
|
||||
// 0 - 1 - @NotNull, @Nullable, parameter
|
||||
if (subDirectionId < IN_OUT_OFFSET) {
|
||||
return new In(paramId, subDirectionId);
|
||||
}
|
||||
if (subDirectionId < IN_THROW_OFFSET) {
|
||||
int valueId = subDirectionId - IN_OUT_OFFSET;
|
||||
return new InOut(paramId, Value.values()[valueId]);
|
||||
}
|
||||
int valueId = subDirectionId - IN_THROW_OFFSET;
|
||||
return new InThrow(paramId, Value.values()[valueId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes Direction object as int.
|
||||
*
|
||||
* @return unique int for direction
|
||||
*/
|
||||
abstract int asInt();
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return asInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(obj == this) return true;
|
||||
if(obj == null || obj.getClass() != this.getClass()) return false;
|
||||
return asInt() == ((Direction)obj).asInt();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Direction explicitDirection(String name) {
|
||||
return new Direction() {
|
||||
private String myName = name;
|
||||
|
||||
@Override
|
||||
int asInt() {
|
||||
for (int i = 0; i < ourConcreteDirections.size(); i++) {
|
||||
if(ourConcreteDirections.get(i) == this) return i;
|
||||
}
|
||||
throw new InternalError("Explicit direction absent in ourConcreteDirections: "+myName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myName;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
abstract static class ParamIdBasedDirection extends Direction {
|
||||
final int paramIndex;
|
||||
|
||||
protected ParamIdBasedDirection(int index) {
|
||||
paramIndex = index;
|
||||
}
|
||||
|
||||
public int paramId() {
|
||||
return paramIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
int asInt() {
|
||||
return CONCRETE_DIRECTIONS_OFFSET + DIRECTIONS_PER_PARAM_ID * this.paramId();
|
||||
}
|
||||
}
|
||||
|
||||
static final class In extends ParamIdBasedDirection {
|
||||
static final int NOT_NULL_MASK = 0;
|
||||
static final int NULLABLE_MASK = 1;
|
||||
/**
|
||||
@@ -28,111 +125,72 @@ public interface Direction {
|
||||
final int nullityMask;
|
||||
|
||||
In(int paramIndex, int nullityMask) {
|
||||
this.paramIndex = paramIndex;
|
||||
super(paramIndex);
|
||||
this.nullityMask = nullityMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
int asInt() {
|
||||
return super.asInt() + nullityMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "In " + paramIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
In in = (In)o;
|
||||
if (paramIndex != in.paramIndex) return false;
|
||||
if (nullityMask != in.nullityMask) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * paramIndex + nullityMask;
|
||||
}
|
||||
|
||||
public int paramId() {
|
||||
return paramIndex;
|
||||
}
|
||||
}
|
||||
|
||||
final class InOut implements Direction {
|
||||
final int paramIndex;
|
||||
static abstract class ParamValueBasedDirection extends ParamIdBasedDirection {
|
||||
final Value inValue;
|
||||
|
||||
InOut(int paramIndex, Value inValue) {
|
||||
this.paramIndex = paramIndex;
|
||||
ParamValueBasedDirection(int paramIndex, Value inValue) {
|
||||
super(paramIndex);
|
||||
this.inValue = inValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
abstract ParamValueBasedDirection withIndex(int paramIndex);
|
||||
}
|
||||
|
||||
InOut inOut = (InOut)o;
|
||||
|
||||
if (paramIndex != inOut.paramIndex) return false;
|
||||
if (inValue != inOut.inValue) return false;
|
||||
|
||||
return true;
|
||||
static final class InOut extends ParamValueBasedDirection {
|
||||
InOut(int paramIndex, Value inValue) {
|
||||
super(paramIndex, inValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = paramIndex;
|
||||
result = 31 * result + inValue.ordinal();
|
||||
return result;
|
||||
InOut withIndex(int paramIndex) {
|
||||
return new InOut(paramIndex, inValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
int asInt() {
|
||||
return super.asInt() + IN_OUT_OFFSET + inValue.ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InOut " + paramIndex + " " + inValue.toString();
|
||||
}
|
||||
|
||||
public int paramId() {
|
||||
return paramIndex;
|
||||
}
|
||||
|
||||
public int valueId() {
|
||||
return inValue.ordinal();
|
||||
}
|
||||
}
|
||||
|
||||
Direction Out = new Direction() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Out";
|
||||
static final class InThrow extends ParamValueBasedDirection {
|
||||
InThrow(int paramIndex, Value inValue) {
|
||||
super(paramIndex, inValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
Direction NullableOut = new Direction() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NullableOut";
|
||||
InThrow withIndex(int paramIndex) {
|
||||
return new InThrow(paramIndex, inValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return -2;
|
||||
}
|
||||
};
|
||||
|
||||
Direction Pure = new Direction() {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return -3;
|
||||
int asInt() {
|
||||
return super.asInt() + IN_THROW_OFFSET + inValue.ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Pure";
|
||||
return "InThrow " + paramIndex + " " + inValue.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.bytecodeAnalysis;
|
||||
|
||||
import one.util.streamex.IntStreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -75,11 +76,24 @@ public final class HKey {
|
||||
return dirKey == 0 ? this : new HKey(key, 0, stable, false);
|
||||
}
|
||||
|
||||
HKey updateDirection(int newDirKey) {
|
||||
return new HKey(key, newDirKey, stable, false);
|
||||
HKey withDirection(Direction dir) {
|
||||
return new HKey(key, dir.asInt(), stable, false);
|
||||
}
|
||||
|
||||
HKey negate() {
|
||||
return new HKey(key, dirKey, stable, true);
|
||||
}
|
||||
|
||||
public Direction getDirection() {
|
||||
return Direction.fromInt(dirKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HKey [" + bytesToString(key) + "|" + (stable ? "S" : "-") + (negated ? "N" : "-") + "|" + getDirection() + "]";
|
||||
}
|
||||
|
||||
static String bytesToString(byte[] key) {
|
||||
return IntStreamEx.of(key).mapToObj(b -> String.format("%02x", b & 0xFF)).joining(".");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,6 @@ import java.util.function.Function;
|
||||
|
||||
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
enum Value {
|
||||
Bot, NotNull, Null, True, False, Pure, Top
|
||||
}
|
||||
|
||||
final class LambdaIndy {
|
||||
private static final String LAMBDA_METAFACTORY_CLASS = "java/lang/invoke/LambdaMetafactory";
|
||||
private static final String LAMBDA_METAFACTORY_METHOD = "metafactory";
|
||||
@@ -36,6 +36,7 @@ import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.ConcurrentFactoryMap;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -286,22 +287,30 @@ public class ProjectBytecodeAnalysis {
|
||||
throws EquationsLimitException {
|
||||
MethodAnnotations result = new MethodAnnotations();
|
||||
|
||||
final Solver outSolver = new Solver(new ELattice<>(Value.Bot, Value.Top), Value.Top);
|
||||
final PuritySolver puritySolver = new PuritySolver();
|
||||
collectEquations(allKeys, outSolver);
|
||||
collectPurityEquations(key.updateDirection(BytecodeAnalysisConverter.mkDirectionKey(Pure)), puritySolver);
|
||||
collectPurityEquations(key.withDirection(Pure), puritySolver);
|
||||
|
||||
Map<HKey, Value> solutions = outSolver.solve();
|
||||
Map<HKey, Set<HEffectQuantum>> puritySolutions = puritySolver.solve();
|
||||
|
||||
int arity = owner.getParameterList().getParameters().length;
|
||||
BytecodeAnalysisConverter.addMethodAnnotations(solutions, result, key, arity);
|
||||
BytecodeAnalysisConverter.addEffectAnnotations(puritySolutions, result, key, owner.isConstructor());
|
||||
|
||||
HKey failureKey = key.withDirection(Throw);
|
||||
final Solver failureSolver = new Solver(new ELattice<>(Value.Fail, Value.Top), Value.Top);
|
||||
collectEquations(Collections.singletonList(failureKey), failureSolver);
|
||||
if (failureSolver.solve().get(failureKey) == Value.Fail) {
|
||||
// Always failing method
|
||||
result.contractsValues.put(key, StreamEx.constant("_", arity).joining(",", "\"", "->fail\""));
|
||||
} else {
|
||||
final Solver outSolver = new Solver(new ELattice<>(Value.Bot, Value.Top), Value.Top);
|
||||
collectEquations(allKeys, outSolver);
|
||||
Map<HKey, Value> solutions = outSolver.solve();
|
||||
BytecodeAnalysisConverter.addMethodAnnotations(solutions, result, key, arity);
|
||||
}
|
||||
|
||||
if (nullableMethod) {
|
||||
final Solver nullableMethodSolver = new Solver(new ELattice<>(Value.Bot, Value.Null), Value.Bot);
|
||||
HKey nullableKey = key.updateDirection(BytecodeAnalysisConverter.mkDirectionKey(NullableOut));
|
||||
HKey nullableKey = key.withDirection(NullableOut);
|
||||
if (nullableMethodTransitivity) {
|
||||
collectEquations(Collections.singletonList(nullableKey), nullableMethodSolver);
|
||||
}
|
||||
|
||||
@@ -243,6 +243,11 @@ final class CoreHKey {
|
||||
result = 31 * result + dirKey;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CoreHKey [" + HKey.bytesToString(key) + "|" + Direction.fromInt(dirKey) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
final class Solver {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.codeInspection.bytecodeAnalysis.asm.ASMUtils;
|
||||
import com.intellij.codeInspection.dataFlow.MethodContract;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
enum Value {
|
||||
Bot, NotNull, Null, True, False, Fail, Pure, Top;
|
||||
|
||||
static Stream<Value> typeValues(Type type) {
|
||||
if(ASMUtils.isReferenceType(type)) {
|
||||
return Stream.of(Null, NotNull);
|
||||
} else if(ASMUtils.isBooleanType(type)) {
|
||||
return Stream.of(True, False);
|
||||
}
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
MethodContract.ValueConstraint toValueConstraint() {
|
||||
switch (this) {
|
||||
case False: return MethodContract.ValueConstraint.FALSE_VALUE;
|
||||
case True: return MethodContract.ValueConstraint.TRUE_VALUE;
|
||||
case NotNull: return MethodContract.ValueConstraint.NOT_NULL_VALUE;
|
||||
case Null: return MethodContract.ValueConstraint.NULL_VALUE;
|
||||
case Fail: return MethodContract.ValueConstraint.THROW_EXCEPTION;
|
||||
default: return MethodContract.ValueConstraint.ANY_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public abstract class MethodContract {
|
||||
/**
|
||||
* @return true if this contract result does not depend on arguments
|
||||
*/
|
||||
boolean isTrivial() {
|
||||
public boolean isTrivial() {
|
||||
return getConditions().isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -175,9 +175,7 @@
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='java.awt.Component java.awt.Dimension getSize(java.awt.Dimension)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null->!null;null->!null""/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='java.awt.Component java.awt.Dimension getSize(java.awt.Dimension) 0'>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
</item>
|
||||
<item name='java.lang.ClassLoader java.lang.Class findClass(java.lang.String)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -666,7 +667,7 @@
|
||||
</item>
|
||||
<item name='java.lang.ClassLoader java.lang.String findLibrary(java.lang.String)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->null;null->null""/>
|
||||
<val name="value" val=""_->null""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
@@ -679,7 +680,7 @@
|
||||
</item>
|
||||
<item name='java.lang.ClassLoader java.net.URL findResource(java.lang.String)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->null;null->null""/>
|
||||
<val name="value" val=""_->null""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
@@ -847,6 +848,7 @@
|
||||
</item>
|
||||
<item name='java.lang.Enum java.lang.Object clone()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2379,6 +2381,7 @@
|
||||
</item>
|
||||
<item name='java.lang.System void checkKey(java.lang.String)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""null->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2426,6 +2429,7 @@
|
||||
</item>
|
||||
<item name='java.lang.ThreadLocal T childValue(T)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</item>
|
||||
<item name='java.lang.invoke.AdapterMethodHandle boolean canBoxArgument(java.lang.Class<?>, java.lang.Class<?>)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null,_->false;_,!null->false;_,null->false;null,_->false""/>
|
||||
<val name="value" val=""_,_->false""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -132,8 +132,7 @@
|
||||
</item>
|
||||
<item name='java.lang.invoke.AdapterMethodHandle java.lang.invoke.MethodHandle makeBoxArgument(java.lang.invoke.MethodType, java.lang.invoke.MethodHandle, int, java.lang.Class<?>)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value"
|
||||
val=""!null,_,_,_->null;_,!null,_,_->null;_,_,_,!null->null;_,_,_,null->null;_,null,_,_->null;null,_,_,_->null""/>
|
||||
<val name="value" val=""_,_,_,_->null""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
@@ -409,6 +408,7 @@
|
||||
</item>
|
||||
<item name='java.lang.invoke.ConstantCallSite void setTarget(java.lang.invoke.MethodHandle)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -440,6 +440,7 @@
|
||||
</item>
|
||||
<item name='java.lang.invoke.FilterGeneric java.lang.invoke.FilterGeneric.Adapter buildAdapterFromBytecodes(java.lang.invoke.MethodType, java.lang.invoke.FilterGeneric.Kind, int)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_,_,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -807,6 +808,7 @@
|
||||
</item>
|
||||
<item name='java.lang.invoke.FromGeneric java.lang.invoke.FromGeneric.Adapter buildAdapterFromBytecodes(java.lang.invoke.MethodType)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -986,6 +988,11 @@
|
||||
<item name='java.lang.invoke.FromGeneric.Adapter java.lang.Class<? extends java.lang.invoke.FromGeneric.Adapter> findSubClass(java.lang.String) 0'>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
</item>
|
||||
<item name='java.lang.invoke.InvokeDynamic InvokeDynamic()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""->fail""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.lang.invoke.InvokeGeneric boolean returnConversionNeeded(java.lang.invoke.MethodType, java.lang.invoke.MethodHandle)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""_,null->false""/>
|
||||
@@ -1002,6 +1009,7 @@
|
||||
</item>
|
||||
<item name='java.lang.invoke.InvokeGeneric java.lang.invoke.MethodHandle addReturnConversion(java.lang.invoke.MethodHandle, java.lang.Class<?>)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -1586,6 +1594,11 @@
|
||||
<item name='java.lang.invoke.MethodHandleNatives void notifyGenericMethodType(java.lang.invoke.MethodType) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='java.lang.invoke.MethodHandleNatives void raiseException(int, java.lang.Object, java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""_,_,_->fail""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.lang.invoke.MethodHandleNatives void raiseException(int, java.lang.Object, java.lang.Object) 1'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
@@ -1990,7 +2003,7 @@
|
||||
</item>
|
||||
<item name='java.lang.invoke.MethodHandles.Lookup java.lang.invoke.MethodHandle makeAccessor(java.lang.Class<?>, java.lang.invoke.MemberName, boolean, boolean)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""_,null,_,_->!null""/>
|
||||
<val val=""_,_,true,_->!null;_,null,_,_->!null""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.lang.invoke.MethodHandles.Lookup java.lang.invoke.MethodHandle makeAccessor(java.lang.Class<?>, java.lang.invoke.MemberName, boolean, boolean) 1'>
|
||||
@@ -2332,6 +2345,7 @@
|
||||
</item>
|
||||
<item name='java.lang.invoke.SpreadGeneric java.lang.invoke.SpreadGeneric.Adapter buildAdapterFromBytecodes(java.lang.invoke.MethodType, int, java.lang.invoke.MethodHandle[])'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_,_,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2510,6 +2524,7 @@
|
||||
</item>
|
||||
<item name='java.lang.invoke.ToGeneric java.lang.invoke.ToGeneric.Adapter buildAdapterFromBytecodes(java.lang.invoke.MethodType)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
|
||||
@@ -119,7 +119,7 @@
|
||||
</item>
|
||||
<item name='java.net.InetAddress boolean equals(java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->false;null->false""/>
|
||||
<val name="value" val=""_->false""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -826,7 +826,7 @@
|
||||
</item>
|
||||
<item name='java.net.URLConnection java.lang.String getDefaultRequestProperty(java.lang.String)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->null;null->null""/>
|
||||
<val name="value" val=""_->null""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
@@ -842,7 +842,7 @@
|
||||
</item>
|
||||
<item name='java.net.URLConnection java.lang.String getHeaderField(java.lang.String)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->null;null->null""/>
|
||||
<val name="value" val=""_->null""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
|
||||
@@ -51,6 +51,11 @@
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='java.security.Identity java.lang.String toString(boolean)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""true->!null""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.security.NoSuchAlgorithmException NoSuchAlgorithmException()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
</item>
|
||||
<item name='java.util.AbstractCollection boolean add(E)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -42,6 +43,7 @@
|
||||
</item>
|
||||
<item name='java.util.AbstractList E set(int, E)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -50,7 +52,7 @@
|
||||
</item>
|
||||
<item name='java.util.AbstractList boolean add(E)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null->true;null->true""/>
|
||||
<val val=""_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.AbstractList boolean addAll(int, java.util.Collection<? extends E>) 1'>
|
||||
@@ -82,6 +84,7 @@
|
||||
</item>
|
||||
<item name='java.util.AbstractList void add(int, E)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -131,6 +134,7 @@
|
||||
</item>
|
||||
<item name='java.util.AbstractMap V put(K, V)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -203,7 +207,7 @@
|
||||
</item>
|
||||
<item name='java.util.AbstractQueue boolean add(E)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null->true;null->true""/>
|
||||
<val val=""_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.AbstractQueue boolean addAll(java.util.Collection<? extends E>) 0'>
|
||||
@@ -251,7 +255,7 @@
|
||||
</item>
|
||||
<item name='java.util.ArrayList boolean add(E)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null->true;null->true""/>
|
||||
<val val=""_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.ArrayList boolean addAll(int, java.util.Collection<? extends E>) 1'>
|
||||
@@ -1322,7 +1326,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.AsLIFOQueue boolean add(E)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null->true;null->true""/>
|
||||
<val val=""_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.Collections.CheckedCollection CheckedCollection(java.util.Collection<E>, java.lang.Class<E>)'>
|
||||
@@ -1422,6 +1426,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.CheckedMap.CheckedEntrySet boolean add(java.util.Map.Entry<K,V>)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -1430,6 +1435,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.CheckedMap.CheckedEntrySet boolean addAll(java.util.Collection<? extends java.util.Map.Entry<K,V>>)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -1640,7 +1646,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.EmptyList boolean contains(java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->false;null->false""/>
|
||||
<val name="value" val=""_->false""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -1721,6 +1727,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.EmptyListIterator void add(E)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -1729,6 +1736,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.EmptyListIterator void set(E)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -1742,7 +1750,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.EmptyMap V get(java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->null;null->null""/>
|
||||
<val name="value" val=""_->null""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
@@ -1752,7 +1760,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.EmptyMap boolean containsKey(java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->false;null->false""/>
|
||||
<val name="value" val=""_->false""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -1761,7 +1769,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.EmptyMap boolean containsValue(java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->false;null->false""/>
|
||||
<val name="value" val=""_->false""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -1826,7 +1834,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.EmptySet boolean contains(java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->false;null->false""/>
|
||||
<val name="value" val=""_->false""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2102,6 +2110,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableCollection boolean add(E)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2110,6 +2119,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableCollection boolean addAll(java.util.Collection<? extends E>)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2118,6 +2128,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableCollection boolean remove(java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2126,6 +2137,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableCollection boolean removeAll(java.util.Collection<?>)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2134,6 +2146,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableCollection boolean retainAll(java.util.Collection<?>)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2155,6 +2168,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableList E set(int, E)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2171,6 +2185,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableList boolean addAll(int, java.util.Collection<? extends E>)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2191,6 +2206,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableList void add(int, E)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2207,6 +2223,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableMap V put(K, V)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2218,6 +2235,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableMap V remove(java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2231,6 +2249,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableMap void putAll(java.util.Map<? extends K,? extends V>)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2272,6 +2291,7 @@
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableMap.UnmodifiableEntrySet.UnmodifiableEntry V setValue(V)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -2996,7 +3016,7 @@
|
||||
</item>
|
||||
<item name='java.util.LinkedHashMap boolean removeEldestEntry(java.util.Map.Entry<K,V>)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->false;null->false""/>
|
||||
<val name="value" val=""_->false""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
|
||||
@@ -235,9 +235,7 @@
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
</item>
|
||||
<item name='javax.swing.JComponent java.awt.Dimension getSize(java.awt.Dimension)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null->!null;null->!null""/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='javax.swing.JComponent java.awt.Dimension getSize(java.awt.Dimension) 0'>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
@@ -592,6 +590,7 @@
|
||||
</item>
|
||||
<item name='javax.swing.SwingUtilities SwingUtilities()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.collections.iterators.AbstractEmptyIterator java.lang.Object setValue(java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -54,6 +55,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.collections.iterators.AbstractEmptyIterator void add(java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -72,6 +74,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.collections.iterators.AbstractEmptyIterator void set(java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
|
||||
@@ -400,7 +400,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.collections.map.LRUMap boolean removeLRU(org.apache.commons.collections.map.AbstractLinkedMap.LinkEntry)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->true;null->true""/>
|
||||
<val name="value" val=""_->true""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
|
||||
@@ -3147,9 +3147,9 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.StringUtils java.lang.String defaultString(java.lang.String)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->!null;null->!null""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.StringUtils java.lang.String defaultString(java.lang.String) 0'>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
@@ -4070,16 +4070,19 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void isTrue(boolean)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""false->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void isTrue(boolean, java.lang.String)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""false,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void isTrue(boolean, java.lang.String, double)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""false,_,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -4088,6 +4091,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void isTrue(boolean, java.lang.String, java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""false,_,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -4099,6 +4103,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void isTrue(boolean, java.lang.String, long)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""false,_,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -4129,6 +4134,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void notEmpty(java.lang.Object[])'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""null->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -4137,6 +4143,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void notEmpty(java.lang.Object[], java.lang.String)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""null,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -4145,6 +4152,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void notEmpty(java.lang.String)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""null->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -4153,6 +4161,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void notEmpty(java.lang.String, java.lang.String)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""null,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -4173,6 +4182,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void notNull(java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""null->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -4181,6 +4191,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void notNull(java.lang.Object, java.lang.String)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""null,_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
|
||||
@@ -280,7 +280,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.math.Fraction org.apache.commons.lang.math.Fraction addSub(org.apache.commons.lang.math.Fraction, boolean)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_->!null""/>
|
||||
<val val=""!null,_->!null;_,false->!null""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.math.Fraction org.apache.commons.lang.math.Fraction addSub(org.apache.commons.lang.math.Fraction, boolean) 0'>
|
||||
@@ -472,11 +472,13 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.math.JVMRandom double nextGaussian()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.math.JVMRandom void nextBytes(byte[])'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
|
||||
@@ -76,9 +76,7 @@
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.text.StrBuilder char[] getChars(char[])'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null->!null;null->!null""/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.text.StrBuilder char[] getChars(char[]) 0'>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
@@ -602,6 +600,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.text.StrMatcher.NoMatcher int isMatch(char[], int, int, int)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_,_,_,_->false""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -942,6 +941,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.text.StrTokenizer void add(java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -955,6 +955,7 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.text.StrTokenizer void set(java.lang.Object)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.app.Velocity boolean mergeTemplate(java.lang.String, java.lang.String, org.apache.velocity.context.Context, java.io.Writer)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_,_,_->true;_,!null,_,_->true;_,_,!null,_->true;_,_,_,!null->true;_,_,_,null->true;_,_,null,_->true;_,null,_,_->true;null,_,_,_->true""/>
|
||||
<val val=""_,_,_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.app.Velocity boolean mergeTemplate(java.lang.String, org.apache.velocity.context.Context, java.io.Writer)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_,_->true;_,!null,_->true;_,_,!null->true;_,_,null->true;_,null,_->true;null,_,_->true""/>
|
||||
<val val=""_,_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.app.VelocityEngine boolean evaluate(org.apache.velocity.context.Context, java.io.Writer, java.lang.String, java.io.InputStream) 3'>
|
||||
@@ -31,7 +31,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.app.VelocityEngine boolean mergeTemplate(java.lang.String, java.lang.String, org.apache.velocity.context.Context, java.io.Writer)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_,_,_->true;_,!null,_,_->true;_,_,!null,_->true;_,_,_,!null->true;_,_,_,null->true;_,_,null,_->true;_,null,_,_->true;null,_,_,_->true""/>
|
||||
<val val=""_,_,_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
</root>
|
||||
@@ -131,7 +131,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.app.event.implement.ReportInvalidReferences boolean invalidSetMethod(org.apache.velocity.context.Context, java.lang.String, java.lang.String, org.apache.velocity.util.introspection.Info)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_,_,_->false;_,!null,_,_->false;_,_,!null,_->false;_,_,_,!null->false;_,_,_,null->false;_,_,null,_->false;_,null,_,_->false;null,_,_,_->false""/>
|
||||
<val val=""_,_,_,_->false""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.app.event.implement.ReportInvalidReferences boolean invalidSetMethod(org.apache.velocity.context.Context, java.lang.String, java.lang.String, org.apache.velocity.util.introspection.Info) 0'>
|
||||
@@ -145,7 +145,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.app.event.implement.ReportInvalidReferences java.lang.Object invalidGetMethod(org.apache.velocity.context.Context, java.lang.String, java.lang.Object, java.lang.String, org.apache.velocity.util.introspection.Info)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_,_,_,_->null;_,!null,_,_,_->null;_,_,!null,_,_->null;_,_,_,!null,_->null;_,_,_,_,!null->null;_,_,_,_,null->null;_,_,_,null,_->null;_,_,null,_,_->null;_,null,_,_,_->null;null,_,_,_,_->null""/>
|
||||
<val val=""_,_,_,_,_->null""/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
</item>
|
||||
@@ -163,7 +163,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.app.event.implement.ReportInvalidReferences java.lang.Object invalidMethod(org.apache.velocity.context.Context, java.lang.String, java.lang.Object, java.lang.String, org.apache.velocity.util.introspection.Info)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_,_,_,_->null;_,!null,_,_,_->null;_,_,!null,_,_->null;_,_,_,!null,_->null;_,_,_,_,!null->null;_,_,_,_,null->null;_,_,_,null,_->null;_,_,null,_,_->null;_,null,_,_,_->null;null,_,_,_,_->null""/>
|
||||
<val val=""_,_,_,_,_->null""/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
</item>
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.io.VelocityWriter void bufferOverflow()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.RuntimeInstance boolean render(org.apache.velocity.context.Context, java.io.Writer, java.lang.String, org.apache.velocity.runtime.parser.node.SimpleNode)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_,_,_->true;_,!null,_,_->true;_,_,!null,_->true;_,_,_,!null->true;_,_,_,null->true;_,_,null,_->true;_,null,_,_->true;null,_,_,_->true""/>
|
||||
<val val=""_,_,_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.RuntimeInstance java.lang.Object getProperty(java.lang.String)'>
|
||||
@@ -166,7 +166,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.VelocimacroManager boolean addVM(java.lang.String, org.apache.velocity.runtime.parser.node.Node, java.lang.String[], java.lang.String, boolean)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_,_,_,_->true;_,!null,_,_,_->true;_,_,!null,_,_->true;_,_,_,!null,_->true;_,_,_,null,_->true;_,_,null,_,_->true;null,_,_,_,_->true""/>
|
||||
<val val=""_,_,_,_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.VelocimacroManager boolean addVM(java.lang.String, org.apache.velocity.runtime.parser.node.Node, java.lang.String[], java.lang.String, boolean) 1'>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.directive.Break boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer, org.apache.velocity.runtime.parser.node.Node)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_,_->false;_,!null,_->false;_,_,!null->false;_,null,_->false;null,_,_->false""/>
|
||||
<val val=""_,_,_->false""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.directive.Break boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer, org.apache.velocity.runtime.parser.node.Node) 1'>
|
||||
@@ -95,7 +95,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.directive.Define boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer, org.apache.velocity.runtime.parser.node.Node)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_,_->true;_,!null,_->true;_,_,!null->true;_,_,null->true;_,null,_->true""/>
|
||||
<val val=""_,_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.directive.Define boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer, org.apache.velocity.runtime.parser.node.Node) 0'>
|
||||
@@ -288,7 +288,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.directive.Include boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer, org.apache.velocity.runtime.parser.node.Node)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_,_->true;_,!null,_->true;_,_,!null->true;_,null,_->true;null,_,_->true""/>
|
||||
<val val=""_,_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.directive.Include boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer, org.apache.velocity.runtime.parser.node.Node) 2'>
|
||||
@@ -333,7 +333,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.directive.Literal boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer, org.apache.velocity.runtime.parser.node.Node)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_,_->true;_,!null,_->true;_,_,!null->true;_,_,null->true;null,_,_->true""/>
|
||||
<val val=""_,_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.directive.Literal boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer, org.apache.velocity.runtime.parser.node.Node) 0'>
|
||||
@@ -374,8 +374,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.directive.Macro boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer, org.apache.velocity.runtime.parser.node.Node)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value"
|
||||
val=""!null,_,_->true;_,!null,_->true;_,_,!null->true;_,_,null->true;_,null,_->true;null,_,_->true""/>
|
||||
<val name="value" val=""_,_,_->true""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -598,7 +597,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.directive.VelocimacroProxy boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer, org.apache.velocity.runtime.parser.node.Node, org.apache.velocity.runtime.Renderable)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_,_,_->true;_,!null,_,_->true;_,_,!null,_->true;_,_,_,!null->true;_,_,_,null->true;_,null,_,_->true;null,_,_,_->true""/>
|
||||
<val val=""_,_,_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.directive.VelocimacroProxy boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer, org.apache.velocity.runtime.parser.node.Node, org.apache.velocity.runtime.Renderable) 2'>
|
||||
|
||||
@@ -233,6 +233,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.log.RuntimeLoggerLog void setLogChute(org.apache.velocity.runtime.log.LogChute)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -241,6 +242,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.log.RuntimeLoggerLog void setShowStackTraces(boolean)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTBlock boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_->true;_,!null->true;_,null->true;null,_->true""/>
|
||||
<val val=""_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTBlock java.lang.Object jjtAccept(org.apache.velocity.runtime.parser.node.ParserVisitor, java.lang.Object) 0'>
|
||||
@@ -83,7 +83,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTComment boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_->true;_,!null->true;null,_->true""/>
|
||||
<val val=""_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTComment boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer) 0'>
|
||||
@@ -115,7 +115,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTDirective boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_->true;_,!null->true;_,null->true;null,_->true""/>
|
||||
<val val=""_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTDirective java.lang.Object init(org.apache.velocity.context.InternalContextAdapter, java.lang.Object)'>
|
||||
@@ -185,7 +185,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTElseStatement boolean evaluate(org.apache.velocity.context.InternalContextAdapter)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->true;null->true""/>
|
||||
<val name="value" val=""_->true""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -207,7 +207,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTEscape boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_->true;_,!null->true;null,_->true""/>
|
||||
<val val=""_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTEscape boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer) 0'>
|
||||
@@ -239,7 +239,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTEscapedDirective boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_->true;_,!null->true;null,_->true""/>
|
||||
<val val=""_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTEscapedDirective boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer) 0'>
|
||||
@@ -276,7 +276,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTFalse boolean evaluate(org.apache.velocity.context.InternalContextAdapter)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->false;null->false""/>
|
||||
<val name="value" val=""_->false""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -382,7 +382,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTIfStatement boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_->true;_,!null->true;_,null->true;null,_->true""/>
|
||||
<val val=""_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTIfStatement java.lang.Object jjtAccept(org.apache.velocity.runtime.parser.node.ParserVisitor, java.lang.Object) 0'>
|
||||
@@ -531,8 +531,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTMathNode java.lang.Object handleSpecial(java.lang.Object, java.lang.Object, org.apache.velocity.context.InternalContextAdapter)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value"
|
||||
val=""!null,_,_->null;_,!null,_->null;_,_,!null->null;_,_,null->null;_,null,_->null;null,_,_->null""/>
|
||||
<val name="value" val=""_,_,_->null""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
@@ -726,7 +725,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTReference boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_->true;_,!null->true;_,null->true;null,_->true""/>
|
||||
<val val=""_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTReference java.lang.Object execute(java.lang.Object, org.apache.velocity.context.InternalContextAdapter)'>
|
||||
@@ -855,7 +854,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTText boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_->true;_,!null->true;null,_->true""/>
|
||||
<val val=""_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTText boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer) 0'>
|
||||
@@ -887,7 +886,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTTextblock boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_->true;_,!null->true;null,_->true""/>
|
||||
<val val=""_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTTextblock boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer) 0'>
|
||||
@@ -919,7 +918,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.ASTTrue boolean evaluate(org.apache.velocity.context.InternalContextAdapter)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->true;null->true""/>
|
||||
<val name="value" val=""_->true""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -1136,7 +1135,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.SimpleNode boolean evaluate(org.apache.velocity.context.InternalContextAdapter)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->false;null->false""/>
|
||||
<val name="value" val=""_->false""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -1150,7 +1149,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.SimpleNode boolean render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""!null,_->true;_,!null->true;_,null->true;null,_->true""/>
|
||||
<val val=""_,_->true""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.SimpleNode int getColumn()'>
|
||||
@@ -1185,7 +1184,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.SimpleNode java.lang.Object execute(java.lang.Object, org.apache.velocity.context.InternalContextAdapter)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null,_->null;_,!null->null;_,null->null;null,_->null""/>
|
||||
<val name="value" val=""_,_->null""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
@@ -1206,7 +1205,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.node.SimpleNode java.lang.Object value(org.apache.velocity.context.InternalContextAdapter)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->null;null->null""/>
|
||||
<val name="value" val=""_->null""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader boolean isSourceModified(org.apache.velocity.runtime.resource.Resource)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->false;null->false""/>
|
||||
<val name="value" val=""_->false""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
@@ -116,7 +116,7 @@
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.resource.loader.JarResourceLoader boolean isSourceModified(org.apache.velocity.runtime.resource.Resource)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""!null->true;null->true""/>
|
||||
<val name="value" val=""_->true""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<root>
|
||||
<item name='org.apache.velocity.servlet.VelocityServlet org.apache.velocity.Template handleRequest(org.apache.velocity.context.Context)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="value" val=""_->fail""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
|
||||
@@ -123,13 +123,29 @@ public class Test01 {
|
||||
return s::trim;
|
||||
}
|
||||
|
||||
@ExpectContract(pure = true)
|
||||
@ExpectContract(pure = true, value="null,_->fail")
|
||||
public static void assertNotNull(@ExpectNotNull Object obj, String message) {
|
||||
if(obj == null) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
@ExpectContract(pure = true, value="false,_,_->fail;true,_,_->true")
|
||||
public static boolean assertTrue(boolean val, String message, int data) {
|
||||
if(!val) {
|
||||
throw new IllegalArgumentException(message+":"+data);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
@ExpectContract(pure = true, value="true,_->fail;_,_->false")
|
||||
public static boolean assertFalse(boolean val, String message) {
|
||||
if(val) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ExpectNotNull
|
||||
@ExpectContract(pure = true)
|
||||
public static long[] copyOfRange(@ExpectNotNull long[] arr, int from, int to) {
|
||||
@@ -154,4 +170,34 @@ public class Test01 {
|
||||
System.arraycopy(arr, from, copy, 0, Math.min(arr.length - from, diff));
|
||||
return copy;
|
||||
}
|
||||
|
||||
@ExpectContract(value = "_->fail", pure = true)
|
||||
public static void callAlwaysFail(int x) {
|
||||
alwaysFail();
|
||||
}
|
||||
|
||||
@ExpectContract(value = "_->fail", pure = true)
|
||||
public static void callAlwaysFailRef(String x) {
|
||||
callAlwaysFailTwoRefs(x, null);
|
||||
}
|
||||
|
||||
@ExpectContract(value = "_,_->fail", pure = true)
|
||||
public static void callAlwaysFailTwoRefs(String x, String y) {
|
||||
alwaysFail();
|
||||
}
|
||||
|
||||
@ExpectContract(value = "->fail", pure = true)
|
||||
private static void alwaysFail() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@ExpectContract(value = "!null->null;null->!null", pure = true)
|
||||
static String invert(String x) {
|
||||
return x == null ? "empty" : null;
|
||||
}
|
||||
|
||||
@ExpectContract(value = "false->true;true->false", pure = true)
|
||||
static boolean invertBool(boolean x) {
|
||||
return !x;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user