straightforward solver

This commit is contained in:
Ilya Klyuchnikov
2014-07-10 10:35:43 +02:00
committed by peter
parent 7056873275
commit 75eb3cb321
5 changed files with 118 additions and 179 deletions
@@ -99,13 +99,13 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
result = new IntIdFinal(((Final<Key, Value>)rhs).value);
} else {
Pending<Key, Value> pending = (Pending<Key, Value>)rhs;
Set<Set<Key>> deltaOrig = pending.delta;
IntIdComponent[] components = new IntIdComponent[deltaOrig.size()];
Set<Product<Key, Value>> sumOrigin = pending.sum;
IntIdComponent[] components = new IntIdComponent[sumOrigin.size()];
int componentI = 0;
for (Set<Key> keyComponent : deltaOrig) {
int[] ids = new int[keyComponent.size()];
for (Product<Key, Value> prod : sumOrigin) {
int[] ids = new int[prod.ids.size()];
int idI = 0;
for (Key id : keyComponent) {
for (Key id : prod.ids) {
int[] compoundKey = mkCompoundKey(id);
int rawId = myCompoundKeyEnumerator.enumerate(compoundKey);
if (rawId <= 0) {
@@ -114,11 +114,11 @@ public class BytecodeAnalysisConverter implements ApplicationComponent {
ids[idI] = id.stable ? rawId : -rawId;
idI++;
}
IntIdComponent intIdComponent = new IntIdComponent(ids);
IntIdComponent intIdComponent = new IntIdComponent(prod.value, ids);
components[componentI] = intIdComponent;
componentI++;
}
result = new IntIdPending(pending.infinum, pending.rigid, components);
result = new IntIdPending(components);
}
int rawKey = myCompoundKeyEnumerator.enumerate(mkCompoundKey(equation.id));
@@ -91,19 +91,17 @@ public class BytecodeAnalysisIndex extends FileBasedIndexExtension<Integer, Coll
IntIdResult rhs = equation.rhs;
if (rhs instanceof IntIdFinal) {
IntIdFinal finalResult = (IntIdFinal)rhs;
out.writeBoolean(true);
out.writeBoolean(true); // final flag
DataInputOutputUtil.writeINT(out, finalResult.value.ordinal());
} else {
IntIdPending pendResult = (IntIdPending)rhs;
out.writeBoolean(false);
DataInputOutputUtil.writeINT(out, pendResult.infinum.ordinal());
out.writeBoolean(pendResult.rigid);
out.writeBoolean(false); // pending flag
DataInputOutputUtil.writeINT(out, pendResult.delta.length);
for (IntIdComponent component : pendResult.delta) {
DataInputOutputUtil.writeINT(out, component.value.ordinal());
int[] ids = component.ids;
DataInputOutputUtil.writeINT(out, ids.length);
for (int id : ids) {
out.writeInt(id);
}
@@ -120,27 +118,27 @@ public class BytecodeAnalysisIndex extends FileBasedIndexExtension<Integer, Coll
for (int x = 0; x < size; x++) {
int equationId = in.readInt();
boolean isFinal = in.readBoolean();
boolean isFinal = in.readBoolean(); // flag
if (isFinal) {
int ordinal = DataInputOutputUtil.readINT(in);
Value value = Value.values()[ordinal];
result.add(new IntIdEquation(equationId, new IntIdFinal(value)));
} else {
int ordinal = DataInputOutputUtil.readINT(in);
Value value = Value.values()[ordinal];
boolean rigid = in.readBoolean();
int deltaLength = DataInputOutputUtil.readINT(in);
IntIdComponent[] components = new IntIdComponent[deltaLength];
for (int i = 0; i < deltaLength; i++) {
int sumLength = DataInputOutputUtil.readINT(in);
IntIdComponent[] components = new IntIdComponent[sumLength];
for (int i = 0; i < sumLength; i++) {
int ordinal = DataInputOutputUtil.readINT(in);
Value value = Value.values()[ordinal];
int componentSize = DataInputOutputUtil.readINT(in);
int[] ids = new int[componentSize];
for (int j = 0; j < componentSize; j++) {
ids[j] = in.readInt();
}
components[i] = new IntIdComponent(ids);
components[i] = new IntIdComponent(value, ids);
}
result.add(new IntIdEquation(equationId, new IntIdPending(value, rigid, components)));
result.add(new IntIdEquation(equationId, new IntIdPending(components)));
}
}
@@ -36,13 +36,11 @@ class InOutAnalysis extends Analysis<Result<Key, Value>> {
private final InOutInterpreter interpreter;
private final Value inValue;
private boolean nullAnalysis;
protected InOutAnalysis(RichControlFlow richControlFlow, Direction direction, TIntHashSet resultOrigins, boolean stable) {
super(richControlFlow, direction, stable);
interpreter = new InOutInterpreter(direction, richControlFlow.controlFlow.methodNode.instructions, resultOrigins);
inValue = direction instanceof InOut ? ((InOut)direction).inValue : null;
nullAnalysis = (direction instanceof InOut) && (((InOut)direction).inValue) == Value.Null;
}
@Override
@@ -65,8 +63,10 @@ class InOutAnalysis extends Analysis<Result<Key, Value>> {
@Override
boolean isEarlyResult(Result<Key, Value> res) {
Value value = res instanceof Final ? ((Final<?, Value>)res).value : ((Pending<?, Value>)res).infinum;
return value == Value.Top;
if (res instanceof Final) {
return ((Final<?, Value>)res).value == Value.Top;
}
return false;
}
@Override
@@ -126,9 +126,7 @@ class InOutAnalysis extends Analysis<Result<Key, Value>> {
}
else if (stackTop instanceof CallResultValue) {
Set<Key> keys = ((CallResultValue) stackTop).inters;
Set<Set<Key>> components = new HashSet<Set<Key>>();
components.add(keys);
results.put(stateIndex, new Pending<Key, Value>(Value.Bot, false, components));
results.put(stateIndex, new Pending<Key, Value>(Collections.singleton(new Product<Key, Value>(Value.Top, keys))));
computed.put(insnIndex, append(computed.get(insnIndex), state));
}
else {
@@ -153,11 +153,11 @@ class NonNullInAnalysis extends Analysis<PResult> {
}
else {
ConditionalNPE condNpe = (ConditionalNPE) result;
Set<Set<Key>> components = new HashSet<Set<Key>>();
Set<Product<Key, Value>> components = new HashSet<Product<Key, Value>>();
for (Set<Key> prod : condNpe.sop) {
components.add(prod);
components.add(new Product<Key, Value>(Value.Top, prod));
}
return new Equation<Key, Value>(aKey, new Pending<Key, Value>(Value.NotNull, false, components));
return new Equation<Key, Value>(aKey, new Pending<Key, Value>(components));
}
}
@@ -17,10 +17,10 @@ package com.intellij.codeInspection.bytecodeAnalysis;
import com.intellij.util.containers.IntStack;
import com.intellij.util.containers.IntToIntSetMap;
import com.sun.istack.internal.NotNull;
import gnu.trove.TIntObjectHashMap;
import java.util.*;
import static com.intellij.codeInspection.bytecodeAnalysis.IdUtils.*;
final class ELattice<T extends Enum<T>> {
final T bot;
@@ -48,81 +48,60 @@ final class ELattice<T extends Enum<T>> {
// component specialized for ints
final class IntIdComponent {
Value value;
final int[] ids;
IntIdComponent(Value value, int[] ids) {
this.value = value;
this.ids = ids;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof IntIdComponent)) return false;
if (o == null || getClass() != o.getClass()) return false;
IntIdComponent component = (IntIdComponent)o;
IntIdComponent that = (IntIdComponent)o;
if (!Arrays.equals(ids, component.ids)) return false;
if (!Arrays.equals(ids, that.ids)) return false;
if (value != that.value) return false;
return true;
}
@Override
public int hashCode() {
return Arrays.hashCode(ids);
return value.ordinal() + Arrays.hashCode(ids);
}
IntIdComponent(int[] ids) {
this.ids = ids;
}
public void remove(int id) {
IdUtils.remove(ids, id);
}
public boolean isEmptyAndTouched() {
return IdUtils.isEmptyAndTouched(ids);
public boolean remove(int id) {
return IdUtils.remove(ids, id);
}
public boolean isEmpty() {
return IdUtils.isEmpty(ids);
}
public void removeAndTouch(int id) {
IdUtils.removeAndTouch(ids, id);
}
}
class IdUtils {
// absent value
static final int nullId = -1;
static final int touchedId = -2;
// removed value
static final int nullId = 0;
static boolean contains(int[] ids, int id) {
for (int i : ids) {
if (i == id) {
return true;
}
for (int id1 : ids) {
if (id1 == id) return true;
}
return false;
}
static boolean isEmpty(int[] ids) {
for (int i : ids) {
if (i != nullId && i != touchedId) {
return false;
}
for (int id : ids) {
if (id != nullId) return false;
}
return true;
}
static boolean isEmptyAndTouched(int[] ids) {
boolean touched = false;
for (int i : ids) {
if (i != nullId && i != touchedId) {
return false;
}
touched = touched || i == touchedId;
}
return touched;
}
static IntIdComponent[] toArray(Collection<IntIdComponent> set) {
IntIdComponent[] result = new IntIdComponent[set.size()];
int i = 0;
@@ -130,23 +109,19 @@ class IdUtils {
result[i] = intIdComponent;
i++;
}
return result;
}
static void remove(int[] ids, int id) {
static boolean remove(int[] ids, int id) {
boolean removed = false;
for (int i = 0; i < ids.length; i++) {
if (ids[i] == id) {
ids[i] = nullId;
removed = true;
}
}
}
static void removeAndTouch(int[] ids, int id) {
for (int i = 0; i < ids.length; i++) {
if (ids[i] == id) {
ids[i] = touchedId;
}
}
return removed;
}
}
@@ -169,19 +144,55 @@ class ResultUtil<Id, T extends Enum<T>> {
return new Final<Id, T>(lattice.join(((Final<?, T>) r1).value, ((Final<?, T>) r2).value));
}
if (r1 instanceof Final && r2 instanceof Pending) {
Final<?, T> f1 = (Final<?, T>)r1;
Pending<Id, T> pending = (Pending<Id, T>) r2;
return new Pending<Id, T>(lattice.join(((Final<Id, T>) r1).value, pending.infinum), true, pending.delta);
Set<Product<Id, T>> sum1 = new HashSet<Product<Id, T>>(pending.sum);
sum1.add(new Product<Id, T>(f1.value, Collections.<Id>emptySet()));
return new Pending<Id, T>(sum1);
}
if (r1 instanceof Pending && r2 instanceof Final) {
Final<?, T> f2 = (Final<?, T>)r2;
Pending<Id, T> pending = (Pending<Id, T>) r1;
return new Pending<Id, T>(lattice.join(((Final<Id, T>) r2).value, pending.infinum), true, pending.delta);
Set<Product<Id, T>> sum1 = new HashSet<Product<Id, T>>(pending.sum);
sum1.add(new Product<Id, T>(f2.value, Collections.<Id>emptySet()));
return new Pending<Id, T>(sum1);
}
Pending<Id, T> pending1 = (Pending<Id, T>) r1;
Pending<Id, T> pending2 = (Pending<Id, T>) r2;
Set<Set<Id>> delta = new HashSet<Set<Id>>();
delta.addAll(pending1.delta);
delta.addAll(pending2.delta);
return new Pending<Id, T>(lattice.join(pending1.infinum, pending2.infinum), pending1.rigid || pending2.rigid, delta);
Set<Product<Id, T>> sum = new HashSet<Product<Id, T>>();
sum.addAll(pending1.sum);
sum.addAll(pending2.sum);
return new Pending<Id, T>(sum);
}
}
final class Product<K, V> {
@NotNull final V value;
@NotNull final Set<K> ids;
Product(@NotNull V value, @NotNull Set<K> ids) {
this.value = value;
this.ids = ids;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Product product = (Product)o;
if (!ids.equals(product.ids)) return false;
if (!value.equals(product.value)) return false;
return true;
}
@Override
public int hashCode() {
int result = value.hashCode();
result = 31 * result + ids.hashCode();
return result;
}
}
@@ -199,20 +210,12 @@ final class Final<Id, T> implements Result<Id, T> {
}
final class Pending<Id, T> implements Result<Id, T> {
final T infinum;
final boolean rigid;
final Set<Set<Id>> delta;
final Set<Product<Id, T>> sum;
Pending(T infinum, boolean rigid, Set<Set<Id>> delta) {
this.infinum = infinum;
this.rigid = rigid;
this.delta = delta;
Pending(Set<Product<Id, T>> sum) {
this.sum = sum;
}
@Override
public String toString() {
return "Pending{" + "infinum=" + infinum + ", rigid=" + rigid + ", delta=" + delta + '}';
}
}
interface IntIdResult {}
@@ -223,14 +226,11 @@ final class IntIdFinal implements IntIdResult {
this.value = value;
}
}
final class IntIdPending implements IntIdResult {
final Value infinum;
final boolean rigid;
final IntIdComponent[] delta;
IntIdPending(Value infinum, boolean rigid, IntIdComponent[] delta) {
this.infinum = infinum;
this.rigid = rigid;
IntIdPending(IntIdComponent[] delta) {
this.delta = delta;
}
@@ -238,22 +238,13 @@ final class IntIdPending implements IntIdResult {
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof IntIdPending)) return false;
IntIdPending pending = (IntIdPending)o;
if (!Arrays.equals(delta, pending.delta)) return false;
if (rigid != pending.rigid) return false;
if (infinum != pending.infinum) return false;
return true;
return !Arrays.equals(delta, pending.delta);
}
@Override
public int hashCode() {
int result = infinum.ordinal();
result = 31 * result + (rigid ? 1 : 0);
result = 31 * result + Arrays.hashCode(delta);
return result;
return Arrays.hashCode(delta);
}
}
@@ -331,14 +322,9 @@ final class IntIdSolver {
moving.push(equation.id);
} else if (rhs instanceof IntIdPending) {
IntIdPending pendResult = (IntIdPending)rhs;
if (pendResult.infinum == lattice.top) {
solved.put(equation.id, lattice.top);
moving.push(equation.id);
} else {
for (IntIdComponent component : pendResult.delta) {
for (int trigger : component.ids) {
dependencies.addOccurence(trigger, equation.id);
}
for (IntIdComponent component : pendResult.delta) {
for (int trigger : component.ids) {
dependencies.addOccurence(trigger, equation.id);
}
pending.put(equation.id, pendResult);
}
@@ -381,67 +367,24 @@ final class IntIdSolver {
// substitute id -> value into pending
IntIdResult substitute(IntIdPending pending, int id, Value value) {
if (value == lattice.bot) {
// remove components (products) with bottom
ArrayList<IntIdComponent> delta = new ArrayList<IntIdComponent>();
for (IntIdComponent component : pending.delta) {
if (!contains(component.ids, id)) {
delta.add(component);
}
}
if (delta.isEmpty()) {
return pending.rigid ? new IntIdFinal(pending.infinum) : new IntIdFinal(lattice.bot);
}
else {
return new IntIdPending(pending.infinum, pending.rigid, toArray(delta));
for (IntIdComponent intIdComponent : pending.delta) {
if (intIdComponent.remove(id)) {
intIdComponent.value = lattice.meet(intIdComponent.value, value);
}
}
else if (value.equals(lattice.top)) {
ArrayList<IntIdComponent> delta = new ArrayList<IntIdComponent>();
// remove top from components
boolean removed = false;
for (IntIdComponent component : pending.delta) {
component.remove(id);
if (!component.isEmptyAndTouched()) {
if (component.isEmpty()) {
return new IntIdFinal(lattice.top);
} else {
delta.add(component);
}
}
else {
removed = true;
}
}
if (delta.isEmpty()) {
return new IntIdFinal(pending.infinum);
}
else {
return new IntIdPending(pending.infinum, pending.rigid || removed, toArray(delta));
}
}
else {
Value infinum = lattice.join(pending.infinum, value);
if (infinum == lattice.top) {
// normalized?
Value acc = lattice.bot;
for (IntIdComponent intIdComponent : pending.delta) {
if (intIdComponent.value == lattice.top && intIdComponent.isEmpty()) {
return new IntIdFinal(lattice.top);
}
ArrayList<IntIdComponent> delta = new ArrayList<IntIdComponent>();
boolean removed = false;
for (IntIdComponent component : pending.delta) {
component.removeAndTouch(id);
if (!component.isEmpty()) {
delta.add(component);
}
else {
removed = true;
}
}
if (delta.isEmpty()) {
return new IntIdFinal(infinum);
}
else {
return new IntIdPending(infinum, pending.rigid || removed, toArray(delta));
else if (intIdComponent.value != lattice.bot) {
if (intIdComponent.isEmpty())
acc = lattice.join(acc, intIdComponent.value);
else
return pending;
}
}
return new IntIdFinal(acc);
}
}