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