1 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.ba.vna.*; 27 import edu.umd.cs.findbugs.props.WarningPropertySet; 28 import java.util.*; 29 import org.apache.bcel.Constants; 30 import org.apache.bcel.classfile.*; 31 import org.apache.bcel.generic.*; 32 33 41 public class FindInconsistentSync2 implements Detector { 42 private static final boolean DEBUG = SystemProperties.getBoolean("fis.debug"); 43 private static final boolean SYNC_ACCESS = true; 44 private static final boolean ADJUST_SUBCLASS_ACCESSES = !SystemProperties.getBoolean("fis.noAdjustSubclass"); 46 private static final boolean EVAL = SystemProperties.getBoolean("fis.eval"); 47 48 51 52 58 private static final int MIN_SYNC_PERCENT = 59 SystemProperties.getInteger("findbugs.fis.minSyncPercent", 50).intValue(); 60 61 66 private static final double WRITE_BIAS = 67 Double.parseDouble(SystemProperties.getProperty("findbugs.fis.writeBias", "2.0")); 68 69 84 private static final double UNSYNC_FACTOR = 85 Double.parseDouble(SystemProperties.getProperty("findbugs.fis.unsyncFactor", "2.0")); 86 87 90 91 private static final int UNLOCKED = 0; 92 private static final int LOCKED = 1; 93 private static final int READ = 0; 94 private static final int WRITE = 2; 95 96 private static final int READ_UNLOCKED = READ | UNLOCKED; 97 private static final int WRITE_UNLOCKED = WRITE | UNLOCKED; 98 private static final int READ_LOCKED = READ | LOCKED; 99 private static final int WRITE_LOCKED = WRITE | LOCKED; 100 101 106 private static class FieldStats { 107 private int[] countList = new int[4]; 108 private int numLocalLocks = 0; 109 private int numGetterMethodAccesses = 0; 110 private LinkedList<SourceLineAnnotation> unsyncAccessList = new LinkedList<SourceLineAnnotation>(); 111 private LinkedList<SourceLineAnnotation> syncAccessList = new LinkedList<SourceLineAnnotation>(); 112 113 public void addAccess(int kind) { 114 countList[kind]++; 115 } 116 117 public int getNumAccesses(int kind) { 118 return countList[kind]; 119 } 120 121 public void addLocalLock() { 122 numLocalLocks++; 123 } 124 125 public int getNumLocalLocks() { 126 return numLocalLocks; 127 } 128 129 public void addGetterMethodAccess() { 130 numGetterMethodAccesses++; 131 } 132 133 public int getNumGetterMethodAccesses() { 134 return numGetterMethodAccesses; 135 } 136 137 public void addAccess(ClassContext classContext, Method method, InstructionHandle handle, boolean isLocked) { 138 if (!SYNC_ACCESS && isLocked) 139 return; 140 141 JavaClass javaClass = classContext.getJavaClass(); 142 String sourceFile = javaClass.getSourceFileName(); 143 MethodGen methodGen = classContext.getMethodGen(method); 144 SourceLineAnnotation accessSourceLine = SourceLineAnnotation.fromVisitedInstruction( 145 classContext, methodGen, sourceFile, handle); 146 if (accessSourceLine != null) 147 (isLocked ? syncAccessList : unsyncAccessList).add(accessSourceLine); 148 } 149 150 public Iterator<SourceLineAnnotation> unsyncAccessIterator() { 151 return unsyncAccessList.iterator(); 152 } 153 154 public Iterator<SourceLineAnnotation> syncAccessIterator() { 155 return syncAccessList.iterator(); 156 } 157 } 158 159 162 163 private BugReporter bugReporter; 164 private Map<XField, FieldStats> statMap = new HashMap<XField, FieldStats>(); 165 166 169 170 public FindInconsistentSync2(BugReporter bugReporter) { 171 this.bugReporter = bugReporter; 172 } 173 174 public void visitClassContext(ClassContext classContext) { 175 JavaClass javaClass = classContext.getJavaClass(); 176 if (DEBUG) System.out.println("******** Analyzing class " + javaClass.getClassName()); 177 178 SelfCalls selfCalls = new SelfCalls(classContext) { 180 @Override 181 public boolean wantCallsFor(Method method) { 182 return !method.isPublic(); 183 } 184 }; 185 186 Set<Method> lockedMethodSet; 187 Set<Method> publicReachableMethods; 188 189 try { 190 selfCalls.execute(); 191 CallGraph callGraph = selfCalls.getCallGraph(); 192 if (DEBUG) 193 System.out.println("Call graph (not unlocked methods): " + callGraph.getNumVertices() + " nodes, " + 194 callGraph.getNumEdges() + " edges"); 195 Set<CallSite> obviouslyLockedSites = findObviouslyLockedCallSites(classContext, selfCalls); 197 lockedMethodSet = findNotUnlockedMethods(classContext, selfCalls, obviouslyLockedSites); 198 lockedMethodSet.retainAll(findLockedMethods(classContext, selfCalls, obviouslyLockedSites)); 199 publicReachableMethods = findPublicReachableMethods(classContext, selfCalls); 200 } catch (CFGBuilderException e) { 201 bugReporter.logError("Error finding locked call sites", e); 202 return; 203 } catch (DataflowAnalysisException e) { 204 bugReporter.logError("Error finding locked call sites", e); 205 return; 206 } 207 208 for (Method method : publicReachableMethods) { 209 if (classContext.getMethodGen(method) == null) 210 continue; 211 212 216 if (method.getName().startsWith("access$")) 217 continue; 220 try { 221 analyzeMethod(classContext, method, lockedMethodSet); 222 } catch (CFGBuilderException e) { 223 bugReporter.logError("Error analyzing method", e); 224 } catch (DataflowAnalysisException e) { 225 bugReporter.logError("Error analyzing method", e); 226 } 227 } 228 } 229 230 public void report() { 231 for (XField xfield : statMap.keySet()) { 232 FieldStats stats = statMap.get(xfield); 233 JCIPAnnotationDatabase jcipAnotationDatabase = AnalysisContext.currentAnalysisContext() 234 .getJCIPAnnotationDatabase(); 235 boolean guardedByThis = "this".equals(jcipAnotationDatabase.getFieldAnnotation(xfield, "GuardedBy")); 236 boolean notThreadSafe = jcipAnotationDatabase.hasClassAnnotation(xfield.getClassName(), "NotThreadSafe"); 237 boolean threadSafe = jcipAnotationDatabase.hasClassAnnotation(xfield.getClassName().replace('/','.'), "ThreadSafe"); 238 if (notThreadSafe) continue; 239 240 WarningPropertySet propertySet = new WarningPropertySet(); 241 242 int numReadUnlocked = stats.getNumAccesses(READ_UNLOCKED); 243 int numWriteUnlocked = stats.getNumAccesses(WRITE_UNLOCKED); 244 int numReadLocked = stats.getNumAccesses(READ_LOCKED); 245 int numWriteLocked = stats.getNumAccesses(WRITE_LOCKED); 246 247 int locked = numReadLocked + numWriteLocked; 248 int biasedLocked = numReadLocked + (int) (WRITE_BIAS * numWriteLocked); 249 int unlocked = numReadUnlocked + numWriteUnlocked; 250 int biasedUnlocked = numReadUnlocked + (int) (WRITE_BIAS * numWriteUnlocked); 251 int writes = numWriteLocked + numWriteUnlocked; 252 253 if (unlocked == 0) { 254 continue; 255 } 257 258 259 if (guardedByThis) { 260 propertySet.addProperty(InconsistentSyncWarningProperty.ANNOTATED_AS_GUARDED_BY_THIS); 261 262 } 263 264 if (threadSafe) { 265 propertySet.addProperty(InconsistentSyncWarningProperty.ANNOTATED_AS_THREAD_SAFE); 266 267 } 268 if (!guardedByThis && locked == 0) { 269 continue; 270 } 272 273 if (DEBUG) { 274 System.out.println("IS2: " + xfield); 275 if (guardedByThis) System.out.println("Guarded by this"); 276 System.out.println(" RL: " + numReadLocked); 277 System.out.println(" WL: " + numWriteLocked); 278 System.out.println(" RU: " + numReadUnlocked); 279 System.out.println(" WU: " + numWriteUnlocked); 280 } 281 if (!EVAL && numReadUnlocked > 0 && ((int) (UNSYNC_FACTOR * biasedUnlocked)) > biasedLocked) { 282 propertySet.addProperty(InconsistentSyncWarningProperty.MANY_BIASED_UNLOCKED); 284 } 285 286 288 if (numWriteUnlocked + numWriteLocked == 0) { 289 if (DEBUG) System.out.println(" No writes outside of constructor"); 291 propertySet.addProperty(InconsistentSyncWarningProperty.NEVER_WRITTEN); 292 } 294 295 if (numReadUnlocked + numReadLocked == 0) { 296 if (DEBUG) System.out.println(" No reads outside of constructor"); 298 propertySet.addProperty(InconsistentSyncWarningProperty.NEVER_READ); 300 } 301 302 if (stats.getNumLocalLocks() == 0) { 303 if (DEBUG) System.out.println(" No local locks"); 304 propertySet.addProperty(InconsistentSyncWarningProperty.NO_LOCAL_LOCKS); 306 } 307 308 int freq; 309 if (locked + unlocked > 0) { 310 freq = (100 * locked) / (locked + unlocked); 311 } else { 312 freq = 0; 313 } 314 if (freq < MIN_SYNC_PERCENT) { 315 propertySet.addProperty(InconsistentSyncWarningProperty.BELOW_MIN_SYNC_PERCENT); 317 } 318 if (DEBUG) System.out.println(" Sync %: " + freq); 319 320 if (stats.getNumGetterMethodAccesses() >= unlocked) { 321 propertySet.addProperty(InconsistentSyncWarningProperty.ONLY_UNSYNC_IN_GETTERS); 323 } 324 325 int priority = propertySet.computePriority(NORMAL_PRIORITY); 327 if (!propertySet.isFalsePositive(priority)) { 328 BugInstance bugInstance = new BugInstance(guardedByThis? "IS_FIELD_NOT_GUARDED" : "IS2_INCONSISTENT_SYNC", priority) 329 .addClass(xfield.getClassName()) 330 .addField(xfield) 331 .addInt(freq).describe(IntAnnotation.INT_SYNC_PERCENT); 332 333 if (FindBugsAnalysisFeatures.isRelaxedMode()) { 334 propertySet.decorateBugInstance(bugInstance); 335 } 336 337 for (Iterator<SourceLineAnnotation> j = stats.unsyncAccessIterator(); j.hasNext();) { 339 SourceLineAnnotation accessSourceLine = j.next(); 340 bugInstance.addSourceLine(accessSourceLine).describe("SOURCE_LINE_UNSYNC_ACCESS"); 341 } 342 343 if (SYNC_ACCESS) { 344 for (Iterator<SourceLineAnnotation> j = stats.syncAccessIterator(); j.hasNext();) { 347 SourceLineAnnotation accessSourceLine = j.next(); 348 bugInstance.addSourceLine(accessSourceLine).describe("SOURCE_LINE_SYNC_ACCESS"); 349 } 350 } 351 352 if (EVAL) { 353 bugInstance.addInt(biasedLocked).describe("INT_BIASED_LOCKED"); 354 bugInstance.addInt(biasedUnlocked).describe("INT_BIASED_UNLOCKED"); 355 } 356 357 bugReporter.reportBug(bugInstance); 358 } 359 } 360 } 361 362 365 366 private static boolean isConstructor(String methodName) { 367 return methodName.equals("<init>") 368 || methodName.equals("<clinit>") 369 || methodName.equals("readObject") 370 || methodName.equals("clone") 371 || methodName.equals("close") 372 || methodName.equals("writeObject") 373 || methodName.equals("toString") 374 || methodName.equals("init") 375 || methodName.equals("initialize") 376 || methodName.equals("dispose") 377 || methodName.equals("finalize") 378 || methodName.equals("this"); 379 } 380 381 private void analyzeMethod(ClassContext classContext, Method method, Set<Method> lockedMethodSet) 382 throws CFGBuilderException, DataflowAnalysisException { 383 384 InnerClassAccessMap icam = AnalysisContext.currentAnalysisContext().getInnerClassAccessMap(); 385 ConstantPoolGen cpg = classContext.getConstantPoolGen(); 386 MethodGen methodGen = classContext.getMethodGen(method); 387 if (methodGen == null) return; 388 CFG cfg = classContext.getCFG(method); 389 LockChecker lockChecker = classContext.getLockChecker(method); 390 ValueNumberDataflow vnaDataflow = classContext.getValueNumberDataflow(method); 391 boolean isGetterMethod = isGetterMethod(classContext, method); 392 393 if (DEBUG) 394 System.out.println("**** Analyzing method " + 395 SignatureConverter.convertMethodSignature(methodGen)); 396 397 for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) { 398 Location location = i.next(); 399 try { 400 Instruction ins = location.getHandle().getInstruction(); 401 XField xfield = null; 402 boolean isWrite = false; 403 boolean isLocal = false; 404 405 if (ins instanceof FieldInstruction) { 406 FieldInstruction fins = (FieldInstruction) ins; 407 xfield = Hierarchy.findXField(fins, cpg); 408 isWrite = ins.getOpcode() == Constants.PUTFIELD; 409 isLocal = fins.getClassName(cpg).equals(classContext.getJavaClass().getClassName()); 410 if (DEBUG) 411 System.out.println("Handling field access: " + location.getHandle() + 412 " (frame=" + vnaDataflow.getFactAtLocation(location) + ")"); 413 } else if (ins instanceof INVOKESTATIC) { 414 INVOKESTATIC inv = (INVOKESTATIC) ins; 415 InnerClassAccess access = icam.getInnerClassAccess(inv, cpg); 416 if (access != null && access.getMethodSignature().equals(inv.getSignature(cpg))) { 417 xfield = access.getField(); 418 isWrite = !access.isLoad(); 419 isLocal = false; 420 if (DEBUG) 421 System.out.println("Handling inner class access: " + location.getHandle() + 422 " (frame=" + vnaDataflow.getFactAtLocation(location) + ")"); 423 } 424 } 425 426 if (xfield == null) 427 continue; 428 429 if (xfield.isStatic() || xfield.isPublic() || xfield.isVolatile() || xfield.isFinal()) 431 continue; 432 433 ValueNumberFrame frame = vnaDataflow.getFactAtLocation(location); 436 if (!frame.isValid()) 437 continue; 438 439 ValueNumber thisValue = !method.isStatic() ? vnaDataflow.getAnalysis().getThisValue() : null; 441 LockSet lockSet = lockChecker.getFactAtLocation(location); 442 InstructionHandle handle = location.getHandle(); 443 ValueNumber instance = frame.getInstance(handle.getInstruction(), cpg); 444 if (DEBUG) { 445 System.out.println("Lock set: " + lockSet); 446 System.out.println("value number: " + instance.getNumber()); 447 System.out.println("Lock count: " + lockSet.getLockCount(instance.getNumber())); 448 } 449 450 451 boolean isExplicitlyLocked = lockSet.getLockCount(instance.getNumber()) > 0; 460 boolean isAccessedThroughThis = thisValue != null && thisValue.equals(instance); 461 boolean isLocked = isExplicitlyLocked 462 || (lockedMethodSet.contains(method) && isAccessedThroughThis) 463 || lockSet.containsReturnValue(vnaDataflow.getAnalysis().getFactory()); 464 465 if (ADJUST_SUBCLASS_ACCESSES) { 471 TypeDataflow typeDataflow = classContext.getTypeDataflow(method); 473 TypeFrame typeFrame = typeDataflow.getFactAtLocation(location); 474 if (!typeFrame.isValid()) continue; 475 Type instanceType = typeFrame.getInstance(handle.getInstruction(), cpg); 476 if (instanceType instanceof TopType) { 477 if (DEBUG) System.out.println("Freaky: typeFrame is " + typeFrame); 478 continue; 479 } 480 if (instanceType != TypeFrame.getNullType()) { 483 if (!(instanceType instanceof ObjectType)) { 484 throw new DataflowAnalysisException("Field accessed through non-object reference " + instanceType, 485 methodGen, handle); 486 } 487 ObjectType objType = (ObjectType) instanceType; 488 489 String instanceClassName = objType.getClassName(); 492 if (!instanceClassName.equals(xfield.getClassName())) { 493 xfield = new InstanceField(instanceClassName, 494 xfield.getName(), 495 xfield.getSignature(), 496 xfield.getAccessFlags()); 497 } 498 } 499 } 500 501 int kind = 0; 502 kind |= isLocked ? LOCKED : UNLOCKED; 503 kind |= isWrite ? WRITE : READ; 504 505 if (isLocked || !isConstructor(method.getName())) { 506 if (DEBUG) 507 System.out.println("IS2:\t" + 508 SignatureConverter.convertMethodSignature(methodGen) + 509 "\t" + xfield + "\t" + ((isWrite ? "W" : "R") + "/" + (isLocked ? "L" : "U"))); 510 511 FieldStats stats = getStats(xfield); 512 stats.addAccess(kind); 513 514 if (isExplicitlyLocked && isLocal) 515 stats.addLocalLock(); 516 517 if (isGetterMethod && !isLocked) 518 stats.addGetterMethodAccess(); 519 520 stats.addAccess(classContext, method, handle, isLocked); 521 } 522 } catch (ClassNotFoundException e) { 523 bugReporter.reportMissingClass(e); 524 } 525 } 526 } 527 528 536 public static boolean isGetterMethod(ClassContext classContext, Method method) { 537 MethodGen methodGen = classContext.getMethodGen(method); 538 if (methodGen == null) return false; 539 InstructionList il = methodGen.getInstructionList(); 540 if (il.getLength() > 60) 542 return false; 543 544 int count = 0; 545 Iterator<InstructionHandle> it = il.iterator(); 546 while (it.hasNext()) { 547 InstructionHandle ih = it.next(); 548 switch (ih.getInstruction().getOpcode()) { 549 case Constants.GETFIELD: 550 count++; 551 if (count > 1) return false; 552 break; 553 case Constants.PUTFIELD: 554 case Constants.BALOAD: 555 case Constants.CALOAD: 556 case Constants.DALOAD: 557 case Constants.FALOAD: 558 case Constants.IALOAD: 559 case Constants.LALOAD: 560 case Constants.SALOAD: 561 case Constants.AALOAD: 562 case Constants.BASTORE: 563 case Constants.CASTORE: 564 case Constants.DASTORE: 565 case Constants.FASTORE: 566 case Constants.IASTORE: 567 case Constants.LASTORE: 568 case Constants.SASTORE: 569 case Constants.AASTORE: 570 case Constants.PUTSTATIC: 571 return false; 572 case Constants.INVOKESTATIC: 573 case Constants.INVOKEVIRTUAL: 574 case Constants.INVOKEINTERFACE: 575 case Constants.INVOKESPECIAL: 576 case Constants.GETSTATIC: 577 579 } 580 } 581 return true; 583 } 584 585 588 private FieldStats getStats(XField field) { 589 FieldStats stats = statMap.get(field); 590 if (stats == null) { 591 stats = new FieldStats(); 592 statMap.put(field, stats); 593 } 594 return stats; 595 } 596 597 602 private Set<Method> findNotUnlockedMethods(ClassContext classContext, SelfCalls selfCalls, 603 Set<CallSite> obviouslyLockedSites) 604 throws CFGBuilderException, DataflowAnalysisException { 605 606 JavaClass javaClass = classContext.getJavaClass(); 607 Method[] methodList = javaClass.getMethods(); 608 609 CallGraph callGraph = selfCalls.getCallGraph(); 610 611 Set<Method> lockedMethodSet = new HashSet<Method>(); 614 lockedMethodSet.addAll(Arrays.asList(methodList)); 615 616 for (Method method : methodList) { 619 if (method.isPublic() 620 && !isConstructor(method.getName())) { 621 lockedMethodSet.remove(method); 622 } 623 } 624 625 boolean change; 628 do { 629 change = false; 630 631 for (Iterator<CallGraphEdge> i = callGraph.edgeIterator(); i.hasNext();) { 632 CallGraphEdge edge = i.next(); 633 CallSite callSite = edge.getCallSite(); 634 635 if (obviouslyLockedSites.contains(callSite)) 637 continue; 638 639 if (lockedMethodSet.contains(callSite.getMethod())) 641 continue; 642 643 CallGraphNode target = edge.getTarget(); 646 if (lockedMethodSet.remove(target.getMethod())) 647 change = true; 648 } 649 } while (change); 650 651 if (DEBUG) { 652 System.out.println("Apparently not unlocked methods:"); 653 for (Method method : lockedMethodSet) { 654 System.out.println("\t" + method.getName()); 655 } 656 } 657 658 return lockedMethodSet; 661 } 662 663 668 private Set<Method> findLockedMethods(ClassContext classContext, SelfCalls selfCalls, 669 Set<CallSite> obviouslyLockedSites) 670 throws CFGBuilderException, DataflowAnalysisException { 671 672 JavaClass javaClass = classContext.getJavaClass(); 673 Method[] methodList = javaClass.getMethods(); 674 675 CallGraph callGraph = selfCalls.getCallGraph(); 676 677 Set<Method> lockedMethodSet = new HashSet<Method>(); 679 680 for (Method method : methodList) { 682 if (method.isSynchronized()) { 683 lockedMethodSet.add(method); 684 } 685 } 686 687 boolean change; 690 do { 691 change = false; 692 693 for (Iterator<CallGraphEdge> i = callGraph.edgeIterator(); i.hasNext();) { 694 CallGraphEdge edge = i.next(); 695 CallSite callSite = edge.getCallSite(); 696 697 if (obviouslyLockedSites.contains(callSite) 700 || lockedMethodSet.contains(callSite.getMethod())) { 701 CallGraphNode target = edge.getTarget(); 704 if (lockedMethodSet.add(target.getMethod())) 705 change = true; 706 } 707 } 708 } while (change); 709 710 if (DEBUG) { 711 System.out.println("Apparently locked methods:"); 712 for (Method method : lockedMethodSet) { 713 System.out.println("\t" + method.getName()); 714 } 715 } 716 717 return lockedMethodSet; 720 } 721 722 726 private Set<Method> findPublicReachableMethods(ClassContext classContext, SelfCalls selfCalls) 727 throws CFGBuilderException, DataflowAnalysisException { 728 729 JavaClass javaClass = classContext.getJavaClass(); 730 Method[] methodList = javaClass.getMethods(); 731 732 CallGraph callGraph = selfCalls.getCallGraph(); 733 734 Set<Method> publicReachableMethodSet = new HashSet<Method>(); 736 737 for (Method method : methodList) { 739 if (method.isPublic() 740 && !isConstructor(method.getName())) { 741 publicReachableMethodSet.add(method); 742 } 743 } 744 745 boolean change; 748 do { 749 change = false; 750 751 for (Iterator<CallGraphEdge> i = callGraph.edgeIterator(); i.hasNext();) { 752 CallGraphEdge edge = i.next(); 753 CallSite callSite = edge.getCallSite(); 754 755 if (publicReachableMethodSet.contains(callSite.getMethod())) { 758 CallGraphNode target = edge.getTarget(); 761 if (publicReachableMethodSet.add(target.getMethod())) 762 change = true; 763 } 764 } 765 } while (change); 766 767 if (DEBUG) { 768 System.out.println("Methods apparently reachable from public non-constructor methods:"); 769 for (Method method : publicReachableMethodSet) { 770 System.out.println("\t" + method.getName()); 771 } 772 } 773 774 return publicReachableMethodSet; 775 } 776 777 780 private Set<CallSite> findObviouslyLockedCallSites(ClassContext classContext, SelfCalls selfCalls) 781 throws CFGBuilderException, DataflowAnalysisException { 782 ConstantPoolGen cpg = classContext.getConstantPoolGen(); 783 784 Set<CallSite> obviouslyLockedSites = new HashSet<CallSite>(); 786 for (Iterator<CallSite> i = selfCalls.callSiteIterator(); i.hasNext();) { 787 CallSite callSite = i.next(); 788 Method method = callSite.getMethod(); 789 Location location = callSite.getLocation(); 790 InstructionHandle handle = location.getHandle(); 791 792 Instruction ins = handle.getInstruction(); 795 if (ins.getOpcode() == Constants.INVOKESTATIC) 796 continue; 797 798 LockChecker lockChecker = classContext.getLockChecker(method); 800 LockSet lockSet = lockChecker.getFactAtLocation(location); 801 802 ValueNumberDataflow vnaDataflow = classContext.getValueNumberDataflow(method); 804 ValueNumberFrame frame = vnaDataflow.getFactAtLocation(location); 805 806 if (!frame.isValid()) 810 continue; 811 812 int numConsumed = ins.consumeStack(cpg); 814 MethodGen methodGen = classContext.getMethodGen(method); 815 assert methodGen != null; 816 if (numConsumed == Constants.UNPREDICTABLE) 817 throw new DataflowAnalysisException( 818 "Unpredictable stack consumption", 819 methodGen, 820 handle); 821 ValueNumber instance = frame.getStackValue(numConsumed - 1); 823 824 int lockCount = lockSet.getLockCount(instance.getNumber()); 826 if (lockCount > 0) { 827 obviouslyLockedSites.add(callSite); 829 } 830 } 831 832 return obviouslyLockedSites; 833 } 834 } 835 836 | Popular Tags |