KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > detect > FindDeadLocalStores


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2004,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.detect;
21
22 import java.util.Arrays JavaDoc;
23 import java.util.BitSet JavaDoc;
24 import java.util.HashSet JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Set JavaDoc;
28
29 import org.apache.bcel.Constants;
30 import org.apache.bcel.classfile.JavaClass;
31 import org.apache.bcel.classfile.LocalVariable;
32 import org.apache.bcel.classfile.LocalVariableTable;
33 import org.apache.bcel.classfile.Method;
34 import org.apache.bcel.generic.ACONST_NULL;
35 import org.apache.bcel.generic.ALOAD;
36 import org.apache.bcel.generic.ANEWARRAY;
37 import org.apache.bcel.generic.ASTORE;
38 import org.apache.bcel.generic.ConstantPushInstruction;
39 import org.apache.bcel.generic.GETFIELD;
40 import org.apache.bcel.generic.IINC;
41 import org.apache.bcel.generic.INVOKESPECIAL;
42 import org.apache.bcel.generic.IndexedInstruction;
43 import org.apache.bcel.generic.Instruction;
44 import org.apache.bcel.generic.InstructionHandle;
45 import org.apache.bcel.generic.LDC;
46 import org.apache.bcel.generic.LoadInstruction;
47 import org.apache.bcel.generic.MULTIANEWARRAY;
48 import org.apache.bcel.generic.MethodGen;
49 import org.apache.bcel.generic.NEWARRAY;
50 import org.apache.bcel.generic.StoreInstruction;
51
52 import edu.umd.cs.findbugs.BugAccumulator;
53 import edu.umd.cs.findbugs.BugInstance;
54 import edu.umd.cs.findbugs.BugReporter;
55 import edu.umd.cs.findbugs.Detector;
56 import edu.umd.cs.findbugs.FindBugsAnalysisFeatures;
57 import edu.umd.cs.findbugs.LocalVariableAnnotation;
58 import edu.umd.cs.findbugs.SourceLineAnnotation;
59 import edu.umd.cs.findbugs.SystemProperties;
60 import edu.umd.cs.findbugs.ba.CFG;
61 import edu.umd.cs.findbugs.ba.CFGBuilderException;
62 import edu.umd.cs.findbugs.ba.ClassContext;
63 import edu.umd.cs.findbugs.ba.Dataflow;
64 import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
65 import edu.umd.cs.findbugs.ba.LiveLocalStoreAnalysis;
66 import edu.umd.cs.findbugs.ba.Location;
67 import edu.umd.cs.findbugs.props.WarningPropertySet;
68 import edu.umd.cs.findbugs.props.WarningPropertyUtil;
69 import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
70
71 /**
72  * Find dead stores to local variables.
73  *
74  * @author David Hovemeyer
75  * @author Bill Pugh
76  */

