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