1 package serp.bytecode;
2
3 import java.io.*;
4 import java.util.*;
5
6 import serp.bytecode.visitor.*;
7
8 /***
9 * The <code>tableswitch</code> instruction.
10 *
11 * @author Abe White
12 */
13 public class TableSwitchInstruction extends JumpInstruction {
14
15 private int _low = 0;
16 private int _high = 0;
17 private List _cases = new LinkedList();
18
19 TableSwitchInstruction(Code owner) {
20 super(owner, Constants.TABLESWITCH);
21 }
22
23 /***
24 * Returns the current byte offsets for the different
25 * switch cases in this Instruction.
26 */
27 public int[] getOffsets() {
28 int bi = getByteIndex();
29 int[] offsets = new int[_cases.size()];
30 for (int i = 0; i < _cases.size(); i++)
31 offsets[i] = ((InstructionPtrStrategy) _cases.get(i)).getByteIndex()
32 - bi;
33 return offsets;
34 }
35
36 /***
37 * Sets the offsets for the instructions representing the different
38 * switch statement cases. WARNING: these offsets will not be changed
39 * in the event that the code is modified following this call. It is
40 * typically a good idea to follow this call with a call to updateTargets
41 * as soon as the instructions at the given offsets are valid, at which
42 * point the Instructions themselves will be used as the targets and the
43 * offsets will be updated as expected.
44 */
45 public void setOffsets(int[] offsets) {
46 int bi = getByteIndex();
47 _cases.clear();
48 for (int i = 0; i < offsets.length; i++) {
49 InstructionPtrStrategy next = new InstructionPtrStrategy(this);
50 next.setByteIndex(offsets[i] + bi);
51 _cases.add(next);
52 }
53 invalidateByteIndexes();
54 }
55
56 int getLength() {
57
58 int length = 1;
59
60
61
62 int byteIndex = getByteIndex() + 1;
63 for (; (byteIndex % 4) != 0; byteIndex++, length++);
64
65
66 length += 12;
67
68
69 length += (4 * _cases.size());
70 return length;
71 }
72
73 /***
74 * Synonymous with {@link #getTarget}.
75 */
76 public Instruction getDefaultTarget() {
77 return getTarget();
78 }
79
80 /***
81 * Synonymous with {@link #setTarget}.
82 */
83 public TableSwitchInstruction setDefaultTarget(Instruction ins) {
84 return (TableSwitchInstruction) setTarget(ins);
85 }
86
87 /***
88 * Synonymous with {@link #getOffset}.
89 */
90 public int getDefaultOffset() {
91 return getOffset();
92 }
93
94 /***
95 * Synonymous with {@link #setOffset}.
96 */
97 public TableSwitchInstruction setDefaultOffset(int offset) {
98 setOffset(offset);
99 return this;
100 }
101
102 public int getLow() {
103 return _low;
104 }
105
106 public TableSwitchInstruction setLow(int low) {
107 _low = low;
108 return this;
109 }
110
111 public int getHigh() {
112 return _high;
113 }
114
115 public TableSwitchInstruction setHigh(int high) {
116 _high = high;
117 return this;
118 }
119
120 /***
121 * Return the targets for this switch, or empty array if not set.
122 */
123 public Instruction[] getTargets() {
124 Instruction[] result = new Instruction[_cases.size()];
125 for (int i = 0; i < _cases.size(); i++)
126 result[i] = ((InstructionPtrStrategy) _cases.get(i)).
127 getTargetInstruction();
128 return result;
129 }
130
131 /***
132 * Set the jump points for this switch.
133 *
134 * @return this instruction, for method chaining
135 */
136 public TableSwitchInstruction setTargets(Instruction[] targets) {
137 _cases.clear();
138 if (targets != null)
139 for (int i = 0; i < targets.length; i++)
140 addTarget(targets[i]);
141 return this;
142 }
143
144 /***
145 * Add a target to this switch.
146 *
147 * @return this instruction, for method chaining
148 */
149 public TableSwitchInstruction addTarget(Instruction target) {
150 _cases.add(new InstructionPtrStrategy(this, target));
151 invalidateByteIndexes();
152 return this;
153 }
154
155 public int getStackChange() {
156 return -1;
157 }
158
159 private Instruction findTarget(int jumpByteIndex, List inss) {
160 Instruction ins;
161 for (Iterator itr = inss.iterator(); itr.hasNext();) {
162 ins = (Instruction) itr.next();
163 if (ins.getByteIndex() == jumpByteIndex)
164 return ins;
165 }
166 return null;
167 }
168
169 public void updateTargets() {
170 super.updateTargets();
171 for (Iterator itr = _cases.iterator(); itr.hasNext();)
172 ((InstructionPtrStrategy) itr.next()).updateTargets();
173 }
174
175 public void replaceTarget(Instruction oldTarget, Instruction newTarget) {
176 super.replaceTarget(oldTarget, newTarget);
177 for (Iterator itr = _cases.iterator(); itr.hasNext();)
178 ((InstructionPtrStrategy) itr.next()).replaceTarget(oldTarget,
179 newTarget);
180 }
181
182 public void acceptVisit(BCVisitor visit) {
183 visit.enterTableSwitchInstruction(this);
184 visit.exitTableSwitchInstruction(this);
185 }
186
187 void read(Instruction orig) {
188 super.read(orig);
189
190 TableSwitchInstruction ins = (TableSwitchInstruction) orig;
191 setLow(ins.getLow());
192 setHigh(ins.getHigh());
193 InstructionPtrStrategy incoming;
194 for (Iterator itr = ins._cases.iterator(); itr.hasNext();) {
195 incoming = (InstructionPtrStrategy) itr.next();
196 InstructionPtrStrategy next = new InstructionPtrStrategy(this);
197 next.setByteIndex(incoming.getByteIndex());
198 _cases.add(next);
199 }
200 invalidateByteIndexes();
201 }
202
203 void read(DataInput in) throws IOException {
204
205 int bi = getByteIndex();
206 for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++)
207 in.readByte();
208
209 setOffset(in.readInt());
210 setLow(in.readInt());
211 setHigh(in.readInt());
212 _cases.clear();
213 for (int i = 0; i < (_high - _low + 1); i++) {
214 InstructionPtrStrategy next = new InstructionPtrStrategy(this);
215 next.setByteIndex(bi + in.readInt());
216 _cases.add(next);
217 }
218 }
219
220 void write(DataOutput out) throws IOException {
221
222 int bi = getByteIndex();
223 for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++)
224 out.writeByte(0);
225
226 out.writeInt(getOffset());
227 out.writeInt(getLow());
228 out.writeInt(getHigh());
229 for (Iterator itr = _cases.iterator(); itr.hasNext();)
230 out.writeInt(((InstructionPtrStrategy) itr.next()).getByteIndex()
231 - bi);
232 }
233 }