KickJava   Java API By Example, From Geeks To Geeks.

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


1 package edu.umd.cs.findbugs.detect;
2
3 import java.util.BitSet JavaDoc;
4 import java.util.HashSet JavaDoc;
5 import java.util.Iterator JavaDoc;
6 import java.util.Set JavaDoc;
7
8 import org.apache.bcel.Constants;
9 import org.apache.bcel.Repository;
10 import org.apache.bcel.classfile.Attribute;
11 import org.apache.bcel.classfile.JavaClass;
12 import org.apache.bcel.classfile.Method;
13 import org.apache.bcel.classfile.Synthetic;
14 import org.apache.bcel.generic.CHECKCAST;
15 import org.apache.bcel.generic.ConstantPoolGen;
16 import org.apache.bcel.generic.INSTANCEOF;
17 import org.apache.bcel.generic.Instruction;
18 import org.apache.bcel.generic.InstructionHandle;
19 import org.apache.bcel.generic.InvokeInstruction;
20 import org.apache.bcel.generic.MethodGen;
21 import org.apache.bcel.generic.ObjectType;
22 import org.apache.bcel.generic.ReferenceType;
23 import org.apache.bcel.generic.Type;
24 import org.apache.bcel.generic.TypedInstruction;
25
26 import edu.umd.cs.findbugs.DeepSubtypeAnalysis;
27 import edu.umd.cs.findbugs.BugAccumulator;
28 import edu.umd.cs.findbugs.BugInstance;
29 import edu.umd.cs.findbugs.BugReporter;
30 import edu.umd.cs.findbugs.Detector;
31 import edu.umd.cs.findbugs.SourceLineAnnotation;
32 import edu.umd.cs.findbugs.SystemProperties;
33 import edu.umd.cs.findbugs.ba.CFG;
34 import edu.umd.cs.findbugs.ba.CFGBuilderException;
35 import edu.umd.cs.findbugs.ba.ClassContext;
36 import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
37 import edu.umd.cs.findbugs.ba.Location;
38 import edu.umd.cs.findbugs.ba.MethodUnprofitableException;
39 import edu.umd.cs.findbugs.ba.npe.IsNullValue;
40 import edu.umd.cs.findbugs.ba.npe.IsNullValueDataflow;
41 import edu.umd.cs.findbugs.ba.npe.IsNullValueFrame;
42 import edu.umd.cs.findbugs.ba.type.NullType;
43 import edu.umd.cs.findbugs.ba.type.TopType;
44 import edu.umd.cs.findbugs.ba.type.TypeDataflow;
45 import edu.umd.cs.findbugs.ba.type.TypeFrame;
46 import edu.umd.cs.findbugs.ba.vna.ValueNumber;
47 import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
48 import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
49
50 public class FindBadCast2 implements Detector {
51
52     private BugReporter bugReporter;
53
54     private Set JavaDoc<String JavaDoc> concreteCollectionClasses = new HashSet JavaDoc<String JavaDoc>();
55
56     private Set JavaDoc<String JavaDoc> abstractCollectionClasses = new HashSet JavaDoc<String JavaDoc>();
57     private Set JavaDoc<String JavaDoc> veryAbstractCollectionClasses = new HashSet JavaDoc<String JavaDoc>();
58
59     private static final boolean DEBUG = SystemProperties.getBoolean("bc.debug");
60
61     public FindBadCast2(BugReporter bugReporter) {
62         this.bugReporter = bugReporter;
63         veryAbstractCollectionClasses.add("java.util.Collection");
64         veryAbstractCollectionClasses.add("java.util.Iterable");
65         abstractCollectionClasses.add("java.util.Collection");
66         abstractCollectionClasses.add("java.util.List");
67         abstractCollectionClasses.add("java.util.Set");
68         abstractCollectionClasses.add("java.util.SortedSet");
69         abstractCollectionClasses.add("java.util.SortedMap");
70         abstractCollectionClasses.add("java.util.Map");
71         concreteCollectionClasses.add("java.util.LinkedHashMap");
72         concreteCollectionClasses.add("java.util.LinkedHashSet");
73         concreteCollectionClasses.add("java.util.HashMap");
74         concreteCollectionClasses.add("java.util.HashSet");
75         concreteCollectionClasses.add("java.util.TreeMap");
76         concreteCollectionClasses.add("java.util.TreeSet");
77         concreteCollectionClasses.add("java.util.ArrayList");
78         concreteCollectionClasses.add("java.util.LinkedList");
79         concreteCollectionClasses.add("java.util.Hashtable");
80         concreteCollectionClasses.add("java.util.Vector");
81     }
82
83     public void visitClassContext(ClassContext classContext) {
84         JavaClass javaClass = classContext.getJavaClass();
85         Method[] methodList = javaClass.getMethods();
86
87         for (Method method : methodList) {
88             if (method.getCode() == null)
89                 continue;
90
91             try {
92                 analyzeMethod(classContext, method);
93             } catch (MethodUnprofitableException e) {
94                 assert true; // move along; nothing to see
95
} catch (CFGBuilderException e) {
96                 String JavaDoc msg = "Detector " + this.getClass().getName()
97                                         + " caught exception while analyzing " + javaClass.getClassName() + "." + method.getName() + " : " + method.getSignature();
98                 bugReporter.logError(msg , e);
99             } catch (DataflowAnalysisException e) {
100                 String JavaDoc msg = "Detector " + this.getClass().getName()
101                                         + " caught exception while analyzing " + javaClass.getClassName() + "." + method.getName() + " : " + method.getSignature();
102                 bugReporter.logError(msg, e);
103             }
104         }
105     }
106
107     public boolean prescreen(ClassContext classContext, Method method) {
108         BitSet JavaDoc bytecodeSet = classContext.getBytecodeSet(method);
109         return bytecodeSet != null && (bytecodeSet.get(Constants.CHECKCAST)
110                 || bytecodeSet.get(Constants.INSTANCEOF));
111     }
112
113     private boolean isSynthetic(Method m) {
114         Attribute[] attrs = m.getAttributes();
115         for (Attribute attr : attrs) {
116             if (attr instanceof Synthetic)
117                 return true;
118         }
119         return false;
120     }
121     private Set JavaDoc<ValueNumber> getParameterValueNumbers(ClassContext classContext, Method method, CFG cfg ) throws DataflowAnalysisException, CFGBuilderException {
122         ValueNumberDataflow vnaDataflow = classContext.getValueNumberDataflow(method);
123         ValueNumberFrame vnaFrameAtEntry = vnaDataflow.getStartFact(cfg
124                 .getEntry());
125         Set JavaDoc<ValueNumber> paramValueNumberSet = new HashSet JavaDoc<ValueNumber>();
126         int firstParam = method.isStatic() ? 0 : 1;
127         for (int i = firstParam; i < vnaFrameAtEntry.getNumLocals(); ++i) {
128             paramValueNumberSet.add(vnaFrameAtEntry.getValue(i));
129         }
130         return paramValueNumberSet;
131     }
132     private void analyzeMethod(ClassContext classContext, Method method)
133             throws CFGBuilderException, DataflowAnalysisException {
134         if (isSynthetic(method) || !prescreen(classContext, method))
135             return;
136         BugAccumulator accumulator = new BugAccumulator(bugReporter);
137         
138         
139         CFG cfg = classContext.getCFG(method);
140         TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
141         IsNullValueDataflow isNullDataflow = classContext.getIsNullValueDataflow(method);
142         Set JavaDoc<ValueNumber> paramValueNumberSet = null;
143         
144         ValueNumberDataflow vnaDataflow = null;
145
146         ConstantPoolGen cpg = classContext.getConstantPoolGen();
147         MethodGen methodGen = classContext.getMethodGen(method);
148         if (methodGen == null) return;
149         String JavaDoc methodName = methodGen.getClassName() + "."
150                 + methodGen.getName();
151         String JavaDoc sourceFile = classContext.getJavaClass().getSourceFileName();
152         if (DEBUG) {
153             System.out.println("Checking " + methodName);
154         }
155
156         Set JavaDoc<SourceLineAnnotation> haveInstanceOf = new HashSet JavaDoc<SourceLineAnnotation>();
157         Set JavaDoc<SourceLineAnnotation> haveCast = new HashSet JavaDoc<SourceLineAnnotation>();
158         Set JavaDoc<SourceLineAnnotation> haveMultipleInstanceOf = new HashSet JavaDoc<SourceLineAnnotation>();
159         Set JavaDoc<SourceLineAnnotation> haveMultipleCast = new HashSet JavaDoc<SourceLineAnnotation>();
160         for (Iterator JavaDoc<Location> i = cfg.locationIterator(); i.hasNext();) {
161             Location location = i.next();
162             InstructionHandle handle = location.getHandle();
163             Instruction ins = handle.getInstruction();
164
165             if (!(ins instanceof CHECKCAST) && !(ins instanceof INSTANCEOF))
166                 continue;
167
168             SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation
169                     .fromVisitedInstruction(classContext, methodGen, sourceFile, handle);
170             if (ins instanceof CHECKCAST) {
171                 if (!haveCast.add(sourceLineAnnotation))
172                     haveMultipleCast.add(sourceLineAnnotation);
173             } else {
174                 if (!haveInstanceOf.add(sourceLineAnnotation))
175                     haveMultipleInstanceOf.add(sourceLineAnnotation);
176             }
177         }
178         for (Iterator JavaDoc<Location> i = cfg.locationIterator(); i.hasNext();) {
179             Location location = i.next();
180     
181             InstructionHandle handle = location.getHandle();
182             int pc = handle.getPosition();
183             Instruction ins = handle.getInstruction();
184     
185             if (!(ins instanceof CHECKCAST) && !(ins instanceof INSTANCEOF))
186                 continue;
187             if (handle.getNext() == null) continue;
188             Instruction nextIns = handle.getNext().getInstruction();
189
190             boolean isCast = ins instanceof CHECKCAST;
191             String JavaDoc kind = isCast ? "checkedCast" : "instanceof";
192             int occurrences = cfg.getLocationsContainingInstructionWithOffset(
193                     pc).size();
194             boolean split = occurrences > 1;
195             IsNullValueFrame nullFrame = isNullDataflow.getFactAtLocation(location);
196             if (!nullFrame.isValid()) continue;
197             IsNullValue operandNullness = nullFrame.getTopValue();
198             if (DEBUG) {
199                 System.out
200                         .println(kind + " at pc: " + pc + " in " + methodName);
201                 System.out.println(" occurrences: " + occurrences);
202                 System.out.println("XXX: " + operandNullness);
203                 
204             }
205
206             if (split && !isCast) {
207                 // don't report this case; it might be infeasible due to inlining
208
continue;
209             }
210
211             TypeFrame frame = typeDataflow.getFactAtLocation(location);
212             if (!frame.isValid()) {
213                 // This basic block is probably dead
214
continue;
215             }
216
217             Type operandType = frame.getTopValue();
218             if (operandType.equals(TopType.instance())) {
219                 // unreachable
220
continue;
221             }
222             boolean operandTypeIsExact = frame.isExact(frame.getStackLocation(0));
223             Type castType = ((TypedInstruction) ins).getType(cpg);
224
225             if (!(castType instanceof ReferenceType)) {
226                 // This shouldn't happen either
227
continue;
228             }
229             String JavaDoc castSig = castType.getSignature();
230             
231             if (operandType.equals(NullType.instance()) || operandNullness.isDefinitelyNull()) {
232                 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation
233                 .fromVisitedInstruction(classContext, methodGen, sourceFile, handle);
234                 String JavaDoc castName = castSig.substring(1, castSig.length() - 1)
235                 .replace('/', '.');
236                 if (!isCast) accumulator.accumulateBug(new BugInstance(this,
237                         "NP_NULL_INSTANCEOF", NORMAL_PRIORITY)
238                         .addClassAndMethod(methodGen, sourceFile)
239                         .addClass(castName), sourceLineAnnotation);
240                 continue;
241
242             }
243             if (!(operandType instanceof ReferenceType)) {
244                 // Shouldn't happen - illegal bytecode
245
continue;
246             }
247             ReferenceType refType = (ReferenceType) operandType;
248
249         
250             if (refType.equals(castType)) {
251                 // System.out.println("self-cast to " + castType.getSignature());
252
continue;
253             }
254             
255             String JavaDoc refSig = refType.getSignature();
256             String JavaDoc castSig2 = castSig;
257             String JavaDoc refSig2 = refSig;
258             while (castSig2.charAt(0) == '[' && refSig2.charAt(0) == '[') {
259                 castSig2 = castSig2.substring(1);
260                 refSig2 = refSig2.substring(1);
261             }
262
263             
264             SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation
265             .fromVisitedInstruction(classContext, methodGen, sourceFile, handle);
266
267             if (refSig2.charAt(0) != 'L' || castSig2.charAt(0) != 'L') {
268                 if ( castSig2.charAt(0) == '[' && (refSig2.equals("Ljava/io/Serializable;")
269                         || refSig2.equals("Ljava/lang/Object;")
270                         || refSig2.equals("Ljava/lang/Cloneable;"))) continue;
271                 if ( refSig2.charAt(0) == '[' && (castSig2.equals("Ljava/io/Serializable;")
272                         || castSig2.equals("Ljava/lang/Object;")
273                         || castSig2.equals("Ljava/lang/Cloneable;"))) continue;
274                 bugReporter.reportBug(
275                         new BugInstance(this,
276                         "BC_IMPOSSIBLE_CAST", HIGH_PRIORITY )
277                         .addClassAndMethod(methodGen, sourceFile)
278                         .addType(refSig)
279                         .addType(castSig)
280                         .addSourceLine(sourceLineAnnotation));
281                 continue;
282             }
283
284             if (refSig2.equals("Ljava/lang/Object;") &!operandTypeIsExact) {
285                 continue;
286             }
287             if (false && isCast && haveMultipleCast.contains(sourceLineAnnotation)
288                     || !isCast
289                     && haveMultipleInstanceOf.contains(sourceLineAnnotation)) {
290                 // skip; might be due to JSR inlining
291
continue;
292             }
293             String JavaDoc castName = castSig2.substring(1, castSig2.length() - 1)
294                     .replace('/', '.');
295             String JavaDoc refName = refSig2.substring(1, refSig2.length() - 1)
296                     .replace('/', '.');
297
298             if (vnaDataflow == null)
299                 vnaDataflow = classContext
300                 .getValueNumberDataflow(method);
301             ValueNumberFrame vFrame = vnaDataflow.getFactAtLocation(location);
302             if (paramValueNumberSet == null)
303                 paramValueNumberSet = getParameterValueNumbers(classContext, method, cfg);
304             boolean isParameter = paramValueNumberSet.contains(vFrame
305                     .getTopValue());
306             try {
307                 JavaClass castJavaClass = Repository.lookupClass(castName);
308                 JavaClass refJavaClass = Repository.lookupClass(refName);
309                 boolean upcast = Repository.instanceOf(refJavaClass,
310                         castJavaClass);
311                 if (upcast) {
312                     if (!isCast)
313                         accumulator.accumulateBug(new BugInstance(this,
314                                 "BC_VACUOUS_INSTANCEOF", NORMAL_PRIORITY)
315                                 .addClassAndMethod(methodGen, sourceFile)
316                             
317                                 .addType(refSig)
318                                 .addType(castSig)
319                                 ,sourceLineAnnotation);
320                 } else {
321                     boolean downcast = Repository.instanceOf(castJavaClass,
322                             refJavaClass);
323                     
324                     if (refName.equals("java.lang.Object") &!operandTypeIsExact) continue;
325                     double rank = 0.0;
326                     boolean castToConcreteCollection = concreteCollectionClasses.contains(castName)
327                             && abstractCollectionClasses.contains(refName);
328                     boolean castToAbstractCollection =
329                             abstractCollectionClasses.contains(castName)
330                             && veryAbstractCollectionClasses.contains(refName);
331     
332                     if (!operandTypeIsExact) {
333                         rank = DeepSubtypeAnalysis.deepInstanceOf(refJavaClass,
334                                 castJavaClass);
335                             if (castToConcreteCollection
336                             && rank > 0.6)
337                           rank = (rank + 0.6) /2;
338                         else if (castToAbstractCollection
339                             && rank > 0.3)
340                           rank = (rank + 0.3) /2;
341                     }
342                     
343                         
344                     if (false)
345                         System.out.println("Rank:\t" + rank + "\t" + refName
346                                 + "\t" + castName);
347                     boolean completeInformation = (!castJavaClass.isInterface() && !refJavaClass
348                             .isInterface())
349                             || refJavaClass.isFinal()
350                             || castJavaClass.isFinal();
351                     if (DEBUG) {
352                         System.out.println("cast from " + refName + " to "
353                                 + castName);
354                         System.out.println(" is downcast: " + downcast);
355                         System.out.println(" operand type is exact: " + operandTypeIsExact);
356                         
357                         System.out.println(" complete information: "
358                                 + completeInformation);
359                         System.out.println(" isParameter: "
360                                 + vFrame.getTopValue());
361                         System.out.println(" score: " + rank);
362                     }
363                     if (!downcast && completeInformation || operandTypeIsExact)
364                         bugReporter.reportBug(new BugInstance(this,
365                                 isCast ? "BC_IMPOSSIBLE_CAST"
366                                         : "BC_IMPOSSIBLE_INSTANCEOF",
367                                 isCast ? HIGH_PRIORITY : NORMAL_PRIORITY)
368                                 .addClassAndMethod(methodGen, sourceFile)
369                                 
370                                 .addType(refSig)
371                                 .addType(castSig)
372                                 .addSourceLine(sourceLineAnnotation));
373                     else if (isCast && rank < 0.9) {
374
375                         int priority = NORMAL_PRIORITY;
376
377                         if (rank > 0.75)
378                             priority += 2;
379                         else if (rank > 0.5)
380                             priority += 1;
381                         else if (rank > 0.25)
382                             priority += 0;
383                         else
384                             priority--;
385
386                         if (DEBUG)
387                             System.out.println(" priority a: " + priority);
388                         if (methodGen.getClassName().startsWith(refName)
389                                 || methodGen.getClassName().startsWith(castName))
390                             priority += 1;
391                         if (DEBUG)
392                             System.out.println(" priority b: " + priority);
393                         if (castJavaClass.isInterface() && !castToAbstractCollection)
394                             priority++;
395                         if (DEBUG)
396                             System.out.println(" priority c: " + priority);
397                         if (castToConcreteCollection
398                             && veryAbstractCollectionClasses.contains(refName))
399                             priority--;
400                         if (DEBUG)
401                             System.out.println(" priority d: " + priority);
402                         if (priority <= LOW_PRIORITY
403                                 && !castToAbstractCollection
404                                 && !castToConcreteCollection
405                                 && (refJavaClass.isInterface() || refJavaClass
406                                         .isAbstract()))
407                             priority++;
408                         if (DEBUG)
409                             System.out.println(" priority e: " + priority);
410                         if (DEBUG)
411                             System.out.println(" ref name: " + refName);
412                         if (methodGen.getName().equals("compareTo"))
413                             priority++;
414                         else if (methodGen.isPublic() && isParameter)
415                             priority--;
416                         if (DEBUG)
417                             System.out.println(" priority h: " + priority);
418                         if (priority < HIGH_PRIORITY)
419                             priority = HIGH_PRIORITY;
420                         if (priority <= LOW_PRIORITY) {
421                             String JavaDoc bug = "BC_UNCONFIRMED_CAST";
422                             if (castToConcreteCollection)
423                                 bug = "BC_BAD_CAST_TO_CONCRETE_COLLECTION";
424                             else if (castToAbstractCollection)
425                                 bug = "BC_BAD_CAST_TO_ABSTRACT_COLLECTION";
426
427                             accumulator.accumulateBug(new BugInstance(this, bug, priority)
428                                     .addClassAndMethod(methodGen, sourceFile)
429                                     .addType(refSig)
430                                     .addType(castSig),
431                                     sourceLineAnnotation
432                                     );
433                         }
434
435                     }
436
437                 }
438             } catch (ClassNotFoundException JavaDoc e) {
439             }
440         }
441         accumulator.reportAccumulatedBugs();
442     }
443
444     public void report() {
445     }
446
447 }
448
Popular Tags