1 19 20 package edu.umd.cs.findbugs.ba.npe; 21 22 import java.util.BitSet ; 23 import java.util.Collections ; 24 import java.util.HashMap ; 25 import java.util.HashSet ; 26 import java.util.Iterator ; 27 import java.util.LinkedList ; 28 import java.util.List ; 29 import java.util.Map ; 30 import java.util.Set ; 31 import java.util.SortedSet ; 32 import java.util.TreeSet ; 33 34 import org.apache.bcel.Constants; 35 import org.apache.bcel.classfile.LineNumberTable; 36 import org.apache.bcel.classfile.Method; 37 import org.apache.bcel.generic.Instruction; 38 import org.apache.bcel.generic.InstructionHandle; 39 import org.apache.bcel.generic.InvokeInstruction; 40 41 import edu.umd.cs.findbugs.SystemProperties; 42 import edu.umd.cs.findbugs.ba.AnalysisFeatures; 43 import edu.umd.cs.findbugs.ba.AssertionMethods; 44 import edu.umd.cs.findbugs.ba.BasicBlock; 45 import edu.umd.cs.findbugs.ba.CFGBuilderException; 46 import edu.umd.cs.findbugs.ba.ClassContext; 47 import edu.umd.cs.findbugs.ba.DataflowAnalysisException; 48 import edu.umd.cs.findbugs.ba.Edge; 49 import edu.umd.cs.findbugs.ba.EdgeTypes; 50 import edu.umd.cs.findbugs.ba.Location; 51 import edu.umd.cs.findbugs.ba.deref.UnconditionalValueDerefDataflow; 52 import edu.umd.cs.findbugs.ba.deref.UnconditionalValueDerefSet; 53 import edu.umd.cs.findbugs.ba.vna.ValueNumber; 54 import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow; 55 import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame; 56 57 64 public class NullDerefAndRedundantComparisonFinder { 65 private static final boolean DEBUG = SystemProperties.getBoolean("fnd.debug"); 66 private static final boolean DEBUG_DEREFS = SystemProperties.getBoolean("fnd.derefs.debug"); 67 68 private ClassContext classContext; 69 private Method method; 70 private NullDerefAndRedundantComparisonCollector collector; 71 private final boolean findGuaranteedDerefs; 72 73 private List <RedundantBranch> redundantBranchList; 74 private BitSet definitelySameBranchSet; 75 private BitSet definitelyDifferentBranchSet; 76 private BitSet undeterminedBranchSet; 77 private BitSet lineMentionedMultipleTimes; 78 79 private IsNullValueDataflow invDataflow; 80 private ValueNumberDataflow vnaDataflow; 81 private UnconditionalValueDerefDataflow uvdDataflow; 82 private AssertionMethods assertionMethods; 83 84 static { 85 if (DEBUG) System.out.println("fnd.debug enabled"); 86 } 87 95 public NullDerefAndRedundantComparisonFinder( 96 ClassContext classContext, 97 Method method, 98 NullDerefAndRedundantComparisonCollector collector) { 99 100 this.classContext = classContext; 101 this.method = method; 102 this.collector = collector; 103 this.findGuaranteedDerefs = classContext.getAnalysisContext().getBoolProperty( 104 AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS); 105 this.lineMentionedMultipleTimes = ClassContext.linesMentionedMultipleTimes(method); 106 107 this.redundantBranchList = new LinkedList <RedundantBranch>(); 108 this.definitelySameBranchSet = new BitSet (); 109 this.definitelyDifferentBranchSet = new BitSet (); 110 this.undeterminedBranchSet = new BitSet (); 111 this.assertionMethods = classContext.getAssertionMethods(); 112 } 113 114 public void execute() throws DataflowAnalysisException, CFGBuilderException { 115 this.invDataflow = classContext.getIsNullValueDataflow(method); 117 this.vnaDataflow = classContext.getValueNumberDataflow(method); 118 if (findGuaranteedDerefs) { 119 if (DEBUG_DEREFS) { 120 System.out.println( 121 "Checking for guaranteed derefs in " + 122 classContext.getCFG(method).getMethodName()); 123 } 124 this.uvdDataflow = classContext.getUnconditionalValueDerefDataflow(method); 125 } 126 127 examineBasicBlocks(); 130 if (findGuaranteedDerefs) { 131 examineNullValues(); 132 } 133 examineRedundantBranches(); 134 135 } 136 137 144 private void examineBasicBlocks() throws DataflowAnalysisException, CFGBuilderException { 145 Iterator <BasicBlock> bbIter = invDataflow.getCFG().blockIterator(); 148 while (bbIter.hasNext()) { 149 BasicBlock basicBlock = bbIter.next(); 150 151 if (basicBlock.isNullCheck()) { 152 analyzeNullCheck(classContext, method, invDataflow, basicBlock); 153 } else if (!basicBlock.isEmpty()) { 154 InstructionHandle lastHandle = basicBlock.getLastInstruction(); 161 Instruction last = lastHandle.getInstruction(); 162 switch (last.getOpcode()) { 163 case Constants.IF_ACMPEQ: 164 case Constants.IF_ACMPNE: 165 analyzeRefComparisonBranch(basicBlock, lastHandle); 166 break; 167 case Constants.IFNULL: 168 case Constants.IFNONNULL: 169 analyzeIfNullBranch(basicBlock, lastHandle); 170 break; 171 } 172 } 173 } 174 } 175 176 181 static class NullValueUnconditionalDeref { 182 private boolean alwaysOnExceptionPath; 183 private Set <Location> derefLocationSet; 184 185 public NullValueUnconditionalDeref() { 186 this.alwaysOnExceptionPath = true; 187 this.derefLocationSet = new HashSet <Location>(); 188 } 189 190 194 public void add(IsNullValue isNullValue, Set <Location> unconditionalDerefLocationSet) { 195 if (!isNullValue.isException()) { 196 alwaysOnExceptionPath = false; 197 } 198 derefLocationSet.addAll(unconditionalDerefLocationSet); 199 } 200 201 204 public Set <Location> getDerefLocationSet() { 205 return derefLocationSet; 206 } 207 208 211 public boolean isAlwaysOnExceptionPath() { 212 return alwaysOnExceptionPath; 213 } 214 } 215 216 224 private void examineNullValues() throws CFGBuilderException, DataflowAnalysisException { 225 Set <LocationWhereValueBecomesNull> locationWhereValueBecomesNullSet = 226 invDataflow.getAnalysis().getLocationWhereValueBecomesNullSet(); 227 if (DEBUG_DEREFS) { 228 System.out.println("----------------------- examineNullValues " + locationWhereValueBecomesNullSet.size()); 229 } 230 231 Map <ValueNumber, SortedSet <Location>> bugLocationMap = 232 new HashMap <ValueNumber, SortedSet <Location>>(); 233 Map <ValueNumber, NullValueUnconditionalDeref> nullValueGuaranteedDerefMap = 236 new HashMap <ValueNumber, NullValueUnconditionalDeref>(); 237 238 for (Iterator <Location> i = classContext.getCFG(method).locationIterator(); i.hasNext();) { 240 Location location = i.next(); 241 242 if (DEBUG_DEREFS) { 243 System.out.println("At location " + location); 244 } 245 { 246 Instruction in = location.getHandle().getInstruction(); 247 if (in instanceof InvokeInstruction &&assertionMethods.isAssertionCall((InvokeInstruction) in) ) { 248 if (DEBUG_DEREFS) 249 System.out.println("Skipping because it is an assertion method "); 250 continue; 251 } 252 253 } 254 255 checkForUnconditionallyDereferencedNullValues( 256 location, 257 bugLocationMap, 258 nullValueGuaranteedDerefMap, 259 vnaDataflow.getFactAtLocation(location), invDataflow.getFactAtLocation(location), uvdDataflow.getFactAfterLocation(location)); 260 } 261 HashSet <ValueNumber> npeIfStatementCovered = new HashSet <ValueNumber>(nullValueGuaranteedDerefMap.keySet()); 262 263 for (Iterator <Edge> i = classContext.getCFG(method).edgeIterator(); i.hasNext();) { 265 Edge edge = i.next(); 266 267 if (edge.isExceptionEdge()) { 268 continue; 269 } 270 271 if (DEBUG_DEREFS) { 272 System.out.println("On edge " + edge.formatAsString(false)); 273 } 274 275 ValueNumberFrame vnaFact = vnaDataflow.getResultFact(edge.getSource()); 276 IsNullValueFrame invFact = invDataflow.getFactOnEdge(edge); 277 UnconditionalValueDerefSet uvdFact = uvdDataflow.getFactOnEdge(edge); 278 Location location = Location.getLastLocation(edge.getSource()); 279 if (location != null) { 280 Instruction in = location.getHandle().getInstruction(); 281 if (in instanceof InvokeInstruction &&assertionMethods.isAssertionCall((InvokeInstruction) in) ) { 282 if (DEBUG_DEREFS) 283 System.out.println("Skipping because it is an assertion method "); 284 continue; 285 } 286 287 288 checkForUnconditionallyDereferencedNullValues( 289 location, 290 bugLocationMap, 291 nullValueGuaranteedDerefMap, 292 vnaFact, invFact, uvdFact); 293 } 294 } 295 Map <ValueNumber, Set <Location>> nullValueAssignmentMap = 299 new HashMap <ValueNumber, Set <Location>>(); 300 for (LocationWhereValueBecomesNull lwvbn : locationWhereValueBecomesNullSet) { 301 if (DEBUG_DEREFS) System.out.println("OOO " + lwvbn); 302 Set <Location> locationSet = nullValueAssignmentMap.get(lwvbn.getValueNumber()); 303 if (locationSet == null) { 304 locationSet = new HashSet <Location>(); 305 nullValueAssignmentMap.put(lwvbn.getValueNumber(), locationSet); 306 } 307 locationSet.add(lwvbn.getLocation()); 308 if (DEBUG_DEREFS) 309 System.out.println(lwvbn.getValueNumber() + " becomes null at " + lwvbn.getLocation()); 310 } 311 312 for (Map.Entry <ValueNumber, NullValueUnconditionalDeref> e : nullValueGuaranteedDerefMap.entrySet()) { 314 ValueNumber valueNumber = e.getKey(); 315 Set <Location> derefLocationSet = e.getValue().getDerefLocationSet(); 316 Set <Location> assignedNullLocationSet = nullValueAssignmentMap.get(valueNumber); 317 if (assignedNullLocationSet == null) { 318 if (DEBUG_DEREFS) { 319 String where = classContext.getJavaClass().getClassName() + "." + method.getName() + ":" + method.getSignature(); 320 System.out.println("Problem at " + where); 321 for (Location loc : derefLocationSet) { 322 System.out.println("Dereference at " + loc); 323 } 324 } 325 if (false) 327 assert false: "No assigned NullLocationSet for " + valueNumber + " in " + nullValueAssignmentMap.keySet() 328 + " while analyzing " + classContext.getJavaClass().getClassName() + "." + method.getName(); 329 assignedNullLocationSet = Collections.EMPTY_SET; 330 } 331 collector.foundGuaranteedNullDeref( 332 assignedNullLocationSet, 333 derefLocationSet, 334 bugLocationMap.get(valueNumber), 335 vnaDataflow, valueNumber, 336 e.getValue().isAlwaysOnExceptionPath(), npeIfStatementCovered.contains(valueNumber)); 337 } 338 } 339 340 350 private void checkForUnconditionallyDereferencedNullValues( 351 Location thisLocation, 352 Map <ValueNumber, SortedSet <Location>> bugLocations, 353 Map <ValueNumber, NullValueUnconditionalDeref> nullValueGuaranteedDerefMap, 354 ValueNumberFrame vnaFrame, IsNullValueFrame invFrame, UnconditionalValueDerefSet derefSet) { 355 356 if (DEBUG_DEREFS) { 357 System.out.println("vna *** " + vnaFrame); 358 System.out.println("inv *** " + invFrame); 359 System.out.println("deref * " + derefSet); 360 } 361 362 if (!vnaFrame.isValid() || !invFrame.isValid() || vnaFrame.getNumSlots() != invFrame.getNumSlots()) { 364 return; 365 } 366 367 for (int j = 0; j < invFrame.getNumSlots(); j++) { 369 IsNullValue isNullValue = invFrame.getValue(j); 370 371 if (isNullValue.isDefinitelyNull()) { 372 ValueNumber valueNumber = vnaFrame.getValue(j); 374 375 if (derefSet.isUnconditionallyDereferenced(valueNumber)) { 376 noteUnconditionallyDereferencedNullValue( 377 thisLocation, 378 bugLocations, 379 nullValueGuaranteedDerefMap, 380 derefSet, isNullValue, valueNumber); 381 } 382 } 383 } 384 385 for (Map.Entry <ValueNumber, IsNullValue> entry : invFrame.getKnownValueMapEntrySet()) { 388 if (!entry.getValue().isDefinitelyNull()) { 389 continue; 390 } 391 392 if (derefSet.isUnconditionallyDereferenced(entry.getKey())) { 393 noteUnconditionallyDereferencedNullValue( 394 thisLocation, 395 bugLocations, 396 nullValueGuaranteedDerefMap, 397 derefSet, entry.getValue(), entry.getKey()); 398 } 399 } 400 401 } 402 403 413 private void noteUnconditionallyDereferencedNullValue(Location thisLocation, Map <ValueNumber, SortedSet <Location>> bugLocations, Map <ValueNumber, NullValueUnconditionalDeref> nullValueGuaranteedDerefMap, UnconditionalValueDerefSet derefSet, IsNullValue isNullValue, ValueNumber valueNumber) { 414 if (DEBUG) { 415 System.out.println("%%% HIT for value number " + valueNumber + " @ " + thisLocation); 416 } 417 418 NullValueUnconditionalDeref thisNullValueDeref = nullValueGuaranteedDerefMap.get(valueNumber); 422 if (thisNullValueDeref == null) { 423 thisNullValueDeref = new NullValueUnconditionalDeref(); 424 nullValueGuaranteedDerefMap.put(valueNumber, thisNullValueDeref); 425 } 426 thisNullValueDeref.add(isNullValue, derefSet.getUnconditionalDerefLocationSet(valueNumber)); 427 428 if (thisLocation != null) { 429 SortedSet <Location> locationsForThisBug = bugLocations.get(valueNumber); 430 431 if (locationsForThisBug == null) { 432 locationsForThisBug = new TreeSet <Location>(); 433 bugLocations.put(valueNumber, locationsForThisBug); 434 } 435 locationsForThisBug.add(thisLocation); 436 } 437 } 438 439 442 private void examineRedundantBranches() { 443 for (RedundantBranch redundantBranch : redundantBranchList) { 444 if (DEBUG) System.out.println("Redundant branch: " + redundantBranch); 445 int lineNumber = redundantBranch.lineNumber; 446 447 452 boolean confused = undeterminedBranchSet.get(lineNumber) || 453 (definitelySameBranchSet.get(lineNumber) && definitelyDifferentBranchSet.get(lineNumber)); 454 455 457 boolean reportIt = true; 458 if (lineMentionedMultipleTimes.get(lineNumber) && confused) 459 reportIt = false; 460 if (redundantBranch.location.getBasicBlock().isInJSRSubroutine() 461 && confused) 462 reportIt = false; 463 if (reportIt) { 464 collector.foundRedundantNullCheck(redundantBranch.location, redundantBranch); 465 } 466 } 467 } 468 469 private void analyzeRefComparisonBranch( 470 BasicBlock basicBlock, 471 InstructionHandle lastHandle) throws DataflowAnalysisException { 472 473 Location location = new Location(lastHandle, basicBlock); 474 475 IsNullValueFrame frame = invDataflow.getFactAtLocation(location); 476 if (!frame.isValid()) { 477 return; 479 } 480 if (frame.getStackDepth() < 2) 481 throw new DataflowAnalysisException("Stack underflow at " + lastHandle); 482 483 int lineNumber = getLineNumber(method, lastHandle); 485 if (lineNumber < 0) 486 return; 487 488 int numSlots = frame.getNumSlots(); 489 IsNullValue top = frame.getValue(numSlots - 1); 490 IsNullValue topNext = frame.getValue(numSlots - 2); 491 492 boolean definitelySame = top.isDefinitelyNull() && topNext.isDefinitelyNull(); 493 boolean definitelyDifferent = 494 (top.isDefinitelyNull() && topNext.isDefinitelyNotNull()) || 495 (top.isDefinitelyNotNull() && topNext.isDefinitelyNull()); 496 497 if (definitelySame || definitelyDifferent) { 498 if (definitelySame) { 499 if (DEBUG) System.out.println("Line " + lineNumber + " always same"); 500 definitelySameBranchSet.set(lineNumber); 501 } 502 if (definitelyDifferent) { 503 if (DEBUG) System.out.println("Line " + lineNumber + " always different"); 504 definitelyDifferentBranchSet.set(lineNumber); 505 } 506 507 RedundantBranch redundantBranch = new RedundantBranch(location, lineNumber, top, topNext); 508 509 boolean wantSame = (lastHandle.getInstruction().getOpcode() == Constants.IF_ACMPEQ); 511 int infeasibleEdgeType = (wantSame == definitelySame) 512 ? EdgeTypes.FALL_THROUGH_EDGE : EdgeTypes.IFCMP_EDGE; 513 Edge infeasibleEdge = invDataflow.getCFG().getOutgoingEdgeWithType(basicBlock, infeasibleEdgeType); 514 redundantBranch.setInfeasibleEdge(infeasibleEdge); 515 516 if (DEBUG) System.out.println("Adding redundant branch: " + redundantBranch); 517 redundantBranchList.add(redundantBranch); 518 } else { 519 if (DEBUG) System.out.println("Line " + lineNumber + " undetermined"); 520 undeterminedBranchSet.set(lineNumber); 521 } 522 } 523 524 private void analyzeIfNullBranch( 526 BasicBlock basicBlock, 527 InstructionHandle lastHandle) throws DataflowAnalysisException { 528 529 Location location = new Location(lastHandle, basicBlock); 530 531 IsNullValueFrame frame = invDataflow.getFactAtLocation(location); 532 533 if (!frame.isValid()) { 534 return; 536 } 537 538 IsNullValue top = frame.getTopValue(); 539 540 int lineNumber = getLineNumber(method, lastHandle); 542 if (lineNumber < 0) 543 return; 544 545 if (!(top.isDefinitelyNull() || top.isDefinitelyNotNull())) { 546 if (DEBUG) System.out.println("Line " + lineNumber + " undetermined"); 547 undeterminedBranchSet.set(lineNumber); 548 return; 549 } 550 551 short opcode = lastHandle.getInstruction().getOpcode(); 554 boolean definitelySame = top.isDefinitelyNull(); 555 if (opcode != Constants.IFNULL) definitelySame = !definitelySame; 556 557 if (definitelySame) { 558 if (DEBUG) System.out.println("Line " + lineNumber + " always same"); 559 definitelySameBranchSet.set(lineNumber); 560 } else { 561 if (DEBUG) System.out.println("Line " + lineNumber + " always different"); 562 definitelyDifferentBranchSet.set(lineNumber); 563 } 564 565 RedundantBranch redundantBranch = new RedundantBranch(location, lineNumber, top); 566 567 boolean wantNull = (opcode == Constants.IFNULL); 569 int infeasibleEdgeType = (wantNull == top.isDefinitelyNull()) 570 ? EdgeTypes.FALL_THROUGH_EDGE : EdgeTypes.IFCMP_EDGE; 571 Edge infeasibleEdge = invDataflow.getCFG().getOutgoingEdgeWithType(basicBlock, infeasibleEdgeType); 572 redundantBranch.setInfeasibleEdge(infeasibleEdge); 573 574 if (DEBUG) System.out.println("Adding redundant branch: " + redundantBranch); 575 redundantBranchList.add(redundantBranch); 576 } 577 578 private void analyzeNullCheck(ClassContext classContext, Method method, IsNullValueDataflow invDataflow, 579 BasicBlock basicBlock) 580 throws DataflowAnalysisException, CFGBuilderException { 581 584 InstructionHandle exceptionThrowerHandle = basicBlock.getExceptionThrower(); 585 Instruction exceptionThrower = exceptionThrowerHandle.getInstruction(); 586 587 IsNullValueFrame frame = invDataflow.getStartFact(basicBlock); 589 if (!frame.isValid()) 590 return; 591 592 593 IsNullValue refValue = frame.getInstance(exceptionThrower, classContext.getConstantPoolGen()); 595 if (DEBUG) { 596 System.out.println("For basic block " + basicBlock + " value is " + refValue); 597 } 598 if (!refValue.mightBeNull()) 599 return; 600 601 ValueNumberFrame vnaFrame = classContext.getValueNumberDataflow(method).getStartFact(basicBlock); 603 if (!vnaFrame.isValid()) 604 return; 605 ValueNumber valueNumber = vnaFrame.getInstance(exceptionThrower, classContext.getConstantPoolGen()); 606 Location location = new Location(exceptionThrowerHandle, basicBlock); 607 if (DEBUG) System.out.println("Warning: VN " + valueNumber + " invf: " + frame + " @ " + location); 608 609 collector.foundNullDeref(classContext, location, valueNumber, refValue, vnaFrame); 611 } 612 613 private static int getLineNumber(Method method, InstructionHandle handle) { 614 LineNumberTable table = method.getCode().getLineNumberTable(); 615 if (table == null) 616 return -1; 617 return table.getSourceLine(handle.getPosition()); 618 } 619 } | Popular Tags |