1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21package proguard.optimize.evaluation; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.*; 25import proguard.classfile.attribute.visitor.*; 26import proguard.classfile.constant.ClassConstant; 27import proguard.classfile.instruction.*; 28import proguard.classfile.util.*; 29import proguard.classfile.visitor.*; 30import proguard.evaluation.*; 31import proguard.evaluation.value.*; 32import proguard.optimize.peephole.BranchTargetFinder; 33 34import java.util.Arrays; 35 36/** 37 * This AttributeVisitor performs partial evaluation on the code attributes 38 * that it visits. 39 * 40 * @author Eric Lafortune 41 */ 42public class PartialEvaluator 43extends SimplifiedVisitor 44implements AttributeVisitor, 45 ExceptionInfoVisitor 46{ 47 //* 48 private static final boolean DEBUG = false; 49 private static final boolean DEBUG_RESULTS = false; 50 /*/ 51 private static boolean DEBUG = true; 52 private static boolean DEBUG_RESULTS = true; 53 //*/ 54 55 private static final int MAXIMUM_EVALUATION_COUNT = 5; 56 57 public static final int NONE = -2; 58 public static final int AT_METHOD_ENTRY = -1; 59 public static final int AT_CATCH_ENTRY = -1; 60 61 private final ValueFactory valueFactory; 62 private final InvocationUnit invocationUnit; 63 private final boolean evaluateAllCode; 64 65 private InstructionOffsetValue[] branchOriginValues = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH]; 66 private InstructionOffsetValue[] branchTargetValues = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH]; 67 private TracedVariables[] variablesBefore = new TracedVariables[ClassConstants.TYPICAL_CODE_LENGTH]; 68 private TracedStack[] stacksBefore = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH]; 69 private TracedVariables[] variablesAfter = new TracedVariables[ClassConstants.TYPICAL_CODE_LENGTH]; 70 private TracedStack[] stacksAfter = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH]; 71 private boolean[] generalizedContexts = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; 72 private int[] evaluationCounts = new int[ClassConstants.TYPICAL_CODE_LENGTH]; 73 private boolean evaluateExceptions; 74 75 private final BasicBranchUnit branchUnit; 76 private final BranchTargetFinder branchTargetFinder; 77 78 private final java.util.Stack callingInstructionBlockStack; 79 private final java.util.Stack instructionBlockStack = new java.util.Stack(); 80 81 82 /** 83 * Creates a simple PartialEvaluator. 84 */ 85 public PartialEvaluator() 86 { 87 this(new ValueFactory(), 88 new BasicInvocationUnit(new ValueFactory()), 89 true); 90 } 91 92 93 /** 94 * Creates a new PartialEvaluator. 95 * @param valueFactory the value factory that will create all values 96 * during evaluation. 97 * @param invocationUnit the invocation unit that will handle all 98 * communication with other fields and methods. 99 * @param evaluateAllCode a flag that specifies whether all casts, branch 100 * targets, and exception handlers should be 101 * evaluated, even if they are unnecessary or 102 * unreachable. 103 */ 104 public PartialEvaluator(ValueFactory valueFactory, 105 InvocationUnit invocationUnit, 106 boolean evaluateAllCode) 107 { 108 this(valueFactory, 109 invocationUnit, 110 evaluateAllCode, 111 evaluateAllCode ? 112 new BasicBranchUnit() : 113 new TracedBranchUnit(), 114 new BranchTargetFinder(), 115 null); 116 } 117 118 119 /** 120 * Creates a new PartialEvaluator, based on an existing one. 121 * @param partialEvaluator the subroutine calling partial evaluator. 122 */ 123 private PartialEvaluator(PartialEvaluator partialEvaluator) 124 { 125 this(partialEvaluator.valueFactory, 126 partialEvaluator.invocationUnit, 127 partialEvaluator.evaluateAllCode, 128 partialEvaluator.branchUnit, 129 partialEvaluator.branchTargetFinder, 130 partialEvaluator.instructionBlockStack); 131 } 132 133 134 /** 135 * Creates a new PartialEvaluator. 136 * @param valueFactory the value factory that will create 137 * all values during evaluation. 138 * @param invocationUnit the invocation unit that will handle 139 * all communication with other fields 140 * and methods. 141 * @param evaluateAllCode a flag that specifies whether all 142 * casts, branch targets, and exception 143 * handlers should be evaluated, even 144 * if they are unnecessary or 145 * unreachable. 146 * @param branchUnit the branch unit that will handle all 147 * branches. 148 * @param branchTargetFinder the utility class that will find all 149 * branches. 150 * @param callingInstructionBlockStack the stack of instruction blocks to 151 * be evaluated 152 */ 153 private PartialEvaluator(ValueFactory valueFactory, 154 InvocationUnit invocationUnit, 155 boolean evaluateAllCode, 156 BasicBranchUnit branchUnit, 157 BranchTargetFinder branchTargetFinder, 158 java.util.Stack callingInstructionBlockStack) 159 { 160 this.valueFactory = valueFactory; 161 this.invocationUnit = invocationUnit; 162 this.evaluateAllCode = evaluateAllCode; 163 this.branchUnit = branchUnit; 164 this.branchTargetFinder = branchTargetFinder; 165 this.callingInstructionBlockStack = callingInstructionBlockStack == null ? 166 this.instructionBlockStack : 167 callingInstructionBlockStack; 168 } 169 170 171 // Implementations for AttributeVisitor. 172 173 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 174 175 176 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 177 { 178// DEBUG = DEBUG_RESULTS = 179// clazz.getName().equals("abc/Def") && 180// method.getName(clazz).equals("abc"); 181 182 // TODO: Remove this when the partial evaluator has stabilized. 183 // Catch any unexpected exceptions from the actual visiting method. 184 try 185 { 186 // Process the code. 187 visitCodeAttribute0(clazz, method, codeAttribute); 188 } 189 catch (RuntimeException ex) 190 { 191 System.err.println("Unexpected error while performing partial evaluation:"); 192 System.err.println(" Class = ["+clazz.getName()+"]"); 193 System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); 194 System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); 195 196 if (DEBUG) 197 { 198 method.accept(clazz, new ClassPrinter()); 199 200 System.out.println("Evaluation results:"); 201 202 int offset = 0; 203 do 204 { 205 if (isBranchOrExceptionTarget(offset)) 206 { 207 System.out.println("Branch target from ["+branchOriginValues[offset]+"]:"); 208 if (isTraced(offset)) 209 { 210 System.out.println(" Vars: "+variablesBefore[offset]); 211 System.out.println(" Stack: "+stacksBefore[offset]); 212 } 213 } 214 215 Instruction instruction = InstructionFactory.create(codeAttribute.code, 216 offset); 217 System.out.println(instruction.toString(offset)); 218 219 if (isTraced(offset)) 220 { 221 int initializationOffset = branchTargetFinder.initializationOffset(offset); 222 if (initializationOffset != NONE) 223 { 224 System.out.println(" is to be initialized at ["+initializationOffset+"]"); 225 } 226 227 InstructionOffsetValue branchTargets = branchTargets(offset); 228 if (branchTargets != null) 229 { 230 System.out.println(" has overall been branching to "+branchTargets); 231 } 232 233 System.out.println(" Vars: "+variablesAfter[offset]); 234 System.out.println(" Stack: "+stacksAfter[offset]); 235 } 236 237 offset += instruction.length(offset); 238 } 239 while (offset < codeAttribute.u4codeLength); 240 } 241 242 throw ex; 243 } 244 } 245 246 247 public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) 248 { 249 // Evaluate the instructions, starting at the entry point. 250 if (DEBUG) 251 { 252 System.out.println(); 253 System.out.println("Partial evaluation: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); 254 System.out.println(" Max locals = "+codeAttribute.u2maxLocals); 255 System.out.println(" Max stack = "+codeAttribute.u2maxStack); 256 } 257 258 // Reuse the existing variables and stack objects, ensuring the right size. 259 TracedVariables variables = new TracedVariables(codeAttribute.u2maxLocals); 260 TracedStack stack = new TracedStack(codeAttribute.u2maxStack); 261 262 // Initialize the reusable arrays and variables. 263 initializeArrays(codeAttribute); 264 initializeParameters(clazz, method, codeAttribute, variables); 265 266 // Find all instruction offsets,... 267 codeAttribute.accept(clazz, method, branchTargetFinder); 268 269 // Start executing the first instruction block. 270 evaluateInstructionBlockAndExceptionHandlers(clazz, 271 method, 272 codeAttribute, 273 variables, 274 stack, 275 0, 276 codeAttribute.u4codeLength); 277 278 if (DEBUG_RESULTS) 279 { 280 System.out.println("Evaluation results:"); 281 282 int offset = 0; 283 do 284 { 285 if (isBranchOrExceptionTarget(offset)) 286 { 287 System.out.println("Branch target from ["+branchOriginValues[offset]+"]:"); 288 if (isTraced(offset)) 289 { 290 System.out.println(" Vars: "+variablesBefore[offset]); 291 System.out.println(" Stack: "+stacksBefore[offset]); 292 } 293 } 294 295 Instruction instruction = InstructionFactory.create(codeAttribute.code, 296 offset); 297 System.out.println(instruction.toString(offset)); 298 299 if (isTraced(offset)) 300 { 301 int initializationOffset = branchTargetFinder.initializationOffset(offset); 302 if (initializationOffset >= 0) 303 { 304 System.out.println(" is to be initialized at ["+initializationOffset+"]"); 305 } 306 307 InstructionOffsetValue branchTargets = branchTargets(offset); 308 if (branchTargets != null) 309 { 310 System.out.println(" has overall been branching to "+branchTargets); 311 } 312 313 System.out.println(" Vars: "+variablesAfter[offset]); 314 System.out.println(" Stack: "+stacksAfter[offset]); 315 } 316 317 offset += instruction.length(offset); 318 } 319 while (offset < codeAttribute.u4codeLength); 320 } 321 } 322 323 324 /** 325 * Returns whether a block of instructions is ever used. 326 */ 327 public boolean isTraced(int startOffset, int endOffset) 328 { 329 for (int index = startOffset; index < endOffset; index++) 330 { 331 if (isTraced(index)) 332 { 333 return true; 334 } 335 } 336 337 return false; 338 } 339 340 341 /** 342 * Returns whether the instruction at the given offset has ever been 343 * executed during the partial evaluation. 344 */ 345 public boolean isTraced(int instructionOffset) 346 { 347 return evaluationCounts[instructionOffset] > 0; 348 } 349 350 351 /** 352 * Returns whether there is an instruction at the given offset. 353 */ 354 public boolean isInstruction(int instructionOffset) 355 { 356 return branchTargetFinder.isInstruction(instructionOffset); 357 } 358 359 360 /** 361 * Returns whether the instruction at the given offset is the target of a 362 * branch instruction or an exception. 363 */ 364 public boolean isBranchOrExceptionTarget(int instructionOffset) 365 { 366 return branchTargetFinder.isBranchTarget(instructionOffset) || 367 branchTargetFinder.isExceptionHandler(instructionOffset); 368 } 369 370 371 /** 372 * Returns whether the instruction at the given offset is the start of a 373 * subroutine. 374 */ 375 public boolean isSubroutineStart(int instructionOffset) 376 { 377 return branchTargetFinder.isSubroutineStart(instructionOffset); 378 } 379 380 381 /** 382 * Returns whether the instruction at the given offset is a subroutine 383 * invocation. 384 */ 385 public boolean isSubroutineInvocation(int instructionOffset) 386 { 387 return branchTargetFinder.isSubroutineInvocation(instructionOffset); 388 } 389 390 391 /** 392 * Returns whether the instruction at the given offset is part of a 393 * subroutine. 394 */ 395 public boolean isSubroutine(int instructionOffset) 396 { 397 return branchTargetFinder.isSubroutine(instructionOffset); 398 } 399 400 401 /** 402 * Returns whether the subroutine at the given offset is ever returning 403 * by means of a regular 'ret' instruction. 404 */ 405 public boolean isSubroutineReturning(int instructionOffset) 406 { 407 return branchTargetFinder.isSubroutineReturning(instructionOffset); 408 } 409 410 411 /** 412 * Returns the offset after the subroutine that starts at the given 413 * offset. 414 */ 415 public int subroutineEnd(int instructionOffset) 416 { 417 return branchTargetFinder.subroutineEnd(instructionOffset); 418 } 419 420 421 /** 422 * Returns the instruction offset at which the object instance that is 423 * created at the given 'new' instruction offset is initialized, or 424 * <code>NONE</code> if it is not being created. 425 */ 426 public int initializationOffset(int instructionOffset) 427 { 428 return branchTargetFinder.initializationOffset(instructionOffset); 429 } 430 431 432 /** 433 * Returns whether the method is an instance initializer. 434 */ 435 public boolean isInitializer() 436 { 437 return branchTargetFinder.isInitializer(); 438 } 439 440 441 /** 442 * Returns the instruction offset at which this initializer is calling 443 * the "super" or "this" initializer method, or <code>NONE</code> if it is 444 * not an initializer. 445 */ 446 public int superInitializationOffset() 447 { 448 return branchTargetFinder.superInitializationOffset(); 449 } 450 451 452 /** 453 * Returns the offset of the 'new' instruction that corresponds to the 454 * invocation of the instance initializer at the given offset, or 455 * <code>AT_METHOD_ENTRY</code> if the invocation is calling the "super" or 456 * "this" initializer method, , or <code>NONE</code> if it is not a 'new' 457 * instruction. 458 */ 459 public int creationOffset(int offset) 460 { 461 return branchTargetFinder.creationOffset(offset); 462 } 463 464 465 /** 466 * Returns the variables before execution of the instruction at the given 467 * offset. 468 */ 469 public TracedVariables getVariablesBefore(int instructionOffset) 470 { 471 return variablesBefore[instructionOffset]; 472 } 473 474 475 /** 476 * Returns the variables after execution of the instruction at the given 477 * offset. 478 */ 479 public TracedVariables getVariablesAfter(int instructionOffset) 480 { 481 return variablesAfter[instructionOffset]; 482 } 483 484 485 /** 486 * Returns the stack before execution of the instruction at the given 487 * offset. 488 */ 489 public TracedStack getStackBefore(int instructionOffset) 490 { 491 return stacksBefore[instructionOffset]; 492 } 493 494 495 /** 496 * Returns the stack after execution of the instruction at the given 497 * offset. 498 */ 499 public TracedStack getStackAfter(int instructionOffset) 500 { 501 return stacksAfter[instructionOffset]; 502 } 503 504 505 /** 506 * Returns the instruction offsets that branch to the given instruction 507 * offset. 508 */ 509 public InstructionOffsetValue branchOrigins(int instructionOffset) 510 { 511 return branchOriginValues[instructionOffset]; 512 } 513 514 515 /** 516 * Returns the instruction offsets to which the given instruction offset 517 * branches. 518 */ 519 public InstructionOffsetValue branchTargets(int instructionOffset) 520 { 521 return branchTargetValues[instructionOffset]; 522 } 523 524 525 // Utility methods to evaluate instruction blocks. 526 527 /** 528 * Pushes block of instructions to be executed in the calling partial 529 * evaluator. 530 */ 531 private void pushCallingInstructionBlock(TracedVariables variables, 532 TracedStack stack, 533 int startOffset) 534 { 535 callingInstructionBlockStack.push(new MyInstructionBlock(variables, 536 stack, 537 startOffset)); 538 } 539 540 541 /** 542 * Pushes block of instructions to be executed in this partial evaluator. 543 */ 544 private void pushInstructionBlock(TracedVariables variables, 545 TracedStack stack, 546 int startOffset) 547 { 548 instructionBlockStack.push(new MyInstructionBlock(variables, 549 stack, 550 startOffset)); 551 } 552 553 554 /** 555 * Evaluates the instruction block and the exception handlers covering the 556 * given instruction range in the given code. 557 */ 558 private void evaluateInstructionBlockAndExceptionHandlers(Clazz clazz, 559 Method method, 560 CodeAttribute codeAttribute, 561 TracedVariables variables, 562 TracedStack stack, 563 int startOffset, 564 int endOffset) 565 { 566 evaluateInstructionBlock(clazz, 567 method, 568 codeAttribute, 569 variables, 570 stack, 571 startOffset); 572 573 evaluateExceptionHandlers(clazz, 574 method, 575 codeAttribute, 576 startOffset, 577 endOffset); 578 } 579 580 581 /** 582 * Evaluates a block of instructions, starting at the given offset and ending 583 * at a branch instruction, a return instruction, or a throw instruction. 584 */ 585 private void evaluateInstructionBlock(Clazz clazz, 586 Method method, 587 CodeAttribute codeAttribute, 588 TracedVariables variables, 589 TracedStack stack, 590 int startOffset) 591 { 592 // Execute the initial instruction block. 593 evaluateSingleInstructionBlock(clazz, 594 method, 595 codeAttribute, 596 variables, 597 stack, 598 startOffset); 599 600 // Execute all resulting instruction blocks on the execution stack. 601 while (!instructionBlockStack.empty()) 602 { 603 if (DEBUG) System.out.println("Popping alternative branch out of "+instructionBlockStack.size()+" blocks"); 604 605 MyInstructionBlock instructionBlock = 606 (MyInstructionBlock)instructionBlockStack.pop(); 607 608 evaluateSingleInstructionBlock(clazz, 609 method, 610 codeAttribute, 611 instructionBlock.variables, 612 instructionBlock.stack, 613 instructionBlock.startOffset); 614 } 615 } 616 617 618 /** 619 * Evaluates a block of instructions, starting at the given offset and ending 620 * at a branch instruction, a return instruction, or a throw instruction. 621 * Instruction blocks that are to be evaluated as a result are pushed on 622 * the given stack. 623 */ 624 private void evaluateSingleInstructionBlock(Clazz clazz, 625 Method method, 626 CodeAttribute codeAttribute, 627 TracedVariables variables, 628 TracedStack stack, 629 int startOffset) 630 { 631 byte[] code = codeAttribute.code; 632 633 if (DEBUG) 634 { 635 System.out.println("Instruction block starting at ["+startOffset+"] in "+ 636 ClassUtil.externalFullMethodDescription(clazz.getName(), 637 0, 638 method.getName(clazz), 639 method.getDescriptor(clazz))); 640 System.out.println("Init vars: "+variables); 641 System.out.println("Init stack: "+stack); 642 } 643 644 Processor processor = new Processor(variables, 645 stack, 646 valueFactory, 647 branchUnit, 648 invocationUnit, 649 evaluateAllCode); 650 651 int instructionOffset = startOffset; 652 653 int maxOffset = startOffset; 654 655 // Evaluate the subsequent instructions. 656 while (true) 657 { 658 if (maxOffset < instructionOffset) 659 { 660 maxOffset = instructionOffset; 661 } 662 663 // Maintain a generalized local variable frame and stack at this 664 // instruction offset, before execution. 665 int evaluationCount = evaluationCounts[instructionOffset]; 666 if (evaluationCount == 0) 667 { 668 // First time we're passing by this instruction. 669 if (variablesBefore[instructionOffset] == null) 670 { 671 // There's not even a context at this index yet. 672 variablesBefore[instructionOffset] = new TracedVariables(variables); 673 stacksBefore[instructionOffset] = new TracedStack(stack); 674 } 675 else 676 { 677 // Reuse the context objects at this index. 678 variablesBefore[instructionOffset].initialize(variables); 679 stacksBefore[instructionOffset].copy(stack); 680 } 681 682 // We'll execute in the generalized context, because it is 683 // the same as the current context. 684 generalizedContexts[instructionOffset] = true; 685 } 686 else 687 { 688 // Merge in the current context. 689 boolean variablesChanged = variablesBefore[instructionOffset].generalize(variables, true); 690 boolean stackChanged = stacksBefore[instructionOffset].generalize(stack); 691 692 //System.out.println("GVars: "+variablesBefore[instructionOffset]); 693 //System.out.println("GStack: "+stacksBefore[instructionOffset]); 694 695 // Bail out if the current context is the same as last time. 696 if (!variablesChanged && 697 !stackChanged && 698 generalizedContexts[instructionOffset]) 699 { 700 if (DEBUG) System.out.println("Repeated variables, stack, and branch targets"); 701 702 break; 703 } 704 705 // See if this instruction has been evaluated an excessive number 706 // of times. 707 if (evaluationCount >= MAXIMUM_EVALUATION_COUNT) 708 { 709 if (DEBUG) System.out.println("Generalizing current context after "+evaluationCount+" evaluations"); 710 711 // Continue, but generalize the current context. 712 // Note that the most recent variable values have to remain 713 // last in the generalizations, for the sake of the ret 714 // instruction. 715 variables.generalize(variablesBefore[instructionOffset], false); 716 stack.generalize(stacksBefore[instructionOffset]); 717 718 // We'll execute in the generalized context. 719 generalizedContexts[instructionOffset] = true; 720 } 721 else 722 { 723 // We'll execute in the current context. 724 generalizedContexts[instructionOffset] = false; 725 } 726 } 727 728 // We'll evaluate this instruction. 729 evaluationCounts[instructionOffset]++; 730 731 // Remember this instruction's offset with any stored value. 732 Value storeValue = new InstructionOffsetValue(instructionOffset); 733 variables.setProducerValue(storeValue); 734 stack.setProducerValue(storeValue); 735 736 // Reset the trace value. 737 InstructionOffsetValue traceValue = InstructionOffsetValue.EMPTY_VALUE; 738 739 // Note that the instruction is only volatile. 740 Instruction instruction = InstructionFactory.create(code, instructionOffset); 741 742 // By default, the next instruction will be the one after this 743 // instruction. 744 int nextInstructionOffset = instructionOffset + 745 instruction.length(instructionOffset); 746 InstructionOffsetValue nextInstructionOffsetValue = new InstructionOffsetValue(nextInstructionOffset); 747 branchUnit.resetCalled(); 748 branchUnit.setTraceBranchTargets(nextInstructionOffsetValue); 749 750 if (DEBUG) 751 { 752 System.out.println(instruction.toString(instructionOffset)); 753 } 754 755 try 756 { 757 // Process the instruction. The processor may modify the 758 // variables and the stack, and it may call the branch unit 759 // and the invocation unit. 760 instruction.accept(clazz, 761 method, 762 codeAttribute, 763 instructionOffset, 764 processor); 765 } 766 catch (RuntimeException ex) 767 { 768 System.err.println("Unexpected error while evaluating instruction:"); 769 System.err.println(" Class = ["+clazz.getName()+"]"); 770 System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); 771 System.err.println(" Instruction = "+instruction.toString(instructionOffset)); 772 System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); 773 774 throw ex; 775 } 776 777 // Collect the branch targets from the branch unit. 778 InstructionOffsetValue branchTargets = branchUnit.getTraceBranchTargets(); 779 int branchTargetCount = branchTargets.instructionOffsetCount(); 780 781 // Stop tracing. 782 branchUnit.setTraceBranchTargets(traceValue); 783 784 if (DEBUG) 785 { 786 if (branchUnit.wasCalled()) 787 { 788 System.out.println(" is branching to "+branchTargets); 789 } 790 if (branchTargetValues[instructionOffset] != null) 791 { 792 System.out.println(" has up till now been branching to "+branchTargetValues[instructionOffset]); 793 } 794 795 System.out.println(" Vars: "+variables); 796 System.out.println(" Stack: "+stack); 797 } 798 799 // Maintain a generalized local variable frame and stack at this 800 // instruction offset, after execution. 801 if (evaluationCount == 0) 802 { 803 // First time we're passing by this instruction. 804 if (variablesAfter[instructionOffset] == null) 805 { 806 // There's not even a context at this index yet. 807 variablesAfter[instructionOffset] = new TracedVariables(variables); 808 stacksAfter[instructionOffset] = new TracedStack(stack); 809 } 810 else 811 { 812 // Reuse the context objects at this index. 813 variablesAfter[instructionOffset].initialize(variables); 814 stacksAfter[instructionOffset].copy(stack); 815 } 816 } 817 else 818 { 819 // Merge in the current context. 820 variablesAfter[instructionOffset].generalize(variables, true); 821 stacksAfter[instructionOffset].generalize(stack); 822 } 823 824 // Did the branch unit get called? 825 if (branchUnit.wasCalled()) 826 { 827 // Accumulate the branch targets at this offset. 828 branchTargetValues[instructionOffset] = branchTargetValues[instructionOffset] == null ? 829 branchTargets : 830 branchTargetValues[instructionOffset].generalize(branchTargets).instructionOffsetValue(); 831 832 // Are there no branch targets at all? 833 if (branchTargetCount == 0) 834 { 835 // Exit from this code block. 836 break; 837 } 838 839 // Accumulate the branch origins at the branch target offsets. 840 InstructionOffsetValue instructionOffsetValue = new InstructionOffsetValue(instructionOffset); 841 for (int index = 0; index < branchTargetCount; index++) 842 { 843 int branchTarget = branchTargets.instructionOffset(index); 844 branchOriginValues[branchTarget] = branchOriginValues[branchTarget] == null ? 845 instructionOffsetValue: 846 branchOriginValues[branchTarget].generalize(instructionOffsetValue).instructionOffsetValue(); 847 } 848 849 // Are there multiple branch targets? 850 if (branchTargetCount > 1) 851 { 852 // Push them on the execution stack and exit from this block. 853 for (int index = 0; index < branchTargetCount; index++) 854 { 855 if (DEBUG) System.out.println("Pushing alternative branch #"+index+" out of "+branchTargetCount+", from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(index)+"]"); 856 857 pushInstructionBlock(new TracedVariables(variables), 858 new TracedStack(stack), 859 branchTargets.instructionOffset(index)); 860 } 861 862 break; 863 } 864 865 if (DEBUG) System.out.println("Definite branch from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(0)+"]"); 866 } 867 868 // Just continue with the next instruction. 869 instructionOffset = branchTargets.instructionOffset(0); 870 871 // Is this a subroutine invocation? 872 if (instruction.opcode == InstructionConstants.OP_JSR || 873 instruction.opcode == InstructionConstants.OP_JSR_W) 874 { 875 // Evaluate the subroutine in another partial evaluator. 876 evaluateSubroutine(clazz, 877 method, 878 codeAttribute, 879 variables, 880 stack, 881 instructionOffset, 882 instructionBlockStack); 883 884 break; 885 } 886 else if (instruction.opcode == InstructionConstants.OP_RET) 887 { 888 // Let the partial evaluator that has called the subroutine 889 // handle the evaluation after the return. 890 pushCallingInstructionBlock(new TracedVariables(variables), 891 new TracedStack(stack), 892 instructionOffset); 893 break; 894 } 895 } 896 897 if (DEBUG) System.out.println("Ending processing of instruction block starting at ["+startOffset+"]"); 898 } 899 900 901 /** 902 * Evaluates a subroutine and its exception handlers, starting at the given 903 * offset and ending at a subroutine return instruction. 904 */ 905 private void evaluateSubroutine(Clazz clazz, 906 Method method, 907 CodeAttribute codeAttribute, 908 TracedVariables variables, 909 TracedStack stack, 910 int subroutineStart, 911 java.util.Stack instructionBlockStack) 912 { 913 int subroutineEnd = branchTargetFinder.subroutineEnd(subroutineStart); 914 915 if (DEBUG) System.out.println("Evaluating subroutine from "+subroutineStart+" to "+subroutineEnd); 916 917 // Create a temporary partial evaluator, so there are no conflicts 918 // with variables that are alive across subroutine invocations, between 919 // different invocations. 920 PartialEvaluator subroutinePartialEvaluator = 921 new PartialEvaluator(this); 922 923 subroutinePartialEvaluator.initializeArrays(codeAttribute); 924 925 // Evaluate the subroutine. 926 subroutinePartialEvaluator.evaluateInstructionBlockAndExceptionHandlers(clazz, 927 method, 928 codeAttribute, 929 variables, 930 stack, 931 subroutineStart, 932 subroutineEnd); 933 934 // Merge back the temporary partial evaluator. This way, we'll get 935 // the lowest common denominator of stacks and variables. 936 generalize(subroutinePartialEvaluator, 0, codeAttribute.u4codeLength); 937 938 if (DEBUG) System.out.println("Ending subroutine from "+subroutineStart+" to "+subroutineEnd); 939 } 940 941 942 /** 943 * Generalizes the results of this partial evaluator with those of another 944 * given partial evaluator, over a given range of instructions. 945 */ 946 private void generalize(PartialEvaluator other, 947 int codeStart, 948 int codeEnd) 949 { 950 if (DEBUG) System.out.println("Generalizing with temporary partial evaluation"); 951 952 for (int offset = codeStart; offset < codeEnd; offset++) 953 { 954 if (other.branchOriginValues[offset] != null) 955 { 956 branchOriginValues[offset] = branchOriginValues[offset] == null ? 957 other.branchOriginValues[offset] : 958 branchOriginValues[offset].generalize(other.branchOriginValues[offset]).instructionOffsetValue(); 959 } 960 961 if (other.isTraced(offset)) 962 { 963 if (other.branchTargetValues[offset] != null) 964 { 965 branchTargetValues[offset] = branchTargetValues[offset] == null ? 966 other.branchTargetValues[offset] : 967 branchTargetValues[offset].generalize(other.branchTargetValues[offset]).instructionOffsetValue(); 968 } 969 970 if (evaluationCounts[offset] == 0) 971 { 972 variablesBefore[offset] = other.variablesBefore[offset]; 973 stacksBefore[offset] = other.stacksBefore[offset]; 974 variablesAfter[offset] = other.variablesAfter[offset]; 975 stacksAfter[offset] = other.stacksAfter[offset]; 976 generalizedContexts[offset] = other.generalizedContexts[offset]; 977 evaluationCounts[offset] = other.evaluationCounts[offset]; 978 } 979 else 980 { 981 variablesBefore[offset].generalize(other.variablesBefore[offset], false); 982 stacksBefore[offset] .generalize(other.stacksBefore[offset]); 983 variablesAfter[offset] .generalize(other.variablesAfter[offset], false); 984 stacksAfter[offset] .generalize(other.stacksAfter[offset]); 985 //generalizedContexts[offset] 986 evaluationCounts[offset] += other.evaluationCounts[offset]; 987 } 988 } 989 } 990 } 991 992 993 /** 994 * Evaluates the exception handlers covering and targeting the given 995 * instruction range in the given code. 996 */ 997 private void evaluateExceptionHandlers(Clazz clazz, 998 Method method, 999 CodeAttribute codeAttribute, 1000 int startOffset, 1001 int endOffset) 1002 { 1003 if (DEBUG) System.out.println("Evaluating exceptions covering ["+startOffset+" -> "+endOffset+"]:"); 1004 1005 ExceptionHandlerFilter exceptionEvaluator = 1006 new ExceptionHandlerFilter(startOffset, 1007 endOffset, 1008 this); 1009 1010 // Evaluate the exception catch blocks, until their entry variables 1011 // have stabilized. 1012 do 1013 { 1014 // Reset the flag to stop evaluating. 1015 evaluateExceptions = false; 1016 1017 // Evaluate all relevant exception catch blocks once. 1018 codeAttribute.exceptionsAccept(clazz, 1019 method, 1020 startOffset, 1021 endOffset, 1022 exceptionEvaluator); 1023 } 1024 while (evaluateExceptions); 1025 } 1026 1027 1028 // Implementations for ExceptionInfoVisitor. 1029 1030 public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) 1031 { 1032 int startPC = exceptionInfo.u2startPC; 1033 int endPC = exceptionInfo.u2endPC; 1034 1035 // Do we have to evaluate this exception catch block? 1036 if (isTraced(startPC, endPC)) 1037 { 1038 int handlerPC = exceptionInfo.u2handlerPC; 1039 int catchType = exceptionInfo.u2catchType; 1040 1041 if (DEBUG) System.out.println("Evaluating exception ["+startPC +" -> "+endPC +": "+handlerPC+"]:"); 1042 1043 // Reuse the existing variables and stack objects, ensuring the 1044 // right size. 1045 TracedVariables variables = new TracedVariables(codeAttribute.u2maxLocals); 1046 TracedStack stack = new TracedStack(codeAttribute.u2maxStack); 1047 1048 // Initialize the trace values. 1049 Value storeValue = new InstructionOffsetValue(AT_CATCH_ENTRY); 1050 variables.setProducerValue(storeValue); 1051 stack.setProducerValue(storeValue); 1052 1053 // Initialize the variables by generalizing the variables of the 1054 // try block. Make sure to include the results of the last 1055 // instruction for preverification. 1056 generalizeVariables(startPC, 1057 endPC, 1058 evaluateAllCode, 1059 variables); 1060 1061 // Initialize the the stack. 1062 //stack.push(valueFactory.createReference((ClassConstant)((ProgramClass)clazz).getConstant(exceptionInfo.u2catchType), false)); 1063 String catchClassName = catchType != 0 ? 1064 clazz.getClassName(catchType) : 1065 ClassConstants.NAME_JAVA_LANG_THROWABLE; 1066 1067 Clazz catchClass = catchType != 0 ? 1068 ((ClassConstant)((ProgramClass)clazz).getConstant(catchType)).referencedClass : 1069 null; 1070 1071 stack.push(valueFactory.createReferenceValue(catchClassName, 1072 catchClass, 1073 false)); 1074 1075 int evaluationCount = evaluationCounts[handlerPC]; 1076 1077 // Evaluate the instructions, starting at the entry point. 1078 evaluateInstructionBlock(clazz, 1079 method, 1080 codeAttribute, 1081 variables, 1082 stack, 1083 handlerPC); 1084 1085 // Remember to evaluate all exception handlers once more. 1086 if (!evaluateExceptions) 1087 { 1088 evaluateExceptions = evaluationCount < evaluationCounts[handlerPC]; 1089 } 1090 } 1091// else if (evaluateAllCode) 1092// { 1093// if (DEBUG) System.out.println("No information for partial evaluation of exception ["+startPC +" -> "+endPC +": "+exceptionInfo.u2handlerPC+"] yet"); 1094// 1095// // We don't have any information on the try block yet, but we do 1096// // have to evaluate the exception handler. 1097// // Remember to evaluate all exception handlers once more. 1098// evaluateExceptions = true; 1099// } 1100 else 1101 { 1102 if (DEBUG) System.out.println("No information for partial evaluation of exception ["+startPC +" -> "+endPC +": "+exceptionInfo.u2handlerPC+"]"); 1103 } 1104 } 1105 1106 1107 // Small utility methods. 1108 1109 /** 1110 * Initializes the data structures for the variables, stack, etc. 1111 */ 1112 private void initializeArrays(CodeAttribute codeAttribute) 1113 { 1114 int codeLength = codeAttribute.u4codeLength; 1115 1116 // Create new arrays for storing information at each instruction offset. 1117 if (variablesAfter.length < codeLength) 1118 { 1119 // Create new arrays. 1120 branchOriginValues = new InstructionOffsetValue[codeLength]; 1121 branchTargetValues = new InstructionOffsetValue[codeLength]; 1122 variablesBefore = new TracedVariables[codeLength]; 1123 stacksBefore = new TracedStack[codeLength]; 1124 variablesAfter = new TracedVariables[codeLength]; 1125 stacksAfter = new TracedStack[codeLength]; 1126 generalizedContexts = new boolean[codeLength]; 1127 evaluationCounts = new int[codeLength]; 1128 } 1129 else 1130 { 1131 // Reset the arrays. 1132 Arrays.fill(branchOriginValues, null); 1133 Arrays.fill(branchTargetValues, null); 1134 Arrays.fill(generalizedContexts, false); 1135 Arrays.fill(evaluationCounts, 0); 1136 1137 for (int index = 0; index < codeLength; index++) 1138 { 1139 if (variablesBefore[index] != null) 1140 { 1141 variablesBefore[index].reset(codeAttribute.u2maxLocals); 1142 } 1143 1144 if (stacksBefore[index] != null) 1145 { 1146 stacksBefore[index].reset(codeAttribute.u2maxStack); 1147 } 1148 1149 if (variablesAfter[index] != null) 1150 { 1151 variablesAfter[index].reset(codeAttribute.u2maxLocals); 1152 } 1153 1154 if (stacksAfter[index] != null) 1155 { 1156 stacksAfter[index].reset(codeAttribute.u2maxStack); 1157 } 1158 } 1159 } 1160 } 1161 1162 1163 /** 1164 * Initializes the data structures for the variables, stack, etc. 1165 */ 1166 private void initializeParameters(Clazz clazz, 1167 Method method, 1168 CodeAttribute codeAttribute, 1169 TracedVariables variables) 1170 { 1171 // Create the method parameters. 1172 TracedVariables parameters = new TracedVariables(codeAttribute.u2maxLocals); 1173 1174 // Remember this instruction's offset with any stored value. 1175 Value storeValue = new InstructionOffsetValue(AT_METHOD_ENTRY); 1176 parameters.setProducerValue(storeValue); 1177 1178 // Initialize the method parameters. 1179 invocationUnit.enterMethod(clazz, method, parameters); 1180 1181 if (DEBUG) 1182 { 1183 System.out.println(" Params: "+parameters); 1184 } 1185 1186 // Initialize the variables with the parameters. 1187 variables.initialize(parameters); 1188 1189 // Set the store value of each parameter variable. 1190 InstructionOffsetValue atMethodEntry = new InstructionOffsetValue(AT_METHOD_ENTRY); 1191 1192 for (int index = 0; index < parameters.size(); index++) 1193 { 1194 variables.setProducerValue(index, atMethodEntry); 1195 } 1196 } 1197 1198 1199 /** 1200 * Generalize the local variable frames of a block of instructions. 1201 */ 1202 private void generalizeVariables(int startOffset, 1203 int endOffset, 1204 boolean includeAfterLastInstruction, 1205 TracedVariables generalizedVariables) 1206 { 1207 boolean first = true; 1208 int lastIndex = -1; 1209 1210 // Generalize the variables before each of the instructions in the block. 1211 for (int index = startOffset; index < endOffset; index++) 1212 { 1213 if (isTraced(index)) 1214 { 1215 TracedVariables tracedVariables = variablesBefore[index]; 1216 1217 if (first) 1218 { 1219 // Initialize the variables with the first traced local 1220 // variable frame. 1221 generalizedVariables.initialize(tracedVariables); 1222 1223 first = false; 1224 } 1225 else 1226 { 1227 // Generalize the variables with the traced local variable 1228 // frame. We can't use the return value, because local 1229 // generalization can be different a couple of times, 1230 // with the global generalization being the same. 1231 generalizedVariables.generalize(tracedVariables, false); 1232 } 1233 1234 lastIndex = index; 1235 } 1236 } 1237 1238 // Generalize the variables after the last instruction in the block, 1239 // if required. 1240 if (includeAfterLastInstruction && 1241 lastIndex >= 0) 1242 { 1243 TracedVariables tracedVariables = variablesAfter[lastIndex]; 1244 1245 if (first) 1246 { 1247 // Initialize the variables with the local variable frame. 1248 generalizedVariables.initialize(tracedVariables); 1249 } 1250 else 1251 { 1252 // Generalize the variables with the local variable frame. 1253 generalizedVariables.generalize(tracedVariables, false); 1254 } 1255 } 1256 1257 // Just clear the variables if there aren't any traced instructions 1258 // in the block. 1259 if (first) 1260 { 1261 generalizedVariables.reset(generalizedVariables.size()); 1262 } 1263 } 1264 1265 1266 private static class MyInstructionBlock 1267 { 1268 private TracedVariables variables; 1269 private TracedStack stack; 1270 private int startOffset; 1271 1272 1273 private MyInstructionBlock(TracedVariables variables, 1274 TracedStack stack, 1275 int startOffset) 1276 { 1277 this.variables = variables; 1278 this.stack = stack; 1279 this.startOffset = startOffset; 1280 } 1281 } 1282} 1283