001    package serp.bytecode;
002    
003    import java.io.*;
004    import java.util.*;
005    
006    import serp.bytecode.visitor.*;
007    
008    /**
009     * The <code>tableswitch</code> instruction.
010     *
011     * @author Abe White
012     */
013    public class TableSwitchInstruction extends JumpInstruction {
014        // case info
015        private int _low = 0;
016        private int _high = 0;
017        private List _cases = new LinkedList();
018    
019        TableSwitchInstruction(Code owner) {
020            super(owner, Constants.TABLESWITCH);
021        }
022    
023        /**
024         * Returns the current byte offsets for the different
025         * switch cases in this Instruction.
026         */
027        public int[] getOffsets() {
028            int bi = getByteIndex();
029            int[] offsets = new int[_cases.size()];
030            for (int i = 0; i < _cases.size(); i++)
031                offsets[i] = ((InstructionPtrStrategy) _cases.get(i)).getByteIndex()
032                    - bi;
033            return offsets;
034        }
035    
036        /**
037         * Sets the offsets for the instructions representing the different
038         * switch statement cases. WARNING: these offsets will not be changed
039         * in the event that the code is modified following this call. It is
040         * typically a good idea to follow this call with a call to updateTargets
041         * as soon as the instructions at the given offsets are valid, at which
042         * point the Instructions themselves will be used as the targets and the
043         * offsets will be updated as expected.
044         */
045        public void setOffsets(int[] offsets) {
046            int bi = getByteIndex();
047            _cases.clear();
048            for (int i = 0; i < offsets.length; i++) {
049                InstructionPtrStrategy next = new InstructionPtrStrategy(this);
050                next.setByteIndex(offsets[i] + bi);
051                _cases.add(next);
052            }
053            invalidateByteIndexes();
054        }
055    
056        int getLength() {
057            // don't call super
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    
065            // default, low, high
066            length += 12;
067    
068            // offsets
069            length += (4 * _cases.size());
070            return length;
071        }
072    
073        /**
074         * Synonymous with {@link #getTarget}.
075         */
076        public Instruction getDefaultTarget() {
077            return getTarget();
078        }
079    
080        /**
081         * Synonymous with {@link #setTarget}.
082         */
083        public TableSwitchInstruction setDefaultTarget(Instruction ins) {
084            return (TableSwitchInstruction) setTarget(ins);
085        }
086    
087        /**
088         * Synonymous with {@link #getOffset}.
089         */
090        public int getDefaultOffset() {
091            return getOffset();
092        }
093    
094        /**
095         * Synonymous with {@link #setOffset}.
096         */
097        public TableSwitchInstruction setDefaultOffset(int offset) {
098            setOffset(offset);
099            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    }