• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2014 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  */
21 package proguard.classfile.instruction;
22 
23 import proguard.classfile.*;
24 import proguard.classfile.attribute.CodeAttribute;
25 import 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  */
33 public class BranchInstruction extends Instruction
34 {
35     public int branchOffset;
36 
37 
38     /**
39      * Creates an uninitialized BranchInstruction.
40      */
BranchInstruction()41     public BranchInstruction() {}
42 
43 
BranchInstruction(byte opcode, int branchOffset)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      */
copy(BranchInstruction branchInstruction)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 
canonicalOpcode()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 
shrink()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 
readInfo(byte[] code, int offset)115     protected void readInfo(byte[] code, int offset)
116     {
117         branchOffset = readSignedValue(code, offset, branchOffsetSize());
118     }
119 
120 
writeInfo(byte[] code, int offset)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 
length(int offset)132     public int length(int offset)
133     {
134         return 1 + branchOffsetSize();
135     }
136 
137 
accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor)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 
toString(int offset)144     public String toString(int offset)
145     {
146         return "["+offset+"] "+toString()+" (target="+(offset+branchOffset)+")";
147     }
148 
149 
150     // Implementations for Object.
151 
toString()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      */
branchOffsetSize()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      */
requiredBranchOffsetSize()175     private int requiredBranchOffsetSize()
176     {
177         return (short)branchOffset == branchOffset ? 2 : 4;
178     }
179 }
180