1 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 extractPackage(String c) { 31 int i = c.lastIndexOf('/'); 32 if (i < 0) return ""; 33 return c.substring(0, i); 34 } 35 36 static boolean mutableSignature(String 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 className; 44 String name; 45 String 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 packageName; 57 Set<String > unsafeValue = new HashSet<String >(); 58 Set<String > interfaces = new HashSet<String >(); 59 Set<String > notFinal = new HashSet<String >(); 60 Set<String > outsidePackage = new HashSet<String >(); 61 Map<String , SourceLineAnnotation> firstFieldUse = new HashMap<String , SourceLineAnnotation>(); 62 private BugReporter bugReporter; 63 64 public MutableStaticFields(BugReporter bugReporter) { 65 this.bugReporter = bugReporter; 66 } 67 68 @Override 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 82 public void visit(Method obj) { 83 zeroOnTOS = false; 84 inStaticInitializer = getMethodName().equals("<clinit>"); 86 } 87 88 @Override 89 public void sawOpcode(int seen) { 90 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 name = (getClassConstantOperand() + "." + getNameConstantOperand()) 104 .replace('/', '.'); 105 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 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 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 177 public void report() { 178 183 for (FieldRecord f : seen) { 184 boolean isFinal = f.isFinal; 185 String className = f.className; 186 String fieldSig = f.signature; 187 String fieldName = f.name; 188 String 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 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 ("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 |