001    package serp.bytecode;
002    
003    import java.io.*;
004    import java.util.*;
005    
006    import serp.bytecode.visitor.*;
007    import serp.util.*;
008    
009    /**
010     * The <code>lookupswitch</code> instruction.
011     *
012     * @author Abe White
013     */
014    public class LookupSwitchInstruction extends JumpInstruction {
015        // case info
016        private List _matches = new LinkedList();
017        private List _cases = new LinkedList();
018    
019        LookupSwitchInstruction(Code owner) {
020            super(owner, Constants.LOOKUPSWITCH);
021        }
022    
023        int getLength() {
024            // don't call super.getLength(), cause JumpInstruction will return
025            // value assuming this is an 'if' or 'goto' instruction
026            int length = 1;
027    
028            // make the first byte of the 'default' a multiple of 4 from the
029            // start of the method
030            int byteIndex = getByteIndex() + 1;
031            for (; (byteIndex % 4) != 0; byteIndex++, length++);
032    
033            // default, npairs
034            length += 8;
035    
036            // pairs
037            length += (8 * _matches.size());
038            return length;
039        }
040    
041        public int getStackChange() {
042            return -1;
043        }
044    
045        /**
046         * Synonymous with {@link #getTarget}.
047         */
048        public Instruction getDefaultTarget() {
049            return getTarget();
050        }
051    
052        /**
053         * Synonymous with {@link #setTarget}.
054         */
055        public LookupSwitchInstruction setDefaultTarget(Instruction ins) {
056            return (LookupSwitchInstruction) setTarget(ins);
057        }
058    
059        /**
060         * Synonymous with {@link #getOffset}.
061         */
062        public int getDefaultOffset() {
063            return getOffset();
064        }
065    
066        /**
067         * Synonymous with {@link #setOffset}.
068         */
069        public LookupSwitchInstruction setDefaultOffset(int offset) {
070            setOffset(offset);
071            return this;
072        }
073    
074        /**
075         * Set the match-jumppt pairs for this switch.
076         *
077         * @return this instruction, for method chaining
078         */
079        public LookupSwitchInstruction setCases(int[] matches, 
080            Instruction[] targets) {
081            _matches.clear();
082            _cases.clear();
083            for (int i = 0; i < matches.length; i++)
084                _matches.add(Numbers.valueOf(matches[i]));
085            for (int i = 0; i < targets.length; i++) {
086                InstructionPtrStrategy next = new InstructionPtrStrategy(this);
087                next.setTargetInstruction(targets[i]);
088                _cases.add(next);
089            }
090            invalidateByteIndexes();
091            return this;
092        }
093    
094        public int[] getOffsets() {
095            int bi = getByteIndex();
096            int[] offsets = new int[_cases.size()];
097            for (int i = 0; i < offsets.length; i++)
098                offsets[i] = ((InstructionPtrStrategy) _cases.get(i)).getByteIndex()
099                    - bi;
100            return offsets;
101        }
102    
103        /**
104         * Return the values of the case statements for this switch.
105         */
106        public int[] getMatches() {
107            int[] matches = new int[_matches.size()];
108            Iterator itr = _matches.iterator();
109            for (int i = 0; i < matches.length; i++)
110                matches[i] = ((Integer) itr.next()).intValue();
111            return matches;
112        }
113    
114        /**
115         * Return the targets of the case statements for this switch.
116         */
117        public Instruction[] getTargets() {
118            Instruction[] result = new Instruction[_cases.size()];
119            for (int i = 0; i < result.length; i++)
120                result[i] = ((InstructionPtrStrategy) _cases.get(i)).
121                    getTargetInstruction();
122            return result;
123        }
124    
125        /**
126         * Add a case to this switch.
127         *
128         * @return this instruction, for method chaining
129         */
130        public LookupSwitchInstruction addCase(int match, Instruction target) {
131            _matches.add(Numbers.valueOf(match));
132            _cases.add(new InstructionPtrStrategy(this, target));
133            invalidateByteIndexes();
134            return this;
135        }
136    
137        private Instruction findJumpPoint(int jumpByteIndex, List inss) {
138            Instruction ins;
139            for (Iterator itr = inss.iterator(); itr.hasNext();) {
140                ins = (Instruction) itr.next();
141                if (ins.getByteIndex() == jumpByteIndex)
142                    return ins;
143            }
144            return null;
145        }
146    
147        public void updateTargets() {
148            super.updateTargets();
149            for (Iterator itr = _cases.iterator(); itr.hasNext();)
150                ((InstructionPtrStrategy) itr.next()).updateTargets();
151        }
152    
153        public void replaceTarget(Instruction oldTarget, Instruction newTarget) {
154            super.replaceTarget(oldTarget, newTarget);
155            for (Iterator itr = _cases.iterator(); itr.hasNext();)
156                ((InstructionPtrStrategy) itr.next()).replaceTarget(oldTarget,
157                    newTarget);
158        }
159    
160        public void acceptVisit(BCVisitor visit) {
161            visit.enterLookupSwitchInstruction(this);
162            visit.exitLookupSwitchInstruction(this);
163        }
164    
165        void read(Instruction orig) {
166            super.read(orig);
167    
168            LookupSwitchInstruction ins = (LookupSwitchInstruction) orig;
169            _matches = new LinkedList(ins._matches);
170            _cases.clear();
171            for (Iterator itr = ins._cases.iterator(); itr.hasNext();) {
172                InstructionPtrStrategy origPtr = (InstructionPtrStrategy)itr.next();
173                InstructionPtrStrategy newPtr = new InstructionPtrStrategy(this);
174                newPtr.setByteIndex(origPtr.getByteIndex());
175                _cases.add(newPtr);
176            }
177            invalidateByteIndexes();
178        }
179    
180        void read(DataInput in) throws IOException {
181            // don't call super
182            int bi = getByteIndex();
183            for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++)
184                in.readByte();
185    
186            setOffset(in.readInt());
187            _matches.clear();
188            _cases.clear();
189            for (int i = 0, pairCount = in.readInt(); i < pairCount; i++) {
190                _matches.add(Numbers.valueOf(in.readInt()));
191                InstructionPtrStrategy next = new InstructionPtrStrategy(this);
192                next.setByteIndex(bi + in.readInt());
193                _cases.add(next);
194            }
195        }
196    
197        void write(DataOutput out) throws IOException {
198            // don't call super
199            int bi = getByteIndex();
200            for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++)
201                out.writeByte(0);
202    
203            out.writeInt(getOffset());
204            out.writeInt(_matches.size());
205            for (int i = 0; i < _matches.size(); i++) {
206                out.writeInt(((Integer) _matches.get(i)).intValue());
207                out.writeInt(((InstructionPtrStrategy) _cases.get(i)).getByteIndex()
208                    - bi);
209            }
210        }
211    }