1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2009 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.AttributeVisitor; 26import proguard.classfile.editor.*; 27import proguard.classfile.instruction.*; 28import proguard.classfile.instruction.visitor.InstructionVisitor; 29import proguard.classfile.util.*; 30import proguard.classfile.visitor.*; 31import proguard.evaluation.*; 32import proguard.evaluation.value.*; 33import proguard.optimize.info.*; 34 35/** 36 * This AttributeVisitor simplifies the code attributes that it visits, based 37 * on partial evaluation. 38 * 39 * @author Eric Lafortune 40 */ 41public class EvaluationSimplifier 42extends SimplifiedVisitor 43implements AttributeVisitor, 44 InstructionVisitor 45{ 46 //* 47 private static final boolean DEBUG = false; 48 /*/ 49 private static boolean DEBUG = true; 50 //*/ 51 52 private final InstructionVisitor extraInstructionVisitor; 53 54 private final PartialEvaluator partialEvaluator; 55 private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true); 56 private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false); 57 58 59 /** 60 * Creates a new EvaluationSimplifier. 61 */ 62 public EvaluationSimplifier() 63 { 64 this(new PartialEvaluator(), null); 65 } 66 67 68 /** 69 * Creates a new EvaluationSimplifier. 70 * @param partialEvaluator the partial evaluator that will 71 * execute the code and provide 72 * information about the results. 73 * @param extraInstructionVisitor an optional extra visitor for all 74 * simplified instructions. 75 */ 76 public EvaluationSimplifier(PartialEvaluator partialEvaluator, 77 InstructionVisitor extraInstructionVisitor) 78 { 79 this.partialEvaluator = partialEvaluator; 80 this.extraInstructionVisitor = extraInstructionVisitor; 81 } 82 83 84 // Implementations for AttributeVisitor. 85 86 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 87 88 89 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 90 { 91// DEBUG = 92// clazz.getName().equals("abc/Def") && 93// method.getName(clazz).equals("abc"); 94 95 // TODO: Remove this when the evaluation simplifier has stabilized. 96 // Catch any unexpected exceptions from the actual visiting method. 97 try 98 { 99 // Process the code. 100 visitCodeAttribute0(clazz, method, codeAttribute); 101 } 102 catch (RuntimeException ex) 103 { 104 System.err.println("Unexpected error while simplifying instructions after partial evaluation:"); 105 System.err.println(" Class = ["+clazz.getName()+"]"); 106 System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); 107 System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); 108 System.err.println("Not optimizing this method"); 109 110 if (DEBUG) 111 { 112 method.accept(clazz, new ClassPrinter()); 113 114 throw ex; 115 } 116 } 117 } 118 119 120 public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) 121 { 122 if (DEBUG) 123 { 124 System.out.println(); 125 System.out.println("Class "+ClassUtil.externalClassName(clazz.getName())); 126 System.out.println("Method "+ClassUtil.externalFullMethodDescription(clazz.getName(), 127 0, 128 method.getName(clazz), 129 method.getDescriptor(clazz))); 130 } 131 132 // Evaluate the method. 133 partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); 134 135 int codeLength = codeAttribute.u4codeLength; 136 137 // Reset the code changes. 138 codeAttributeEditor.reset(codeLength); 139 140 // Replace any instructions that can be simplified. 141 for (int offset = 0; offset < codeLength; offset++) 142 { 143 if (partialEvaluator.isTraced(offset)) 144 { 145 Instruction instruction = InstructionFactory.create(codeAttribute.code, 146 offset); 147 148 instruction.accept(clazz, method, codeAttribute, offset, this); 149 } 150 } 151 152 // Apply all accumulated changes to the code. 153 codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); 154 } 155 156 157 // Implementations for InstructionVisitor. 158 159 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 160 { 161 switch (simpleInstruction.opcode) 162 { 163 case InstructionConstants.OP_IALOAD: 164 case InstructionConstants.OP_BALOAD: 165 case InstructionConstants.OP_CALOAD: 166 case InstructionConstants.OP_SALOAD: 167 case InstructionConstants.OP_IADD: 168 case InstructionConstants.OP_ISUB: 169 case InstructionConstants.OP_IMUL: 170 case InstructionConstants.OP_IDIV: 171 case InstructionConstants.OP_IREM: 172 case InstructionConstants.OP_INEG: 173 case InstructionConstants.OP_ISHL: 174 case InstructionConstants.OP_ISHR: 175 case InstructionConstants.OP_IUSHR: 176 case InstructionConstants.OP_IAND: 177 case InstructionConstants.OP_IOR: 178 case InstructionConstants.OP_IXOR: 179 case InstructionConstants.OP_L2I: 180 case InstructionConstants.OP_F2I: 181 case InstructionConstants.OP_D2I: 182 case InstructionConstants.OP_I2B: 183 case InstructionConstants.OP_I2C: 184 case InstructionConstants.OP_I2S: 185 replaceIntegerPushInstruction(clazz, offset, simpleInstruction); 186 break; 187 188 case InstructionConstants.OP_LALOAD: 189 case InstructionConstants.OP_LADD: 190 case InstructionConstants.OP_LSUB: 191 case InstructionConstants.OP_LMUL: 192 case InstructionConstants.OP_LDIV: 193 case InstructionConstants.OP_LREM: 194 case InstructionConstants.OP_LNEG: 195 case InstructionConstants.OP_LSHL: 196 case InstructionConstants.OP_LSHR: 197 case InstructionConstants.OP_LUSHR: 198 case InstructionConstants.OP_LAND: 199 case InstructionConstants.OP_LOR: 200 case InstructionConstants.OP_LXOR: 201 case InstructionConstants.OP_I2L: 202 case InstructionConstants.OP_F2L: 203 case InstructionConstants.OP_D2L: 204 replaceLongPushInstruction(clazz, offset, simpleInstruction); 205 break; 206 207 case InstructionConstants.OP_FALOAD: 208 case InstructionConstants.OP_FADD: 209 case InstructionConstants.OP_FSUB: 210 case InstructionConstants.OP_FMUL: 211 case InstructionConstants.OP_FDIV: 212 case InstructionConstants.OP_FREM: 213 case InstructionConstants.OP_FNEG: 214 case InstructionConstants.OP_I2F: 215 case InstructionConstants.OP_L2F: 216 case InstructionConstants.OP_D2F: 217 replaceFloatPushInstruction(clazz, offset, simpleInstruction); 218 break; 219 220 case InstructionConstants.OP_DALOAD: 221 case InstructionConstants.OP_DADD: 222 case InstructionConstants.OP_DSUB: 223 case InstructionConstants.OP_DMUL: 224 case InstructionConstants.OP_DDIV: 225 case InstructionConstants.OP_DREM: 226 case InstructionConstants.OP_DNEG: 227 case InstructionConstants.OP_I2D: 228 case InstructionConstants.OP_L2D: 229 case InstructionConstants.OP_F2D: 230 replaceDoublePushInstruction(clazz, offset, simpleInstruction); 231 break; 232 233 case InstructionConstants.OP_AALOAD: 234 replaceReferencePushInstruction(clazz, offset, simpleInstruction); 235 break; 236 } 237 } 238 239 240 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 241 { 242 int variableIndex = variableInstruction.variableIndex; 243 244 switch (variableInstruction.opcode) 245 { 246 case InstructionConstants.OP_ILOAD: 247 case InstructionConstants.OP_ILOAD_0: 248 case InstructionConstants.OP_ILOAD_1: 249 case InstructionConstants.OP_ILOAD_2: 250 case InstructionConstants.OP_ILOAD_3: 251 replaceIntegerPushInstruction(clazz, offset, variableInstruction, variableIndex); 252 break; 253 254 case InstructionConstants.OP_LLOAD: 255 case InstructionConstants.OP_LLOAD_0: 256 case InstructionConstants.OP_LLOAD_1: 257 case InstructionConstants.OP_LLOAD_2: 258 case InstructionConstants.OP_LLOAD_3: 259 replaceLongPushInstruction(clazz, offset, variableInstruction, variableIndex); 260 break; 261 262 case InstructionConstants.OP_FLOAD: 263 case InstructionConstants.OP_FLOAD_0: 264 case InstructionConstants.OP_FLOAD_1: 265 case InstructionConstants.OP_FLOAD_2: 266 case InstructionConstants.OP_FLOAD_3: 267 replaceFloatPushInstruction(clazz, offset, variableInstruction, variableIndex); 268 break; 269 270 case InstructionConstants.OP_DLOAD: 271 case InstructionConstants.OP_DLOAD_0: 272 case InstructionConstants.OP_DLOAD_1: 273 case InstructionConstants.OP_DLOAD_2: 274 case InstructionConstants.OP_DLOAD_3: 275 replaceDoublePushInstruction(clazz, offset, variableInstruction, variableIndex); 276 break; 277 278 case InstructionConstants.OP_ALOAD: 279 case InstructionConstants.OP_ALOAD_0: 280 case InstructionConstants.OP_ALOAD_1: 281 case InstructionConstants.OP_ALOAD_2: 282 case InstructionConstants.OP_ALOAD_3: 283 replaceReferencePushInstruction(clazz, offset, variableInstruction); 284 break; 285 286 case InstructionConstants.OP_ASTORE: 287 case InstructionConstants.OP_ASTORE_0: 288 case InstructionConstants.OP_ASTORE_1: 289 case InstructionConstants.OP_ASTORE_2: 290 case InstructionConstants.OP_ASTORE_3: 291 deleteReferencePopInstruction(clazz, offset, variableInstruction); 292 break; 293 294 case InstructionConstants.OP_RET: 295 replaceBranchInstruction(clazz, offset, variableInstruction); 296 break; 297 } 298 } 299 300 301 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 302 { 303 switch (constantInstruction.opcode) 304 { 305 case InstructionConstants.OP_GETSTATIC: 306 case InstructionConstants.OP_GETFIELD: 307 replaceAnyPushInstruction(clazz, offset, constantInstruction); 308 break; 309 310 case InstructionConstants.OP_INVOKEVIRTUAL: 311 case InstructionConstants.OP_INVOKESPECIAL: 312 case InstructionConstants.OP_INVOKESTATIC: 313 case InstructionConstants.OP_INVOKEINTERFACE: 314 if (constantInstruction.stackPushCount(clazz) > 0 && 315 !sideEffectInstructionChecker.hasSideEffects(clazz, 316 method, 317 codeAttribute, 318 offset, 319 constantInstruction)) 320 { 321 replaceAnyPushInstruction(clazz, offset, constantInstruction); 322 } 323 324 break; 325 326 case InstructionConstants.OP_CHECKCAST: 327 replaceReferencePushInstruction(clazz, offset, constantInstruction); 328 break; 329 } 330 } 331 332 333 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 334 { 335 switch (branchInstruction.opcode) 336 { 337 case InstructionConstants.OP_GOTO: 338 case InstructionConstants.OP_GOTO_W: 339 // Don't replace unconditional branches. 340 break; 341 342 case InstructionConstants.OP_JSR: 343 case InstructionConstants.OP_JSR_W: 344 replaceJsrInstruction(clazz, offset, branchInstruction); 345 break; 346 347 default: 348 replaceBranchInstruction(clazz, offset, branchInstruction); 349 break; 350 } 351 } 352 353 354 public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) 355 { 356 // First try to simplify it to a simple branch. 357 replaceBranchInstruction(clazz, offset, switchInstruction); 358 359 // Otherwise make sure all branch targets are valid. 360 if (!codeAttributeEditor.isModified(offset)) 361 { 362 replaceSwitchInstruction(clazz, offset, switchInstruction); 363 } 364 } 365 366 367 // Small utility methods. 368 369 /** 370 * Replaces the push instruction at the given offset by a simpler push 371 * instruction, if possible. 372 */ 373 private void replaceAnyPushInstruction(Clazz clazz, 374 int offset, 375 Instruction instruction) 376 { 377 Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); 378 if (pushedValue.isParticular()) 379 { 380 switch (pushedValue.computationalType()) 381 { 382 case Value.TYPE_INTEGER: 383 replaceIntegerPushInstruction(clazz, offset, instruction); 384 break; 385 case Value.TYPE_LONG: 386 replaceLongPushInstruction(clazz, offset, instruction); 387 break; 388 case Value.TYPE_FLOAT: 389 replaceFloatPushInstruction(clazz, offset, instruction); 390 break; 391 case Value.TYPE_DOUBLE: 392 replaceDoublePushInstruction(clazz, offset, instruction); 393 break; 394 case Value.TYPE_REFERENCE: 395 replaceReferencePushInstruction(clazz, offset, instruction); 396 break; 397 } 398 } 399 } 400 401 402 /** 403 * Replaces the integer pushing instruction at the given offset by a simpler 404 * push instruction, if possible. 405 */ 406 private void replaceIntegerPushInstruction(Clazz clazz, 407 int offset, 408 Instruction instruction) 409 { 410 replaceIntegerPushInstruction(clazz, 411 offset, 412 instruction, 413 partialEvaluator.getVariablesBefore(offset).size()); 414 } 415 416 417 /** 418 * Replaces the integer pushing instruction at the given offset by a simpler 419 * push instruction, if possible. 420 */ 421 private void replaceIntegerPushInstruction(Clazz clazz, 422 int offset, 423 Instruction instruction, 424 int maxVariableIndex) 425 { 426 Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); 427 if (pushedValue.isParticular()) 428 { 429 int value = pushedValue.integerValue().value(); 430 if (value << 16 >> 16 == value) 431 { 432 replaceConstantPushInstruction(clazz, 433 offset, 434 instruction, 435 InstructionConstants.OP_SIPUSH, 436 value); 437 } 438 else 439 { 440 ConstantPoolEditor constantPoolEditor = 441 new ConstantPoolEditor((ProgramClass)clazz); 442 443 Instruction replacementInstruction = 444 new ConstantInstruction(InstructionConstants.OP_LDC, 445 constantPoolEditor.addIntegerConstant(value)).shrink(); 446 447 replaceInstruction(clazz, offset, instruction, replacementInstruction); 448 } 449 } 450 else if (pushedValue.isSpecific()) 451 { 452 TracedVariables variables = partialEvaluator.getVariablesBefore(offset); 453 for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) 454 { 455 if (pushedValue.equals(variables.load(variableIndex))) 456 { 457 replaceVariablePushInstruction(clazz, 458 offset, 459 instruction, 460 InstructionConstants.OP_ILOAD, 461 variableIndex); 462 } 463 } 464 } 465 } 466 467 468 /** 469 * Replaces the long pushing instruction at the given offset by a simpler 470 * push instruction, if possible. 471 */ 472 private void replaceLongPushInstruction(Clazz clazz, 473 int offset, 474 Instruction instruction) 475 { 476 replaceLongPushInstruction(clazz, 477 offset, 478 instruction, 479 partialEvaluator.getVariablesBefore(offset).size()); 480 } 481 482 483 /** 484 * Replaces the long pushing instruction at the given offset by a simpler 485 * push instruction, if possible. 486 */ 487 private void replaceLongPushInstruction(Clazz clazz, 488 int offset, 489 Instruction instruction, 490 int maxVariableIndex) 491 { 492 Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); 493 if (pushedValue.isParticular()) 494 { 495 long value = pushedValue.longValue().value(); 496 if (value == 0L || 497 value == 1L) 498 { 499 replaceConstantPushInstruction(clazz, 500 offset, 501 instruction, 502 InstructionConstants.OP_LCONST_0, 503 (int)value); 504 } 505 else 506 { 507 ConstantPoolEditor constantPoolEditor = 508 new ConstantPoolEditor((ProgramClass)clazz); 509 510 Instruction replacementInstruction = 511 new ConstantInstruction(InstructionConstants.OP_LDC2_W, 512 constantPoolEditor.addLongConstant(value)).shrink(); 513 514 replaceInstruction(clazz, offset, instruction, replacementInstruction); 515 } 516 } 517 else if (pushedValue.isSpecific()) 518 { 519 TracedVariables variables = partialEvaluator.getVariablesBefore(offset); 520 for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) 521 { 522 if (pushedValue.equals(variables.load(variableIndex))) 523 { 524 replaceVariablePushInstruction(clazz, 525 offset, 526 instruction, 527 InstructionConstants.OP_LLOAD, 528 variableIndex); 529 } 530 } 531 } 532 } 533 534 535 /** 536 * Replaces the float pushing instruction at the given offset by a simpler 537 * push instruction, if possible. 538 */ 539 private void replaceFloatPushInstruction(Clazz clazz, 540 int offset, 541 Instruction instruction) 542 { 543 replaceFloatPushInstruction(clazz, 544 offset, 545 instruction, 546 partialEvaluator.getVariablesBefore(offset).size()); 547 } 548 549 550 /** 551 * Replaces the float pushing instruction at the given offset by a simpler 552 * push instruction, if possible. 553 */ 554 private void replaceFloatPushInstruction(Clazz clazz, 555 int offset, 556 Instruction instruction, 557 int maxVariableIndex) 558 { 559 Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); 560 if (pushedValue.isParticular()) 561 { 562 float value = pushedValue.floatValue().value(); 563 if (value == 0f || 564 value == 1f || 565 value == 2f) 566 { 567 replaceConstantPushInstruction(clazz, 568 offset, 569 instruction, 570 InstructionConstants.OP_FCONST_0, 571 (int)value); 572 } 573 else 574 { 575 ConstantPoolEditor constantPoolEditor = 576 new ConstantPoolEditor((ProgramClass)clazz); 577 578 Instruction replacementInstruction = 579 new ConstantInstruction(InstructionConstants.OP_LDC, 580 constantPoolEditor.addFloatConstant(value)).shrink(); 581 582 replaceInstruction(clazz, offset, instruction, replacementInstruction); 583 } 584 } 585 else if (pushedValue.isSpecific()) 586 { 587 TracedVariables variables = partialEvaluator.getVariablesBefore(offset); 588 for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) 589 { 590 if (pushedValue.equals(variables.load(variableIndex))) 591 { 592 replaceVariablePushInstruction(clazz, 593 offset, 594 instruction, 595 InstructionConstants.OP_FLOAD, 596 variableIndex); 597 } 598 } 599 } 600 } 601 602 603 /** 604 * Replaces the double pushing instruction at the given offset by a simpler 605 * push instruction, if possible. 606 */ 607 private void replaceDoublePushInstruction(Clazz clazz, 608 int offset, 609 Instruction instruction) 610 { 611 replaceDoublePushInstruction(clazz, 612 offset, 613 instruction, 614 partialEvaluator.getVariablesBefore(offset).size()); 615 } 616 617 618 /** 619 * Replaces the double pushing instruction at the given offset by a simpler 620 * push instruction, if possible. 621 */ 622 private void replaceDoublePushInstruction(Clazz clazz, 623 int offset, 624 Instruction instruction, 625 int maxVariableIndex) 626 { 627 Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); 628 if (pushedValue.isParticular()) 629 { 630 double value = pushedValue.doubleValue().value(); 631 if (value == 0.0 || 632 value == 1.0) 633 { 634 replaceConstantPushInstruction(clazz, 635 offset, 636 instruction, 637 InstructionConstants.OP_DCONST_0, 638 (int)value); 639 } 640 else 641 { 642 ConstantPoolEditor constantPoolEditor = 643 new ConstantPoolEditor((ProgramClass)clazz); 644 645 Instruction replacementInstruction = 646 new ConstantInstruction(InstructionConstants.OP_LDC2_W, 647 constantPoolEditor.addDoubleConstant(value)).shrink(); 648 649 replaceInstruction(clazz, offset, instruction, replacementInstruction); 650 } 651 } 652 else if (pushedValue.isSpecific()) 653 { 654 TracedVariables variables = partialEvaluator.getVariablesBefore(offset); 655 for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) 656 { 657 if (pushedValue.equals(variables.load(variableIndex))) 658 { 659 replaceVariablePushInstruction(clazz, 660 offset, 661 instruction, 662 InstructionConstants.OP_DLOAD, 663 variableIndex); 664 } 665 } 666 } 667 } 668 669 670 /** 671 * Replaces the reference pushing instruction at the given offset by a 672 * simpler push instruction, if possible. 673 */ 674 private void replaceReferencePushInstruction(Clazz clazz, 675 int offset, 676 Instruction instruction) 677 { 678 Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); 679 if (pushedValue.isParticular()) 680 { 681 // A reference value can only be specific if it is null. 682 replaceConstantPushInstruction(clazz, 683 offset, 684 instruction, 685 InstructionConstants.OP_ACONST_NULL, 686 0); 687 } 688 } 689 690 691 /** 692 * Replaces the instruction at a given offset by a given push instruction 693 * of a constant. 694 */ 695 private void replaceConstantPushInstruction(Clazz clazz, 696 int offset, 697 Instruction instruction, 698 byte replacementOpcode, 699 int value) 700 { 701 Instruction replacementInstruction = 702 new SimpleInstruction(replacementOpcode, value).shrink(); 703 704 replaceInstruction(clazz, offset, instruction, replacementInstruction); 705 } 706 707 708 /** 709 * Replaces the instruction at a given offset by a given push instruction 710 * of a variable. 711 */ 712 private void replaceVariablePushInstruction(Clazz clazz, 713 int offset, 714 Instruction instruction, 715 byte replacementOpcode, 716 int variableIndex) 717 { 718 Instruction replacementInstruction = 719 new VariableInstruction(replacementOpcode, variableIndex).shrink(); 720 721 replaceInstruction(clazz, offset, instruction, replacementInstruction); 722 } 723 724 725 /** 726 * Replaces the given 'jsr' instruction by a simpler branch instruction, 727 * if it jumps to a subroutine that doesn't return or a subroutine that 728 * is only called from one place. 729 */ 730 private void replaceJsrInstruction(Clazz clazz, 731 int offset, 732 BranchInstruction branchInstruction) 733 { 734 // Is the subroutine ever returning? 735 int subroutineStart = offset + branchInstruction.branchOffset; 736 if (!partialEvaluator.isSubroutineReturning(subroutineStart) || 737 partialEvaluator.branchOrigins(subroutineStart).instructionOffsetCount() == 1) 738 { 739 // All 'jsr' instructions to this subroutine can be replaced 740 // by unconditional branch instructions. 741 replaceBranchInstruction(clazz, offset, branchInstruction); 742 } 743 else if (!partialEvaluator.isTraced(offset + branchInstruction.length(offset))) 744 { 745 // We have to make sure the instruction after this 'jsr' 746 // instruction is valid, even if it is never reached. 747 replaceByInfiniteLoop(clazz, offset + branchInstruction.length(offset), branchInstruction); 748 } 749 } 750 751 752 /** 753 * Deletes the reference popping instruction at the given offset, if 754 * it is at the start of a subroutine that doesn't return or a subroutine 755 * that is only called from one place. 756 */ 757 private void deleteReferencePopInstruction(Clazz clazz, 758 int offset, 759 Instruction instruction) 760 { 761 if (partialEvaluator.isSubroutineStart(offset) && 762 (!partialEvaluator.isSubroutineReturning(offset) || 763 partialEvaluator.branchOrigins(offset).instructionOffsetCount() == 1)) 764 { 765 if (DEBUG) System.out.println(" Deleting store of subroutine return address "+instruction.toString(offset)); 766 767 // A reference value can only be specific if it is null. 768 codeAttributeEditor.deleteInstruction(offset); 769 } 770 } 771 772 773 /** 774 * Deletes the given branch instruction, or replaces it by a simpler branch 775 * instruction, if possible. 776 */ 777 private void replaceBranchInstruction(Clazz clazz, 778 int offset, 779 Instruction instruction) 780 { 781 InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset); 782 783 // Is there exactly one branch target (not from a goto or jsr)? 784 if (branchTargets != null && 785 branchTargets.instructionOffsetCount() == 1) 786 { 787 // Is it branching to the next instruction? 788 int branchOffset = branchTargets.instructionOffset(0) - offset; 789 if (branchOffset == instruction.length(offset)) 790 { 791 if (DEBUG) System.out.println(" Ignoring zero branch instruction at ["+offset+"]"); 792 } 793 else 794 { 795 // Replace the branch instruction by a simple branch instruction. 796 Instruction replacementInstruction = 797 new BranchInstruction(InstructionConstants.OP_GOTO_W, 798 branchOffset).shrink(); 799 800 replaceInstruction(clazz, offset, instruction, replacementInstruction); 801 } 802 } 803 } 804 805 806 /** 807 * Makes sure all branch targets of the given switch instruction are valid. 808 */ 809 private void replaceSwitchInstruction(Clazz clazz, 810 int offset, 811 SwitchInstruction switchInstruction) 812 { 813 // Get the actual branch targets. 814 InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset); 815 816 // Get an offset that can serve as a valid default offset. 817 int defaultOffset = 818 branchTargets.instructionOffset(branchTargets.instructionOffsetCount()-1) - 819 offset; 820 821 Instruction replacementInstruction = null; 822 823 // Check the jump offsets. 824 int[] jumpOffsets = switchInstruction.jumpOffsets; 825 for (int index = 0; index < jumpOffsets.length; index++) 826 { 827 if (!branchTargets.contains(offset + jumpOffsets[index])) 828 { 829 // Replace the unused offset. 830 jumpOffsets[index] = defaultOffset; 831 832 // Remember to replace the instruction. 833 replacementInstruction = switchInstruction; 834 } 835 } 836 837 // Check the default offset. 838 if (!branchTargets.contains(offset + switchInstruction.defaultOffset)) 839 { 840 // Replace the unused offset. 841 switchInstruction.defaultOffset = defaultOffset; 842 843 // Remember to replace the instruction. 844 replacementInstruction = switchInstruction; 845 } 846 847 if (replacementInstruction != null) 848 { 849 replaceInstruction(clazz, offset, switchInstruction, replacementInstruction); 850 } 851 } 852 853 854 /** 855 * Replaces the given instruction by an infinite loop. 856 */ 857 private void replaceByInfiniteLoop(Clazz clazz, 858 int offset, 859 Instruction instruction) 860 { 861 // Replace the instruction by an infinite loop. 862 Instruction replacementInstruction = 863 new BranchInstruction(InstructionConstants.OP_GOTO, 0); 864 865 if (DEBUG) System.out.println(" Replacing unreachable instruction by infinite loop "+replacementInstruction.toString(offset)); 866 867 codeAttributeEditor.replaceInstruction(offset, replacementInstruction); 868 869 // Visit the instruction, if required. 870 if (extraInstructionVisitor != null) 871 { 872 // Note: we're not passing the right arguments for now, knowing that 873 // they aren't used anyway. 874 instruction.accept(clazz, null, null, offset, extraInstructionVisitor); 875 } 876 } 877 878 879 /** 880 * Replaces the instruction at a given offset by a given push instruction. 881 */ 882 private void replaceInstruction(Clazz clazz, 883 int offset, 884 Instruction instruction, 885 Instruction replacementInstruction) 886 { 887 // Pop unneeded stack entries if necessary. 888 int popCount = 889 instruction.stackPopCount(clazz) - 890 replacementInstruction.stackPopCount(clazz); 891 892 insertPopInstructions(offset, popCount); 893 894 if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstruction.toString()+(popCount == 0 ? "" : " ("+popCount+" pops)")); 895 896 codeAttributeEditor.replaceInstruction(offset, replacementInstruction); 897 898 // Visit the instruction, if required. 899 if (extraInstructionVisitor != null) 900 { 901 // Note: we're not passing the right arguments for now, knowing that 902 // they aren't used anyway. 903 instruction.accept(clazz, null, null, offset, extraInstructionVisitor); 904 } 905 } 906 907 908 /** 909 * Pops the given number of stack entries before the instruction at the 910 * given offset. 911 */ 912 private void insertPopInstructions(int offset, int popCount) 913 { 914 switch (popCount) 915 { 916 case 0: 917 { 918 break; 919 } 920 case 1: 921 { 922 // Insert a single pop instruction. 923 Instruction popInstruction = 924 new SimpleInstruction(InstructionConstants.OP_POP); 925 926 codeAttributeEditor.insertBeforeInstruction(offset, 927 popInstruction); 928 break; 929 } 930 case 2: 931 { 932 // Insert a single pop2 instruction. 933 Instruction popInstruction = 934 new SimpleInstruction(InstructionConstants.OP_POP2); 935 936 codeAttributeEditor.insertBeforeInstruction(offset, 937 popInstruction); 938 break; 939 } 940 default: 941 { 942 // Insert the specified number of pop instructions. 943 Instruction[] popInstructions = 944 new Instruction[popCount / 2 + popCount % 2]; 945 946 Instruction popInstruction = 947 new SimpleInstruction(InstructionConstants.OP_POP2); 948 949 for (int index = 0; index < popCount / 2; index++) 950 { 951 popInstructions[index] = popInstruction; 952 } 953 954 if (popCount % 2 == 1) 955 { 956 popInstruction = 957 new SimpleInstruction(InstructionConstants.OP_POP); 958 959 popInstructions[popCount / 2] = popInstruction; 960 } 961 962 codeAttributeEditor.insertBeforeInstruction(offset, 963 popInstructions); 964 break; 965 } 966 } 967 } 968} 969