001 package serp.bytecode;
002
003 import java.util.*;
004
005 import serp.bytecode.visitor.*;
006 import serp.util.*;
007
008 /**
009 * A conversion opcode such as <code>i2l, f2i</code>, etc.
010 * Changing the types of the instruction will automatically
011 * update the underlying opcode. Converting from one type to the same
012 * type will result in a <code>nop</code>.
013 *
014 * @author Abe White
015 */
016 public class ConvertInstruction extends TypedInstruction {
017 private static final Class[][] _mappings = new Class[][] {
018 { boolean.class, int.class },
019 { void.class, int.class },
020 { Object.class, int.class },
021 };
022 private static final Class[][] _fromMappings = new Class[][] {
023 { boolean.class, int.class },
024 { void.class, int.class },
025 { Object.class, int.class },
026 { byte.class, int.class },
027 { char.class, int.class },
028 { short.class, int.class },
029 };
030 String _toType = null;
031 String _fromType = null;
032
033 ConvertInstruction(Code owner) {
034 super(owner);
035 }
036
037 ConvertInstruction(Code owner, int opcode) {
038 super(owner, opcode);
039 }
040
041 public int getLogicalStackChange() {
042 return 0;
043 }
044
045 public int getStackChange() {
046 switch (getOpcode()) {
047 case Constants.I2L:
048 case Constants.I2D:
049 case Constants.F2L:
050 case Constants.F2D:
051 return 1;
052 case Constants.L2I:
053 case Constants.L2F:
054 case Constants.D2I:
055 case Constants.D2F:
056 return -1;
057 default:
058 return 0;
059 }
060 }
061
062 public String getTypeName() {
063 switch (getOpcode()) {
064 case Constants.L2I:
065 case Constants.F2I:
066 case Constants.D2I:
067 return int.class.getName();
068 case Constants.I2L:
069 case Constants.F2L:
070 case Constants.D2L:
071 return long.class.getName();
072 case Constants.I2F:
073 case Constants.L2F:
074 case Constants.D2F:
075 return float.class.getName();
076 case Constants.I2D:
077 case Constants.L2D:
078 case Constants.F2D:
079 return double.class.getName();
080 case Constants.I2B:
081 return byte.class.getName();
082 case Constants.I2C:
083 return char.class.getName();
084 case Constants.I2S:
085 return short.class.getName();
086 default:
087 return _toType;
088 }
089 }
090
091 public TypedInstruction setType(String type) {
092 String toType = mapType(type, _mappings, true);
093 String fromType = getFromTypeName();
094
095 // if no valid opcode, remember current types in case they reset one
096 // to create a valid opcode
097 if (toType == null || fromType == null || toType.equals(fromType)) {
098 _toType = toType;
099 _fromType = fromType;
100 return (TypedInstruction) setOpcode(Constants.NOP);
101 }
102
103 // ok, valid conversion possible, forget saved types
104 _toType = null;
105 _fromType = null;
106
107 char to = toType.charAt(0);
108 char from = fromType.charAt(0);
109 switch (to) {
110 case 'i':
111 switch (from) {
112 case 'l':
113 return (TypedInstruction) setOpcode(Constants.L2I);
114 case 'f':
115 return (TypedInstruction) setOpcode(Constants.F2I);
116 case 'd':
117 return (TypedInstruction) setOpcode(Constants.D2I);
118 }
119 case 'l':
120 switch (from) {
121 case 'i':
122 return (TypedInstruction) setOpcode(Constants.I2L);
123 case 'f':
124 return (TypedInstruction) setOpcode(Constants.F2L);
125 case 'd':
126 return (TypedInstruction) setOpcode(Constants.D2L);
127 }
128 case 'f':
129 switch (from) {
130 case 'i':
131 return (TypedInstruction) setOpcode(Constants.I2F);
132 case 'l':
133 return (TypedInstruction) setOpcode(Constants.L2F);
134 case 'd':
135 return (TypedInstruction) setOpcode(Constants.D2F);
136 }
137 case 'd':
138 switch (from) {
139 case 'i':
140 return (TypedInstruction) setOpcode(Constants.I2D);
141 case 'l':
142 return (TypedInstruction) setOpcode(Constants.L2D);
143 case 'f':
144 return (TypedInstruction) setOpcode(Constants.F2D);
145 }
146 case 'b':
147 if (from == 'i')
148 return (TypedInstruction) setOpcode(Constants.I2B);
149 case 'C':
150 if (from == 'i')
151 return (TypedInstruction) setOpcode(Constants.I2C);
152 case 'S':
153 if (from == 'i')
154 return (TypedInstruction) setOpcode(Constants.I2S);
155 default:
156 throw new IllegalStateException();
157 }
158 }
159
160 /**
161 * Return the name of the type being converted from.
162 * If neither type has been set, this method will return null.
163 */
164 public String getFromTypeName() {
165 switch (getOpcode()) {
166 case Constants.I2L:
167 case Constants.I2F:
168 case Constants.I2D:
169 case Constants.I2B:
170 case Constants.I2S:
171 case Constants.I2C:
172 return int.class.getName();
173 case Constants.L2I:
174 case Constants.L2F:
175 case Constants.L2D:
176 return long.class.getName();
177 case Constants.F2I:
178 case Constants.F2L:
179 case Constants.F2D:
180 return float.class.getName();
181 case Constants.D2I:
182 case Constants.D2L:
183 case Constants.D2F:
184 return double.class.getName();
185 default:
186 return _fromType;
187 }
188 }
189
190 /**
191 * Return the {@link Class} of the type being converted from.
192 * If neither type has been set, this method will return null.
193 */
194 public Class getFromType() {
195 String type = getFromTypeName();
196 if (type == null)
197 return null;
198 return Strings.toClass(type, getClassLoader());
199 }
200
201 /**
202 * Return the bytecode of the type being converted from.
203 * If neither type has been set, this method will return null.
204 */
205 public BCClass getFromTypeBC() {
206 String type = getFromTypeName();
207 if (type == null)
208 return null;
209 return getProject().loadClass(type, getClassLoader());
210 }
211
212 /**
213 * Set the type being converted from. Types that have no direct
214 * support will be converted accordingly.
215 *
216 * @return this instruction, for method chaining
217 */
218 public ConvertInstruction setFromType(String type) {
219 String fromType = mapType(type, _fromMappings, true);
220 String toType = getTypeName();
221
222 // if no valid opcode, remember current types in case they reset one
223 // to create a valid opcode
224 if ((toType == null) || (fromType == null) || toType.equals(fromType)) {
225 _toType = toType;
226 _fromType = fromType;
227 return (ConvertInstruction) setOpcode(Constants.NOP);
228 }
229
230 // ok, valid conversion possible, forget saved types
231 _toType = null;
232 _fromType = null;
233
234 char to = toType.charAt(0);
235 char from = fromType.charAt(0);
236 switch (from) {
237 case 'i':
238 switch (to) {
239 case 'l':
240 return (ConvertInstruction) setOpcode(Constants.I2L);
241 case 'f':
242 return (ConvertInstruction) setOpcode(Constants.I2F);
243 case 'd':
244 return (ConvertInstruction) setOpcode(Constants.I2D);
245 case 'b':
246 return (ConvertInstruction) setOpcode(Constants.I2B);
247 case 'c':
248 return (ConvertInstruction) setOpcode(Constants.I2C);
249 case 's':
250 return (ConvertInstruction) setOpcode(Constants.I2S);
251 }
252 case 'l':
253 switch (to) {
254 case 'i':
255 return (ConvertInstruction) setOpcode(Constants.L2I);
256 case 'f':
257 return (ConvertInstruction) setOpcode(Constants.L2F);
258 case 'd':
259 return (ConvertInstruction) setOpcode(Constants.L2D);
260 }
261 case 'f':
262 switch (to) {
263 case 'i':
264 return (ConvertInstruction) setOpcode(Constants.F2I);
265 case 'l':
266 return (ConvertInstruction) setOpcode(Constants.F2L);
267 case 'd':
268 return (ConvertInstruction) setOpcode(Constants.F2D);
269 }
270 case 'd':
271 switch (to) {
272 case 'i':
273 return (ConvertInstruction) setOpcode(Constants.D2I);
274 case 'l':
275 return (ConvertInstruction) setOpcode(Constants.D2L);
276 case 'f':
277 return (ConvertInstruction) setOpcode(Constants.D2F);
278 }
279 default:
280 throw new IllegalStateException();
281 }
282 }
283
284 /**
285 * Set the type being converted from. Types that have no direct
286 * support will be converted accordingly.
287 *
288 * @return this instruction, for method chaining
289 */
290 public ConvertInstruction setFromType(Class type) {
291 if (type == null)
292 return setFromType((String) null);
293 return setFromType(type.getName());
294 }
295
296 /**
297 * Set the type being converted from. Types that have no direct
298 * support will be converted accordingly.
299 *
300 * @return this instruction, for method chaining
301 */
302 public ConvertInstruction setFromType(BCClass type) {
303 if (type == null)
304 return setFromType((String) null);
305 return setFromType(type.getName());
306 }
307
308 /**
309 * ConvertInstructions are equal if the types they convert between are
310 * either equal or unset.
311 */
312 public boolean equalsInstruction(Instruction other) {
313 if (other == this)
314 return true;
315 if (!(other instanceof ConvertInstruction))
316 return false;
317
318 ConvertInstruction ins = (ConvertInstruction) other;
319 if (getOpcode() != Constants.NOP && getOpcode() == ins.getOpcode())
320 return true;
321
322 String type = getTypeName();
323 String otherType = ins.getTypeName();
324 if (!(type == null || otherType == null || type.equals(otherType)))
325 return false;
326
327 type = getFromTypeName();
328 otherType = ins.getFromTypeName();
329 return type == null || otherType == null || type.equals(otherType);
330 }
331
332 public void acceptVisit(BCVisitor visit) {
333 visit.enterConvertInstruction(this);
334 visit.exitConvertInstruction(this);
335 }
336
337 void read(Instruction orig) {
338 super.read(orig);
339 ConvertInstruction ins = (ConvertInstruction) orig;
340 _toType = ins._toType;
341 _fromType = ins._fromType;
342 }
343 }