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.instruction; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.CodeAttribute; 25import proguard.classfile.instruction.visitor.InstructionVisitor; 26 27/** 28 * This interface describes an instruction that branches to a given offset in 29 * the code. 30 * 31 * @author Eric Lafortune 32 */ 33public class BranchInstruction extends Instruction 34{ 35 public int branchOffset; 36 37 38 /** 39 * Creates an uninitialized BranchInstruction. 40 */ 41 public BranchInstruction() {} 42 43 44 public BranchInstruction(byte opcode, int branchOffset) 45 { 46 this.opcode = opcode; 47 this.branchOffset = branchOffset; 48 } 49 50 51 /** 52 * Copies the given instruction into this instruction. 53 * @param branchInstruction the instruction to be copied. 54 * @return this instruction. 55 */ 56 public BranchInstruction copy(BranchInstruction branchInstruction) 57 { 58 this.opcode = branchInstruction.opcode; 59 this.branchOffset = branchInstruction.branchOffset; 60 61 return this; 62 } 63 64 65 // Implementations for Instruction. 66 67 public byte canonicalOpcode() 68 { 69 // Remove the _w extension, if any. 70 switch (opcode) 71 { 72 case InstructionConstants.OP_GOTO_W: return InstructionConstants.OP_GOTO; 73 74 case InstructionConstants.OP_JSR_W: return InstructionConstants.OP_JSR; 75 76 default: return opcode; 77 } 78 } 79 80 public Instruction shrink() 81 { 82 // Do we need an ordinary branch or a wide branch? 83 if (requiredBranchOffsetSize() == 2) 84 { 85 // Can we replace the wide branch by an ordinary branch? 86 if (opcode == InstructionConstants.OP_GOTO_W) 87 { 88 opcode = InstructionConstants.OP_GOTO; 89 } 90 else if (opcode == InstructionConstants.OP_JSR_W) 91 { 92 opcode = InstructionConstants.OP_JSR; 93 } 94 } 95 else 96 { 97 // Should we replace the ordinary branch by a wide branch? 98 if (opcode == InstructionConstants.OP_GOTO) 99 { 100 opcode = InstructionConstants.OP_GOTO_W; 101 } 102 else if (opcode == InstructionConstants.OP_JSR) 103 { 104 opcode = InstructionConstants.OP_JSR_W; 105 } 106 else 107 { 108 throw new IllegalArgumentException("Branch instruction can't be widened ("+this.toString()+")"); 109 } 110 } 111 112 return this; 113 } 114 115 protected void readInfo(byte[] code, int offset) 116 { 117 branchOffset = readSignedValue(code, offset, branchOffsetSize()); 118 } 119 120 121 protected void writeInfo(byte[] code, int offset) 122 { 123 if (requiredBranchOffsetSize() > branchOffsetSize()) 124 { 125 throw new IllegalArgumentException("Instruction has invalid branch offset size ("+this.toString(offset)+")"); 126 } 127 128 writeSignedValue(code, offset, branchOffset, branchOffsetSize()); 129 } 130 131 132 public int length(int offset) 133 { 134 return 1 + branchOffsetSize(); 135 } 136 137 138 public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) 139 { 140 instructionVisitor.visitBranchInstruction(clazz, method, codeAttribute, offset, this); 141 } 142 143 144 public String toString(int offset) 145 { 146 return "["+offset+"] "+toString()+" (target="+(offset+branchOffset)+")"; 147 } 148 149 150 // Implementations for Object. 151 152 public String toString() 153 { 154 return getName()+" "+(branchOffset >= 0 ? "+" : "")+branchOffset; 155 } 156 157 158 // Small utility methods. 159 160 /** 161 * Returns the branch offset size for this instruction. 162 */ 163 private int branchOffsetSize() 164 { 165 return opcode == InstructionConstants.OP_GOTO_W || 166 opcode == InstructionConstants.OP_JSR_W ? 4 : 167 2; 168 } 169 170 171 /** 172 * Computes the required branch offset size for this instruction's branch 173 * offset. 174 */ 175 private int requiredBranchOffsetSize() 176 { 177 return branchOffset << 16 >> 16 == branchOffset ? 2 : 178 4; 179 } 180} 181