001    package sysModel.classFile.code.instructions;
002    
003    import sysModel.classFile.code.Opcode;
004    import sysModel.classFile.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 short getBytecodeLength(short pc) {
069            int pad = 3 - (pc % 4);
070            return (short)(pad + 9 + (_targets.length) * 4);
071        }
072    
073        /**
074         * Make a new TABLESWITCH instruction from the bytecode stating at pc, padded using paddingPC, and use the line
075         * number table for branches.
076         *
077         * @param bytecode  bytecode
078         * @param pc        starting index in bytecode
079         * @param paddingPC PC for padding
080         * @param lnt       line number table for branches
081         */
082        public TableSwitchInstruction(byte[] bytecode, short pc, short paddingPC, LineNumberTable lnt) {
083            int[] branchTargets = Opcode.getBranchTargets(bytecode, pc, paddingPC);
084            int pad = 3 - (paddingPC % 4);
085            _low = Types.intFromBytes(bytecode, pc + pad + 5);
086            _high = Types.intFromBytes(bytecode, pc + pad + 9);
087    
088            _targets = new int[branchTargets.length];
089            int i = 0;
090            for(int bt : branchTargets) {
091                _targets[i++] = lnt.getLineNumber((short)bt);
092            }
093        }
094    
095        /**
096         * Get the bytecode for this instruction, padded for the specified program counter value, with branch targets
097         * according to the specified line number table
098         *
099         * @param pc  PC for padding
100         * @param lnt line number table for branches
101         *
102         * @return bytecode
103         */
104        public byte[] getBytecode(short pc, LineNumberTable lnt) {
105            int pad = 3 - (pc % 4);
106            byte[] b = new byte[getBytecodeLength(pc)];
107            b[0] = getOpcode();
108            Types.bytesFromInt(lnt.getPC((short)_targets[0]) - pc, b, pad + 1);
109            Types.bytesFromInt((int)_low, b, pad + 5);
110            Types.bytesFromInt((int)_high, b, pad + 9);
111            for(int i = 1; i < _targets.length; ++i) {
112                Types.bytesFromInt(lnt.getPC((short)_targets[i]) - pc, b, pad + 9 + i * 4);
113            }
114            return b;
115        }
116    
117        /**
118         * Return true of this instruction and the other object are equal.
119         *
120         * @param o other object
121         *
122         * @return true if equal
123         */
124        public boolean equals(Object o) {
125            if (this == o) {
126                return true;
127            }
128            if (!(o instanceof TableSwitchInstruction)) {
129                return false;
130            }
131    
132            TableSwitchInstruction tableSwitchInstruction = (TableSwitchInstruction)o;
133    
134            if (_high != tableSwitchInstruction._high) {
135                return false;
136            }
137            if (_low != tableSwitchInstruction._low) {
138                return false;
139            }
140            return Arrays.equals(_targets, tableSwitchInstruction._targets);
141    
142        }
143    
144        /**
145         * Return hash code.
146         *
147         * @return hash code
148         */
149        public int hashCode() {
150            int result;
151            result = (int)(_low ^ (_low >>> 32));
152            result = 29 * result + (int)(_high ^ (_high >>> 32));
153            return result;
154        }
155    
156        /**
157         * Return an array of target indices.
158         *
159         * @return array of target indices.
160         */
161        public short[] getBranchTargets() {
162            short[] bt = new short[_targets.length];
163            int i = 0;
164            for(int t : _targets) {
165                bt[i++] = (short)t;
166            }
167            return bt;
168        }
169    
170        /**
171         * Set the branch target indices.
172         *
173         * @param branchTargets array of target indices
174         */
175        public void setBranchTargets(short[] branchTargets) {
176            if (branchTargets.length != _targets.length) {
177                throw new IllegalArgumentException("TABLESWITCH has incorrect number of targets");
178            }
179            for(int i = 0; i < branchTargets.length; ++i) {
180                _targets[i] = branchTargets[i];
181            }
182        }
183    
184        /**
185         * Return instruction in human-readable form.
186         *
187         * @return string representation
188         */
189        public String toString() {
190            StringBuffer x = new StringBuffer();
191            x.append(Opcode.getOpcodeName(Opcode.TABLESWITCH));
192            x.append(" default = ");
193            x.append(_targets[0]);
194            x.append(" low = ");
195            x.append(_low);
196            x.append(" high = ");
197            x.append(_high);
198            x.append(" n = ");
199            x.append(_targets.length - 1);
200            for(int i = 0; i < _targets.length - 1; ++i) {
201                x.append(" (");
202                x.append(_low + i);
203                x.append("->");
204                x.append(_targets[i + 1]);
205                x.append(')');
206            }
207            return x.toString();
208        }
209    }
210