Source code: com/techtrader/modules/tools/bytecode/ConstantInstruction.java
1 package com.techtrader.modules.tools.bytecode;
2
3
4 import java.io.IOException;
5 import java.io.DataInput;
6 import java.io.DataOutput;
7
8 import com.techtrader.modules.tools.bytecode.visitor.BCVisitor;
9
10
11 /**
12 * Represents an instruction that that loads a constant onto the stack.
13 * The opcode represented by this Instruction may change depending on the
14 * type and value of the constant set. For example, if the constant value
15 * is initially set to '5', the opcode will be iconst_5; if later incremented
16 * to '6', the opcode will be changed to bipush(6).
17 *
18 * @author Abe White
19 */
20 public class ConstantInstruction
21 extends Instruction
22 {
23 private Object _value = null;
24 private int _arg = -1;
25
26
27 protected ConstantInstruction (Code owner)
28 {
29 super (owner);
30 }
31
32
33 protected ConstantInstruction (Code owner, int opcode, Object value)
34 {
35 super (owner);
36 _opcode = opcode;
37 _value = value;
38 }
39
40
41 /**
42 * Set the constant to the given Object value. The Object should be
43 * an instance of String, Integer, Long, Double, or Float depending
44 * on the constant type.
45 *
46 * @return this Instruction, for method chaining
47 */
48 public ConstantInstruction setConstant (Object value)
49 {
50 _value = value;
51 calculateOpCode ();
52
53 return this;
54 }
55
56
57 /**
58 * Return the constant value as an Object; will be an instance of
59 * String, Integer, Float, Double, or Long, as necessary. Returns null
60 * if the constant has not been set, or if this represents the
61 * aconst_null opcode.
62 */
63 public Object getConstant ()
64 {
65 return _value;
66 }
67
68
69 /**
70 * Return the class of constant this instruction references. Will return
71 * one of: Object.class, String.class, int.class, long.class,
72 * double.class, float.class.
73 */
74 public Class getConstantType ()
75 {
76 if (_value == null)
77 return Object.class;
78
79 Class cls = _value.getClass ();
80 if (cls.equals (Float.class))
81 return float.class;
82 if (cls.equals (Double.class))
83 return double.class;
84 if (cls.equals (Long.class))
85 return long.class;
86 if (cls.equals (String.class))
87 return String.class;
88 return int.class;
89 }
90
91
92 /**
93 * Return the class of constant this instruction references. Will return
94 * one of: java.lang.Object, java.lang.String, int, long, double, float.
95 */
96 public String getConstantTypeName ()
97 {
98 return getConstantType ().getName ();
99 }
100
101
102 /**
103 * Set the constant to load, for String constants.
104 *
105 * @return this Instruction, for method chaining
106 */
107 public ConstantInstruction setStringConstant (String value)
108 {
109 return setConstant (value);
110 }
111
112
113 /**
114 * Get the constant to load, for String constants.
115 */
116 public String getStringConstant ()
117 {
118 return (String) getConstant ();
119 }
120
121
122 /**
123 * Set the constant to load, for int constants.
124 *
125 * @return this Instruction, for method chaining
126 */
127 public ConstantInstruction setIntConstant (int value)
128 {
129 return setConstant (new Integer (value));
130 }
131
132
133 /**
134 * Get the constant to load, for int constants.
135 */
136 public int getIntConstant ()
137 {
138 return (_value == null) ? 0 : ((Number) _value).intValue ();
139 }
140
141
142 /**
143 * Set the constant to load, for float constants.
144 *
145 * @return this Instruction, for method chaining
146 */
147 public ConstantInstruction setFloatConstant (float value)
148 {
149 return setConstant (new Float (value));
150 }
151
152
153 /**
154 * Get the constant to load, for float constants.
155 */
156 public float getFloatConstant ()
157 {
158 return (_value == null) ? 0F : ((Number) _value).floatValue ();
159 }
160
161
162 /**
163 * Set the constant to load, for long constants; must be a ldc2
164 * instruction.
165 *
166 * @return this Instruction, for method chaining
167 */
168 public ConstantInstruction setLongConstant (long value)
169 {
170 return setConstant (new Long (value));
171 }
172
173
174 /**
175 * Get the constant to load, for float constants; must be a ldc2
176 * instruction.
177 */
178 public long getLongConstant ()
179 {
180 return (_value == null) ? 0L : ((Number) _value).longValue ();
181 }
182
183
184 /**
185 * Set the constant to load, for double constants; must be a ldc2
186 * instruction.
187 *
188 * @return this Instruction, for method chaining
189 */
190 public ConstantInstruction setDoubleConstant (double value)
191 {
192 return setConstant (new Double (value));
193 }
194
195
196 /**
197 * Get the constant to double, for float constants; must be a ldc2
198 * instruction.
199 */
200 public double getDoubleConstant ()
201 {
202 return (_value == null) ? 0D : ((Number) _value).doubleValue ();
203 }
204
205
206 /**
207 * ConstantInstructions are equal if the const they reference is the same,
208 * or if the const of either is unset.
209 */
210 public boolean equals (Object other)
211 {
212 if (this == other)
213 return true;
214 if (!(other instanceof ConstantInstruction))
215 return false;
216
217 ConstantInstruction ins = (ConstantInstruction) other;
218
219 Object value = getConstant ();
220 Object insValue = ins.getConstant ();
221
222 return (value == null && !Object.class.equals (getConstantType ()))
223 || (insValue == null && !Object.class.equals(ins.getConstantType()))
224 || (value == null && insValue == null)
225 || (value != null && value.equals (insValue));
226 }
227
228
229 public int getLength ()
230 {
231 switch (_opcode)
232 {
233 case BIPUSH:
234 case LDC:
235 return super.getLength () + 1;
236 case SIPUSH:
237 case LDC_W:
238 case LDC2_W:
239 return super.getLength () + 2;
240 default:
241 return super.getLength ();
242 }
243 }
244
245
246 public int getStackChange ()
247 {
248 if (_value instanceof Long || _value instanceof Double)
249 return 2;
250
251 return 1;
252 }
253
254
255 protected void copy (Instruction orig)
256 {
257 super.copy (orig);
258
259 ConstantInstruction ci = (ConstantInstruction) orig;
260 _value = ci._value;
261 _arg = ci._arg;
262 }
263
264
265 protected void readData (DataInput in)
266 throws IOException
267 {
268 switch (_opcode)
269 {
270 case BIPUSH:
271 _value = new Integer (in.readUnsignedByte ());
272 break;
273 case SIPUSH:
274 _value = new Integer (in.readUnsignedShort ());
275 break;
276 case LDC:
277 _arg = in.readUnsignedByte ();
278 _value = _owner.getPool ().getConstant (_arg);
279 break;
280 case LDC_W:
281 case LDC2_W:
282 _arg = in.readUnsignedShort ();
283 _value = _owner.getPool ().getConstant (_arg);
284 break;
285 }
286 }
287
288
289 protected void writeData (DataOutput out)
290 throws IOException
291 {
292 switch (_opcode)
293 {
294 case BIPUSH:
295 out.writeByte (getIntConstant ());
296 break;
297 case SIPUSH:
298 out.writeShort (getIntConstant ());
299 break;
300 case LDC:
301 out.writeByte (_arg);
302 break;
303 case LDC_W:
304 case LDC2_W:
305 out.writeShort (_arg);
306 break;
307 }
308 }
309
310
311 /**
312 * Helper method to calculate the optimal opcode for the constant
313 * to be pushed onto the stack.
314 */
315 private void calculateOpCode ()
316 {
317 Class type = getConstantType ();
318 double val = 0;
319 if (_value instanceof Number)
320 val = getDoubleConstant ();
321 else if (_value instanceof Boolean)
322 val = (((Boolean) _value).booleanValue ()) ? 1 : 0;
323 _arg = -1;
324
325 if (type.equals (Object.class))
326 _opcode = ACONST_NULL;
327 else if (type.equals (float.class) && (val == 0 || val == 1
328 || val == 2))
329 _opcode = FCONST_0 + (int) val;
330 else if (type.equals (long.class) && val > -1 && val < 2)
331 _opcode = LCONST_0 + (int) val;
332 else if (type.equals (double.class) && (val == 0 || val == 1
333 || val == 2))
334 _opcode = DCONST_0 + (int) val;
335 else if (type.equals(int.class) && val >= -(2 << 15) && val < (2 << 15))
336 {
337 if (val >= -1 && val <= 5)
338 _opcode = ICONST_0 + (int) val;
339 else if (val >= -(2 << 7) && val < (2 << 7))
340 _opcode = BIPUSH;
341 else
342 _opcode = SIPUSH;
343 }
344 else
345 {
346 _arg = _owner.getPool ().setConstant (0, _value);
347 if (type.equals (long.class) || type.equals (double.class))
348 _opcode = LDC2_W;
349 else
350 _opcode = (_arg > 255) ? LDC_W : LDC;
351 }
352 }
353
354
355 public void acceptVisit (BCVisitor visit)
356 {
357 visit.enterConstantInstruction (this);
358 visit.exitConstantInstruction (this);
359 }
360 }