CodeAttributeComposer.java revision cfead78069f3dc32998dc118ee08cab3867acea2
1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2011 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; 31 32import java.util.Arrays; 33 34/** 35 * This AttributeVisitor accumulates instructions and exceptions, and then 36 * copies them into code attributes that it visits. 37 * 38 * @author Eric Lafortune 39 */ 40public class CodeAttributeComposer 41extends SimplifiedVisitor 42implements AttributeVisitor, 43 InstructionVisitor, 44 ExceptionInfoVisitor, 45 StackMapFrameVisitor, 46 VerificationTypeVisitor, 47 LineNumberInfoVisitor, 48 LocalVariableInfoVisitor, 49 LocalVariableTypeInfoVisitor 50{ 51 //* 52 private static final boolean DEBUG = false; 53 /*/ 54 public static boolean DEBUG = false; 55 //*/ 56 57 58 private static final int MAXIMUM_LEVELS = 32; 59 private static final int INVALID = -1; 60 61 62 private boolean allowExternalExceptionHandlers; 63 64 private int maximumCodeLength; 65 private int codeLength; 66 private int exceptionTableLength; 67 private int level = -1; 68 69 private byte[] code = new byte[ClassConstants.TYPICAL_CODE_LENGTH]; 70 private int[] oldInstructionOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH]; 71 72 private final int[] codeFragmentOffsets = new int[MAXIMUM_LEVELS]; 73 private final int[] codeFragmentLengths = new int[MAXIMUM_LEVELS]; 74 private final int[][] instructionOffsetMap = new int[MAXIMUM_LEVELS][ClassConstants.TYPICAL_CODE_LENGTH + 1]; 75 76 private ExceptionInfo[] exceptionTable = new ExceptionInfo[ClassConstants.TYPICAL_EXCEPTION_TABLE_LENGTH]; 77 78 private int expectedStackMapFrameOffset; 79 80 private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater(); 81 private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater(); 82// private final InstructionWriter instructionWriter = new InstructionWriter(); 83 84 85 /** 86 * Creates a new CodeAttributeComposer that doesn't allow external exception 87 * handlers. 88 */ 89 public CodeAttributeComposer() 90 { 91 this(false); 92 } 93 94 95 /** 96 * Creates a new CodeAttributeComposer that optionally allows external 97 * exception handlers. 98 */ 99 public CodeAttributeComposer(boolean allowExternalExceptionHandlers) 100 { 101 this.allowExternalExceptionHandlers = allowExternalExceptionHandlers; 102 } 103 104 105 /** 106 * Starts a new code definition. 107 */ 108 public void reset() 109 { 110 maximumCodeLength = 0; 111 codeLength = 0; 112 exceptionTableLength = 0; 113 level = -1; 114 } 115 116 117 /** 118 * Starts a new code fragment. Branch instructions that are added are 119 * assumed to be relative within such code fragments. 120 * @param maximumCodeFragmentLength the maximum length of the code that will 121 * be added as part of this fragment. 122 */ 123 public void beginCodeFragment(int maximumCodeFragmentLength) 124 { 125 level++; 126 127 if (level >= MAXIMUM_LEVELS) 128 { 129 throw new IllegalArgumentException("Maximum number of code fragment levels exceeded ["+level+"]"); 130 } 131 132// // TODO: Figure out some length. 133// if (level == 0) 134// { 135// // Prepare for possible widening of instructions. 136// instructionWriter.reset(2 * maximumCodeFragmentLength); 137// } 138 139 // Make sure there is sufficient space for adding the code fragment. 140 maximumCodeLength += maximumCodeFragmentLength; 141 142 ensureCodeLength(maximumCodeLength); 143 144 // Try to reuse the previous array for this code fragment. 145 if (instructionOffsetMap[level].length <= maximumCodeFragmentLength) 146 { 147 instructionOffsetMap[level] = new int[maximumCodeFragmentLength + 1]; 148 } 149 150 // Initialize the offset map. 151 for (int index = 0; index <= maximumCodeFragmentLength; index++) 152 { 153 instructionOffsetMap[level][index] = INVALID; 154 } 155 156 // Remember the location of the code fragment. 157 codeFragmentOffsets[level] = codeLength; 158 codeFragmentLengths[level] = maximumCodeFragmentLength; 159 } 160 161 162 /** 163 * Appends the given instruction with the given old offset. 164 * @param oldInstructionOffset the old offset of the instruction, to which 165 * branches and other references in the current 166 * code fragment are pointing. 167 * @param instruction the instruction to be appended. 168 */ 169 public void appendInstruction(int oldInstructionOffset, 170 Instruction instruction) 171 { 172 if (DEBUG) 173 { 174 println("["+codeLength+"] <- ", instruction.toString(oldInstructionOffset)); 175 } 176 177 // Make sure the code array is large enough. 178 int newCodeLength = codeLength + instruction.length(codeLength); 179 180 ensureCodeLength(newCodeLength); 181 182 // Remember the old offset of the appended instruction. 183 oldInstructionOffsets[codeLength] = oldInstructionOffset; 184 185 // Write the instruction. 186// instruction.accept(null, 187// null, 188// new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null), 189// codeLength, 190// instructionWriter); 191 instruction.write(code, codeLength); 192 193 // Fill out the new offset of the appended instruction. 194 instructionOffsetMap[level][oldInstructionOffset] = codeLength; 195 196 // Continue appending at the next instruction offset. 197 codeLength = newCodeLength; 198 } 199 200 201 /** 202 * Appends the given label with the given old offset. 203 * @param oldInstructionOffset the old offset of the label, to which 204 * branches and other references in the current 205 * code fragment are pointing. 206 */ 207 public void appendLabel(int oldInstructionOffset) 208 { 209 if (DEBUG) 210 { 211 println("["+codeLength+"] <- ", "[" + oldInstructionOffset + "] (label)"); 212 } 213 214 // Fill out the new offset of the appended instruction. 215 instructionOffsetMap[level][oldInstructionOffset] = codeLength; 216 } 217 218 219 /** 220 * Appends the given exception to the exception table. 221 * @param exceptionInfo the exception to be appended. 222 */ 223 public void appendException(ExceptionInfo exceptionInfo) 224 { 225 if (DEBUG) 226 { 227 print(" ", "Exception ["+exceptionInfo.u2startPC+" -> "+exceptionInfo.u2endPC+": "+exceptionInfo.u2handlerPC+"]"); 228 } 229 230 // Remap the exception right away. 231 visitExceptionInfo(null, null, null, exceptionInfo); 232 233 if (DEBUG) 234 { 235 System.out.println(" -> ["+exceptionInfo.u2startPC+" -> "+exceptionInfo.u2endPC+": "+exceptionInfo.u2handlerPC+"]"); 236 } 237 238 // Don't add the exception if its instruction range is empty. 239 if (exceptionInfo.u2startPC == exceptionInfo.u2endPC) 240 { 241 if (DEBUG) 242 { 243 println(" ", " (not added because of empty instruction range)"); 244 } 245 246 return; 247 } 248 249 // Make sure there is sufficient space in the exception table. 250 if (exceptionTable.length <= exceptionTableLength) 251 { 252 ExceptionInfo[] newExceptionTable = new ExceptionInfo[exceptionTableLength+1]; 253 System.arraycopy(exceptionTable, 0, newExceptionTable, 0, exceptionTableLength); 254 exceptionTable = newExceptionTable; 255 } 256 257 // Add the exception. 258 exceptionTable[exceptionTableLength++] = exceptionInfo; 259 } 260 261 262 /** 263 * Wraps up the current code fragment, continuing with the previous one on 264 * the stack. 265 */ 266 public void endCodeFragment() 267 { 268 if (level < 0) 269 { 270 throw new IllegalArgumentException("Code fragment not begun ["+level+"]"); 271 } 272 273 // Remap the instructions of the code fragment. 274 int instructionOffset = codeFragmentOffsets[level]; 275 while (instructionOffset < codeLength) 276 { 277 // Get the next instruction. 278 Instruction instruction = InstructionFactory.create(code, instructionOffset); 279 280 // Does this instruction still have to be remapped? 281 if (oldInstructionOffsets[instructionOffset] >= 0) 282 { 283 // Adapt the instruction for its new offset. 284 instruction.accept(null, null, null, instructionOffset, this); 285 286 // Write the instruction back. 287// instruction.accept(null, 288// null, 289// new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null), 290// instructionOffset, 291// instructionWriter); 292 instruction.write(code, instructionOffset); 293 294 // Don't remap this instruction again. 295 oldInstructionOffsets[instructionOffset] = -1; 296 } 297 298 // Continue remapping at the next instruction offset. 299 instructionOffset += instruction.length(instructionOffset); 300 } 301 302 // Correct the estimated maximum code length, now that we know the 303 // actual length of this code fragment. 304 maximumCodeLength += codeLength - codeFragmentOffsets[level] - 305 codeFragmentLengths[level]; 306 307 // Try to remap the exception handlers that couldn't be remapped before. 308 if (allowExternalExceptionHandlers) 309 { 310 for (int index = 0; index < exceptionTableLength; index++) 311 { 312 ExceptionInfo exceptionInfo = exceptionTable[index]; 313 314 // Unmapped exception handlers are still negated. 315 int handlerPC = -exceptionInfo.u2handlerPC; 316 if (handlerPC > 0) 317 { 318 if (remappableExceptionHandler(handlerPC)) 319 { 320 exceptionInfo.u2handlerPC = remapInstructionOffset(handlerPC); 321 } 322 else if (level == 0) 323 { 324 throw new IllegalStateException("Couldn't remap exception handler offset ["+handlerPC+"]"); 325 } 326 } 327 } 328 } 329 330 level--; 331 } 332 333 334 // Implementations for AttributeVisitor. 335 336 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 337 338 339 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 340 { 341 if (DEBUG) 342 { 343 System.out.println("CodeAttributeComposer: putting results in ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); 344 } 345 346 if (level != -1) 347 { 348 throw new IllegalArgumentException("Code fragment not ended ["+level+"]"); 349 } 350 351 level++; 352 353 // Make sure the code attribute has sufficient space for the composed 354 // code. 355 if (codeAttribute.u4codeLength < codeLength) 356 { 357 codeAttribute.code = new byte[codeLength]; 358 } 359 360 // Copy the composed code over into the code attribute. 361 codeAttribute.u4codeLength = codeLength; 362 System.arraycopy(code, 0, codeAttribute.code, 0, codeLength); 363 364 // Remove exceptions with empty code blocks (done before). 365 //exceptionTableLength = 366 // removeEmptyExceptions(exceptionTable, exceptionTableLength); 367 368 // Make sure the exception table has sufficient space for the composed 369 // exceptions. 370 if (codeAttribute.exceptionTable.length < exceptionTableLength) 371 { 372 codeAttribute.exceptionTable = new ExceptionInfo[exceptionTableLength]; 373 } 374 375 // Copy the exception table. 376 codeAttribute.u2exceptionTableLength = exceptionTableLength; 377 System.arraycopy(exceptionTable, 0, codeAttribute.exceptionTable, 0, exceptionTableLength); 378 379 // Update the maximum stack size and local variable frame size. 380 stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); 381 variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); 382 383 // Remap the line number table and the local variable table. 384 codeAttribute.attributesAccept(clazz, method, this); 385 386 // Remap the exception table. 387 //codeAttribute.exceptionsAccept(clazz, method, this); 388 389 // Remove exceptions with empty code blocks (done before). 390 //codeAttribute.u2exceptionTableLength = 391 // removeEmptyExceptions(codeAttribute.exceptionTable, 392 // codeAttribute.u2exceptionTableLength); 393 394// // Make sure instructions are widened if necessary. 395// instructionWriter.visitCodeAttribute(clazz, method, codeAttribute); 396 397 level--; 398 } 399 400 401 public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) 402 { 403 // Remap all stack map entries. 404 expectedStackMapFrameOffset = -1; 405 stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); 406 } 407 408 409 public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) 410 { 411 // Remap all stack map table entries. 412 expectedStackMapFrameOffset = 0; 413 stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); 414 } 415 416 417 public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) 418 { 419 // Remap all line number table entries. 420 lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this); 421 422 // Remove line numbers with empty code blocks. 423 lineNumberTableAttribute.u2lineNumberTableLength = 424 removeEmptyLineNumbers(lineNumberTableAttribute.lineNumberTable, 425 lineNumberTableAttribute.u2lineNumberTableLength, 426 codeAttribute.u4codeLength); 427 } 428 429 430 public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) 431 { 432 // Remap all local variable table entries. 433 localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 434 435 // Remove local variables with empty code blocks. 436 localVariableTableAttribute.u2localVariableTableLength = 437 removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable, 438 localVariableTableAttribute.u2localVariableTableLength, 439 codeAttribute.u2maxLocals); 440 } 441 442 443 public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) 444 { 445 // Remap all local variable table entries. 446 localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 447 448 // Remove local variables with empty code blocks. 449 localVariableTypeTableAttribute.u2localVariableTypeTableLength = 450 removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable, 451 localVariableTypeTableAttribute.u2localVariableTypeTableLength, 452 codeAttribute.u2maxLocals); 453 } 454 455 456 // Implementations for InstructionVisitor. 457 458 public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} 459 460 461 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 462 { 463 // Adjust the branch offset. 464 branchInstruction.branchOffset = remapBranchOffset(offset, 465 branchInstruction.branchOffset); 466 } 467 468 469 public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) 470 { 471 // Adjust the default jump offset. 472 switchInstruction.defaultOffset = remapBranchOffset(offset, 473 switchInstruction.defaultOffset); 474 475 // Adjust the jump offsets. 476 remapJumpOffsets(offset, 477 switchInstruction.jumpOffsets); 478 } 479 480 481 // Implementations for ExceptionInfoVisitor. 482 483 public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) 484 { 485 // Remap the code offsets. Note that the instruction offset map also has 486 // an entry for the first offset after the code, for u2endPC. 487 exceptionInfo.u2startPC = remapInstructionOffset(exceptionInfo.u2startPC); 488 exceptionInfo.u2endPC = remapInstructionOffset(exceptionInfo.u2endPC); 489 490 // See if we can remap the handler right away. Unmapped exception 491 // handlers are negated, in order to mark them as external. 492 int handlerPC = exceptionInfo.u2handlerPC; 493 exceptionInfo.u2handlerPC = 494 !allowExternalExceptionHandlers || 495 remappableExceptionHandler(handlerPC) ? 496 remapInstructionOffset(handlerPC) : 497 -handlerPC; 498 } 499 500 501 // Implementations for StackMapFrameVisitor. 502 503 public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) 504 { 505 // Remap the stack map frame offset. 506 int stackMapFrameOffset = remapInstructionOffset(offset); 507 508 int offsetDelta = stackMapFrameOffset; 509 510 // Compute the offset delta if the frame is part of a stack map frame 511 // table (for JDK 6.0) instead of a stack map (for Java Micro Edition). 512 if (expectedStackMapFrameOffset >= 0) 513 { 514 offsetDelta -= expectedStackMapFrameOffset; 515 516 expectedStackMapFrameOffset = stackMapFrameOffset + 1; 517 } 518 519 stackMapFrame.u2offsetDelta = offsetDelta; 520 } 521 522 523 public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame) 524 { 525 // Remap the stack map frame offset. 526 visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame); 527 528 // Remap the verification type offset. 529 sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this); 530 } 531 532 533 public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) 534 { 535 // Remap the stack map frame offset. 536 visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame); 537 538 // Remap the verification type offsets. 539 moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this); 540 } 541 542 543 public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) 544 { 545 // Remap the stack map frame offset. 546 visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame); 547 548 // Remap the verification type offsets. 549 fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this); 550 fullFrame.stackAccept(clazz, method, codeAttribute, offset, this); 551 } 552 553 554 // Implementations for VerificationTypeVisitor. 555 556 public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {} 557 558 559 public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType) 560 { 561 // Remap the offset of the 'new' instruction. 562 uninitializedType.u2newInstructionOffset = remapInstructionOffset(uninitializedType.u2newInstructionOffset); 563 } 564 565 566 // Implementations for LineNumberInfoVisitor. 567 568 public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) 569 { 570 // Remap the code offset. 571 lineNumberInfo.u2startPC = remapInstructionOffset(lineNumberInfo.u2startPC); 572 } 573 574 575 // Implementations for LocalVariableInfoVisitor. 576 577 public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) 578 { 579 // Remap the code offset and length. 580 // TODO: The local variable frame might not be strictly preserved. 581 int startPC = remapInstructionOffset(localVariableInfo.u2startPC); 582 int endPC = remapInstructionOffset(localVariableInfo.u2startPC + localVariableInfo.u2length); 583 584 localVariableInfo.u2startPC = startPC; 585 localVariableInfo.u2length = endPC - startPC; 586 } 587 588 // Implementations for LocalVariableTypeInfoVisitor. 589 590 public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) 591 { 592 // Remap the code offset and length. 593 // TODO: The local variable frame might not be strictly preserved. 594 int startPC = remapInstructionOffset(localVariableTypeInfo.u2startPC); 595 int endPC = remapInstructionOffset(localVariableTypeInfo.u2startPC + localVariableTypeInfo.u2length); 596 597 localVariableTypeInfo.u2startPC = startPC; 598 localVariableTypeInfo.u2length = endPC - startPC; 599 } 600 601 602 // Small utility methods. 603 604 /** 605 * Make sure the code arrays have at least the given size. 606 */ 607 private void ensureCodeLength(int newCodeLength) 608 { 609 if (code.length < newCodeLength) 610 { 611 // Add 20% to avoid extending the arrays too often. 612 newCodeLength = newCodeLength * 6 / 5; 613 614 byte[] newCode = new byte[newCodeLength]; 615 System.arraycopy(code, 0, newCode, 0, codeLength); 616 code = newCode; 617 618 int[] newOldInstructionOffsets = new int[newCodeLength]; 619 System.arraycopy(oldInstructionOffsets, 0, newOldInstructionOffsets, 0, codeLength); 620 oldInstructionOffsets = newOldInstructionOffsets; 621 } 622 } 623 624 625 /** 626 * Adjusts the given jump offsets for the instruction at the given offset. 627 */ 628 private void remapJumpOffsets(int offset, int[] jumpOffsets) 629 { 630 for (int index = 0; index < jumpOffsets.length; index++) 631 { 632 jumpOffsets[index] = remapBranchOffset(offset, jumpOffsets[index]); 633 } 634 } 635 636 637 /** 638 * Computes the new branch offset for the instruction at the given new offset 639 * with the given old branch offset. 640 */ 641 private int remapBranchOffset(int newInstructionOffset, int branchOffset) 642 { 643 if (newInstructionOffset < 0 || 644 newInstructionOffset > codeLength) 645 { 646 throw new IllegalArgumentException("Invalid instruction offset ["+newInstructionOffset +"] in code with length ["+codeLength+"]"); 647 } 648 649 int oldInstructionOffset = oldInstructionOffsets[newInstructionOffset]; 650 651 return remapInstructionOffset(oldInstructionOffset + branchOffset) - 652 remapInstructionOffset(oldInstructionOffset); 653 } 654 655 656 /** 657 * Computes the new instruction offset for the instruction at the given old 658 * offset. 659 */ 660 private int remapInstructionOffset(int oldInstructionOffset) 661 { 662 if (oldInstructionOffset < 0 || 663 oldInstructionOffset > codeFragmentLengths[level]) 664 { 665 throw new IllegalArgumentException("Instruction offset ["+oldInstructionOffset +"] out of range in code fragment with length ["+codeFragmentLengths[level]+"] at level "+level); 666 } 667 668 int newInstructionOffset = instructionOffsetMap[level][oldInstructionOffset]; 669 if (newInstructionOffset == INVALID) 670 { 671 throw new IllegalArgumentException("Invalid instruction offset ["+oldInstructionOffset +"] in code fragment at level "+level); 672 } 673 674 return newInstructionOffset; 675 } 676 677 678 /** 679 * Returns whether the given old exception handler can be remapped in the 680 * current code fragment. 681 */ 682 private boolean remappableExceptionHandler(int oldInstructionOffset) 683 { 684 if (oldInstructionOffset > codeFragmentLengths[level]) 685 { 686 return false; 687 } 688 689 int newInstructionOffset = 690 instructionOffsetMap[level][oldInstructionOffset]; 691 692 return newInstructionOffset > INVALID && 693 newInstructionOffset < codeLength; 694 } 695 696 697 /** 698 * Returns the given list of exceptions, without the ones that have empty 699 * code blocks. 700 */ 701 private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos, 702 int exceptionInfoCount) 703 { 704 // Overwrite all empty exceptions. 705 int newIndex = 0; 706 for (int index = 0; index < exceptionInfoCount; index++) 707 { 708 ExceptionInfo exceptionInfo = exceptionInfos[index]; 709 if (exceptionInfo.u2startPC < exceptionInfo.u2endPC) 710 { 711 exceptionInfos[newIndex++] = exceptionInfo; 712 } 713 } 714 715 // Clear the unused array entries. 716 Arrays.fill(exceptionInfos, newIndex, exceptionInfoCount, null); 717 718 return newIndex; 719 } 720 721 722 /** 723 * Returns the given list of line numbers, without the ones that have empty 724 * code blocks or that exceed the code size. 725 */ 726 private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos, 727 int lineNumberInfoCount, 728 int codeLength) 729 { 730 // Overwrite all empty line number entries. 731 int newIndex = 0; 732 for (int index = 0; index < lineNumberInfoCount; index++) 733 { 734 LineNumberInfo lineNumberInfo = lineNumberInfos[index]; 735 int startPC = lineNumberInfo.u2startPC; 736 if (startPC < codeLength && 737 (index == 0 || startPC > lineNumberInfos[index-1].u2startPC)) 738 { 739 lineNumberInfos[newIndex++] = lineNumberInfo; 740 } 741 } 742 743 // Clear the unused array entries. 744 Arrays.fill(lineNumberInfos, newIndex, lineNumberInfoCount, null); 745 746 return newIndex; 747 } 748 749 750 /** 751 * Returns the given list of local variables, without the ones that have empty 752 * code blocks or that exceed the actual number of local variables. 753 */ 754 private int removeEmptyLocalVariables(LocalVariableInfo[] localVariableInfos, 755 int localVariableInfoCount, 756 int maxLocals) 757 { 758 // Overwrite all empty local variable entries. 759 int newIndex = 0; 760 for (int index = 0; index < localVariableInfoCount; index++) 761 { 762 LocalVariableInfo localVariableInfo = localVariableInfos[index]; 763 if (localVariableInfo.u2length > 0 && 764 localVariableInfo.u2index < maxLocals) 765 { 766 localVariableInfos[newIndex++] = localVariableInfo; 767 } 768 } 769 770 // Clear the unused array entries. 771 Arrays.fill(localVariableInfos, newIndex, localVariableInfoCount, null); 772 773 return newIndex; 774 } 775 776 777 /** 778 * Returns the given list of local variable types, without the ones that 779 * have empty code blocks or that exceed the actual number of local variables. 780 */ 781 private int removeEmptyLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos, 782 int localVariableTypeInfoCount, 783 int maxLocals) 784 { 785 // Overwrite all empty local variable type entries. 786 int newIndex = 0; 787 for (int index = 0; index < localVariableTypeInfoCount; index++) 788 { 789 LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index]; 790 if (localVariableTypeInfo.u2length > 0 && 791 localVariableTypeInfo.u2index < maxLocals) 792 { 793 localVariableTypeInfos[newIndex++] = localVariableTypeInfo; 794 } 795 } 796 797 // Clear the unused array entries. 798 Arrays.fill(localVariableTypeInfos, newIndex, localVariableTypeInfoCount, null); 799 800 return newIndex; 801 } 802 803 804 private void println(String string1, String string2) 805 { 806 print(string1, string2); 807 808 System.out.println(); 809 } 810 811 private void print(String string1, String string2) 812 { 813 System.out.print(string1); 814 815 for (int index = 0; index < level; index++) 816 { 817 System.out.print(" "); 818 } 819 820 System.out.print(string2); 821 } 822 823 824 public static void main(String[] args) 825 { 826 CodeAttributeComposer composer = new CodeAttributeComposer(); 827 828 composer.beginCodeFragment(4); 829 composer.appendInstruction(0, new SimpleInstruction(InstructionConstants.OP_ICONST_0)); 830 composer.appendInstruction(1, new VariableInstruction(InstructionConstants.OP_ISTORE, 0)); 831 composer.appendInstruction(2, new BranchInstruction(InstructionConstants.OP_GOTO, 1)); 832 833 composer.beginCodeFragment(4); 834 composer.appendInstruction(0, new VariableInstruction(InstructionConstants.OP_IINC, 0, 1)); 835 composer.appendInstruction(1, new VariableInstruction(InstructionConstants.OP_ILOAD, 0)); 836 composer.appendInstruction(2, new SimpleInstruction(InstructionConstants.OP_ICONST_5)); 837 composer.appendInstruction(3, new BranchInstruction(InstructionConstants.OP_IFICMPLT, -3)); 838 composer.endCodeFragment(); 839 840 composer.appendInstruction(3, new SimpleInstruction(InstructionConstants.OP_RETURN)); 841 composer.endCodeFragment(); 842 } 843} 844