001 package serp.bytecode;
002
003 import java.io.*;
004 import java.util.*;
005
006 /**
007 * Contains functionality common to the different switch types
008 * (TableSwitch and LookupSwitch).
009 *
010 * @author Eric Lindauer
011 */
012 public abstract class SwitchInstruction extends JumpInstruction {
013 private List _cases = new LinkedList();
014
015 public SwitchInstruction(Code owner, int opcode) {
016 super(owner, opcode);
017 }
018
019 /**
020 * Returns the current byte offsets for the different
021 * switch cases in this Instruction.
022 */
023 public int[] getOffsets() {
024 int bi = getByteIndex();
025 int[] offsets = new int[_cases.size()];
026 for (int i = 0; i < offsets.length; i++)
027 offsets[i] = ((InstructionPtrStrategy) _cases.get(i)).getByteIndex()
028 - bi;
029 return offsets;
030 }
031
032 /**
033 * Sets the offsets for the instructions representing the different
034 * switch statement cases. WARNING: these offsets will not be changed
035 * in the event that the code is modified following this call. It is
036 * typically a good idea to follow this call with a call to updateTargets
037 * as soon as the instructions at the given offsets are valid, at which
038 * point the Instructions themselves will be used as the targets and the
039 * offsets will be updated as expected.
040 */
041 public void setOffsets(int[] offsets) {
042 int bi = getByteIndex();
043 _cases.clear();
044 for (int i = 0; i < offsets.length; i++) {
045 InstructionPtrStrategy next = new InstructionPtrStrategy(this);
046 next.setByteIndex(offsets[i] + bi);
047 _cases.add(next);
048 }
049 }
050
051 public int countTargets() {
052 return _cases.size();
053 }
054
055 int getLength() {
056 // don't call super.getLength(), cause JumpInstruction will return
057 // value assuming this is an 'if' or 'goto' instruction
058 int length = 1;
059
060 // make the first byte of the 'default' a multiple of 4 from the
061 // start of the method
062 int byteIndex = getByteIndex() + 1;
063 for (; (byteIndex % 4) != 0; byteIndex++, length++);
064 return length;
065 }
066
067 /**
068 * Synonymous with {@link #getTarget}.
069 */
070 public Instruction getDefaultTarget() {
071 return getTarget();
072 }
073
074 /**
075 * Synonymous with {@link #getOffset}.
076 */
077 public int getDefaultOffset() {
078 return getOffset();
079 }
080
081 /**
082 * Synonymous with {@link #setOffset}.
083 */
084 public SwitchInstruction setDefaultOffset(int offset) {
085 setOffset(offset);
086 return this;
087 }
088
089 /**
090 * Synonymous with {@link #setTarget}.
091 */
092 public SwitchInstruction setDefaultTarget(Instruction ins) {
093 return (SwitchInstruction) setTarget(ins);
094 }
095
096 /**
097 * Return the targets for this switch, or empty array if not set.
098 */
099 public Instruction[] getTargets() {
100 Instruction[] result = new Instruction[_cases.size()];
101 for (int i = 0; i < _cases.size(); i++)
102 result[i] = ((InstructionPtrStrategy) _cases.get(i)).
103 getTargetInstruction();
104 return result;
105 }
106
107 /**
108 * Set the jump points for this switch.
109 *
110 * @return this instruction, for method chaining
111 */
112 public SwitchInstruction setTargets(Instruction[] targets) {
113 _cases.clear();
114 if (targets != null)
115 for (int i = 0; i < targets.length; i++)
116 addTarget(targets[i]);
117 return this;
118 }
119
120 /**
121 * Add a target to this switch.
122 *
123 * @return this instruction, for method chaining
124 */
125 public SwitchInstruction addTarget(Instruction target) {
126 _cases.add(new InstructionPtrStrategy(this, target));
127 return this;
128 }
129
130 public int getStackChange() {
131 return -1;
132 }
133
134 public void updateTargets() {
135 super.updateTargets();
136 for (Iterator itr = _cases.iterator(); itr.hasNext();)
137 ((InstructionPtrStrategy) itr.next()).updateTargets();
138 }
139
140 public void replaceTarget(Instruction oldTarget, Instruction newTarget) {
141 super.replaceTarget(oldTarget, newTarget);
142 for (Iterator itr = _cases.iterator(); itr.hasNext();)
143 ((InstructionPtrStrategy) itr.next()).replaceTarget(oldTarget,
144 newTarget);
145 }
146
147 void read(Instruction orig) {
148 super.read(orig);
149
150 SwitchInstruction ins = (SwitchInstruction) orig;
151 _cases.clear();
152 InstructionPtrStrategy incoming;
153 for (Iterator itr = ins._cases.iterator(); itr.hasNext();) {
154 incoming = (InstructionPtrStrategy) itr.next();
155 InstructionPtrStrategy next = new InstructionPtrStrategy(this);
156 next.setByteIndex(incoming.getByteIndex());
157 _cases.add(next);
158 }
159 }
160
161 void clearTargets() {
162 _cases.clear();
163 }
164
165 void readTarget(DataInput in) throws IOException {
166 InstructionPtrStrategy next = new InstructionPtrStrategy(this);
167 next.setByteIndex(getByteIndex() + in.readInt());
168 _cases.add(next);
169 }
170
171 /**
172 * Set the match-jumppt pairs for this switch.
173 *
174 * @return this instruction, for method chaining
175 */
176 public SwitchInstruction setCases(int[] matches, Instruction[] targets) {
177 setMatches(matches);
178 setTargets(targets);
179 return this;
180 }
181
182 public SwitchInstruction setMatches(int[] matches) {
183 clearMatches();
184 for (int i = 0; i < matches.length; i++)
185 addMatch(matches[i]);
186 return this;
187 }
188
189 /**
190 * Add a case to this switch.
191 *
192 * @return this instruction, for method chaining
193 */
194 public SwitchInstruction addCase(int match, Instruction target) {
195 addMatch(match);
196 addTarget(target);
197 return this;
198 }
199
200 public abstract SwitchInstruction addMatch(int match);
201
202 public abstract int[] getMatches();
203
204 abstract void clearMatches();
205
206 void calculateOpcode() {
207 }
208 }