clean up some IPCFG issues
git-svn-id: https://wala.svn.sourceforge.net/svnroot/wala/trunk@1877 f5eafffb-2e1d-0410-98e4-8ec43c5233c4
This commit is contained in:
parent
992238fc1c
commit
cffb55a1e2
|
@ -1,577 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* 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.cfg;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.ibm.wala.classLoader.IMethod;
|
|
||||||
import com.ibm.wala.shrikeBT.IInstruction;
|
|
||||||
import com.ibm.wala.ssa.ISSABasicBlock;
|
|
||||||
import com.ibm.wala.ssa.SSAInstruction;
|
|
||||||
import com.ibm.wala.ssa.SSAPhiInstruction;
|
|
||||||
import com.ibm.wala.ssa.SSAPiInstruction;
|
|
||||||
import com.ibm.wala.ssa.SSAThrowInstruction;
|
|
||||||
import com.ibm.wala.util.IteratorPlusOne;
|
|
||||||
import com.ibm.wala.util.collections.EmptyIterator;
|
|
||||||
import com.ibm.wala.util.debug.Assertions;
|
|
||||||
import com.ibm.wala.util.debug.Trace;
|
|
||||||
import com.ibm.wala.util.debug.UnimplementedError;
|
|
||||||
import com.ibm.wala.util.graph.impl.NumberedNodeIterator;
|
|
||||||
import com.ibm.wala.util.intset.BitVector;
|
|
||||||
import com.ibm.wala.util.intset.FixedSizeBitVector;
|
|
||||||
import com.ibm.wala.util.intset.IntSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* This provides a view of a control flow graph with two exits, one for normal
|
|
||||||
* returns and one for exceptional exits.
|
|
||||||
*
|
|
||||||
* @author sfink
|
|
||||||
*/
|
|
||||||
public class TwoExitCFG implements ControlFlowGraph<ISSABasicBlock> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DEBUG_LEVEL: 0 No output 1 Print some simple stats and warning information
|
|
||||||
* 2 Detailed debugging
|
|
||||||
*/
|
|
||||||
static final int DEBUG_LEVEL = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A "normal" cfg with one exit node
|
|
||||||
*/
|
|
||||||
private final ControlFlowGraph<ISSABasicBlock> delegate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A distinguished basic block representing the exceptional exit.
|
|
||||||
*/
|
|
||||||
private final ISSABasicBlock exceptionalExit = new ExceptionalExitBlock();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Numbers of the "normal" predecessors of the delegate's exit() node
|
|
||||||
*/
|
|
||||||
private FixedSizeBitVector normalPred;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Numbers of the "exceptional" predecessors of the delegate's exit() node
|
|
||||||
*/
|
|
||||||
private FixedSizeBitVector exceptionalPred;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cached here for efficiency: the "number" of the delegate's exit() node
|
|
||||||
*/
|
|
||||||
private final int delegateExitNumber;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* compute edges lazily
|
|
||||||
*/
|
|
||||||
private boolean edgesAreComputed = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param delegate
|
|
||||||
* A "normal" cfg with one exit node
|
|
||||||
* @throws IllegalArgumentException if delegate is null
|
|
||||||
*/
|
|
||||||
public TwoExitCFG(ControlFlowGraph<ISSABasicBlock> delegate) {
|
|
||||||
if (delegate == null) {
|
|
||||||
throw new IllegalArgumentException("delegate is null");
|
|
||||||
}
|
|
||||||
if (Assertions.verifyAssertions) {
|
|
||||||
Assertions._assert(!(delegate instanceof TwoExitCFG), "bad recursion");
|
|
||||||
}
|
|
||||||
this.delegate = delegate;
|
|
||||||
this.delegateExitNumber = delegate.getNumber(delegate.exit());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ensureEdgesReady() {
|
|
||||||
if (!edgesAreComputed) {
|
|
||||||
computeEdges();
|
|
||||||
edgesAreComputed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param delegate
|
|
||||||
*/
|
|
||||||
private void computeEdges() {
|
|
||||||
normalPred = (delegate instanceof AbstractCFG) ? ((AbstractCFG) delegate).getNormalToExit() : new FixedSizeBitVector(delegate
|
|
||||||
.getMaxNumber() + 1);
|
|
||||||
exceptionalPred = (delegate instanceof AbstractCFG) ? ((AbstractCFG) delegate).getExceptionalToExit() : new FixedSizeBitVector(
|
|
||||||
delegate.getMaxNumber() + 1);
|
|
||||||
if (!(delegate instanceof AbstractCFG)) {
|
|
||||||
IInstruction[] instructions = delegate.getInstructions();
|
|
||||||
for (Iterator it = delegate.getPredNodes(delegate.exit()); it.hasNext();) {
|
|
||||||
ISSABasicBlock b = (ISSABasicBlock) it.next();
|
|
||||||
if (b.getLastInstructionIndex() >= 0) {
|
|
||||||
IInstruction last = instructions[b.getLastInstructionIndex()];
|
|
||||||
if (last != null && last.isPEI()) {
|
|
||||||
exceptionalPred.set(b.getNumber());
|
|
||||||
// occasionally for weird CFGs we may actually fall
|
|
||||||
// thru to the exit. TODO: perhaps enforce an invariant
|
|
||||||
// that all "normal" predecessors of exit end in return.
|
|
||||||
if (!(last instanceof SSAThrowInstruction)) {
|
|
||||||
if (b.getLastInstructionIndex() == instructions.length - 1) {
|
|
||||||
normalPred.set(b.getNumber());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
normalPred.set(b.getNumber());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISSABasicBlock entry() {
|
|
||||||
return delegate.entry();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISSABasicBlock exit() throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException("don't call this");
|
|
||||||
}
|
|
||||||
|
|
||||||
public BitVector getCatchBlocks() {
|
|
||||||
return delegate.getCatchBlocks();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISSABasicBlock getBlockForInstruction(int index) {
|
|
||||||
return delegate.getBlockForInstruction(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IInstruction[] getInstructions() {
|
|
||||||
return delegate.getInstructions();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getProgramCounter(int index) {
|
|
||||||
return delegate.getProgramCounter(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.Graph#removeNodeAndEdges(java.lang.Object)
|
|
||||||
*/
|
|
||||||
public void removeNodeAndEdges(ISSABasicBlock N) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNumber(ISSABasicBlock N) {
|
|
||||||
if (N == null) {
|
|
||||||
throw new IllegalArgumentException("N is null");
|
|
||||||
}
|
|
||||||
if (N.equals(exceptionalExit)) {
|
|
||||||
return getMaxNumber();
|
|
||||||
} else {
|
|
||||||
return delegate.getNumber(N);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.NumberedNodeManager#getNode(int)
|
|
||||||
*/
|
|
||||||
public ISSABasicBlock getNode(int number) {
|
|
||||||
return (number == getMaxNumber()) ? exceptionalExit : delegate.getNode(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.NumberedNodeManager#getMaxNumber()
|
|
||||||
*/
|
|
||||||
public int getMaxNumber() {
|
|
||||||
return delegate.getMaxNumber() + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.NodeManager#iterateNodes()
|
|
||||||
*/
|
|
||||||
public Iterator<ISSABasicBlock> iterator() {
|
|
||||||
return IteratorPlusOne.make(delegate.iterator(), exceptionalExit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.NodeManager#getNumberOfNodes()
|
|
||||||
*/
|
|
||||||
public int getNumberOfNodes() {
|
|
||||||
return delegate.getNumberOfNodes() + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.NodeManager#addNode(java.lang.Object)
|
|
||||||
*/
|
|
||||||
public void addNode(ISSABasicBlock n) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.NodeManager#removeNode(java.lang.Object)
|
|
||||||
*/
|
|
||||||
public void removeNode(ISSABasicBlock n) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.NodeManager#containsNode(java.lang.Object)
|
|
||||||
*/
|
|
||||||
public boolean containsNode(ISSABasicBlock N) {
|
|
||||||
return delegate.containsNode(N) || N.equals(exceptionalExit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.EdgeManager#getPredNodes(java.lang.Object)
|
|
||||||
*/
|
|
||||||
public Iterator<? extends ISSABasicBlock> getPredNodes(ISSABasicBlock N) {
|
|
||||||
if (N == null) {
|
|
||||||
throw new IllegalArgumentException("N is null");
|
|
||||||
}
|
|
||||||
if (N.equals(exceptionalExit)) {
|
|
||||||
return delegate.getExceptionalPredecessors(delegate.exit()).iterator();
|
|
||||||
} else if (N.equals(delegate.exit())) {
|
|
||||||
return delegate.getNormalPredecessors(delegate.exit()).iterator();
|
|
||||||
} else {
|
|
||||||
return delegate.getPredNodes(N);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.EdgeManager#getPredNodeCount(java.lang.Object)
|
|
||||||
*/
|
|
||||||
public int getPredNodeCount(ISSABasicBlock N) {
|
|
||||||
if (N == null) {
|
|
||||||
throw new IllegalArgumentException("N is null");
|
|
||||||
}
|
|
||||||
ensureEdgesReady();
|
|
||||||
if (N.equals(delegate.exit())) {
|
|
||||||
return normalPred.populationCount();
|
|
||||||
} else if (N.equals(exceptionalExit)) {
|
|
||||||
return exceptionalPred.populationCount();
|
|
||||||
} else {
|
|
||||||
return delegate.getPredNodeCount(N);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.EdgeManager#getSuccNodes(java.lang.Object)
|
|
||||||
*/
|
|
||||||
public Iterator<? extends ISSABasicBlock> getSuccNodes(ISSABasicBlock N) {
|
|
||||||
if (N == null) {
|
|
||||||
throw new IllegalArgumentException("N is null");
|
|
||||||
}
|
|
||||||
if (DEBUG_LEVEL > 1) {
|
|
||||||
Trace.println("TwoExitCFG: getSuccNodes " + N);
|
|
||||||
}
|
|
||||||
ensureEdgesReady();
|
|
||||||
ISSABasicBlock bb = N;
|
|
||||||
if (N.equals(exceptionalExit)) {
|
|
||||||
return EmptyIterator.instance();
|
|
||||||
} else if (exceptionalPred.get(bb.getNumber())) {
|
|
||||||
if (normalPred.get(bb.getNumber())) {
|
|
||||||
return IteratorPlusOne.make(delegate.getSuccNodes(N), exceptionalExit);
|
|
||||||
} else {
|
|
||||||
return new SubstitutionIterator(delegate.getSuccNodes(N));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return delegate.getSuccNodes(N);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.EdgeManager#getSuccNodeCount(java.lang.Object)
|
|
||||||
*/
|
|
||||||
public int getSuccNodeCount(ISSABasicBlock N) {
|
|
||||||
if (N == null) {
|
|
||||||
throw new IllegalArgumentException("N is null");
|
|
||||||
}
|
|
||||||
if (N.equals(exceptionalExit)) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
ensureEdgesReady();
|
|
||||||
int result = delegate.getSuccNodeCount(N);
|
|
||||||
ISSABasicBlock bb = N;
|
|
||||||
if (exceptionalPred.get(bb.getNumber()) && normalPred.get(bb.getNumber())) {
|
|
||||||
result++;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.EdgeManager#addEdge(java.lang.Object,
|
|
||||||
* java.lang.Object)
|
|
||||||
*/
|
|
||||||
public void addEdge(ISSABasicBlock src, ISSABasicBlock dst) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeEdge(ISSABasicBlock src, ISSABasicBlock dst) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasEdge(ISSABasicBlock src, ISSABasicBlock dst) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeAllIncidentEdges(ISSABasicBlock node) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An additional basic block to model exceptional exits
|
|
||||||
*/
|
|
||||||
public final class ExceptionalExitBlock implements ISSABasicBlock {
|
|
||||||
|
|
||||||
public ControlFlowGraph getDelegate() {
|
|
||||||
return delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.cfg.ISSABasicBlock#getFirstInstructionIndex()
|
|
||||||
*/
|
|
||||||
public int getFirstInstructionIndex() {
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.cfg.ISSABasicBlock#getLastInstructionIndex()
|
|
||||||
*/
|
|
||||||
public int getLastInstructionIndex() {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.cfg.ISSABasicBlock#isCatchBlock()
|
|
||||||
*/
|
|
||||||
public boolean isCatchBlock() {
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.cfg.ISSABasicBlock#isExitBlock()
|
|
||||||
*/
|
|
||||||
public boolean isExitBlock() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.cfg.ISSABasicBlock#isEntryBlock()
|
|
||||||
*/
|
|
||||||
public boolean isEntryBlock() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.cfg.ISSABasicBlock#getMethod()
|
|
||||||
*/
|
|
||||||
public IMethod getMethod() {
|
|
||||||
return delegate.getMethod();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.INodeWithNumber#getGraphNodeId()
|
|
||||||
*/
|
|
||||||
public int getGraphNodeId() {
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.INodeWithNumber#setGraphNodeId(int)
|
|
||||||
*/
|
|
||||||
public void setGraphNodeId(int number) {
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see java.lang.Object#equals(java.lang.Object)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object arg0) {
|
|
||||||
if (arg0 instanceof ExceptionalExitBlock) {
|
|
||||||
ExceptionalExitBlock other = (ExceptionalExitBlock) arg0;
|
|
||||||
return delegate.exit().equals(other.getDelegate().exit());
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return delegate.exit().hashCode() * 8467;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Exceptional Exit[ " + getMethod() + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.cfg.ISSABasicBlock#getNumber()
|
|
||||||
*/
|
|
||||||
public int getNumber() {
|
|
||||||
return getMaxNumber();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterator<SSAPhiInstruction> iteratePhis() {
|
|
||||||
return EmptyIterator.instance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterator<SSAPiInstruction> iteratePis() {
|
|
||||||
return EmptyIterator.instance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterator<IInstruction> iterator() {
|
|
||||||
return EmptyIterator.instance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SSAInstruction getLastInstruction() {
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.cfg.ControlFlowGraph#getMethod()
|
|
||||||
*/
|
|
||||||
public IMethod getMethod() {
|
|
||||||
return delegate.getMethod();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An iterator that substitutes exceptionalExit for exit()
|
|
||||||
*/
|
|
||||||
private class SubstitutionIterator implements Iterator<ISSABasicBlock> {
|
|
||||||
private final Iterator it;
|
|
||||||
|
|
||||||
SubstitutionIterator(Iterator it) {
|
|
||||||
this.it = it;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove() {
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasNext() {
|
|
||||||
return it.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISSABasicBlock next() {
|
|
||||||
ISSABasicBlock n = (ISSABasicBlock) it.next();
|
|
||||||
if (n.getNumber() == delegateExitNumber) {
|
|
||||||
return exceptionalExit;
|
|
||||||
} else {
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.cfg.ControlFlowGraph#getExceptionalSuccessors(com.ibm.wala.cfg.ISSABasicBlock)
|
|
||||||
*/
|
|
||||||
public List<ISSABasicBlock> getExceptionalSuccessors(ISSABasicBlock b) {
|
|
||||||
if (b == null) {
|
|
||||||
throw new IllegalArgumentException("b is null");
|
|
||||||
}
|
|
||||||
if (b.equals(exceptionalExit)) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
} else {
|
|
||||||
List<ISSABasicBlock> c = new ArrayList<ISSABasicBlock>(getSuccNodeCount(b));
|
|
||||||
for (Iterator<ISSABasicBlock> it = delegate.getExceptionalSuccessors(b).iterator(); it.hasNext(); ) {
|
|
||||||
ISSABasicBlock o = it.next();
|
|
||||||
if (o.equals(delegate.exit())) {
|
|
||||||
c.add(exceptionalExit);
|
|
||||||
} else {
|
|
||||||
c.add(o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (DEBUG_LEVEL > 1) {
|
|
||||||
Trace.println("Used delegate " + delegate.getClass());
|
|
||||||
Trace.println("Exceptional succ of " + b + " " + c);
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.cfg.ControlFlowGraph#getNormalSuccessors(com.ibm.wala.cfg.ISSABasicBlock)
|
|
||||||
*/
|
|
||||||
public Collection<ISSABasicBlock> getNormalSuccessors(ISSABasicBlock b) {
|
|
||||||
if (b == null) {
|
|
||||||
throw new IllegalArgumentException("b is null");
|
|
||||||
}
|
|
||||||
if (b.equals(exceptionalExit)) {
|
|
||||||
return Collections.emptySet();
|
|
||||||
} else {
|
|
||||||
return delegate.getNormalSuccessors(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return A distinguished basic block representing the normal exit
|
|
||||||
*/
|
|
||||||
public ISSABasicBlock getNormalExit() {
|
|
||||||
return delegate.exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return A distinguished basic block representing the exceptional exit
|
|
||||||
*/
|
|
||||||
public ISSABasicBlock getExceptionalExit() {
|
|
||||||
return exceptionalExit;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
StringBuffer result = new StringBuffer("Two-Exit CFG");
|
|
||||||
result.append("\ndelegate\n" + delegate);
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.NumberedNodeManager#iterateNodes(com.ibm.wala.util.intset.IntSet)
|
|
||||||
*/
|
|
||||||
public Iterator<ISSABasicBlock> iterateNodes(IntSet s) {
|
|
||||||
return new NumberedNodeIterator<ISSABasicBlock>(s, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeIncomingEdges(ISSABasicBlock node) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeOutgoingEdges(ISSABasicBlock node) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ISSABasicBlock> getExceptionalPredecessors(ISSABasicBlock b) throws UnimplementedError {
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ISSABasicBlock> getNormalPredecessors(ISSABasicBlock b) throws UnimplementedError {
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntSet getSuccNodeNumbers(ISSABasicBlock node) throws UnimplementedError {
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntSet getPredNodeNumbers(ISSABasicBlock node) throws UnimplementedError {
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ControlFlowGraph getDelegate() {
|
|
||||||
return delegate;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,7 +19,6 @@ import java.util.Set;
|
||||||
|
|
||||||
import com.ibm.wala.cfg.ControlFlowGraph;
|
import com.ibm.wala.cfg.ControlFlowGraph;
|
||||||
import com.ibm.wala.cfg.IBasicBlock;
|
import com.ibm.wala.cfg.IBasicBlock;
|
||||||
import com.ibm.wala.cfg.TwoExitCFG;
|
|
||||||
import com.ibm.wala.ipa.callgraph.CGNode;
|
import com.ibm.wala.ipa.callgraph.CGNode;
|
||||||
import com.ibm.wala.ipa.callgraph.CallGraph;
|
import com.ibm.wala.ipa.callgraph.CallGraph;
|
||||||
import com.ibm.wala.ipa.cfg.BasicBlockInContext;
|
import com.ibm.wala.ipa.cfg.BasicBlockInContext;
|
||||||
|
@ -131,8 +130,7 @@ public class PartiallyCollapsedSupergraph extends AbstractGraph<Object> implemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.noCollapse = noCollapse;
|
this.noCollapse = noCollapse;
|
||||||
this.partialIPFG = new InterproceduralCFG(cg, new Filtersection<CGNode>(relevant, new CollectionFilter<CGNode>(noCollapse)),
|
this.partialIPFG = new InterproceduralCFG(cg, new Filtersection<CGNode>(relevant, new CollectionFilter<CGNode>(noCollapse)));
|
||||||
true);
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
if (DEBUG_LEVEL > 0) {
|
||||||
Trace.println("IPFG \n" + partialIPFG.toString());
|
Trace.println("IPFG \n" + partialIPFG.toString());
|
||||||
}
|
}
|
||||||
|
@ -186,13 +184,7 @@ public class PartiallyCollapsedSupergraph extends AbstractGraph<Object> implemen
|
||||||
public Object[] getExitsForProcedure(CGNode node) {
|
public Object[] getExitsForProcedure(CGNode node) {
|
||||||
if (noCollapse.contains(node)) {
|
if (noCollapse.contains(node)) {
|
||||||
ControlFlowGraph<ISSABasicBlock> cfg = partialIPFG.getCFG(node);
|
ControlFlowGraph<ISSABasicBlock> cfg = partialIPFG.getCFG(node);
|
||||||
if (cfg instanceof TwoExitCFG) {
|
return new Object[] { new BasicBlockInContext<ISSABasicBlock>(node, cfg.exit()) };
|
||||||
ISSABasicBlock o1 = ((TwoExitCFG) cfg).getNormalExit();
|
|
||||||
ISSABasicBlock o2 = ((TwoExitCFG) cfg).getExceptionalExit();
|
|
||||||
return new Object[] { new BasicBlockInContext(node, o1), new BasicBlockInContext(node, o2) };
|
|
||||||
} else {
|
|
||||||
return new Object[] { new BasicBlockInContext(node, cfg.exit()) };
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return new Object[] { nodeManager.getCollapsedExit(node) };
|
return new Object[] { nodeManager.getCollapsedExit(node) };
|
||||||
}
|
}
|
||||||
|
@ -251,9 +243,10 @@ public class PartiallyCollapsedSupergraph extends AbstractGraph<Object> implemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public Iterator<? extends Object> getCallSites(Object object) {
|
public Iterator<? extends Object> getCallSites(Object object) {
|
||||||
if (object instanceof BasicBlockInContext) {
|
if (object instanceof BasicBlockInContext) {
|
||||||
return partialIPFG.getCallSites((BasicBlockInContext) object);
|
return partialIPFG.getCallSites((BasicBlockInContext<ISSABasicBlock>) object);
|
||||||
} else {
|
} else {
|
||||||
CGNode n = nodeManager.getProcOfCollapsedNode(object);
|
CGNode n = nodeManager.getProcOfCollapsedNode(object);
|
||||||
return new NonNullSingletonIterator<CollapsedNode>(nodeManager.getCollapsedEntry(n));
|
return new NonNullSingletonIterator<CollapsedNode>(nodeManager.getCollapsedEntry(n));
|
||||||
|
@ -334,28 +327,13 @@ public class PartiallyCollapsedSupergraph extends AbstractGraph<Object> implemen
|
||||||
if (noCollapse.contains(outNode)) {
|
if (noCollapse.contains(outNode)) {
|
||||||
ControlFlowGraph<ISSABasicBlock> cfg = partialIPFG.getCFG(outNode);
|
ControlFlowGraph<ISSABasicBlock> cfg = partialIPFG.getCFG(outNode);
|
||||||
// add an edge to the entry block
|
// add an edge to the entry block
|
||||||
BasicBlockInContext entry = new BasicBlockInContext(outNode, cfg.entry());
|
BasicBlockInContext<ISSABasicBlock> entry = new BasicBlockInContext<ISSABasicBlock>(outNode, cfg.entry());
|
||||||
Set<Object> incoming = MapUtil.findOrCreateSet(incomingTransverseEdges, entry);
|
Set<Object> incoming = MapUtil.findOrCreateSet(incomingTransverseEdges, entry);
|
||||||
incoming.add(n);
|
incoming.add(n);
|
||||||
Set<Object> outgoing = MapUtil.findOrCreateSet(outgoingTransverseEdges, n);
|
Set<Object> outgoing = MapUtil.findOrCreateSet(outgoingTransverseEdges, n);
|
||||||
outgoing.add(entry);
|
outgoing.add(entry);
|
||||||
|
|
||||||
// add the edge representing the return from the call.
|
// add the edge representing the return from the call.
|
||||||
if (cfg instanceof TwoExitCFG) {
|
|
||||||
TwoExitCFG t2 = (TwoExitCFG) cfg;
|
|
||||||
BasicBlockInContext exit = new BasicBlockInContext(outNode, t2.getNormalExit());
|
|
||||||
Object retSite = nodeManager.getCollapsedExit(node);
|
|
||||||
incoming = MapUtil.findOrCreateSet(incomingTransverseEdges, retSite);
|
|
||||||
incoming.add(exit);
|
|
||||||
outgoing = MapUtil.findOrCreateSet(outgoingTransverseEdges, exit);
|
|
||||||
outgoing.add(retSite);
|
|
||||||
|
|
||||||
exit = new BasicBlockInContext(outNode, t2.getExceptionalExit());
|
|
||||||
incoming.add(exit);
|
|
||||||
outgoing = MapUtil.findOrCreateSet(outgoingTransverseEdges, exit);
|
|
||||||
outgoing.add(retSite);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
IBasicBlock exit = cfg.exit();
|
IBasicBlock exit = cfg.exit();
|
||||||
Object retSite = nodeManager.getCollapsedExit(node);
|
Object retSite = nodeManager.getCollapsedExit(node);
|
||||||
incoming = MapUtil.findOrCreateSet(incomingTransverseEdges, retSite);
|
incoming = MapUtil.findOrCreateSet(incomingTransverseEdges, retSite);
|
||||||
|
@ -366,7 +344,6 @@ public class PartiallyCollapsedSupergraph extends AbstractGraph<Object> implemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
@ -840,11 +817,7 @@ public class PartiallyCollapsedSupergraph extends AbstractGraph<Object> implemen
|
||||||
if (noCollapse.contains(n)) {
|
if (noCollapse.contains(n)) {
|
||||||
// p is cg node which is expanded in the IPFG
|
// p is cg node which is expanded in the IPFG
|
||||||
ControlFlowGraph cfg = partialIPFG.getCFG(n);
|
ControlFlowGraph cfg = partialIPFG.getCFG(n);
|
||||||
if (cfg instanceof TwoExitCFG) {
|
|
||||||
return ((TwoExitCFG) cfg).getNormalExit();
|
|
||||||
} else {
|
|
||||||
return cfg.exit();
|
return cfg.exit();
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// p is a collapsed node, for which all blocks map to the node itself;
|
// p is a collapsed node, for which all blocks map to the node itself;
|
||||||
return nodeManager.getCollapsedExit(n);
|
return nodeManager.getCollapsedExit(n);
|
||||||
|
@ -854,6 +827,7 @@ public class PartiallyCollapsedSupergraph extends AbstractGraph<Object> implemen
|
||||||
/*
|
/*
|
||||||
* @see com.ibm.wala.dataflow.IFDS.ISupergraph#isReturn(java.lang.Object)
|
* @see com.ibm.wala.dataflow.IFDS.ISupergraph#isReturn(java.lang.Object)
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public boolean isReturn(Object object) {
|
public boolean isReturn(Object object) {
|
||||||
if (object instanceof BasicBlockInContext) {
|
if (object instanceof BasicBlockInContext) {
|
||||||
return partialIPFG.isReturn((BasicBlockInContext) object);
|
return partialIPFG.isReturn((BasicBlockInContext) object);
|
||||||
|
|
|
@ -12,13 +12,11 @@ package com.ibm.wala.ipa.callgraph;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import com.ibm.wala.cfg.ControlFlowGraph;
|
|
||||||
import com.ibm.wala.classLoader.CallSiteReference;
|
import com.ibm.wala.classLoader.CallSiteReference;
|
||||||
import com.ibm.wala.classLoader.IMethod;
|
import com.ibm.wala.classLoader.IMethod;
|
||||||
import com.ibm.wala.classLoader.NewSiteReference;
|
import com.ibm.wala.classLoader.NewSiteReference;
|
||||||
import com.ibm.wala.ssa.DefUse;
|
import com.ibm.wala.ssa.DefUse;
|
||||||
import com.ibm.wala.ssa.IR;
|
import com.ibm.wala.ssa.IR;
|
||||||
import com.ibm.wala.ssa.ISSABasicBlock;
|
|
||||||
import com.ibm.wala.util.graph.INodeWithNumber;
|
import com.ibm.wala.util.graph.INodeWithNumber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,17 +57,6 @@ public interface CGNode extends INodeWithNumber, ContextItem {
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public boolean addTarget(CallSiteReference site, CGNode target);
|
public boolean addTarget(CallSiteReference site, CGNode target);
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @return the number of nodes that the call site current may resolve
|
|
||||||
// * to
|
|
||||||
// */
|
|
||||||
// public int getNumberOfTargets(CallSiteReference site);
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @return the call graph in which this node dwells
|
|
||||||
// */
|
|
||||||
// public CallGraph getCallGraph();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the "default" IR for this node used by the governing call graph
|
* @return the "default" IR for this node used by the governing call graph
|
||||||
*/
|
*/
|
||||||
|
@ -80,11 +67,6 @@ public interface CGNode extends INodeWithNumber, ContextItem {
|
||||||
*/
|
*/
|
||||||
public DefUse getDU();
|
public DefUse getDU();
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a CFG that represents the node, or null if it's an unmodelled native method
|
|
||||||
*/
|
|
||||||
public ControlFlowGraph<ISSABasicBlock> getCFG();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return an Iterator of the types that may be allocated by a given
|
* @return an Iterator of the types that may be allocated by a given
|
||||||
* method in a given context.
|
* method in a given context.
|
||||||
|
|
|
@ -0,0 +1,694 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* 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.cfg;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.ibm.wala.cfg.ControlFlowGraph;
|
||||||
|
import com.ibm.wala.cfg.IBasicBlock;
|
||||||
|
import com.ibm.wala.classLoader.CallSiteReference;
|
||||||
|
import com.ibm.wala.ipa.callgraph.CGNode;
|
||||||
|
import com.ibm.wala.ipa.callgraph.CallGraph;
|
||||||
|
import com.ibm.wala.shrikeBT.IInstruction;
|
||||||
|
import com.ibm.wala.shrikeBT.IInvokeInstruction;
|
||||||
|
import com.ibm.wala.shrikeBT.InvokeInstruction;
|
||||||
|
import com.ibm.wala.ssa.ISSABasicBlock;
|
||||||
|
import com.ibm.wala.types.ClassLoaderReference;
|
||||||
|
import com.ibm.wala.types.MethodReference;
|
||||||
|
import com.ibm.wala.util.Function;
|
||||||
|
import com.ibm.wala.util.IndiscriminateFilter;
|
||||||
|
import com.ibm.wala.util.MapIterator;
|
||||||
|
import com.ibm.wala.util.collections.Filter;
|
||||||
|
import com.ibm.wala.util.collections.FilterIterator;
|
||||||
|
import com.ibm.wala.util.collections.HashSetFactory;
|
||||||
|
import com.ibm.wala.util.debug.Assertions;
|
||||||
|
import com.ibm.wala.util.debug.Trace;
|
||||||
|
import com.ibm.wala.util.debug.UnimplementedError;
|
||||||
|
import com.ibm.wala.util.graph.GraphIntegrity;
|
||||||
|
import com.ibm.wala.util.graph.NumberedGraph;
|
||||||
|
import com.ibm.wala.util.graph.GraphIntegrity.UnsoundGraphException;
|
||||||
|
import com.ibm.wala.util.graph.impl.SlowSparseNumberedGraph;
|
||||||
|
import com.ibm.wala.util.intset.BitVector;
|
||||||
|
import com.ibm.wala.util.intset.IntSet;
|
||||||
|
import com.ibm.wala.util.perf.EngineTimings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Interprocedural control-flow graph.
|
||||||
|
*
|
||||||
|
* TODO: think about a better implementation; perhaps a lazy view of the
|
||||||
|
* constituent CFGs Lots of ways this can be optimized?
|
||||||
|
*
|
||||||
|
* @author sfink
|
||||||
|
* @author Julian Dolby
|
||||||
|
*/
|
||||||
|
public abstract class AbstractInterproceduralCFG<T extends ISSABasicBlock> implements NumberedGraph<BasicBlockInContext> {
|
||||||
|
|
||||||
|
private static final int DEBUG_LEVEL = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should the graph include call-to-return edges?
|
||||||
|
*/
|
||||||
|
private final static boolean CALL_TO_RETURN_EDGES = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Graph implementation we delegate to.
|
||||||
|
*/
|
||||||
|
final private NumberedGraph<BasicBlockInContext> G = new SlowSparseNumberedGraph<BasicBlockInContext>(2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Governing call graph
|
||||||
|
*/
|
||||||
|
private final CallGraph cg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter that determines relevant call graph nodes
|
||||||
|
*/
|
||||||
|
private final Filter<CGNode> relevant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a cache: for each node (Basic Block), does that block end in a call?
|
||||||
|
*/
|
||||||
|
private final BitVector hasCallVector = new BitVector();
|
||||||
|
|
||||||
|
protected abstract ControlFlowGraph<T> getCFG(CGNode n);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an Interprocedural CFG from a call graph. This version defaults to
|
||||||
|
* using whatever CFGs the call graph provides by default, and includes all
|
||||||
|
* nodes in the call graph.
|
||||||
|
*
|
||||||
|
* @param CG
|
||||||
|
* the call graph
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public AbstractInterproceduralCFG(CallGraph CG) {
|
||||||
|
this(CG, IndiscriminateFilter.singleton());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an Interprocedural CFG from a call graph.
|
||||||
|
*
|
||||||
|
* @param CG
|
||||||
|
* the call graph
|
||||||
|
* @param relevant
|
||||||
|
* a filter which accepts those call graph nodes which should be
|
||||||
|
* included in the I-CFG. Other nodes are ignored.
|
||||||
|
*/
|
||||||
|
public AbstractInterproceduralCFG(CallGraph CG, Filter<CGNode> relevant) {
|
||||||
|
|
||||||
|
EngineTimings.startVirtual("InterproceduralCFG.<init>");
|
||||||
|
this.cg = CG;
|
||||||
|
this.relevant = relevant;
|
||||||
|
|
||||||
|
// create nodes for IPCFG
|
||||||
|
createNodes();
|
||||||
|
|
||||||
|
// create edges for IPCFG
|
||||||
|
createEdges();
|
||||||
|
|
||||||
|
if (DEBUG_LEVEL > 1) {
|
||||||
|
try {
|
||||||
|
GraphIntegrity.check(this);
|
||||||
|
} catch (UnsoundGraphException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Assertions.UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EngineTimings.finishVirtual("InterproceduralCFG.<init>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create the edges that define this graph.
|
||||||
|
*/
|
||||||
|
private void createEdges() {
|
||||||
|
for (Iterator ns = cg.iterator(); ns.hasNext();) {
|
||||||
|
CGNode n = (CGNode) ns.next();
|
||||||
|
if (relevant.accepts(n)) {
|
||||||
|
// retrieve a cfg for node n.
|
||||||
|
ControlFlowGraph<T> cfg = getCFG(n);
|
||||||
|
if (cfg == null) {
|
||||||
|
// n is an unmodelled native method
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
IInstruction[] instrs = cfg.getInstructions();
|
||||||
|
// create edges for node n.
|
||||||
|
for (Iterator<T> bbs = cfg.iterator(); bbs.hasNext();) {
|
||||||
|
T bb = bbs.next();
|
||||||
|
// entry node gets edges from callers
|
||||||
|
if (bb == cfg.entry()) {
|
||||||
|
addEdgesToEntryBlock(n, bb);
|
||||||
|
}
|
||||||
|
// other instructions get edges from predecessors,
|
||||||
|
// with special handling for calls.
|
||||||
|
else {
|
||||||
|
addEdgesToNonEntryBlock(n, cfg, instrs, bb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create the nodes. side effect: populates the hasCallVector
|
||||||
|
*/
|
||||||
|
private void createNodes() {
|
||||||
|
for (Iterator ns = cg.iterator(); ns.hasNext();) {
|
||||||
|
CGNode n = (CGNode) ns.next();
|
||||||
|
if (relevant.accepts(n)) {
|
||||||
|
if (DEBUG_LEVEL > 0) {
|
||||||
|
Trace.println("Found a relevant node: " + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieve a cfg for node n.
|
||||||
|
ControlFlowGraph<? extends T> cfg = getCFG(n);
|
||||||
|
if (cfg != null) {
|
||||||
|
// create a node for each basic block.
|
||||||
|
addNodeForEachBasicBlock(cfg, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add edges to the IPCFG for the incoming edges incident on a basic block bb
|
||||||
|
*
|
||||||
|
* @param n
|
||||||
|
* a call graph node
|
||||||
|
* @param cfg
|
||||||
|
* the CFG for n
|
||||||
|
* @param instrs
|
||||||
|
* the instructions for node n
|
||||||
|
* @param bb
|
||||||
|
* a basic block in the CFG
|
||||||
|
*/
|
||||||
|
private void addEdgesToNonEntryBlock(CGNode n, ControlFlowGraph<T> cfg, IInstruction[] instrs, T bb) {
|
||||||
|
|
||||||
|
if (DEBUG_LEVEL > 0) {
|
||||||
|
Trace.println("addEdgesForNonEntryBlock: " + bb);
|
||||||
|
Trace.println("cfg class: " + cfg.getClass());
|
||||||
|
Trace.println("nPred: " + cfg.getPredNodeCount(bb));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Iterator<? extends T> ps = cfg.getPredNodes(bb); ps.hasNext();) {
|
||||||
|
T pb = ps.next();
|
||||||
|
if (DEBUG_LEVEL > 0) {
|
||||||
|
Trace.println("Consider previous block: " + pb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pb.getLastInstructionIndex() < 0) {
|
||||||
|
// pb is the entry block for a cfg with
|
||||||
|
// no instructions.
|
||||||
|
BasicBlockInContext p = new BasicBlockInContext<T>(n, pb);
|
||||||
|
BasicBlockInContext b = new BasicBlockInContext<T>(n, bb);
|
||||||
|
G.addEdge(p, b);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = pb.getLastInstructionIndex();
|
||||||
|
IInstruction inst = instrs[index];
|
||||||
|
if (DEBUG_LEVEL > 0) {
|
||||||
|
Trace.println("Last instruction is : " + inst);
|
||||||
|
}
|
||||||
|
if (inst instanceof IInvokeInstruction) {
|
||||||
|
// a previous instruction is a call instruction. If necessary,
|
||||||
|
// add an edge from the exit() of each target of the call.
|
||||||
|
IInvokeInstruction call = (IInvokeInstruction) inst;
|
||||||
|
CallSiteReference site = makeCallSiteReference(n.getMethod().getDeclaringClass().getClassLoader().getReference(), cfg
|
||||||
|
.getProgramCounter(index), call);
|
||||||
|
if (DEBUG_LEVEL > 0) {
|
||||||
|
Trace.println("got Site: " + site);
|
||||||
|
}
|
||||||
|
boolean irrelevantTargets = false;
|
||||||
|
for (Iterator ts = cg.getPossibleTargets(n, site).iterator(); ts.hasNext();) {
|
||||||
|
CGNode tn = (CGNode) ts.next();
|
||||||
|
if (!relevant.accepts(tn)) {
|
||||||
|
if (DEBUG_LEVEL > 0) {
|
||||||
|
Trace.println("Irrelevant target: " + tn);
|
||||||
|
}
|
||||||
|
irrelevantTargets = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG_LEVEL > 0) {
|
||||||
|
Trace.println("Relevant target: " + tn);
|
||||||
|
}
|
||||||
|
// add an edge from tn exit to this node
|
||||||
|
ControlFlowGraph<? extends T> tcfg = getCFG(tn);
|
||||||
|
// tcfg might be null if tn is an unmodelled native method
|
||||||
|
if (tcfg != null) {
|
||||||
|
addEdgesFromExitToReturn(n, bb, tn, tcfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (irrelevantTargets || CALL_TO_RETURN_EDGES) {
|
||||||
|
// at least one call target was ignored. So add a "normal" edge
|
||||||
|
// from the predecessor block to this block.
|
||||||
|
BasicBlockInContext p = new BasicBlockInContext<ISSABasicBlock>(n, pb);
|
||||||
|
BasicBlockInContext b = new BasicBlockInContext<ISSABasicBlock>(n, bb);
|
||||||
|
G.addEdge(p, b);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// previous instruction is not a call instruction.
|
||||||
|
BasicBlockInContext p = new BasicBlockInContext<ISSABasicBlock>(n, pb);
|
||||||
|
BasicBlockInContext b = new BasicBlockInContext<ISSABasicBlock>(n, bb);
|
||||||
|
if (Assertions.verifyAssertions) {
|
||||||
|
if (!G.containsNode(p) || !G.containsNode(b)) {
|
||||||
|
Assertions._assert(G.containsNode(p), "IPCFG does not contain " + p);
|
||||||
|
Assertions._assert(G.containsNode(b), "IPCFG does not contain " + b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
G.addEdge(p, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CallSiteReference makeCallSiteReference(ClassLoaderReference loader, int pc, IInvokeInstruction call)
|
||||||
|
throws IllegalArgumentException, IllegalArgumentException {
|
||||||
|
|
||||||
|
if (call == null) {
|
||||||
|
throw new IllegalArgumentException("call == null");
|
||||||
|
}
|
||||||
|
if (!(call instanceof com.ibm.wala.ssa.SSAInvokeInstruction) && !(call instanceof com.ibm.wala.shrikeBT.InvokeInstruction)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"(not ( call instanceof com.ibm.wala.ssa.SSAInvokeInstruction ) ) and (not ( call instanceof com.ibm.wala.shrikeBT.InvokeInstruction ) )");
|
||||||
|
}
|
||||||
|
CallSiteReference site = null;
|
||||||
|
if (call instanceof InvokeInstruction) {
|
||||||
|
InvokeInstruction c = (InvokeInstruction) call;
|
||||||
|
site = CallSiteReference.make(pc, MethodReference.findOrCreate(loader, c.getClassType(), c.getMethodName(), c
|
||||||
|
.getMethodSignature()), call.getInvocationCode());
|
||||||
|
} else {
|
||||||
|
com.ibm.wala.ssa.SSAInvokeInstruction c = (com.ibm.wala.ssa.SSAInvokeInstruction) call;
|
||||||
|
site = CallSiteReference.make(pc, c.getDeclaredTarget(), call.getInvocationCode());
|
||||||
|
}
|
||||||
|
return site;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an edge from the exit() block of a callee to a return site in the
|
||||||
|
* caller
|
||||||
|
*
|
||||||
|
* @param returnBlock
|
||||||
|
* the return site for a call
|
||||||
|
* @param targetCFG
|
||||||
|
* the called method
|
||||||
|
*/
|
||||||
|
private void addEdgesFromExitToReturn(CGNode caller, T returnBlock, CGNode target,
|
||||||
|
ControlFlowGraph<? extends T> targetCFG) {
|
||||||
|
T texit = targetCFG.exit();
|
||||||
|
BasicBlockInContext exit = new BasicBlockInContext<T>(target, texit);
|
||||||
|
BasicBlockInContext ret = new BasicBlockInContext<T>(caller, returnBlock);
|
||||||
|
if (Assertions.verifyAssertions) {
|
||||||
|
if (!G.containsNode(exit) || !G.containsNode(ret)) {
|
||||||
|
Assertions._assert(G.containsNode(exit), "IPCFG does not contain " + exit);
|
||||||
|
Assertions._assert(G.containsNode(ret), "IPCFG does not contain " + ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
G.addEdge(exit, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the incoming edges to the entry() block for a call graph node.
|
||||||
|
*
|
||||||
|
* @param n
|
||||||
|
* a node in the call graph
|
||||||
|
* @param bb
|
||||||
|
* the entry() block for n
|
||||||
|
*/
|
||||||
|
private void addEdgesToEntryBlock(CGNode n, T bb) {
|
||||||
|
|
||||||
|
if (DEBUG_LEVEL > 0) {
|
||||||
|
Trace.println("addEdgesToEntryBlock " + bb);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Iterator callers = cg.getPredNodes(n); callers.hasNext();) {
|
||||||
|
CGNode caller = (CGNode) callers.next();
|
||||||
|
if (DEBUG_LEVEL > 0) {
|
||||||
|
Trace.println("got caller " + caller);
|
||||||
|
}
|
||||||
|
if (relevant.accepts(caller)) {
|
||||||
|
if (DEBUG_LEVEL > 0) {
|
||||||
|
Trace.println("caller " + caller + "is relevant");
|
||||||
|
}
|
||||||
|
ControlFlowGraph<? extends T> ccfg = getCFG(caller);
|
||||||
|
IInstruction[] cinsts = ccfg.getInstructions();
|
||||||
|
|
||||||
|
if (DEBUG_LEVEL > 0) {
|
||||||
|
Trace.println("Visiting " + cinsts.length + " instructions");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < cinsts.length; i++) {
|
||||||
|
if (cinsts[i] instanceof IInvokeInstruction) {
|
||||||
|
if (DEBUG_LEVEL > 0) {
|
||||||
|
Trace.println("Checking invokeinstruction: " + cinsts[i]);
|
||||||
|
}
|
||||||
|
IInvokeInstruction call = (IInvokeInstruction) cinsts[i];
|
||||||
|
CallSiteReference site = makeCallSiteReference(n.getMethod().getDeclaringClass().getClassLoader().getReference(), ccfg
|
||||||
|
.getProgramCounter(i), call);
|
||||||
|
if (cg.getPossibleTargets(caller, site).contains(n)) {
|
||||||
|
if (DEBUG_LEVEL > 0) {
|
||||||
|
Trace.println("Adding edge " + ccfg.getBlockForInstruction(i) + " to " + bb);
|
||||||
|
}
|
||||||
|
T callerBB = ccfg.getBlockForInstruction(i);
|
||||||
|
BasicBlockInContext b1 = new BasicBlockInContext<T>(caller, callerBB);
|
||||||
|
BasicBlockInContext b2 = new BasicBlockInContext<T>(n, bb);
|
||||||
|
G.addEdge(b1, b2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a node to the IPCFG for each node in a CFG. side effect: populates the
|
||||||
|
* hasCallVector
|
||||||
|
*
|
||||||
|
* @param cfg
|
||||||
|
* a control-flow graph
|
||||||
|
*/
|
||||||
|
private void addNodeForEachBasicBlock(ControlFlowGraph<? extends T> cfg, CGNode N) {
|
||||||
|
for (Iterator<? extends T> bbs = cfg.iterator(); bbs.hasNext();) {
|
||||||
|
T bb = bbs.next();
|
||||||
|
if (DEBUG_LEVEL > 0) {
|
||||||
|
Trace.println("IPCFG Add basic block " + bb);
|
||||||
|
}
|
||||||
|
BasicBlockInContext b = new BasicBlockInContext<T>(N, bb);
|
||||||
|
G.addNode(b);
|
||||||
|
if (hasCall(b, cfg)) {
|
||||||
|
hasCallVector.set(getNumber(b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the original CFG from whence B came
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if B == null
|
||||||
|
*/
|
||||||
|
public ControlFlowGraph<T> getCFG(BasicBlockInContext B) throws IllegalArgumentException {
|
||||||
|
if (B == null) {
|
||||||
|
throw new IllegalArgumentException("B == null");
|
||||||
|
}
|
||||||
|
return getCFG(getCGNode(B));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the original CGNode from whence B came
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if B == null
|
||||||
|
*/
|
||||||
|
public CGNode getCGNode(BasicBlockInContext B) throws IllegalArgumentException {
|
||||||
|
if (B == null) {
|
||||||
|
throw new IllegalArgumentException("B == null");
|
||||||
|
}
|
||||||
|
return B.getNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see com.ibm.wala.util.graph.Graph#removeNodeAndEdges(com.ibm.wala.util.graph.Node)
|
||||||
|
*/
|
||||||
|
public void removeNodeAndEdges(BasicBlockInContext N) throws UnsupportedOperationException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see com.ibm.wala.util.graph.NodeManager#iterateNodes()
|
||||||
|
*/
|
||||||
|
public Iterator<BasicBlockInContext> iterator() {
|
||||||
|
return G.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see com.ibm.wala.util.graph.NodeManager#getNumberOfNodes()
|
||||||
|
*/
|
||||||
|
public int getNumberOfNodes() {
|
||||||
|
return G.getNumberOfNodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see com.ibm.wala.util.graph.NodeManager#addNode(com.ibm.wala.util.graph.Node)
|
||||||
|
*/
|
||||||
|
public void addNode(BasicBlockInContext n) throws UnsupportedOperationException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see com.ibm.wala.util.graph.NodeManager#removeNode(com.ibm.wala.util.graph.Node)
|
||||||
|
*/
|
||||||
|
public void removeNode(BasicBlockInContext n) throws UnsupportedOperationException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see com.ibm.wala.util.graph.EdgeManager#getPredNodes(com.ibm.wala.util.graph.Node)
|
||||||
|
*/
|
||||||
|
public Iterator<? extends BasicBlockInContext> getPredNodes(BasicBlockInContext N) {
|
||||||
|
return G.getPredNodes(N);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see com.ibm.wala.util.graph.EdgeManager#getPredNodeCount(com.ibm.wala.util.graph.Node)
|
||||||
|
*/
|
||||||
|
public int getPredNodeCount(BasicBlockInContext N) {
|
||||||
|
return G.getPredNodeCount(N);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see com.ibm.wala.util.graph.EdgeManager#getSuccNodes(com.ibm.wala.util.graph.Node)
|
||||||
|
*/
|
||||||
|
public Iterator<? extends BasicBlockInContext> getSuccNodes(BasicBlockInContext N) {
|
||||||
|
return G.getSuccNodes(N);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see com.ibm.wala.util.graph.EdgeManager#getSuccNodeCount(com.ibm.wala.util.graph.Node)
|
||||||
|
*/
|
||||||
|
public int getSuccNodeCount(BasicBlockInContext N) {
|
||||||
|
return G.getSuccNodeCount(N);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see com.ibm.wala.util.graph.EdgeManager#addEdge(com.ibm.wala.util.graph.Node,
|
||||||
|
* com.ibm.wala.util.graph.Node)
|
||||||
|
*/
|
||||||
|
public void addEdge(BasicBlockInContext src, BasicBlockInContext dst) throws UnsupportedOperationException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeEdge(BasicBlockInContext src, BasicBlockInContext dst) throws UnsupportedOperationException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see com.ibm.wala.util.graph.EdgeManager#removeEdges(com.ibm.wala.util.graph.Node)
|
||||||
|
*/
|
||||||
|
public void removeAllIncidentEdges(BasicBlockInContext node) throws UnsupportedOperationException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return G.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @see com.ibm.wala.util.graph.Graph#containsNode(com.ibm.wala.util.graph.Node)
|
||||||
|
*/
|
||||||
|
public boolean containsNode(BasicBlockInContext N) {
|
||||||
|
return G.containsNode(N);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param B
|
||||||
|
* @return true iff basic block B ends in a call instuction
|
||||||
|
*/
|
||||||
|
public boolean hasCall(BasicBlockInContext B) {
|
||||||
|
if (Assertions.verifyAssertions) {
|
||||||
|
if (!containsNode(B)) {
|
||||||
|
Assertions._assert(containsNode(B));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasCallVector.get(getNumber(B));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true iff basic block B ends in a call instuction
|
||||||
|
*/
|
||||||
|
private boolean hasCall(BasicBlockInContext B, ControlFlowGraph cfg) {
|
||||||
|
IInstruction[] statements = cfg.getInstructions();
|
||||||
|
|
||||||
|
int lastIndex = B.getLastInstructionIndex();
|
||||||
|
if (lastIndex >= 0) {
|
||||||
|
|
||||||
|
if (Assertions.verifyAssertions) {
|
||||||
|
if (statements.length <= lastIndex) {
|
||||||
|
System.err.println(statements.length);
|
||||||
|
System.err.println(cfg);
|
||||||
|
Assertions._assert(lastIndex < statements.length, "bad BB " + B + " and CFG for " + getCGNode(B));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IInstruction last = statements[lastIndex];
|
||||||
|
return (last instanceof IInvokeInstruction);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param B
|
||||||
|
* @return the set of CGNodes that B may call, according to the governing call
|
||||||
|
* graph.
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if B is null
|
||||||
|
*/
|
||||||
|
public Set<CGNode> getCallTargets(BasicBlockInContext B) {
|
||||||
|
if (B == null) {
|
||||||
|
throw new IllegalArgumentException("B is null");
|
||||||
|
}
|
||||||
|
ControlFlowGraph cfg = getCFG(B);
|
||||||
|
return getCallTargets(B, cfg, getCGNode(B));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the set of CGNodes that B may call, according to the governing call
|
||||||
|
* graph.
|
||||||
|
*/
|
||||||
|
private Set<CGNode> getCallTargets(IBasicBlock B, ControlFlowGraph cfg, CGNode Bnode) {
|
||||||
|
IInstruction[] statements = cfg.getInstructions();
|
||||||
|
IInvokeInstruction call = (IInvokeInstruction) statements[B.getLastInstructionIndex()];
|
||||||
|
int pc = cfg.getProgramCounter(B.getLastInstructionIndex());
|
||||||
|
CallSiteReference site = makeCallSiteReference(B.getMethod().getDeclaringClass().getClassLoader().getReference(), pc, call);
|
||||||
|
HashSet<CGNode> result = HashSetFactory.make(cg.getNumberOfTargets(Bnode, site));
|
||||||
|
for (Iterator<CGNode> it = cg.getPossibleTargets(Bnode, site).iterator(); it.hasNext();) {
|
||||||
|
CGNode target = it.next();
|
||||||
|
result.add(target);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeIncomingEdges(BasicBlockInContext node) throws UnsupportedOperationException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeOutgoingEdges(BasicBlockInContext node) throws UnsupportedOperationException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasEdge(BasicBlockInContext src, BasicBlockInContext dst) {
|
||||||
|
return G.hasEdge(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumber(BasicBlockInContext N) {
|
||||||
|
return G.getNumber(N);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BasicBlockInContext getNode(int number) throws UnimplementedError {
|
||||||
|
Assertions.UNREACHABLE();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxNumber() {
|
||||||
|
return G.getMaxNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator<BasicBlockInContext> iterateNodes(IntSet s) throws UnimplementedError {
|
||||||
|
Assertions.UNREACHABLE();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntSet getSuccNodeNumbers(BasicBlockInContext node) {
|
||||||
|
return G.getSuccNodeNumbers(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntSet getPredNodeNumbers(BasicBlockInContext node) {
|
||||||
|
return G.getPredNodeNumbers(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BasicBlockInContext<T> getEntry(CGNode n) {
|
||||||
|
ControlFlowGraph<? extends T> cfg = getCFG(n);
|
||||||
|
T entry = cfg.entry();
|
||||||
|
return new BasicBlockInContext<T>(n, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BasicBlockInContext<T> getExit(CGNode n) {
|
||||||
|
ControlFlowGraph<? extends T> cfg = getCFG(n);
|
||||||
|
T entry = cfg.exit();
|
||||||
|
return new BasicBlockInContext<T>(n, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bb
|
||||||
|
* node in the IPCFG that ends in a call
|
||||||
|
* @return the nodes that are return sites for this call.
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if bb is null
|
||||||
|
*/
|
||||||
|
public Iterator<BasicBlockInContext> getReturnSites(BasicBlockInContext bb) {
|
||||||
|
if (bb == null) {
|
||||||
|
throw new IllegalArgumentException("bb is null");
|
||||||
|
}
|
||||||
|
final CGNode node = bb.getNode();
|
||||||
|
|
||||||
|
// a successor node is a return site if it is in the same
|
||||||
|
// procedure, and is not the entry() node.
|
||||||
|
Filter isReturn = new Filter() {
|
||||||
|
public boolean accepts(Object o) {
|
||||||
|
if (Assertions.verifyAssertions) {
|
||||||
|
Assertions._assert(o instanceof BasicBlockInContext);
|
||||||
|
}
|
||||||
|
BasicBlockInContext other = (BasicBlockInContext) o;
|
||||||
|
return !other.isEntryBlock() && node.equals(other.getNode());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return new FilterIterator<BasicBlockInContext>(getSuccNodes(bb), isReturn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator<BasicBlockInContext> getCallSites(BasicBlockInContext<T> bb) {
|
||||||
|
if (bb == null) {
|
||||||
|
throw new IllegalArgumentException("bb is null");
|
||||||
|
}
|
||||||
|
ControlFlowGraph<T> cfg = getCFG(bb);
|
||||||
|
Iterator<? extends T> it = cfg.getPredNodes(bb.getDelegate());
|
||||||
|
final CGNode node = bb.getNode();
|
||||||
|
Function<T, BasicBlockInContext<T>> toContext = new Function<T, BasicBlockInContext<T>>() {
|
||||||
|
public BasicBlockInContext<T> apply(T object) {
|
||||||
|
T b = object;
|
||||||
|
return new BasicBlockInContext<T>(node, b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
MapIterator<T, BasicBlockInContext<T>> m = new MapIterator<T, BasicBlockInContext<T>>(it, toContext);
|
||||||
|
return new FilterIterator<BasicBlockInContext>(m, isCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Filter isCall = new Filter() {
|
||||||
|
public boolean accepts(Object o) {
|
||||||
|
return hasCall((BasicBlockInContext) o);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public boolean isReturn(BasicBlockInContext<T> bb) throws IllegalArgumentException {
|
||||||
|
if (bb == null) {
|
||||||
|
throw new IllegalArgumentException("bb == null");
|
||||||
|
}
|
||||||
|
ControlFlowGraph<T> cfg = getCFG(bb);
|
||||||
|
for (Iterator<? extends T> it = cfg.getPredNodes(bb.getDelegate()); it.hasNext();) {
|
||||||
|
T b = it.next();
|
||||||
|
if (hasCall(new BasicBlockInContext<T>(bb.getNode(), b))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,12 +25,12 @@ import com.ibm.wala.util.graph.impl.NodeWithNumber;
|
||||||
*
|
*
|
||||||
* @author sfink
|
* @author sfink
|
||||||
*/
|
*/
|
||||||
public final class BasicBlockInContext extends NodeWithNumber implements IBasicBlock {
|
public final class BasicBlockInContext<T extends ISSABasicBlock> extends NodeWithNumber implements IBasicBlock {
|
||||||
private final ISSABasicBlock delegate;
|
private final T delegate;
|
||||||
|
|
||||||
private final CGNode node;
|
private final CGNode node;
|
||||||
|
|
||||||
public BasicBlockInContext(CGNode node, ISSABasicBlock bb) {
|
public BasicBlockInContext(CGNode node, T bb) {
|
||||||
this.delegate = bb;
|
this.delegate = bb;
|
||||||
this.node = node;
|
this.node = node;
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ public final class BasicBlockInContext extends NodeWithNumber implements IBasicB
|
||||||
return delegate.hashCode() + 229 * node.hashCode();
|
return delegate.hashCode() + 229 * node.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ISSABasicBlock getDelegate() {
|
public T getDelegate() {
|
||||||
return delegate;
|
return delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* 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.cfg;
|
||||||
|
|
||||||
|
import com.ibm.wala.cfg.ControlFlowGraph;
|
||||||
|
import com.ibm.wala.ipa.callgraph.CGNode;
|
||||||
|
import com.ibm.wala.ipa.callgraph.CallGraph;
|
||||||
|
import com.ibm.wala.ssa.IR;
|
||||||
|
import com.ibm.wala.ssa.analysis.ExplodedControlFlowGraph;
|
||||||
|
import com.ibm.wala.ssa.analysis.ExplodedControlFlowGraph.ExplodedBasicBlock;
|
||||||
|
import com.ibm.wala.util.collections.Filtersection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Interprocedural control-flow graph.
|
||||||
|
*
|
||||||
|
* TODO: think about a better implementation; perhaps a lazy view of the
|
||||||
|
* constituent CFGs Lots of ways this can be optimized?
|
||||||
|
*
|
||||||
|
* @author sfink
|
||||||
|
* @author Julian Dolby
|
||||||
|
*/
|
||||||
|
public class ExplodedInterproceduralCFG extends AbstractInterproceduralCFG<ExplodedBasicBlock> {
|
||||||
|
|
||||||
|
|
||||||
|
public static ExplodedInterproceduralCFG make(CallGraph CG) {
|
||||||
|
return new ExplodedInterproceduralCFG(CG);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExplodedInterproceduralCFG(CallGraph CG) {
|
||||||
|
super(CG);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExplodedInterproceduralCFG(CallGraph cg, Filtersection<CGNode> filtersection) {
|
||||||
|
super(cg,filtersection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the cfg for n, or null if none found
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if n == null
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ControlFlowGraph<ExplodedBasicBlock> getCFG(CGNode n) throws IllegalArgumentException {
|
||||||
|
if (n == null) {
|
||||||
|
throw new IllegalArgumentException("n == null");
|
||||||
|
}
|
||||||
|
IR ir = n.getIR();
|
||||||
|
return ExplodedControlFlowGraph.make(ir);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -10,38 +10,11 @@
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package com.ibm.wala.ipa.cfg;
|
package com.ibm.wala.ipa.cfg;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import com.ibm.wala.cfg.ControlFlowGraph;
|
import com.ibm.wala.cfg.ControlFlowGraph;
|
||||||
import com.ibm.wala.cfg.IBasicBlock;
|
|
||||||
import com.ibm.wala.cfg.TwoExitCFG;
|
|
||||||
import com.ibm.wala.classLoader.CallSiteReference;
|
|
||||||
import com.ibm.wala.ipa.callgraph.CGNode;
|
import com.ibm.wala.ipa.callgraph.CGNode;
|
||||||
import com.ibm.wala.ipa.callgraph.CallGraph;
|
import com.ibm.wala.ipa.callgraph.CallGraph;
|
||||||
import com.ibm.wala.shrikeBT.IInstruction;
|
|
||||||
import com.ibm.wala.shrikeBT.IInvokeInstruction;
|
|
||||||
import com.ibm.wala.shrikeBT.InvokeInstruction;
|
|
||||||
import com.ibm.wala.ssa.ISSABasicBlock;
|
import com.ibm.wala.ssa.ISSABasicBlock;
|
||||||
import com.ibm.wala.types.ClassLoaderReference;
|
import com.ibm.wala.util.collections.Filtersection;
|
||||||
import com.ibm.wala.types.MethodReference;
|
|
||||||
import com.ibm.wala.util.Function;
|
|
||||||
import com.ibm.wala.util.IndiscriminateFilter;
|
|
||||||
import com.ibm.wala.util.MapIterator;
|
|
||||||
import com.ibm.wala.util.collections.Filter;
|
|
||||||
import com.ibm.wala.util.collections.FilterIterator;
|
|
||||||
import com.ibm.wala.util.collections.HashSetFactory;
|
|
||||||
import com.ibm.wala.util.debug.Assertions;
|
|
||||||
import com.ibm.wala.util.debug.Trace;
|
|
||||||
import com.ibm.wala.util.debug.UnimplementedError;
|
|
||||||
import com.ibm.wala.util.graph.GraphIntegrity;
|
|
||||||
import com.ibm.wala.util.graph.NumberedGraph;
|
|
||||||
import com.ibm.wala.util.graph.GraphIntegrity.UnsoundGraphException;
|
|
||||||
import com.ibm.wala.util.graph.impl.SlowSparseNumberedGraph;
|
|
||||||
import com.ibm.wala.util.intset.BitVector;
|
|
||||||
import com.ibm.wala.util.intset.IntSet;
|
|
||||||
import com.ibm.wala.util.perf.EngineTimings;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -53,141 +26,15 @@ import com.ibm.wala.util.perf.EngineTimings;
|
||||||
* @author sfink
|
* @author sfink
|
||||||
* @author Julian Dolby
|
* @author Julian Dolby
|
||||||
*/
|
*/
|
||||||
public class InterproceduralCFG implements NumberedGraph<BasicBlockInContext> {
|
public class InterproceduralCFG extends AbstractInterproceduralCFG<ISSABasicBlock> {
|
||||||
|
|
||||||
private static final int DEBUG_LEVEL = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should the graph include call-to-return edges?
|
|
||||||
*/
|
|
||||||
private final static boolean CALL_TO_RETURN_EDGES = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Graph implementation we delegate to.
|
|
||||||
*/
|
|
||||||
final private NumberedGraph<BasicBlockInContext> G = new SlowSparseNumberedGraph<BasicBlockInContext>(2);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Governing call graph
|
|
||||||
*/
|
|
||||||
private final CallGraph cg;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should we introduce a distinguished "exceptional exit" from each individual
|
|
||||||
* CFG?
|
|
||||||
*/
|
|
||||||
final private boolean partitionExits;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter that determines relevant call graph nodes
|
|
||||||
*/
|
|
||||||
private final Filter<CGNode> relevant;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* a cache: for each node (Basic Block), does that block end in a call?
|
|
||||||
*/
|
|
||||||
private final BitVector hasCallVector = new BitVector();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build an Interprocedural CFG from a call graph. This version defaults to
|
|
||||||
* using whatever CFGs the call graph provides by default, and includes all
|
|
||||||
* nodes in the call graph.
|
|
||||||
*
|
|
||||||
* @param CG
|
|
||||||
* the call graph
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public InterproceduralCFG(CallGraph CG) {
|
public InterproceduralCFG(CallGraph CG) {
|
||||||
this(CG, IndiscriminateFilter.singleton(), false);
|
super(CG);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public InterproceduralCFG(CallGraph cg, Filtersection<CGNode> filtersection) {
|
||||||
* Build an Interprocedural CFG from a call graph.
|
super(cg,filtersection);
|
||||||
*
|
|
||||||
* @param CG
|
|
||||||
* the call graph
|
|
||||||
* @param relevant
|
|
||||||
* a filter which accepts those call graph nodes which should be
|
|
||||||
* included in the I-CFG. Other nodes are ignored.
|
|
||||||
*/
|
|
||||||
public InterproceduralCFG(CallGraph CG, Filter<CGNode> relevant, boolean partitionExits) {
|
|
||||||
|
|
||||||
EngineTimings.startVirtual("InterproceduralCFG.<init>");
|
|
||||||
this.cg = CG;
|
|
||||||
this.relevant = relevant;
|
|
||||||
this.partitionExits = partitionExits;
|
|
||||||
|
|
||||||
// create nodes for IPCFG
|
|
||||||
createNodes();
|
|
||||||
|
|
||||||
// create edges for IPCFG
|
|
||||||
createEdges();
|
|
||||||
|
|
||||||
if (DEBUG_LEVEL > 1) {
|
|
||||||
try {
|
|
||||||
GraphIntegrity.check(this);
|
|
||||||
} catch (UnsoundGraphException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EngineTimings.finishVirtual("InterproceduralCFG.<init>");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create the edges that define this graph.
|
|
||||||
*/
|
|
||||||
private void createEdges() {
|
|
||||||
|
|
||||||
for (Iterator ns = cg.iterator(); ns.hasNext();) {
|
|
||||||
CGNode n = (CGNode) ns.next();
|
|
||||||
if (relevant.accepts(n)) {
|
|
||||||
// retrieve a cfg for node n.
|
|
||||||
ControlFlowGraph<ISSABasicBlock> cfg = n.getCFG();
|
|
||||||
if (cfg == null) {
|
|
||||||
// n is an unmodelled native method
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (partitionExits) {
|
|
||||||
cfg = new TwoExitCFG(cfg);
|
|
||||||
}
|
|
||||||
IInstruction[] instrs = cfg.getInstructions();
|
|
||||||
// create edges for node n.
|
|
||||||
for (Iterator bbs = cfg.iterator(); bbs.hasNext();) {
|
|
||||||
ISSABasicBlock bb = (ISSABasicBlock) bbs.next();
|
|
||||||
// entry node gets edges from callers
|
|
||||||
if (bb == cfg.entry()) {
|
|
||||||
addEdgesToEntryBlock(n, bb);
|
|
||||||
}
|
|
||||||
// other instructions get edges from predecessors,
|
|
||||||
// with special handling for calls.
|
|
||||||
else {
|
|
||||||
addEdgesToNonEntryBlock(n, cfg, instrs, bb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create the nodes. side effect: populates the hasCallVector
|
|
||||||
*/
|
|
||||||
private void createNodes() {
|
|
||||||
for (Iterator ns = cg.iterator(); ns.hasNext();) {
|
|
||||||
CGNode n = (CGNode) ns.next();
|
|
||||||
if (relevant.accepts(n)) {
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("Found a relevant node: " + n);
|
|
||||||
}
|
|
||||||
|
|
||||||
// retrieve a cfg for node n.
|
|
||||||
ControlFlowGraph<ISSABasicBlock> cfg = getCFG(n);
|
|
||||||
if (cfg != null) {
|
|
||||||
// create a node for each basic block.
|
|
||||||
addNodeForEachBasicBlock(cfg, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -195,645 +42,17 @@ public class InterproceduralCFG implements NumberedGraph<BasicBlockInContext> {
|
||||||
* @throws IllegalArgumentException
|
* @throws IllegalArgumentException
|
||||||
* if n == null
|
* if n == null
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public ControlFlowGraph<ISSABasicBlock> getCFG(CGNode n) throws IllegalArgumentException {
|
public ControlFlowGraph<ISSABasicBlock> getCFG(CGNode n) throws IllegalArgumentException {
|
||||||
if (n == null) {
|
if (n == null) {
|
||||||
throw new IllegalArgumentException("n == null");
|
throw new IllegalArgumentException("n == null");
|
||||||
}
|
}
|
||||||
ControlFlowGraph<ISSABasicBlock> cfg = n.getCFG();
|
ControlFlowGraph<ISSABasicBlock> cfg = n.getIR().getControlFlowGraph();
|
||||||
if (cfg == null) {
|
if (cfg == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG_LEVEL > 1) {
|
|
||||||
// TODO: we NEED TO FIX synthetic methods to have
|
|
||||||
// meaningful IRs for flow-sensitive analysis
|
|
||||||
if (!n.getMethod().isSynthetic()) {
|
|
||||||
try {
|
|
||||||
GraphIntegrity.check(cfg);
|
|
||||||
} catch (UnsoundGraphException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (partitionExits) {
|
|
||||||
cfg = new TwoExitCFG(cfg);
|
|
||||||
}
|
|
||||||
if (DEBUG_LEVEL > 1) {
|
|
||||||
// TODO: we NEED TO FIX synthetic methods to have
|
|
||||||
// meaningful IRs for flow-sensitive analysis
|
|
||||||
if (!n.getMethod().isSynthetic()) {
|
|
||||||
try {
|
|
||||||
GraphIntegrity.check(cfg);
|
|
||||||
} catch (UnsoundGraphException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add edges to the IPCFG for the incoming edges incident on a basic block bb
|
|
||||||
*
|
|
||||||
* @param n
|
|
||||||
* a call graph node
|
|
||||||
* @param cfg
|
|
||||||
* the CFG for n
|
|
||||||
* @param instrs
|
|
||||||
* the instructions for node n
|
|
||||||
* @param bb
|
|
||||||
* a basic block in the CFG
|
|
||||||
*/
|
|
||||||
private void addEdgesToNonEntryBlock(CGNode n, ControlFlowGraph<ISSABasicBlock> cfg, IInstruction[] instrs, ISSABasicBlock bb) {
|
|
||||||
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("addEdgesForNonEntryBlock: " + bb);
|
|
||||||
Trace.println("cfg class: " + cfg.getClass());
|
|
||||||
Trace.println("nPred: " + cfg.getPredNodeCount(bb));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Iterator ps = cfg.getPredNodes(bb); ps.hasNext();) {
|
|
||||||
ISSABasicBlock pb = (ISSABasicBlock) ps.next();
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("Consider previous block: " + pb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pb.getLastInstructionIndex() < 0) {
|
|
||||||
// pb is the entry block for a cfg with
|
|
||||||
// no instructions.
|
|
||||||
BasicBlockInContext p = new BasicBlockInContext(n, pb);
|
|
||||||
BasicBlockInContext b = new BasicBlockInContext(n, bb);
|
|
||||||
G.addEdge(p, b);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = pb.getLastInstructionIndex();
|
|
||||||
IInstruction inst = instrs[index];
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("Last instruction is : " + inst);
|
|
||||||
}
|
|
||||||
if (inst instanceof IInvokeInstruction) {
|
|
||||||
// a previous instruction is a call instruction. If necessary,
|
|
||||||
// add an edge from the exit() of each target of the call.
|
|
||||||
IInvokeInstruction call = (IInvokeInstruction) inst;
|
|
||||||
CallSiteReference site = makeCallSiteReference(n.getMethod().getDeclaringClass().getClassLoader().getReference(), cfg
|
|
||||||
.getProgramCounter(index), call);
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("got Site: " + site);
|
|
||||||
}
|
|
||||||
boolean irrelevantTargets = false;
|
|
||||||
for (Iterator ts = cg.getPossibleTargets(n, site).iterator(); ts.hasNext();) {
|
|
||||||
CGNode tn = (CGNode) ts.next();
|
|
||||||
if (!relevant.accepts(tn)) {
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("Irrelevant target: " + tn);
|
|
||||||
}
|
|
||||||
irrelevantTargets = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("Relevant target: " + tn);
|
|
||||||
}
|
|
||||||
// add an edge from tn exit to this node
|
|
||||||
ControlFlowGraph<ISSABasicBlock> tcfg = tn.getCFG();
|
|
||||||
// tcfg might be null if tn is an unmodelled native method
|
|
||||||
if (tcfg != null) {
|
|
||||||
if (partitionExits) {
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("normal? " + representsNormalReturn(cfg, bb, pb));
|
|
||||||
Trace.println("exceptional? " + representsExceptionalReturn(cfg, bb, pb));
|
|
||||||
}
|
|
||||||
if (!(tcfg instanceof TwoExitCFG)) {
|
|
||||||
tcfg = new TwoExitCFG(tcfg);
|
|
||||||
}
|
|
||||||
if (representsNormalReturn(cfg, bb, pb)) {
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("representsNormalReturn: " + bb + " " + pb);
|
|
||||||
}
|
|
||||||
addEdgesFromNormalExitToReturn(n, bb, tn, (TwoExitCFG) tcfg);
|
|
||||||
}
|
|
||||||
if (representsExceptionalReturn(cfg, bb, pb)) {
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("representsExceptionalReturn: " + bb + " " + pb);
|
|
||||||
}
|
|
||||||
addEdgesFromExceptionalExitToReturn(n, bb, tn, (TwoExitCFG) tcfg);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
addEdgesFromExitToReturn(n, bb, tn, tcfg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (irrelevantTargets || CALL_TO_RETURN_EDGES) {
|
|
||||||
// at least one call target was ignored. So add a "normal" edge
|
|
||||||
// from the predecessor block to this block.
|
|
||||||
BasicBlockInContext p = new BasicBlockInContext(n, pb);
|
|
||||||
BasicBlockInContext b = new BasicBlockInContext(n, bb);
|
|
||||||
G.addEdge(p, b);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// previous instruction is not a call instruction.
|
|
||||||
BasicBlockInContext p = new BasicBlockInContext(n, pb);
|
|
||||||
BasicBlockInContext b = new BasicBlockInContext(n, bb);
|
|
||||||
if (Assertions.verifyAssertions) {
|
|
||||||
if (!G.containsNode(p) || !G.containsNode(b)) {
|
|
||||||
Assertions._assert(G.containsNode(p), "IPCFG does not contain " + p);
|
|
||||||
Assertions._assert(G.containsNode(b), "IPCFG does not contain " + b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
G.addEdge(p, b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CallSiteReference makeCallSiteReference(ClassLoaderReference loader, int pc, IInvokeInstruction call)
|
|
||||||
throws IllegalArgumentException, IllegalArgumentException {
|
|
||||||
|
|
||||||
if (call == null) {
|
|
||||||
throw new IllegalArgumentException("call == null");
|
|
||||||
}
|
|
||||||
if (!(call instanceof com.ibm.wala.ssa.SSAInvokeInstruction) && !(call instanceof com.ibm.wala.shrikeBT.InvokeInstruction)) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"(not ( call instanceof com.ibm.wala.ssa.SSAInvokeInstruction ) ) and (not ( call instanceof com.ibm.wala.shrikeBT.InvokeInstruction ) )");
|
|
||||||
}
|
|
||||||
CallSiteReference site = null;
|
|
||||||
if (call instanceof InvokeInstruction) {
|
|
||||||
InvokeInstruction c = (InvokeInstruction) call;
|
|
||||||
site = CallSiteReference.make(pc, MethodReference.findOrCreate(loader, c.getClassType(), c.getMethodName(), c
|
|
||||||
.getMethodSignature()), call.getInvocationCode());
|
|
||||||
} else {
|
|
||||||
com.ibm.wala.ssa.SSAInvokeInstruction c = (com.ibm.wala.ssa.SSAInvokeInstruction) call;
|
|
||||||
site = CallSiteReference.make(pc, c.getDeclaredTarget(), call.getInvocationCode());
|
|
||||||
}
|
|
||||||
return site;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an edge from the exceptional exit block of a callee to a return site in
|
|
||||||
* the caller
|
|
||||||
*
|
|
||||||
* @param returnBlock
|
|
||||||
* the return site for a call
|
|
||||||
* @param targetCFG
|
|
||||||
* the called method
|
|
||||||
*/
|
|
||||||
private void addEdgesFromExceptionalExitToReturn(CGNode caller, ISSABasicBlock returnBlock, CGNode target, TwoExitCFG targetCFG) {
|
|
||||||
ISSABasicBlock texit = targetCFG.getExceptionalExit();
|
|
||||||
BasicBlockInContext exit = new BasicBlockInContext(target, texit);
|
|
||||||
BasicBlockInContext ret = new BasicBlockInContext(caller, returnBlock);
|
|
||||||
if (Assertions.verifyAssertions) {
|
|
||||||
if (!G.containsNode(exit) || !G.containsNode(ret)) {
|
|
||||||
Assertions._assert(G.containsNode(exit), "IPCFG does not contain " + exit);
|
|
||||||
Assertions._assert(G.containsNode(ret), "IPCFG does not contain " + ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
G.addEdge(exit, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param cfg
|
|
||||||
* the governing cfg
|
|
||||||
* @param ret
|
|
||||||
* a return site for the call
|
|
||||||
* @param call
|
|
||||||
* a basic block that ends in a call
|
|
||||||
* @return true iff bb is reached from pb by exceptional control flow
|
|
||||||
*/
|
|
||||||
private boolean representsExceptionalReturn(ControlFlowGraph<ISSABasicBlock> cfg, ISSABasicBlock ret, ISSABasicBlock call) {
|
|
||||||
for (Iterator it = cfg.getExceptionalSuccessors(call).iterator(); it.hasNext();) {
|
|
||||||
if (ret.equals(it.next())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an edge from the normal exit block of a callee to a return site in the
|
|
||||||
* caller
|
|
||||||
*
|
|
||||||
* @param returnBlock
|
|
||||||
* the return site for a call
|
|
||||||
* @param targetCFG
|
|
||||||
* the called method
|
|
||||||
*/
|
|
||||||
private void addEdgesFromNormalExitToReturn(CGNode caller, ISSABasicBlock returnBlock, CGNode target, TwoExitCFG targetCFG) {
|
|
||||||
ISSABasicBlock texit = targetCFG.getNormalExit();
|
|
||||||
BasicBlockInContext exit = new BasicBlockInContext(target, texit);
|
|
||||||
BasicBlockInContext ret = new BasicBlockInContext(caller, returnBlock);
|
|
||||||
if (Assertions.verifyAssertions) {
|
|
||||||
if (!G.containsNode(exit) || !G.containsNode(ret)) {
|
|
||||||
Assertions._assert(G.containsNode(exit), "IPCFG does not contain " + exit);
|
|
||||||
Assertions._assert(G.containsNode(ret), "IPCFG does not contain " + ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
G.addEdge(exit, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param cfg
|
|
||||||
* the governing cfg
|
|
||||||
* @return true iff bb is reached from pb by a normal return flow
|
|
||||||
*/
|
|
||||||
private boolean representsNormalReturn(ControlFlowGraph<ISSABasicBlock> cfg, ISSABasicBlock ret, ISSABasicBlock call) {
|
|
||||||
for (Iterator it = cfg.getNormalSuccessors(call).iterator(); it.hasNext();) {
|
|
||||||
if (ret.equals(it.next())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an edge from the exit() block of a callee to a return site in the
|
|
||||||
* caller
|
|
||||||
*
|
|
||||||
* @param returnBlock
|
|
||||||
* the return site for a call
|
|
||||||
* @param targetCFG
|
|
||||||
* the called method
|
|
||||||
*/
|
|
||||||
private void addEdgesFromExitToReturn(CGNode caller, ISSABasicBlock returnBlock, CGNode target,
|
|
||||||
ControlFlowGraph<ISSABasicBlock> targetCFG) {
|
|
||||||
ISSABasicBlock texit = targetCFG.exit();
|
|
||||||
BasicBlockInContext exit = new BasicBlockInContext(target, texit);
|
|
||||||
BasicBlockInContext ret = new BasicBlockInContext(caller, returnBlock);
|
|
||||||
if (Assertions.verifyAssertions) {
|
|
||||||
if (!G.containsNode(exit) || !G.containsNode(ret)) {
|
|
||||||
Assertions._assert(G.containsNode(exit), "IPCFG does not contain " + exit);
|
|
||||||
Assertions._assert(G.containsNode(ret), "IPCFG does not contain " + ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
G.addEdge(exit, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the incoming edges to the entry() block for a call graph node.
|
|
||||||
*
|
|
||||||
* @param n
|
|
||||||
* a node in the call graph
|
|
||||||
* @param bb
|
|
||||||
* the entry() block for n
|
|
||||||
*/
|
|
||||||
private void addEdgesToEntryBlock(CGNode n, ISSABasicBlock bb) {
|
|
||||||
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("addEdgesToEntryBlock " + bb);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Iterator callers = cg.getPredNodes(n); callers.hasNext();) {
|
|
||||||
CGNode caller = (CGNode) callers.next();
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("got caller " + caller);
|
|
||||||
}
|
|
||||||
if (relevant.accepts(caller)) {
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("caller " + caller + "is relevant");
|
|
||||||
}
|
|
||||||
ControlFlowGraph<ISSABasicBlock> ccfg = caller.getCFG();
|
|
||||||
IInstruction[] cinsts = ccfg.getInstructions();
|
|
||||||
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("Visiting " + cinsts.length + " instructions");
|
|
||||||
}
|
|
||||||
for (int i = 0; i < cinsts.length; i++) {
|
|
||||||
if (cinsts[i] instanceof IInvokeInstruction) {
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("Checking invokeinstruction: " + cinsts[i]);
|
|
||||||
}
|
|
||||||
IInvokeInstruction call = (IInvokeInstruction) cinsts[i];
|
|
||||||
CallSiteReference site = makeCallSiteReference(n.getMethod().getDeclaringClass().getClassLoader().getReference(), ccfg
|
|
||||||
.getProgramCounter(i), call);
|
|
||||||
if (cg.getPossibleTargets(caller, site).contains(n)) {
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("Adding edge " + ccfg.getBlockForInstruction(i) + " to " + bb);
|
|
||||||
}
|
|
||||||
ISSABasicBlock callerBB = ccfg.getBlockForInstruction(i);
|
|
||||||
BasicBlockInContext b1 = new BasicBlockInContext(caller, callerBB);
|
|
||||||
BasicBlockInContext b2 = new BasicBlockInContext(n, bb);
|
|
||||||
G.addEdge(b1, b2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a node to the IPCFG for each node in a CFG. side effect: populates the
|
|
||||||
* hasCallVector
|
|
||||||
*
|
|
||||||
* @param cfg
|
|
||||||
* a control-flow graph
|
|
||||||
*/
|
|
||||||
private void addNodeForEachBasicBlock(ControlFlowGraph<ISSABasicBlock> cfg, CGNode N) {
|
|
||||||
for (Iterator<ISSABasicBlock> bbs = cfg.iterator(); bbs.hasNext();) {
|
|
||||||
ISSABasicBlock bb = bbs.next();
|
|
||||||
if (DEBUG_LEVEL > 0) {
|
|
||||||
Trace.println("IPCFG Add basic block " + bb);
|
|
||||||
}
|
|
||||||
BasicBlockInContext b = new BasicBlockInContext(N, bb);
|
|
||||||
G.addNode(b);
|
|
||||||
if (hasCall(b, cfg)) {
|
|
||||||
hasCallVector.set(getNumber(b));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the original CFG from whence B came
|
|
||||||
* @throws IllegalArgumentException
|
|
||||||
* if B == null
|
|
||||||
*/
|
|
||||||
public ControlFlowGraph<ISSABasicBlock> getCFG(BasicBlockInContext B) throws IllegalArgumentException {
|
|
||||||
if (B == null) {
|
|
||||||
throw new IllegalArgumentException("B == null");
|
|
||||||
}
|
|
||||||
return getCFG(getCGNode(B));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the original CGNode from whence B came
|
|
||||||
* @throws IllegalArgumentException
|
|
||||||
* if B == null
|
|
||||||
*/
|
|
||||||
public CGNode getCGNode(BasicBlockInContext B) throws IllegalArgumentException {
|
|
||||||
if (B == null) {
|
|
||||||
throw new IllegalArgumentException("B == null");
|
|
||||||
}
|
|
||||||
return B.getNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.Graph#removeNodeAndEdges(com.ibm.wala.util.graph.Node)
|
|
||||||
*/
|
|
||||||
public void removeNodeAndEdges(BasicBlockInContext N) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.NodeManager#iterateNodes()
|
|
||||||
*/
|
|
||||||
public Iterator<BasicBlockInContext> iterator() {
|
|
||||||
return G.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.NodeManager#getNumberOfNodes()
|
|
||||||
*/
|
|
||||||
public int getNumberOfNodes() {
|
|
||||||
return G.getNumberOfNodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.NodeManager#addNode(com.ibm.wala.util.graph.Node)
|
|
||||||
*/
|
|
||||||
public void addNode(BasicBlockInContext n) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.NodeManager#removeNode(com.ibm.wala.util.graph.Node)
|
|
||||||
*/
|
|
||||||
public void removeNode(BasicBlockInContext n) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.EdgeManager#getPredNodes(com.ibm.wala.util.graph.Node)
|
|
||||||
*/
|
|
||||||
public Iterator<? extends BasicBlockInContext> getPredNodes(BasicBlockInContext N) {
|
|
||||||
return G.getPredNodes(N);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.EdgeManager#getPredNodeCount(com.ibm.wala.util.graph.Node)
|
|
||||||
*/
|
|
||||||
public int getPredNodeCount(BasicBlockInContext N) {
|
|
||||||
return G.getPredNodeCount(N);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.EdgeManager#getSuccNodes(com.ibm.wala.util.graph.Node)
|
|
||||||
*/
|
|
||||||
public Iterator<? extends BasicBlockInContext> getSuccNodes(BasicBlockInContext N) {
|
|
||||||
return G.getSuccNodes(N);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.EdgeManager#getSuccNodeCount(com.ibm.wala.util.graph.Node)
|
|
||||||
*/
|
|
||||||
public int getSuccNodeCount(BasicBlockInContext N) {
|
|
||||||
return G.getSuccNodeCount(N);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.EdgeManager#addEdge(com.ibm.wala.util.graph.Node,
|
|
||||||
* com.ibm.wala.util.graph.Node)
|
|
||||||
*/
|
|
||||||
public void addEdge(BasicBlockInContext src, BasicBlockInContext dst) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeEdge(BasicBlockInContext src, BasicBlockInContext dst) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.EdgeManager#removeEdges(com.ibm.wala.util.graph.Node)
|
|
||||||
*/
|
|
||||||
public void removeAllIncidentEdges(BasicBlockInContext node) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return G.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @see com.ibm.wala.util.graph.Graph#containsNode(com.ibm.wala.util.graph.Node)
|
|
||||||
*/
|
|
||||||
public boolean containsNode(BasicBlockInContext N) {
|
|
||||||
return G.containsNode(N);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param B
|
|
||||||
* @return true iff basic block B ends in a call instuction
|
|
||||||
*/
|
|
||||||
public boolean hasCall(BasicBlockInContext B) {
|
|
||||||
if (Assertions.verifyAssertions) {
|
|
||||||
if (!containsNode(B)) {
|
|
||||||
Assertions._assert(containsNode(B));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hasCallVector.get(getNumber(B));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true iff basic block B ends in a call instuction
|
|
||||||
*/
|
|
||||||
private boolean hasCall(BasicBlockInContext B, ControlFlowGraph cfg) {
|
|
||||||
IInstruction[] statements = cfg.getInstructions();
|
|
||||||
|
|
||||||
int lastIndex = B.getLastInstructionIndex();
|
|
||||||
if (lastIndex >= 0) {
|
|
||||||
|
|
||||||
if (Assertions.verifyAssertions) {
|
|
||||||
if (statements.length <= lastIndex) {
|
|
||||||
System.err.println(statements.length);
|
|
||||||
System.err.println(cfg);
|
|
||||||
Assertions._assert(lastIndex < statements.length, "bad BB " + B + " and CFG for " + getCGNode(B));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
IInstruction last = statements[lastIndex];
|
|
||||||
return (last instanceof IInvokeInstruction);
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param B
|
|
||||||
* @return the set of CGNodes that B may call, according to the governing call
|
|
||||||
* graph.
|
|
||||||
* @throws IllegalArgumentException
|
|
||||||
* if B is null
|
|
||||||
*/
|
|
||||||
public Set<CGNode> getCallTargets(BasicBlockInContext B) {
|
|
||||||
if (B == null) {
|
|
||||||
throw new IllegalArgumentException("B is null");
|
|
||||||
}
|
|
||||||
ControlFlowGraph cfg = getCFG(B);
|
|
||||||
return getCallTargets(B, cfg, getCGNode(B));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the set of CGNodes that B may call, according to the governing call
|
|
||||||
* graph.
|
|
||||||
*/
|
|
||||||
private Set<CGNode> getCallTargets(IBasicBlock B, ControlFlowGraph cfg, CGNode Bnode) {
|
|
||||||
IInstruction[] statements = cfg.getInstructions();
|
|
||||||
IInvokeInstruction call = (IInvokeInstruction) statements[B.getLastInstructionIndex()];
|
|
||||||
int pc = cfg.getProgramCounter(B.getLastInstructionIndex());
|
|
||||||
CallSiteReference site = makeCallSiteReference(B.getMethod().getDeclaringClass().getClassLoader().getReference(), pc, call);
|
|
||||||
HashSet<CGNode> result = HashSetFactory.make(cg.getNumberOfTargets(Bnode, site));
|
|
||||||
for (Iterator<CGNode> it = cg.getPossibleTargets(Bnode, site).iterator(); it.hasNext();) {
|
|
||||||
CGNode target = it.next();
|
|
||||||
result.add(target);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeIncomingEdges(BasicBlockInContext node) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeOutgoingEdges(BasicBlockInContext node) throws UnsupportedOperationException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasEdge(BasicBlockInContext src, BasicBlockInContext dst) {
|
|
||||||
return G.hasEdge(src, dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNumber(BasicBlockInContext N) {
|
|
||||||
return G.getNumber(N);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BasicBlockInContext getNode(int number) throws UnimplementedError {
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxNumber() {
|
|
||||||
return G.getMaxNumber();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterator<BasicBlockInContext> iterateNodes(IntSet s) throws UnimplementedError {
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntSet getSuccNodeNumbers(BasicBlockInContext node) {
|
|
||||||
return G.getSuccNodeNumbers(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntSet getPredNodeNumbers(BasicBlockInContext node) {
|
|
||||||
return G.getPredNodeNumbers(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getEntry(CGNode n) {
|
|
||||||
ControlFlowGraph<ISSABasicBlock> cfg = getCFG(n);
|
|
||||||
ISSABasicBlock entry = cfg.entry();
|
|
||||||
return new BasicBlockInContext(n, entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param bb
|
|
||||||
* node in the IPCFG that ends in a call
|
|
||||||
* @return the nodes that are return sites for this call.
|
|
||||||
* @throws IllegalArgumentException
|
|
||||||
* if bb is null
|
|
||||||
*/
|
|
||||||
public Iterator<BasicBlockInContext> getReturnSites(BasicBlockInContext bb) {
|
|
||||||
if (bb == null) {
|
|
||||||
throw new IllegalArgumentException("bb is null");
|
|
||||||
}
|
|
||||||
final CGNode node = bb.getNode();
|
|
||||||
|
|
||||||
// a successor node is a return site if it is in the same
|
|
||||||
// procedure, and is not the entry() node.
|
|
||||||
Filter isReturn = new Filter() {
|
|
||||||
public boolean accepts(Object o) {
|
|
||||||
if (Assertions.verifyAssertions) {
|
|
||||||
Assertions._assert(o instanceof BasicBlockInContext);
|
|
||||||
}
|
|
||||||
BasicBlockInContext other = (BasicBlockInContext) o;
|
|
||||||
return !other.isEntryBlock() && node.equals(other.getNode());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return new FilterIterator<BasicBlockInContext>(getSuccNodes(bb), isReturn);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterator<BasicBlockInContext> getCallSites(BasicBlockInContext bb) {
|
|
||||||
if (bb == null) {
|
|
||||||
throw new IllegalArgumentException("bb is null");
|
|
||||||
}
|
|
||||||
ControlFlowGraph<ISSABasicBlock> cfg = getCFG(bb);
|
|
||||||
Iterator<? extends ISSABasicBlock> it = cfg.getPredNodes(bb.getDelegate());
|
|
||||||
final CGNode node = bb.getNode();
|
|
||||||
Function<ISSABasicBlock, BasicBlockInContext> toContext = new Function<ISSABasicBlock, BasicBlockInContext>() {
|
|
||||||
public BasicBlockInContext apply(ISSABasicBlock object) {
|
|
||||||
ISSABasicBlock b = object;
|
|
||||||
return new BasicBlockInContext(node, b);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
MapIterator<ISSABasicBlock, BasicBlockInContext> m = new MapIterator<ISSABasicBlock, BasicBlockInContext>(it, toContext);
|
|
||||||
return new FilterIterator<BasicBlockInContext>(m, isCall);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Filter isCall = new Filter() {
|
|
||||||
public boolean accepts(Object o) {
|
|
||||||
return hasCall((BasicBlockInContext) o);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public boolean isReturn(BasicBlockInContext bb) throws IllegalArgumentException {
|
|
||||||
if (bb == null) {
|
|
||||||
throw new IllegalArgumentException("bb == null");
|
|
||||||
}
|
|
||||||
ControlFlowGraph<ISSABasicBlock> cfg = getCFG(bb);
|
|
||||||
for (Iterator<? extends ISSABasicBlock> it = cfg.getPredNodes(bb.getDelegate()); it.hasNext();) {
|
|
||||||
ISSABasicBlock b = it.next();
|
|
||||||
if (hasCall(new BasicBlockInContext(bb.getNode(), b))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue