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.classfile.editor; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.*; 25import proguard.classfile.attribute.annotation.*; 26import proguard.classfile.attribute.annotation.target.*; 27import proguard.classfile.attribute.annotation.target.visitor.*; 28import proguard.classfile.attribute.annotation.visitor.TypeAnnotationVisitor; 29import proguard.classfile.attribute.preverification.*; 30import proguard.classfile.attribute.preverification.visitor.*; 31import proguard.classfile.attribute.visitor.*; 32import proguard.classfile.instruction.*; 33import proguard.classfile.instruction.visitor.InstructionVisitor; 34import proguard.classfile.util.SimplifiedVisitor; 35import proguard.util.ArrayUtil; 36 37import java.util.Arrays; 38 39/** 40 * This AttributeVisitor accumulates specified changes to code, and then applies 41 * these accumulated changes to the code attributes that it visits. 42 * 43 * @author Eric Lafortune 44 */ 45public class CodeAttributeEditor 46extends SimplifiedVisitor 47implements AttributeVisitor, 48 InstructionVisitor, 49 ExceptionInfoVisitor, 50 StackMapFrameVisitor, 51 VerificationTypeVisitor, 52 LineNumberInfoVisitor, 53 LocalVariableInfoVisitor, 54 LocalVariableTypeInfoVisitor, 55 TypeAnnotationVisitor, 56 TargetInfoVisitor, 57 LocalVariableTargetElementVisitor 58{ 59 //* 60 private static final boolean DEBUG = false; 61 /*/ 62 public static boolean DEBUG = false; 63 //*/ 64 65 66 private final boolean updateFrameSizes; 67 private final boolean shrinkInstructions; 68 69 private int codeLength; 70 private boolean modified; 71 private boolean simple; 72 73 /*private*/public Instruction[] preInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH]; 74 /*private*/public Instruction[] replacements = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH]; 75 /*private*/public Instruction[] postInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH]; 76 /*private*/public boolean[] deleted = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; 77 78 private int[] newInstructionOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH]; 79 private int newOffset; 80 private boolean lengthIncreased; 81 82 private int expectedStackMapFrameOffset; 83 84 private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater(); 85 private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater(); 86 private final InstructionWriter instructionWriter = new InstructionWriter(); 87 88 89 /** 90 * Creates a new CodeAttributeEditor that automatically updates frame 91 * sizes and shrinks instructions. 92 */ 93 public CodeAttributeEditor() 94 { 95 this(true, true); 96 } 97 98 99 /** 100 * Creates a new CodeAttributeEditor. 101 * @param updateFrameSizes specifies whether frame sizes of edited code 102 * should be updated. 103 * @param shrinkInstructions specifies whether added instructions should 104 * automatically be shrunk before being written. 105 */ 106 public CodeAttributeEditor(boolean updateFrameSizes, 107 boolean shrinkInstructions) 108 { 109 this.updateFrameSizes = updateFrameSizes; 110 this.shrinkInstructions = shrinkInstructions; 111 } 112 113 114 /** 115 * Resets the accumulated code changes. 116 * @param codeLength the length of the code that will be edited next. 117 */ 118 public void reset(int codeLength) 119 { 120 // Try to reuse the previous arrays. 121 if (preInsertions.length < codeLength) 122 { 123 preInsertions = new Instruction[codeLength]; 124 replacements = new Instruction[codeLength]; 125 postInsertions = new Instruction[codeLength]; 126 deleted = new boolean[codeLength]; 127 } 128 else 129 { 130 Arrays.fill(preInsertions, 0, codeLength, null); 131 Arrays.fill(replacements, 0, codeLength, null); 132 Arrays.fill(postInsertions, 0, codeLength, null); 133 Arrays.fill(deleted, 0, codeLength, false); 134 } 135 136 this.codeLength = codeLength; 137 138 modified = false; 139 simple = true; 140 } 141 142 143 /** 144 * Extends the size of the accumulated code changes. 145 * @param codeLength the length of the code that will be edited next. 146 */ 147 public void extend(int codeLength) 148 { 149 // Try to reuse the previous arrays. 150 if (preInsertions.length < codeLength) 151 { 152 preInsertions = (Instruction[])ArrayUtil.extendArray(preInsertions, codeLength); 153 replacements = (Instruction[])ArrayUtil.extendArray(replacements, codeLength); 154 postInsertions = (Instruction[])ArrayUtil.extendArray(postInsertions, codeLength); 155 deleted = ArrayUtil.extendArray(deleted, codeLength); 156 } 157 else 158 { 159 Arrays.fill(preInsertions, this.codeLength, codeLength, null); 160 Arrays.fill(replacements, this.codeLength, codeLength, null); 161 Arrays.fill(postInsertions, this.codeLength, codeLength, null); 162 Arrays.fill(deleted, this.codeLength, codeLength, false); 163 } 164 165 this.codeLength = codeLength; 166 } 167 168 169 /** 170 * Remembers to place the given instruction right before the instruction 171 * at the given offset. 172 * @param instructionOffset the offset of the instruction. 173 * @param instruction the new instruction. 174 */ 175 public void insertBeforeInstruction(int instructionOffset, Instruction instruction) 176 { 177 if (instructionOffset < 0 || 178 instructionOffset >= codeLength) 179 { 180 throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); 181 } 182 183 preInsertions[instructionOffset] = shrinkInstructions ? 184 instruction.shrink() : 185 instruction; 186 187 modified = true; 188 simple = false; 189 } 190 191 192 /** 193 * Remembers to place the given instructions right before the instruction 194 * at the given offset. 195 * @param instructionOffset the offset of the instruction. 196 * @param instructions the new instructions. 197 */ 198 public void insertBeforeInstruction(int instructionOffset, Instruction[] instructions) 199 { 200 if (instructionOffset < 0 || 201 instructionOffset >= codeLength) 202 { 203 throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); 204 } 205 206 CompositeInstruction instruction = 207 new CompositeInstruction(instructions); 208 209 preInsertions[instructionOffset] = shrinkInstructions ? 210 instruction.shrink() : 211 instruction; 212 213 modified = true; 214 simple = false; 215 } 216 217 218 /** 219 * Remembers to replace the instruction at the given offset by the given 220 * instruction. 221 * @param instructionOffset the offset of the instruction to be replaced. 222 * @param instruction the new instruction. 223 */ 224 public void replaceInstruction(int instructionOffset, Instruction instruction) 225 { 226 if (instructionOffset < 0 || 227 instructionOffset >= codeLength) 228 { 229 throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); 230 } 231 232 replacements[instructionOffset] = shrinkInstructions ? 233 instruction.shrink() : 234 instruction; 235 236 modified = true; 237 } 238 239 240 /** 241 * Remembers to replace the instruction at the given offset by the given 242 * instructions. 243 * @param instructionOffset the offset of the instruction to be replaced. 244 * @param instructions the new instructions. 245 */ 246 public void replaceInstruction(int instructionOffset, Instruction[] instructions) 247 { 248 if (instructionOffset < 0 || 249 instructionOffset >= codeLength) 250 { 251 throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); 252 } 253 254 CompositeInstruction instruction = 255 new CompositeInstruction(instructions); 256 257 replacements[instructionOffset] = shrinkInstructions ? 258 instruction.shrink() : 259 instruction; 260 261 modified = true; 262 } 263 264 265 /** 266 * Remembers to place the given instruction right after the instruction 267 * at the given offset. 268 * @param instructionOffset the offset of the instruction. 269 * @param instruction the new instruction. 270 */ 271 public void insertAfterInstruction(int instructionOffset, Instruction instruction) 272 { 273 if (instructionOffset < 0 || 274 instructionOffset >= codeLength) 275 { 276 throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); 277 } 278 279 postInsertions[instructionOffset] = shrinkInstructions ? 280 instruction.shrink() : 281 instruction; 282 283 modified = true; 284 simple = false; 285 } 286 287 288 /** 289 * Remembers to place the given instructions right after the instruction 290 * at the given offset. 291 * @param instructionOffset the offset of the instruction. 292 * @param instructions the new instructions. 293 */ 294 public void insertAfterInstruction(int instructionOffset, Instruction[] instructions) 295 { 296 if (instructionOffset < 0 || 297 instructionOffset >= codeLength) 298 { 299 throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); 300 } 301 302 CompositeInstruction instruction = 303 new CompositeInstruction(instructions); 304 305 postInsertions[instructionOffset] = shrinkInstructions ? 306 instruction.shrink() : 307 instruction; 308 309 modified = true; 310 simple = false; 311 } 312 313 314 /** 315 * Remembers to delete the instruction at the given offset. 316 * @param instructionOffset the offset of the instruction to be deleted. 317 */ 318 public void deleteInstruction(int instructionOffset) 319 { 320 if (instructionOffset < 0 || 321 instructionOffset >= codeLength) 322 { 323 throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); 324 } 325 326 deleted[instructionOffset] = true; 327 328 modified = true; 329 simple = false; 330 } 331 332 333 /** 334 * Remembers not to delete the instruction at the given offset. 335 * @param instructionOffset the offset of the instruction not to be deleted. 336 */ 337 public void undeleteInstruction(int instructionOffset) 338 { 339 if (instructionOffset < 0 || 340 instructionOffset >= codeLength) 341 { 342 throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); 343 } 344 345 deleted[instructionOffset] = false; 346 } 347 348 349 /** 350 * Clears all modifications of the instruction at the given offset. 351 * @param instructionOffset the offset of the instruction to be deleted. 352 */ 353 public void clearModifications(int instructionOffset) 354 { 355 if (instructionOffset < 0 || 356 instructionOffset >= codeLength) 357 { 358 throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); 359 } 360 361 preInsertions[instructionOffset] = null; 362 replacements[instructionOffset] = null; 363 postInsertions[instructionOffset] = null; 364 deleted[instructionOffset] = false; 365 } 366 367 368 /** 369 * Returns whether the code has been modified in any way. 370 */ 371 public boolean isModified() 372 { 373 return modified; 374 } 375 376 377 /** 378 * Returns whether the instruction at the given offset has been modified 379 * in any way. 380 */ 381 public boolean isModified(int instructionOffset) 382 { 383 return preInsertions[instructionOffset] != null || 384 replacements[instructionOffset] != null || 385 postInsertions[instructionOffset] != null || 386 deleted[instructionOffset]; 387 } 388 389 390 // Implementations for AttributeVisitor. 391 392 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 393 394 395 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 396 { 397// DEBUG = 398// clazz.getName().equals("abc/Def") && 399// method.getName(clazz).equals("abc"); 400 401 // TODO: Remove this when the code has stabilized. 402 // Catch any unexpected exceptions from the actual visiting method. 403 try 404 { 405 // Process the code. 406 visitCodeAttribute0(clazz, method, codeAttribute); 407 } 408 catch (RuntimeException ex) 409 { 410 System.err.println("Unexpected error while editing code:"); 411 System.err.println(" Class = ["+clazz.getName()+"]"); 412 System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); 413 System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); 414 415 throw ex; 416 } 417 } 418 419 420 public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) 421 { 422 // Do we have to update the code? 423 if (modified) 424 { 425 if (DEBUG) 426 { 427 System.out.println("CodeAttributeEditor: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); 428 } 429 430 // Can we perform a faster simple replacement of instructions? 431 if (canPerformSimpleReplacements(codeAttribute)) 432 { 433 if (DEBUG) 434 { 435 System.out.println(" Simple editing"); 436 } 437 438 // Simply overwrite the instructions. 439 performSimpleReplacements(codeAttribute); 440 } 441 else 442 { 443 if (DEBUG) 444 { 445 System.out.println(" Full editing"); 446 } 447 448 // Move and remap the instructions. 449 codeAttribute.u4codeLength = 450 updateInstructions(clazz, method, codeAttribute); 451 452 // Update the exception table. 453 codeAttribute.exceptionsAccept(clazz, method, this); 454 455 // Remove exceptions with empty code blocks. 456 codeAttribute.u2exceptionTableLength = 457 removeEmptyExceptions(codeAttribute.exceptionTable, 458 codeAttribute.u2exceptionTableLength); 459 460 // Update the line number table and the local variable tables. 461 codeAttribute.attributesAccept(clazz, method, this); 462 } 463 464 // Make sure instructions are widened if necessary. 465 instructionWriter.visitCodeAttribute(clazz, method, codeAttribute); 466 } 467 468 // Update the maximum stack size and local variable frame size. 469 if (updateFrameSizes) 470 { 471 stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); 472 variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); 473 } 474 } 475 476 477 public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) 478 { 479 // Update all stack map entries. 480 expectedStackMapFrameOffset = -1; 481 stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); 482 } 483 484 485 public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) 486 { 487 // Update all stack map table entries. 488 expectedStackMapFrameOffset = 0; 489 stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); 490 } 491 492 493 public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) 494 { 495 // Update all line number table entries. 496 lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this); 497 498 // Remove line numbers with empty code blocks. 499 lineNumberTableAttribute.u2lineNumberTableLength = 500 removeEmptyLineNumbers(lineNumberTableAttribute.lineNumberTable, 501 lineNumberTableAttribute.u2lineNumberTableLength, 502 codeAttribute.u4codeLength); 503 } 504 505 506 public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) 507 { 508 // Update all local variable table entries. 509 localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 510 } 511 512 513 public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) 514 { 515 // Update all local variable table entries. 516 localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 517 } 518 519 520 public void visitAnyTypeAnnotationsAttribute(Clazz clazz, TypeAnnotationsAttribute typeAnnotationsAttribute) 521 { 522 typeAnnotationsAttribute.typeAnnotationsAccept(clazz, this); 523 } 524 525 526 /** 527 * Checks if it is possible to modifies the given code without having to 528 * update any offsets. 529 * @param codeAttribute the code to be changed. 530 * @return the new code length. 531 */ 532 private boolean canPerformSimpleReplacements(CodeAttribute codeAttribute) 533 { 534 if (!simple) 535 { 536 return false; 537 } 538 539 byte[] code = codeAttribute.code; 540 int codeLength = codeAttribute.u4codeLength; 541 542 // Go over all replacement instructions. 543 for (int offset = 0; offset < codeLength; offset++) 544 { 545 // Check if the replacement instruction, if any, has a different 546 // length than the original instruction. 547 Instruction replacementInstruction = replacements[offset]; 548 if (replacementInstruction != null && 549 replacementInstruction.length(offset) != 550 InstructionFactory.create(code, offset).length(offset)) 551 { 552 return false; 553 } 554 } 555 556 return true; 557 } 558 559 560 /** 561 * Modifies the given code without updating any offsets. 562 * @param codeAttribute the code to be changed. 563 */ 564 private void performSimpleReplacements(CodeAttribute codeAttribute) 565 { 566 int codeLength = codeAttribute.u4codeLength; 567 568 // Go over all replacement instructions. 569 for (int offset = 0; offset < codeLength; offset++) 570 { 571 // Overwrite the original instruction with the replacement 572 // instruction if any. 573 Instruction replacementInstruction = replacements[offset]; 574 if (replacementInstruction != null) 575 { 576 replacementInstruction.write(codeAttribute, offset); 577 578 if (DEBUG) 579 { 580 System.out.println(" Replaced "+replacementInstruction.toString(offset)); 581 } 582 } 583 } 584 } 585 586 587 /** 588 * Modifies the given code based on the previously specified changes. 589 * @param clazz the class file of the code to be changed. 590 * @param method the method of the code to be changed. 591 * @param codeAttribute the code to be changed. 592 * @return the new code length. 593 */ 594 private int updateInstructions(Clazz clazz, 595 Method method, 596 CodeAttribute codeAttribute) 597 { 598 byte[] oldCode = codeAttribute.code; 599 int oldLength = codeAttribute.u4codeLength; 600 601 // Make sure there is a sufficiently large instruction offset map. 602 if (newInstructionOffsets.length < oldLength + 1) 603 { 604 newInstructionOffsets = new int[oldLength + 1]; 605 } 606 607 // Fill out the instruction offset map. 608 int newLength = mapInstructions(oldCode, 609 oldLength); 610 611 // Create a new code array if necessary. 612 if (lengthIncreased) 613 { 614 codeAttribute.code = new byte[newLength]; 615 } 616 617 // Prepare for possible widening of instructions. 618 instructionWriter.reset(newLength); 619 620 // Move the instructions into the new code array. 621 moveInstructions(clazz, 622 method, 623 codeAttribute, 624 oldCode, 625 oldLength); 626 627 // We can return the new length. 628 return newLength; 629 } 630 631 632 /** 633 * Fills out the instruction offset map for the given code block. 634 * @param oldCode the instructions to be moved. 635 * @param oldLength the code length. 636 * @return the new code length. 637 */ 638 private int mapInstructions(byte[] oldCode, int oldLength) 639 { 640 // Start mapping instructions at the beginning. 641 newOffset = 0; 642 lengthIncreased = false; 643 644 int oldOffset = 0; 645 do 646 { 647 // Get the next instruction. 648 Instruction instruction = InstructionFactory.create(oldCode, oldOffset); 649 650 // Compute the mapping of the instruction. 651 mapInstruction(oldOffset, instruction); 652 653 oldOffset += instruction.length(oldOffset); 654 655 if (newOffset > oldOffset) 656 { 657 lengthIncreased = true; 658 } 659 } 660 while (oldOffset < oldLength); 661 662 // Also add an entry for the first offset after the code. 663 newInstructionOffsets[oldOffset] = newOffset; 664 665 return newOffset; 666 } 667 668 669 /** 670 * Fills out the instruction offset map for the given instruction. 671 * @param oldOffset the instruction's old offset. 672 * @param instruction the instruction to be moved. 673 */ 674 private void mapInstruction(int oldOffset, 675 Instruction instruction) 676 { 677 newInstructionOffsets[oldOffset] = newOffset; 678 679 // Account for the pre-inserted instruction, if any. 680 Instruction preInstruction = preInsertions[oldOffset]; 681 if (preInstruction != null) 682 { 683 newOffset += preInstruction.length(newOffset); 684 } 685 686 // Account for the replacement instruction, or for the current 687 // instruction, if it shouldn't be deleted. 688 Instruction replacementInstruction = replacements[oldOffset]; 689 if (replacementInstruction != null) 690 { 691 newOffset += replacementInstruction.length(newOffset); 692 } 693 else if (!deleted[oldOffset]) 694 { 695 // Note that the instruction's length may change at its new offset, 696 // e.g. if it is a switch instruction. 697 newOffset += instruction.length(newOffset); 698 } 699 700 // Account for the post-inserted instruction, if any. 701 Instruction postInstruction = postInsertions[oldOffset]; 702 if (postInstruction != null) 703 { 704 newOffset += postInstruction.length(newOffset); 705 } 706 } 707 708 709 /** 710 * Moves the given code block to the new offsets. 711 * @param clazz the class file of the code to be changed. 712 * @param method the method of the code to be changed. 713 * @param codeAttribute the code to be changed. 714 * @param oldCode the original code to be moved. 715 * @param oldLength the original code length. 716 */ 717 private void moveInstructions(Clazz clazz, 718 Method method, 719 CodeAttribute codeAttribute, 720 byte[] oldCode, 721 int oldLength) 722 { 723 // Start writing instructions at the beginning. 724 newOffset = 0; 725 726 int oldOffset = 0; 727 do 728 { 729 // Get the next instruction. 730 Instruction instruction = InstructionFactory.create(oldCode, oldOffset); 731 732 // Move the instruction to its new offset. 733 moveInstruction(clazz, 734 method, 735 codeAttribute, 736 oldOffset, 737 instruction); 738 739 oldOffset += instruction.length(oldOffset); 740 } 741 while (oldOffset < oldLength); 742 } 743 744 745 /** 746 * Moves the given instruction to its new offset. 747 * @param clazz the class file of the code to be changed. 748 * @param method the method of the code to be changed. 749 * @param codeAttribute the code to be changed. 750 * @param oldOffset the original instruction offset. 751 * @param instruction the original instruction. 752 */ 753 private void moveInstruction(Clazz clazz, 754 Method method, 755 CodeAttribute codeAttribute, 756 int oldOffset, 757 Instruction instruction) 758 { 759 // Update and insert the pre-inserted instruction, if any. 760 Instruction preInstruction = preInsertions[oldOffset]; 761 if (preInstruction != null) 762 { 763 if (DEBUG) 764 { 765 System.out.println(" Pre-inserted ["+oldOffset+"] -> "+preInstruction.toString(newOffset)); 766 } 767 768 // Update the instruction. 769 preInstruction.accept(clazz, method, codeAttribute, oldOffset, this); 770 } 771 772 // Update and insert the replacement instruction, or the current 773 // instruction, if it shouldn't be deleted. 774 Instruction replacementInstruction = replacements[oldOffset]; 775 if (replacementInstruction != null) 776 { 777 if (DEBUG) 778 { 779 System.out.println(" Replaced ["+oldOffset+"] -> "+replacementInstruction.toString(newOffset)); 780 } 781 782 // Update the instruction. 783 replacementInstruction.accept(clazz, method, codeAttribute, oldOffset, this); 784 } 785 else if (!deleted[oldOffset]) 786 { 787 if (DEBUG) 788 { 789 System.out.println(" Copied ["+oldOffset+"] -> "+instruction.toString(newOffset)); 790 } 791 792 // Update the instruction. 793 instruction.accept(clazz, method, codeAttribute, oldOffset, this); 794 } 795 796 // Update and insert the post-inserted instruction, if any. 797 Instruction postInstruction = postInsertions[oldOffset]; 798 if (postInstruction != null) 799 { 800 if (DEBUG) 801 { 802 System.out.println(" Post-inserted ["+oldOffset+"] -> "+postInstruction.toString(newOffset)); 803 } 804 805 // Update the instruction. 806 postInstruction.accept(clazz, method, codeAttribute, oldOffset, this); 807 } 808 } 809 810 811 // Implementations for InstructionVisitor. 812 813 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 814 { 815 // Write out the instruction. 816 instructionWriter.visitSimpleInstruction(clazz, 817 method, 818 codeAttribute, 819 newOffset, 820 simpleInstruction); 821 822 newOffset += simpleInstruction.length(newOffset); 823 } 824 825 826 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 827 { 828 // Write out the instruction. 829 instructionWriter.visitConstantInstruction(clazz, 830 method, 831 codeAttribute, 832 newOffset, 833 constantInstruction); 834 835 newOffset += constantInstruction.length(newOffset); 836 } 837 838 839 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 840 { 841 // Write out the instruction. 842 instructionWriter.visitVariableInstruction(clazz, 843 method, 844 codeAttribute, 845 newOffset, 846 variableInstruction); 847 848 newOffset += variableInstruction.length(newOffset); 849 } 850 851 852 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 853 { 854 // Update the branch offset, relative to the precise new offset. 855 branchInstruction.branchOffset = 856 newBranchOffset(offset, branchInstruction.branchOffset, newOffset); 857 858 // Write out the instruction. 859 instructionWriter.visitBranchInstruction(clazz, 860 method, 861 codeAttribute, 862 newOffset, 863 branchInstruction); 864 865 newOffset += branchInstruction.length(newOffset); 866 } 867 868 869 public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) 870 { 871 // Update the default jump offset, relative to the precise new offset. 872 tableSwitchInstruction.defaultOffset = 873 newBranchOffset(offset, tableSwitchInstruction.defaultOffset, newOffset); 874 875 // Update the jump offsets, relative to the precise new offset. 876 newJumpOffsets(offset, 877 tableSwitchInstruction.jumpOffsets, 878 newOffset); 879 880 // Write out the instruction. 881 instructionWriter.visitTableSwitchInstruction(clazz, 882 method, 883 codeAttribute, 884 newOffset, 885 tableSwitchInstruction); 886 887 newOffset += tableSwitchInstruction.length(newOffset); 888 } 889 890 891 public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) 892 { 893 // Update the default jump offset, relative to the precise new offset. 894 lookUpSwitchInstruction.defaultOffset = 895 newBranchOffset(offset, lookUpSwitchInstruction.defaultOffset, newOffset); 896 897 // Update the jump offsets, relative to the precise new offset. 898 newJumpOffsets(offset, 899 lookUpSwitchInstruction.jumpOffsets, 900 newOffset); 901 902 // Write out the instruction. 903 instructionWriter.visitLookUpSwitchInstruction(clazz, 904 method, 905 codeAttribute, 906 newOffset, 907 lookUpSwitchInstruction); 908 909 newOffset += lookUpSwitchInstruction.length(newOffset); 910 } 911 912 913 // Implementations for ExceptionInfoVisitor. 914 915 public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) 916 { 917 // Update the code offsets. Note that the instruction offset map also 918 // has an entry for the first offset after the code, for u2endPC. 919 exceptionInfo.u2startPC = newInstructionOffset(exceptionInfo.u2startPC); 920 exceptionInfo.u2endPC = newInstructionOffset(exceptionInfo.u2endPC); 921 exceptionInfo.u2handlerPC = newInstructionOffset(exceptionInfo.u2handlerPC); 922 } 923 924 925 // Implementations for StackMapFrameVisitor. 926 927 public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) 928 { 929 // Update the stack map frame offset. 930 int stackMapFrameOffset = newInstructionOffset(offset); 931 932 int offsetDelta = stackMapFrameOffset; 933 934 // Compute the offset delta if the frame is part of a stack map frame 935 // table (for JDK 6.0) instead of a stack map (for Java Micro Edition). 936 if (expectedStackMapFrameOffset >= 0) 937 { 938 offsetDelta -= expectedStackMapFrameOffset; 939 940 expectedStackMapFrameOffset = stackMapFrameOffset + 1; 941 } 942 943 stackMapFrame.u2offsetDelta = offsetDelta; 944 } 945 946 947 public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame) 948 { 949 // Update the stack map frame offset. 950 visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame); 951 952 // Update the verification type offset. 953 sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this); 954 } 955 956 957 public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) 958 { 959 // Update the stack map frame offset. 960 visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame); 961 962 // Update the verification type offsets. 963 moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this); 964 } 965 966 967 public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) 968 { 969 // Update the stack map frame offset. 970 visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame); 971 972 // Update the verification type offsets. 973 fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this); 974 fullFrame.stackAccept(clazz, method, codeAttribute, offset, this); 975 } 976 977 978 // Implementations for VerificationTypeVisitor. 979 980 public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {} 981 982 983 public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType) 984 { 985 // Update the offset of the 'new' instruction. 986 uninitializedType.u2newInstructionOffset = newInstructionOffset(uninitializedType.u2newInstructionOffset); 987 } 988 989 990 // Implementations for LineNumberInfoVisitor. 991 992 public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) 993 { 994 // Update the code offset. 995 lineNumberInfo.u2startPC = newInstructionOffset(lineNumberInfo.u2startPC); 996 } 997 998 999 // Implementations for LocalVariableInfoVisitor. 1000 1001 public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) 1002 { 1003 // Update the code offset and length. 1004 // Be careful to update the length first. 1005 localVariableInfo.u2length = newBranchOffset(localVariableInfo.u2startPC, localVariableInfo.u2length); 1006 localVariableInfo.u2startPC = newInstructionOffset(localVariableInfo.u2startPC); 1007 } 1008 1009 1010 // Implementations for LocalVariableTypeInfoVisitor. 1011 1012 public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) 1013 { 1014 // Update the code offset and length. 1015 // Be careful to update the length first. 1016 localVariableTypeInfo.u2length = newBranchOffset(localVariableTypeInfo.u2startPC, localVariableTypeInfo.u2length); 1017 localVariableTypeInfo.u2startPC = newInstructionOffset(localVariableTypeInfo.u2startPC); 1018 } 1019 1020 1021 // Implementations for TypeAnnotationVisitor. 1022 1023 public void visitTypeAnnotation(Clazz clazz, TypeAnnotation typeAnnotation) 1024 { 1025 // Update all local variable targets. 1026 typeAnnotation.targetInfoAccept(clazz, this); 1027 } 1028 1029 1030 // Implementations for TargetInfoVisitor. 1031 1032 public void visitAnyTargetInfo(Clazz clazz, TypeAnnotation typeAnnotation, TargetInfo targetInfo) {} 1033 1034 1035 public void visitLocalVariableTargetInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, LocalVariableTargetInfo localVariableTargetInfo) 1036 { 1037 // Update the offsets of the variables. 1038 localVariableTargetInfo.targetElementsAccept(clazz, method, codeAttribute, typeAnnotation, this); 1039 } 1040 1041 1042 public void visitOffsetTargetInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, OffsetTargetInfo offsetTargetInfo) 1043 { 1044 // Update the offset. 1045 offsetTargetInfo.u2offset = newInstructionOffset(offsetTargetInfo.u2offset); 1046 } 1047 1048 1049 // Implementations for LocalVariableTargetElementVisitor. 1050 1051 public void visitLocalVariableTargetElement(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, LocalVariableTargetInfo localVariableTargetInfo, LocalVariableTargetElement localVariableTargetElement) 1052 { 1053 // Update the variable start offset and length. 1054 // Be careful to update the length first. 1055 localVariableTargetElement.u2length = newBranchOffset(localVariableTargetElement.u2startPC, localVariableTargetElement.u2length); 1056 localVariableTargetElement.u2startPC = newInstructionOffset(localVariableTargetElement.u2startPC); 1057 } 1058 1059 1060 // Small utility methods. 1061 1062 /** 1063 * Updates the given jump offsets for the instruction at the given offset, 1064 * relative to the given new offset. 1065 */ 1066 private void newJumpOffsets(int oldInstructionOffset, 1067 int[] oldJumpOffsets, 1068 int newInstructionOffset) 1069 { 1070 for (int index = 0; index < oldJumpOffsets.length; index++) 1071 { 1072 oldJumpOffsets[index] = newBranchOffset(oldInstructionOffset, 1073 oldJumpOffsets[index], 1074 newInstructionOffset); 1075 } 1076 } 1077 1078 1079 /** 1080 * Computes the new branch offset for the instruction at the given offset 1081 * with the given branch offset, relative to the new instruction (block) 1082 * offset. 1083 */ 1084 private int newBranchOffset(int oldInstructionOffset, 1085 int oldBranchOffset) 1086 { 1087 return newInstructionOffset(oldInstructionOffset + oldBranchOffset) - 1088 newInstructionOffset(oldInstructionOffset); 1089 } 1090 1091 1092 /** 1093 * Computes the new branch offset for the instruction at the given offset 1094 * with the given branch offset, relative to the given new offset. 1095 */ 1096 private int newBranchOffset(int oldInstructionOffset, 1097 int oldBranchOffset, 1098 int newInstructionOffset) 1099 { 1100 return newInstructionOffset(oldInstructionOffset + oldBranchOffset) - 1101 newInstructionOffset; 1102 } 1103 1104 1105 /** 1106 * Computes the new instruction offset for the instruction at the given 1107 * offset. 1108 */ 1109 private int newInstructionOffset(int oldInstructionOffset) 1110 { 1111 if (oldInstructionOffset < 0 || 1112 oldInstructionOffset > codeLength) 1113 { 1114 throw new IllegalArgumentException("Invalid instruction offset ["+oldInstructionOffset+"] in code with length ["+codeLength+"]"); 1115 } 1116 1117 return newInstructionOffsets[oldInstructionOffset]; 1118 } 1119 1120 1121 /** 1122 * Returns the given list of exceptions, without the ones that have empty 1123 * code blocks. 1124 */ 1125 private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos, 1126 int exceptionInfoCount) 1127 { 1128 // Overwrite all empty exceptions. 1129 int newIndex = 0; 1130 for (int index = 0; index < exceptionInfoCount; index++) 1131 { 1132 ExceptionInfo exceptionInfo = exceptionInfos[index]; 1133 if (exceptionInfo.u2startPC < exceptionInfo.u2endPC) 1134 { 1135 exceptionInfos[newIndex++] = exceptionInfo; 1136 } 1137 } 1138 1139 return newIndex; 1140 } 1141 1142 1143 /** 1144 * Returns the given list of line numbers, without the ones that have empty 1145 * code blocks or that exceed the code size. 1146 */ 1147 private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos, 1148 int lineNumberInfoCount, 1149 int codeLength) 1150 { 1151 // Overwrite all empty line number entries. 1152 int newIndex = 0; 1153 for (int index = 0; index < lineNumberInfoCount; index++) 1154 { 1155 LineNumberInfo lineNumberInfo = lineNumberInfos[index]; 1156 int startPC = lineNumberInfo.u2startPC; 1157 if (startPC < codeLength && 1158 (index == 0 || startPC > lineNumberInfos[index-1].u2startPC)) 1159 { 1160 lineNumberInfos[newIndex++] = lineNumberInfo; 1161 } 1162 } 1163 1164 return newIndex; 1165 } 1166 1167 1168 /** 1169 * This instruction is a composite of other instructions, for local use 1170 * inside the editor class only. 1171 */ 1172 private class CompositeInstruction 1173 extends Instruction 1174 { 1175 private Instruction[] instructions; 1176 1177 1178 private CompositeInstruction(Instruction[] instructions) 1179 { 1180 this.instructions = instructions; 1181 } 1182 1183 1184 // Implementations for Instruction. 1185 1186 public Instruction shrink() 1187 { 1188 for (int index = 0; index < instructions.length; index++) 1189 { 1190 instructions[index] = instructions[index].shrink(); 1191 } 1192 1193 return this; 1194 } 1195 1196 1197 public void write(byte[] code, int offset) 1198 { 1199 for (int index = 0; index < instructions.length; index++) 1200 { 1201 Instruction instruction = instructions[index]; 1202 1203 instruction.write(code, offset); 1204 1205 offset += instruction.length(offset); 1206 } 1207 } 1208 1209 1210 protected void readInfo(byte[] code, int offset) 1211 { 1212 throw new UnsupportedOperationException("Can't read composite instruction"); 1213 } 1214 1215 1216 protected void writeInfo(byte[] code, int offset) 1217 { 1218 throw new UnsupportedOperationException("Can't write composite instruction"); 1219 } 1220 1221 1222 public int length(int offset) 1223 { 1224 int newOffset = offset; 1225 1226 for (int index = 0; index < instructions.length; index++) 1227 { 1228 newOffset += instructions[index].length(newOffset); 1229 } 1230 1231 return newOffset - offset; 1232 } 1233 1234 1235 public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) 1236 { 1237 if (instructionVisitor != CodeAttributeEditor.this) 1238 { 1239 throw new UnsupportedOperationException("Unexpected visitor ["+instructionVisitor+"]"); 1240 } 1241 1242 for (int index = 0; index < instructions.length; index++) 1243 { 1244 Instruction instruction = instructions[index]; 1245 1246 instruction.accept(clazz, method, codeAttribute, offset, CodeAttributeEditor.this); 1247 1248 offset += instruction.length(offset); 1249 } 1250 } 1251 1252 1253 // Implementations for Object. 1254 1255 public String toString() 1256 { 1257 StringBuffer stringBuffer = new StringBuffer(); 1258 1259 for (int index = 0; index < instructions.length; index++) 1260 { 1261 stringBuffer.append(instructions[index].toString()).append("; "); 1262 } 1263 1264 return stringBuffer.toString(); 1265 } 1266 } 1267} 1268