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    }