KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2003,2004 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 java.util.*;
25 import org.apache.bcel.classfile.*;
26
27 public class MutableStaticFields extends BytecodeScanningDetector {
28
29
30     static String JavaDoc extractPackage(String JavaDoc c) {
31         int i = c.lastIndexOf('/');
32         if (i < 0) return "";
33         return c.substring(0, i);
34     }
35
36     static boolean mutableSignature(String JavaDoc sig) {
37         return sig.equals("Ljava/util/Hashtable;")
38                 || sig.equals("Ljava/util/Date;")
39                 || sig.charAt(0) == '[';
40     }
41
42     static class FieldRecord {
43         String JavaDoc className;
44         String JavaDoc name;
45         String JavaDoc signature;
46         boolean isPublic;
47         boolean isFinal;
48     }
49
50
51     LinkedList<FieldRecord> seen = new LinkedList<FieldRecord>();
52     boolean publicClass;
53     boolean zeroOnTOS;
54     boolean emptyArrayOnTOS;
55     boolean inStaticInitializer;
56     String JavaDoc packageName;
57     Set<String JavaDoc> unsafeValue = new HashSet<String JavaDoc>();
58     Set<String JavaDoc> interfaces = new HashSet<String JavaDoc>();
59     Set<String JavaDoc> notFinal = new HashSet<String JavaDoc>();
60     Set<String JavaDoc> outsidePackage = new HashSet<String JavaDoc>();
61     Map<String JavaDoc, SourceLineAnnotation> firstFieldUse = new HashMap<String JavaDoc, SourceLineAnnotation>();
62     private BugReporter bugReporter;
63
64     public MutableStaticFields(BugReporter bugReporter) {
65         this.bugReporter = bugReporter;
66     }
67
68     @Override JavaDoc
69          public void visit(JavaClass obj) {
70         super.visit(obj);
71         int flags = obj.getAccessFlags();
72         publicClass = (flags & ACC_PUBLIC) != 0
73                 && !getDottedClassName().startsWith("sun.");
74         if ((flags & ACC_INTERFACE) != 0) {
75             interfaces.add(getDottedClassName());
76             }
77
78         packageName = extractPackage(getClassName());
79     }
80
81     @Override JavaDoc
82          public void visit(Method obj) {
83         zeroOnTOS = false;
84         // System.out.println(methodName);
85
inStaticInitializer = getMethodName().equals("<clinit>");
86     }
87
88     @Override JavaDoc
89          public void sawOpcode(int seen) {
90         // System.out.println("saw " + OPCODE_NAMES[seen] + " " + zeroOnTOS);
91
switch (seen) {
92         case GETSTATIC:
93         case PUTSTATIC:
94             boolean samePackage =
95                     packageName.equals(extractPackage(getClassConstantOperand()));
96             boolean initOnly =
97                     seen == GETSTATIC ||
98                     getClassName().equals(getClassConstantOperand())
99                     && inStaticInitializer;
100             boolean safeValue =
101                     seen == GETSTATIC || emptyArrayOnTOS
102                     || !mutableSignature(getSigConstantOperand());
103             String JavaDoc name = (getClassConstantOperand() + "." + getNameConstantOperand())
104                     .replace('/', '.');
105             /*
106             System.out.println("In " + betterClassName
107                 + " accessing "
108                 + (classConstant + "." + nameConstant)
109                 + " " + samePackage
110                 + " " + initOnly
111                 + " " + safeValue
112                 );
113             */

114
115             if (!samePackage)
116                 outsidePackage.add(name);
117
118             if (!initOnly)
119                 notFinal.add(name);
120
121             if (!safeValue)
122                 unsafeValue.add(name);
123             
124             //Remove inStaticInitializer check to report all source lines of first use
125
//doing so, however adds quite a bit of memory bloat.
126
if (inStaticInitializer && !firstFieldUse.containsKey(name)) {
127                 SourceLineAnnotation sla = SourceLineAnnotation.fromVisitedInstruction(this);
128                 firstFieldUse.put(name, sla);
129             }
130             break;
131         case ANEWARRAY:
132         case NEWARRAY:
133             if (zeroOnTOS)
134                 emptyArrayOnTOS = true;
135             zeroOnTOS = false;
136             return;
137         case ICONST_0:
138             zeroOnTOS = true;
139             emptyArrayOnTOS = false;
140             return;
141         }
142         zeroOnTOS = false;
143         emptyArrayOnTOS = false;
144     }
145
146     @Override JavaDoc
147          public void visit(Field obj) {
148         super.visit(obj);
149         int flags = obj.getAccessFlags();
150         boolean isStatic = (flags & ACC_STATIC) != 0;
151         if (!isStatic) return;
152         boolean isVolatile = (flags & ACC_VOLATILE) != 0;
153         if (isVolatile) return;
154         boolean isFinal = (flags & ACC_FINAL) != 0;
155         boolean isPublic = publicClass && (flags & ACC_PUBLIC) != 0;
156         boolean isProtected = publicClass && (flags & ACC_PROTECTED) != 0;
157         if (!isPublic && !isProtected) return;
158
159         boolean isHashtable =
160             getFieldSig().equals("Ljava/util/Hashtable;");
161         boolean isArray = getFieldSig().charAt(0) == '[';
162
163         if (isFinal && !(isHashtable || isArray)) return;
164
165         FieldRecord f = new FieldRecord();
166         f.className = getDottedClassName();
167         f.name = getFieldName();
168         f.signature = getFieldSig();
169         f.isPublic = isPublic;
170         f.isFinal = isFinal;
171
172         seen.add(f);
173
174     }
175
176     @Override JavaDoc
177          public void report() {
178         /*
179         for(Iterator i = unsafeValue.iterator(); i.hasNext(); ) {
180             System.out.println("Unsafe: " + i.next());
181             }
182         */

183         for (FieldRecord f : seen) {
184             boolean isFinal = f.isFinal;
185             String JavaDoc className = f.className;
186             String JavaDoc fieldSig = f.signature;
187             String JavaDoc fieldName = f.name;
188             String JavaDoc name = className + "." + fieldName;
189             boolean couldBeFinal = !isFinal
190                     && !notFinal.contains(name);
191             boolean isPublic = f.isPublic;
192             boolean couldBePackage = !outsidePackage.contains(name);
193             boolean movedOutofInterface = couldBePackage &&
194                     interfaces.contains(className);
195             boolean isHashtable = fieldSig.equals("Ljava.util.Hashtable;");
196             boolean isArray = fieldSig.charAt(0) == '['
197                     && unsafeValue.contains(name);
198             
199             if (false)
200                           System.out.println(className + "." + fieldName
201                                       + " : " + fieldSig
202                                   + " " + isHashtable
203                                   + " " + isArray
204                                       );
205                         
206
207             String JavaDoc bugType;
208             int priority = NORMAL_PRIORITY;
209             if (isFinal && !isHashtable && !isArray) {
210                 continue;
211             } else if (movedOutofInterface) {
212                 bugType = "MS_OOI_PKGPROTECT";
213             } else if (couldBePackage && couldBeFinal && (isHashtable || isArray))
214                 bugType = "MS_FINAL_PKGPROTECT";
215             else if (couldBeFinal && !isHashtable && !isArray) {
216                 bugType = "MS_SHOULD_BE_FINAL";
217                 if (fieldName.equals(fieldName.toUpperCase())
218                         || fieldSig.charAt(0) == 'L')
219                     priority = HIGH_PRIORITY;
220             } else if (couldBePackage)
221                 bugType = "MS_PKGPROTECT";
222             else if (isHashtable) {
223                 bugType = "MS_MUTABLE_HASHTABLE";
224                 if (!isFinal)
225                     priority = HIGH_PRIORITY;
226             } else if (isArray) {
227                 bugType = "MS_MUTABLE_ARRAY";
228                 if (fieldSig.indexOf("L") >= 0 || !isFinal)
229                     priority = HIGH_PRIORITY;
230             } else if (!isFinal)
231                 bugType = "MS_CANNOT_BE_FINAL";
232             else
233                 throw new IllegalStateException JavaDoc("impossible");
234
235
236             BugInstance bug = new BugInstance(this, bugType, priority)
237                                                 .addClass(className)
238                                                 .addField(className, fieldName, fieldSig, true);
239             SourceLineAnnotation firstPC = firstFieldUse.get(className + "." + fieldName);
240             if (firstPC != null)
241                 bug.addSourceLine(firstPC);
242             bugReporter.reportBug(bug);
243
244         }
245     }
246 }
247
Popular Tags