KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > ba > obl > ObligationAnalysis


1 /*
2  * Bytecode Analysis Framework
3  * Copyright (C) 2005, University of Maryland
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

19
20 package edu.umd.cs.findbugs.ba.obl;
21
22 import java.util.Iterator JavaDoc;
23 import java.util.Map JavaDoc;
24
25 import org.apache.bcel.Constants;
26 import org.apache.bcel.generic.ConstantPoolGen;
27 import org.apache.bcel.generic.Instruction;
28 import org.apache.bcel.generic.InstructionHandle;
29 import org.apache.bcel.generic.InvokeInstruction;
30 import org.apache.bcel.generic.MethodGen;
31 import org.apache.bcel.generic.ObjectType;
32 import org.apache.bcel.generic.Type;
33
34 import edu.umd.cs.findbugs.SystemProperties;
35 import edu.umd.cs.findbugs.annotations.CheckForNull;
36 import edu.umd.cs.findbugs.ba.AnalysisContext;
37 import edu.umd.cs.findbugs.ba.BasicBlock;
38 import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
39 import edu.umd.cs.findbugs.ba.DepthFirstSearch;
40 import edu.umd.cs.findbugs.ba.Edge;
41 import edu.umd.cs.findbugs.ba.EdgeTypes;
42 import edu.umd.cs.findbugs.ba.ForwardDataflowAnalysis;
43 import edu.umd.cs.findbugs.ba.Location;
44 import edu.umd.cs.findbugs.ba.RepositoryLookupFailureCallback;
45 import edu.umd.cs.findbugs.ba.type.TypeDataflow;
46 import edu.umd.cs.findbugs.ba.type.TypeFrame;
47
48 /**
49  * Dataflow analysis to track obligations (i/o streams and other
50  * resources which must be closed).
51  *
52  * <p>See Weimer and Necula,
53  * <a HREF="http://doi.acm.org/10.1145/1028976.1029011"
54  * >Finding and preventing run-time error handling mistakes</a>,
55  * OOPSLA 2004.</p>
56  *
57  * @author David Hovemeyer
58  */

59 public class ObligationAnalysis
60     extends ForwardDataflowAnalysis<StateSet> {
61     
62     private static final boolean DEBUG = SystemProperties.getBoolean("oa.debug");
63
64     private TypeDataflow typeDataflow;
65     private MethodGen methodGen;
66     private ObligationFactory factory;
67     private PolicyDatabase database;
68     private RepositoryLookupFailureCallback lookupFailureCallback;
69
70     /**
71      * Constructor.
72      *
73      * @param dfs a DepthFirstSearch on the method to be analyzed
74      * @param methodGen the MethodGen of the method being analyzed
75      * @param factory the ObligationFactory defining the obligation types
76      * @param database the PolicyDatabase defining the methods which
77      * add and delete obligations
78      * @param lookupFailureCallback callback to use when reporting
79      * missing classes
80      */

81     public ObligationAnalysis(
82             DepthFirstSearch dfs,
83             TypeDataflow typeDataflow,
84             MethodGen methodGen,
85             ObligationFactory factory,
86             PolicyDatabase database,
87             RepositoryLookupFailureCallback lookupFailureCallback) {
88         super(dfs);
89         this.typeDataflow = typeDataflow;
90         this.methodGen = methodGen;
91         this.factory = factory;
92         this.database = database;
93         this.lookupFailureCallback = lookupFailureCallback;
94     }
95
96     public StateSet createFact() {
97         return new StateSet(factory);
98     }
99
100     @Override JavaDoc
101     public boolean isFactValid(StateSet fact) {
102         return fact.isValid();
103     }
104
105     @Override JavaDoc
106     public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock, StateSet fact)
107             throws DataflowAnalysisException {
108         
109         Obligation obligation;
110         
111         if ((obligation = addsObligation(handle)) != null) {
112             // Add obligation to all states
113
if (DEBUG) { System.out.println("Adding obligation " + obligation.toString()); }
114             fact.addObligation(obligation);
115         } else if ((obligation = deletesObligation(handle)) != null) {
116             // Delete obligation from all states
117
if (DEBUG) { System.out.println("Deleting obligation " + obligation.toString()); }
118             deleteObligation(fact, obligation, handle);
119         }
120
121     }
122     
123     /* (non-Javadoc)
124      * @see edu.umd.cs.findbugs.ba.AbstractDataflowAnalysis#transfer(edu.umd.cs.findbugs.ba.BasicBlock, org.apache.bcel.generic.InstructionHandle, java.lang.Object, java.lang.Object)
125      */

