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