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