126     @Override JavaDoc
127     public void transfer(BasicBlock basicBlock, @CheckForNull InstructionHandle end, StateSet start, StateSet result) throws DataflowAnalysisException {
128         super.transfer(basicBlock, end, start, result);
129         endTransfer(basicBlock, end, result);
130     }
131     
132     public void endTransfer(BasicBlock basicBlock, @CheckForNull InstructionHandle end, StateSet result)
133             throws DataflowAnalysisException {
134         // Append this block id to the Paths of all States
135
for (Iterator JavaDoc<State> i = result.stateIterator(); i.hasNext(); ) {
136             State state = i.next();
137             state.getPath().append(basicBlock.getId());
138         }
139     }
140
141     private Obligation addsObligation(InstructionHandle handle) {
142         return addsOrDeletesObligation(handle, PolicyDatabase.ADD);
143     }
144
145     private Obligation deletesObligation(InstructionHandle handle) {
146         return addsOrDeletesObligation(handle, PolicyDatabase.DEL);
147     }
148
149     private Obligation addsOrDeletesObligation(InstructionHandle handle, int action) {
150         Instruction ins = handle.getInstruction();
151
152         if (!(ins instanceof InvokeInstruction))
153             return null;
154         
155         InvokeInstruction inv = (InvokeInstruction) ins;
156         
157         ConstantPoolGen cpg = methodGen.getConstantPool();
158         
159         String JavaDoc className = inv.getClassName(cpg);
160         // FIXME: could prescreen class here...?
161

162         String JavaDoc methodName = inv.getName(cpg);
163         String JavaDoc signature = inv.getSignature(cpg);
164         boolean isStatic = inv.getOpcode() == Constants.INVOKESTATIC;
165         
166         if (DEBUG) {
167             System.out.println("Checking instruction: " + handle);
168             System.out.println(" class =" + className);
169             System.out.println(" method =" + methodName);
170             System.out.println(" signature=" + signature);
171         }
172         
173         try {
174             return database.lookup(
175                 className, methodName, signature, isStatic, action);
176         } catch (ClassNotFoundException JavaDoc e) {
177             lookupFailureCallback.reportMissingClass(e);
178             return null;
179         }
180         
181     }
182     
183     /**
184      * Delete Obligation from all states, throwing a DataflowAnalysisException
185      * if any of the states doesn't contain that Obligation.
186      *
187      * @param fact the StateSet to remove the Obligation from
188      * @param obligation the Obligation
189      * @param handle the instruction which deletes the obligation
190      * @throw DataflowAnalysisException if any State doesn't contain the obligation
191      */

192     private void deleteObligation(StateSet fact, Obligation obligation, InstructionHandle handle)
193             throws DataflowAnalysisException {
194         try {
195             fact.deleteObligation(obligation);
196         } catch (NonexistentObligationException e) {
197             throw new DataflowAnalysisException(
198                     "Removing nonexistent obligation of type " + obligation.toString(),
199                     methodGen, handle, e);
200         }
201     }
202
203     /* (non-Javadoc)
204      * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#copy(edu.umd.cs.findbugs.ba.obl.StateSet, edu.umd.cs.findbugs.ba.obl.StateSet)
205      */

206     public void copy(StateSet src, StateSet dest) {
207         dest.copyFrom(src);
208     }
209
210     /* (non-Javadoc)
211      * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#initEntryFact(edu.umd.cs.findbugs.ba.obl.StateSet)
212      */

213     public void initEntryFact(StateSet fact) throws DataflowAnalysisException {
214         fact.initEntryFact(factory);
215     }
216
217     /* (non-Javadoc)
218      * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#initResultFact(edu.umd.cs.findbugs.ba.obl.StateSet)
219      */

220     public void initResultFact(StateSet fact) {
221         fact.setTop();
222     }
223
224     /* (non-Javadoc)
225      * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#makeFactTop(edu.umd.cs.findbugs.ba.obl.StateSet)
226      */

227     public void makeFactTop(StateSet fact) {
228         fact.setTop();
229     }
230     public boolean isTop(StateSet fact) {
231         return fact.isTop();
232     }
233
234     /* (non-Javadoc)
235      * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#same(edu.umd.cs.findbugs.ba.obl.StateSet, edu.umd.cs.findbugs.ba.obl.StateSet)
236      */

237     public boolean same(StateSet a, StateSet b) {
238         return a.equals(b);
239     }
240
241     /* (non-Javadoc)
242      * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#meetInto(edu.umd.cs.findbugs.ba.obl.StateSet, edu.umd.cs.findbugs.ba.Edge, edu.umd.cs.findbugs.ba.obl.StateSet)
243      */

