1 package serp.bytecode;
2
3 import java.util.*;
4
5 import serp.bytecode.visitor.*;
6 import serp.util.*;
7
8 /***
9 * A conversion opcode such as <code>i2l, f2i</code>, etc.
10 * Changing the types of the instruction will automatically
11 * update the underlying opcode. Converting from one type to the same
12 * type will result in a <code>nop</code>.
13 *
14 * @author Abe White
15 */
16 public class ConvertInstruction extends TypedInstruction {
17 private static final Class[][] _mappings = new Class[][] {
18 { boolean.class, int.class },
19 { void.class, int.class },
20 { Object.class, int.class },
21 };
22 private static final Class[][] _fromMappings = new Class[][] {
23 { boolean.class, int.class },
24 { void.class, int.class },
25 { Object.class, int.class },
26 { byte.class, int.class },
27 { char.class, int.class },
28 { short.class, int.class },
29 };
30 String _toType = null;
31 String _fromType = null;
32
33 ConvertInstruction(Code owner) {
34 super(owner);
35 }
36
37 ConvertInstruction(Code owner, int opcode) {
38 super(owner, opcode);
39 }
40
41 public int getLogicalStackChange() {
42 return 0;
43 }
44
45 public int getStackChange() {
46 switch (getOpcode()) {
47 case Constants.I2L:
48 case Constants.I2D:
49 case Constants.F2L:
50 case Constants.F2D:
51 return 1;
52 case Constants.L2I:
53 case Constants.L2F:
54 case Constants.D2I:
55 case Constants.D2F:
56 return -1;
57 default:
58 return 0;
59 }
60 }
61
62 public String getTypeName() {
63 switch (getOpcode()) {
64 case Constants.L2I:
65 case Constants.F2I:
66 case Constants.D2I:
67 return int.class.getName();
68 case Constants.I2L:
69 case Constants.F2L:
70 case Constants.D2L:
71 return long.class.getName();
72 case Constants.I2F:
73 case Constants.L2F:
74 case Constants.D2F:
75 return float.class.getName();
76 case Constants.I2D:
77 case Constants.L2D:
78 case Constants.F2D:
79 return double.class.getName();
80 case Constants.I2B:
81 return byte.class.getName();
82 case Constants.I2C:
83 return char.class.getName();
84 case Constants.I2S:
85 return short.class.getName();
86 default:
87 return _toType;
88 }
89 }
90
91 public TypedInstruction setType(String type) {
92 String toType = mapType(type, _mappings, true);
93 String fromType = getFromTypeName();
94
95
96
97 if (toType == null || fromType == null || toType.equals(fromType)) {
98 _toType = toType;
99 _fromType = fromType;
100 return (TypedInstruction) setOpcode(Constants.NOP);
101 }
102
103
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
223
224 if ((toType == null) || (fromType == null) || toType.equals(fromType)) {
225 _toType = toType;
226 _fromType = fromType;
227 return (ConvertInstruction) setOpcode(Constants.NOP);
228 }
229
230
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 }