/******************************************************************************
* Copyright (c) 2009 - 2015 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.memsat.frontEnd.fieldssa;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import com.ibm.wala.classLoader.CallSiteReference;
import com.ibm.wala.dataflow.graph.AbstractMeetOperator;
import com.ibm.wala.dataflow.graph.DataflowSolver;
import com.ibm.wala.dataflow.graph.IKilldallFramework;
import com.ibm.wala.dataflow.graph.ITransferFunctionProvider;
import com.ibm.wala.fixpoint.IVariable;
import com.ibm.wala.fixpoint.UnaryOperator;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.propagation.PointerKey;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.Predicate;
import com.ibm.wala.util.collections.ComposedIterator;
import com.ibm.wala.util.collections.FilterIterator;
import com.ibm.wala.util.collections.IteratorPlusOne;
import com.ibm.wala.util.collections.IteratorUtil;
import com.ibm.wala.util.collections.MapIterator;
import com.ibm.wala.util.collections.NonNullSingletonIterator;
import com.ibm.wala.util.collections.Pair;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.functions.Function;
import com.ibm.wala.util.graph.AbstractGraph;
import com.ibm.wala.util.graph.EdgeManager;
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.graph.NodeManager;
public class IPFieldAccessAnalysis implements IKilldallFramework {
private final CallGraph CG;
private final Collection interestingNodes;
private final FieldAccessesFactory localAccesses;
private class FieldAccessesVariable implements IVariable {
private int nodeId = -1;
private int orderNumber;
private Set readReferences;
private Set writeReferences;
public int getGraphNodeId() { return nodeId; }
public void setGraphNodeId(int i) { nodeId = i; }
public int getOrderNumber() { return orderNumber; }
public void setOrderNumber(int i) { orderNumber = i; }
public void copyState(IVariable v) {
FieldAccessesVariable other = (FieldAccessesVariable)v;
this.orderNumber = other.orderNumber;
this.readReferences = new HashSet();
this.readReferences.addAll( other.readReferences );
this.writeReferences = new HashSet();
this.writeReferences.addAll( other.writeReferences );
}
private boolean addReads(Set newReferences) {
if (newReferences == null) return false;
if (readReferences == null) {
if (! newReferences.isEmpty()) {
readReferences = new HashSet();
readReferences.addAll( newReferences );
return true;
} else {
return false;
}
} else {
if (readReferences.containsAll( newReferences )) {
return false;
} else {
readReferences.addAll( newReferences );
return true;
}
}
}
private boolean addWrites(Set newReferences) {
if (newReferences == null) return false;
if (writeReferences == null) {
if (! newReferences.isEmpty()) {
writeReferences = new HashSet();
writeReferences.addAll( newReferences );
return true;
} else {
return false;
}
} else {
if (writeReferences.containsAll( newReferences )) {
return false;
} else {
writeReferences.addAll( newReferences );
return true;
}
}
}
}
private class NodeTransferFunction extends UnaryOperator {
private final CGNode node;
private NodeTransferFunction(CGNode node) {
this.node = node;
}
public String toString() {
return "FieldAccessAnalysis transfer for " + node;
}
public int hashCode() {
return node.hashCode();
}
public boolean equals(Object o) {
return (o instanceof NodeTransferFunction)
&&
((NodeTransferFunction)o).node.equals(node);
}
public byte evaluate(IVariable lhs, IVariable rhs) {
FieldAccessesVariable lv = (FieldAccessesVariable)lhs;
FieldAccessesVariable rv = (FieldAccessesVariable)rhs;
IR ir = node.getIR();
Set localReads = new HashSet();
Set localWrites = new HashSet();
if (ir != null) {
FieldAccesses accesses = localAccesses.get(node);
for(Iterator insts = getInstructions(node); insts.hasNext();) {
SSAInstruction inst = (SSAInstruction) insts.next();
PointerKey[] defs = accesses.getDefs(inst);
for(int f = 0; f < defs.length; f++) {
localWrites.add( defs[f] );
}
PointerKey[] uses = accesses.getUses(inst);
for(int f = 0; f < uses.length; f++) {
localReads.add( uses[f] );
}
}
}
boolean changed = false;
changed |= lv.addReads( rv.readReferences );
changed |= lv.addReads( localReads );
changed |= lv.addWrites( rv.writeReferences );
changed |= lv.addWrites( localWrites );
if (changed)
return CHANGED;
else
return NOT_CHANGED;
}
}
private UnaryOperator identityTransferFunction = new UnaryOperator() {
public boolean isIdentity() {
return true;
}
public String toString() {
return "FieldAccessAnalysis identity transfer function";
}
public int hashCode() {
return 86587567;
}
public boolean equals(Object o) {
return o == this;
}
public byte evaluate(IVariable lhs, IVariable rhs) {
FieldAccessesVariable lv = (FieldAccessesVariable)lhs;
FieldAccessesVariable rv = (FieldAccessesVariable)rhs;
boolean newReads = lv.addReads(rv.readReferences);
boolean newWrites = lv.addWrites(rv.writeReferences);
if (newReads || newWrites)
return CHANGED;
else
return NOT_CHANGED;
}
};
private AbstractMeetOperator unionMeetOperator = new AbstractMeetOperator() {
public byte evaluate(IVariable lhs, IVariable[] rhs) {
FieldAccessesVariable lv = (FieldAccessesVariable)lhs;
boolean newReads = false;
boolean newWrites = false;
for(int i = 0; i < rhs.length; i++) {
FieldAccessesVariable rv = (FieldAccessesVariable)rhs[i];
newReads |= lv.addReads(rv.readReferences);
newWrites |= lv.addWrites(rv.writeReferences);
}
if (newReads || newWrites)
return CHANGED;
else
return NOT_CHANGED;
}
public int hashCode() {
return 143464675;
}
public boolean equals(Object o) {
return o == this;
}
public String toString() {
return "FieldAccessesAnalysis meet operator";
}
};
private final ITransferFunctionProvider fieldAccessFunctions =
new ITransferFunctionProvider() {
public UnaryOperator getNodeTransferFunction(Object node) {
if (node instanceof Pair) {
return identityTransferFunction;
} else {
return new NodeTransferFunction((CGNode)node);
}
}
public boolean hasNodeTransferFunctions() {
return true;
}
public UnaryOperator getEdgeTransferFunction(Object src, Object dst) {
Assertions.UNREACHABLE();
return null;
}
public boolean hasEdgeTransferFunctions() {
return false;
}
public AbstractMeetOperator getMeetOperator() {
return unionMeetOperator;
}
};
private class IPFieldAccessSolver extends DataflowSolver {
protected IVariable makeNodeVariable(Object n, boolean IN) {
return new FieldAccessesVariable();
}
protected IVariable makeEdgeVariable(Object src, Object dst) {
Assertions.UNREACHABLE();
return null;
}
private IPFieldAccessSolver(IKilldallFramework problem) {
super(problem);
}
@Override
protected IVariable[] makeStmtRHS(int size) {
return new IVariable[size];
}
}
protected Iterator getInstructions(CGNode node) {
return node.getIR().iterateAllInstructions();
}
private Iterator iterateNodes() {
if (interestingNodes != null)
return interestingNodes.iterator();
else
return CG.iterator();
}
private boolean containsNode(CGNode node) {
if (interestingNodes != null)
return interestingNodes.contains(node);
else
return CG.containsNode(node);
}
public ITransferFunctionProvider getTransferFunctionProvider() {
return fieldAccessFunctions;
}
public Graph getFlowGraph() {
return new AbstractGraph() {
private final NodeManager nodeManager = new NodeManager() {
public Iterator iterator() {
return new ComposedIterator(IPFieldAccessAnalysis.this.iterateNodes()) {
public Iterator makeInner(Object outer) {
final CGNode node = (CGNode)outer;
return
IteratorPlusOne.make(
new MapIterator(
node.iterateCallSites(),
new Function() {
public Object apply(Object object) {
return Pair.make(node, object);
}
}),
node);
}
};
}
public int getNumberOfNodes() {
int count = 0;
for(Iterator nodes = iterateNodes(); nodes.hasNext(); ) {
CGNode node = (CGNode) nodes.next();
count++;
for(Iterator sites = node.iterateCallSites(); sites.hasNext(); ) {
CallSiteReference site = (CallSiteReference)sites.next();
count++;
}
}
return count;
}
public void addNode(Object n) {
Assertions.UNREACHABLE();
}
public void removeNode(Object n) {
Assertions.UNREACHABLE();
}
public boolean containsNode(Object N) {
if (N instanceof CGNode){
return containsNode((CGNode)N);
} else if (N instanceof Pair) {
Pair p = (Pair)N;
if (containsNode((CGNode)p.fst)) {
return IteratorUtil.contains(((CGNode)p.fst).iterateCallSites(), (CallSiteReference)p.snd);
} else {
return false;
}
} else {
return false;
}
}
};
private final EdgeManager edgeManager = new EdgeManager() {
public Iterator getSuccNodes(Object N) {
if (N instanceof Pair) {
return new NonNullSingletonIterator(((Pair)N).fst);
} else {
assert N instanceof CGNode;
final CGNode node = (CGNode)N;
return new ComposedIterator(iterateNodes()) {
public Iterator makeInner(Object outer) {
final CGNode caller = (CGNode)outer;
return
new MapIterator(
new FilterIterator(
caller.iterateCallSites(),
new Predicate() {
public boolean test(Object site) {
return CG
.getPossibleTargets(caller, (CallSiteReference)site)
.contains(node);
}
}),
new Function() {
public Object apply(Object site) {
return Pair.make(caller, site);
}
});
}
};
}
}
public int getSuccNodeCount(Object N) {
return IteratorUtil.count( getSuccNodes(N) );
}
public Iterator getPredNodes(final Object N) {
if (N instanceof CGNode) {
return new MapIterator(
((CGNode)N).iterateCallSites(),
new Function() {
public Object apply(Object site) {
return Pair.make(N, site);
}
});
} else {
assert N instanceof Pair;
Pair p = (Pair)N;
return
CG.getPossibleTargets((CGNode)p.fst, (CallSiteReference)p.snd)
.iterator();
}
}
public int getPredNodeCount(Object N) {
return IteratorUtil.count( getPredNodes(N) );
}
public boolean hasEdge(Object N, Object dst) {
if (N instanceof CGNode) {
return (dst instanceof Pair) &&
((Pair)dst).fst.equals(N) &&
IteratorUtil.contains(
((CGNode)N).iterateCallSites(),
(CallSiteReference)((Pair)dst).snd);
} else {
assert N instanceof Pair;
Pair p = (Pair)N;
return
CG.getPossibleTargets((CGNode)p.fst, (CallSiteReference)p.snd)
.contains(dst);
}
}
public void addEdge(Object src, Object dst) {
Assertions.UNREACHABLE();
}
public void removeEdge(Object src, Object dst) {
Assertions.UNREACHABLE();
}
public void removeAllIncidentEdges(Object node) {
Assertions.UNREACHABLE();
}
public void removeIncomingEdges(Object node) {
Assertions.UNREACHABLE();
}
public void removeOutgoingEdges(Object node) {
Assertions.UNREACHABLE();
}
};
protected NodeManager getNodeManager() {
return nodeManager;
}
protected EdgeManager getEdgeManager() {
return edgeManager;
}
};
}
public IPFieldAccessAnalysis(CallGraph CG,
FieldAccessesFactory localAccesses)
{
this(CG, null, localAccesses);
}
public IPFieldAccessAnalysis(CallGraph CG,
Collection interestingNodes,
FieldAccessesFactory localAccesses)
{
this.CG = CG;
this.interestingNodes = interestingNodes;
this.localAccesses = localAccesses;
}
public IPFieldAccessesResult solve() throws CancelException {
final IPFieldAccessSolver solver = new IPFieldAccessSolver(this);
solver.solve(null);
return new IPFieldAccessesResult() {
public FieldAccesses getFieldAccesses(final CGNode node) {
return new FieldAccesses() {
public PointerKey[] getUses(SSAInstruction i) {
SSAAbstractInvokeInstruction inst=(SSAAbstractInvokeInstruction)i;
FieldAccessesVariable var = (FieldAccessesVariable)
solver.getIn(Pair.make(node, inst.getCallSite()));
if (var.readReferences == null)
return new PointerKey[0];
else
return (PointerKey[])
var.readReferences
.toArray(new PointerKey[ var.readReferences.size() ]);
}
public PointerKey[] getDefs(SSAInstruction i) {
SSAAbstractInvokeInstruction inst=(SSAAbstractInvokeInstruction)i;
FieldAccessesVariable var = (FieldAccessesVariable)
solver.getIn(Pair.make(node, inst.getCallSite()));
if (var.writeReferences == null)
return new PointerKey[0];
else
return (PointerKey[])
var.writeReferences
.toArray(new PointerKey[ var.writeReferences.size() ]);
}
};
}
};
}
}