244     public void meetInto(StateSet fact, Edge edge, StateSet result)
245             throws DataflowAnalysisException {
246
247         // If the edge is an exception thrown from a method that
248
// tries to discharge an obligation, then that obligation needs to
249
// be removed from all states in the input fact.
250
if (edge.isExceptionEdge() && fact.isValid()) {
251             BasicBlock sourceBlock = edge.getSource();
252             InstructionHandle handle = sourceBlock.getExceptionThrower();
253             Obligation obligation;
254             if ((obligation = deletesObligation(handle)) != null) {
255                 fact = fact.duplicate();
256                 deleteObligation(fact, obligation, handle);
257             }
258         }
259         
260         // Similarly, if the incoming edge is from a reference comparision
261
// which has established that a reference of an obligation type
262
// is null, then we remove one occurrence of that type of
263
// obligation from all states.
264
if (isPossibleIfComparison(edge)) {
265             Obligation obligation;
266             if ((obligation = comparesObligationTypeToNull(edge)) != null) {
267                 fact = fact.duplicate();
268                 if (DEBUG) {
269                     System.out.println("Deleting " + obligation.toString() +
270                             " on edge from comparision " + edge.getSource().getLastInstruction());
271                 }
272                 deleteObligation(fact, obligation, edge.getSource().getLastInstruction());
273             }
274         }
275
276         final StateSet inputFact = fact;
277
278         // Handle easy top and bottom cases
279
if (inputFact.isTop() || result.isBottom()) {
280             // Nothing to do
281
} else if (inputFact.isBottom() || result.isTop()) {
282             copy(inputFact, result);
283         } else {
284             // Various things need to happen here
285
// - Match up states with equal ObligationSets
286
// - Paths with multiple occurences of a program point,
287
// but different obligation sets on different passes
288
// (i.e., obligation created inside a loop but not deleted)
289
// (how do we detect this?)
290

291             // We will destructively replace the state map of the result fact
292
// we're building.
293
final Map JavaDoc<ObligationSet, State> updatedStateMap = result.createEmptyMap();
294             
295             // Get all of the States from the input fact that don't
296
// have matching states. These will be copied verbatim
297
// into the result fact.
298
for (Iterator JavaDoc<State> i = inputFact.stateIterator(); i.hasNext(); ) {
299                 State otherState = i.next();
300                 if (result.getStateWithObligationSet(otherState.getObligationSet()) == null) {
301                     // Input fact has a State with an ObligationSet not in
302
// the result fact. Add a duplicate of it.
303
State dup = otherState.duplicate();
304                     updatedStateMap.put(dup.getObligationSet(), dup);
305                 }
306             }
307
308             // Find states from the input fact that have obligation sets
309
// which match a State in the result fact, and combine them
310
// into a single State.
311
StateSet.StateCallback callback = new StateSet.StateCallback() {
312                 public void apply(State state) throws NonexistentObligationException {
313                     // Find state in other fact with same obligation set (if any).
314
State matchingState = inputFact.getStateWithObligationSet(state.getObligationSet());
315                     if (matchingState != null) {
316                         // Combine the states by using the shorter of the two paths.
317
if (state.getPath().getLength() > matchingState.getPath().getLength()) {
318                             state.getPath().copyFrom(matchingState.getPath());
319                         }
320                     }
321                     updatedStateMap.put(state.getObligationSet(), state);
322                 }
323             };
324             
325             try {
326                 result.applyToAllStatesAndUpdateMap(callback, updatedStateMap);
327             } catch (NonexistentObligationException e) {
328                 // This can't happen, since we're not removing an obligation.
329
// But we'll propagate the exception just to be cautious.
330
throw new DataflowAnalysisException("This shouldn't happen", e);
331             }
332         }
333     }
334     
335     private boolean isPossibleIfComparison(Edge edge) {
336         return edge.getType() == EdgeTypes.IFCMP_EDGE
337             || edge.getType() == EdgeTypes.FALL_THROUGH_EDGE;
338     }
339     
340     private Obligation comparesObligationTypeToNull(Edge edge)
341             throws DataflowAnalysisException {
342         BasicBlock sourceBlock = edge.getSource();
343         InstructionHandle last = sourceBlock.getLastInstruction();
344         if (last == null)
345             return null;
346         
347         Type type;
348         
349         short opcode = last.getInstruction().getOpcode();
350         switch (opcode) {
351         case Constants.IFNULL:
352         case Constants.IFNONNULL:
353             if ( (edge.getType() == EdgeTypes.IFCMP_EDGE && opcode == Constants.IFNONNULL)
354                 || (edge.getType() == EdgeTypes.FALL_THROUGH_EDGE && opcode == Constants.IFNULL))
355                 return null;
356             
357             Location location = new Location(last, sourceBlock);
358             TypeFrame typeFrame = typeDataflow.getFactAtLocation(location);
359             type = typeFrame.getTopValue();
360             break;
361             
362 /*
363         // FIXME: handle IF_ACMPXX
364         case Constants.IFACMP_EQ:
365         case Constants.IFACMP_NE:
366             // ...
367             break;
368         
369  */

370         
371         default:
372             return null;
373         }
374         
375         if (!(type instanceof ObjectType))
376             return null;
377         
378         try {
379             return factory.getObligationByType((ObjectType) type);
380         } catch (ClassNotFoundException JavaDoc e) {
381             AnalysisContext.reportMissingClass(e);
382             throw new DataflowAnalysisException(
383                     "Subtype query failed during ObligationAnalysis", e);
384         }
385
386     }
387 }
388
389 // vim:ts=4
390
Popular Tags