mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
BytecodeAnalysis: partial purity solution before serialization
All purity dependencies within current class are solved before serialization. This reduces index space and solve complexity after reading (in particular for some methods complete solution could be found analyzing their declaring class only). Also this may reduce the necessity to load some dependencies. E.g. if X::a calls X::b and Y::c (in any order) and X::b is not pure then X::a is solved to be not pure and class Y could be not analyzed at all.
This commit is contained in:
+15
@@ -71,6 +71,7 @@ public class ClassDataIndexer implements VirtualFileGist.GistCalculator<Map<HMet
|
||||
try {
|
||||
MessageDigest md = BytecodeAnalysisConverter.getMessageDigest();
|
||||
Map<EKey, Equations> allEquations = processClass(new ClassReader(file.contentsToByteArray(false)), file.getPresentableUrl());
|
||||
allEquations = solvePartially(allEquations);
|
||||
allEquations.forEach((methodKey, equations) -> map.put(methodKey.method.hashed(md), hash(equations, md)));
|
||||
}
|
||||
catch (ProcessCanceledException e) {
|
||||
@@ -85,6 +86,20 @@ public class ClassDataIndexer implements VirtualFileGist.GistCalculator<Map<HMet
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<EKey, Equations> solvePartially(Map<EKey, Equations> map) {
|
||||
PuritySolver solver = new PuritySolver();
|
||||
EntryStream.of(map).mapToKey((key, eqs) -> new EKey(key.method, Pure, eqs.stable, false))
|
||||
.flatMapValues(eqs -> eqs.results.stream().map(drp -> drp.result))
|
||||
.selectValues(Effects.class)
|
||||
.forKeyValue(solver::addEquation);
|
||||
Map<EKey, Effects> solved = solver.solve();
|
||||
Map<EKey, Effects> partiallySolvedPurity =
|
||||
StreamEx.of(solved, solver.pending).flatMapToEntry(Function.identity()).removeValues(Effects::isTop).toMap();
|
||||
return EntryStream.of(map)
|
||||
.mapToValue((key, eqs) -> eqs.update(Pure, partiallySolvedPurity.get(new EKey(key.method, Pure, eqs.stable, false))))
|
||||
.toMap();
|
||||
}
|
||||
|
||||
private static Equations hash(Equations equations, MessageDigest md) {
|
||||
return new Equations(ContainerUtil.map(equations.results, drp -> hash(drp, md)), equations.stable);
|
||||
}
|
||||
|
||||
@@ -15,12 +15,11 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.bytecodeAnalysis;
|
||||
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
@@ -129,6 +128,15 @@ class Equations {
|
||||
public int hashCode() {
|
||||
return 31 * results.hashCode() + (stable ? 1 : 0);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
Equations update(Direction direction, Effects newResult) {
|
||||
List<DirectionResultPair> newPairs = StreamEx.of(this.results)
|
||||
.map(drp -> drp.updateForDirection(direction, newResult))
|
||||
.nonNull()
|
||||
.toList();
|
||||
return new Equations(newPairs, this.stable);
|
||||
}
|
||||
}
|
||||
|
||||
class DirectionResultPair {
|
||||
@@ -159,6 +167,16 @@ class DirectionResultPair {
|
||||
public String toString() {
|
||||
return Direction.fromInt(directionKey) + "->" + result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
DirectionResultPair updateForDirection(Direction direction, Result newResult) {
|
||||
if (this.directionKey == direction.asInt()) {
|
||||
return newResult == null ? null : new DirectionResultPair(direction.asInt(), newResult);
|
||||
}
|
||||
else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Result {}
|
||||
@@ -227,6 +245,8 @@ final class Pending implements Result {
|
||||
}
|
||||
|
||||
final class Effects implements Result {
|
||||
static final Set<EffectQuantum> TOP_EFFECTS = Collections.singleton(EffectQuantum.TopEffectQuantum);
|
||||
|
||||
@NotNull final DataValue returnValue;
|
||||
@NotNull final Set<EffectQuantum> effects;
|
||||
|
||||
@@ -239,6 +259,10 @@ final class Effects implements Result {
|
||||
return Stream.concat(returnValue.dependencies(), effects.stream().flatMap(EffectQuantum::dependencies));
|
||||
}
|
||||
|
||||
public boolean isTop() {
|
||||
return returnValue == DataValue.UnknownDataValue1 && effects.equals(TOP_EFFECTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@@ -254,6 +278,7 @@ final class Effects implements Result {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Effects[" + effects.size() + "|" + returnValue + "]";
|
||||
Object effectsPresentation = effects.isEmpty() ? "Pure" : effects.size() == 1 ? effects.iterator().next() : effects.size();
|
||||
return "Effects[" + effectsPresentation + "|" + returnValue + "]";
|
||||
}
|
||||
}
|
||||
+9
-10
@@ -66,7 +66,7 @@ public class PurityAnalysis {
|
||||
for (EffectQuantum effectQuantum : quanta) {
|
||||
if (effectQuantum != null) {
|
||||
if (effectQuantum == EffectQuantum.TopEffectQuantum) {
|
||||
return returnValue == DataValue.UnknownDataValue1 ? null : new Equation(key, new Effects(returnValue, PuritySolver.TOP_EFFECTS));
|
||||
return returnValue == DataValue.UnknownDataValue1 ? null : new Equation(key, new Effects(returnValue, Effects.TOP_EFFECTS));
|
||||
}
|
||||
effects.add(effectQuantum);
|
||||
}
|
||||
@@ -559,11 +559,10 @@ class DataInterpreter extends Interpreter<DataValue> {
|
||||
}
|
||||
|
||||
final class PuritySolver {
|
||||
static final Set<EffectQuantum> TOP_EFFECTS = Collections.singleton(EffectQuantum.TopEffectQuantum);
|
||||
private HashMap<EKey, Effects> solved = new HashMap<>();
|
||||
private HashMap<EKey, Set<EKey>> dependencies = new HashMap<>();
|
||||
private final Stack<EKey> moving = new Stack<>();
|
||||
private HashMap<EKey, Effects> pending = new HashMap<>();
|
||||
HashMap<EKey, Effects> pending = new HashMap<>();
|
||||
|
||||
void addEquation(EKey key, Effects effects) {
|
||||
Set<EKey> depKeys = effects.dependencies().collect(Collectors.toSet());
|
||||
@@ -593,7 +592,7 @@ final class PuritySolver {
|
||||
}
|
||||
else {
|
||||
propagateKeys = new EKey[]{key.mkStable(), key};
|
||||
propagateEffects = new Effects[]{effects, new Effects(DataValue.UnknownDataValue1, TOP_EFFECTS)};
|
||||
propagateEffects = new Effects[]{effects, new Effects(DataValue.UnknownDataValue1, Effects.TOP_EFFECTS)};
|
||||
}
|
||||
for (int i = 0; i < propagateKeys.length; i++) {
|
||||
EKey pKey = propagateKeys[i];
|
||||
@@ -615,7 +614,7 @@ final class PuritySolver {
|
||||
EffectQuantum.CallQuantum call = substitute((EffectQuantum.CallQuantum)dEffect, pKey, pEffects);
|
||||
if (call.key.equals(pKey)) {
|
||||
delta = substitute(pEffects, call.data, call.isStatic);
|
||||
if(delta.equals(TOP_EFFECTS)) {
|
||||
if(delta.equals(Effects.TOP_EFFECTS)) {
|
||||
newEffects = delta;
|
||||
break;
|
||||
}
|
||||
@@ -630,7 +629,7 @@ final class PuritySolver {
|
||||
EffectQuantum.ReturnChangeQuantum retChange = (EffectQuantum.ReturnChangeQuantum)dEffect;
|
||||
if (retChange.key.equals(pKey)) {
|
||||
if(pEffects.returnValue != DataValue.LocalDataValue) {
|
||||
newEffects = delta = TOP_EFFECTS;
|
||||
newEffects = delta = Effects.TOP_EFFECTS;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
@@ -639,8 +638,8 @@ final class PuritySolver {
|
||||
newEffects.add(dEffect);
|
||||
}
|
||||
|
||||
if (TOP_EFFECTS.equals(delta) && returnValue.equals(DataValue.UnknownDataValue1)) {
|
||||
solved.put(dKey, new Effects(returnValue, TOP_EFFECTS));
|
||||
if (Effects.TOP_EFFECTS.equals(delta) && returnValue.equals(DataValue.UnknownDataValue1)) {
|
||||
solved.put(dKey, new Effects(returnValue, Effects.TOP_EFFECTS));
|
||||
moving.push(dKey);
|
||||
}
|
||||
else {
|
||||
@@ -682,7 +681,7 @@ final class PuritySolver {
|
||||
}
|
||||
|
||||
private static Set<EffectQuantum> substitute(Effects effects, DataValue[] data, boolean isStatic) {
|
||||
if (effects.effects.isEmpty() || TOP_EFFECTS.equals(effects.effects)) {
|
||||
if (effects.effects.isEmpty() || Effects.TOP_EFFECTS.equals(effects.effects)) {
|
||||
return effects.effects;
|
||||
}
|
||||
Set<EffectQuantum> newEffects = new HashSet<>(effects.effects.size());
|
||||
@@ -710,7 +709,7 @@ final class PuritySolver {
|
||||
newEffects.add(new EffectQuantum.ReturnChangeQuantum(((DataValue.ReturnDataValue)arg).key));
|
||||
continue;
|
||||
}
|
||||
return TOP_EFFECTS;
|
||||
return Effects.TOP_EFFECTS;
|
||||
}
|
||||
return newEffects;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user