View Javadoc

1   package serp.bytecode;
2   
3   import java.io.*;
4   import java.util.*;
5   
6   /**
7    * Contains functionality common to the different switch types
8    * (TableSwitch and LookupSwitch).
9    *
10   * @author Eric Lindauer
11   */
12  public abstract class SwitchInstruction extends JumpInstruction {
13      private List _cases = new LinkedList();
14  
15      public SwitchInstruction(Code owner, int opcode) {
16          super(owner, opcode);
17      }
18  
19      /**
20       * Returns the current byte offsets for the different
21       * switch cases in this Instruction.
22       */
23      public int[] getOffsets() {
24          int bi = getByteIndex();
25          int[] offsets = new int[_cases.size()];
26          for (int i = 0; i < offsets.length; i++)
27              offsets[i] = ((InstructionPtrStrategy) _cases.get(i)).getByteIndex()
28                  - bi;
29          return offsets;
30      }
31  
32      /**
33       * Sets the offsets for the instructions representing the different
34       * switch statement cases. WARNING: these offsets will not be changed
35       * in the event that the code is modified following this call. It is
36       * typically a good idea to follow this call with a call to updateTargets
37       * as soon as the instructions at the given offsets are valid, at which
38       * point the Instructions themselves will be used as the targets and the
39       * offsets will be updated as expected.
40       */
41      public void setOffsets(int[] offsets) {
42          int bi = getByteIndex();
43          _cases.clear();
44          for (int i = 0; i < offsets.length; i++) {
45              InstructionPtrStrategy next = new InstructionPtrStrategy(this);
46              next.setByteIndex(offsets[i] + bi);
47              _cases.add(next);
48          }
49      }
50  
51      public int countTargets() {
52          return _cases.size();
53      }
54  
55      int getLength() {
56          // don't call super.getLength(), cause JumpInstruction will return
57          // value assuming this is an 'if' or 'goto' instruction
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          return length;
65      }
66  
67      /**
68       * Synonymous with {@link #getTarget}.
69       */
70      public Instruction getDefaultTarget() {
71          return getTarget();
72      }
73  
74      /**
75       * Synonymous with {@link #getOffset}.
76       */
77      public int getDefaultOffset() {
78          return getOffset();
79      }
80  
81      /**
82       * Synonymous with {@link #setOffset}.
83       */
84      public SwitchInstruction setDefaultOffset(int offset) {
85          setOffset(offset);
86          return this;
87      }
88  
89      /**
90       * Synonymous with {@link #setTarget}.
91       */
92      public SwitchInstruction setDefaultTarget(Instruction ins) {
93          return (SwitchInstruction) setTarget(ins);
94      }
95  
96      /**
97       * Return the targets for this switch, or empty array if not set.
98       */
99      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 }