001: /*
002: * Bytecode Analysis Framework
003: * Copyright (C) 2003,2004 University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs.ba;
021:
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.LinkedList;
025: import java.util.List;
026: import java.util.Set;
027:
028: import org.apache.bcel.generic.MethodGen;
029:
030: import edu.umd.cs.findbugs.SystemProperties;
031: import edu.umd.cs.findbugs.ba.ch.Subtypes2;
032: import edu.umd.cs.findbugs.ba.type.ExceptionSet;
033: import edu.umd.cs.findbugs.ba.type.TypeAnalysis;
034: import edu.umd.cs.findbugs.ba.type.TypeDataflow;
035:
036: /**
037: * Prune a CFG to remove infeasible exception edges.
038: * In order to determine what kinds of exceptions can be thrown by
039: * explicit ATHROW instructions, type analysis must first be
040: * performed on the unpruned CFG.
041: *
042: * @author David Hovemeyer
043: * @see CFG
044: * @see TypeAnalysis
045: */
046: public class PruneInfeasibleExceptionEdges implements EdgeTypes {
047: private static final boolean DEBUG = SystemProperties
048: .getBoolean("cfg.prune.debug");
049: private static final boolean STATS = SystemProperties
050: .getBoolean("cfg.prune.stats");
051: private static int numEdgesPruned = 0;
052:
053: static {
054: if (STATS) {
055: Runtime.getRuntime().addShutdownHook(new Thread() {
056: @Override
057: public void run() {
058: System.err.println("Exception edges pruned: "
059: + numEdgesPruned);
060: }
061: });
062: }
063: }
064:
065: /**
066: * A momento to remind us of how we classified a particular
067: * exception edge. If pruning and classifying succeeds,
068: * then these momentos can be applied to actually change
069: * the state of the edges. The issue is that the entire
070: * pruning/classifying operation must either fail or succeed
071: * as a whole. Thus, we don't commit any CFG changes until
072: * we know everything was successful.
073: */
074: private static class MarkedEdge {
075: private Edge edge;
076: private int flag;
077:
078: public MarkedEdge(Edge edge, int flag) {
079: this .edge = edge;
080: this .flag = flag;
081: }
082:
083: public void apply() {
084: int flags = edge.getFlags();
085: flags |= this .flag;
086: edge.setFlags(flags);
087: }
088: }
089:
090: private CFG cfg;
091: private TypeDataflow typeDataflow;
092: private boolean cfgModified;
093:
094: /**
095: * Constructor.
096: *
097: * @param cfg the CFG to prune
098: * @param methodGen the method
099: * @param typeDataflow initialized TypeDataflow object for the CFG,
100: * indicating the types of all stack locations
101: */
102: public PruneInfeasibleExceptionEdges(CFG cfg, MethodGen methodGen,
103: TypeDataflow typeDataflow) {
104: this .cfg = cfg;
105: this .typeDataflow = typeDataflow;
106: }
107:
108: /**
109: * Prune infeasible exception edges from the CFG.
110: * If the method returns normally, then the operation
111: * was successful, and the CFG should no longer contain infeasible
112: * exception edges. If ClassNotFoundException or DataflowAnalysisException
113: * are thrown, then the operation was unsuccessful,
114: * <em>but the CFG is still valid because it was not modified</em>.
115: * If a runtime exception is thrown, then the CFG may be
116: * partially modified and should be considered invalid.
117: */
118: public void execute() throws ClassNotFoundException {
119: Set<Edge> deletedEdgeSet = new HashSet<Edge>();
120: List<MarkedEdge> markedEdgeList = new LinkedList<MarkedEdge>();
121:
122: // Mark edges to delete,
123: // mark edges to set properties of
124: for (Iterator<Edge> i = cfg.edgeIterator(); i.hasNext();) {
125: Edge edge = i.next();
126: if (!edge.isExceptionEdge())
127: continue;
128:
129: ExceptionSet exceptionSet = typeDataflow
130: .getEdgeExceptionSet(edge);
131: if (exceptionSet.isEmpty()) {
132: // No exceptions are actually thrown on this edge,
133: // so we can delete the edge.
134: deletedEdgeSet.add(edge);
135: } else {
136: if (exceptionSet
137: .isSingleton("java.lang.CloneNotSupportedException")
138: && cfg.getMethodName().endsWith(".clone()")) {
139: String className = cfg.getMethodGen()
140: .getClassName();
141: if (Subtypes2.instanceOf(className,
142: "java.lang.Cloneable")) {
143: deletedEdgeSet.add(edge);
144: continue;
145: }
146: }
147: // Some exceptions appear to be thrown on the edge.
148: // Mark to indicate if any of the exceptions are checked,
149: // and if any are explicit (checked or explicitly declared
150: // or thrown unchecked).
151: boolean someChecked = exceptionSet
152: .containsCheckedExceptions();
153: boolean someExplicit = exceptionSet
154: .containsExplicitExceptions();
155:
156: int flags = 0;
157: if (someChecked)
158: flags |= CHECKED_EXCEPTIONS_FLAG;
159: if (someExplicit)
160: flags |= EXPLICIT_EXCEPTIONS_FLAG;
161:
162: markedEdgeList.add(new MarkedEdge(edge, flags));
163: }
164: }
165:
166: // Remove deleted edges
167: for (Edge edge : deletedEdgeSet) {
168: cfg.removeEdge(edge);
169: if (STATS)
170: ++numEdgesPruned;
171: cfgModified = true;
172: }
173:
174: // Mark edges
175: for (MarkedEdge markedEdge : markedEdgeList) {
176: markedEdge.apply();
177: }
178: }
179:
180: /**
181: * @return true if modified
182: */
183: public boolean wasCFGModified() {
184: return cfgModified;
185: }
186: }
187:
188: // vim:ts=4
|