001    package edu.rice.cs.cunit.classFile.code.instructions;
002    
003    import edu.rice.cs.cunit.classFile.code.Opcode;
004    import edu.rice.cs.cunit.util.Types;
005    
006    import java.util.Arrays;
007    
008    /**
009     * TABLESWITCH Java instruction.
010     *
011     * @author Mathias Ricken
012     */
013    public class TableSwitchInstruction extends AInstruction {
014        /**
015         * Lower bound.
016         */
017        protected long _low;
018    
019        /**
020         * Upper bound.
021         */
022        protected long _high;
023    
024        /**
025         * Branch targets.
026         */
027        protected int[] _targets;
028    
029        /**
030         * Constructor.
031         *
032         * @param defaultTarget default target line number
033         * @param low           lower bound
034         * @param high          upper bound
035         * @param targets       target line nu,bers
036         */
037        public TableSwitchInstruction(int defaultTarget, long low, long high, int[] targets) {
038            if (low > high) {
039                throw new IllegalArgumentException("High cannot be greater than low.");
040            }
041            _low = low;
042            _high = high;
043            long npairs = high - low + 1;
044            if (npairs != targets.length) {
045                throw new IllegalArgumentException("Invalid number of targets");
046            }
047            _targets = new int[(int)npairs + 1];
048            _targets[0] = defaultTarget;
049            System.arraycopy(targets, 0, _targets, 1, (int)npairs);
050        }
051    
052        /**
053         * Get the opcode of this instruction.
054         *
055         * @return opcode
056         */
057        public byte getOpcode() {
058            return Opcode.TABLESWITCH;
059        }
060    
061        /**
062         * Get the length bytecode for this instruction, padded for the specified program counter value.
063         *
064         * @param pc PC for padding
065         *
066         * @return bytecode length
067         */
068        public int getBytecodeLength(int pc) {
069            int pad = 3 - (pc % 4);
070            int res = pad + 9 + (_targets.length) * 4;
071            assert(res <= 0xffff);
072            return res;
073        }
074    
075        /**
076         * Make a new TABLESWITCH instruction from the bytecode stating at pc, padded using paddingPC, and use the line
077         * number table for branches.
078         *
079         * @param bytecode  bytecode
080         * @param pc        starting index in bytecode
081         * @param paddingPC PC for padding
082         * @param lnt       line number table for branches
083         */
084        public TableSwitchInstruction(byte[] bytecode, int pc, int paddingPC, LineNumberTable lnt) {
085            int[] branchTargets = Opcode.getBranchTargets(bytecode, pc, paddingPC);
086            int pad = 3 - (paddingPC % 4);
087            _low = Types.intFromBytes(bytecode, pc + pad + 5);
088            _high = Types.intFromBytes(bytecode, pc + pad + 9);
089    
090            _targets = new int[branchTargets.length];
091            int i = 0;
092            for(int bt : branchTargets) {
093                _targets[i++] = lnt.getLineNumber(bt);
094            }
095        }
096    
097        /**
098         * Get the bytecode for this instruction, padded for the specified program counter value, with branch targets
099         * according to the specified line number table
100         *
101         * @param pc  PC for padding
102         * @param lnt line number table for branches
103         *
104         * @return bytecode
105         */
106        public byte[] getBytecode(int pc, LineNumberTable lnt) {
107            int pad = 3 - (pc % 4);
108            byte[] b = new byte[getBytecodeLength(pc)];
109            b[0] = getOpcode();
110            Types.bytesFromInt(lnt.getPC(_targets[0]) - pc, b, pad + 1);
111            Types.bytesFromInt((int)_low, b, pad + 5);
112            Types.bytesFromInt((int)_high, b, pad + 9);
113            for(int i = 1; i < _targets.length; ++i) {
114                Types.bytesFromInt(lnt.getPC(_targets[i]) - pc, b, pad + 9 + i * 4);
115            }
116            return b;
117        }
118    
119        /**
120         * Return true of this instruction and the other object are equal.
121         *
122         * @param o other object
123         *
124         * @return true if equal
125         */
126        public boolean equals(Object o) {
127            if (this == o) {
128                return true;
129            }
130            if (!(o instanceof TableSwitchInstruction)) {
131                return false;
132            }
133    
134            final TableSwitchInstruction tableSwitchInstruction = (TableSwitchInstruction)o;
135    
136            if (_high != tableSwitchInstruction._high) {
137                return false;
138            }
139            if (_low != tableSwitchInstruction._low) {
140                return false;
141            }
142            if (!Arrays.equals(_targets, tableSwitchInstruction._targets)) {
143                return false;
144            }
145    
146            return true;
147        }
148    
149        /**
150         * Return hash code.
151         *
152         * @return hash code
153         */
154        public int hashCode() {
155            int result;
156            result = (int)(_low ^ (_low >>> 32));
157            result = 29 * result + (int)(_high ^ (_high >>> 32));
158            return result;
159        }
160    
161        /**
162         * Return an array of target indices.
163         *
164         * @return array of target indices.
165         */
166        public int[] getBranchTargets() {
167            int[] bt = new int[_targets.length];
168            int i = 0;
169            for(int t : _targets) {
170                bt[i++] = t;
171            }
172            return bt;
173        }
174    
175        /**
176         * Set the branch target indices.
177         *
178         * @param branchTargets array of target indices
179         */
180        public void setBranchTargets(int[] branchTargets) {
181            if (branchTargets.length != _targets.length) {
182                throw new IllegalArgumentException("TABLESWITCH has incorrect number of targets");
183            }
184            for(int i = 0; i < branchTargets.length; ++i) {
185                assert(branchTargets[i] <= 0xffff);
186                _targets[i] = branchTargets[i];
187            }
188        }
189    
190        /**
191         * Return instruction in human-readable form.
192         *
193         * @return string representation
194         */
195        public String toString() {
196            StringBuilder x = new StringBuilder();
197            x.append(Opcode.getOpcodeName(Opcode.TABLESWITCH));
198            x.append(" default = ");
199            x.append(_targets[0]);
200            x.append(" low = ");
201            x.append(_low);
202            x.append(" high = ");
203            x.append(_high);
204            x.append(" n = ");
205            x.append(_targets.length - 1);
206            for(int i = 0; i < _targets.length - 1; ++i) {
207                x.append(" (");
208                x.append(_low + i);
209                x.append("->");
210                x.append(_targets[i + 1]);
211                x.append(")");
212            }
213            return x.toString();
214        }
215    }
216