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