Source code: com/techtrader/modules/tools/bytecode/ConvertInstruction.java
1 package com.techtrader.modules.tools.bytecode;
2
3
4 import java.util.Map;
5 import java.util.HashMap;
6
7 import com.techtrader.modules.tools.bytecode.visitor.BCVisitor;
8
9
10 /**
11 * Represents one of the conversion opcodes defined in the
12 * {@link Constants} interface for converting between primitive types.
13 * Changing the types of the instruction will automatically
14 * update the underlying opcode. If converting from one type to the same
15 * type will result in a NOP. Note that the result of conversions not
16 * supported directly by the JVM (i.e. char to double) is undefined.
17 *
18 * @author Abe White
19 */
20 public class ConvertInstruction
21 extends Instruction
22 {
23 private static final Map _typeNames = new HashMap ();
24 static
25 {
26 _typeNames.put ("long", long.class);
27 _typeNames.put ("float", float.class);
28 _typeNames.put ("double", double.class);
29 }
30
31 private Class _fromType = null;
32 private Class _toType = null;
33
34
35 protected ConvertInstruction (Code owner)
36 {
37 super (owner);
38 }
39
40
41 protected ConvertInstruction (Code owner, int opcode, Class from, Class to)
42 {
43 super (owner);
44 _opcode = opcode;
45 _fromType = from;
46 _toType = to;
47 }
48
49
50 /**
51 * Get the type of being converted from; will be one of:
52 * int, float, double, long.
53 * If the type has not been set, this method will return null.
54 */
55 public Class getFromType ()
56 {
57 return _fromType;
58 }
59
60
61 /**
62 * Get the type of being converted from; will be one of:
63 * int, float, double, long.
64 * If the type has not been set, this method will return null.
65 */
66 public String getFromTypeName ()
67 {
68 if (_fromType == null)
69 return null;
70
71 return _fromType.getName ();
72 }
73
74
75 /**
76 * Set the type to convert from. Types without direct support are
77 * demoted to int.class.
78 *
79 * @return this Instruction, for method chaining
80 */
81 public ConvertInstruction setFromType (Class type)
82 {
83 if (float.class.equals (type) || double.class.equals (type)
84 || long.class.equals (type))
85 _fromType = type;
86 else
87 _fromType = int.class;
88
89 calculateOpCode ();
90 return this;
91 }
92
93
94 /**
95 * Set the type to convert from by name.
96 *
97 * @return this Instruction, for method chaining
98 *
99 * @see #setFromType(java.lang.Class)
100 */
101 public ConvertInstruction setFromTypeName (String name)
102 {
103 _fromType = (Class) _typeNames.get (name);
104 if (_fromType == null && name != null)
105 _fromType = int.class;
106
107 calculateOpCode ();
108 return this;
109 }
110
111
112 /**
113 * Get the type being converted to; will be one of:
114 * int, float, double, long, byte, char, short.
115 * If the type has not been set, this method will return null.
116 */
117 public Class getToType ()
118 {
119 return _toType;
120 }
121
122
123 /**
124 * Get the type being converted to; will be one of:
125 * int, float, double, long, byte, char, short.
126 * If the type has not been set, this method will return null.
127 */
128 public String getToTypeName ()
129 {
130 if (_toType == null)
131 return null;
132
133 return _toType.getName ();
134 }
135
136
137 /**
138 * Set the type to convert to. Types without direct support are
139 * demoted to int.class.
140 *
141 * @return this Instruction, for method chaining
142 */
143 public ConvertInstruction setToType (Class type)
144 {
145 if (_opcodeTypes.indexOf (type) != -1)
146 _toType = type;
147 else
148 _toType = int.class;
149
150 calculateOpCode ();
151 return this;
152 }
153
154
155 /**
156 * Set the type to convert to by name.
157 *
158 * @return this Instruction, for method chaining
159 *
160 * @see #setToType(java.lang.Class)
161 */
162 public ConvertInstruction setToTypeName (String name)
163 {
164 _toType = (Class) _typeNames.get (name);
165
166 // there are more 'to' types available than 'from' types
167 if (_toType == null && name != null)
168 {
169 if (name.equals ("byte"))
170 _toType = byte.class;
171 else if (name.equals ("char"))
172 _toType = char.class;
173 else if (name.equals ("short"))
174 _toType = short.class;
175 else
176 _toType = int.class;
177 }
178
179 calculateOpCode ();
180 return this;
181 }
182
183
184 /**
185 * ConvertInstructions are equal if they convert between the same types,
186 * or the types of either is unset.
187 */
188 public boolean equals (Object other)
189 {
190 if (this == other)
191 return true;
192 if (!(other instanceof ConvertInstruction))
193 return false;
194
195 ConvertInstruction ins = (ConvertInstruction) other;
196
197 boolean fromEq = (_fromType == null || ins._fromType == null
198 || _fromType.equals (ins._fromType));
199 boolean toEq = (_toType == null || ins._toType == null
200 || _toType.equals (ins._toType));
201
202 return fromEq && toEq;
203 }
204
205
206 public int getStackChange ()
207 {
208 switch (_opcode)
209 {
210 case I2L:
211 case I2D:
212 case F2L:
213 case F2D:
214 return 1;
215 case L2I:
216 case L2F:
217 case D2I:
218 case D2F:
219 return -1;
220 default:
221 return 0;
222 }
223 }
224
225
226 protected void copy (Instruction orig)
227 {
228 super.copy (orig);
229
230 ConvertInstruction ins = (ConvertInstruction) orig;
231 _fromType = ins._fromType;
232 _toType = ins._toType;
233 }
234
235
236 /**
237 * Helper method to calculate the correct opcode for this conversion.
238 */
239 private void calculateOpCode ()
240 {
241 int fromIdx = _opcodeTypes.indexOf (_fromType);
242 int toIdx = _opcodeTypes.indexOf (_toType);
243
244 // take advantage of the grouping of the opcodes
245 _opcode = I2L + 3 * fromIdx;
246
247 // if not converting from int then any conversion to byte,char,float
248 // should be considered a conversion to int
249 if (fromIdx != 0 && toIdx > 4)
250 toIdx = 0;
251 else if (toIdx > 4) // the weird 'to' types are placed after the rest
252 _opcode += 8;
253
254 // do nothing if converting to the same type
255 if (fromIdx == toIdx)
256 {
257 _opcode = NOP;
258 return;
259 }
260
261 // move from the <fromtype>2I opcode to <fromtype>2<totype>
262 _opcode += toIdx;
263 // compensate: there is no <fromtype>2<fromtype> opcode
264 if (toIdx > fromIdx)
265 _opcode--;
266 }
267
268
269 public void acceptVisit (BCVisitor visit)
270 {
271 visit.enterConvertInstruction (this);
272 visit.exitConvertInstruction (this);
273 }
274 }