1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.bcel.generic;
18
19 import java.io.DataOutputStream;
20 import java.io.IOException;
21 import org.apache.bcel.util.ByteSequence;
22
23 /***
24 * Abstract super class for branching instructions like GOTO, IFEQ, etc..
25 * Branch instructions may have a variable length, namely GOTO, JSR,
26 * LOOKUPSWITCH and TABLESWITCH.
27 *
28 * @see InstructionList
29 * @version $Id: BranchInstruction.java 386056 2006-03-15 11:31:56Z tcurdt $
30 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
31 */
32 public abstract class BranchInstruction extends Instruction implements InstructionTargeter {
33
34 protected int index;
35 protected InstructionHandle target;
36 protected int position;
37
38
39 /***
40 * Empty constructor needed for the Class.newInstance() statement in
41 * Instruction.readInstruction(). Not to be used otherwise.
42 */
43 BranchInstruction() {
44 }
45
46
47 /*** Common super constructor
48 * @param opcode Instruction opcode
49 * @param target instruction to branch to
50 */
51 protected BranchInstruction(short opcode, InstructionHandle target) {
52 super(opcode, (short) 3);
53 setTarget(target);
54 }
55
56
57 /***
58 * Dump instruction as byte code to stream out.
59 * @param out Output stream
60 */
61 public void dump( DataOutputStream out ) throws IOException {
62 out.writeByte(opcode);
63 index = getTargetOffset();
64 if (Math.abs(index) >= 32767) {
65 throw new ClassGenException("Branch target offset too large for short");
66 }
67 out.writeShort(index);
68 }
69
70
71 /***
72 * @param _target branch target
73 * @return the offset to `target' relative to this instruction
74 */
75 protected int getTargetOffset( InstructionHandle _target ) {
76 if (_target == null) {
77 throw new ClassGenException("Target of " + super.toString(true)
78 + " is invalid null handle");
79 }
80 int t = _target.getPosition();
81 if (t < 0) {
82 throw new ClassGenException("Invalid branch target position offset for "
83 + super.toString(true) + ":" + t + ":" + _target);
84 }
85 return t - position;
86 }
87
88
89 /***
90 * @return the offset to this instruction's target
91 */
92 protected int getTargetOffset() {
93 return getTargetOffset(target);
94 }
95
96
97 /***
98 * Called by InstructionList.setPositions when setting the position for every
99 * instruction. In the presence of variable length instructions `setPositions'
100 * performs multiple passes over the instruction list to calculate the
101 * correct (byte) positions and offsets by calling this function.
102 *
103 * @param offset additional offset caused by preceding (variable length) instructions
104 * @param max_offset the maximum offset that may be caused by these instructions
105 * @return additional offset caused by possible change of this instruction's length
106 */
107 protected int updatePosition( int offset, int max_offset ) {
108 position += offset;
109 return 0;
110 }
111
112
113 /***
114 * Long output format:
115 *
116 * <position in byte code>
117 * <name of opcode> "["<opcode number>"]"
118 * "("<length of instruction>")"
119 * "<"<target instruction>">" "@"<branch target offset>
120 *
121 * @param verbose long/short format switch
122 * @return mnemonic for instruction
123 */
124 public String toString( boolean verbose ) {
125 String s = super.toString(verbose);
126 String t = "null";
127 if (verbose) {
128 if (target != null) {
129 if (target.getInstruction() == this) {
130 t = "<points to itself>";
131 } else if (target.getInstruction() == null) {
132 t = "<null instruction!!!?>";
133 } else {
134 t = target.getInstruction().toString(false);
135 }
136 }
137 } else {
138 if (target != null) {
139 index = getTargetOffset();
140 t = "" + (index + position);
141 }
142 }
143 return s + " -> " + t;
144 }
145
146
147 /***
148 * Read needed data (e.g. index) from file. Conversion to a InstructionHandle
149 * is done in InstructionList(byte[]).
150 *
151 * @param bytes input stream
152 * @param wide wide prefix?
153 * @see InstructionList
154 */
155 protected void initFromFile( ByteSequence bytes, boolean wide ) throws IOException {
156 length = 3;
157 index = bytes.readShort();
158 }
159
160
161 /***
162 * @return target offset in byte code
163 */
164 public final int getIndex() {
165 return index;
166 }
167
168
169 /***
170 * @return target of branch instruction
171 */
172 public InstructionHandle getTarget() {
173 return target;
174 }
175
176
177 /***
178 * Set branch target
179 * @param target branch target
180 */
181 public void setTarget( InstructionHandle target ) {
182 notifyTarget(this.target, target, this);
183 this.target = target;
184 }
185
186
187 /***
188 * Used by BranchInstruction, LocalVariableGen, CodeExceptionGen
189 */
190 static final void notifyTarget( InstructionHandle old_ih, InstructionHandle new_ih,
191 InstructionTargeter t ) {
192 if (old_ih != null) {
193 old_ih.removeTargeter(t);
194 }
195 if (new_ih != null) {
196 new_ih.addTargeter(t);
197 }
198 }
199
200
201 /***
202 * @param old_ih old target
203 * @param new_ih new target
204 */
205 public void updateTarget( InstructionHandle old_ih, InstructionHandle new_ih ) {
206 if (target == old_ih) {
207 setTarget(new_ih);
208 } else {
209 throw new ClassGenException("Not targeting " + old_ih + ", but " + target);
210 }
211 }
212
213
214 /***
215 * @return true, if ih is target of this instruction
216 */
217 public boolean containsTarget( InstructionHandle ih ) {
218 return (target == ih);
219 }
220
221
222 /***
223 * Inform target that it's not targeted anymore.
224 */
225 void dispose() {
226 setTarget(null);
227 index = -1;
228 position = -1;
229 }
230 }