Make FilterIterator and Predicate statically type-correct

Previously FilterIterator was very permissive regarding the type
relationships between the original iterator, the filtered iterator,
and the predicate used to prune the former down to the latter.  Now we
enforce those relationships more strictly, including proper use of
covariant ("<? extends T>") and contravariant ("<? super T>")
polymorphic type parameters where appropriate.

This lets us get rid of seven suppressed warnings about generic types
and/or unchecked conversions.  It also moves us toward being able to
use modern Java features like lambdas and streams more easily.
This commit is contained in:
Ben Liblit 2017-11-21 17:20:35 -06:00
parent 5ac8bf881d
commit 28f0e09435
21 changed files with 67 additions and 81 deletions

View File

@ -82,7 +82,7 @@ public abstract class CAstAbstractLoader implements IClassLoader {
}
private Iterator<ModuleEntry> getMessages(final byte severity) {
return new MapIterator<>(new FilterIterator<Map.Entry<ModuleEntry,Set<Warning>>>(errors.entrySet().iterator(), new Predicate<Map.Entry<ModuleEntry,Set<Warning>>>() {
return new MapIterator<>(new FilterIterator<>(errors.entrySet().iterator(), new Predicate<Map.Entry<ModuleEntry,Set<Warning>>>() {
@Override public boolean test(Entry<ModuleEntry, Set<Warning>> o) {
for(Warning w : o.getValue()) {
if (w.getLevel() == severity) {

View File

@ -80,7 +80,7 @@ public class CAstFunctions {
};
}
public static Iterator<CAstNode> findAll(CAstNode tree, Predicate<?> f) {
public static Iterator<CAstNode> findAll(CAstNode tree, Predicate<? super CAstNode> f) {
return new FilterIterator<>(iterateNodes(tree), f);
}

View File

@ -52,7 +52,7 @@ public abstract class HeapGraphImpl<T extends InstanceKey> implements HeapGraph<
@Override
public Collection<Object> getReachableInstances(Set<Object> roots) {
Predicate f = new Predicate() {
Predicate<Object> f = new Predicate<Object>() {
@Override public boolean test(Object o) {
return (o instanceof InstanceKey);
}

View File

@ -76,14 +76,13 @@ public class BackwardsSupergraph<T, P> implements ISupergraph<T, P> {
/**
* a filter that accepts only exit nodes from the original graph.
*/
private class ExitFilter implements Predicate {
private class ExitFilter implements Predicate<T> {
/*
* @see com.ibm.wala.util.Filter#accepts(java.lang.Object)
*/
@Override
@SuppressWarnings("unchecked")
public boolean test(Object o) {
return delegate.isExit((T) o);
public boolean test(T o) {
return delegate.isExit(o);
}
}
@ -98,7 +97,7 @@ public class BackwardsSupergraph<T, P> implements ISupergraph<T, P> {
if (DEBUG_LEVEL > 1) {
System.err.println(getClass() + " getCalledNodes " + ret);
System.err.println("called nodes: "
+ Iterator2Collection.toSet(new FilterIterator<Object>(getSuccNodes(ret), exitFilter)));
+ Iterator2Collection.toSet(new FilterIterator<>(getSuccNodes(ret), exitFilter)));
}
return new FilterIterator<T>(getSuccNodes(ret), exitFilter);
}
@ -110,15 +109,15 @@ public class BackwardsSupergraph<T, P> implements ISupergraph<T, P> {
*/
@Override
public Iterator<T> getNormalSuccessors(final T ret) {
Iterator<? extends Object> allPreds = delegate.getPredNodes(ret);
Predicate sameProc = new Predicate<T>() {
Iterator<T> allPreds = delegate.getPredNodes(ret);
Predicate<T> sameProc = new Predicate<T>() {
@Override public boolean test(T o) {
// throw out the exit node, which can be a predecessor due to tail recursion.
return getProcOf(ret).equals(getProcOf(o)) && !delegate.isExit(o);
}
};
Iterator<Object> sameProcPreds = new FilterIterator<Object>(allPreds, sameProc);
Predicate notCall = new Predicate<T>() {
Iterator<T> sameProcPreds = new FilterIterator<>(allPreds, sameProc);
Predicate<T> notCall = new Predicate<T>() {
@Override public boolean test(T o) {
return !delegate.isCall(o);
}

View File

@ -57,7 +57,7 @@ public class LocalLiveRangeAnalysis {
final Collection<BasicBlock> uses = findBlocks(ir, du.getUses(v));
// a filter which accepts everything but the block which defs v
Predicate notDef = new Predicate() {
Predicate<Object> notDef = new Predicate<Object>() {
@Override public boolean test(Object o) {
return (defBlock == null || !defBlock.equals(o));
}

View File

@ -187,9 +187,9 @@ public class ExplicitCallGraph extends BasicCallGraph<SSAContextInterpreter> imp
*/
protected Iterator<CallSiteReference> getPossibleSites(final CGNode to) {
final int n = getCallGraph().getNumber(to);
return new FilterIterator<CallSiteReference>(iterateCallSites(), new Predicate() {
@Override public boolean test(Object o) {
IntSet s = getPossibleTargetNumbers((CallSiteReference) o);
return new FilterIterator<CallSiteReference>(iterateCallSites(), new Predicate<CallSiteReference>() {
@Override public boolean test(CallSiteReference o) {
IntSet s = getPossibleTargetNumbers(o);
return s == null ? false : s.contains(n);
}
});

View File

@ -122,9 +122,9 @@ public class PartialCallGraph extends DelegatingGraph<CGNode> implements CallGra
@Override
public Iterator<CGNode> iterateNodes(IntSet nodes) {
return new FilterIterator<CGNode>(cg.iterateNodes(nodes), new Predicate() {
@Override public boolean test(Object o) {
return containsNode((CGNode) o);
return new FilterIterator<CGNode>(cg.iterateNodes(nodes), new Predicate<CGNode>() {
@Override public boolean test(CGNode o) {
return containsNode(o);
}
});
}

View File

@ -201,9 +201,9 @@ public class PointsToMap {
* @return {@link Iterator}&lt;{@link PointerKey}&gt;
*/
public Iterator<PointerKey> getTransitiveRoots() {
return new FilterIterator<PointerKey>(iterateKeys(), new Predicate() {
@Override public boolean test(Object o) {
return isTransitiveRoot((PointerKey) o);
return new FilterIterator<PointerKey>(iterateKeys(), new Predicate<PointerKey>() {
@Override public boolean test(PointerKey o) {
return isTransitiveRoot(o);
}
});
}

View File

@ -26,11 +26,7 @@ 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.util.collections.CompoundIterator;
import com.ibm.wala.util.collections.EmptyIterator;
import com.ibm.wala.util.collections.FilterIterator;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.SmallMap;
import com.ibm.wala.util.collections.*;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.debug.UnimplementedError;
import com.ibm.wala.util.graph.AbstractNumberedGraph;
@ -274,14 +270,14 @@ public class PropagationGraph implements IFixedPointSystem<PointsToSetVariable>
}
@Override
@SuppressWarnings("unchecked")
public Iterator<AbstractStatement> getStatements() {
Iterator<AbstractStatement> it = new FilterIterator(delegateGraph.iterator(), new Predicate() {
@Override public boolean test(Object x) {
Iterator<INodeWithNumber> it = new FilterIterator<>(delegateGraph.iterator(), new Predicate<INodeWithNumber>() {
@Override public boolean test(INodeWithNumber x) {
return x instanceof AbstractStatement;
}
});
return new CompoundIterator<AbstractStatement>(it, new GlobalImplicitIterator());
Iterator<AbstractStatement> converted = new MapIterator<>(it, AbstractStatement.class::cast);
return new CompoundIterator<AbstractStatement>(converted, new GlobalImplicitIterator());
}
/**
@ -765,14 +761,14 @@ public class PropagationGraph implements IFixedPointSystem<PointsToSetVariable>
}
@Override
@SuppressWarnings("unchecked")
public Iterator<PointsToSetVariable> getVariables() {
Iterator<PointsToSetVariable> it = new FilterIterator(delegateGraph.iterator(), new Predicate() {
@Override public boolean test(Object x) {
Iterator<INodeWithNumber> it = new FilterIterator<>(delegateGraph.iterator(), new Predicate<INodeWithNumber>() {
@Override public boolean test(INodeWithNumber x) {
return x instanceof IVariable;
}
});
return it;
Iterator<PointsToSetVariable> converted = new MapIterator<INodeWithNumber, PointsToSetVariable>(it, PointsToSetVariable.class::cast);
return converted;
}
/*

View File

@ -37,9 +37,7 @@ import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.FilterIterator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.*;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.debug.UnimplementedError;
@ -178,12 +176,13 @@ public class TypeBasedHeapModel implements HeapModel {
@Override
public Iterator<PointerKey> iteratePointerKeys() {
initAllPKeys();
return new FilterIterator<>(pKeys.values().iterator(), new Predicate() {
Iterator<Object> filtered = new FilterIterator<Object>(pKeys.values().iterator(), new Predicate<Object>() {
@Override
public boolean test(Object o) {
return o instanceof PointerKey;
}
});
return new MapIterator<Object, PointerKey>(filtered, PointerKey.class::cast);
}
@Override

View File

@ -842,9 +842,8 @@ public abstract class AbstractInterproceduralCFG<T extends ISSABasicBlock> imple
// a successor node is a return site if it is in the same
// procedure, and is not the entry() node.
Predicate isReturn = new Predicate() {
@Override public boolean test(Object o) {
BasicBlockInContext other = (BasicBlockInContext) o;
Predicate<BasicBlockInContext> isReturn = new Predicate<BasicBlockInContext>() {
@Override public boolean test(BasicBlockInContext other) {
return !other.isEntryBlock() && node.equals(other.getNode());
}
};
@ -863,7 +862,7 @@ public abstract class AbstractInterproceduralCFG<T extends ISSABasicBlock> imple
Iterator<? extends T> it = cfg.getPredNodes(returnBlock.getDelegate());
final CGNode node = returnBlock.getNode();
Predicate<? extends T> dispatchFilter = new Predicate<T>() {
Predicate<T> dispatchFilter = new Predicate<T>() {
@Override public boolean test(T callBlock) {
BasicBlockInContext<T> bb = new BasicBlockInContext<T>(node, callBlock);
if (!hasCall(bb, cfg)) {

View File

@ -225,9 +225,9 @@ public class PrunedCFG<I, T extends IBasicBlock<I>> extends AbstractNumberedGrap
return max;
}
private Iterator<T> filterNodes(Iterator nodeIterator) {
return new FilterIterator<T>(nodeIterator, new Predicate() {
@Override public boolean test(Object o) {
private Iterator<T> filterNodes(Iterator<T> nodeIterator) {
return new FilterIterator<T>(nodeIterator, new Predicate<T>() {
@Override public boolean test(T o) {
return subset.contains(o);
}
});

View File

@ -644,8 +644,8 @@ public class HeapReachingDefs<T extends InstanceKey> {
return null;
} else {
// only static fields are actually killed
Predicate staticFilter = new Predicate() {
@Override public boolean test(Object o) {
Predicate<PointerKey> staticFilter = new Predicate<PointerKey>() {
@Override public boolean test(PointerKey o) {
return o instanceof StaticFieldKey;
}
};
@ -654,10 +654,9 @@ public class HeapReachingDefs<T extends InstanceKey> {
if (kill.isEmpty()) {
return null;
} else {
Predicate f = new Predicate() {
Predicate<Statement> f = new Predicate<Statement>() {
// accept any statement which writes a killed location.
@Override public boolean test(Object o) {
Statement s = (Statement) o;
@Override public boolean test(Statement s) {
Collection m = getMod(s, node, heapModel, pa, exclusions);
for (PointerKey k : kill) {
if (m.contains(k)) {

View File

@ -53,12 +53,7 @@ import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.FilterIterator;
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.Iterator2Iterable;
import com.ibm.wala.util.collections.MapUtil;
import com.ibm.wala.util.collections.*;
import com.ibm.wala.util.config.SetOfClasses;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.debug.UnimplementedError;
@ -679,8 +674,8 @@ public class PDG<T extends InstanceKey> implements NumberedGraph<Statement> {
// in reaching defs calculation, exclude heap statements that are
// irrelevant.
Predicate f = new Predicate() {
@Override public boolean test(Object o) {
Predicate<Statement> f = new Predicate<Statement>() {
@Override public boolean test(Statement o) {
if (o instanceof HeapStatement) {
HeapStatement h = (HeapStatement) o;
return h.getLocation().equals(pk);
@ -771,8 +766,8 @@ public class PDG<T extends InstanceKey> implements NumberedGraph<Statement> {
* @return Statements representing each return instruction in the ir
*/
private Collection<NormalStatement> computeReturnStatements(final IR ir) {
Predicate filter = new Predicate() {
@Override public boolean test(Object o) {
Predicate<Statement> filter = new Predicate<Statement>() {
@Override public boolean test(Statement o) {
if (o instanceof NormalStatement) {
NormalStatement s = (NormalStatement) o;
SSAInstruction st = ir.getInstructions()[s.getInstructionIndex()];
@ -782,7 +777,10 @@ public class PDG<T extends InstanceKey> implements NumberedGraph<Statement> {
}
}
};
return Iterator2Collection.toSet(new FilterIterator<NormalStatement>(iterator(), filter));
return Iterator2Collection.toSet(
new MapIterator<Statement, NormalStatement>(
new FilterIterator<Statement>(iterator(), filter),
NormalStatement.class::cast));
}
/**

View File

@ -97,9 +97,8 @@ class SDGSupergraph implements ISupergraph<Statement, PDG<? extends InstanceKey>
public Iterator<? extends Statement> getCalledNodes(Statement call) {
switch (call.getKind()) {
case NORMAL:
Predicate<?> f = new Predicate() {
@Override public boolean test(Object o) {
Statement s = (Statement) o;
Predicate<Statement> f = new Predicate<Statement>() {
@Override public boolean test(Statement s) {
return isEntry(s);
}
};

View File

@ -258,9 +258,9 @@ public class DefaultFixedPointSystem<T extends IVariable<T>> implements IFixedPo
}
@Override
public Iterator<T> getVariables() {
return new FilterIterator<>(graph.iterator(), new Predicate<T>() {
@Override public boolean test(T x) {
public Iterator<INodeWithNumber> getVariables() {
return new FilterIterator<>(graph.iterator(), new Predicate<Object>() {
@Override public boolean test(Object x) {
return x != null;
}
});

View File

@ -18,10 +18,9 @@ import java.util.function.Predicate;
* A <code>FilterIterator</code> filters an <code>Iterator</code> to generate a new one.
*/
public class FilterIterator<T> implements java.util.Iterator<T> {
final Iterator<?> i;
final Iterator<? extends T> i;
@SuppressWarnings("rawtypes")
final Predicate f;
final Predicate<? super T> f;
private T next = null;
@ -31,8 +30,7 @@ public class FilterIterator<T> implements java.util.Iterator<T> {
* @param i the original iterator
* @param f a filter which defines which elements belong to the generated iterator
*/
@SuppressWarnings("rawtypes")
public FilterIterator(Iterator<?> i, Predicate f) {
public FilterIterator(Iterator<? extends T> i, Predicate<? super T> f) {
if (i == null) {
throw new IllegalArgumentException("null i");
}
@ -47,12 +45,11 @@ public class FilterIterator<T> implements java.util.Iterator<T> {
/**
* update the internal state to prepare for the next access to this iterator
*/
@SuppressWarnings("unchecked")
private void advance() {
while (i.hasNext()) {
Object o = i.next();
T o = i.next();
if (f.test(o)) {
next = (T) o;
next = o;
return;
}
}

View File

@ -47,7 +47,7 @@ public class IteratorUtil {
public static <T, S extends T> Iterator<S> filter(Iterator<T> iterator, final Class<S> cls) {
return new MapIterator<>(
new FilterIterator<T>(iterator, new Predicate<T>() {
new FilterIterator<>(iterator, new Predicate<T>() {
@Override public boolean test(T o) {
return cls.isInstance(o);
}

View File

@ -58,12 +58,12 @@ public class GraphReachability<T, S> {
* @param filter "interesting" node definition
* @throws IllegalArgumentException if g is null
*/
public GraphReachability(Graph<T> g, Predicate<?> filter) {
public GraphReachability(Graph<T> g, Predicate<? super T> filter) {
if (g == null) {
throw new IllegalArgumentException("g is null");
}
this.g = g;
Iterator<S> i = new FilterIterator<>(g.iterator(), filter);
Iterator<T> i = new FilterIterator<>(g.iterator(), filter);
domain = new MutableMapping<>((Iterator2Collection.toSet(i)).toArray());
}

View File

@ -41,7 +41,7 @@ public class DFS {
* @throws IllegalArgumentException if C is null
*/
@SuppressWarnings("serial")
public static <T> Collection<T> getReachableNodes(final Graph<T> G, Collection<? extends T> C, @SuppressWarnings("rawtypes") final Predicate filter) {
public static <T> Collection<T> getReachableNodes(final Graph<T> G, Collection<? extends T> C, final Predicate<? super T> filter) {
if (C == null) {
throw new IllegalArgumentException("C is null");
}

View File

@ -35,7 +35,7 @@ public class RtJar {
public static void main(String[] args) {
@SuppressWarnings("resource")
JarFile rt = getRtJar(new MapIterator<>(
new FilterIterator<String>(
new FilterIterator<>(
new ArrayIterator<>(System.getProperty("sun.boot.class.path").split(File.pathSeparator)),
new Predicate<String>() {
@Override