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 * Select - Abstract super class for LOOKUPSWITCH and TABLESWITCH instructions.
25 *
26 * <p>We use our super's <code>target</code> property as the default target.
27 *
28 * @version $Id: Select.java 386056 2006-03-15 11:31:56Z tcurdt $
29 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
30 * @see LOOKUPSWITCH
31 * @see TABLESWITCH
32 * @see InstructionList
33 */
34 public abstract class Select extends BranchInstruction implements VariableLengthInstruction,
35 StackProducer {
36
37 protected int[] match;
38 protected int[] indices;
39 protected InstructionHandle[] targets;
40 protected int fixed_length;
41 protected int match_length;
42 protected int padding = 0;
43
44
45 /***
46 * Empty constructor needed for the Class.newInstance() statement in
47 * Instruction.readInstruction(). Not to be used otherwise.
48 */
49 Select() {
50 }
51
52
53 /***
54 * (Match, target) pairs for switch.
55 * `Match' and `targets' must have the same length of course.
56 *
57 * @param match array of matching values
58 * @param targets instruction targets
59 * @param defaultTarget default instruction target
60 */
61 Select(short opcode, int[] match, InstructionHandle[] targets, InstructionHandle defaultTarget) {
62 super(opcode, defaultTarget);
63 this.targets = targets;
64 for (int i = 0; i < targets.length; i++) {
65 notifyTarget(null, targets[i], this);
66 }
67 this.match = match;
68 if ((match_length = match.length) != targets.length) {
69 throw new ClassGenException("Match and target array have not the same length");
70 }
71 indices = new int[match_length];
72 }
73
74
75 /***
76 * Since this is a variable length instruction, it may shift the following
77 * instructions which then need to update their position.
78 *
79 * Called by InstructionList.setPositions when setting the position for every
80 * instruction. In the presence of variable length instructions `setPositions'
81 * performs multiple passes over the instruction list to calculate the
82 * correct (byte) positions and offsets by calling this function.
83 *
84 * @param offset additional offset caused by preceding (variable length) instructions
85 * @param max_offset the maximum offset that may be caused by these instructions
86 * @return additional offset caused by possible change of this instruction's length
87 */
88 protected int updatePosition( int offset, int max_offset ) {
89 position += offset;
90 short old_length = length;
91
92
93 padding = (4 - ((position + 1) % 4)) % 4;
94 length = (short) (fixed_length + padding);
95 return length - old_length;
96 }
97
98
99 /***
100 * Dump instruction as byte code to stream out.
101 * @param out Output stream
102 */
103 public void dump( DataOutputStream out ) throws IOException {
104 out.writeByte(opcode);
105 for (int i = 0; i < padding; i++) {
106 out.writeByte(0);
107 }
108 index = getTargetOffset();
109 out.writeInt(index);
110 }
111
112
113 /***
114 * Read needed data (e.g. index) from file.
115 */
116 protected void initFromFile( ByteSequence bytes, boolean wide ) throws IOException {
117 padding = (4 - (bytes.getIndex() % 4)) % 4;
118 for (int i = 0; i < padding; i++) {
119 bytes.readByte();
120 }
121
122 index = bytes.readInt();
123 }
124
125
126 /***
127 * @return mnemonic for instruction
128 */
129 public String toString( boolean verbose ) {
130 StringBuffer buf = new StringBuffer(super.toString(verbose));
131 if (verbose) {
132 for (int i = 0; i < match_length; i++) {
133 String s = "null";
134 if (targets[i] != null) {
135 s = targets[i].getInstruction().toString();
136 }
137 buf.append("(").append(match[i]).append(", ").append(s).append(" = {").append(
138 indices[i]).append("})");
139 }
140 } else {
141 buf.append(" ...");
142 }
143 return buf.toString();
144 }
145
146
147 /***
148 * Set branch target for `i'th case
149 */
150 public void setTarget( int i, InstructionHandle target ) {
151 notifyTarget(targets[i], target, this);
152 targets[i] = target;
153 }
154
155
156 /***
157 * @param old_ih old target
158 * @param new_ih new target
159 */
160 public void updateTarget( InstructionHandle old_ih, InstructionHandle new_ih ) {
161 boolean targeted = false;
162 if (target == old_ih) {
163 targeted = true;
164 setTarget(new_ih);
165 }
166 for (int i = 0; i < targets.length; i++) {
167 if (targets[i] == old_ih) {
168 targeted = true;
169 setTarget(i, new_ih);
170 }
171 }
172 if (!targeted) {
173 throw new ClassGenException("Not targeting " + old_ih);
174 }
175 }
176
177
178 /***
179 * @return true, if ih is target of this instruction
180 */
181 public boolean containsTarget( InstructionHandle ih ) {
182 if (target == ih) {
183 return true;
184 }
185 for (int i = 0; i < targets.length; i++) {
186 if (targets[i] == ih) {
187 return true;
188 }
189 }
190 return false;
191 }
192
193
194 protected Object clone() throws CloneNotSupportedException {
195 Select copy = (Select) super.clone();
196 copy.match = (int[]) match.clone();
197 copy.indices = (int[]) indices.clone();
198 copy.targets = (InstructionHandle[]) targets.clone();
199 return copy;
200 }
201
202
203 /***
204 * Inform targets that they're not targeted anymore.
205 */
206 void dispose() {
207 super.dispose();
208 for (int i = 0; i < targets.length; i++) {
209 targets[i].removeTargeter(this);
210 }
211 }
212
213
214 /***
215 * @return array of match indices
216 */
217 public int[] getMatchs() {
218 return match;
219 }
220
221
222 /***
223 * @return array of match target offsets
224 */
225 public int[] getIndices() {
226 return indices;
227 }
228
229
230 /***
231 * @return array of match targets
232 */
233 public InstructionHandle[] getTargets() {
234 return targets;
235 }
236 }