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.optimize.peephole; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.*; 25import proguard.classfile.attribute.visitor.*; 26import proguard.classfile.constant.*; 27import proguard.classfile.constant.visitor.ConstantVisitor; 28import proguard.classfile.instruction.*; 29import proguard.classfile.instruction.visitor.InstructionVisitor; 30import proguard.classfile.util.SimplifiedVisitor; 31 32/** 33 * This AttributeVisitor finds all instruction offsets, branch targets, and 34 * exception targets in the CodeAttribute objects that it visits. 35 * 36 * @author Eric Lafortune 37 */ 38public class BranchTargetFinder 39extends SimplifiedVisitor 40implements AttributeVisitor, 41 InstructionVisitor, 42 ExceptionInfoVisitor, 43 ConstantVisitor 44{ 45 //* 46 private static final boolean DEBUG = false; 47 /*/ 48 private static boolean DEBUG = true; 49 //*/ 50 51 public static final int NONE = -2; 52 public static final int AT_METHOD_ENTRY = -1; 53 54 private static final short INSTRUCTION = 1 << 0; 55 private static final short BRANCH_ORIGIN = 1 << 1; 56 private static final short BRANCH_TARGET = 1 << 2; 57 private static final short AFTER_BRANCH = 1 << 3; 58 private static final short EXCEPTION_START = 1 << 4; 59 private static final short EXCEPTION_END = 1 << 5; 60 private static final short EXCEPTION_HANDLER = 1 << 6; 61 private static final short SUBROUTINE_INVOCATION = 1 << 7; 62 private static final short SUBROUTINE_RETURNING = 1 << 8; 63 64 private static final int MAXIMUM_CREATION_OFFSETS = 32; 65 66 67 private short[] instructionMarks = new short[ClassConstants.TYPICAL_CODE_LENGTH + 1]; 68 private int[] subroutineStarts = new int[ClassConstants.TYPICAL_CODE_LENGTH]; 69 private int[] subroutineEnds = new int[ClassConstants.TYPICAL_CODE_LENGTH]; 70 private int[] creationOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH]; 71 private int[] initializationOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH]; 72 private int superInitializationOffset; 73 74 private int currentSubroutineStart; 75 private int currentSubroutineEnd; 76 private int[] recentCreationOffsets = new int[MAXIMUM_CREATION_OFFSETS]; 77 private int recentCreationOffsetIndex; 78 private boolean isInitializer; 79 80 81 /** 82 * Returns whether there is an instruction at the given offset in the 83 * CodeAttribute that was visited most recently. 84 */ 85 public boolean isInstruction(int offset) 86 { 87 return (instructionMarks[offset] & INSTRUCTION) != 0; 88 } 89 90 91 /** 92 * Returns whether the instruction at the given offset is the target of 93 * any kind in the CodeAttribute that was visited most recently. 94 */ 95 public boolean isTarget(int offset) 96 { 97 return offset == 0 || 98 (instructionMarks[offset] & (BRANCH_TARGET | 99 EXCEPTION_START | 100 EXCEPTION_END | 101 EXCEPTION_HANDLER)) != 0; 102 } 103 104 105 /** 106 * Returns whether the instruction at the given offset is the origin of a 107 * branch instruction in the CodeAttribute that was visited most recently. 108 */ 109 public boolean isBranchOrigin(int offset) 110 { 111 return (instructionMarks[offset] & BRANCH_ORIGIN) != 0; 112 } 113 114 115 /** 116 * Returns whether the instruction at the given offset is the target of a 117 * branch instruction in the CodeAttribute that was visited most recently. 118 */ 119 public boolean isBranchTarget(int offset) 120 { 121 return (instructionMarks[offset] & BRANCH_TARGET) != 0; 122 } 123 124 125 /** 126 * Returns whether the instruction at the given offset comes right after a 127 * definite branch instruction in the CodeAttribute that was visited most 128 * recently. 129 */ 130 public boolean isAfterBranch(int offset) 131 { 132 return (instructionMarks[offset] & AFTER_BRANCH) != 0; 133 } 134 135 136 /** 137 * Returns whether the instruction at the given offset is the start of an 138 * exception try block in the CodeAttribute that was visited most recently. 139 */ 140 public boolean isExceptionStart(int offset) 141 { 142 return (instructionMarks[offset] & EXCEPTION_START) != 0; 143 } 144 145 146 /** 147 * Returns whether the instruction at the given offset is the end of an 148 * exception try block in the CodeAttribute that was visited most recently. 149 */ 150 public boolean isExceptionEnd(int offset) 151 { 152 return (instructionMarks[offset] & EXCEPTION_END) != 0; 153 } 154 155 156 /** 157 * Returns whether the instruction at the given offset is the start of an 158 * exception catch block in the CodeAttribute that was visited most recently. 159 */ 160 public boolean isExceptionHandler(int offset) 161 { 162 return (instructionMarks[offset] & EXCEPTION_HANDLER) != 0; 163 } 164 165 166 /** 167 * Returns whether the instruction at the given offset is a subroutine 168 * invocation in the CodeAttribute that was visited most recently. 169 */ 170 public boolean isSubroutineInvocation(int offset) 171 { 172 return (instructionMarks[offset] & SUBROUTINE_INVOCATION) != 0; 173 } 174 175 176 /** 177 * Returns whether the instruction at the given offset is the start of a 178 * subroutine in the CodeAttribute that was visited most recently. 179 */ 180 public boolean isSubroutineStart(int offset) 181 { 182 return subroutineStarts[offset] == offset; 183 } 184 185 186 /** 187 * Returns whether the instruction at the given offset is part of a 188 * subroutine in the CodeAttribute that was visited most recently. 189 */ 190 public boolean isSubroutine(int offset) 191 { 192 return subroutineStarts[offset] != NONE; 193 } 194 195 196 /** 197 * Returns whether the subroutine at the given offset is ever returning 198 * by means of a regular 'ret' instruction. 199 */ 200 public boolean isSubroutineReturning(int offset) 201 { 202 return (instructionMarks[offset] & SUBROUTINE_RETURNING) != 0; 203 } 204 205 206 /** 207 * Returns the start offset of the subroutine at the given offset, in the 208 * CodeAttribute that was visited most recently. 209 */ 210 public int subroutineStart(int offset) 211 { 212 return subroutineStarts[offset]; 213 } 214 215 216 /** 217 * Returns the offset after the subroutine at the given offset, in the 218 * CodeAttribute that was visited most recently. 219 */ 220 public int subroutineEnd(int offset) 221 { 222 return subroutineEnds[offset]; 223 } 224 225 226 /** 227 * Returns whether the instruction at the given offset is a 'new' 228 * instruction, in the CodeAttribute that was visited most recently. 229 */ 230 public boolean isNew(int offset) 231 { 232 return initializationOffsets[offset] != NONE; 233 } 234 235 236 /** 237 * Returns the instruction offset at which the object instance that is 238 * created at the given 'new' instruction offset is initialized, or 239 * <code>NONE</code> if it is not being created. 240 */ 241 public int initializationOffset(int offset) 242 { 243 return initializationOffsets[offset]; 244 } 245 246 247 /** 248 * Returns whether the method is an instance initializer, in the 249 * CodeAttribute that was visited most recently. 250 */ 251 public boolean isInitializer() 252 { 253 return superInitializationOffset != NONE; 254 } 255 256 257 /** 258 * Returns the instruction offset at which this initializer is calling 259 * the "super" or "this" initializer method, or <code>NONE</code> if it is 260 * not an initializer. 261 */ 262 public int superInitializationOffset() 263 { 264 return superInitializationOffset; 265 } 266 267 268 /** 269 * Returns whether the instruction at the given offset is the special 270 * invocation of an instance initializer, in the CodeAttribute that was 271 * visited most recently. 272 */ 273 public boolean isInitializer(int offset) 274 { 275 return creationOffsets[offset] != NONE; 276 } 277 278 279 /** 280 * Returns the offset of the 'new' instruction that corresponds to the 281 * invocation of the instance initializer at the given offset, or 282 * <code>AT_METHOD_ENTRY</code> if the invocation is calling the "super" or 283 * "this" initializer method, , or <code>NONE</code> if it is not a 'new' 284 * instruction. 285 */ 286 public int creationOffset(int offset) 287 { 288 return creationOffsets[offset]; 289 } 290 291 292 // Implementations for AttributeVisitor. 293 294 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 295 296 297 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 298 { 299// DEBUG = 300// clazz.getName().equals("abc/Def") && 301// method.getName(clazz).equals("abc"); 302 303 // Make sure there are sufficiently large arrays. 304 int codeLength = codeAttribute.u4codeLength; 305 if (subroutineStarts.length < codeLength) 306 { 307 // Create new arrays. 308 instructionMarks = new short[codeLength + 1]; 309 subroutineStarts = new int[codeLength]; 310 subroutineEnds = new int[codeLength]; 311 creationOffsets = new int[codeLength]; 312 initializationOffsets = new int[codeLength]; 313 314 // Reset the arrays. 315 for (int index = 0; index < codeLength; index++) 316 { 317 subroutineStarts[index] = NONE; 318 subroutineEnds[index] = NONE; 319 creationOffsets[index] = NONE; 320 initializationOffsets[index] = NONE; 321 } 322 } 323 else 324 { 325 // Reset the arrays. 326 for (int index = 0; index < codeLength; index++) 327 { 328 instructionMarks[index] = 0; 329 subroutineStarts[index] = NONE; 330 subroutineEnds[index] = NONE; 331 creationOffsets[index] = NONE; 332 initializationOffsets[index] = NONE; 333 } 334 335 instructionMarks[codeLength] = 0; 336 } 337 338 superInitializationOffset = NONE; 339 340 // We're assuming all subroutines are contiguous blocks of code. 341 // We're not starting in a subroutine. 342 currentSubroutineStart = NONE; 343 currentSubroutineEnd = NONE; 344 345 recentCreationOffsetIndex = 0; 346 347 // Initialize the stack of 'new' instruction offsets if this method is 348 // an instance initializer. 349 if (method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) 350 { 351 recentCreationOffsets[recentCreationOffsetIndex++] = AT_METHOD_ENTRY; 352 } 353 354 // The end of the code is a branch target sentinel. 355 instructionMarks[codeLength] = BRANCH_TARGET; 356 357 // Mark branch targets by going over all instructions. 358 codeAttribute.instructionsAccept(clazz, method, this); 359 360 // Mark branch targets in the exception table. 361 codeAttribute.exceptionsAccept(clazz, method, this); 362 363 // Fill out any gaps in the subroutine starts and the subroutine ends 364 // and subroutine returning flags, working backward. 365 366 // We're not starting in a subroutine. 367 int subroutineStart = NONE; 368 int subroutineEnd = codeLength; 369 boolean subroutineReturning = false; 370 371 for (int index = codeLength - 1; index >= 0; index--) 372 { 373 if (isInstruction(index)) 374 { 375 // Are we inside a previously marked subroutine? 376 if (subroutineStarts[index] != NONE) 377 { 378 // Update the current subroutine start. 379 subroutineStart = subroutineStarts[index]; 380 } 381 else if (subroutineStart != NONE) 382 { 383 // Mark the subroutine start. 384 subroutineStarts[index] = subroutineStart; 385 } 386 387 // Did we reach the start of the subroutine. 388 if (isSubroutineStart(index)) 389 { 390 // Stop marking it. 391 subroutineStart = NONE; 392 } 393 394 // Are we inside a subroutine? 395 if (isSubroutine(index)) 396 { 397 // Mark the subroutine end. 398 subroutineEnds[index] = subroutineEnd; 399 400 // Update or mark the subroutine returning flag. 401 if (isSubroutineReturning(index)) 402 { 403 subroutineReturning = true; 404 } 405 else if (subroutineReturning) 406 { 407 instructionMarks[index] |= SUBROUTINE_RETURNING; 408 } 409 } 410 else 411 { 412 // Update the subroutine end and returning flag. 413 subroutineEnd = index; 414 subroutineReturning = false; 415 } 416 } 417 } 418 419 if (DEBUG) 420 { 421 System.out.println(); 422 System.out.println("Branch targets: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); 423 424 for (int index = 0; index < codeLength; index++) 425 { 426 if (isInstruction(index)) 427 { 428 System.out.println("" + 429 (isBranchOrigin(index) ? 'B' : '-') + 430 (isAfterBranch(index) ? 'b' : '-') + 431 (isBranchTarget(index) ? 'T' : '-') + 432 (isExceptionStart(index) ? 'E' : '-') + 433 (isExceptionEnd(index) ? 'e' : '-') + 434 (isExceptionHandler(index) ? 'H' : '-') + 435 (isSubroutineInvocation(index) ? 'J' : '-') + 436 (isSubroutineStart(index) ? 'S' : '-') + 437 (isSubroutineReturning(index) ? 'r' : '-') + 438 (isSubroutine(index) ? " ["+subroutineStart(index)+" -> "+subroutineEnd(index)+"]" : "") + 439 (isNew(index) ? " ["+initializationOffset(index)+"] " : " ---- ") + 440 InstructionFactory.create(codeAttribute.code, index).toString(index)); 441 } 442 } 443 } 444 } 445 446 447 // Implementations for InstructionVisitor. 448 449 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 450 { 451 // Mark the instruction. 452 instructionMarks[offset] |= INSTRUCTION; 453 454 // Check if this is the first instruction of a subroutine. 455 checkSubroutine(offset); 456 457 byte opcode = simpleInstruction.opcode; 458 if (opcode == InstructionConstants.OP_IRETURN || 459 opcode == InstructionConstants.OP_LRETURN || 460 opcode == InstructionConstants.OP_FRETURN || 461 opcode == InstructionConstants.OP_DRETURN || 462 opcode == InstructionConstants.OP_ARETURN || 463 opcode == InstructionConstants.OP_ATHROW) 464 { 465 // Mark the branch origin. 466 markBranchOrigin(offset); 467 468 // Mark the next instruction. 469 markAfterBranchOrigin(offset + simpleInstruction.length(offset)); 470 } 471 } 472 473 474 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 475 { 476 // Mark the instruction. 477 instructionMarks[offset] |= INSTRUCTION; 478 479 // Check if this is the first instruction of a subroutine. 480 checkSubroutine(offset); 481 482 // Check if the instruction is a 'new' instruction. 483 if (constantInstruction.opcode == InstructionConstants.OP_NEW) 484 { 485 // Push the 'new' instruction offset on the stack. 486 recentCreationOffsets[recentCreationOffsetIndex++] = offset; 487 } 488 else 489 { 490 // Check if the instruction is an initializer invocation. 491 isInitializer = false; 492 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); 493 if (isInitializer) 494 { 495 // Pop the 'new' instruction offset from the stack. 496 int recentCreationOffset = recentCreationOffsets[--recentCreationOffsetIndex]; 497 498 // Fill it out in the creation offsets. 499 creationOffsets[offset] = recentCreationOffset; 500 501 // Fill out the initialization offsets. 502 if (recentCreationOffset == AT_METHOD_ENTRY) 503 { 504 superInitializationOffset = offset; 505 } 506 else 507 { 508 initializationOffsets[recentCreationOffset] = offset; 509 } 510 } 511 } 512 } 513 514 515 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 516 { 517 // Mark the instruction. 518 instructionMarks[offset] |= INSTRUCTION; 519 520 // Check if this is the first instruction of a subroutine. 521 checkSubroutine(offset); 522 523 if (variableInstruction.opcode == InstructionConstants.OP_RET) 524 { 525 // Mark the branch origin. 526 markBranchOrigin(offset); 527 528 // Mark the regular subroutine return. 529 instructionMarks[offset] |= SUBROUTINE_RETURNING; 530 531 // Mark the next instruction. 532 markAfterBranchOrigin(offset + variableInstruction.length(offset)); 533 } 534 } 535 536 537 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 538 { 539 // Mark the branch origin. 540 markBranchOrigin(offset); 541 542 // Check if this is the first instruction of a subroutine. 543 checkSubroutine(offset); 544 545 // Mark the branch target. 546 markBranchTarget(offset, branchInstruction.branchOffset); 547 548 byte opcode = branchInstruction.opcode; 549 if (opcode == InstructionConstants.OP_JSR || 550 opcode == InstructionConstants.OP_JSR_W) 551 { 552 // Mark the subroutine invocation. 553 instructionMarks[offset] |= SUBROUTINE_INVOCATION; 554 555 // Mark the subroutine start. 556 int targetOffset = offset + branchInstruction.branchOffset; 557 subroutineStarts[targetOffset] = targetOffset; 558 } 559 else if (opcode == InstructionConstants.OP_GOTO || 560 opcode == InstructionConstants.OP_GOTO_W) 561 { 562 // Mark the next instruction. 563 markAfterBranchOrigin(offset + branchInstruction.length(offset)); 564 } 565 } 566 567 568 public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) 569 { 570 // Mark the branch origin. 571 markBranchOrigin(offset); 572 573 // Check if this is the first instruction of a subroutine. 574 checkSubroutine(offset); 575 576 // Mark the branch targets of the default jump offset. 577 markBranchTarget(offset, switchInstruction.defaultOffset); 578 579 // Mark the branch targets of the jump offsets. 580 markBranchTargets(offset, 581 switchInstruction.jumpOffsets); 582 583 // Mark the next instruction. 584 markAfterBranchOrigin(offset + switchInstruction.length(offset)); 585 } 586 587 588 // Implementations for ConstantVisitor. 589 590 public void visitAnyConstant(Clazz clazz, Constant constant) {} 591 592 593 public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) 594 { 595 isInitializer = methodrefConstant.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT); 596 } 597 598 599 // Implementations for ExceptionInfoVisitor. 600 601 public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) 602 { 603 // Mark the exception offsets. 604 instructionMarks[exceptionInfo.u2startPC] |= EXCEPTION_START; 605 instructionMarks[exceptionInfo.u2endPC] |= EXCEPTION_END; 606 instructionMarks[exceptionInfo.u2handlerPC] |= EXCEPTION_HANDLER; 607 } 608 609 610 // Small utility methods. 611 612 /** 613 * Marks the branch targets of the given jump offsets for the instruction 614 * at the given offset. 615 */ 616 private void markBranchTargets(int offset, int[] jumpOffsets) 617 { 618 for (int index = 0; index < jumpOffsets.length; index++) 619 { 620 markBranchTarget(offset, jumpOffsets[index]); 621 } 622 } 623 624 625 /** 626 * Marks the branch origin at the given offset. 627 */ 628 private void markBranchOrigin(int offset) 629 { 630 instructionMarks[offset] |= INSTRUCTION | BRANCH_ORIGIN; 631 } 632 633 634 /** 635 * Marks the branch target at the given offset. 636 */ 637 private void markBranchTarget(int offset, int jumpOffset) 638 { 639 int targetOffset = offset + jumpOffset; 640 641 instructionMarks[targetOffset] |= BRANCH_TARGET; 642 643 // Are we inside a previously marked subroutine? 644 if (isSubroutine(offset)) 645 { 646 // Mark the subroutine start of the target. 647 subroutineStarts[targetOffset] = currentSubroutineStart; 648 649 // Update the current subroutine end. 650 if (currentSubroutineEnd < targetOffset) 651 { 652 currentSubroutineEnd = targetOffset; 653 } 654 } 655 } 656 657 658 /** 659 * Marks the instruction at the given offset, after a branch. 660 */ 661 private void markAfterBranchOrigin(int nextOffset) 662 { 663 instructionMarks[nextOffset] |= AFTER_BRANCH; 664 665 // Are we at the end of the current subroutine? 666 if (currentSubroutineEnd <= nextOffset) 667 { 668 // Reset the subroutine start. 669 currentSubroutineStart = NONE; 670 } 671 } 672 673 674 /** 675 * Checks if the specified instruction is inside a subroutine. 676 */ 677 private void checkSubroutine(int offset) 678 { 679 // Are we inside a previously marked subroutine? 680 if (isSubroutine(offset)) 681 { 682 // Update the current subroutine start. 683 currentSubroutineStart = subroutineStarts[offset]; 684 } 685 else 686 { 687 // Mark the subroutine start (or NONE). 688 subroutineStarts[offset] = currentSubroutineStart; 689 } 690 } 691} 692