package com.intellij.codeInspection.bytecodeAnalysis;
import com.intellij.ide.highlighter.JavaClassFileType;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.util.SystemProperties;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.gist.GistManager;
+import com.intellij.util.gist.VirtualFileGist;
import com.intellij.util.indexing.*;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.DataInputOutputUtil;
import com.intellij.util.io.DifferentSerializableBytesImplyNonEqualityPolicy;
import com.intellij.util.io.KeyDescriptor;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.org.objectweb.asm.ClassReader;
+import org.jetbrains.org.objectweb.asm.MethodVisitor;
+import org.jetbrains.org.objectweb.asm.tree.MethodNode;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis.LOG;
/**
* @author lambdamix
*/
-public class BytecodeAnalysisIndex extends FileBasedIndexExtension<Bytes, HEquations> {
- public static final ID<Bytes, HEquations> NAME = ID.create("bytecodeAnalysis");
- private final HEquationsExternalizer myExternalizer = new HEquationsExternalizer();
- private static final ClassDataIndexer INDEXER = new ClassDataIndexer();
+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 int ourInternalVersion = 9;
- private static final boolean ourEnabled = SystemProperties.getBooleanProperty("idea.enable.bytecode.contract.inference", true);
+ private static final VirtualFileGist<Map<Bytes, HEquations>> ourGist = GistManager.getInstance().newVirtualFileGist(
+ "BytecodeAnalysisIndex", 0, new HEquationsExternalizer(), new ClassDataIndexer());
@NotNull
@Override
- public ID<Bytes, HEquations> getName() {
+ public ID<Bytes, Void> getName() {
return NAME;
}
@NotNull
@Override
- public DataIndexer<Bytes, HEquations, FileContent> getIndexer() {
- return INDEXER;
+ public DataIndexer<Bytes, Void, FileContent> getIndexer() {
+ return inputData -> {
+ try {
+ return collectKeys(inputData.getContent());
+ }
+ catch (ProcessCanceledException e) {
+ throw e;
+ }
+ catch (Throwable e) {
+ // incorrect bytecode may result in Runtime exceptions during analysis
+ // so here we suppose that exception is due to incorrect bytecode
+ LOG.debug("Unexpected Error during indexing of bytecode", e);
+ return Collections.emptyMap();
+ }
+ };
}
@NotNull
- @Override
- public KeyDescriptor<Bytes> getKeyDescriptor() {
- return KEY_DESCRIPTOR;
+ private static Map<Bytes, Void> collectKeys(byte[] content) throws NoSuchAlgorithmException {
+ HashMap<Bytes, Void> map = new HashMap<>();
+ MessageDigest md = BytecodeAnalysisConverter.getMessageDigest();
+ new ClassReader(content).accept(new KeyedMethodVisitor() {
+ @Nullable
+ @Override
+ MethodVisitor visitMethod(MethodNode node, Key key) {
+ map.put(ClassDataIndexer.compressKey(md, key), null);
+ return null;
+ }
+ }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+ return map;
}
@NotNull
@Override
- public DataExternalizer<HEquations> getValueExternalizer() {
- return myExternalizer;
+ public KeyDescriptor<Bytes> getKeyDescriptor() {
+ return KEY_DESCRIPTOR;
}
@Override
@NotNull
@Override
public FileBasedIndex.InputFilter getInputFilter() {
- return new DefaultFileTypeSpecificInputFilter(JavaClassFileType.INSTANCE) {
- @Override
- public boolean acceptInput(@NotNull VirtualFile file) {
- return ourEnabled && super.acceptInput(file);
- }
- };
+ return new DefaultFileTypeSpecificInputFilter(JavaClassFileType.INSTANCE);
}
@Override
@Override
public int getVersion() {
- return ourInternalVersion + (ourEnabled ? 0xFF : 0);
+ return 10;
+ }
+
+ @NotNull
+ static List<HEquations> getEquations(GlobalSearchScope scope, Bytes key) {
+ Project project = ProjectManager.getInstance().getDefaultProject(); // the data is project-independent
+ return ContainerUtil.mapNotNull(FileBasedIndex.getInstance().getContainingFiles(NAME, key, scope),
+ file -> ourGist.getFileData(project, file).get(key));
}
/**
/**
* Externalizer for compressed equations.
*/
- public static class HEquationsExternalizer implements DataExternalizer<HEquations> {
+ public static class HEquationsExternalizer implements DataExternalizer<Map<Bytes, HEquations>> {
@Override
- public void save(@NotNull DataOutput out, HEquations eqs) throws IOException {
+ public void save(@NotNull DataOutput out, Map<Bytes, HEquations> value) throws IOException {
+ DataInputOutputUtil.writeSeq(out, value.entrySet(), entry -> {
+ KEY_DESCRIPTOR.save(out, entry.getKey());
+ saveEquations(out, entry.getValue());
+ });
+ }
+
+ @Override
+ public Map<Bytes, HEquations> read(@NotNull DataInput in) throws IOException {
+ return DataInputOutputUtil.readSeq(in, () -> Pair.create(KEY_DESCRIPTOR.read(in), readEquations(in))).
+ stream().collect(Collectors.toMap(p -> p.getFirst(), p -> p.getSecond()));
+ }
+
+ private static void saveEquations(@NotNull DataOutput out, HEquations eqs) throws IOException {
out.writeBoolean(eqs.stable);
DataInputOutputUtil.writeINT(out, eqs.results.size());
for (DirectionResultPair pair : eqs.results) {
}
}
- @Override
- public HEquations read(@NotNull DataInput in) throws IOException {
+ private static HEquations readEquations(@NotNull DataInput in) throws IOException {
boolean stable = in.readBoolean();
int size = DataInputOutputUtil.readINT(in);
ArrayList<DirectionResultPair> results = new ArrayList<>(size);
import com.intellij.codeInspection.bytecodeAnalysis.asm.*;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManager;
-import com.intellij.openapi.util.Pair;
-import com.intellij.util.indexing.DataIndexer;
-import com.intellij.util.indexing.FileContent;
+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 gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.jetbrains.org.objectweb.asm.*;
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
import java.security.MessageDigest;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*;
import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis.LOG;
*
* @author lambdamix
*/
-public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileContent> {
+public class ClassDataIndexer implements VirtualFileGist.GistCalculator<Map<Bytes, HEquations>> {
- private static final int STABLE_FLAGS = Opcodes.ACC_FINAL | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
public static final Final FINAL_TOP = new Final(Value.Top);
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);
- @NotNull
+ @Nullable
@Override
- public Map<Bytes, HEquations> map(@NotNull FileContent inputData) {
+ public Map<Bytes, HEquations> calcData(@NotNull Project project, @NotNull VirtualFile file) {
HashMap<Bytes, HEquations> map = new HashMap<>();
try {
MessageDigest md = BytecodeAnalysisConverter.getMessageDigest();
- Map<Key, List<Equation>> allEquations = processClass(new ClassReader(inputData.getContent()), inputData.getFile().getPresentableUrl());
+ Map<Key, List<Equation>> allEquations = processClass(new ClassReader(file.contentsToByteArray()), file.getPresentableUrl());
for (Map.Entry<Key, List<Equation>> entry: allEquations.entrySet()) {
-
Key methodKey = entry.getKey();
- // method equations with raw (not-compressed keys)
- List<Equation> rawMethodEquations = entry.getValue();
- //
- List<DirectionResultPair> compressedMethodEquations =
- new ArrayList<>(rawMethodEquations.size());
- for (Equation equation : rawMethodEquations) {
- compressedMethodEquations.add(BytecodeAnalysisConverter.convert(equation, md));
- }
- map.put(new Bytes(BytecodeAnalysisConverter.asmKey(methodKey, md).key), new HEquations(compressedMethodEquations, methodKey.stable));
+ map.put(compressKey(md, methodKey), convertEquations(md, methodKey, entry.getValue()));
}
}
catch (ProcessCanceledException e) {
return map;
}
+ @NotNull
+ static Bytes compressKey(MessageDigest md, Key methodKey) {
+ return new Bytes(BytecodeAnalysisConverter.asmKey(methodKey, md).key);
+ }
+
+ @NotNull
+ private static HEquations convertEquations(MessageDigest md, Key methodKey, List<Equation> rawMethodEquations) {
+ List<DirectionResultPair> compressedMethodEquations =
+ ContainerUtil.map(rawMethodEquations, equation -> BytecodeAnalysisConverter.convert(equation, md));
+ return new HEquations(compressedMethodEquations, methodKey.stable);
+ }
+
public static Map<Key, List<Equation>> processClass(final ClassReader classReader, final String presentableUrl) {
// It is OK to share pending states, actions and results for analyses.
final PResults.PResult[] sharedResults = new PResults.PResult[Analysis.STEPS_LIMIT];
final Map<Key, List<Equation>> equations = new HashMap<>();
- classReader.accept(new ClassVisitor(Opcodes.API_VERSION) {
- private String className;
- private boolean stableClass;
-
- @Override
- public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
- className = name;
- stableClass = (access & Opcodes.ACC_FINAL) != 0;
- super.visit(version, access, name, signature, superName, interfaces);
- }
+ classReader.accept(new KeyedMethodVisitor() {
- @Override
- public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
- final MethodNode node = new MethodNode(Opcodes.API_VERSION, access, name, desc, signature, exceptions);
+ protected MethodVisitor visitMethod(final MethodNode node, final Key key) {
return new MethodVisitor(Opcodes.API_VERSION, node) {
private boolean jsr;
@Override
public void visitEnd() {
super.visitEnd();
- Pair<Key, List<Equation>> methodEquations = processMethod(node, jsr);
- equations.put(methodEquations.first, methodEquations.second);
+ equations.put(key, processMethod(node, jsr, key.method, key.stable));
}
};
}
*
* @param methodNode asm node for method
* @param jsr whether a method has jsr instruction
- * @return pair of (primaryKey, equations)
*/
- private Pair<Key, List<Equation>> processMethod(final MethodNode methodNode, boolean jsr) {
+ private List<Equation> processMethod(final MethodNode methodNode, boolean jsr, Method method, boolean stable) {
ProgressManager.checkCanceled();
final Type[] argumentTypes = Type.getArgumentTypes(methodNode.desc);
final Type resultType = Type.getReturnType(methodNode.desc);
final boolean isBooleanResult = ASMUtils.isBooleanType(resultType);
final boolean isInterestingResult = isReferenceResult || isBooleanResult;
- final Method method = new Method(className, methodNode.name, methodNode.desc);
- final boolean stable = stableClass || (methodNode.access & STABLE_FLAGS) != 0 || "<init>".equals(methodNode.name);
-
- Key primaryKey = new Key(method, Out, stable);
-
// 4*n: for each reference parameter: @NotNull IN, @Nullable, null -> ... contract, !null -> contract
// 3: @NotNull OUT, @Nullable OUT, purity analysis
List<Equation> equations = new ArrayList<>(argumentTypes.length * 4 + 3);
if (argumentTypes.length == 0 && !isInterestingResult) {
// no need to continue analysis
- return Pair.create(primaryKey, equations);
+ return equations;
}
try {
if (richControlFlow.reducible()) {
NegationAnalysis negated = tryNegation(method, argumentTypes, graph, isBooleanResult, dfs, jsr);
processBranchingMethod(method, methodNode, richControlFlow, argumentTypes, isReferenceResult, isBooleanResult, stable, jsr, equations, negated);
- return Pair.create(primaryKey, equations);
+ return equations;
}
LOG.debug(method + ": CFG is not reducible");
}
// simple
else {
processNonBranchingMethod(method, argumentTypes, graph, isReferenceResult, isBooleanResult, stable, equations);
- return Pair.create(primaryKey, equations);
+ return equations;
}
}
- return Pair.create(primaryKey, topEquations(method, argumentTypes, isReferenceResult, isInterestingResult, stable));
+ return topEquations(method, argumentTypes, isReferenceResult, isInterestingResult, stable);
}
catch (ProcessCanceledException e) {
throw e;
// incorrect bytecode may result in Runtime exceptions during analysis
// so here we suppose that exception is due to incorrect bytecode
LOG.debug("Unexpected Error during processing of " + method + " in " + presentableUrl, e);
- return Pair.create(primaryKey, topEquations(method, argumentTypes, isReferenceResult, isInterestingResult, stable));
+ return topEquations(method, argumentTypes, isReferenceResult, isInterestingResult, stable);
}
}
--- /dev/null
+/*
+ * Copyright 2000-2016 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 org.jetbrains.annotations.Nullable;
+import org.jetbrains.org.objectweb.asm.ClassVisitor;
+import org.jetbrains.org.objectweb.asm.MethodVisitor;
+import org.jetbrains.org.objectweb.asm.Opcodes;
+import org.jetbrains.org.objectweb.asm.tree.MethodNode;
+
+import static com.intellij.codeInspection.bytecodeAnalysis.Direction.Out;
+
+/**
+ * @author peter
+ */
+abstract class KeyedMethodVisitor extends ClassVisitor {
+ private static final int STABLE_FLAGS = Opcodes.ACC_FINAL | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
+
+ KeyedMethodVisitor() {
+ super(Opcodes.API_VERSION);
+ }
+
+ String className;
+ private boolean stableClass;
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+ className = name;
+ stableClass = (access & Opcodes.ACC_FINAL) != 0;
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+ MethodNode node = new MethodNode(Opcodes.API_VERSION, access, name, desc, signature, exceptions);
+ Method method = new Method(className, node.name, node.desc);
+ boolean stable = stableClass || (node.access & STABLE_FLAGS) != 0 || "<init>".equals(node.name);
+
+ return visitMethod(node, new Key(method, Out, stable));
+ }
+
+ @Nullable
+ abstract MethodVisitor visitMethod(final MethodNode node, final Key key);
+}
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
-import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.ProjectScope;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiFormatUtil;
+import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ConcurrentFactoryMap;
+import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.Stack;
-import com.intellij.util.indexing.FileBasedIndex;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
private final Project myProject;
private final boolean nullableMethod;
private final boolean nullableMethodTransitivity;
+ private final Map<Bytes, List<HEquations>> myEquationCache = ContainerUtil.createConcurrentSoftValueMap();
public static ProjectBytecodeAnalysis getInstance(@NotNull Project project) {
return ServiceManager.getService(project, ProjectBytecodeAnalysis.class);
myProject = project;
nullableMethod = Registry.is(NULLABLE_METHOD);
nullableMethodTransitivity = Registry.is(NULLABLE_METHOD_TRANSITIVITY);
+ myProject.getMessageBus().connect().subscribe(PsiModificationTracker.TOPIC, () -> myEquationCache.clear());
}
@Nullable
private ParameterAnnotations loadParameterAnnotations(@NotNull HKey notNullKey)
throws EquationsLimitException {
- Map<Bytes, List<HEquations>> equationsCache = new HashMap<>();
-
final Solver notNullSolver = new Solver(new ELattice<>(Value.NotNull, Value.Top), Value.Top);
- collectEquations(Collections.singletonList(notNullKey), notNullSolver, equationsCache);
+ collectEquations(Collections.singletonList(notNullKey), notNullSolver);
Map<HKey, Value> notNullSolutions = notNullSolver.solve();
// subtle point
final Solver nullableSolver = new Solver(new ELattice<>(Value.Null, Value.Top), Value.Top);
final HKey nullableKey = new HKey(notNullKey.key, notNullKey.dirKey + 1, true, false);
- collectEquations(Collections.singletonList(nullableKey), nullableSolver, equationsCache);
+ collectEquations(Collections.singletonList(nullableKey), nullableSolver);
Map<HKey, Value> nullableSolutions = nullableSolver.solve();
// subtle point
boolean nullable =
private MethodAnnotations loadMethodAnnotations(@NotNull PsiMethod owner, @NotNull HKey key, ArrayList<HKey> allKeys)
throws EquationsLimitException {
MethodAnnotations result = new MethodAnnotations();
- Map<Bytes, List<HEquations>> equationsCache = new HashMap<>();
final Solver outSolver = new Solver(new ELattice<>(Value.Bot, Value.Top), Value.Top);
final PuritySolver puritySolver = new PuritySolver();
- collectEquations(allKeys, outSolver, equationsCache);
- collectPurityEquations(key.updateDirection(BytecodeAnalysisConverter.mkDirectionKey(Pure)), puritySolver, equationsCache);
+ collectEquations(allKeys, outSolver);
+ collectPurityEquations(key.updateDirection(BytecodeAnalysisConverter.mkDirectionKey(Pure)), puritySolver);
Map<HKey, Value> solutions = outSolver.solve();
Map<HKey, Set<HEffectQuantum>> puritySolutions = puritySolver.solve();
final Solver nullableMethodSolver = new Solver(new ELattice<>(Value.Bot, Value.Null), Value.Bot);
HKey nullableKey = key.updateDirection(BytecodeAnalysisConverter.mkDirectionKey(NullableOut));
if (nullableMethodTransitivity) {
- collectEquations(Collections.singletonList(nullableKey), nullableMethodSolver, equationsCache);
+ collectEquations(Collections.singletonList(nullableKey), nullableMethodSolver);
}
else {
- collectSingleEquation(nullableKey, nullableMethodSolver, equationsCache);
+ collectSingleEquation(nullableKey, nullableMethodSolver);
}
Map<HKey, Value> nullableSolutions = nullableMethodSolver.solve();
if (nullableSolutions.get(nullableKey) == Value.Null || nullableSolutions.get(nullableKey.invertStability()) == Value.Null) {
return result;
}
- private void collectPurityEquations(HKey key, PuritySolver puritySolver, Map<Bytes, List<HEquations>> cache)
+ private List<HEquations> getEquations(Bytes key) {
+ List<HEquations> result = myEquationCache.get(key);
+ if (result == null) {
+ myEquationCache.put(key, result = BytecodeAnalysisIndex.getEquations(ProjectScope.getLibrariesScope(myProject), key));
+ }
+ return result;
+ }
+
+ private void collectPurityEquations(HKey key, PuritySolver puritySolver)
throws EquationsLimitException {
- GlobalSearchScope librariesScope = ProjectScope.getLibrariesScope(myProject);
HashSet<HKey> queued = new HashSet<>();
Stack<HKey> queue = new Stack<>();
queue.push(key);
queued.add(key);
- FileBasedIndex index = FileBasedIndex.getInstance();
-
while (!queue.empty()) {
if (queued.size() > EQUATIONS_LIMIT) {
throw new EquationsLimitException();
HKey hKey = queue.pop();
Bytes bytes = new Bytes(hKey.key);
- List<HEquations> hEquationss = cache.get(bytes);
- if (hEquationss == null) {
- hEquationss = index.getValues(BytecodeAnalysisIndex.NAME, bytes, librariesScope);
- cache.put(bytes, hEquationss);
- }
-
- for (HEquations hEquations : hEquationss) {
+ for (HEquations hEquations : getEquations(bytes)) {
boolean stable = hEquations.stable;
for (DirectionResultPair pair : hEquations.results) {
int dirKey = pair.directionKey;
}
}
- private void collectEquations(List<HKey> keys, Solver solver, @NotNull Map<Bytes, List<HEquations>> cache) throws EquationsLimitException {
-
- GlobalSearchScope librariesScope = ProjectScope.getLibrariesScope(myProject);
+ private void collectEquations(List<HKey> keys, Solver solver) throws EquationsLimitException {
HashSet<HKey> queued = new HashSet<>();
Stack<HKey> queue = new Stack<>();
queued.add(key);
}
- FileBasedIndex index = FileBasedIndex.getInstance();
-
while (!queue.empty()) {
if (queued.size() > EQUATIONS_LIMIT) {
throw new EquationsLimitException();
HKey hKey = queue.pop();
Bytes bytes = new Bytes(hKey.key);
- List<HEquations> hEquationss = cache.get(bytes);
- if (hEquationss == null) {
- hEquationss = index.getValues(BytecodeAnalysisIndex.NAME, bytes, librariesScope);
- cache.put(bytes, hEquationss);
- }
-
- for (HEquations hEquations : hEquationss) {
+ for (HEquations hEquations : getEquations(bytes)) {
boolean stable = hEquations.stable;
for (DirectionResultPair pair : hEquations.results) {
int dirKey = pair.directionKey;
}
}
- private void collectSingleEquation(HKey hKey, Solver solver, @NotNull Map<Bytes, List<HEquations>> cache) throws EquationsLimitException {
- GlobalSearchScope librariesScope = ProjectScope.getLibrariesScope(myProject);
-
- FileBasedIndex index = FileBasedIndex.getInstance();
-
+ private void collectSingleEquation(HKey hKey, Solver solver) throws EquationsLimitException {
ProgressManager.checkCanceled();
Bytes bytes = new Bytes(hKey.key);
- List<HEquations> hEquationss = cache.get(bytes);
- if (hEquationss == null) {
- hEquationss = index.getValues(BytecodeAnalysisIndex.NAME, bytes, librariesScope);
- cache.put(bytes, hEquationss);
- }
-
- for (HEquations hEquations : hEquationss) {
+ for (HEquations hEquations : getEquations(bytes)) {
boolean stable = hEquations.stable;
for (DirectionResultPair pair : hEquations.results) {
int dirKey = pair.directionKey;