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