/******************************************************************************* * 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.analysis; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import com.ibm.wala.cfg.ControlFlowGraph; import com.ibm.wala.classLoader.IMethod; import com.ibm.wala.ssa.IR; import com.ibm.wala.ssa.ISSABasicBlock; import com.ibm.wala.ssa.SSACFG.ExceptionHandlerBasicBlock; import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSAPhiInstruction; import com.ibm.wala.ssa.SSAPiInstruction; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.collections.EmptyIterator; import com.ibm.wala.util.collections.HashSetFactory; import com.ibm.wala.util.collections.Iterator2Collection; import com.ibm.wala.util.collections.NonNullSingletonIterator; import com.ibm.wala.util.collections.SimpleVector; import com.ibm.wala.util.debug.Assertions; import com.ibm.wala.util.debug.UnimplementedError; import com.ibm.wala.util.intset.BitVector; import com.ibm.wala.util.intset.IntSet; import com.ibm.wala.util.intset.MutableSparseIntSet; /** * A view of a control flow graph where each basic block corresponds to exactly one SSA instruction index. * * Prototype: Not terribly efficient. */ public class ExplodedControlFlowGraph implements ControlFlowGraph<SSAInstruction, IExplodedBasicBlock> { private final static int ENTRY_INDEX = -1; private final static int EXIT_INDEX = -2; private final IR ir; /** * The ith element of this vector is the basic block holding instruction i. this basic block has number i+1. */ private final SimpleVector<IExplodedBasicBlock> normalNodes = new SimpleVector<IExplodedBasicBlock>(); private final Collection<IExplodedBasicBlock> allNodes = HashSetFactory.make(); private final IExplodedBasicBlock entry; private final IExplodedBasicBlock exit; private ExplodedControlFlowGraph(IR ir) { assert ir != null; this.ir = ir; this.entry = new ExplodedBasicBlock(ENTRY_INDEX, null); this.exit = new ExplodedBasicBlock(EXIT_INDEX, null); createNodes(); } private void createNodes() { allNodes.add(entry); allNodes.add(exit); for (ISSABasicBlock b : ir.getControlFlowGraph()) { for (int i = b.getFirstInstructionIndex(); i <= b.getLastInstructionIndex(); i++) { IExplodedBasicBlock bb = new ExplodedBasicBlock(i, b); normalNodes.set(i, bb); allNodes.add(bb); } } } public static ExplodedControlFlowGraph make(IR ir) { if (ir == null) { throw new IllegalArgumentException("ir == null"); } return new ExplodedControlFlowGraph(ir); } @Override public IExplodedBasicBlock entry() { return entry; } @Override public IExplodedBasicBlock exit() { return exit; } @Override public IExplodedBasicBlock getBlockForInstruction(int index) { return normalNodes.get(index); } /* * @see com.ibm.wala.cfg.ControlFlowGraph#getCatchBlocks() */ @Override public BitVector getCatchBlocks() { BitVector original = ir.getControlFlowGraph().getCatchBlocks(); BitVector result = new BitVector(); for (int i = 0; i <= original.max(); i++) { if (original.get(i)) { result.set(i + 1); } } return result; } @Override public Collection<IExplodedBasicBlock> getExceptionalPredecessors(IExplodedBasicBlock bb) { ExplodedBasicBlock eb = (ExplodedBasicBlock) bb; assert eb != null; if (eb.equals(entry)) { return Collections.emptySet(); } if (eb.isExitBlock() || eb.instructionIndex == eb.original.getFirstInstructionIndex()) { List<IExplodedBasicBlock> result = new ArrayList<IExplodedBasicBlock>(); for (ISSABasicBlock s : ir.getControlFlowGraph().getExceptionalPredecessors(eb.original)) { assert normalNodes.get(s.getLastInstructionIndex()) != null; result.add(normalNodes.get(s.getLastInstructionIndex())); } return result; } else { return Collections.emptySet(); } } @Override public List<IExplodedBasicBlock> getExceptionalSuccessors(IExplodedBasicBlock bb) { ExplodedBasicBlock eb = (ExplodedBasicBlock) bb; assert eb != null; if (eb.equals(exit)) { return Collections.emptyList(); } if (eb.isEntryBlock() || eb.instructionIndex == eb.original.getLastInstructionIndex()) { List<IExplodedBasicBlock> result = new ArrayList<IExplodedBasicBlock>(); ISSABasicBlock orig = eb.original; if (eb.isEntryBlock() && orig == null) { orig = ir.getControlFlowGraph().entry(); } for (ISSABasicBlock s : ir.getControlFlowGraph().getExceptionalSuccessors(orig)) { if (s.equals(ir.getControlFlowGraph().exit())) { result.add(exit()); } else { assert normalNodes.get(s.getFirstInstructionIndex()) != null; result.add(normalNodes.get(s.getFirstInstructionIndex())); } } return result; } else { return Collections.emptyList(); } } @Override public SSAInstruction[] getInstructions() { return ir.getInstructions(); } @Override public IMethod getMethod() throws UnimplementedError { return ir.getMethod(); } @Override public Collection<IExplodedBasicBlock> getNormalPredecessors(IExplodedBasicBlock bb) { ExplodedBasicBlock eb = (ExplodedBasicBlock) bb; assert eb != null; if (eb.equals(entry)) { return Collections.emptySet(); } if (eb.isExitBlock() || eb.instructionIndex == eb.original.getFirstInstructionIndex()) { List<IExplodedBasicBlock> result = new ArrayList<IExplodedBasicBlock>(); for (ISSABasicBlock s : ir.getControlFlowGraph().getNormalPredecessors(eb.original)) { if (s.equals(ir.getControlFlowGraph().entry())) { if (s.getLastInstructionIndex() >= 0) { assert normalNodes.get(s.getLastInstructionIndex()) != null; result.add(normalNodes.get(s.getLastInstructionIndex())); } else { result.add(entry()); } } else { assert normalNodes.get(s.getLastInstructionIndex()) != null; result.add(normalNodes.get(s.getLastInstructionIndex())); } } return result; } else { assert normalNodes.get(eb.instructionIndex - 1) != null; return Collections.singleton(normalNodes.get(eb.instructionIndex - 1)); } } @Override public Collection<IExplodedBasicBlock> getNormalSuccessors(IExplodedBasicBlock bb) { ExplodedBasicBlock eb = (ExplodedBasicBlock) bb; assert eb != null; if (eb.equals(exit)) { return Collections.emptySet(); } if (eb.isEntryBlock()) { return Collections.singleton(normalNodes.get(0)); } if (eb.instructionIndex == eb.original.getLastInstructionIndex()) { List<IExplodedBasicBlock> result = new ArrayList<IExplodedBasicBlock>(); for (ISSABasicBlock s : ir.getControlFlowGraph().getNormalSuccessors(eb.original)) { if (s.equals(ir.getControlFlowGraph().exit())) { result.add(exit()); } else { assert normalNodes.get(s.getFirstInstructionIndex()) != null; result.add(normalNodes.get(s.getFirstInstructionIndex())); } } return result; } else { assert normalNodes.get(eb.instructionIndex + 1) != null; return Collections.singleton(normalNodes.get(eb.instructionIndex + 1)); } } /* * @see com.ibm.wala.cfg.ControlFlowGraph#getProgramCounter(int) */ @Override public int getProgramCounter(int index) throws UnimplementedError { return ir.getControlFlowGraph().getProgramCounter(index); } @Override public void removeNodeAndEdges(IExplodedBasicBlock N) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } @Override public void addNode(IExplodedBasicBlock n) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } @Override public boolean containsNode(IExplodedBasicBlock N) { return allNodes.contains(N); } @Override public int getNumberOfNodes() { return allNodes.size(); } @Override public Iterator<IExplodedBasicBlock> iterator() { return allNodes.iterator(); } @Override public void removeNode(IExplodedBasicBlock n) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } @Override public void addEdge(IExplodedBasicBlock src, IExplodedBasicBlock dst) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } @Override public int getPredNodeCount(IExplodedBasicBlock bb) throws IllegalArgumentException { ExplodedBasicBlock eb = (ExplodedBasicBlock) bb; if (eb == null) { throw new IllegalArgumentException("eb == null"); } if (eb.isEntryBlock()) { return 0; } if (eb.isExitBlock()) { return ir.getControlFlowGraph().getPredNodeCount(ir.getControlFlowGraph().exit()); } if (eb.instructionIndex == eb.original.getFirstInstructionIndex()) { if (eb.original.isEntryBlock()) { return 1; } else { return ir.getControlFlowGraph().getPredNodeCount(eb.original); } } else { return 1; } } @Override public Iterator<IExplodedBasicBlock> getPredNodes(IExplodedBasicBlock bb) throws IllegalArgumentException { ExplodedBasicBlock eb = (ExplodedBasicBlock) bb; if (eb == null) { throw new IllegalArgumentException("eb == null"); } if (eb.isEntryBlock()) { return EmptyIterator.instance(); } ISSABasicBlock original = eb.isExitBlock() ? ir.getControlFlowGraph().exit() : eb.original; if (eb.isExitBlock() || eb.instructionIndex == eb.original.getFirstInstructionIndex()) { List<IExplodedBasicBlock> result = new ArrayList<IExplodedBasicBlock>(); if (eb.original != null && eb.original.isEntryBlock()) { result.add(entry); } for (Iterator<ISSABasicBlock> it = ir.getControlFlowGraph().getPredNodes(original); it.hasNext();) { ISSABasicBlock s = it.next(); if (s.isEntryBlock()) { // it's possible for an entry block to have instructions; in this case, add // the exploded basic block for the last instruction in the entry block if (s.getLastInstructionIndex() >= 0) { result.add(normalNodes.get(s.getLastInstructionIndex())); } else { result.add(entry); } } else { assert normalNodes.get(s.getLastInstructionIndex()) != null; result.add(normalNodes.get(s.getLastInstructionIndex())); } } return result.iterator(); } else { assert normalNodes.get(eb.instructionIndex - 1) != null; return NonNullSingletonIterator.make(normalNodes.get(eb.instructionIndex - 1)); } } @Override public int getSuccNodeCount(IExplodedBasicBlock N) throws UnimplementedError { return Iterator2Collection.toSet(getSuccNodes(N)).size(); } /* * @see com.ibm.wala.util.graph.EdgeManager#getSuccNodes(java.lang.Object) */ @Override public Iterator<IExplodedBasicBlock> getSuccNodes(IExplodedBasicBlock bb) { ExplodedBasicBlock eb = (ExplodedBasicBlock) bb; assert eb != null; if (eb.isExitBlock()) { return EmptyIterator.instance(); } if (eb.isEntryBlock()) { IExplodedBasicBlock z = normalNodes.get(0); return z == null ? EmptyIterator.<IExplodedBasicBlock> instance() : NonNullSingletonIterator.make(z); } if (eb.instructionIndex == eb.original.getLastInstructionIndex()) { List<IExplodedBasicBlock> result = new ArrayList<IExplodedBasicBlock>(); for (Iterator<ISSABasicBlock> it = ir.getControlFlowGraph().getSuccNodes(eb.original); it.hasNext();) { ISSABasicBlock s = it.next(); if (s.equals(ir.getControlFlowGraph().exit())) { result.add(exit()); } else { // there can be a weird corner case where a void method without a // return statement // can have trailing empty basic blocks with no instructions. ignore // these. if (normalNodes.get(s.getFirstInstructionIndex()) != null) { result.add(normalNodes.get(s.getFirstInstructionIndex())); } } } return result.iterator(); } else { assert normalNodes.get(eb.instructionIndex + 1) != null; return NonNullSingletonIterator.make(normalNodes.get(eb.instructionIndex + 1)); } } @Override public boolean hasEdge(IExplodedBasicBlock src, IExplodedBasicBlock dst) throws UnimplementedError { for (Iterator<IExplodedBasicBlock> it = getSuccNodes(src); it.hasNext();) { IExplodedBasicBlock succ = it.next(); if (succ == dst) { return true; } } return false; } @Override public void removeAllIncidentEdges(IExplodedBasicBlock node) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } @Override public void removeEdge(IExplodedBasicBlock src, IExplodedBasicBlock dst) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } @Override public void removeIncomingEdges(IExplodedBasicBlock node) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } @Override public void removeOutgoingEdges(IExplodedBasicBlock node) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } @Override public int getMaxNumber() { return getNumberOfNodes() - 1; } @Override public IExplodedBasicBlock getNode(int number) { if (number == 0) { return entry(); } else if (number == getNumberOfNodes() - 1) { return exit(); } else { return normalNodes.get(number - 1); } } @Override public int getNumber(IExplodedBasicBlock n) throws IllegalArgumentException { if (n == null) { throw new IllegalArgumentException("n == null"); } return n.getNumber(); } @Override public Iterator<IExplodedBasicBlock> iterateNodes(IntSet s) throws UnimplementedError { Assertions.UNREACHABLE(); return null; } @Override public IntSet getPredNodeNumbers(IExplodedBasicBlock node) { MutableSparseIntSet result = MutableSparseIntSet.makeEmpty(); for (Iterator<? extends IExplodedBasicBlock> it = getPredNodes(node); it.hasNext();) { result.add(getNumber(it.next())); } return result; } @Override public IntSet getSuccNodeNumbers(IExplodedBasicBlock node) throws UnimplementedError { Assertions.UNREACHABLE(); return null; } /** * A basic block with exactly one normal instruction (which may be null), corresponding to a single instruction index in the SSA * instruction array. * * The block may also have phis. */ private class ExplodedBasicBlock implements IExplodedBasicBlock { private final int instructionIndex; private final ISSABasicBlock original; public ExplodedBasicBlock(int instructionIndex, ISSABasicBlock original) { this.instructionIndex = instructionIndex; this.original = original; } @SuppressWarnings("unused") public ExplodedControlFlowGraph getExplodedCFG() { return ExplodedControlFlowGraph.this; } @Override public Iterator<TypeReference> getCaughtExceptionTypes() { if (original instanceof ExceptionHandlerBasicBlock) { ExceptionHandlerBasicBlock eb = (ExceptionHandlerBasicBlock) original; return eb.getCaughtExceptionTypes(); } else { return EmptyIterator.instance(); } } @Override public int getFirstInstructionIndex() { return instructionIndex; } @Override public int getLastInstructionIndex() { return instructionIndex; } @Override public IMethod getMethod() { return ExplodedControlFlowGraph.this.getMethod(); } @Override public int getNumber() { if (isEntryBlock()) { return 0; } else if (isExitBlock()) { return getNumberOfNodes() - 1; } else { return instructionIndex + 1; } } @Override public boolean isCatchBlock() { if (original == null) { return false; } return (original.isCatchBlock() && instructionIndex == original.getFirstInstructionIndex()); } @Override public SSAGetCaughtExceptionInstruction getCatchInstruction() { if (!(original instanceof ExceptionHandlerBasicBlock)) { throw new IllegalArgumentException("block does not represent an exception handler"); } ExceptionHandlerBasicBlock e = (ExceptionHandlerBasicBlock) original; return e.getCatchInstruction(); } @Override public boolean isEntryBlock() { return instructionIndex == ENTRY_INDEX; } @Override public boolean isExitBlock() { return instructionIndex == EXIT_INDEX; } @Override public int getGraphNodeId() { return getNumber(); } @Override public void setGraphNodeId(int number) { Assertions.UNREACHABLE(); } @Override public Iterator<SSAInstruction> iterator() { if (isEntryBlock() || isExitBlock() || getInstruction() == null) { return EmptyIterator.instance(); } else { return NonNullSingletonIterator.make(getInstruction()); } } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + instructionIndex; result = prime * result + ((original == null) ? 0 : original.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final ExplodedBasicBlock other = (ExplodedBasicBlock) obj; if (instructionIndex != other.instructionIndex) return false; if (original == null) { if (other.original != null) return false; } else if (!original.equals(other.original)) return false; return true; } @Override public SSAInstruction getInstruction() { if (isEntryBlock() || isExitBlock()) { return null; } else { return ir.getInstructions()[instructionIndex]; } } @Override public SSAInstruction getLastInstruction() { if (getLastInstructionIndex() < 0) { return null; } else { return ir.getInstructions()[getLastInstructionIndex()]; } } @Override public Iterator<SSAPhiInstruction> iteratePhis() { if (isEntryBlock() || isExitBlock() || instructionIndex != original.getFirstInstructionIndex()) { return EmptyIterator.instance(); } else { return original.iteratePhis(); } } @Override public Iterator<SSAPiInstruction> iteratePis() { if (isEntryBlock() || isExitBlock() || instructionIndex != original.getLastInstructionIndex()) { return EmptyIterator.instance(); } else { return original.iteratePis(); } } @Override public String toString() { if (isEntryBlock()) { return "ExplodedBlock[" + getNumber() + "](entry:" + getMethod() + ")"; } if (isExitBlock()) { return "ExplodedBlock[" + getNumber() + "](exit:" + getMethod() + ")"; } return "ExplodedBlock[" + getNumber() + "](original:" + original + ")"; } } @Override public String toString() { StringBuffer s = new StringBuffer(""); for (Iterator<IExplodedBasicBlock> it = iterator(); it.hasNext();) { IExplodedBasicBlock bb = it.next(); s.append("BB").append(getNumber(bb)).append("\n"); Iterator<? extends IExplodedBasicBlock> succNodes = getSuccNodes(bb); while (succNodes.hasNext()) { s.append(" -> BB").append(getNumber(succNodes.next())).append("\n"); } } return s.toString(); } public IR getIR() { return ir; } }