View Javadoc

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          // if no valid opcode, remember current types in case they reset one
96          // to create a valid opcode
97          if (toType == null || fromType == null || toType.equals(fromType)) {
98              _toType = toType;
99              _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 }