View Javadoc

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      // case info
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          // don't call super
58          int length = 1;
59  
60          // make the first byte of the 'default' a multiple of 4 from the
61          // start of the method
62          int byteIndex = getByteIndex() + 1;
63          for (; (byteIndex % 4) != 0; byteIndex++, length++);
64  
65          // default, low, high
66          length += 12;
67  
68          // offsets
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         // don't call super
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         // don't call super
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 }