001: /*
002: * ProGuard -- shrinking, optimization, obfuscation, and preverification
003: * of Java bytecode.
004: *
005: * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
006: *
007: * This program is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License as published by the Free
009: * Software Foundation; either version 2 of the License, or (at your option)
010: * any later version.
011: *
012: * This program is distributed in the hope that it will be useful, but WITHOUT
013: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
015: * more details.
016: *
017: * You should have received a copy of the GNU General Public License along
018: * with this program; if not, write to the Free Software Foundation, Inc.,
019: * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: package proguard.classfile.instruction;
022:
023: import proguard.classfile.*;
024: import proguard.classfile.attribute.CodeAttribute;
025: import proguard.classfile.instruction.visitor.InstructionVisitor;
026:
027: /**
028: * This interface describes an instruction that branches to a given offset in
029: * the code.
030: *
031: * @author Eric Lafortune
032: */
033: public class BranchInstruction extends Instruction {
034: public int branchOffset;
035:
036: /**
037: * Creates an uninitialized BranchInstruction.
038: */
039: public BranchInstruction() {
040: }
041:
042: public BranchInstruction(byte opcode, int branchOffset) {
043: this .opcode = opcode;
044: this .branchOffset = branchOffset;
045: }
046:
047: /**
048: * Copies the given instruction into this instruction.
049: * @param branchInstruction the instruction to be copied.
050: * @return this instruction.
051: */
052: public BranchInstruction copy(BranchInstruction branchInstruction) {
053: this .opcode = branchInstruction.opcode;
054: this .branchOffset = branchInstruction.branchOffset;
055:
056: return this ;
057: }
058:
059: // Implementations for Instruction.
060:
061: public byte canonicalOpcode() {
062: // Remove the _w extension, if any.
063: switch (opcode) {
064: case InstructionConstants.OP_GOTO_W:
065: return InstructionConstants.OP_GOTO;
066:
067: case InstructionConstants.OP_JSR_W:
068: return InstructionConstants.OP_JSR;
069:
070: default:
071: return opcode;
072: }
073: }
074:
075: public Instruction shrink() {
076: // Do we need an ordinary branch or a wide branch?
077: if (requiredBranchOffsetSize() == 2) {
078: // Can we replace the wide branch by an ordinary branch?
079: if (opcode == InstructionConstants.OP_GOTO_W) {
080: opcode = InstructionConstants.OP_GOTO;
081: } else if (opcode == InstructionConstants.OP_JSR_W) {
082: opcode = InstructionConstants.OP_JSR;
083: }
084: } else {
085: // Should we replace the ordinary branch by a wide branch?
086: if (opcode == InstructionConstants.OP_GOTO) {
087: opcode = InstructionConstants.OP_GOTO_W;
088: } else if (opcode == InstructionConstants.OP_JSR) {
089: opcode = InstructionConstants.OP_JSR_W;
090: } else {
091: throw new IllegalArgumentException(
092: "Branch instruction can't be widened ("
093: + this .toString() + ")");
094: }
095: }
096:
097: return this ;
098: }
099:
100: protected void readInfo(byte[] code, int offset) {
101: branchOffset = readSignedValue(code, offset, branchOffsetSize());
102: }
103:
104: protected void writeInfo(byte[] code, int offset) {
105: if (requiredBranchOffsetSize() > branchOffsetSize()) {
106: throw new IllegalArgumentException(
107: "Instruction has invalid branch offset size ("
108: + this .toString(offset) + ")");
109: }
110:
111: writeSignedValue(code, offset, branchOffset, branchOffsetSize());
112: }
113:
114: public int length(int offset) {
115: return 1 + branchOffsetSize();
116: }
117:
118: public void accept(Clazz clazz, Method method,
119: CodeAttribute codeAttribute, int offset,
120: InstructionVisitor instructionVisitor) {
121: instructionVisitor.visitBranchInstruction(clazz, method,
122: codeAttribute, offset, this );
123: }
124:
125: public String toString(int offset) {
126: return "[" + offset + "] " + toString() + " (target="
127: + (offset + branchOffset) + ")";
128: }
129:
130: // Implementations for Object.
131:
132: public String toString() {
133: return getName() + " " + (branchOffset >= 0 ? "+" : "")
134: + branchOffset;
135: }
136:
137: // Small utility methods.
138:
139: /**
140: * Returns the branch offset size for this instruction.
141: */
142: private int branchOffsetSize() {
143: return opcode == InstructionConstants.OP_GOTO_W
144: || opcode == InstructionConstants.OP_JSR_W ? 4 : 2;
145: }
146:
147: /**
148: * Computes the required branch offset size for this instruction's branch
149: * offset.
150: */
151: private int requiredBranchOffsetSize() {
152: return branchOffset << 16 >> 16 == branchOffset ? 2 : 4;
153: }
154: }
|