77 public class FindDeadLocalStores implements Detector {
78     
79     private static final boolean DEBUG = SystemProperties.getBoolean("fdls.debug");
80     
81     // Define the name of the property that is used to exclude named local variables
82
// from Dead Local Storage detection...
83
private static final String JavaDoc FINDBUGS_EXCLUDED_LOCALS_PROP_NAME = "findbugs.dls.exclusions";
84     
85     // Define a collection of excluded local variables...
86
private static final Set JavaDoc<String JavaDoc> EXCLUDED_LOCALS = new HashSet JavaDoc<String JavaDoc>();
87     
88     private static final boolean DO_EXCLUDE_LOCALS =
89         SystemProperties.getProperty(FINDBUGS_EXCLUDED_LOCALS_PROP_NAME) != null;
90     static {
91         // Get the value of the property...
92
String JavaDoc exclLocalsProperty = SystemProperties.getProperty(FINDBUGS_EXCLUDED_LOCALS_PROP_NAME);
93         
94         // If we have one, then split its contents into a table...
95
if (exclLocalsProperty != null) {
96             EXCLUDED_LOCALS.addAll( (List JavaDoc<String JavaDoc>)Arrays.asList(exclLocalsProperty.split(",")));
97             EXCLUDED_LOCALS.remove("");
98         }
99     }
100     
101     //private static final Set<String> classesAlreadyReportedOn = new HashSet<String>();
102
/**
103      * Opcodes of instructions that load constant values that
104      * often indicate defensive programming.
105      */

106     private static final BitSet JavaDoc defensiveConstantValueOpcodes = new BitSet JavaDoc();
107     static {
108         defensiveConstantValueOpcodes.set(Constants.DCONST_0);
109         defensiveConstantValueOpcodes.set(Constants.DCONST_1);
110         defensiveConstantValueOpcodes.set(Constants.FCONST_0);
111         defensiveConstantValueOpcodes.set(Constants.FCONST_1);
112         defensiveConstantValueOpcodes.set(Constants.ACONST_NULL);
113         defensiveConstantValueOpcodes.set(Constants.ICONST_0);
114         defensiveConstantValueOpcodes.set(Constants.ICONST_1);
115     }
116     
117     private BugReporter bugReporter;
118     
119     public FindDeadLocalStores(BugReporter bugReporter) {
120         this.bugReporter = bugReporter;
121         if (DEBUG) System.out.println("Debugging FindDeadLocalStores detector");
122     }
123     
124     private boolean prescreen(ClassContext classContext, Method method) {
125         return true;
126     }
127     
128     public void visitClassContext(ClassContext classContext) {
129         JavaClass javaClass = classContext.getJavaClass();
130         Method[] methodList = javaClass.getMethods();
131
132         for (Method method : methodList) {
133             MethodGen methodGen = classContext.getMethodGen(method);
134             if (methodGen == null)
135                 continue;
136
137             if (!prescreen(classContext, method))
138                 continue;
139
140             try {
141                 analyzeMethod(classContext, method);
142             } catch (DataflowAnalysisException e) {
143                 bugReporter.logError("Error analyzing " + method.toString(), e);
144             } catch (CFGBuilderException e) {
145                 bugReporter.logError("Error analyzing " + method.toString(), e);
146             }
147         }
148     }
149     
150     private void analyzeMethod(ClassContext classContext, Method method)
151             throws DataflowAnalysisException, CFGBuilderException {
152         
153         if (DEBUG) {
154             System.out.println(" Analyzing method " + classContext.getJavaClass().getClassName() + "." + method.getName());
155         }
156         
157         JavaClass javaClass = classContext.getJavaClass();
158         
159         BugAccumulator accumulator = new BugAccumulator(bugReporter);
160         Dataflow<BitSet JavaDoc, LiveLocalStoreAnalysis> llsaDataflow =
161             classContext.getLiveLocalStoreDataflow(method);
162         
163         int numLocals = method.getCode().getMaxLocals();
164         int [] localStoreCount = new int[numLocals];
165         int [] localLoadCount = new int[numLocals];
166         int [] localIncrementCount = new int[numLocals];
167         MethodGen methodGen = classContext.getMethodGen(method);
168         CFG cfg = classContext.getCFG(method);
169         BitSet JavaDoc liveStoreSetAtEntry = llsaDataflow.getAnalysis().getResultFact(cfg.getEntry());
170         BitSet JavaDoc complainedAbout = new BitSet JavaDoc();
171         
172         // Get number of locals that are parameters.
173
int localsThatAreParameters = PreorderVisitor.getNumberArguments(method.getSignature());
174         if (!method.isStatic()) localsThatAreParameters++;
175         
176         // Scan method to determine number of loads, stores, and increments
177
// of local variables.
178
countLocalStoresLoadsAndIncrements(
179                 localStoreCount, localLoadCount, localIncrementCount, cfg);
180         
181         // Scan method for
182
// - dead stores
183
// - stores to parameters that are dead upon entry to the method
184
for (Iterator JavaDoc<Location> i = cfg.locationIterator(); i.hasNext(); ) {
185             Location location = i.next();
186             
187             BugInstance pendingBugReportAboutOverwrittenParameter = null;
188             try {
189             WarningPropertySet propertySet = new WarningPropertySet();
190             // Skip any instruction which is not a store
191
if (!isStore(location))
192                 continue;
193             
194             // Heuristic: exception handler blocks often contain
195
// dead stores generated by the compiler.
196
if (location.getBasicBlock().isExceptionHandler())
197                 propertySet.addProperty(DeadLocalStoreProperty.EXCEPTION_HANDLER);
198             
199             IndexedInstruction ins = (IndexedInstruction) location.getHandle().getInstruction();
200             
201             
202             LocalVariableAnnotation lvAnnotation
203             = LocalVariableAnnotation.getLocalVariableAnnotation(method, location, ins);
204             
205             String JavaDoc name = lvAnnotation.getName();
206             if (name.charAt(0) == '$' || name.charAt(0) == '_') propertySet.addProperty(DeadLocalStoreProperty.SYNTHETIC_NAME);
207             if (EXCLUDED_LOCALS.contains(name)) continue;
208             propertySet.setProperty(DeadLocalStoreProperty.LOCAL_NAME, name);
209             
210             int local = ins.getIndex();
211             // Is this a store to a parameter which was dead on entry to the method?
212
boolean parameterThatIsDeadAtEntry = local < localsThatAreParameters
213                 && !llsaDataflow.getAnalysis().isStoreAlive(liveStoreSetAtEntry, local);
214             if (parameterThatIsDeadAtEntry && !complainedAbout.get(local)) {
215                 
216             
217                 
218                 // TODO: add warning properties?
219
pendingBugReportAboutOverwrittenParameter = new BugInstance(this, "IP_PARAMETER_IS_DEAD_BUT_OVERWRITTEN", NORMAL_PRIORITY)
220                     .addClassAndMethod(methodGen, javaClass.getSourceFileName())
221                     .add(lvAnnotation)
222                     .addSourceLine(classContext, methodGen, javaClass.getSourceFileName(), location.getHandle());
223                 complainedAbout.set(local);
224             }
225             
226             boolean storeOfNull = false;
227             InstructionHandle prevInsHandle = location.getHandle().getPrev();
228             if (prevInsHandle != null) {
229                 Instruction prevIns = prevInsHandle.getInstruction();
230                 if (prevIns instanceof LDC || prevIns instanceof ConstantPushInstruction)
231                     continue;
232                 if ( prevIns instanceof ACONST_NULL) {
233                     storeOfNull = true;
234                     propertySet.addProperty(DeadLocalStoreProperty.STORE_OF_NULL);
235                 }
236                 
237             }
238
239             // Get live stores at this instruction.
240
// Note that the analysis also computes which stores were
241
// killed by a subsequent unconditional store.
242
BitSet JavaDoc liveStoreSet = llsaDataflow.getAnalysis().getFactAtLocation(location);
243             
244             // Is store alive?
245
if (llsaDataflow.getAnalysis().isStoreAlive(liveStoreSet, local))
246                 continue;
247             // Store is dead
248

249             // Ignore assignments that were killed by a subsequent assignment.
250
boolean killedBySubsequentStore = llsaDataflow.getAnalysis().killedByStore(liveStoreSet, local);
251             if (killedBySubsequentStore)
252                 propertySet.addProperty(DeadLocalStoreProperty.KILLED_BY_SUBSEQUENT_STORE);
253             
254             // Ignore dead assignments of null and 0.
255
// These often indicate defensive programming.
256
InstructionHandle prev = location.getBasicBlock().getPredecessorOf(location.getHandle());
257             int prevOpCode = -1;
258
259             if (prev != null
260                     && defensiveConstantValueOpcodes.get(prev.getInstruction().getOpcode())) {
261                 propertySet.addProperty(DeadLocalStoreProperty.DEFENSIVE_CONSTANT_OPCODE);
262                 prevOpCode = prev.getInstruction().getOpcode();
263                 }
264
265             if (prev != null && prev.getInstruction() instanceof GETFIELD) {
266                 InstructionHandle prev2 = prev.getPrev();
267
268                 if (prev2 != null
269                                 && prev2.getInstruction() instanceof ALOAD)
270                     propertySet.addProperty(DeadLocalStoreProperty.CACHING_VALUE);
271             }
272
273             
274             if (ins instanceof IINC) {
275                 // special handling of IINC
276

277                 propertySet.addProperty(DeadLocalStoreProperty.DEAD_INCREMENT);
278                 if (localIncrementCount[local] == 1) {
279                     propertySet.addProperty(DeadLocalStoreProperty.SINGLE_DEAD_INCREMENT);
280                 }
281                 
282             } else if (ins instanceof ASTORE && prev != null) {
283                 // Look for objects created but never used
284

285                 Instruction prevIns = prev.getInstruction();
286                 if ((prevIns instanceof INVOKESPECIAL &&
287                         ((INVOKESPECIAL)prevIns).getMethodName(methodGen.getConstantPool()).equals("<init>"))
288                         || prevIns instanceof ANEWARRAY
289                         || prevIns instanceof NEWARRAY
290                         || prevIns instanceof MULTIANEWARRAY) {
291                     propertySet.addProperty(DeadLocalStoreProperty.DEAD_OBJECT_STORE);
292                 }
293                 
294             } else if (!killedBySubsequentStore
295                     && localStoreCount[local] == 2 && localLoadCount[local] > 0) {
296                 // TODO: why is this significant?
297

298                 propertySet.addProperty(DeadLocalStoreProperty.TWO_STORES_MULTIPLE_LOADS);
299                 
300             } else if (!parameterThatIsDeadAtEntry && localStoreCount[local] == 1) {
301                 // TODO: why is this significant?
302

303                 propertySet.addProperty(DeadLocalStoreProperty.SINGLE_STORE);
304                 
305             } else if (!parameterThatIsDeadAtEntry && localLoadCount[local] == 0) {
306                 // TODO: why is this significant?
307

308                 propertySet.addProperty(DeadLocalStoreProperty.NO_LOADS);
309                 
310             }
311             
312             if (parameterThatIsDeadAtEntry) {
313                 propertySet.addProperty(DeadLocalStoreProperty.PARAM_DEAD_ON_ENTRY);
314                 if (pendingBugReportAboutOverwrittenParameter != null)
315                     pendingBugReportAboutOverwrittenParameter.setPriority(Detector.HIGH_PRIORITY);
316             }
317
318             if (localStoreCount[local] > 3)
319                 propertySet.addProperty(DeadLocalStoreProperty.MANY_STORES);
320             int priority = propertySet.computePriority(NORMAL_PRIORITY);
321             if (priority <= Detector.EXP_PRIORITY) {
322                 
323                 
324                 // Report the warning
325
BugInstance bugInstance = new BugInstance(this, storeOfNull ? "DLS_DEAD_LOCAL_STORE_OF_NULL" : "DLS_DEAD_LOCAL_STORE", priority)
326                     .addClassAndMethod(methodGen, javaClass.getSourceFileName())
327                     .add(lvAnnotation);
328                 
329
330                 
331                 // If in relaxed reporting mode, encode heuristic information.
332
if (FindBugsAnalysisFeatures.isRelaxedMode()) {
333                     // Add general-purpose warning properties
334
WarningPropertyUtil.addPropertiesForLocation(
335                             propertySet,
336                             classContext,
337                             method,
338                             location);
339                     
340                     // Turn all warning properties into BugProperties
341
propertySet.decorateBugInstance(bugInstance);
342                 }
343                 SourceLineAnnotation sourceLineAnnotation =
344                     SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen, javaClass.getSourceFileName(),
345                             location.getHandle());
346                 
347
348                 if (DEBUG) {
349                 System.out.println(
350                     javaClass.getSourceFileName() + " : " +
351                         methodGen.getName());
352                 System.out.println("priority: " + priority);
353                 System.out.println("Reporting " + bugInstance);
354                 System.out.println(propertySet);
355                 }
356                 accumulator.accumulateBug(bugInstance, sourceLineAnnotation);
357             }
358             } finally {
359                 if (pendingBugReportAboutOverwrittenParameter != null)
360                     bugReporter.reportBug(pendingBugReportAboutOverwrittenParameter);
361             }
362         }
363         accumulator.reportAccumulatedBugs();
364     }
365     
366     
367
368     
369     /**
370      * Count stores, loads, and increments of local variables
371      * in method whose CFG is given.
372      *
373      * @param localStoreCount counts of local stores (indexed by local)
374      * @param localLoadCount counts of local loads (indexed by local)
375      * @param localIncrementCount counts of local increments (indexed by local)
376      * @param cfg control flow graph (CFG) of method
377      */

