921 lines
31 KiB
Java
921 lines
31 KiB
Java
/*******************************************************************************
|
|
* Copyright (c) 2002 - 2006 IBM Corporation.
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
*
|
|
* Contributors:
|
|
* IBM Corporation - initial API and implementation
|
|
*******************************************************************************/
|
|
package com.ibm.wala.ipa.callgraph.propagation;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import com.ibm.wala.classLoader.ArrayClass;
|
|
import com.ibm.wala.classLoader.IClass;
|
|
import com.ibm.wala.fixedpoint.impl.DefaultFixedPointSolver;
|
|
import com.ibm.wala.fixedpoint.impl.Worklist;
|
|
import com.ibm.wala.fixpoint.AbstractOperator;
|
|
import com.ibm.wala.fixpoint.AbstractStatement;
|
|
import com.ibm.wala.fixpoint.IFixedPointSystem;
|
|
import com.ibm.wala.fixpoint.IVariable;
|
|
import com.ibm.wala.fixpoint.UnaryOperator;
|
|
import com.ibm.wala.fixpoint.UnaryStatement;
|
|
import com.ibm.wala.ipa.callgraph.CallGraph;
|
|
import com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder.FilterOperator;
|
|
import com.ibm.wala.ipa.cha.ClassHierarchyException;
|
|
import com.ibm.wala.ipa.cha.ClassHierarchyWarning;
|
|
import com.ibm.wala.types.TypeReference;
|
|
import com.ibm.wala.util.collections.HashMapFactory;
|
|
import com.ibm.wala.util.collections.HashSetFactory;
|
|
import com.ibm.wala.util.collections.Iterator2Collection;
|
|
import com.ibm.wala.util.collections.MapUtil;
|
|
import com.ibm.wala.util.debug.Assertions;
|
|
import com.ibm.wala.util.debug.VerboseAction;
|
|
import com.ibm.wala.util.graph.Graph;
|
|
import com.ibm.wala.util.graph.NumberedGraph;
|
|
import com.ibm.wala.util.heapTrace.HeapTracer;
|
|
import com.ibm.wala.util.intset.IntIterator;
|
|
import com.ibm.wala.util.intset.IntSet;
|
|
import com.ibm.wala.util.intset.IntSetAction;
|
|
import com.ibm.wala.util.intset.IntSetUtil;
|
|
import com.ibm.wala.util.intset.MutableIntSet;
|
|
import com.ibm.wala.util.intset.MutableMapping;
|
|
import com.ibm.wala.util.ref.ReferenceCleanser;
|
|
import com.ibm.wala.util.warnings.Warnings;
|
|
|
|
/**
|
|
* System of constraints that define propagation for call graph construction
|
|
*/
|
|
public class PropagationSystem extends DefaultFixedPointSolver<PointsToSetVariable> {
|
|
|
|
private final static boolean DEBUG = false;
|
|
|
|
private final static boolean DEBUG_MEMORY = false;
|
|
|
|
private static int DEBUG_MEM_COUNTER = 0;
|
|
|
|
private final static int DEBUG_MEM_INTERVAL = 5;
|
|
|
|
/**
|
|
* object that tracks points-to sets
|
|
*/
|
|
protected final PointsToMap pointsToMap = new PointsToMap();
|
|
|
|
/**
|
|
* Implementation of the underlying dataflow graph
|
|
*/
|
|
private final PropagationGraph flowGraph = new PropagationGraph();
|
|
|
|
/**
|
|
* bijection from InstanceKey <=> Integer
|
|
*/
|
|
protected final MutableMapping<InstanceKey> instanceKeys = MutableMapping.make();
|
|
|
|
/**
|
|
* A mapping from IClass -> MutableSharedBitVectorIntSet The range represents the instance keys that correspond to a given class.
|
|
* This mapping is used to filter sets based on declared types; e.g., in cast constraints
|
|
*/
|
|
final private Map<IClass, MutableIntSet> class2InstanceKey = HashMapFactory.make();
|
|
|
|
/**
|
|
* An abstraction of the pointer analysis result
|
|
*/
|
|
private PointerAnalysis<InstanceKey> pointerAnalysis;
|
|
|
|
/**
|
|
* Meta-data regarding how pointers are modelled.
|
|
*/
|
|
private final PointerKeyFactory pointerKeyFactory;
|
|
|
|
/**
|
|
* Meta-data regarding how instances are modelled.
|
|
*/
|
|
private final InstanceKeyFactory instanceKeyFactory;
|
|
|
|
/**
|
|
* When doing unification, we must also updated the fixed sets in unary side effects.
|
|
*
|
|
* This maintains a map from PointsToSetVariable -> Set<UnarySideEffect>
|
|
*/
|
|
final private Map<PointsToSetVariable, Set<UnarySideEffect>> fixedSetMap = HashMapFactory.make();
|
|
|
|
/**
|
|
* Governing call graph;
|
|
*/
|
|
protected final CallGraph cg;
|
|
|
|
private int verboseInterval = DEFAULT_VERBOSE_INTERVAL;
|
|
|
|
private int periodicMaintainInterval = DEFAULT_PERIODIC_MAINTENANCE_INTERVAL;
|
|
|
|
public PropagationSystem(CallGraph cg, PointerKeyFactory pointerKeyFactory, InstanceKeyFactory instanceKeyFactory) {
|
|
if (cg == null) {
|
|
throw new IllegalArgumentException("null cg");
|
|
}
|
|
this.cg = cg;
|
|
this.pointerKeyFactory = pointerKeyFactory;
|
|
this.instanceKeyFactory = instanceKeyFactory;
|
|
// when doing paranoid checking of points-to sets, code in PointsToSetVariable needs to know about the instance key
|
|
// mapping
|
|
if (PointsToSetVariable.PARANOID) {
|
|
PointsToSetVariable.instanceKeys = instanceKeys;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return an object which encapsulates the pointer analysis result
|
|
*/
|
|
public PointerAnalysis<InstanceKey> makePointerAnalysis(PropagationCallGraphBuilder builder) {
|
|
return new PointerAnalysisImpl(builder, cg, pointsToMap, instanceKeys, pointerKeyFactory, instanceKeyFactory);
|
|
}
|
|
|
|
protected void registerFixedSet(PointsToSetVariable p, UnarySideEffect s) {
|
|
Set<UnarySideEffect> set = MapUtil.findOrCreateSet(fixedSetMap, p);
|
|
set.add(s);
|
|
}
|
|
|
|
protected void updateSideEffects(PointsToSetVariable p, PointsToSetVariable rep) {
|
|
Set<UnarySideEffect> set = fixedSetMap.get(p);
|
|
if (set != null) {
|
|
for (Iterator it = set.iterator(); it.hasNext();) {
|
|
UnarySideEffect s = (UnarySideEffect) it.next();
|
|
s.replaceFixedSet(rep);
|
|
}
|
|
Set<UnarySideEffect> s2 = MapUtil.findOrCreateSet(fixedSetMap, rep);
|
|
s2.addAll(set);
|
|
fixedSetMap.remove(p);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Keep this method private .. this returns the actual backing set for the class, which we do not want to expose to clients.
|
|
*/
|
|
private MutableIntSet findOrCreateSparseSetForClass(IClass klass) {
|
|
assert klass.getReference() != TypeReference.JavaLangObject;
|
|
MutableIntSet result = class2InstanceKey.get(klass);
|
|
if (result == null) {
|
|
result = IntSetUtil.getDefaultIntSetFactory().make();
|
|
class2InstanceKey.put(klass, result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @return a set of integers representing the instance keys that correspond to a given class. This method creates a new set, which
|
|
* the caller may bash at will.
|
|
*/
|
|
MutableIntSet cloneInstanceKeysForClass(IClass klass) {
|
|
assert klass.getReference() != TypeReference.JavaLangObject;
|
|
MutableIntSet set = class2InstanceKey.get(klass);
|
|
if (set == null) {
|
|
return IntSetUtil.getDefaultIntSetFactory().make();
|
|
} else {
|
|
// return a copy.
|
|
return IntSetUtil.getDefaultIntSetFactory().makeCopy(set);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return a set of integers representing the instance keys that correspond to a given class, or null if there are none.
|
|
* @throws IllegalArgumentException if klass is null
|
|
*/
|
|
public IntSet getInstanceKeysForClass(IClass klass) {
|
|
if (klass == null) {
|
|
throw new IllegalArgumentException("klass is null");
|
|
}
|
|
assert klass != klass.getClassHierarchy().getRootClass();
|
|
return class2InstanceKey.get(klass);
|
|
}
|
|
|
|
/**
|
|
* @return the instance key numbered with index i
|
|
*/
|
|
public InstanceKey getInstanceKey(int i) {
|
|
return instanceKeys.getMappedObject(i);
|
|
}
|
|
|
|
public int getInstanceIndex(InstanceKey ik) {
|
|
return instanceKeys.getMappedIndex(ik);
|
|
}
|
|
|
|
/**
|
|
* TODO: optimize; this may be inefficient;
|
|
*
|
|
* @return an List of instance keys corresponding to the integers in a set
|
|
*/
|
|
List<InstanceKey> getInstances(IntSet set) {
|
|
LinkedList<InstanceKey> result = new LinkedList<InstanceKey>();
|
|
for (IntIterator it = set.intIterator(); it.hasNext();) {
|
|
int j = it.next();
|
|
result.add(getInstanceKey(j));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
protected void initializeVariables() {
|
|
// don't have to do anything; all variables initialized
|
|
// by default to TOP (the empty set);
|
|
}
|
|
|
|
/**
|
|
* record that a particular points-to-set is represented implicitly.
|
|
*/
|
|
public void recordImplicitPointsToSet(PointerKey key) {
|
|
if (key == null) {
|
|
throw new IllegalArgumentException("null key");
|
|
}
|
|
if (key instanceof LocalPointerKey) {
|
|
LocalPointerKey lpk = (LocalPointerKey) key;
|
|
if (lpk.isParameter()) {
|
|
System.err.println("------------------ ERROR:");
|
|
System.err.println("LocalPointerKey: " + lpk);
|
|
System.err.println("Constant? " + lpk.getNode().getIR().getSymbolTable().isConstant(lpk.getValueNumber()));
|
|
System.err.println(" -- IR:");
|
|
System.err.println(lpk.getNode().getIR());
|
|
Assertions.UNREACHABLE("How can parameter be implicit?");
|
|
}
|
|
}
|
|
pointsToMap.recordImplicit(key);
|
|
}
|
|
|
|
/**
|
|
* If key is unified, returns the representative
|
|
*
|
|
* @param key
|
|
* @return the dataflow variable that tracks the points-to set for key
|
|
*/
|
|
public PointsToSetVariable findOrCreatePointsToSet(PointerKey key) {
|
|
|
|
if (key == null) {
|
|
throw new IllegalArgumentException("null key");
|
|
}
|
|
|
|
if (pointsToMap.isImplicit(key)) {
|
|
System.err.println("Did not expect to findOrCreatePointsToSet for implicitly represented PointerKey");
|
|
System.err.println(key);
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
PointsToSetVariable result = pointsToMap.getPointsToSet(key);
|
|
if (result == null) {
|
|
result = new PointsToSetVariable(key);
|
|
pointsToMap.put(key, result);
|
|
} else {
|
|
// check that the filter for this variable remains unique
|
|
if (!pointsToMap.isUnified(key) && key instanceof FilteredPointerKey) {
|
|
PointerKey pk = result.getPointerKey();
|
|
if (!(pk instanceof FilteredPointerKey)) {
|
|
// add a filter for all future evaluations.
|
|
// this is tricky, but the logic is OK .. any constraints that need
|
|
// the filter will see it ...
|
|
// CALLERS MUST BE EXTRA CAREFUL WHEN DEALING WITH UNIFICATION!
|
|
result.setPointerKey(key);
|
|
pk = key;
|
|
}
|
|
FilteredPointerKey fpk = (FilteredPointerKey) pk;
|
|
if (fpk == null) {
|
|
Assertions.UNREACHABLE("fpk is null");
|
|
}
|
|
if (key == null) {
|
|
Assertions.UNREACHABLE("key is null");
|
|
}
|
|
if (fpk.getTypeFilter() == null) {
|
|
Assertions.UNREACHABLE("fpk.getTypeFilter() is null");
|
|
}
|
|
if (!fpk.getTypeFilter().equals(((FilteredPointerKey) key).getTypeFilter())) {
|
|
Assertions.UNREACHABLE("Cannot use filter " + ((FilteredPointerKey) key).getTypeFilter() + " for " + key
|
|
+ ": previously created different filter " + fpk.getTypeFilter());
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public int findOrCreateIndexForInstanceKey(InstanceKey key) {
|
|
int result = instanceKeys.getMappedIndex(key);
|
|
if (result == -1) {
|
|
result = instanceKeys.add(key);
|
|
}
|
|
if (DEBUG) {
|
|
System.err.println("getIndexForInstanceKey " + key + " " + result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* NB: this is idempotent ... if the given constraint exists, it will not be added to the system; however, this will be more
|
|
* expensive since it must check if the constraint pre-exits.
|
|
*
|
|
* @return true iff the system changes
|
|
*/
|
|
public boolean newConstraint(PointerKey lhs, UnaryOperator<PointsToSetVariable> op, PointerKey rhs) {
|
|
if (lhs == null) {
|
|
throw new IllegalArgumentException("null lhs");
|
|
}
|
|
if (op == null) {
|
|
throw new IllegalArgumentException("op null");
|
|
}
|
|
if (rhs == null) {
|
|
throw new IllegalArgumentException("rhs null");
|
|
}
|
|
if (DEBUG) {
|
|
System.err.println("Add constraint A: " + lhs + " " + op + " " + rhs);
|
|
}
|
|
PointsToSetVariable L = findOrCreatePointsToSet(lhs);
|
|
PointsToSetVariable R = findOrCreatePointsToSet(rhs);
|
|
if (op instanceof FilterOperator) {
|
|
// we do not want to revert the lhs to pre-transitive form;
|
|
// we instead want to check in the outer loop of the pre-transitive
|
|
// solver if the value of L changes.
|
|
pointsToMap.recordTransitiveRoot(L.getPointerKey());
|
|
if (!(L.getPointerKey() instanceof FilteredPointerKey)) {
|
|
Assertions.UNREACHABLE("expected filtered lhs " + L.getPointerKey() + " " + L.getPointerKey().getClass() + " " + lhs + " "
|
|
+ lhs.getClass());
|
|
}
|
|
}
|
|
return newStatement(L, op, R, true, true);
|
|
}
|
|
|
|
public boolean newConstraint(PointerKey lhs, AbstractOperator<PointsToSetVariable> op, PointerKey rhs) {
|
|
if (lhs == null) {
|
|
throw new IllegalArgumentException("lhs null");
|
|
}
|
|
if (op == null) {
|
|
throw new IllegalArgumentException("op null");
|
|
}
|
|
if (rhs == null) {
|
|
throw new IllegalArgumentException("rhs null");
|
|
}
|
|
if (DEBUG) {
|
|
System.err.println("Add constraint A: " + lhs + " " + op + " " + rhs);
|
|
}
|
|
assert !pointsToMap.isUnified(lhs);
|
|
assert !pointsToMap.isUnified(rhs);
|
|
PointsToSetVariable L = findOrCreatePointsToSet(lhs);
|
|
PointsToSetVariable R = findOrCreatePointsToSet(rhs);
|
|
return newStatement(L, op, new PointsToSetVariable[] { R }, true, true);
|
|
}
|
|
|
|
public boolean newConstraint(PointerKey lhs, AbstractOperator<PointsToSetVariable> op, PointerKey rhs1, PointerKey rhs2) {
|
|
if (lhs == null) {
|
|
throw new IllegalArgumentException("null lhs");
|
|
}
|
|
if (op == null) {
|
|
throw new IllegalArgumentException("null op");
|
|
}
|
|
if (rhs1 == null) {
|
|
throw new IllegalArgumentException("null rhs1");
|
|
}
|
|
if (rhs2 == null) {
|
|
throw new IllegalArgumentException("null rhs2");
|
|
}
|
|
if (DEBUG) {
|
|
System.err.println("Add constraint A: " + lhs + " " + op + " " + rhs1 + ", " + rhs2);
|
|
}
|
|
assert !pointsToMap.isUnified(lhs);
|
|
assert !pointsToMap.isUnified(rhs1);
|
|
assert !pointsToMap.isUnified(rhs2);
|
|
PointsToSetVariable L = findOrCreatePointsToSet(lhs);
|
|
PointsToSetVariable R1 = findOrCreatePointsToSet(rhs1);
|
|
PointsToSetVariable R2 = findOrCreatePointsToSet(rhs2);
|
|
return newStatement(L, op, R1, R2, true, true);
|
|
}
|
|
|
|
/**
|
|
* @return true iff the system changes
|
|
*/
|
|
public boolean newFieldWrite(PointerKey lhs, UnaryOperator<PointsToSetVariable> op, PointerKey rhs, PointerKey container) {
|
|
return newConstraint(lhs, op, rhs);
|
|
}
|
|
|
|
/**
|
|
* @return true iff the system changes
|
|
*/
|
|
public boolean newFieldRead(PointerKey lhs, UnaryOperator<PointsToSetVariable> op, PointerKey rhs, PointerKey container) {
|
|
return newConstraint(lhs, op, rhs);
|
|
}
|
|
|
|
/**
|
|
* @return true iff the system changes
|
|
*/
|
|
public boolean newConstraint(PointerKey lhs, InstanceKey value) {
|
|
if (DEBUG) {
|
|
System.err.println("Add constraint B: " + lhs + " U= " + value);
|
|
}
|
|
pointsToMap.recordTransitiveRoot(lhs);
|
|
|
|
// we don't actually add a constraint.
|
|
// instead, we immediately add the value to the points-to set.
|
|
// This works since the solver is monotonic with TOP = {}
|
|
PointsToSetVariable L = findOrCreatePointsToSet(lhs);
|
|
int index = findOrCreateIndexForInstanceKey(value);
|
|
if (L.contains(index)) {
|
|
// a no-op
|
|
return false;
|
|
} else {
|
|
L.add(index);
|
|
|
|
// also register that we have an instanceKey for the klass
|
|
assert value.getConcreteType() != null;
|
|
|
|
if (!value.getConcreteType().getReference().equals(TypeReference.JavaLangObject)) {
|
|
registerInstanceOfClass(value.getConcreteType(), index);
|
|
}
|
|
|
|
// we'd better update the worklist appropriately
|
|
// if graphNodeId == -1, then there are no equations that use this
|
|
// variable.
|
|
if (L.getGraphNodeId() > -1) {
|
|
changedVariable(L);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Record that we have a new instanceKey for a given declared type.
|
|
*/
|
|
private void registerInstanceOfClass(IClass klass, int index) {
|
|
|
|
if (DEBUG) {
|
|
System.err.println("registerInstanceOfClass " + klass + " " + index);
|
|
}
|
|
|
|
assert !klass.getReference().equals(TypeReference.JavaLangObject);
|
|
|
|
try {
|
|
IClass T = klass;
|
|
registerInstanceWithAllSuperclasses(index, T);
|
|
registerInstanceWithAllInterfaces(klass, index);
|
|
|
|
if (klass.isArrayClass()) {
|
|
ArrayClass aClass = (ArrayClass) klass;
|
|
int dim = aClass.getDimensionality();
|
|
registerMultiDimArraysForArrayOfObjectTypes(dim, index, aClass);
|
|
|
|
IClass elementClass = aClass.getInnermostElementClass();
|
|
if (elementClass != null) {
|
|
registerArrayInstanceWithAllSuperclassesOfElement(index, elementClass, dim);
|
|
registerArrayInstanceWithAllInterfacesOfElement(index, elementClass, dim);
|
|
}
|
|
}
|
|
} catch (ClassHierarchyException e) {
|
|
Warnings.add(ClassHierarchyWarning.create(e.getMessage()));
|
|
}
|
|
}
|
|
|
|
private int registerMultiDimArraysForArrayOfObjectTypes(int dim, int index, ArrayClass aClass) {
|
|
|
|
for (int i = 1; i < dim; i++) {
|
|
TypeReference jlo = makeArray(TypeReference.JavaLangObject, i);
|
|
IClass jloClass = null;
|
|
jloClass = aClass.getClassLoader().lookupClass(jlo.getName());
|
|
MutableIntSet set = findOrCreateSparseSetForClass(jloClass);
|
|
set.add(index);
|
|
}
|
|
return dim;
|
|
}
|
|
|
|
private void registerArrayInstanceWithAllInterfacesOfElement(int index, IClass elementClass, int dim) {
|
|
Collection ifaces = null;
|
|
ifaces = elementClass.getAllImplementedInterfaces();
|
|
for (Iterator it = ifaces.iterator(); it.hasNext();) {
|
|
IClass I = (IClass) it.next();
|
|
TypeReference iArrayRef = makeArray(I.getReference(), dim);
|
|
IClass iArrayClass = null;
|
|
iArrayClass = I.getClassLoader().lookupClass(iArrayRef.getName());
|
|
MutableIntSet set = findOrCreateSparseSetForClass(iArrayClass);
|
|
set.add(index);
|
|
if (DEBUG) {
|
|
System.err.println("dense filter for interface " + iArrayClass + " " + set);
|
|
}
|
|
}
|
|
}
|
|
|
|
private TypeReference makeArray(TypeReference element, int dim) {
|
|
TypeReference iArrayRef = element;
|
|
for (int i = 0; i < dim; i++) {
|
|
iArrayRef = TypeReference.findOrCreateArrayOf(iArrayRef);
|
|
}
|
|
return iArrayRef;
|
|
}
|
|
|
|
private void registerArrayInstanceWithAllSuperclassesOfElement(int index, IClass elementClass, int dim)
|
|
throws ClassHierarchyException {
|
|
IClass T;
|
|
// register the array with each supertype of the element class
|
|
T = elementClass.getSuperclass();
|
|
while (T != null) {
|
|
TypeReference tArrayRef = makeArray(T.getReference(), dim);
|
|
IClass tArrayClass = null;
|
|
tArrayClass = T.getClassLoader().lookupClass(tArrayRef.getName());
|
|
MutableIntSet set = findOrCreateSparseSetForClass(tArrayClass);
|
|
set.add(index);
|
|
if (DEBUG) {
|
|
System.err.println("dense filter for class " + tArrayClass + " " + set);
|
|
}
|
|
T = T.getSuperclass();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param klass
|
|
* @param index
|
|
* @throws ClassHierarchyException
|
|
*/
|
|
private void registerInstanceWithAllInterfaces(IClass klass, int index) throws ClassHierarchyException {
|
|
Collection ifaces = klass.getAllImplementedInterfaces();
|
|
for (Iterator it = ifaces.iterator(); it.hasNext();) {
|
|
IClass I = (IClass) it.next();
|
|
MutableIntSet set = findOrCreateSparseSetForClass(I);
|
|
set.add(index);
|
|
if (DEBUG) {
|
|
System.err.println("dense filter for interface " + I + " " + set);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param index
|
|
* @param T
|
|
* @throws ClassHierarchyException
|
|
*/
|
|
private void registerInstanceWithAllSuperclasses(int index, IClass T) throws ClassHierarchyException {
|
|
while (T != null && !T.getReference().equals(TypeReference.JavaLangObject)) {
|
|
MutableIntSet set = findOrCreateSparseSetForClass(T);
|
|
set.add(index);
|
|
if (DEBUG) {
|
|
System.err.println("dense filter for class " + T + " " + set);
|
|
}
|
|
T = T.getSuperclass();
|
|
}
|
|
}
|
|
|
|
public void newSideEffect(UnaryOperator<PointsToSetVariable> op, PointerKey arg0) {
|
|
if (arg0 == null) {
|
|
throw new IllegalArgumentException("null arg0");
|
|
}
|
|
if (DEBUG) {
|
|
System.err.println("add constraint D: " + op + " " + arg0);
|
|
}
|
|
assert !pointsToMap.isUnified(arg0);
|
|
PointsToSetVariable v1 = findOrCreatePointsToSet(arg0);
|
|
newStatement(null, op, v1, true, true);
|
|
}
|
|
|
|
public void newSideEffect(AbstractOperator<PointsToSetVariable> op, PointerKey[] arg0) {
|
|
if (arg0 == null) {
|
|
throw new IllegalArgumentException("null arg0");
|
|
}
|
|
if (DEBUG) {
|
|
System.err.println("add constraint D: " + op + " " + Arrays.toString(arg0));
|
|
}
|
|
PointsToSetVariable[] vs = new PointsToSetVariable[ arg0.length ];
|
|
for(int i = 0; i < arg0.length; i++) {
|
|
assert !pointsToMap.isUnified(arg0[i]);
|
|
vs[i] = findOrCreatePointsToSet(arg0[i]);
|
|
}
|
|
newStatement(null, op, vs, true, true);
|
|
}
|
|
|
|
public void newSideEffect(AbstractOperator<PointsToSetVariable> op, PointerKey arg0, PointerKey arg1) {
|
|
if (DEBUG) {
|
|
System.err.println("add constraint D: " + op + " " + arg0);
|
|
}
|
|
assert !pointsToMap.isUnified(arg0);
|
|
assert !pointsToMap.isUnified(arg1);
|
|
PointsToSetVariable v1 = findOrCreatePointsToSet(arg0);
|
|
PointsToSetVariable v2 = findOrCreatePointsToSet(arg1);
|
|
newStatement(null, op, v1, v2, true, true);
|
|
}
|
|
|
|
@Override
|
|
protected void initializeWorkList() {
|
|
addAllStatementsToWorkList();
|
|
}
|
|
|
|
/**
|
|
* @return an object that encapsulates the pointer analysis results
|
|
*/
|
|
public PointerAnalysis<InstanceKey> extractPointerAnalysis(PropagationCallGraphBuilder builder) {
|
|
if (pointerAnalysis == null) {
|
|
pointerAnalysis = makePointerAnalysis(builder);
|
|
}
|
|
return pointerAnalysis;
|
|
}
|
|
|
|
@Override
|
|
public void performVerboseAction() {
|
|
super.performVerboseAction();
|
|
if (DEBUG_MEMORY) {
|
|
DEBUG_MEM_COUNTER++;
|
|
if (DEBUG_MEM_COUNTER % DEBUG_MEM_INTERVAL == 0) {
|
|
DEBUG_MEM_COUNTER = 0;
|
|
ReferenceCleanser.clearSoftCaches();
|
|
|
|
System.err.println(flowGraph.spaceReport());
|
|
|
|
System.err.println("Analyze leaks..");
|
|
HeapTracer.traceHeap(Collections.singleton(this), true);
|
|
System.err.println("done analyzing leaks");
|
|
}
|
|
}
|
|
if (getFixedPointSystem() instanceof VerboseAction) {
|
|
((VerboseAction) getFixedPointSystem()).performVerboseAction();
|
|
}
|
|
if (!workList.isEmpty()) {
|
|
AbstractStatement s = workList.takeStatement();
|
|
System.err.println(printRHSInstances(s));
|
|
workList.insertStatement(s);
|
|
System.err.println("CGNodes: " + cg.getNumberOfNodes());
|
|
}
|
|
|
|
}
|
|
|
|
private String printRHSInstances(AbstractStatement s) {
|
|
if (s instanceof UnaryStatement) {
|
|
UnaryStatement u = (UnaryStatement) s;
|
|
PointsToSetVariable rhs = (PointsToSetVariable) u.getRightHandSide();
|
|
IntSet value = rhs.getValue();
|
|
final int[] topFive = new int[5];
|
|
value.foreach(new IntSetAction() {
|
|
@Override
|
|
public void act(int x) {
|
|
for (int i = 0; i < 4; i++) {
|
|
topFive[i] = topFive[i + 1];
|
|
}
|
|
topFive[4] = x;
|
|
}
|
|
});
|
|
StringBuffer result = new StringBuffer();
|
|
for (int i = 0; i < 5; i++) {
|
|
int p = topFive[i];
|
|
if (p != 0) {
|
|
InstanceKey ik = getInstanceKey(p);
|
|
result.append(p).append(" ").append(ik).append("\n");
|
|
}
|
|
}
|
|
return result.toString();
|
|
} else {
|
|
return s.getClass().toString();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public IFixedPointSystem<PointsToSetVariable> getFixedPointSystem() {
|
|
return flowGraph;
|
|
}
|
|
|
|
/*
|
|
* @see com.ibm.wala.ipa.callgraph.propagation.HeapModel#iteratePointerKeys()
|
|
*/
|
|
public Iterator<PointerKey> iteratePointerKeys() {
|
|
return pointsToMap.iterateKeys();
|
|
}
|
|
|
|
/**
|
|
* warning: this is _real_ slow; don't use it anywhere performance critical
|
|
*/
|
|
public int getNumberOfPointerKeys() {
|
|
return pointsToMap.getNumberOfPointerKeys();
|
|
}
|
|
|
|
/**
|
|
* Use with care.
|
|
*/
|
|
Worklist getWorklist() {
|
|
return workList;
|
|
}
|
|
|
|
public Iterator<AbstractStatement> getStatementsThatUse(PointsToSetVariable v) {
|
|
return flowGraph.getStatementsThatUse(v);
|
|
}
|
|
|
|
public Iterator<AbstractStatement> getStatementsThatDef(PointsToSetVariable v) {
|
|
return flowGraph.getStatementsThatDef(v);
|
|
}
|
|
|
|
public NumberedGraph<PointsToSetVariable> getAssignmentGraph() {
|
|
return flowGraph.getAssignmentGraph();
|
|
}
|
|
|
|
public Graph<PointsToSetVariable> getFilterAsssignmentGraph() {
|
|
return flowGraph.getFilterAssignmentGraph();
|
|
}
|
|
|
|
/**
|
|
* NOTE: do not use this method unless you really know what you are doing. Functionality is fragile and may not work in the
|
|
* future.
|
|
*/
|
|
public Graph<PointsToSetVariable> getFlowGraphIncludingImplicitConstraints() {
|
|
return flowGraph.getFlowGraphIncludingImplicitConstraints();
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public void revertToPreTransitive() {
|
|
pointsToMap.revertToPreTransitive();
|
|
}
|
|
|
|
public Iterator getTransitiveRoots() {
|
|
return pointsToMap.getTransitiveRoots();
|
|
}
|
|
|
|
public boolean isTransitiveRoot(PointerKey key) {
|
|
return pointsToMap.isTransitiveRoot(key);
|
|
}
|
|
|
|
@Override
|
|
protected void periodicMaintenance() {
|
|
super.periodicMaintenance();
|
|
ReferenceCleanser.clearSoftCaches();
|
|
}
|
|
|
|
@Override
|
|
public int getVerboseInterval() {
|
|
return verboseInterval;
|
|
}
|
|
|
|
/**
|
|
* @param verboseInterval The verboseInterval to set.
|
|
*/
|
|
public void setVerboseInterval(int verboseInterval) {
|
|
this.verboseInterval = verboseInterval;
|
|
}
|
|
|
|
@Override
|
|
public int getPeriodicMaintainInterval() {
|
|
return periodicMaintainInterval;
|
|
}
|
|
|
|
/**
|
|
* @param periodicMaintainInteval
|
|
*/
|
|
public void setPeriodicMaintainInterval(int periodicMaintainInteval) {
|
|
this.periodicMaintainInterval = periodicMaintainInteval;
|
|
}
|
|
|
|
/**
|
|
* Unify the points-to-sets for the variables identified by the set s
|
|
*
|
|
* @param s numbers of points-to-set variables
|
|
* @throws IllegalArgumentException if s is null
|
|
*/
|
|
public void unify(IntSet s) {
|
|
if (s == null) {
|
|
throw new IllegalArgumentException("s is null");
|
|
}
|
|
// cache the variables represented
|
|
HashSet<PointsToSetVariable> cache = HashSetFactory.make(s.size());
|
|
for (IntIterator it = s.intIterator(); it.hasNext();) {
|
|
int i = it.next();
|
|
cache.add(pointsToMap.getPointsToSet(i));
|
|
}
|
|
|
|
// unify the variables
|
|
pointsToMap.unify(s);
|
|
int rep = pointsToMap.getRepresentative(s.intIterator().next());
|
|
|
|
// clean up the equations
|
|
updateEquationsForUnification(cache, rep);
|
|
|
|
// special logic to clean up side effects
|
|
updateSideEffectsForUnification(cache, rep);
|
|
}
|
|
|
|
/**
|
|
* Update side effect after unification
|
|
*
|
|
* @param s set of PointsToSetVariables that have been unified
|
|
* @param rep number of the representative variable for the unified set.
|
|
*/
|
|
private void updateSideEffectsForUnification(HashSet<PointsToSetVariable> s, int rep) {
|
|
PointsToSetVariable pRef = pointsToMap.getPointsToSet(rep);
|
|
for (Iterator<PointsToSetVariable> it = s.iterator(); it.hasNext();) {
|
|
PointsToSetVariable p = it.next();
|
|
updateSideEffects(p, pRef);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update equation def/uses after unification
|
|
*
|
|
* @param s set of PointsToSetVariables that have been unified
|
|
* @param rep number of the representative variable for the unified set.
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
private void updateEquationsForUnification(HashSet<PointsToSetVariable> s, int rep) {
|
|
PointsToSetVariable pRef = pointsToMap.getPointsToSet(rep);
|
|
for (Iterator<PointsToSetVariable> it = s.iterator(); it.hasNext();) {
|
|
PointsToSetVariable p = it.next();
|
|
|
|
if (p != pRef) {
|
|
// pRef is the representative for p.
|
|
// be careful: cache the defs before mucking with the underlying system
|
|
for (Iterator d = Iterator2Collection.toSet(getStatementsThatDef(p)).iterator(); d.hasNext();) {
|
|
AbstractStatement as = (AbstractStatement) d.next();
|
|
|
|
if (as instanceof AssignEquation) {
|
|
AssignEquation assign = (AssignEquation) as;
|
|
PointsToSetVariable rhs = assign.getRightHandSide();
|
|
int rhsRep = pointsToMap.getRepresentative(pointsToMap.getIndex(rhs.getPointerKey()));
|
|
if (rhsRep == rep) {
|
|
flowGraph.removeStatement(as);
|
|
} else {
|
|
replaceLHS(pRef, p, as);
|
|
}
|
|
} else {
|
|
replaceLHS(pRef, p, as);
|
|
}
|
|
}
|
|
// be careful: cache the defs before mucking with the underlying system
|
|
for (Iterator u = Iterator2Collection.toSet(getStatementsThatUse(p)).iterator(); u.hasNext();) {
|
|
AbstractStatement as = (AbstractStatement) u.next();
|
|
if (as instanceof AssignEquation) {
|
|
AssignEquation assign = (AssignEquation) as;
|
|
PointsToSetVariable lhs = assign.getLHS();
|
|
int lhsRep = pointsToMap.getRepresentative(pointsToMap.getIndex(lhs.getPointerKey()));
|
|
if (lhsRep == rep) {
|
|
flowGraph.removeStatement(as);
|
|
} else {
|
|
replaceRHS(pRef, p, as);
|
|
}
|
|
} else {
|
|
replaceRHS(pRef, p, as);
|
|
}
|
|
}
|
|
if (flowGraph.getNumberOfStatementsThatDef(p) == 0 && flowGraph.getNumberOfStatementsThatUse(p) == 0) {
|
|
flowGraph.removeVariable(p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* replace all occurrences of p on the rhs of a statement with pRef
|
|
*
|
|
* @param as a statement that uses p in it's right-hand side
|
|
*/
|
|
private void replaceRHS(PointsToSetVariable pRef, PointsToSetVariable p,
|
|
AbstractStatement<PointsToSetVariable, AbstractOperator<PointsToSetVariable>> as) {
|
|
if (as instanceof UnaryStatement) {
|
|
assert ((UnaryStatement) as).getRightHandSide() == p;
|
|
newStatement(as.getLHS(), (UnaryOperator<PointsToSetVariable>) as.getOperator(), pRef, false, false);
|
|
} else {
|
|
IVariable[] rhs = as.getRHS();
|
|
PointsToSetVariable[] newRHS = new PointsToSetVariable[rhs.length];
|
|
for (int i = 0; i < rhs.length; i++) {
|
|
if (rhs[i].equals(p)) {
|
|
newRHS[i] = pRef;
|
|
} else {
|
|
newRHS[i] = (PointsToSetVariable) rhs[i];
|
|
}
|
|
}
|
|
newStatement(as.getLHS(), as.getOperator(), newRHS, false, false);
|
|
}
|
|
flowGraph.removeStatement(as);
|
|
}
|
|
|
|
/**
|
|
* replace all occurences of p on the lhs of a statement with pRef
|
|
*
|
|
* @param as a statement that defs p
|
|
*/
|
|
private void replaceLHS(PointsToSetVariable pRef, PointsToSetVariable p,
|
|
AbstractStatement<PointsToSetVariable, AbstractOperator<PointsToSetVariable>> as) {
|
|
assert as.getLHS() == p;
|
|
if (as instanceof UnaryStatement) {
|
|
newStatement(pRef, (UnaryOperator<PointsToSetVariable>) as.getOperator(), (PointsToSetVariable) ((UnaryStatement) as)
|
|
.getRightHandSide(), false, false);
|
|
} else {
|
|
newStatement(pRef, as.getOperator(), as.getRHS(), false, false);
|
|
}
|
|
flowGraph.removeStatement(as);
|
|
}
|
|
|
|
public boolean isUnified(PointerKey result) {
|
|
return pointsToMap.isUnified(result);
|
|
}
|
|
|
|
public int getNumber(PointerKey p) {
|
|
return pointsToMap.getIndex(p);
|
|
}
|
|
|
|
@Override
|
|
protected PointsToSetVariable[] makeStmtRHS(int size) {
|
|
return new PointsToSetVariable[size];
|
|
}
|
|
}
|