/******************************************************************************* * 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.Collection; import java.util.Iterator; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.Language; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.shrikeCT.InvalidClassFileException; import com.ibm.wala.ssa.IR; import com.ibm.wala.ssa.ISSABasicBlock; import com.ibm.wala.ssa.SSACFG; import com.ibm.wala.ssa.SSACFG.ExceptionHandlerBasicBlock; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSAInvokeInstruction; import com.ibm.wala.ssa.SSAReturnInstruction; import com.ibm.wala.ssa.SSAThrowInstruction; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.WalaException; import com.ibm.wala.util.debug.Assertions; import com.ibm.wala.util.graph.Graph; import com.ibm.wala.util.graph.impl.SlowSparseNumberedGraph; /** * Utility class to remove exceptional edges to exit() from a CFG */ public class CFGSanitizer { /** * Return a view of the {@link ControlFlowGraph} for an {@link IR}, which elides all exceptional exits from PEIs in the IR. */ public static Graph<ISSABasicBlock> sanitize(IR ir, IClassHierarchy cha) throws IllegalArgumentException, WalaException { if (ir == null) { throw new IllegalArgumentException("ir cannot be null"); } ControlFlowGraph<SSAInstruction, ISSABasicBlock> cfg = ir.getControlFlowGraph(); Graph<ISSABasicBlock> g = SlowSparseNumberedGraph.make(); // add all nodes to the graph for (Iterator<? extends ISSABasicBlock> it = cfg.iterator(); it.hasNext();) { g.addNode(it.next()); } // add all edges to the graph, except those that go to exit for (Iterator it = cfg.iterator(); it.hasNext();) { ISSABasicBlock b = (ISSABasicBlock) it.next(); for (Iterator it2 = cfg.getSuccNodes(b); it2.hasNext();) { ISSABasicBlock b2 = (ISSABasicBlock) it2.next(); if (!b2.isExitBlock()) { g.addEdge(b, b2); } } } // now add edges to exit, ignoring undeclared exceptions ISSABasicBlock exit = cfg.exit(); for (Iterator it = cfg.getPredNodes(exit); it.hasNext();) { // for each predecessor of exit ... ISSABasicBlock b = (ISSABasicBlock) it.next(); SSAInstruction s = ir.getInstructions()[b.getLastInstructionIndex()]; if (s == null) { // TODO: this shouldn't happen? continue; } if (s instanceof SSAReturnInstruction || s instanceof SSAThrowInstruction || cfg.getSuccNodeCount(b) == 1) { // return or athrow, or some statement which is not an athrow or return whose only successor is the exit node (can only // occur in synthetic methods without a return statement? --MS); add edge to exit g.addEdge(b, exit); } else { // compute types of exceptions the pei may throw TypeReference[] exceptions = null; try { exceptions = computeExceptions(cha, ir, s); } catch (InvalidClassFileException e1) { e1.printStackTrace(); Assertions.UNREACHABLE(); } // remove any exceptions that are caught by catch blocks for (Iterator it2 = cfg.getSuccNodes(b); it2.hasNext();) { IBasicBlock c = (IBasicBlock) it2.next(); if (c.isCatchBlock()) { SSACFG.ExceptionHandlerBasicBlock cb = (ExceptionHandlerBasicBlock) c; for (Iterator it3 = cb.getCaughtExceptionTypes(); it3.hasNext();) { TypeReference ex = (TypeReference) it3.next(); IClass exClass = cha.lookupClass(ex); if (exClass == null) { throw new WalaException("failed to find " + ex); } for (int i = 0; i < exceptions.length; i++) { if (exceptions[i] != null) { IClass exi = cha.lookupClass(exceptions[i]); if (exi == null) { throw new WalaException("failed to find " + exceptions[i]); } if (cha.isSubclassOf(exi, exClass)) { exceptions[i] = null; } } } } } } // check the remaining uncaught exceptions TypeReference[] declared = null; try { declared = ir.getMethod().getDeclaredExceptions(); } catch (InvalidClassFileException e) { e.printStackTrace(); Assertions.UNREACHABLE(); } if (declared != null && exceptions != null) { for (int i = 0; i < exceptions.length; i++) { boolean isDeclared = false; if (exceptions[i] != null) { IClass exi = cha.lookupClass(exceptions[i]); if (exi == null) { throw new WalaException("failed to find " + exceptions[i]); } for (int j = 0; j < declared.length; j++) { IClass dc = cha.lookupClass(declared[j]); if (dc == null) { throw new WalaException("failed to find " + declared[j]); } if (cha.isSubclassOf(exi, dc)) { isDeclared = true; break; } } if (isDeclared) { // found a declared exceptional edge g.addEdge(b, exit); } } } } } } return g; } /** * What are the exception types which s may throw? */ private static TypeReference[] computeExceptions(IClassHierarchy cha, IR ir, SSAInstruction s) throws InvalidClassFileException { Collection c = null; Language l = ir.getMethod().getDeclaringClass().getClassLoader().getLanguage(); if (s instanceof SSAInvokeInstruction) { SSAInvokeInstruction call = (SSAInvokeInstruction) s; c = l.inferInvokeExceptions(call.getDeclaredTarget(), cha); } else { c = s.getExceptionTypes(); } if (c == null) { return null; } else { TypeReference[] exceptions = new TypeReference[c.size()]; Iterator it = c.iterator(); for (int i = 0; i < exceptions.length; i++) { exceptions[i] = (TypeReference) it.next(); } return exceptions; } } }