001 package serp.bytecode;
002
003 import java.io.*;
004 import java.util.*;
005
006 import serp.bytecode.visitor.*;
007 import serp.util.*;
008
009 /**
010 * The <code>lookupswitch</code> instruction.
011 *
012 * @author Abe White
013 */
014 public class LookupSwitchInstruction extends JumpInstruction {
015 // case info
016 private List _matches = new LinkedList();
017 private List _cases = new LinkedList();
018
019 LookupSwitchInstruction(Code owner) {
020 super(owner, Constants.LOOKUPSWITCH);
021 }
022
023 int getLength() {
024 // don't call super.getLength(), cause JumpInstruction will return
025 // value assuming this is an 'if' or 'goto' instruction
026 int length = 1;
027
028 // make the first byte of the 'default' a multiple of 4 from the
029 // start of the method
030 int byteIndex = getByteIndex() + 1;
031 for (; (byteIndex % 4) != 0; byteIndex++, length++);
032
033 // default, npairs
034 length += 8;
035
036 // pairs
037 length += (8 * _matches.size());
038 return length;
039 }
040
041 public int getStackChange() {
042 return -1;
043 }
044
045 /**
046 * Synonymous with {@link #getTarget}.
047 */
048 public Instruction getDefaultTarget() {
049 return getTarget();
050 }
051
052 /**
053 * Synonymous with {@link #setTarget}.
054 */
055 public LookupSwitchInstruction setDefaultTarget(Instruction ins) {
056 return (LookupSwitchInstruction) setTarget(ins);
057 }
058
059 /**
060 * Synonymous with {@link #getOffset}.
061 */
062 public int getDefaultOffset() {
063 return getOffset();
064 }
065
066 /**
067 * Synonymous with {@link #setOffset}.
068 */
069 public LookupSwitchInstruction setDefaultOffset(int offset) {
070 setOffset(offset);
071 return this;
072 }
073
074 /**
075 * Set the match-jumppt pairs for this switch.
076 *
077 * @return this instruction, for method chaining
078 */
079 public LookupSwitchInstruction setCases(int[] matches,
080 Instruction[] targets) {
081 _matches.clear();
082 _cases.clear();
083 for (int i = 0; i < matches.length; i++)
084 _matches.add(Numbers.valueOf(matches[i]));
085 for (int i = 0; i < targets.length; i++) {
086 InstructionPtrStrategy next = new InstructionPtrStrategy(this);
087 next.setTargetInstruction(targets[i]);
088 _cases.add(next);
089 }
090 invalidateByteIndexes();
091 return this;
092 }
093
094 public int[] getOffsets() {
095 int bi = getByteIndex();
096 int[] offsets = new int[_cases.size()];
097 for (int i = 0; i < offsets.length; i++)
098 offsets[i] = ((InstructionPtrStrategy) _cases.get(i)).getByteIndex()
099 - bi;
100 return offsets;
101 }
102
103 /**
104 * Return the values of the case statements for this switch.
105 */
106 public int[] getMatches() {
107 int[] matches = new int[_matches.size()];
108 Iterator itr = _matches.iterator();
109 for (int i = 0; i < matches.length; i++)
110 matches[i] = ((Integer) itr.next()).intValue();
111 return matches;
112 }
113
114 /**
115 * Return the targets of the case statements for this switch.
116 */
117 public Instruction[] getTargets() {
118 Instruction[] result = new Instruction[_cases.size()];
119 for (int i = 0; i < result.length; i++)
120 result[i] = ((InstructionPtrStrategy) _cases.get(i)).
121 getTargetInstruction();
122 return result;
123 }
124
125 /**
126 * Add a case to this switch.
127 *
128 * @return this instruction, for method chaining
129 */
130 public LookupSwitchInstruction addCase(int match, Instruction target) {
131 _matches.add(Numbers.valueOf(match));
132 _cases.add(new InstructionPtrStrategy(this, target));
133 invalidateByteIndexes();
134 return this;
135 }
136
137 private Instruction findJumpPoint(int jumpByteIndex, List inss) {
138 Instruction ins;
139 for (Iterator itr = inss.iterator(); itr.hasNext();) {
140 ins = (Instruction) itr.next();
141 if (ins.getByteIndex() == jumpByteIndex)
142 return ins;
143 }
144 return null;
145 }
146
147 public void updateTargets() {
148 super.updateTargets();
149 for (Iterator itr = _cases.iterator(); itr.hasNext();)
150 ((InstructionPtrStrategy) itr.next()).updateTargets();
151 }
152
153 public void replaceTarget(Instruction oldTarget, Instruction newTarget) {
154 super.replaceTarget(oldTarget, newTarget);
155 for (Iterator itr = _cases.iterator(); itr.hasNext();)
156 ((InstructionPtrStrategy) itr.next()).replaceTarget(oldTarget,
157 newTarget);
158 }
159
160 public void acceptVisit(BCVisitor visit) {
161 visit.enterLookupSwitchInstruction(this);
162 visit.exitLookupSwitchInstruction(this);
163 }
164
165 void read(Instruction orig) {
166 super.read(orig);
167
168 LookupSwitchInstruction ins = (LookupSwitchInstruction) orig;
169 _matches = new LinkedList(ins._matches);
170 _cases.clear();
171 for (Iterator itr = ins._cases.iterator(); itr.hasNext();) {
172 InstructionPtrStrategy origPtr = (InstructionPtrStrategy)itr.next();
173 InstructionPtrStrategy newPtr = new InstructionPtrStrategy(this);
174 newPtr.setByteIndex(origPtr.getByteIndex());
175 _cases.add(newPtr);
176 }
177 invalidateByteIndexes();
178 }
179
180 void read(DataInput in) throws IOException {
181 // don't call super
182 int bi = getByteIndex();
183 for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++)
184 in.readByte();
185
186 setOffset(in.readInt());
187 _matches.clear();
188 _cases.clear();
189 for (int i = 0, pairCount = in.readInt(); i < pairCount; i++) {
190 _matches.add(Numbers.valueOf(in.readInt()));
191 InstructionPtrStrategy next = new InstructionPtrStrategy(this);
192 next.setByteIndex(bi + in.readInt());
193 _cases.add(next);
194 }
195 }
196
197 void write(DataOutput out) throws IOException {
198 // don't call super
199 int bi = getByteIndex();
200 for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++)
201 out.writeByte(0);
202
203 out.writeInt(getOffset());
204 out.writeInt(_matches.size());
205 for (int i = 0; i < _matches.size(); i++) {
206 out.writeInt(((Integer) _matches.get(i)).intValue());
207 out.writeInt(((InstructionPtrStrategy) _cases.get(i)).getByteIndex()
208 - bi);
209 }
210 }
211 }