KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2003-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
23 import edu.umd.cs.findbugs.*;
24 import edu.umd.cs.findbugs.ba.*;
25 import edu.umd.cs.findbugs.ba.type.*;
26 import edu.umd.cs.findbugs.props.*;
27 import java.util.*;
28 import org.apache.bcel.Constants;
29 import org.apache.bcel.classfile.*;
30 import org.apache.bcel.generic.*;
31
32 /**
33  * Find suspicious reference comparisons.
34  * This includes:
35  * <ul>
36  * <li>Strings and other java.lang objects compared by reference equality</li>
37  * <li>Calls to equals(Object) where the argument is a different type than
38  * the receiver object</li>
39  * </ul>
40  *
41  * @author David Hovemeyer
42  * @author Bill Pugh
43  */

44 public class FindRefComparison implements Detector, ExtendedTypes {
45     private static final boolean DEBUG = SystemProperties.getBoolean("frc.debug");
46     private static final boolean REPORT_ALL_REF_COMPARISONS = SystemProperties.getBoolean("findbugs.refcomp.reportAll");
47     private static final int BASE_ES_PRIORITY = SystemProperties.getInteger("es.basePriority", NORMAL_PRIORITY);
48
49     /**
50      * Classes that are suspicious if compared by reference.
51      */

52     private static final HashSet<String JavaDoc> suspiciousSet = new HashSet<String JavaDoc>();
53
54     static {
55         suspiciousSet.add("java.lang.Boolean");
56         suspiciousSet.add("java.lang.Byte");
57         suspiciousSet.add("java.lang.Character");
58         suspiciousSet.add("java.lang.Double");
59         suspiciousSet.add("java.lang.Float");
60         suspiciousSet.add("java.lang.Integer");
61         suspiciousSet.add("java.lang.Long");
62         suspiciousSet.add("java.lang.Short");
63     }
64
65     /**
66      * Set of opcodes that invoke instance methods on an object.
67      */

68     private static final BitSet invokeInstanceSet = new BitSet();
69
70     static {
71         invokeInstanceSet.set(Constants.INVOKEVIRTUAL);
72         invokeInstanceSet.set(Constants.INVOKEINTERFACE);
73         invokeInstanceSet.set(Constants.INVOKESPECIAL);
74     }
75
76     /**
77      * Set of bytecodes using for prescreening.
78      */

79     private static final BitSet prescreenSet = new BitSet();
80
81     static {
82         prescreenSet.or(invokeInstanceSet);
83         prescreenSet.set(Constants.IF_ACMPEQ);
84         prescreenSet.set(Constants.IF_ACMPNE);
85     }
86
87     /* ----------------------------------------------------------------------
88      * Helper classes
89      * ---------------------------------------------------------------------- */

90
91     private static final byte T_DYNAMIC_STRING = T_AVAIL_TYPE + 0;
92     private static final byte T_STATIC_STRING = T_AVAIL_TYPE + 1;
93     private static final byte T_PARAMETER_STRING = T_AVAIL_TYPE + 2;
94
95     private static final String JavaDoc STRING_SIGNATURE = "Ljava/lang/String;";
96
97     /**
98      * Type representing a dynamically created String.
99      * This sort of String should never be compared using reference
100      * equality.
101      */

102     private static class DynamicStringType extends ObjectType {
103         private static final long serialVersionUID = 1L;
104
105         public DynamicStringType() {
106             super("java.lang.String");
107         }
108
109         @Override JavaDoc
110         public byte getType() {
111             return T_DYNAMIC_STRING;
112         }
113
114         @Override JavaDoc
115         public int hashCode() {
116             return System.identityHashCode(this);
117         }
118
119         @Override JavaDoc
120         public boolean equals(Object JavaDoc o) {
121             return o == this;
122         }
123
124         @Override JavaDoc
125         public String JavaDoc toString() {
126             return "<dynamic string>";
127         }
128     }
129
130     private static final Type dynamicStringTypeInstance = new DynamicStringType();
131
132     /**
133      * Type representing a static String.
134      * E.g., interned strings and constant strings.
135      * It is generally OK to compare this sort of String
136      * using reference equality.
137      */

138     private static class StaticStringType extends ObjectType {
139         private static final long serialVersionUID = 1L;
140
141         public StaticStringType() {
142             super("java.lang.String");
143         }
144
145         @Override JavaDoc
146         public byte getType() {
147             return T_STATIC_STRING;
148         }
149
150         @Override JavaDoc
151         public int hashCode() {
152             return System.identityHashCode(this);
153         }
154
155         @Override JavaDoc
156         public boolean equals(Object JavaDoc o) {
157             return o == this;
158         }
159
160         @Override JavaDoc
161         public String JavaDoc toString() {
162             return "<static string>";
163         }
164     }
165
166     private static final Type staticStringTypeInstance = new StaticStringType();
167
168     /**
169      * Type representing a String passed as a parameter.
170      */

171     private static class ParameterStringType extends ObjectType {
172         private static final long serialVersionUID = 1L;
173
174         public ParameterStringType() {
175             super("java.lang.String");
176         }
177
178         @Override JavaDoc
179         public byte getType() {
180             return T_PARAMETER_STRING;
181         }
182
183         @Override JavaDoc
184         public int hashCode() {
185             return System.identityHashCode(this);
186         }
187
188         @Override JavaDoc
189         public boolean equals(Object JavaDoc o) {
190             return o == this;
191         }
192
193         @Override JavaDoc
194         public String JavaDoc toString() {
195             return "<parameter string>";
196         }
197     }
198
199     private static final Type parameterStringTypeInstance = new ParameterStringType();
200
201     private static class RefComparisonTypeFrameModelingVisitor extends TypeFrameModelingVisitor {
202         private RepositoryLookupFailureCallback lookupFailureCallback;
203         private boolean sawStringIntern;
204
205         public RefComparisonTypeFrameModelingVisitor(
206                 ConstantPoolGen cpg,
207                 RepositoryLookupFailureCallback lookupFailureCallback) {
208             super(cpg);
209             this.lookupFailureCallback = lookupFailureCallback;
210             this.sawStringIntern = false;
211         }
212
213         public boolean sawStringIntern() {
214             return sawStringIntern;
215         }
216
217         // Override handlers for bytecodes that may return String objects
218
// known to be dynamic or static.
219

220         @Override JavaDoc
221         public void visitINVOKESTATIC(INVOKESTATIC obj) {
222             consumeStack(obj);
223             if (returnsString(obj)) {
224                 String JavaDoc className = obj.getClassName(getCPG());
225                 if (className.equals("java.lang.String")) {
226                     pushValue(dynamicStringTypeInstance);
227                 } else {
228                     pushReturnType(obj);
229                 }
230             } else {
231                 pushReturnType(obj);
232             }
233         }
234
235         @Override JavaDoc
236         public void visitINVOKESPECIAL(INVOKESPECIAL obj) {
237             handleInstanceMethod(obj);
238         }
239
240         @Override JavaDoc
241         public void visitINVOKEINTERFACE(INVOKEINTERFACE obj) {
242             handleInstanceMethod(obj);
243         }
244
245         @Override JavaDoc
246         public void visitINVOKEVIRTUAL(INVOKEVIRTUAL obj) {
247             handleInstanceMethod(obj);
248         }
249
250         private boolean returnsString(InvokeInstruction inv) {
251             String JavaDoc methodSig = inv.getSignature(getCPG());
252             return methodSig.endsWith(")Ljava/lang/String;");
253         }
254
255         private void handleInstanceMethod(InvokeInstruction obj) {
256             consumeStack(obj);
257             if (returnsString(obj)) {
258                 String JavaDoc className = obj.getClassName(getCPG());
259                 String JavaDoc methodName = obj.getName(getCPG());
260                 // System.out.println(className + "." + methodName);
261

262                 if (methodName.equals("intern") && className.equals("java.lang.String")) {
263                     sawStringIntern = true;
264                     pushValue(staticStringTypeInstance);
265                 } else if (methodName.equals("toString")
266                         || className.equals("java.lang.String")) {
267                     pushValue(dynamicStringTypeInstance);
268                     // System.out.println(" dynamic");
269
} else
270                     pushReturnType(obj);
271             } else
272                 pushReturnType(obj);
273         }
274
275         @Override JavaDoc
276         public void visitLDC(LDC obj) {
277             Type type = obj.getType(getCPG());
278             pushValue(isString(type) ? staticStringTypeInstance : type);
279         }
280
281         @Override JavaDoc
282         public void visitLDC2_W(LDC2_W obj) {
283             Type type = obj.getType(getCPG());
284             pushValue(isString(type) ? staticStringTypeInstance : type);
285         }
286
287         private boolean isString(Type type) {
288             return type.getSignature().equals(STRING_SIGNATURE);
289         }
290
291         @Override JavaDoc
292         public void visitGETSTATIC(GETSTATIC obj) {
293             handleLoad(obj);
294         }
295
296         @Override JavaDoc
297         public void visitGETFIELD(GETFIELD obj) {
298             handleLoad(obj);
299         }
300
301         private void handleLoad(FieldInstruction obj) {
302             consumeStack(obj);
303
304             Type type = obj.getType(getCPG());
305             if (type.getSignature().equals(STRING_SIGNATURE)) {
306                 try {
307                     String JavaDoc className = obj.getClassName(getCPG());
308                     String JavaDoc fieldName = obj.getName(getCPG());
309                     Field field = Hierarchy.findField(className, fieldName);
310
311                     if (field != null) {
312                         // If the field is final, we'll assume that the String value
313
// is static.
314
if (field.isFinal())
315                             pushValue(staticStringTypeInstance);
316                         else
317                             pushValue(type);
318
319                         return;
320                     }
321                 } catch (ClassNotFoundException JavaDoc ex) {
322                     lookupFailureCallback.reportMissingClass(ex);
323                 }
324             }
325
326             pushValue(type);
327         }
328     }
329
330     /**
331      * Type merger to use the extended String types.
332      */

333     private static class RefComparisonTypeMerger extends StandardTypeMerger {
334         public RefComparisonTypeMerger(RepositoryLookupFailureCallback lookupFailureCallback,
335                 ExceptionSetFactory exceptionSetFactory) {
336             super(lookupFailureCallback, exceptionSetFactory);
337         }
338
339         @Override JavaDoc
340         protected boolean isReferenceType(byte type) {
341             return super.isReferenceType(type) || type == T_STATIC_STRING || type == T_DYNAMIC_STRING;
342         }
343
344         @Override JavaDoc
345         protected Type mergeReferenceTypes(ReferenceType aRef, ReferenceType bRef) throws DataflowAnalysisException {
346             byte aType = aRef.getType();
347             byte bType = bRef.getType();
348
349             if (isExtendedStringType(aType) || isExtendedStringType(bType)) {
350                 // If both types are the same extended String type,
351
// then the same type is returned. Otherwise, extended
352
// types are downgraded to plain java.lang.String,
353
// and a standard merge is applied.
354
if (aType == bType)
355                     return aRef;
356
357                 if (isExtendedStringType(aType))
358                     aRef = Type.STRING;
359                 if (isExtendedStringType(bType))
360                     bRef = Type.STRING;
361             }
362
363             return super.mergeReferenceTypes(aRef, bRef);
364         }
365
366         private boolean isExtendedStringType(byte type) {
367             return type == T_DYNAMIC_STRING || type == T_STATIC_STRING || type == T_PARAMETER_STRING;
368         }
369     }
370
371     /* ----------------------------------------------------------------------
372      * Fields
373      * ---------------------------------------------------------------------- */

374
375     private BugReporter bugReporter;
376     private ClassContext classContext;
377
378     /* ----------------------------------------------------------------------
379      * Implementation
380      * ---------------------------------------------------------------------- */

381
382     public FindRefComparison(BugReporter bugReporter) {
383         this.bugReporter = bugReporter;
384     }
385
386     public void visitClassContext(ClassContext classContext) {
387         this.classContext = classContext;
388
389         JavaClass jclass = classContext.getJavaClass();
390         Method[] methodList = jclass.getMethods();
391
392         for (Method method : methodList) {
393             MethodGen methodGen = classContext.getMethodGen(method);
394             if (methodGen == null)
395                 continue;
396
397             // Prescreening - must have IF_ACMPEQ, IF_ACMPNE,
398
// or an invocation of an instance method
399
BitSet bytecodeSet = classContext.getBytecodeSet(method);
400             if (bytecodeSet == null || !bytecodeSet.intersects(prescreenSet))
401                 continue;
402
403             if (DEBUG)
404                 System.out.println("FindRefComparison: analyzing " +
405                         SignatureConverter.convertMethodSignature(methodGen));
406
407             try {
408                 analyzeMethod(classContext, method);
409             } catch (CFGBuilderException e) {
410                 bugReporter.logError("Error analyzing " + method.toString(), e);
411             } catch (DataflowAnalysisException e) {
412                 // bugReporter.logError("Error analyzing " + method.toString(), e);
413
}
414         }
415     }
416
417     /**
418      * A BugInstance and its WarningPropertySet.
419      */

420     private static class WarningWithProperties {
421         BugInstance instance;
422         WarningPropertySet propertySet;
423         Location location;
424
425         WarningWithProperties(BugInstance warning, WarningPropertySet propertySet, Location location) {
426             this.instance = warning;
427             this.propertySet = propertySet;
428             this.location = location;
429         }
430     }
431
432     private interface WarningDecorator {
433         public void decorate(WarningWithProperties warn);
434     }
435
436     private void analyzeMethod(ClassContext classContext, final Method method)
437     throws CFGBuilderException, DataflowAnalysisException {
438
439         MethodGen methodGen = classContext.getMethodGen(method);
440         if (methodGen == null) return;
441         boolean sawCallToEquals = false;
442         JavaClass jclass = classContext.getJavaClass();
443         ConstantPoolGen cpg = classContext.getConstantPoolGen();
444
445
446         // Enqueue all of the potential violations we find in the method.
447
// Normally we'll only report the first highest-priority warning,
448
// but if in relaxed mode or if REPORT_ALL_REF_COMPARISONS is set,
449
// then we'll report everything.
450
LinkedList<WarningWithProperties> refComparisonList =
451             new LinkedList<WarningWithProperties>();
452         LinkedList<WarningWithProperties> stringComparisonList =
453             new LinkedList<WarningWithProperties>();
454
455         CFG cfg = classContext.getCFG(method);
456         DepthFirstSearch dfs = classContext.getDepthFirstSearch(method);
457         ExceptionSetFactory exceptionSetFactory =
458             classContext.getExceptionSetFactory(method);
459
460         // Perform type analysis using our special type merger
461
// (which handles String types specially, keeping track of
462
// which ones appear to be dynamically created)
463
RefComparisonTypeMerger typeMerger =
464             new RefComparisonTypeMerger(bugReporter, exceptionSetFactory);
465         RefComparisonTypeFrameModelingVisitor visitor =
466             new RefComparisonTypeFrameModelingVisitor(methodGen.getConstantPool(), bugReporter);
467         TypeAnalysis typeAnalysis =
468             new TypeAnalysis(methodGen, cfg, dfs, typeMerger, visitor, bugReporter, exceptionSetFactory) {
469             @Override JavaDoc public void initEntryFact(TypeFrame result) {
470                 super.initEntryFact(result);
471                 for(int i = 0; i < methodGen.getMaxLocals(); i++) {
472                     Type t = result.getValue(i);
473                     if (t.equals(ObjectType.STRING))
474                         result.setValue(i, parameterStringTypeInstance);
475                 }
476             }
477         };
478         TypeDataflow typeDataflow = new TypeDataflow(cfg, typeAnalysis);
479         typeDataflow.execute();
480
481         // Inspect Locations in the method for suspicious ref comparisons and calls to equals()
482
for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
483             Location location = i.next();
484
485             sawCallToEquals = inspectLocation(
486                     sawCallToEquals,
487                     jclass,
488                     cpg,
489                     methodGen,
490                     refComparisonList,
491                     stringComparisonList,
492                     visitor,
493                     typeDataflow,
494                     location);
495         }
496
497         // Add method-wide properties to BugInstances
498
final boolean sawEquals = sawCallToEquals;
499         decorateWarnings(stringComparisonList, new WarningDecorator(){
500             public void decorate(WarningWithProperties warn) {
501                 if (sawEquals) {
502                     warn.propertySet.addProperty(RefComparisonWarningProperty.SAW_CALL_TO_EQUALS);
503                 }
504
505                 if (false && !(method.isPublic() || method.isProtected())) {
506                     warn.propertySet.addProperty(RefComparisonWarningProperty.PRIVATE_METHOD);
507                 }
508             }
509         });
510         decorateWarnings(refComparisonList, new WarningDecorator() {
511             public void decorate(WarningWithProperties warn) {
512                 if (sawEquals) {
513                     warn.propertySet.addProperty(RefComparisonWarningProperty.SAW_CALL_TO_EQUALS);
514                 }
515             }
516         });
517
518         // Report violations
519
boolean relaxed = FindBugsAnalysisFeatures.isRelaxedMode();
520         reportBest(classContext, method, stringComparisonList, relaxed);
521         reportBest(classContext, method, refComparisonList, relaxed);
522     }
523
524     private boolean inspectLocation(
525             boolean sawCallToEquals,
526             JavaClass jclass,
527             ConstantPoolGen cpg,
528             MethodGen methodGen,
529             LinkedList<WarningWithProperties> refComparisonList,
530             LinkedList<WarningWithProperties> stringComparisonList,
531             RefComparisonTypeFrameModelingVisitor visitor,
532             TypeDataflow typeDataflow,
533             Location location) throws DataflowAnalysisException {
534         Instruction ins = location.getHandle().getInstruction();
535         short opcode = ins.getOpcode();
536         if (opcode == Constants.IF_ACMPEQ || opcode == Constants.IF_ACMPNE) {
537             checkRefComparison(
538                     location,
539                     jclass,
540                     methodGen,
541                     visitor,
542                     typeDataflow,
543                     stringComparisonList,
544                     refComparisonList);
545         } else if (invokeInstanceSet.get(opcode)) {
546             InvokeInstruction inv = (InvokeInstruction) ins;
547             String JavaDoc methodName = inv.getMethodName(cpg);
548             String JavaDoc methodSig = inv.getSignature(cpg);
549             if (isEqualsMethod(methodName, methodSig)) {
550                 sawCallToEquals = true;
551                 checkEqualsComparison(location, jclass, methodGen, typeDataflow);
552             }
553         }
554         return sawCallToEquals;
555     }
556
557     private void decorateWarnings(
558             LinkedList<WarningWithProperties> stringComparisonList,
559             WarningDecorator warningDecorator) {
560         for (WarningWithProperties warn : stringComparisonList) {
561             warningDecorator.decorate(warn);
562             warn.instance.setPriority(warn.propertySet.computePriority(NORMAL_PRIORITY));
563         }
564     }
565
566     private void reportBest(
567             ClassContext classContext,
568             Method method,
569             LinkedList<WarningWithProperties> warningList,
570             boolean relaxed) {
571         boolean reportAll = relaxed || REPORT_ALL_REF_COMPARISONS;
572
573         WarningWithProperties best = null;
574         for (WarningWithProperties warn : warningList) {
575             if (best == null || warn.instance.getPriority() < best.instance.getPriority()) {
576                 best = warn;
577             }
578
579             if (reportAll) {
580                 if (relaxed) {
581                     // Add general warning properties
582
WarningPropertyUtil.addPropertiesForLocation(
583                             warn.propertySet,
584                             classContext,
585                             method,
586                             warn.location);
587
588                     // Convert warning properties to bug properties
589
warn.propertySet.decorateBugInstance(warn.instance);
590                 }
591                 bugReporter.reportBug(warn.instance);
592             }
593         }
594         if (best != null && !reportAll) {
595             bugReporter.reportBug(best.instance);
596         }
597     }
598
599     private boolean isEqualsMethod(String JavaDoc methodName, String JavaDoc methodSig) {
600         return (methodName.equals("equals") && methodSig.equals("(Ljava/lang/Object;)Z"))
601         || (methodName.equals("equalIgnoreCases") && methodSig.equals("(Ljava/lang/String;)Z"));
602     }
603
604     private void checkRefComparison(
605             Location location,
606             JavaClass jclass,
607             MethodGen methodGen,
608             RefComparisonTypeFrameModelingVisitor visitor,
609             TypeDataflow typeDataflow,
610             List<WarningWithProperties> stringComparisonList,
611             List<WarningWithProperties> refComparisonList) throws DataflowAnalysisException {
612
613         InstructionHandle handle = location.getHandle();
614
615         TypeFrame frame = typeDataflow.getFactAtLocation(location);
616         if (frame.getStackDepth() < 2)
617             throw new DataflowAnalysisException("Stack underflow", methodGen, handle);
618
619         int numSlots = frame.getNumSlots();
620         Type lhsType = frame.getValue(numSlots - 1);
621         Type rhsType = frame.getValue(numSlots - 2);
622
623         if (lhsType instanceof ReferenceType && rhsType instanceof ReferenceType) {
624             String JavaDoc lhs = SignatureConverter.convert(lhsType.getSignature());
625             String JavaDoc rhs = SignatureConverter.convert(rhsType.getSignature());
626
627             if (!lhs.equals(rhs))
628                 return;
629
630             if (lhs.equals("java.lang.String")) {
631                 handleStringComparison(jclass, methodGen, visitor, stringComparisonList, location, lhsType, rhsType);
632             } else if (suspiciousSet.contains(lhs)) {
633                 handleSuspiciousRefComparison(jclass, methodGen, refComparisonList, location, lhs);
634             }
635         }
636     }
637
638     private void handleStringComparison(
639             JavaClass jclass,
640             MethodGen methodGen,
641             RefComparisonTypeFrameModelingVisitor visitor,
642             List<WarningWithProperties> stringComparisonList,
643             Location location,
644             Type lhsType,
645             Type rhsType) {
646         if (DEBUG) System.out.println("String/String comparison at " + location.getHandle());
647
648         // Compute the priority:
649
// - two static strings => do not report
650
// - dynamic string and anything => high
651
// - static string and unknown => medium
652
// - all other cases => low
653
// System.out.println("Compare " + lhsType + " == " + rhsType);
654
byte type1 = lhsType.getType();
655         byte type2 = rhsType.getType();
656
657         String JavaDoc bugPattern = "ES_COMPARING_STRINGS_WITH_EQ";
658         // T1 T2 result
659
// S S no-op
660
// D ? high
661
// ? D high
662
// S ? normal
663
// ? S normal
664
WarningPropertySet propertySet = new WarningPropertySet();
665         if (type1 == T_STATIC_STRING && type2 == T_STATIC_STRING) {
666             propertySet.addProperty(RefComparisonWarningProperty.COMPARE_STATIC_STRINGS);
667         } else if (type1 == T_DYNAMIC_STRING || type2 == T_DYNAMIC_STRING) {
668             propertySet.addProperty(RefComparisonWarningProperty.DYNAMIC_AND_UNKNOWN);
669         } else if (type2 == T_PARAMETER_STRING || type1 == T_PARAMETER_STRING) {
670             bugPattern = "ES_COMPARING_PARAMETER_STRING_WITH_EQ";
671             if (methodGen.isPublic() || methodGen.isProtected()) propertySet.addProperty(RefComparisonWarningProperty.STRING_PARAMETER_IN_PUBLIC_METHOD);
672             else propertySet.addProperty(RefComparisonWarningProperty.STRING_PARAMETER);
673         } else if (type1 == T_STATIC_STRING || type2 == T_STATIC_STRING) {
674             propertySet.addProperty(RefComparisonWarningProperty.STATIC_AND_UNKNOWN);
675         } else if (visitor.sawStringIntern()) {
676             propertySet.addProperty(RefComparisonWarningProperty.SAW_INTERN);
677         }
678
679         String JavaDoc sourceFile = jclass.getSourceFileName();
680         BugInstance instance =
681             new BugInstance(this, bugPattern, BASE_ES_PRIORITY)
682         .addClassAndMethod(methodGen, sourceFile)
683         .addType("Ljava/lang/String;")
684         .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle());
685
686         WarningWithProperties warn = new WarningWithProperties(instance, propertySet, location);
687         stringComparisonList.add(warn);
688     }
689
690     private void handleSuspiciousRefComparison(
691             JavaClass jclass,
692             MethodGen methodGen,
693             List<WarningWithProperties> refComparisonList,
694             Location location,
695             String JavaDoc lhs) {
696         String JavaDoc sourceFile = jclass.getSourceFileName();
697         BugInstance instance = new BugInstance(this, "RC_REF_COMPARISON", lhs.equals("java.lang.Boolean") ? NORMAL_PRIORITY : HIGH_PRIORITY)
698         .addClassAndMethod(methodGen, sourceFile)
699         .addType("L" + lhs.replace('.', '/')+";")
700         .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle());
701
702         refComparisonList.add(new WarningWithProperties(instance, new WarningPropertySet(), location));
703     }
704
705     private static boolean testLikeName(String JavaDoc name) {
706         return name.toLowerCase().indexOf("test") >= 0;
707     }
708     private void checkEqualsComparison(
709             Location location,
710             JavaClass jclass,
711             MethodGen methodGen,
712             TypeDataflow typeDataflow) throws DataflowAnalysisException {
713
714         InstructionHandle handle = location.getHandle();
715         String JavaDoc sourceFile = jclass.getSourceFileName();
716
717         TypeFrame frame = typeDataflow.getFactAtLocation(location);
718         if (frame.getStackDepth() < 2)
719             throw new DataflowAnalysisException("Stack underflow", methodGen, handle);
720
721         int numSlots = frame.getNumSlots();
722         Type lhsType_ = frame.getValue(numSlots - 2);
723         Type rhsType_ = frame.getValue(numSlots - 1);
724
725
726         // Ignore top and bottom values
727
if (lhsType_.getType() == T_TOP || lhsType_.getType() == T_BOTTOM
728                 || rhsType_.getType() == T_TOP || rhsType_.getType() == T_BOTTOM)
729             return;
730
731         Method method = methodGen.getMethod();
732         boolean looksLikeTestCase = method.getName().startsWith("test") && method.isPublic() && method.getSignature().equals("()V")
733                 || testLikeName(jclass.getClassName())|| testLikeName(jclass.getSuperclassName());
734         int priorityModifier = 0;
735         if (looksLikeTestCase) priorityModifier = 1;
736         if (methodGen.getName().startsWith("test") && methodGen.getSignature().equals("()V")) {
737             try {
738                 if (jclass.getSuperclassName().equals("junit.framework.TestCase") || Hierarchy.isSubtype(methodGen.getClassName(), "junit.framework.TestCase"))
739                     priorityModifier=2;
740             } catch (ClassNotFoundException JavaDoc e) {
741                 AnalysisContext.reportMissingClass(e);
742             }
743         }
744
745         if (!(lhsType_ instanceof ReferenceType) || !(rhsType_ instanceof ReferenceType)) {
746             if (rhsType_.getType() == T_NULL) {
747                 // A literal null value was passed directly to equals().
748
if (!looksLikeTestCase)
749                     bugReporter.reportBug(new BugInstance(this, "EC_NULL_ARG", NORMAL_PRIORITY)
750                     .addClassAndMethod(methodGen, sourceFile)
751                     .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle()));
752             } else if (lhsType_.getType() == T_NULL) {
753                 // Hmm...in this case, equals() is being invoked on
754
// a literal null value. This is really the
755
// purview of FindNullDeref. So, we'll just do nothing.
756
} else {
757                 bugReporter.logError("equals() used to compare non-object type(s) " +
758                         lhsType_ + " and " + rhsType_ +
759                         " in " +
760                         SignatureConverter.convertMethodSignature(methodGen) +
761                         " at " + location.getHandle());
762             }
763             return;
764         }
765         if (lhsType_ instanceof ArrayType && rhsType_ instanceof ArrayType)
766             bugReporter.reportBug(new BugInstance(this, "EC_BAD_ARRAY_COMPARE", NORMAL_PRIORITY)
767             .addClassAndMethod(methodGen, sourceFile)
768             .addType(lhsType_.getSignature())
769             .addType(rhsType_.getSignature())
770             .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle())
771             );
772         IncompatibleTypes result = IncompatibleTypes.getPriorityForAssumingCompatible(lhsType_, rhsType_);
773                 if (result == IncompatibleTypes.ARRAY_AND_NON_ARRAY || result == IncompatibleTypes.ARRAY_AND_OBJECT)
774             bugReporter.reportBug(new BugInstance(this, "EC_ARRAY_AND_NONARRAY", result.getPriority())
775             .addClassAndMethod(methodGen, sourceFile)
776             .addType(lhsType_.getSignature())
777             .addType(rhsType_.getSignature())
778             .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle())
779             );
780         else if (result == IncompatibleTypes.INCOMPATIBLE_CLASSES) {
781             String JavaDoc lhsSig = lhsType_.getSignature();
782             String JavaDoc rhsSig = rhsType_.getSignature();
783             boolean core = lhsSig.startsWith("Ljava") && rhsSig.startsWith("Ljava");
784             if (core) {
785                 looksLikeTestCase = false;
786                 priorityModifier = 0;
787             }
788             if (!looksLikeTestCase) bugReporter.reportBug(new BugInstance(this, "EC_UNRELATED_TYPES", result.getPriority() + priorityModifier)
789             .addClassAndMethod(methodGen, sourceFile)
790             .addType(lhsSig)
791             .addType(rhsSig)
792             .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle())
793             );
794         }
795         else if (result == IncompatibleTypes.UNRELATED_CLASS_AND_INTERFACE)
796             bugReporter.reportBug(new BugInstance(this, "EC_UNRELATED_CLASS_AND_INTERFACE", result.getPriority())
797             .addClassAndMethod(methodGen, sourceFile)
798             .addType(lhsType_.getSignature())
799             .addType(rhsType_.getSignature())
800             .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle())
801             );
802         else if (result == IncompatibleTypes.UNRELATED_INTERFACES)
803             bugReporter.reportBug(new BugInstance(this, "EC_UNRELATED_INTERFACES", result.getPriority())
804             .addClassAndMethod(methodGen, sourceFile)
805             .addType(lhsType_.getSignature())
806             .addType(rhsType_.getSignature())
807             .addSourceLine(this.classContext, methodGen, sourceFile, location.getHandle())
808             );
809     }
810
811     public static void main(String JavaDoc[] argv) throws Exception JavaDoc {
812         if (argv.length != 1) {
813             System.err.println("Usage: " + FindRefComparison.class.getName() + " <class file>");
814             System.exit(1);
815         }
816
817         DataflowTestDriver<TypeFrame, TypeAnalysis> driver =
818             new DataflowTestDriver<TypeFrame, TypeAnalysis>() {
819             @Override JavaDoc
820             public Dataflow<TypeFrame, TypeAnalysis> createDataflow(ClassContext classContext, Method method)
821             throws CFGBuilderException, DataflowAnalysisException {
822
823                 RepositoryLookupFailureCallback lookupFailureCallback =
824                     classContext.getLookupFailureCallback();
825                 MethodGen methodGen = classContext.getMethodGen(method);
826                 if (methodGen == null)
827                     throw new DataflowAnalysisException("Could not get methodGen for " + method.toString());
828                 DepthFirstSearch dfs = classContext.getDepthFirstSearch(method);
829                 CFG cfg = classContext.getCFG(method);
830                 ExceptionSetFactory exceptionSetFactory = classContext.getExceptionSetFactory(method);
831
832                 TypeMerger typeMerger =
833                     new RefComparisonTypeMerger(lookupFailureCallback, exceptionSetFactory);
834                 TypeFrameModelingVisitor visitor =
835                     new RefComparisonTypeFrameModelingVisitor(methodGen.getConstantPool(), lookupFailureCallback);
836                 TypeAnalysis analysis = new TypeAnalysis(methodGen, cfg, dfs, typeMerger, visitor,
837                         lookupFailureCallback, exceptionSetFactory);
838                 Dataflow<TypeFrame, TypeAnalysis> dataflow =
839                     new Dataflow<TypeFrame, TypeAnalysis>(cfg, analysis);
840                 dataflow.execute();
841
842                 return dataflow;
843             }
844         };
845
846         driver.execute(argv[0]);
847     }
848
849
850
851     public void report() {
852         // do nothing
853
}
854 }
855
856 //vim:ts=3
857
Popular Tags