001package serp.bytecode;
002
003import java.io.*;
004import java.util.*;
005
006/**
007 * Contains functionality common to the different switch types
008 * (TableSwitch and LookupSwitch).
009 *
010 * @author Eric Lindauer
011 */
012public 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}