1 19 20 package edu.umd.cs.findbugs.detect; 21 22 import java.io.IOException ; 23 import java.io.InputStreamReader ; 24 import java.util.ArrayList ; 25 import java.util.HashSet ; 26 import java.util.ListIterator ; 27 import java.util.Set ; 28 29 import org.apache.bcel.classfile.Code; 30 import org.apache.bcel.classfile.CodeException; 31 import org.apache.bcel.classfile.LineNumber; 32 import org.apache.bcel.classfile.LineNumberTable; 33 import org.apache.bcel.classfile.Utility; 34 35 import edu.umd.cs.findbugs.BugInstance; 36 import edu.umd.cs.findbugs.BugReporter; 37 import edu.umd.cs.findbugs.Detector; 38 import edu.umd.cs.findbugs.SourceLineAnnotation; 39 import edu.umd.cs.findbugs.SystemProperties; 40 import edu.umd.cs.findbugs.Token; 41 import edu.umd.cs.findbugs.Tokenizer; 42 import edu.umd.cs.findbugs.ba.AnalysisContext; 43 import edu.umd.cs.findbugs.ba.ClassContext; 44 import edu.umd.cs.findbugs.ba.Hierarchy; 45 import edu.umd.cs.findbugs.ba.SourceFile; 46 import edu.umd.cs.findbugs.ba.SourceFinder; 47 import edu.umd.cs.findbugs.visitclass.PreorderVisitor; 48 49 public class DroppedException extends PreorderVisitor implements Detector { 50 private static final boolean DEBUG = SystemProperties.getBoolean("de.debug"); 51 private static final boolean LOOK_IN_SOURCE_TO_FIND_COMMENTED_CATCH_BLOCKS 52 = SystemProperties.getBoolean("findbugs.de.comment"); 53 54 Set <String > reported = new HashSet <String >(); 55 Set <String > causes = new HashSet <String >(); 56 Set <String > checkedCauses = new HashSet <String >(); 57 private BugReporter bugReporter; 58 private ClassContext classContext; 59 60 public DroppedException(BugReporter bugReporter) { 61 this.bugReporter = bugReporter; 62 if (DEBUG) System.out.println("Dropped Exception debugging turned on"); 63 } 64 65 public void visitClassContext(ClassContext classContext) { 66 this.classContext = classContext; 67 classContext.getJavaClass().accept(this); 68 } 69 70 public void report() { 71 } 72 73 boolean isChecked(String c) { 74 if (!causes.add(c)) return checkedCauses.contains(c); 75 try { 76 if (Hierarchy.isSubtype(c, "java.lang.Exception") 77 && !Hierarchy.isSubtype(c, "java.lang.RuntimeException")) { 78 checkedCauses.add(c); 79 return true; 80 } 81 } catch (ClassNotFoundException e) { 82 bugReporter.reportMissingClass(e); 83 } 84 return false; 85 } 86 87 88 private int getUnsignedShort(byte[] a, int i) { 89 return asUnsignedByte(a[i]) << 8 90 | asUnsignedByte(a[i + 1]); 91 } 92 93 @Override 94 public void visit(Code obj) { 95 96 CodeException[] exp = obj.getExceptionTable(); 97 LineNumberTable lineNumbers = obj.getLineNumberTable(); 98 if (exp == null) return; 99 byte[] code = obj.getCode(); 100 101 for (CodeException aExp : exp) { 102 int handled = aExp.getHandlerPC(); 103 int start = aExp.getStartPC(); 104 int end = aExp.getEndPC(); 105 int cause = aExp.getCatchType(); 106 boolean exitInTryBlock = false; 107 if (DEBUG) { 108 System.out.println("start = " + start 109 + ", end = " + end 110 + ", codeLength = " + code.length 111 + ", handled = " + handled); 112 } 113 114 for (int j = start; j <= end && j < code.length;) { 115 int opcode = asUnsignedByte(code[j]); 116 if (NO_OF_OPERANDS[opcode] < 0) { 117 exitInTryBlock = true; 118 break; 119 } 120 j += 1 + NO_OF_OPERANDS[opcode]; 121 if (opcode >= IRETURN && opcode <= RETURN 122 || opcode >= IFEQ && opcode <= GOTO 123 && (opcode != GOTO || j < end) 124 ) { 125 exitInTryBlock = true; 126 if (DEBUG) 127 System.out.println(" exit: " + opcode 128 + " in " + getFullyQualifiedMethodName()); 129 break; 130 } 131 132 } 133 134 if (exitInTryBlock) { 135 if (DEBUG) System.out.println("Exit in try block"); 136 continue; 137 } 138 if (handled < 5) continue; 139 String c; 140 if (cause == 0) 141 c = "Throwable"; 142 else { 143 c = Utility.compactClassName(getConstantPool().getConstantString(cause, 144 CONSTANT_Class), false); 145 if (!isChecked(c)) continue; 146 } 147 148 149 int jumpAtEnd = 0; 150 if (asUnsignedByte(code[end]) == GOTO) { 151 jumpAtEnd = getUnsignedShort(code, end + 1); 152 if (jumpAtEnd < handled) jumpAtEnd = 0; 153 } 154 155 int opcode = asUnsignedByte(code[handled]); 156 int afterHandler = 0; 157 if (DEBUG) System.out.println("DE: opcode is " + opcode + ", " + asUnsignedByte(code[handled + 1])); 158 boolean drops = false; 159 boolean startsWithASTORE03 = opcode >= ASTORE_0 160 && opcode <= ASTORE_3; 161 if (startsWithASTORE03 162 && asUnsignedByte(code[handled + 1]) == RETURN) { 163 if (DEBUG) System.out.println("Drop 1"); 164 drops = true; 165 afterHandler = handled + 1; 166 } 167 if (handled + 2 < code.length 168 && opcode == ASTORE 169 && asUnsignedByte(code[handled + 2]) == RETURN) { 170 drops = true; 171 afterHandler = handled + 2; 172 if (DEBUG) System.out.println("Drop 2"); 173 } 174 if (handled + 3 < code.length 175 && !exitInTryBlock) { 176 if (DEBUG) System.out.println("DE: checking for jumps"); 177 if (startsWithASTORE03 178 && asUnsignedByte(code[handled - 3]) == GOTO) { 179 int offsetBefore = 180 getUnsignedShort(code, handled - 2); 181 if (DEBUG) System.out.println("offset before = " + offsetBefore); 182 if (offsetBefore == 4) { 183 drops = true; 184 afterHandler = handled + 1; 185 if (DEBUG) System.out.println("Drop 3"); 186 } 187 } 188 if (opcode == ASTORE 189 && asUnsignedByte(code[handled - 3]) == GOTO) { 190 int offsetBefore = 191 getUnsignedShort(code, handled - 2); 192 if (offsetBefore == 5) { 193 drops = true; 194 afterHandler = handled + 2; 195 if (DEBUG) System.out.println("Drop 4"); 196 } 197 } 198 if (startsWithASTORE03 199 && asUnsignedByte(code[handled + 1]) == GOTO 200 && asUnsignedByte(code[handled - 3]) == GOTO) { 201 int offsetBefore = 202 getUnsignedShort(code, handled - 2); 203 int offsetAfter = 204 getUnsignedShort(code, handled + 2); 205 206 if (offsetAfter > 0 && offsetAfter + 4 == offsetBefore) { 207 drops = true; 208 afterHandler = handled + 4; 209 if (DEBUG) System.out.println("Drop 5"); 210 } 211 } 212 213 if (opcode == ASTORE 214 && asUnsignedByte(code[handled + 2]) == GOTO 215 && asUnsignedByte(code[handled - 3]) == GOTO) { 216 int offsetBefore = 217 getUnsignedShort(code, handled - 2); 218 int offsetAfter = 219 getUnsignedShort(code, handled + 3); 220 221 if (offsetAfter > 0 && offsetAfter + 5 == offsetBefore) { 222 drops = true; 223 afterHandler = handled + 5; 224 if (DEBUG) System.out.println("Drop 6"); 225 } 226 } 227 228 } 229 230 boolean multiLineHandler = false; 231 if (DEBUG) 232 System.out.println("afterHandler = " + afterHandler 233 + ", handled = " + handled); 234 if (afterHandler > handled && lineNumbers != null) { 235 int startHandlerLinenumber = lineNumbers.getSourceLine(handled); 236 237 int endHandlerLinenumber 238 = getNextExecutableLineNumber(lineNumbers, afterHandler) - 1; 239 if (DEBUG) 240 System.out.println("Handler in lines " 241 + startHandlerLinenumber 242 + "-" 243 + endHandlerLinenumber); 244 if (endHandlerLinenumber > startHandlerLinenumber) { 245 multiLineHandler = true; 246 if (DEBUG) System.out.println("Multiline handler"); 247 } 248 } 249 250 if (end - start >= 5 && drops && !c.equals("java.lang.InterruptedException") 251 && !c.equals("java.lang.CloneNotSupportedException")) { 252 int priority = NORMAL_PRIORITY; 253 if (exitInTryBlock) priority++; 254 SourceLineAnnotation srcLine 255 = SourceLineAnnotation.fromVisitedInstruction(this.classContext, this, handled); 256 if (srcLine != null && LOOK_IN_SOURCE_TO_FIND_COMMENTED_CATCH_BLOCKS) { 257 if (catchBlockHasComment(srcLine)) 258 return; 259 else 260 priority++; 261 } else { 262 if (lineNumbers == null || multiLineHandler) priority += 2; 264 } 265 if (c.equals("java.lang.Error") 266 || c.equals("java.lang.Exception") 267 || c.equals("java.lang.Throwable") 268 || c.equals("java.lang.RuntimeException")) { 269 priority--; 270 if (end - start > 30) priority--; 271 } 272 273 if (DEBUG) { 274 System.out.println("Priority is " + priority); 275 } 276 if (priority > LOW_PRIORITY) return; 277 if (priority < HIGH_PRIORITY) priority = HIGH_PRIORITY; 278 if (DEBUG) { 279 System.out.println("reporting warning"); 280 } 281 282 String key = (exitInTryBlock ? "mightDrop," : "mightIgnore,") + getFullyQualifiedMethodName() + "," + c; 283 if (reported.add(key)) { 284 BugInstance bugInstance = new BugInstance(this, exitInTryBlock ? "DE_MIGHT_DROP" : "DE_MIGHT_IGNORE", 285 priority) 286 .addClassAndMethod(this); 287 bugInstance.addClass(c).describe("CLASS_EXCEPTION"); 288 bugInstance.addSourceLine(srcLine); 289 bugReporter.reportBug(bugInstance); 290 } 291 292 } 293 } 294 } 295 296 private int getNextExecutableLineNumber(LineNumberTable linenumbers, int PC) { 297 LineNumber[] entries = linenumbers.getLineNumberTable(); 298 int beforePC = 0; 299 int i = 0; 300 for (; i < entries.length && entries[i].getStartPC() < PC; i++) { 301 int line = entries[i].getLineNumber(); 302 if (line > beforePC) 303 beforePC = line; 304 } 305 306 if (i < entries.length) { 307 int secondChoice = entries[i].getLineNumber(); 308 for (; i < entries.length; i++) { 309 int line = entries[i].getLineNumber(); 310 if (line > beforePC) 311 return line; 312 } 313 return secondChoice; 314 } else 315 return entries[entries.length - 1].getLineNumber(); 316 } 317 318 319 private static final int START = 0; 320 private static final int CATCH = 1; 321 private static final int OPEN_PAREN = 2; 322 private static final int CLOSE_PAREN = 3; 323 private static final int OPEN_BRACE = 4; 324 325 331 private static final int NUM_CONTEXT_LINES = 3; 332 333 337 private static final int MAX_LINES = 7; 338 339 347 private boolean catchBlockHasComment(SourceLineAnnotation srcLine) { 348 if (!LOOK_IN_SOURCE_TO_FIND_COMMENTED_CATCH_BLOCKS) 349 return false; 350 351 SourceFinder sourceFinder = AnalysisContext.currentAnalysisContext().getSourceFinder(); 352 try { 353 SourceFile sourceFile = sourceFinder.findSourceFile(srcLine.getPackageName(), srcLine.getSourceFile()); 354 int startLine = srcLine.getStartLine(); 355 356 int scanStartLine = startLine - NUM_CONTEXT_LINES; 357 if (scanStartLine < 1) 358 scanStartLine = 1; 359 360 int offset = sourceFile.getLineOffset(scanStartLine - 1); 361 if (offset < 0) 362 return false; Tokenizer tokenizer = new Tokenizer(new InputStreamReader (sourceFile.getInputStreamFromOffset(offset))); 364 365 ArrayList <Token> tokenList = new ArrayList <Token>(40); 369 int eolOfCatchBlockStart = -1; 370 for (int line = scanStartLine; line < scanStartLine + MAX_LINES;) { 371 Token token = tokenizer.next(); 372 int kind = token.getKind(); 373 if (kind == Token.EOF) 374 break; 375 376 if (kind == Token.EOL) { 377 if (line == startLine) 378 eolOfCatchBlockStart = tokenList.size(); 379 ++line; 380 } 381 382 tokenList.add(token); 383 } 384 385 if (eolOfCatchBlockStart < 0) 386 return false; 388 ListIterator <Token> iter = tokenList.listIterator(eolOfCatchBlockStart); 391 boolean foundCatch = false; 392 393 while (iter.hasPrevious()) { 394 Token token = iter.previous(); 395 if (token.getKind() == Token.WORD && token.getLexeme().equals("catch")) { 396 foundCatch = true; 397 break; 398 } 399 } 400 401 if (!foundCatch) 402 return false; 404 boolean done = false; 410 int numLines = 0; 411 int state = START; 412 int level = 0; 413 do { 414 if (!iter.hasNext()) 415 break; 416 417 Token token = iter.next(); 418 int type = token.getKind(); 419 String value = token.getLexeme(); 420 421 switch (type) { 422 case Token.EOL: 423 if (DEBUG) System.out.println("Saw token: [EOL]"); 424 ++numLines; 425 if (numLines >= MAX_LINES) 426 done = true; 427 break; 428 default: 429 if (DEBUG) System.out.println("Got token: " + value); 430 switch (state) { 431 case START: 432 if (value.equals("catch")) 433 state = CATCH; 434 break; 435 case CATCH: 436 if (value.equals("(")) 437 state = OPEN_PAREN; 438 break; 439 case OPEN_PAREN: 440 if (value.equals(")")) { 441 if (level == 0) 442 state = CLOSE_PAREN; 443 else 444 --level; 445 } else if (value.equals("(")) { 446 ++level; 447 } 448 break; 449 case CLOSE_PAREN: 450 if (value.equals("{")) 451 state = OPEN_BRACE; 452 break; 453 case OPEN_BRACE: 454 boolean closeBrace = value.equals("}"); 455 if (DEBUG && !closeBrace) System.out.println("Found a comment in catch block: " + value); 456 return !closeBrace; 457 } 458 break; 459 } 460 } while (!done); 461 } catch (IOException e) { 462 if (DEBUG) e.printStackTrace(); 464 } 465 return false; 466 } 467 } 468 469 | Popular Tags |