1 21 package proguard.classfile.editor; 22 23 import proguard.classfile.*; 24 import proguard.classfile.visitor.ClassPrinter; 25 import proguard.classfile.attribute.*; 26 import proguard.classfile.attribute.preverification.*; 27 import proguard.classfile.attribute.preverification.visitor.*; 28 import proguard.classfile.attribute.visitor.*; 29 import proguard.classfile.instruction.*; 30 import proguard.classfile.instruction.visitor.InstructionVisitor; 31 import proguard.classfile.util.SimplifiedVisitor; 32 33 39 public class CodeAttributeEditor 40 extends SimplifiedVisitor 41 implements AttributeVisitor, 42 InstructionVisitor, 43 ExceptionInfoVisitor, 44 StackMapFrameVisitor, 45 VerificationTypeVisitor, 46 LineNumberInfoVisitor, 47 LocalVariableInfoVisitor, 48 LocalVariableTypeInfoVisitor 49 { 50 private static final boolean DEBUG = false; 52 55 56 57 private int codeLength; 58 private boolean modified; 59 private boolean simple; 60 61 public Instruction[] preInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH]; 62 public Instruction[] replacements = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH]; 63 public Instruction[] postInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH]; 64 private boolean[] deleted = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; 65 66 private int[] instructionOffsetMap = new int[ClassConstants.TYPICAL_CODE_LENGTH]; 67 private int newOffset; 68 private boolean lengthIncreased; 69 70 private int expectedStackMapFrameOffset; 71 72 private StackSizeUpdater stackSizeUpdater = new StackSizeUpdater(); 73 private VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater(); 74 private InstructionWriter instructionWriter = new InstructionWriter(); 75 76 77 81 public void reset(int codeLength) 82 { 83 this.codeLength = codeLength; 84 85 if (preInsertions.length < codeLength) 87 { 88 preInsertions = new Instruction[codeLength]; 89 replacements = new Instruction[codeLength]; 90 postInsertions = new Instruction[codeLength]; 91 deleted = new boolean[codeLength]; 92 } 93 else 94 { 95 for (int index = 0; index < codeLength; index++) 96 { 97 preInsertions[index] = null; 98 replacements[index] = null; 99 postInsertions[index] = null; 100 deleted[index] = false; 101 } 102 } 103 104 modified = false; 105 simple = true; 106 107 } 108 109 110 116 public void insertBeforeInstruction(int instructionOffset, Instruction instruction) 117 { 118 if (DEBUG) 119 { 120 System.out.println("Inserting instruction before ["+instructionOffset+"]: "+instruction); 121 } 122 123 if (instructionOffset < 0 || 124 instructionOffset >= codeLength) 125 { 126 throw new IllegalArgumentException ("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); 127 } 128 129 preInsertions[instructionOffset] = instruction; 130 131 modified = true; 132 simple = false; 133 134 } 135 136 137 143 public void replaceInstruction(int instructionOffset, Instruction instruction) 144 { 145 if (instructionOffset < 0 || 146 instructionOffset >= codeLength) 147 { 148 throw new IllegalArgumentException ("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); 149 } 150 151 replacements[instructionOffset] = instruction; 152 153 modified = true; 154 } 155 156 157 163 public void insertAfterInstruction(int instructionOffset, Instruction instruction) 164 { 165 if (instructionOffset < 0 || 166 instructionOffset >= codeLength) 167 { 168 throw new IllegalArgumentException ("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); 169 } 170 171 postInsertions[instructionOffset] = instruction; 172 173 modified = true; 174 simple = false; 175 } 176 177 178 182 public void deleteInstruction(int instructionOffset) 183 { 184 if (instructionOffset < 0 || 185 instructionOffset >= codeLength) 186 { 187 throw new IllegalArgumentException ("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); 188 } 189 190 deleted[instructionOffset] = true; 191 192 modified = true; 193 simple = false; 194 } 195 196 197 201 public void undeleteInstruction(int instructionOffset) 202 { 203 if (instructionOffset < 0 || 204 instructionOffset >= codeLength) 205 { 206 throw new IllegalArgumentException ("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); 207 } 208 209 deleted[instructionOffset] = false; 210 } 211 212 213 217 public boolean isModified(int instructionOffset) 218 { 219 return preInsertions[instructionOffset] != null || 220 replacements[instructionOffset] != null || 221 postInsertions[instructionOffset] != null || 222 deleted[instructionOffset]; 223 } 224 225 226 228 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 229 230 231 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 232 { 233 237 try 240 { 241 visitCodeAttribute0(clazz, method, codeAttribute); 243 } 244 catch (RuntimeException ex) 245 { 246 System.err.println("Unexpected error while editing code:"); 247 System.err.println(" Class = ["+clazz.getName()+"]"); 248 System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); 249 System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); 250 251 throw ex; 252 } 253 } 254 255 256 public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) 257 { 258 if (DEBUG) 259 { 260 System.out.println("CodeAttributeEditor: ["+clazz.getName()+"."+method.getName(clazz)+"]"); 261 } 262 263 if (!modified) 265 { 266 return; 267 } 268 269 if (canPerformSimpleReplacements(codeAttribute)) 271 { 272 performSimpleReplacements(codeAttribute); 274 275 stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); 277 variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); 278 } 279 else 280 { 281 codeAttribute.u4codeLength = 283 updateInstructions(clazz, method, codeAttribute); 284 285 codeAttribute.exceptionsAccept(clazz, method, this); 287 288 codeAttribute.u2exceptionTableLength = 290 removeEmptyExceptions(codeAttribute.exceptionTable, 291 codeAttribute.u2exceptionTableLength); 292 293 stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); 295 variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); 296 297 codeAttribute.attributesAccept(clazz, method, this); 299 300 instructionWriter.visitCodeAttribute(clazz, method, codeAttribute); 302 } 303 } 304 305 306 public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) 307 { 308 expectedStackMapFrameOffset = -1; 310 stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); 311 } 312 313 314 public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) 315 { 316 expectedStackMapFrameOffset = 0; 318 stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); 319 } 320 321 322 public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) 323 { 324 lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this); 326 327 lineNumberTableAttribute.u2lineNumberTableLength = 329 removeEmptyLineNumbers(lineNumberTableAttribute.lineNumberTable, 330 lineNumberTableAttribute.u2lineNumberTableLength, 331 codeAttribute.u4codeLength); 332 } 333 334 335 public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) 336 { 337 localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 339 340 localVariableTableAttribute.u2localVariableTableLength = 342 removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable, 343 localVariableTableAttribute.u2localVariableTableLength, 344 codeAttribute.u2maxLocals); 345 } 346 347 348 public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) 349 { 350 localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 352 353 localVariableTypeTableAttribute.u2localVariableTypeTableLength = 355 removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable, 356 localVariableTypeTableAttribute.u2localVariableTypeTableLength, 357 codeAttribute.u2maxLocals); 358 } 359 360 361 367 private boolean canPerformSimpleReplacements(CodeAttribute codeAttribute) 368 { 369 if (!simple) 370 { 371 return false; 372 } 373 374 byte[] code = codeAttribute.code; 375 int codeLength = codeAttribute.u4codeLength; 376 377 for (int offset = 0; offset < codeLength; offset++) 379 { 380 Instruction replacementInstruction = replacements[offset]; 383 if (replacementInstruction != null && 384 replacementInstruction.length(offset) != 385 InstructionFactory.create(code, offset).length(offset)) 386 { 387 return false; 388 } 389 } 390 391 return true; 392 } 393 394 395 399 private void performSimpleReplacements(CodeAttribute codeAttribute) 400 { 401 int codeLength = codeAttribute.u4codeLength; 402 403 for (int offset = 0; offset < codeLength; offset++) 405 { 406 Instruction replacementInstruction = replacements[offset]; 409 if (replacementInstruction != null) 410 { 411 replacementInstruction.write(codeAttribute, offset); 412 } 413 } 414 } 415 416 417 424 private int updateInstructions(Clazz clazz, 425 Method method, 426 CodeAttribute codeAttribute) 427 { 428 byte[] oldCode = codeAttribute.code; 429 int oldLength = codeAttribute.u4codeLength; 430 431 if (instructionOffsetMap == null || 433 instructionOffsetMap.length < oldLength + 1) 434 { 435 instructionOffsetMap = new int[oldLength + 1]; 436 } 437 438 int newLength = mapInstructions(oldCode, 440 oldLength); 441 442 if (lengthIncreased) 444 { 445 codeAttribute.code = new byte[newLength]; 446 } 447 448 instructionWriter.reset(newLength); 450 451 moveInstructions(clazz, 453 method, 454 codeAttribute, 455 oldCode, 456 oldLength); 457 458 return newLength; 460 } 461 462 463 469 private int mapInstructions(byte[] oldCode, int oldLength) 470 { 471 newOffset = 0; 473 lengthIncreased = false; 474 475 int oldOffset = 0; 476 do 477 { 478 Instruction instruction = InstructionFactory.create(oldCode, oldOffset); 480 481 mapInstruction(oldOffset, instruction); 483 484 oldOffset += instruction.length(oldOffset); 485 486 if (newOffset > oldOffset) 487 { 488 lengthIncreased = true; 489 } 490 } 491 while (oldOffset < oldLength); 492 493 instructionOffsetMap[oldOffset] = newOffset; 495 496 return newOffset; 497 } 498 499 500 505 private void mapInstruction(int oldOffset, 506 Instruction instruction) 507 { 508 instructionOffsetMap[oldOffset] = newOffset; 509 510 Instruction preInstruction = preInsertions[oldOffset]; 512 if (preInstruction != null) 513 { 514 newOffset += preInstruction.length(newOffset); 515 } 516 517 Instruction replacementInstruction = replacements[oldOffset]; 520 if (replacementInstruction != null) 521 { 522 newOffset += replacementInstruction.length(newOffset); 523 } 524 else if (!deleted[oldOffset]) 525 { 526 newOffset += instruction.length(newOffset); 529 } 530 531 Instruction postInstruction = postInsertions[oldOffset]; 533 if (postInstruction != null) 534 { 535 newOffset += postInstruction.length(newOffset); 536 } 537 } 538 539 540 548 private void moveInstructions(Clazz clazz, 549 Method method, 550 CodeAttribute codeAttribute, 551 byte[] oldCode, 552 int oldLength) 553 { 554 newOffset = 0; 556 557 int oldOffset = 0; 558 do 559 { 560 Instruction instruction = InstructionFactory.create(oldCode, oldOffset); 562 563 moveInstruction(clazz, 565 method, 566 codeAttribute, 567 oldOffset, 568 instruction); 569 570 oldOffset += instruction.length(oldOffset); 571 } 572 while (oldOffset < oldLength); 573 } 574 575 576 584 private void moveInstruction(Clazz clazz, 585 Method method, 586 CodeAttribute codeAttribute, 587 int oldOffset, 588 Instruction instruction) 589 { 590 Instruction preInstruction = preInsertions[oldOffset]; 592 if (preInstruction != null) 593 { 594 preInstruction.accept(clazz, method, codeAttribute, oldOffset, this); 596 597 if (DEBUG) 598 { 599 System.out.println(" Pre-inserted "+preInstruction.toString(newOffset)); 600 } 601 602 newOffset += preInstruction.length(newOffset); 603 } 604 605 Instruction replacementInstruction = replacements[oldOffset]; 608 if (replacementInstruction != null) 609 { 610 replacementInstruction.accept(clazz, method, codeAttribute, oldOffset, this); 612 613 if (DEBUG) 614 { 615 System.out.println(" Replaced "+replacementInstruction.toString(newOffset)); 616 } 617 618 newOffset += replacementInstruction.length(newOffset); 619 } 620 else if (!deleted[oldOffset]) 621 { 622 instruction.accept(clazz, method, codeAttribute, oldOffset, this); 624 625 if (DEBUG) 626 { 627 System.out.println(" Copied "+instruction.toString(newOffset)); 628 } 629 630 newOffset += instruction.length(newOffset); 631 } 632 633 Instruction postInstruction = postInsertions[oldOffset]; 635 if (postInstruction != null) 636 { 637 postInstruction.accept(clazz, method, codeAttribute, oldOffset, this); 639 640 if (DEBUG) 641 { 642 System.out.println(" Post-inserted "+postInstruction.toString(newOffset)); 643 } 644 645 newOffset += postInstruction.length(newOffset); 646 } 647 } 648 649 650 652 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 653 { 654 instructionWriter.visitSimpleInstruction(clazz, 656 method, 657 codeAttribute, 658 newOffset, 659 simpleInstruction); 660 } 661 662 663 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 664 { 665 instructionWriter.visitConstantInstruction(clazz, 667 method, 668 codeAttribute, 669 newOffset, 670 constantInstruction); 671 } 672 673 674 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 675 { 676 instructionWriter.visitVariableInstruction(clazz, 678 method, 679 codeAttribute, 680 newOffset, 681 variableInstruction); 682 } 683 684 685 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 686 { 687 branchInstruction.branchOffset = remapBranchOffset(offset, 689 branchInstruction.branchOffset); 690 691 instructionWriter.visitBranchInstruction(clazz, 693 method, 694 codeAttribute, 695 newOffset, 696 branchInstruction); 697 } 698 699 700 public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) 701 { 702 tableSwitchInstruction.defaultOffset = remapBranchOffset(offset, 704 tableSwitchInstruction.defaultOffset); 705 706 remapJumpOffsets(offset, 708 tableSwitchInstruction.jumpOffsets); 709 710 instructionWriter.visitTableSwitchInstruction(clazz, 712 method, 713 codeAttribute, 714 newOffset, 715 tableSwitchInstruction); 716 } 717 718 719 public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) 720 { 721 lookUpSwitchInstruction.defaultOffset = remapBranchOffset(offset, 723 lookUpSwitchInstruction.defaultOffset); 724 725 remapJumpOffsets(offset, 727 lookUpSwitchInstruction.jumpOffsets); 728 729 instructionWriter.visitLookUpSwitchInstruction(clazz, 731 method, 732 codeAttribute, 733 newOffset, 734 lookUpSwitchInstruction); 735 } 736 737 738 740 public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) 741 { 742 exceptionInfo.u2startPC = remapInstructionOffset(exceptionInfo.u2startPC); 745 exceptionInfo.u2endPC = remapInstructionOffset(exceptionInfo.u2endPC); 746 exceptionInfo.u2handlerPC = remapInstructionOffset(exceptionInfo.u2handlerPC); 747 } 748 749 750 752 public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) 753 { 754 int stackMapFrameOffset = remapInstructionOffset(offset); 756 757 int offsetDelta = stackMapFrameOffset; 758 759 if (expectedStackMapFrameOffset >= 0) 762 { 763 offsetDelta -= expectedStackMapFrameOffset; 764 765 expectedStackMapFrameOffset = stackMapFrameOffset + 1; 766 } 767 768 stackMapFrame.u2offsetDelta = offsetDelta; 769 } 770 771 772 public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame) 773 { 774 visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame); 776 777 sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this); 779 } 780 781 782 public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) 783 { 784 visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame); 786 787 moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this); 789 } 790 791 792 public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) 793 { 794 visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame); 796 797 fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this); 799 fullFrame.stackAccept(clazz, method, codeAttribute, offset, this); 800 } 801 802 803 805 public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {} 806 807 808 public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType) 809 { 810 uninitializedType.u2newInstructionOffset = remapInstructionOffset(uninitializedType.u2newInstructionOffset); 812 } 813 814 815 817 public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) 818 { 819 lineNumberInfo.u2startPC = remapInstructionOffset(lineNumberInfo.u2startPC); 821 } 822 823 824 826 public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) 827 { 828 localVariableInfo.u2length = remapBranchOffset(localVariableInfo.u2startPC, 831 localVariableInfo.u2length); 832 localVariableInfo.u2startPC = remapInstructionOffset(localVariableInfo.u2startPC); 833 } 834 835 836 838 public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) 839 { 840 localVariableTypeInfo.u2length = remapBranchOffset(localVariableTypeInfo.u2startPC, 843 localVariableTypeInfo.u2length); 844 localVariableTypeInfo.u2startPC = remapInstructionOffset(localVariableTypeInfo.u2startPC); 845 } 846 847 848 850 853 private void remapJumpOffsets(int offset, int[] jumpOffsets) 854 { 855 for (int index = 0; index < jumpOffsets.length; index++) 856 { 857 jumpOffsets[index] = remapBranchOffset(offset, jumpOffsets[index]); 858 } 859 } 860 861 862 866 private int remapBranchOffset(int offset, int branchOffset) 867 { 868 return remapInstructionOffset(offset + branchOffset) - 869 remapInstructionOffset(offset); 870 } 871 872 873 876 private int remapInstructionOffset(int offset) 877 { 878 if (offset < 0 || 879 offset > codeLength) 880 { 881 throw new IllegalArgumentException ("Invalid instruction offset ["+offset+"] in code with length ["+codeLength+"]"); 882 } 883 884 return instructionOffsetMap[offset]; 885 } 886 887 888 892 private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos, 893 int exceptionInfoCount) 894 { 895 int newIndex = 0; 897 for (int index = 0; index < exceptionInfoCount; index++) 898 { 899 ExceptionInfo exceptionInfo = exceptionInfos[index]; 900 if (exceptionInfo.u2startPC < exceptionInfo.u2endPC) 901 { 902 exceptionInfos[newIndex++] = exceptionInfo; 903 } 904 } 905 906 return newIndex; 907 } 908 909 910 914 private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos, 915 int lineNumberInfoCount, 916 int codeLength) 917 { 918 int newIndex = 0; 920 for (int index = 0; index < lineNumberInfoCount; index++) 921 { 922 LineNumberInfo lineNumberInfo = lineNumberInfos[index]; 923 int startPC = lineNumberInfo.u2startPC; 924 if (startPC < codeLength && 925 (index == 0 || startPC > lineNumberInfos[index-1].u2startPC)) 926 { 927 lineNumberInfos[newIndex++] = lineNumberInfo; 928 } 929 } 930 931 return newIndex; 932 } 933 934 935 939 private int removeEmptyLocalVariables(LocalVariableInfo[] localVariableInfos, 940 int localVariableInfoCount, 941 int maxLocals) 942 { 943 int newIndex = 0; 945 for (int index = 0; index < localVariableInfoCount; index++) 946 { 947 LocalVariableInfo localVariableInfo = localVariableInfos[index]; 948 if (localVariableInfo.u2length > 0 && 949 localVariableInfo.u2index < maxLocals) 950 { 951 localVariableInfos[newIndex++] = localVariableInfo; 952 } 953 } 954 955 return newIndex; 956 } 957 958 959 963 private int removeEmptyLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos, 964 int localVariableTypeInfoCount, 965 int maxLocals) 966 { 967 int newIndex = 0; 969 for (int index = 0; index < localVariableTypeInfoCount; index++) 970 { 971 LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index]; 972 if (localVariableTypeInfo.u2length > 0 && 973 localVariableTypeInfo.u2index < maxLocals) 974 { 975 localVariableTypeInfos[newIndex++] = localVariableTypeInfo; 976 } 977 } 978 979 return newIndex; 980 } 981 } 982 | Popular Tags |