378     private void countLocalStoresLoadsAndIncrements(int[] localStoreCount, int[] localLoadCount, int[] localIncrementCount, CFG cfg) {
379         for (Iterator JavaDoc<Location> i = cfg.locationIterator(); i.hasNext(); ) {
380             Location location = i.next();
381             
382             if (location.getBasicBlock().isExceptionHandler())
383                 continue;
384             
385             boolean isStore = isStore(location);
386             boolean isLoad = isLoad(location);
387             if (!isStore && !isLoad)
388                 continue;
389             
390             IndexedInstruction ins = (IndexedInstruction) location.getHandle().getInstruction();
391             int local = ins.getIndex();
392             if (ins instanceof IINC) {
393                 localStoreCount[local]++;
394                 localLoadCount[local]++;
395                 localIncrementCount[local]++;
396             } else if (isStore)
397                 localStoreCount[local]++;
398             else
399                 localLoadCount[local]++;
400         }
401     }
402     
403     /**
404      * Get the name of given local variable (if possible) and store it in
405      * the HeuristicPropertySet.
406      *
407      * @param lvt the LocalVariableTable
408      * @param local index of the local
409      * @param pc program counter value of the instruction
410      */

411     private void checkLocalVariableName(LocalVariableTable lvt, int local, int pc,
412             WarningPropertySet propertySet) {
413         if (lvt != null) {
414             LocalVariable lv = lvt.getLocalVariable(local, pc);
415             if (lv != null) {
416                 String JavaDoc localName = lv.getName();
417                 propertySet.setProperty(DeadLocalStoreProperty.LOCAL_NAME, localName);
418             }
419         }
420         
421     }
422     
423     /**
424      * Is instruction at given location a store?
425      *
426      * @param location the location
427      * @return true if instruction at given location is a store, false if not
428      */

429     private boolean isStore(Location location) {
430         Instruction ins = location.getHandle().getInstruction();
431         return (ins instanceof StoreInstruction) || (ins instanceof IINC);
432     }
433
434     /**
435      * Is instruction at given location a load?
436      *
437      * @param location the location
438      * @return true if instruction at given location is a load, false if not
439      */

440     private boolean isLoad(Location location) {
441         Instruction ins = location.getHandle().getInstruction();
442         return (ins instanceof LoadInstruction) || (ins instanceof IINC);
443     }
444     
445     public void report() {
446     }
447 }
448
449 //vim:ts=4
450
Popular Tags