001package serp.bytecode;
002
003import java.io.*;
004import java.util.*;
005
006import serp.bytecode.visitor.*;
007
008/**
009 * The <code>tableswitch</code> instruction.
010 *
011 * @author Abe White
012 */
013public 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}