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     * 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 short getBytecodeLength(short pc) {
059            int pad = 3 - (pc % 4);
060            return (short)(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, short pc, short paddingPC, LineNumberTable lnt) {
073            int[] branchTargets = Opcode.getBranchTargets(bytecode, pc, paddingPC);
074    
075            _defaultTarget = lnt.getLineNumber((short)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((short)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(short 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((short)_defaultTarget) - pc, b, pad + 1);
100            Types.bytesFromInt((int)_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((short)_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            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            return Arrays.equals(_targets, lookupSwitchInstruction._targets);
132    
133        }
134    
135        /**
136         * Return hash code.
137         *
138         * @return hash code
139         */
140        public int hashCode() {
141            return _defaultTarget;
142        }
143    
144        /**
145         * Return an array of target indices.
146         *
147         * @return array of target indices.
148         */
149        public short[] getBranchTargets() {
150            short[] bt = new short[_targets.length + 1];
151            bt[0] = (short)_defaultTarget;
152            int i = 1;
153            for(int t : _targets) {
154                bt[i++] = (short)t;
155            }
156            return bt;
157        }
158    
159        /**
160         * Set the branch target indices.
161         *
162         * @param branchTargets array of target indices
163         */
164        public void setBranchTargets(short[] branchTargets) {
165            if (branchTargets.length != _targets.length + 1) {
166                throw new IllegalArgumentException("LOOKUPSWITCH has incorrect number of targets");
167            }
168            _defaultTarget = branchTargets[0];
169            for(int i = 1; i < branchTargets.length; ++i) {
170                _targets[i - 1] = branchTargets[i];
171            }
172        }
173    
174        /**
175         * Return instruction in human-readable form.
176         *
177         * @return string representation
178         */
179        public String toString() {
180            StringBuffer x = new StringBuffer();
181            x.append(Opcode.getOpcodeName(Opcode.LOOKUPSWITCH));
182            x.append(" default = ");
183            x.append(_defaultTarget);
184            x.append(" n = ");
185            x.append(_targets.length);
186            for(int i = 0; i < _targets.length; ++i) {
187                x.append(" (");
188                x.append(_keys[i]);
189                x.append("->");
190                x.append(_targets[i]);
191                x.append(')');
192            }
193            return x.toString();
194        }
195    }
196