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