1 19 20 package edu.umd.cs.findbugs.ba.vna; 21 22 import java.util.HashMap ; 23 import java.util.IdentityHashMap ; 24 25 import org.apache.bcel.Constants; 26 import org.apache.bcel.classfile.ConstantClass; 27 import org.apache.bcel.generic.AALOAD; 28 import org.apache.bcel.generic.ArrayInstruction; 29 import org.apache.bcel.generic.CHECKCAST; 30 import org.apache.bcel.generic.ConstantPoolGen; 31 import org.apache.bcel.generic.GETFIELD; 32 import org.apache.bcel.generic.GETSTATIC; 33 import org.apache.bcel.generic.IINC; 34 import org.apache.bcel.generic.INVOKEINTERFACE; 35 import org.apache.bcel.generic.INVOKESPECIAL; 36 import org.apache.bcel.generic.INVOKESTATIC; 37 import org.apache.bcel.generic.INVOKEVIRTUAL; 38 import org.apache.bcel.generic.Instruction; 39 import org.apache.bcel.generic.InstructionHandle; 40 import org.apache.bcel.generic.InvokeInstruction; 41 import org.apache.bcel.generic.LDC; 42 import org.apache.bcel.generic.MONITORENTER; 43 import org.apache.bcel.generic.MethodGen; 44 import org.apache.bcel.generic.PUTFIELD; 45 import org.apache.bcel.generic.PUTSTATIC; 46 47 import edu.umd.cs.findbugs.ba.AbstractFrameModelingVisitor; 48 import edu.umd.cs.findbugs.ba.DataflowAnalysisException; 49 import edu.umd.cs.findbugs.ba.Debug; 50 import edu.umd.cs.findbugs.ba.Frame; 51 import edu.umd.cs.findbugs.ba.Hierarchy; 52 import edu.umd.cs.findbugs.ba.InstanceField; 53 import edu.umd.cs.findbugs.ba.InvalidBytecodeException; 54 import edu.umd.cs.findbugs.ba.RepositoryLookupFailureCallback; 55 import edu.umd.cs.findbugs.ba.StaticField; 56 import edu.umd.cs.findbugs.ba.XField; 57 58 67 public class ValueNumberFrameModelingVisitor 68 extends AbstractFrameModelingVisitor<ValueNumber, ValueNumberFrame> 69 implements Debug, ValueNumberAnalysisFeatures { 70 71 74 75 private MethodGen methodGen; 76 private ValueNumberFactory factory; 77 private ValueNumberCache cache; 78 private LoadedFieldSet loadedFieldSet; 79 private HashMap <String , ValueNumber> classObjectValueMap; 80 private HashMap <Object , ValueNumber> constantValueMap; 81 private HashMap <ValueNumber, String > stringConstantMap; 82 private RepositoryLookupFailureCallback lookupFailureCallback; 83 private InstructionHandle handle; 84 85 88 89 98 public ValueNumberFrameModelingVisitor(MethodGen methodGen, ValueNumberFactory factory, 99 ValueNumberCache cache, 100 LoadedFieldSet loadedFieldSet, 101 RepositoryLookupFailureCallback lookupFailureCallback) { 102 103 super(methodGen.getConstantPool()); 104 this.methodGen = methodGen; 105 this.factory = factory; 106 this.cache = cache; 107 this.loadedFieldSet = loadedFieldSet; 108 this.classObjectValueMap = new HashMap <String , ValueNumber>(); 109 this.constantValueMap = new HashMap <Object , ValueNumber>(); 110 this.stringConstantMap = new HashMap <ValueNumber, String >(); 111 this.lookupFailureCallback = lookupFailureCallback; 112 } 113 114 @Override 115 public ValueNumber getDefaultValue() { 116 return factory.createFreshValue(); 117 } 118 119 123 public void setHandle(InstructionHandle handle) { 124 this.handle = handle; 125 } 126 127 130 131 139 private boolean doRedundantLoadElimination() { 140 if (!REDUNDANT_LOAD_ELIMINATION) 141 return false; 142 143 XField xfield = loadedFieldSet.getField(handle); 144 if (xfield == null) 145 return false; 146 147 if (!xfield.isReferenceType()) 148 return false; 149 150 if (false && loadedFieldSet.getLoadStoreCount(xfield).getLoadCount() <= 1) 153 return false; 154 155 return true; 156 } 157 158 166 private boolean doForwardSubstitution() { 167 if (!REDUNDANT_LOAD_ELIMINATION) 168 return false; 169 170 XField xfield = loadedFieldSet.getField(handle); 171 if (xfield == null) 172 return false; 173 174 if (!xfield.isReferenceType()) 175 return false; 176 177 if (!loadedFieldSet.isLoaded(xfield)) 180 return false; 181 182 return true; 183 } 184 185 private void checkConsumedAndProducedValues(Instruction ins, ValueNumber[] consumedValueList, 186 ValueNumber[] producedValueList) { 187 int numConsumed = ins.consumeStack(getCPG()); 188 int numProduced = ins.produceStack(getCPG()); 189 190 if (numConsumed == Constants.UNPREDICTABLE) 191 throw new InvalidBytecodeException("Unpredictable stack consumption for " + ins); 192 if (numProduced == Constants.UNPREDICTABLE) 193 throw new InvalidBytecodeException("Unpredictable stack production for " + ins); 194 195 if (consumedValueList.length != numConsumed) { 196 throw new IllegalStateException ("Wrong number of values consumed for " + ins + 197 ": expected " + numConsumed + ", got " + consumedValueList.length); 198 } 199 200 if (producedValueList.length != numProduced) { 201 throw new IllegalStateException ("Wrong number of values produced for " + ins + 202 ": expected " + numProduced + ", got " + producedValueList.length); 203 } 204 } 205 206 209 @Override 210 public void modelNormalInstruction(Instruction ins, int numWordsConsumed, int numWordsProduced) { 211 212 int flags = 0; 213 if (ins instanceof InvokeInstruction) 214 flags = ValueNumber.RETURN_VALUE; 215 else if (ins instanceof ArrayInstruction) flags = ValueNumber.ARRAY_VALUE; 216 217 218 ValueNumber[] inputValueList = popInputValues(numWordsConsumed); 220 221 ValueNumber[] outputValueList = getOutputValues(inputValueList, numWordsProduced, flags); 225 226 if (VERIFY_INTEGRITY) { 227 checkConsumedAndProducedValues(ins, inputValueList, outputValueList); 228 } 229 230 pushOutputValues(outputValueList); 232 } 233 234 @Override 235 public void visitGETFIELD(GETFIELD obj) { 236 if (doRedundantLoadElimination()) { 237 238 try { 239 XField xfield = Hierarchy.findXField(obj, getCPG()); 240 if (xfield != null) { 241 loadInstanceField((InstanceField) xfield, obj); 242 return; 243 } 244 } catch (ClassNotFoundException e) { 245 lookupFailureCallback.reportMissingClass(e); 246 } 247 } 248 handleNormalInstruction(obj); 249 } 250 251 @Override 252 public void visitPUTFIELD(PUTFIELD obj) { 253 if (doForwardSubstitution()) { 254 try { 255 XField xfield = Hierarchy.findXField(obj, getCPG()); 256 if (xfield != null) { 257 storeInstanceField((InstanceField) xfield, obj, false); 258 return; 259 } 260 } catch (ClassNotFoundException e) { 261 lookupFailureCallback.reportMissingClass(e); 262 } 263 } 264 handleNormalInstruction(obj); 265 } 266 267 private static final ValueNumber[] EMPTY_INPUT_VALUE_LIST = new ValueNumber[0]; 268 269 @Override 270 public void visitGETSTATIC(GETSTATIC obj) { 271 ConstantPoolGen cpg = getCPG(); 272 273 String fieldName = obj.getName(cpg); 274 String fieldSig = obj.getSignature(cpg); 275 ValueNumberFrame frame = getFrame(); 276 277 if (RLE_DEBUG) { 278 System.out.println("GETSTATIC of " + fieldName + " : " + fieldSig); 279 } 280 if (fieldName.startsWith("class$") && fieldSig.equals("Ljava/lang/Class;")) { 282 String className = fieldName.substring("class$".length()).replace('$', '.'); 283 if (RLE_DEBUG) System.out.println("[found load of class object " + className + "]"); 284 ValueNumber value = getClassObjectValue(className); 285 frame.pushValue(value); 286 return; 287 } 288 if (doRedundantLoadElimination()) { 289 try { 290 XField xfield = Hierarchy.findXField(obj, getCPG()); 291 if (xfield != null) { 292 loadStaticField((StaticField) xfield, obj); 293 return; 294 } 295 } catch (ClassNotFoundException e) { 296 lookupFailureCallback.reportMissingClass(e); 297 } 298 } 299 300 handleNormalInstruction(obj); 301 } 302 303 @Override 304 public void visitPUTSTATIC(PUTSTATIC obj) { 305 if (doForwardSubstitution()) { 306 try { 307 XField xfield = Hierarchy.findXField(obj, getCPG()); 308 if (xfield != null) { 309 storeStaticField((StaticField) xfield, obj, false); 310 return; 311 } 312 } catch (ClassNotFoundException e) { 313 lookupFailureCallback.reportMissingClass(e); 314 } 315 } 316 handleNormalInstruction(obj); 317 } 318 319 @Override 320 public void visitINVOKESTATIC(INVOKESTATIC obj) { 321 if (REDUNDANT_LOAD_ELIMINATION) { 322 ConstantPoolGen cpg = getCPG(); 323 String targetClassName = obj.getClassName(cpg); 324 String methodName = obj.getName(cpg); 325 String methodSig = obj.getSignature(cpg); 326 327 if ((methodName.equals("forName") && targetClassName.equals("java.lang.Class") || methodName.equals("class$")) && methodSig.equals("(Ljava/lang/String;)Ljava/lang/Class;")) { 328 ValueNumberFrame frame = getFrame(); 330 try { 331 ValueNumber arg = frame.getTopValue(); 332 String className = stringConstantMap.get(arg); 333 if (className != null) { 334 frame.popValue(); 335 if (RLE_DEBUG) System.out.println("[found access of class object " + className + "]"); 336 frame.pushValue(getClassObjectValue(className)); 337 return; 338 } 339 } catch (DataflowAnalysisException e) { 340 throw new InvalidBytecodeException("stack underflow", methodGen, handle, e); 341 } 342 } else if (Hierarchy.isInnerClassAccess(obj, cpg)) { 343 XField xfield = loadedFieldSet.getField(handle); 345 if (xfield != null) { 346 if (loadedFieldSet.instructionIsLoad(handle)) { 347 if (doRedundantLoadElimination()) { 349 if (xfield.isStatic()) 350 loadStaticField((StaticField) xfield, obj); 351 else 352 loadInstanceField((InstanceField) xfield, obj); 353 return; 354 } 355 } else { 356 if (doForwardSubstitution()) { 358 boolean pushValue = !methodSig.endsWith(")V"); 361 362 if (xfield.isStatic()) 363 storeStaticField((StaticField) xfield, obj, pushValue); 364 else 365 storeInstanceField((InstanceField) xfield, obj, pushValue); 366 367 return; 368 } 369 } 370 } else { 371 killLoadsOfObjectsPassed(obj); 374 getFrame().killAllLoadsOf(null); 375 } 376 } else { 377 killLoadsOfObjectsPassed(obj); 380 getFrame().killAllLoadsOf(null); 381 } 382 } 383 384 handleNormalInstruction(obj); 385 } 386 387 private void killLoadsOfObjectsPassed(Instruction ins) { 388 int passed = getNumWordsConsumed(ins); 389 ValueNumber [] arguments = new ValueNumber[passed]; 390 try { 391 getFrame().getTopStackWords(arguments); 392 for(ValueNumber v : arguments) 393 getFrame().killAllLoadsOf(v); 394 395 } catch (DataflowAnalysisException e) { 396 e.printStackTrace(); 398 } 399 } 400 @Override 401 public void visitMONITORENTER(MONITORENTER obj) { 402 getFrame().killAllLoads(); 405 handleNormalInstruction(obj); 406 } 407 408 @Override 409 public void visitINVOKESPECIAL(INVOKESPECIAL obj) { 410 killLoadsOfObjectsPassed(obj); 413 handleNormalInstruction(obj); 414 } 415 416 @Override 417 public void visitINVOKEINTERFACE(INVOKEINTERFACE obj) { 418 if (obj.getMethodName(cpg).equals("lock")) 421 getFrame().killAllLoads(); 422 else killLoadsOfObjectsPassed(obj); 423 handleNormalInstruction(obj); 424 } 425 426 @Override 427 public void visitINVOKEVIRTUAL(INVOKEVIRTUAL obj) { 428 if (obj.getMethodName(cpg).equals("lock")) 431 getFrame().killAllLoads(); 432 else killLoadsOfObjectsPassed(obj); 433 handleNormalInstruction(obj); 434 } 435 436 @Override 437 public void visitLDC(LDC obj) { 438 Object constantValue = obj.getValue(cpg); 439 ValueNumber value; 440 if (constantValue instanceof ConstantClass) { 441 ConstantClass constantClass = (ConstantClass) constantValue; 442 String className = constantClass.getBytes(cpg.getConstantPool()); 443 value = getClassObjectValue(className); 444 } else { 445 value = constantValueMap.get(constantValue); 446 if (value == null) { 447 value = factory.createFreshValue(); 448 constantValueMap.put(constantValue, value); 449 450 452 if (constantValue instanceof String ) { 453 stringConstantMap.put(value, (String ) constantValue); 454 } 455 } 456 } 457 getFrame().pushValue(value); 458 } 459 460 @Override 461 public void visitIINC(IINC obj) { 462 if (obj.getIncrement() == 0) { 463 return; 465 } 466 467 472 int local = obj.getIndex(); 473 474 ValueNumber[] input = new ValueNumber[]{ getFrame().getValue(local) }; 475 ValueNumberCache.Entry entry = new ValueNumberCache.Entry(handle, input); 476 ValueNumber[] output = cache.lookupOutputValues(entry); 477 if (output == null) { 478 output = new ValueNumber[]{ factory.createFreshValue() }; 479 cache.addOutputValues(entry, output); 480 } 481 482 getFrame().setValue(local, output[0]); 483 } 484 485 @Override 486 public void visitCHECKCAST(CHECKCAST obj) { 487 } 489 490 493 494 498 private ValueNumber[] popInputValues(int numWordsConsumed) { 499 ValueNumberFrame frame = getFrame(); 500 ValueNumber[] inputValueList = new ValueNumber[numWordsConsumed]; 501 502 try { 504 frame.getTopStackWords(inputValueList); 505 while (numWordsConsumed-- > 0) { 506 frame.popValue(); 507 } 508 } catch (DataflowAnalysisException e) { 509 throw new InvalidBytecodeException("Error getting input operands", e); 510 } 511 512 return inputValueList; 513 } 514 515 518 private void pushOutputValues(ValueNumber[] outputValueList) { 519 ValueNumberFrame frame = getFrame(); 520 for (ValueNumber aOutputValueList : outputValueList) 521 frame.pushValue(aOutputValueList); 522 } 523 524 527 private ValueNumber[] getOutputValues(ValueNumber[] inputValueList, int numWordsProduced) { 528 return getOutputValues(inputValueList, numWordsProduced, 0); 529 } 530 531 private ValueNumber[] getOutputValues(ValueNumber[] inputValueList, int numWordsProduced, int flags) { 532 ValueNumberCache.Entry entry = new ValueNumberCache.Entry(handle, inputValueList); 533 ValueNumber[] outputValueList = cache.lookupOutputValues(entry); 534 if (outputValueList == null) { 535 outputValueList = new ValueNumber[numWordsProduced]; 536 for (int i = 0; i < numWordsProduced; ++i) { 537 ValueNumber freshValue = factory.createFreshValue(); 538 freshValue.setFlags(flags); 539 outputValueList[i] = freshValue; 540 } 541 if (false && RLE_DEBUG) { 542 System.out.println("<<cache fill for " + handle.getPosition() + ": " + 543 vlts(inputValueList) + " ==> " + vlts(outputValueList) + ">>"); 544 } 545 cache.addOutputValues(entry, outputValueList); 546 } else if (false && RLE_DEBUG) { 547 System.out.println("<<cache hit for " + handle.getPosition() + ": " + 548 vlts(inputValueList) + " ==> " + vlts(outputValueList) + ">>"); 549 } 550 return outputValueList; 551 } 552 553 private static String vlts(ValueNumber[] vl) { 554 StringBuffer buf = new StringBuffer (); 555 for (ValueNumber aVl : vl) { 556 if (buf.length() > 0) buf.append(','); 557 buf.append(aVl.getNumber()); 558 } 559 return buf.toString(); 560 } 561 562 568 private void loadInstanceField(InstanceField instanceField, Instruction obj) { 569 if (RLE_DEBUG) { 570 System.out.println("[loadInstanceField for field " + instanceField + " in instruction " + handle); 571 } 572 573 ValueNumberFrame frame = getFrame(); 574 575 try { 576 ValueNumber reference = frame.popValue(); 577 578 AvailableLoad availableLoad = new AvailableLoad(reference, instanceField); 579 if (RLE_DEBUG) System.out.println("[getfield of " + availableLoad + "]"); 580 ValueNumber[] loadedValue = frame.getAvailableLoad(availableLoad); 581 582 if (loadedValue == null) { 583 ValueNumber[] inputValueList = new ValueNumber[]{reference}; 585 loadedValue = getOutputValues(inputValueList, getNumWordsProduced(obj)); 586 587 frame.addAvailableLoad(availableLoad, loadedValue); 589 if (RLE_DEBUG) { 590 System.out.println("[Making load available " + 591 availableLoad + " <- " + 592 vlts(loadedValue) + "]"); 593 } 594 } else { 595 if (RLE_DEBUG) { 597 System.out.println("[Found available load " + 598 availableLoad + " <- " + vlts(loadedValue) + "]"); 599 } 600 } 601 602 pushOutputValues(loadedValue); 603 604 if (VERIFY_INTEGRITY) { 605 checkConsumedAndProducedValues(obj, new ValueNumber[]{reference}, loadedValue); 606 } 607 } catch (DataflowAnalysisException e) { 608 throw new InvalidBytecodeException("Error loading from instance field", e); 609 } 610 } 611 612 618 private void loadStaticField(StaticField staticField, Instruction obj) { 619 if (RLE_DEBUG) { 620 System.out.println("[loadStaticField for field " + staticField + " in instruction " + handle); 621 } 622 623 ValueNumberFrame frame = getFrame(); 624 625 AvailableLoad availableLoad = new AvailableLoad(staticField); 626 ValueNumber[] loadedValue = frame.getAvailableLoad(availableLoad); 627 628 if (loadedValue == null) { 629 int numWordsProduced = getNumWordsProduced(obj); 631 loadedValue = getOutputValues(EMPTY_INPUT_VALUE_LIST, numWordsProduced); 632 633 frame.addAvailableLoad(availableLoad, loadedValue); 634 635 if (RLE_DEBUG) System.out.println("[making load of " + staticField + " available]"); 636 } else { 637 if (RLE_DEBUG) System.out.println("[found available load of " + staticField + "]"); 638 } 639 640 if (VERIFY_INTEGRITY) { 641 checkConsumedAndProducedValues(obj, new ValueNumber[0], loadedValue); 642 } 643 644 pushOutputValues(loadedValue); 645 } 646 647 655 private void storeInstanceField(InstanceField instanceField, Instruction obj, boolean pushStoredValue) { 656 if (RLE_DEBUG) { 657 System.out.println("[storeInstanceField for field " + instanceField + " in instruction " + handle); 658 } 659 660 ValueNumberFrame frame = getFrame(); 661 662 int numWordsConsumed = getNumWordsConsumed(obj); 663 667 ValueNumber[] inputValueList = popInputValues(numWordsConsumed); 668 ValueNumber reference = inputValueList[0]; 669 ValueNumber[] storedValue = new ValueNumber[inputValueList.length - 1]; 670 System.arraycopy(inputValueList, 1, storedValue, 0, inputValueList.length - 1); 671 672 if (pushStoredValue) 673 pushOutputValues(storedValue); 674 675 frame.killLoadsOfField(instanceField); 678 679 frame.addAvailableLoad(new AvailableLoad(reference, instanceField), storedValue); 681 682 if (RLE_DEBUG) System.out.println("[making store of " + instanceField + " available]"); 683 684 if (VERIFY_INTEGRITY) { 685 688 checkConsumedAndProducedValues(obj, inputValueList, 689 pushStoredValue ? storedValue : new ValueNumber[0]); 690 } 691 } 692 693 701 private void storeStaticField(StaticField staticField, Instruction obj, boolean pushStoredValue) { 702 if (RLE_DEBUG) { 703 System.out.println("[storeStaticField for field " + staticField + " in instruction " + handle); 704 } 705 706 ValueNumberFrame frame = getFrame(); 707 708 AvailableLoad availableLoad = new AvailableLoad(staticField); 709 710 int numWordsConsumed = getNumWordsConsumed(obj); 711 ValueNumber[] inputValueList = popInputValues(numWordsConsumed); 712 713 if (pushStoredValue) 714 pushOutputValues(inputValueList); 715 716 frame.killLoadsOfField(staticField); 718 719 frame.addAvailableLoad(availableLoad, inputValueList); 721 722 if (RLE_DEBUG) System.out.println("[making store of " + staticField + " available]"); 723 724 if (VERIFY_INTEGRITY) { 725 checkConsumedAndProducedValues(obj, inputValueList, 726 pushStoredValue ? inputValueList : new ValueNumber[0]); 727 } 728 } 729 730 735 public ValueNumber getClassObjectValue(String className) { 736 className = className.replace('/','.'); 738 ValueNumber value = classObjectValueMap.get(className); 739 if (value == null) { 740 value = factory.createFreshValue(); 741 value.setFlag(ValueNumber.CONSTANT_CLASS_OBJECT); 742 classObjectValueMap.put(className, value); 743 } 744 return value; 745 } 746 747 } 748 749 | Popular Tags |