/******************************************************************************* * 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.ssa; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import com.ibm.wala.cfg.AbstractCFG; import com.ibm.wala.cfg.BytecodeCFG; import com.ibm.wala.cfg.ControlFlowGraph; import com.ibm.wala.cfg.IBasicBlock; import com.ibm.wala.cfg.InducedCFG; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.IClassLoader; import com.ibm.wala.classLoader.IMethod; import com.ibm.wala.shrikeBT.ExceptionHandler; import com.ibm.wala.shrikeBT.IInstruction; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.collections.EmptyIterator; import com.ibm.wala.util.collections.HashMapFactory; import com.ibm.wala.util.collections.Iterator2Collection; import com.ibm.wala.util.collections.MapIterator; import com.ibm.wala.util.debug.Assertions; import com.ibm.wala.util.debug.UnimplementedError; import com.ibm.wala.util.functions.Function; import com.ibm.wala.util.graph.impl.NumberedNodeIterator; import com.ibm.wala.util.intset.BitVector; import com.ibm.wala.util.intset.IntSet; import com.ibm.wala.util.shrike.ShrikeUtil; import com.ibm.wala.util.warnings.Warning; import com.ibm.wala.util.warnings.Warnings; /** * A control-flow graph for ssa form. * * This implementation is uglified in the name of performance. This implementation does not directly track the graph structure, but * instead delegates to a prebuilt {@link ControlFlowGraph} which stores the structure. This decision from 2004 may have been * premature optimization, left over from a world where {@link IR}s and related structures were long-lived. In today's system, they * are cached and reconstituted by {@link SSACache}. Perhaps we should just extend {@link AbstractCFG} and not worry so much about * space. * * As the current implementation stands, the delegate graph stores the graph structure, and this class additionally stores * {@link BasicBlock}s and the {@link SSAInstruction} array. */ public class SSACFG implements ControlFlowGraph<SSAInstruction, ISSABasicBlock> { private static final boolean DEBUG = false; /** * The {@link ISSABasicBlock}s which live in this graph. These {@link BasicBlock}s must have the same numbers as the corresponding * {@link IBasicBlock}s in the delegate {@link AbstractCFG}. This array is additionally co-indexed by these numbers. */ private BasicBlock[] basicBlocks; /** * The "normal" instructions which constitute the SSA form. This does not include {@link SSAPhiInstruction}s, which dwell in * {@link BasicBlock}s instead. */ final protected SSAInstruction[] instructions; /** * The {@link IMethod} this {@link ControlFlowGraph} represents */ final protected IMethod method; /** * A delegate CFG, pre-built, which stores the graph structure of this CFG. */ final protected AbstractCFG<IInstruction, IBasicBlock<IInstruction>> delegate; /** * cache a ref to the exit block for efficient access */ private BasicBlock exit; /** * @throws IllegalArgumentException if method is null */ @SuppressWarnings("unchecked") public SSACFG(IMethod method, AbstractCFG cfg, SSAInstruction[] instructions) { if (method == null) { throw new IllegalArgumentException("method is null"); } this.delegate = cfg; if (DEBUG) { System.err.println(("Incoming CFG for " + method + ":")); System.err.println(cfg.toString()); } this.method = method; assert method.getDeclaringClass() != null : "null declaring class for " + method; createBasicBlocks(cfg); if (cfg instanceof InducedCFG) { addPhisFromInducedCFG((InducedCFG) cfg); addPisFromInducedCFG((InducedCFG) cfg); } if (cfg instanceof BytecodeCFG) { recordExceptionTypes(((BytecodeCFG) cfg).getExceptionHandlers(), method.getDeclaringClass().getClassLoader()); } this.instructions = instructions; } /** * This is ugly. Clean it up someday. {@link InducedCFG}s carry around pii instructions. add these pii instructions to the * SSABasicBlocks */ private void addPisFromInducedCFG(InducedCFG cfg) { for (Iterator<? extends InducedCFG.BasicBlock> it = cfg.iterator(); it.hasNext();) { InducedCFG.BasicBlock ib = it.next(); // we rely on the invariant that basic blocks in this cfg are numbered identically as in the source // InducedCFG BasicBlock b = getBasicBlock(ib.getNumber()); for (SSAPiInstruction pi : ib.getPis()) { BasicBlock path = getBasicBlock(pi.getSuccessor()); b.addPiForRefAndPath(pi.getVal(), path, pi); } } } /** * This is ugly. Clean it up someday. {@link InducedCFG}s carry around phi instructions. add these phi instructions to the * SSABasicBlocks */ private void addPhisFromInducedCFG(InducedCFG cfg) { for (Iterator<? extends InducedCFG.BasicBlock> it = cfg.iterator(); it.hasNext();) { InducedCFG.BasicBlock ib = it.next(); // we rely on the invariant that basic blocks in this cfg are numbered identically as in the source // InducedCFG BasicBlock b = getBasicBlock(ib.getNumber()); // this is really ugly. we pretend that each successively phi in the basic block defs a // particular 'local'. TODO: fix the API in some way so that this is unnecessary. // {What does Julian do for, say, Javascript?} int local = 0; for (SSAPhiInstruction phi : ib.getPhis()) { b.addPhiForLocal(local++, phi); } } } @Override public int hashCode() { return -3 * delegate.hashCode(); } @Override public boolean equals(Object o) { return (o instanceof SSACFG) && delegate.equals(((SSACFG) o).delegate); } private void recordExceptionTypes(Set<ExceptionHandler> set, IClassLoader loader) { for (Iterator<ExceptionHandler> it = set.iterator(); it.hasNext();) { ExceptionHandler handler = it.next(); TypeReference t = null; if (handler.getCatchClass() == null) { // by convention, in ShrikeCT this means catch everything t = TypeReference.JavaLangThrowable; } else { TypeReference exceptionType = ShrikeUtil.makeTypeReference(loader.getReference(), handler.getCatchClass()); IClass klass = null; klass = loader.lookupClass(exceptionType.getName()); if (klass == null) { Warnings.add(ExceptionLoadFailure.create(exceptionType, method)); t = exceptionType; } else { t = klass.getReference(); } } int instructionIndex = handler.getHandler(); IBasicBlock b = getBlockForInstruction(instructionIndex); if (!(b instanceof ExceptionHandlerBasicBlock)) { assert b instanceof ExceptionHandlerBasicBlock : "not exception handler " + b + " index " + instructionIndex; } ExceptionHandlerBasicBlock bb = (ExceptionHandlerBasicBlock) getBlockForInstruction(instructionIndex); bb.addCaughtExceptionType(t); } } private void createBasicBlocks(AbstractCFG G) { basicBlocks = new BasicBlock[G.getNumberOfNodes()]; for (int i = 0; i <= G.getMaxNumber(); i++) { if (G.getCatchBlocks().get(i)) { basicBlocks[i] = new ExceptionHandlerBasicBlock(i); } else { basicBlocks[i] = new BasicBlock(i); } } exit = basicBlocks[delegate.getNumber(delegate.exit())]; } /** * Get the basic block an instruction belongs to. Note: the instruction2Block array is filled in lazily. During initialization, * the mapping is set up only for the first instruction of each basic block. */ @Override public SSACFG.BasicBlock getBlockForInstruction(int instructionIndex) { IBasicBlock<IInstruction> N = delegate.getBlockForInstruction(instructionIndex); int number = delegate.getNumber(N); return basicBlocks[number]; } /** * NB: Use iterators such as IR.iterateAllInstructions() instead of this method. This will probably be deprecated someday. * * Return the instructions. Note that the CFG is created from the Shrike CFG prior to creating the SSA instructions. * * @return an array containing the SSA instructions. */ @Override public SSAInstruction[] getInstructions() { return instructions; } private final Map<RefPathKey, SSAPiInstruction> piInstructions = HashMapFactory.make(2); private static class RefPathKey { private final int n; private final Object src; private final Object path; RefPathKey(int n, Object src, Object path) { this.n = n; this.src = src; this.path = path; } @Override public int hashCode() { return n * path.hashCode(); } @Override public boolean equals(Object x) { return (x instanceof RefPathKey) && n == ((RefPathKey) x).n && src == ((RefPathKey) x).src && path == ((RefPathKey) x).path; } } /** * A Basic Block in an SSA IR */ public class BasicBlock implements ISSABasicBlock { /** * state needed for the numbered graph. */ private final int number; /** * List of PhiInstructions associated with the entry of this block. */ private SSAPhiInstruction stackSlotPhis[]; private SSAPhiInstruction localPhis[]; private final static int initialCapacity = 10; public BasicBlock(int number) { this.number = number; } @Override public int getNumber() { return number; } /** * Method getFirstInstructionIndex. */ @Override public int getFirstInstructionIndex() { IBasicBlock B = delegate.getNode(number); return B.getFirstInstructionIndex(); } /** * Is this block marked as a catch block? */ @Override public boolean isCatchBlock() { return delegate.getCatchBlocks().get(getNumber()); } @Override public int getLastInstructionIndex() { IBasicBlock B = delegate.getNode(number); return B.getLastInstructionIndex(); } /* * @see com.ibm.wala.ssa.ISSABasicBlock#iteratePhis() */ @Override public Iterator<SSAPhiInstruction> iteratePhis() { compressPhis(); if (stackSlotPhis == null) { if (localPhis == null) { return EmptyIterator.instance(); } else { LinkedList<SSAPhiInstruction> result = new LinkedList<SSAPhiInstruction>(); for (SSAPhiInstruction phi : localPhis) { if (phi != null) { result.add(phi); } } return result.iterator(); } } else { // stackSlotPhis != null LinkedList<SSAPhiInstruction> result = new LinkedList<SSAPhiInstruction>(); for (SSAPhiInstruction phi : stackSlotPhis) { if (phi != null) { result.add(phi); } } if (localPhis == null) { return result.iterator(); } else { for (SSAPhiInstruction phi : localPhis) { if (phi != null) { result.add(phi); } } return result.iterator(); } } } /** * This method is used during SSA construction. */ public SSAPhiInstruction getPhiForStackSlot(int slot) { if (stackSlotPhis == null) { return null; } else { if (slot >= stackSlotPhis.length) { return null; } else { return stackSlotPhis[slot]; } } } /** * This method is used during SSA construction. */ public SSAPhiInstruction getPhiForLocal(int n) { if (localPhis == null) { return null; } else { if (n >= localPhis.length) { return null; } else { return localPhis[n]; } } } public void addPhiForStackSlot(int slot, SSAPhiInstruction phi) { if (stackSlotPhis == null) { stackSlotPhis = new SSAPhiInstruction[initialCapacity]; } if (slot >= stackSlotPhis.length) { SSAPhiInstruction[] temp = stackSlotPhis; stackSlotPhis = new SSAPhiInstruction[slot * 2]; System.arraycopy(temp, 0, stackSlotPhis, 0, temp.length); } stackSlotPhis[slot] = phi; } public void addPhiForLocal(int n, SSAPhiInstruction phi) { if (localPhis == null) { localPhis = new SSAPhiInstruction[initialCapacity]; } if (n >= localPhis.length) { SSAPhiInstruction[] temp = localPhis; localPhis = new SSAPhiInstruction[n * 2]; System.arraycopy(temp, 0, localPhis, 0, temp.length); } localPhis[n] = phi; } /** * Remove any phis in the set. */ public void removePhis(Set<SSAPhiInstruction> toRemove) { int nRemoved = 0; if (stackSlotPhis != null) { for (int i = 0; i < stackSlotPhis.length; i++) { if (toRemove.contains(stackSlotPhis[i])) { stackSlotPhis[i] = null; nRemoved++; } } } if (nRemoved > 0) { int newLength = stackSlotPhis.length - nRemoved; if (newLength == 0) { stackSlotPhis = null; } else { SSAPhiInstruction[] old = stackSlotPhis; stackSlotPhis = new SSAPhiInstruction[newLength]; int j = 0; for (int i = 0; i < old.length; i++) { if (old[i] != null) { stackSlotPhis[j++] = old[i]; } } } } nRemoved = 0; if (localPhis != null) { for (int i = 0; i < localPhis.length; i++) { if (toRemove.contains(localPhis[i])) { localPhis[i] = null; nRemoved++; } } } if (nRemoved > 0) { int newLength = localPhis.length - nRemoved; if (newLength == 0) { localPhis = null; } else { SSAPhiInstruction[] old = localPhis; localPhis = new SSAPhiInstruction[newLength]; int j = 0; for (int i = 0; i < old.length; i++) { if (old[i] != null) { localPhis[j++] = old[i]; } } } } } public SSAPiInstruction getPiForRefAndPath(int n, Object path) { return piInstructions.get(new RefPathKey(n, this, path)); } private final LinkedList<SSAPiInstruction> blockPiInstructions = new LinkedList<SSAPiInstruction>(); /** * * @param n can be the val in the pi instruction * @param path can be the successor block in the pi instruction * @param pi */ public void addPiForRefAndPath(int n, Object path, SSAPiInstruction pi) { piInstructions.put(new RefPathKey(n, this, path), pi); blockPiInstructions.add(pi); } @Override public Iterator<SSAPiInstruction> iteratePis() { return blockPiInstructions.iterator(); } public Iterator<SSAInstruction> iterateNormalInstructions() { int lookup = getFirstInstructionIndex(); final int end = getLastInstructionIndex(); // skip to first non-null instruction while (lookup <= end && instructions[lookup] == null) { lookup++; } final int dummy = lookup; return new Iterator<SSAInstruction>() { private int start = dummy; @Override public boolean hasNext() { return (start <= end); } @Override public SSAInstruction next() { SSAInstruction i = instructions[start]; start++; while (start <= end && instructions[start] == null) { start++; } return i; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } /** * TODO: make this more efficient if needed */ public List<SSAInstruction> getAllInstructions() { compressPhis(); ArrayList<SSAInstruction> result = new ArrayList<SSAInstruction>(); for (Iterator<? extends SSAInstruction> it = iteratePhis(); it.hasNext();) { result.add(it.next()); } for (int i = getFirstInstructionIndex(); i <= getLastInstructionIndex(); i++) { SSAInstruction s = instructions[i]; if (s != null) { result.add(s); } } for (Iterator<? extends SSAInstruction> it = iteratePis(); it.hasNext();) { result.add(it.next()); } return result; } /** * rewrite the phi arrays so they have no null entries. */ private void compressPhis() { if (stackSlotPhis != null && stackSlotPhis[stackSlotPhis.length - 1] == null) { int size = countNonNull(stackSlotPhis); if (size == 0) { stackSlotPhis = null; } else { SSAPhiInstruction[] old = stackSlotPhis; stackSlotPhis = new SSAPhiInstruction[size]; int j = 0; for (int i = 0; i < old.length; i++) { if (old[i] != null) { stackSlotPhis[j++] = old[i]; } } } } if (localPhis != null && localPhis[localPhis.length - 1] == null) { int size = countNonNull(localPhis); if (size == 0) { localPhis = null; } else { SSAPhiInstruction[] old = localPhis; localPhis = new SSAPhiInstruction[size]; int j = 0; for (int i = 0; i < old.length; i++) { if (old[i] != null) { localPhis[j++] = old[i]; } } } } } private int countNonNull(SSAPhiInstruction[] a) { int result = 0; for (int i = 0; i < a.length; i++) { if (a[i] != null) { result++; } } return result; } @Override public Iterator<SSAInstruction> iterator() { return getAllInstructions().iterator(); } /** * @return true iff this basic block has at least one phi */ public boolean hasPhi() { return stackSlotPhis != null || localPhis != null; } /* * @see com.ibm.wala.util.graph.INodeWithNumber#getGraphNodeId() */ @Override public int getGraphNodeId() { return number; } /* * @see com.ibm.wala.util.graph.INodeWithNumber#setGraphNodeId(int) */ @Override public void setGraphNodeId(int number) { // TODO Auto-generated method stub } /** * @see java.lang.Object#toString() */ @Override public String toString() { return "BB[SSA:" + getFirstInstructionIndex() + ".." + getLastInstructionIndex() + "]" + getNumber() + " - " + method.getSignature(); } private SSACFG getGraph() { return SSACFG.this; } @Override public boolean equals(Object arg0) { if (arg0 instanceof BasicBlock) { BasicBlock b = (BasicBlock) arg0; if (getNumber() == b.getNumber()) { if (getMethod().equals(b.getMethod())) { return getGraph().equals(b.getGraph()); } else { return false; } } else { return false; } } else { return false; } } @Override public IMethod getMethod() { return method; } @Override public int hashCode() { return delegate.getNode(getNumber()).hashCode() * 6271; } /* * @see com.ibm.wala.cfg.IBasicBlock#isExitBlock() */ @Override public boolean isExitBlock() { return this == SSACFG.this.exit(); } /* * @see com.ibm.wala.cfg.IBasicBlock#isEntryBlock() */ @Override public boolean isEntryBlock() { return this == SSACFG.this.entry(); } @Override public SSAInstruction getLastInstruction() { return instructions[getLastInstructionIndex()]; } /** * The {@link ExceptionHandlerBasicBlock} subclass will override this. * * @see com.ibm.wala.ssa.ISSABasicBlock#getCaughtExceptionTypes() */ @Override public Iterator<TypeReference> getCaughtExceptionTypes() { return EmptyIterator.instance(); } } public class ExceptionHandlerBasicBlock extends BasicBlock { /** * The type of the exception caught by this block. */ private TypeReference[] exceptionTypes; private final static int initialCapacity = 3; private int nExceptionTypes = 0; /** * Instruction that defines the exception value this block catches */ private SSAGetCaughtExceptionInstruction catchInstruction; public ExceptionHandlerBasicBlock(int number) { super(number); } public SSAGetCaughtExceptionInstruction getCatchInstruction() { return catchInstruction; } public void setCatchInstruction(SSAGetCaughtExceptionInstruction catchInstruction) { this.catchInstruction = catchInstruction; } @Override public Iterator<TypeReference> getCaughtExceptionTypes() { return new Iterator<TypeReference>() { int next = 0; @Override public boolean hasNext() { return next < nExceptionTypes; } @Override public TypeReference next() { return exceptionTypes[next++]; } @Override public void remove() { Assertions.UNREACHABLE(); } }; } @Override public String toString() { return "BB(Handler)[SSA]" + getNumber() + " - " + method.getSignature(); } public void addCaughtExceptionType(TypeReference exceptionType) { if (exceptionTypes == null) { exceptionTypes = new TypeReference[initialCapacity]; } nExceptionTypes++; if (nExceptionTypes > exceptionTypes.length) { TypeReference[] temp = exceptionTypes; exceptionTypes = new TypeReference[nExceptionTypes * 2]; System.arraycopy(temp, 0, exceptionTypes, 0, temp.length); } exceptionTypes[nExceptionTypes - 1] = exceptionType; } @Override public List<SSAInstruction> getAllInstructions() { List<SSAInstruction> result = super.getAllInstructions(); if (catchInstruction != null) { // it's disturbing that catchInstruction can be null here. must be some // strange corner case // involving dead code. oh well .. keep on chugging until we have hard // evidence that this is a bug result.add(0, catchInstruction); } return result; } } @Override public String toString() { StringBuffer s = new StringBuffer(""); for (int i = 0; i <= getNumber(exit()); i++) { BasicBlock bb = getNode(i); s.append("BB").append(i).append("[").append(bb.getFirstInstructionIndex()).append("..").append(bb.getLastInstructionIndex()) .append("]\n"); Iterator succNodes = getSuccNodes(bb); while (succNodes.hasNext()) { s.append(" -> BB").append(((BasicBlock) succNodes.next()).getNumber()).append("\n"); } } return s.toString(); } @Override public BitVector getCatchBlocks() { return delegate.getCatchBlocks(); } /** * is the given i a catch block? * * @return true if catch block, false otherwise */ public boolean isCatchBlock(int i) { return delegate.isCatchBlock(i); } /* * @see com.ibm.wala.cfg.ControlFlowGraph#entry() */ @Override public SSACFG.BasicBlock entry() { return basicBlocks[0]; } /* * @see com.ibm.wala.cfg.ControlFlowGraph#exit() */ @Override public SSACFG.BasicBlock exit() { return exit; } /* * @see com.ibm.wala.util.graph.NumberedGraph#getNumber(com.ibm.wala.util.graph.Node) */ @Override public int getNumber(ISSABasicBlock b) throws IllegalArgumentException { if (b == null) { throw new IllegalArgumentException("N == null"); } return b.getNumber(); } /* * @see com.ibm.wala.util.graph.NumberedGraph#getNode(int) */ @Override public BasicBlock getNode(int number) { return basicBlocks[number]; } /* * @see com.ibm.wala.util.graph.NumberedGraph#getMaxNumber() */ @Override public int getMaxNumber() { return basicBlocks.length - 1; } /* * @see com.ibm.wala.util.graph.Graph#iterateNodes() */ @Override public Iterator<ISSABasicBlock> iterator() { ArrayList<ISSABasicBlock> list = new ArrayList<ISSABasicBlock>(); for (BasicBlock b : basicBlocks) { list.add(b); } return list.iterator(); } /* * @see com.ibm.wala.util.graph.Graph#getNumberOfNodes() */ @Override public int getNumberOfNodes() { return delegate.getNumberOfNodes(); } /* * @see com.ibm.wala.util.graph.Graph#getPredNodes(com.ibm.wala.util.graph.Node) */ @Override public Iterator<ISSABasicBlock> getPredNodes(ISSABasicBlock b) throws IllegalArgumentException { if (b == null) { throw new IllegalArgumentException("b == null"); } IBasicBlock<IInstruction> n = delegate.getNode(b.getNumber()); final Iterator i = delegate.getPredNodes(n); return new Iterator<ISSABasicBlock>() { @Override public boolean hasNext() { return i.hasNext(); } @Override public BasicBlock next() { IBasicBlock n = (IBasicBlock) i.next(); int number = n.getNumber(); return basicBlocks[number]; } @Override public void remove() { Assertions.UNREACHABLE(); } }; } /* * @see com.ibm.wala.util.graph.Graph#getPredNodeCount(com.ibm.wala.util.graph.Node) */ @Override public int getPredNodeCount(ISSABasicBlock b) throws IllegalArgumentException { if (b == null) { throw new IllegalArgumentException("b == null"); } IBasicBlock<IInstruction> n = delegate.getNode(b.getNumber()); return delegate.getPredNodeCount(n); } /* * @see com.ibm.wala.util.graph.Graph#getSuccNodes(com.ibm.wala.util.graph.Node) */ @Override public Iterator<ISSABasicBlock> getSuccNodes(ISSABasicBlock b) throws IllegalArgumentException { if (b == null) { throw new IllegalArgumentException("b == null"); } IBasicBlock<IInstruction> n = delegate.getNode(b.getNumber()); final Iterator i = delegate.getSuccNodes(n); return new Iterator<ISSABasicBlock>() { @Override public boolean hasNext() { return i.hasNext(); } @Override public ISSABasicBlock next() { IBasicBlock n = (IBasicBlock) i.next(); int number = n.getNumber(); return basicBlocks[number]; } @Override public void remove() { Assertions.UNREACHABLE(); } }; } /* * @see com.ibm.wala.util.graph.Graph#getSuccNodeCount(com.ibm.wala.util.graph.Node) */ @Override public int getSuccNodeCount(ISSABasicBlock b) throws IllegalArgumentException { if (b == null) { throw new IllegalArgumentException("b == null"); } IBasicBlock<IInstruction> n = delegate.getNode(b.getNumber()); return delegate.getSuccNodeCount(n); } /* * @see com.ibm.wala.util.graph.NumberedGraph#addNode(com.ibm.wala.util.graph.Node) */ @Override public void addNode(ISSABasicBlock n) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } @Override public void addEdge(ISSABasicBlock src, ISSABasicBlock dst) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } @Override public void removeEdge(ISSABasicBlock src, ISSABasicBlock dst) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /* * @see com.ibm.wala.util.graph.EdgeManager#removeEdges(com.ibm.wala.util.graph.Node) */ @Override public void removeAllIncidentEdges(ISSABasicBlock node) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /* * @see com.ibm.wala.util.graph.Graph#removeNode(com.ibm.wala.util.graph.Node) */ @Override public void removeNodeAndEdges(ISSABasicBlock N) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /* * @see com.ibm.wala.util.graph.NodeManager#remove(com.ibm.wala.util.graph.Node) */ @Override public void removeNode(ISSABasicBlock n) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } /* * @see com.ibm.wala.cfg.ControlFlowGraph#getProgramCounter(int) */ @Override public int getProgramCounter(int index) { // delegate to incoming cfg. return delegate.getProgramCounter(index); } /* * @see com.ibm.wala.util.graph.Graph#containsNode(com.ibm.wala.util.graph.Node) */ @Override public boolean containsNode(ISSABasicBlock N) { if (N instanceof BasicBlock) { return basicBlocks[getNumber(N)] == N; } else { return false; } } /* * @see com.ibm.wala.cfg.ControlFlowGraph#getMethod() */ @Override public IMethod getMethod() { return method; } /* * @see com.ibm.wala.cfg.ControlFlowGraph#getExceptionalSuccessors(com.ibm.wala.cfg.IBasicBlock) */ @Override public List<ISSABasicBlock> getExceptionalSuccessors(final ISSABasicBlock b) { if (b == null) { throw new IllegalArgumentException("b is null"); } final IBasicBlock<IInstruction> n = delegate.getNode(b.getNumber()); final Iterator<IBasicBlock<IInstruction>> i = delegate.getExceptionalSuccessors(n).iterator(); final List<ISSABasicBlock> c = new ArrayList<ISSABasicBlock>(getSuccNodeCount(b)); for (; i.hasNext();) { final IBasicBlock<IInstruction> s = i.next(); c.add(basicBlocks[delegate.getNumber(s)]); } return c; } /* * @see com.ibm.wala.cfg.ControlFlowGraph#getExceptionalSuccessors(com.ibm.wala.cfg.IBasicBlock) */ @Override public Collection<ISSABasicBlock> getExceptionalPredecessors(ISSABasicBlock b) { if (b == null) { throw new IllegalArgumentException("b is null"); } IBasicBlock<IInstruction> n = delegate.getNode(b.getNumber()); Function<IBasicBlock<IInstruction>, ISSABasicBlock> f = new Function<IBasicBlock<IInstruction>, ISSABasicBlock>() { @Override public ISSABasicBlock apply(IBasicBlock<IInstruction> object) { return basicBlocks[delegate.getNumber(object)]; } }; return Iterator2Collection.toSet(new MapIterator<IBasicBlock<IInstruction>, ISSABasicBlock>(delegate .getExceptionalPredecessors(n).iterator(), f)); } private IBasicBlock<IInstruction> getUnderlyingBlock(SSACFG.BasicBlock block) { return delegate.getNode(getNumber(block)); } /** * has exceptional edge src -> dest * * @throws IllegalArgumentException if dest is null */ public boolean hasExceptionalEdge(BasicBlock src, BasicBlock dest) { if (dest == null) { throw new IllegalArgumentException("dest is null"); } if (dest.isExitBlock()) { int srcNum = getNumber(src); return delegate.getExceptionalToExit().get(srcNum); } return delegate.hasExceptionalEdge(getUnderlyingBlock(src), getUnderlyingBlock(dest)); } /** * has normal edge src -> dest * * @throws IllegalArgumentException if dest is null */ public boolean hasNormalEdge(BasicBlock src, BasicBlock dest) { if (dest == null) { throw new IllegalArgumentException("dest is null"); } if (dest.isExitBlock()) { int srcNum = getNumber(src); return delegate.getNormalToExit().get(srcNum); } return delegate.hasNormalEdge(getUnderlyingBlock(src), getUnderlyingBlock(dest)); } /* * @see com.ibm.wala.cfg.ControlFlowGraph#getNormalSuccessors(com.ibm.wala.cfg.IBasicBlock) */ @Override public Collection<ISSABasicBlock> getNormalSuccessors(ISSABasicBlock b) { if (b == null) { throw new IllegalArgumentException("b is null"); } IBasicBlock<IInstruction> n = delegate.getNode(b.getNumber()); final Iterator<IBasicBlock<IInstruction>> i = delegate.getNormalSuccessors(n).iterator(); Collection<ISSABasicBlock> c = new ArrayList<ISSABasicBlock>(getSuccNodeCount(b)); for (; i.hasNext();) { IBasicBlock<IInstruction> s = i.next(); c.add(basicBlocks[delegate.getNumber(s)]); } return c; } /* * @see com.ibm.wala.cfg.ControlFlowGraph#getNormalSuccessors(com.ibm.wala.cfg.IBasicBlock) */ @Override public Collection<ISSABasicBlock> getNormalPredecessors(ISSABasicBlock b) { if (b == null) { throw new IllegalArgumentException("b is null"); } IBasicBlock<IInstruction> n = delegate.getNode(b.getNumber()); final Iterator<IBasicBlock<IInstruction>> i = delegate.getNormalPredecessors(n).iterator(); Collection<ISSABasicBlock> c = new ArrayList<ISSABasicBlock>(getPredNodeCount(b)); for (; i.hasNext();) { IBasicBlock<IInstruction> s = i.next(); c.add(basicBlocks[delegate.getNumber(s)]); } return c; } /* * @see com.ibm.wala.util.graph.NumberedNodeManager#iterateNodes(com.ibm.wala.util.intset.IntSet) */ @Override public Iterator<ISSABasicBlock> iterateNodes(IntSet s) { return new NumberedNodeIterator<ISSABasicBlock>(s, this); } @Override public void removeIncomingEdges(ISSABasicBlock node) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } @Override public void removeOutgoingEdges(ISSABasicBlock node) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } @Override public boolean hasEdge(ISSABasicBlock src, ISSABasicBlock dst) throws UnimplementedError { return getSuccNodeNumbers(src).contains(getNumber(dst)); } @Override public IntSet getSuccNodeNumbers(ISSABasicBlock b) throws IllegalArgumentException { if (b == null) { throw new IllegalArgumentException("b == null"); } IBasicBlock<IInstruction> n = delegate.getNode(b.getNumber()); return delegate.getSuccNodeNumbers(n); } @Override public IntSet getPredNodeNumbers(ISSABasicBlock node) throws UnimplementedError { Assertions.UNREACHABLE(); return null; } /** * A warning for when we fail to resolve the type for a checkcast */ private static class ExceptionLoadFailure extends Warning { final TypeReference type; final IMethod method; ExceptionLoadFailure(TypeReference type, IMethod method) { super(Warning.MODERATE); this.type = type; this.method = method; } @Override public String getMsg() { return getClass().toString() + " : " + type + " " + method; } public static ExceptionLoadFailure create(TypeReference type, IMethod method) { return new ExceptionLoadFailure(type, method); } } /** * @return the basic block with a particular number */ public BasicBlock getBasicBlock(int bb) { return basicBlocks[bb]; } }