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