1 package serp.bytecode;
2
3 import java.io.*;
4
5 import serp.bytecode.lowlevel.*;
6 import serp.bytecode.visitor.*;
7 import serp.util.*;
8
9 /***
10 * An instruction that that loads a constant onto the stack.
11 * The opcode represented by this instruction may change depending on the
12 * type and value of the constant set. For example, if the constant value
13 * is initially set to 5, the opcode will be <code>iconst5</code>; if later
14 * incremented to 6, the opcode will be changed to <code>bipush(6)</code>.
15 *
16 * @author Abe White
17 */
18 public class ConstantInstruction extends TypedInstruction {
19 private int _arg = -1;
20
21 ConstantInstruction(Code owner) {
22 super(owner);
23 }
24
25 ConstantInstruction(Code owner, int opcode) {
26 super(owner, opcode);
27 }
28
29 int getLength() {
30 switch (getOpcode()) {
31 case Constants.BIPUSH:
32 case Constants.LDC:
33 return super.getLength() + 1;
34 case Constants.SIPUSH:
35 case Constants.LDCW:
36 case Constants.LDC2W:
37 return super.getLength() + 2;
38 default:
39 return super.getLength();
40 }
41 }
42
43 public int getStackChange() {
44 String type = getTypeName();
45 if (double.class.getName().equals(type)
46 || long.class.getName().equals(type))
47 return 2;
48 return 1;
49 }
50
51 public int getLogicalStackChange() {
52 return 1;
53 }
54
55 public String getTypeName() {
56 int opcode = getOpcode();
57 switch (opcode) {
58 case Constants.NOP:
59 return null;
60 case Constants.ACONSTNULL:
61 return Object.class.getName();
62 case Constants.ICONSTM1:
63 case Constants.ICONST0:
64 case Constants.ICONST1:
65 case Constants.ICONST2:
66 case Constants.ICONST3:
67 case Constants.ICONST4:
68 case Constants.ICONST5:
69 case Constants.BIPUSH:
70 case Constants.SIPUSH:
71 return int.class.getName();
72 case Constants.LCONST0:
73 case Constants.LCONST1:
74 return long.class.getName();
75 case Constants.FCONST0:
76 case Constants.FCONST1:
77 case Constants.FCONST2:
78 return float.class.getName();
79 case Constants.DCONST0:
80 case Constants.DCONST1:
81 return double.class.getName();
82 }
83
84 Entry entry = getPool().getEntry(_arg);
85 switch (entry.getType()) {
86 case Entry.UTF8:
87 case Entry.STRING:
88 return String.class.getName();
89 case Entry.INT:
90 return int.class.getName();
91 case Entry.FLOAT:
92 return float.class.getName();
93 case Entry.LONG:
94 return long.class.getName();
95 case Entry.DOUBLE:
96 return double.class.getName();
97 case Entry.CLASS:
98 return Class.class.getName();
99 default:
100 return null;
101 }
102 }
103
104 public TypedInstruction setType(String type) {
105 throw new UnsupportedOperationException("Use setValue");
106 }
107
108 /***
109 * Return the value of the constant as its wrapper type, or null if
110 * not set. Returns class values as the class name.
111 */
112 public Object getValue() {
113 int opcode = getOpcode();
114 switch (opcode) {
115 case Constants.NOP:
116 case Constants.ACONSTNULL:
117 return null;
118 case Constants.ICONSTM1:
119 case Constants.ICONST0:
120 case Constants.ICONST1:
121 case Constants.ICONST2:
122 case Constants.ICONST3:
123 case Constants.ICONST4:
124 case Constants.ICONST5:
125 return Numbers.valueOf(opcode - Constants.ICONST0);
126 case Constants.LCONST0:
127 case Constants.LCONST1:
128 return Numbers.valueOf((long) (opcode - Constants.LCONST0));
129 case Constants.FCONST0:
130 case Constants.FCONST1:
131 case Constants.FCONST2:
132 return new Float(opcode - Constants.FCONST0);
133 case Constants.DCONST0:
134 case Constants.DCONST1:
135 return new Double(opcode - Constants.DCONST0);
136 case Constants.BIPUSH:
137 case Constants.SIPUSH:
138 return Numbers.valueOf(_arg);
139 default:
140 Entry entry = getPool().getEntry(_arg);
141 Object val = ((ConstantEntry) entry).getConstant();
142 if (entry.getType() == Entry.CLASS)
143 return getProject().getNameCache().getExternalForm((String) val,
144 false);
145 return val;
146 }
147 }
148
149 /***
150 * Set the constant to the given value. The value should be
151 * an instance of String, Integer, Long, Double, Float, Class, BCClass, or
152 * null depending on the constant type. If the given value is not
153 * supported directly, it will be converted accordingly.
154 *
155 * @return this instruction, for method chaining
156 */
157 public ConstantInstruction setValue(Object value) {
158 boolean clsName = false;
159 if (value instanceof Boolean)
160 value = Numbers.valueOf((((Boolean) value).booleanValue()) ? 1 : 0);
161 else if (value instanceof Character)
162 value = Numbers.valueOf((int) ((Character) value).charValue());
163 else if (value instanceof Byte)
164 value = Numbers.valueOf(((Byte) value).intValue());
165 else if (value instanceof Short)
166 value = Numbers.valueOf(((Short) value).intValue());
167 else if (value instanceof Class) {
168 value = ((Class) value).getName();
169 clsName = true;
170 } else if (value instanceof BCClass) {
171 value = ((BCClass) value).getName();
172 clsName = true;
173 } else if (value != null && !(value instanceof Number)
174 && !(value instanceof String))
175 throw new IllegalArgumentException("value = " + value);
176
177 calculateOpcode(value, clsName, false);
178 return this;
179 }
180
181 /***
182 * Return the string value of this constant, or null if not set.
183 */
184 public String getStringValue() {
185 return (String) getValue();
186 }
187
188 /***
189 * Return the int value of this constant, or 0 if not set.
190 */
191 public int getIntValue() {
192 Object value = getValue();
193 return (value == null) ? 0 : ((Number) value).intValue();
194 }
195
196 /***
197 * Return the long value of this constant, or 0 if not set.
198 */
199 public long getLongValue() {
200 Object value = getValue();
201 return (value == null) ? 0L : ((Number) value).longValue();
202 }
203
204 /***
205 * Return the float value of this constant, or 0 if not set.
206 */
207 public float getFloatValue() {
208 Object value = getValue();
209 return (value == null) ? 0F : ((Number) value).floatValue();
210 }
211
212 /***
213 * Return the double value of this constant, or 0 if not set.
214 */
215 public double getDoubleValue() {
216 Object value = getValue();
217 return (value == null) ? 0D : ((Number) value).doubleValue();
218 }
219
220 /***
221 * Return the class value of this constant, or null if not set.
222 */
223 public String getClassNameValue() {
224 return (String) getValue();
225 }
226
227 /***
228 * Set this constant to null.
229 *
230 * @return this instruction, for method chaining
231 */
232 public ConstantInstruction setNull() {
233 calculateOpcode(null, false, false);
234 return this;
235 }
236
237 /***
238 * Set the value of this constant.
239 *
240 * @return this instruction, for method chaining
241 */
242 public ConstantInstruction setValue(String value) {
243 return setValue(value, false);
244 }
245
246 public ConstantInstruction setValue(String value, boolean clsName) {
247 calculateOpcode(value, clsName, false);
248 return this;
249 }
250
251 /***
252 * Set the value of this constant.
253 *
254 * @return this instruction, for method chaining
255 */
256 public ConstantInstruction setValue(Class value) {
257 if (value == null)
258 return setNull();
259 calculateOpcode(value.getName(), true, false);
260 return this;
261 }
262
263 /***
264 * Set the value of this constant.
265 *
266 * @return this instruction, for method chaining
267 */
268 public ConstantInstruction setValue(BCClass value) {
269 if (value == null)
270 return setNull();
271 calculateOpcode(value.getName(), true, false);
272 return this;
273 }
274
275 /***
276 * Set the value of this constant.
277 *
278 * @return this instruction, for method chaining
279 */
280 public ConstantInstruction setValue(int value) {
281 calculateOpcode(Numbers.valueOf(value), false, false);
282 return this;
283 }
284
285 /***
286 * Set the value of this constant.
287 *
288 * @return this instruction, for method chaining
289 */
290 public ConstantInstruction setValue(long value) {
291 calculateOpcode(Numbers.valueOf(value), false, false);
292 return this;
293 }
294
295 /***
296 * Set the value of this constant.
297 *
298 * @return this instruction, for method chaining
299 */
300 public ConstantInstruction setValue(float value) {
301 calculateOpcode(new Float(value), false, false);
302 return this;
303 }
304
305 /***
306 * Set the value of this constant.
307 *
308 * @return this instruction, for method chaining
309 */
310 public ConstantInstruction setValue(double value) {
311 calculateOpcode(new Double(value), false, false);
312 return this;
313 }
314
315 /***
316 * Set the value of this constant; note that this type is converted to int.
317 *
318 * @return this instruction, for method chaining
319 */
320 public ConstantInstruction setValue(boolean value) {
321 return setValue((value) ? 1 : 0);
322 }
323
324 /***
325 * Set the value of this constant; note that this type is converted to int.
326 *
327 * @return this instruction, for method chaining
328 */
329 public ConstantInstruction setValue(short value) {
330 return setValue((int) value);
331 }
332
333 /***
334 * Set the value of this constant; note that this type is converted to int.
335 *
336 * @return this instruction, for method chaining
337 */
338 public ConstantInstruction setValue(char value) {
339 return setValue((int) value);
340 }
341
342 /***
343 * ConstantInstructions are equal if the const they reference is the same,
344 * or if the const of either is unset.
345 */
346 public boolean equalsInstruction(Instruction other) {
347 if (this == other)
348 return true;
349 if (!(other instanceof ConstantInstruction))
350 return false;
351
352 ConstantInstruction ci = (ConstantInstruction) other;
353 Object value = getValue();
354 Object otherValue = ci.getValue();
355 if (value == null || otherValue == null)
356 return true;
357 if (getTypeName() == null || ci.getTypeName() == null)
358 return true;
359 return value.equals(otherValue)
360 && getTypeName().equals(ci.getTypeName());
361 }
362
363 public void acceptVisit(BCVisitor visit) {
364 visit.enterConstantInstruction(this);
365 visit.exitConstantInstruction(this);
366 }
367
368 void read(Instruction orig) {
369 super.read(orig);
370 ConstantInstruction ci = (ConstantInstruction) orig;
371 calculateOpcode(ci.getValue(),
372 Class.class.getName().equals(ci.getTypeName()),
373 ci.getOpcode() == Constants.LDCW);
374 }
375
376 void read(DataInput in) throws IOException {
377 super.read(in);
378 switch (getOpcode()) {
379 case Constants.BIPUSH:
380 case Constants.LDC:
381 _arg = in.readUnsignedByte();
382 break;
383 case Constants.SIPUSH:
384 case Constants.LDCW:
385 case Constants.LDC2W:
386 _arg = in.readUnsignedShort();
387 }
388 }
389
390 void write(DataOutput out) throws IOException {
391 super.write(out);
392 switch (getOpcode()) {
393 case Constants.BIPUSH:
394 case Constants.LDC:
395 out.writeByte(_arg);
396 break;
397 case Constants.SIPUSH:
398 case Constants.LDCW:
399 case Constants.LDC2W:
400 out.writeShort(_arg);
401 break;
402 }
403 }
404
405 private void calculateOpcode(Object value, boolean clsName, boolean wide) {
406 int len = getLength();
407 _arg = -1;
408 if (value == null)
409 setOpcode(Constants.ACONSTNULL);
410 else if (clsName) {
411 String name = getProject().getNameCache().getInternalForm((String)
412 value, false);
413 _arg = getPool().findClassEntry(name, true);
414 setOpcode(Constants.LDCW);
415 ensureBytecodeVersion();
416 } else if (value instanceof Float) {
417 float floatVal = ((Float) value).floatValue();
418 if ((floatVal == 0) || (floatVal == 1) || (floatVal == 2))
419 setOpcode(Constants.FCONST0 + (int) floatVal);
420 else {
421 _arg = getPool().findFloatEntry((float) floatVal, true);
422 setOpcode((_arg > 255 || wide) ? Constants.LDCW
423 : Constants.LDC);
424 }
425 } else if (value instanceof Long) {
426 long longVal = ((Long) value).longValue();
427 if (longVal == 0 || longVal == 1)
428 setOpcode(Constants.LCONST0 + (int) longVal);
429 else {
430 _arg = getPool().findLongEntry(longVal, true);
431 setOpcode(Constants.LDC2W);
432 }
433 } else if (value instanceof Double) {
434 double doubleVal = ((Double) value).doubleValue();
435 if (doubleVal == 0 || doubleVal == 1)
436 setOpcode(Constants.DCONST0 + (int) doubleVal);
437 else {
438 _arg = getPool().findDoubleEntry(doubleVal, true);
439 setOpcode(Constants.LDC2W);
440 }
441 } else if (value instanceof Integer) {
442 int intVal = ((Integer) value).intValue();
443 if (intVal >= -1 && intVal <= 5)
444 setOpcode(Constants.ICONST0 + intVal);
445 else if ((intVal >= -(2 << 6)) && (intVal < (2 << 6))) {
446 setOpcode(Constants.BIPUSH);
447 _arg = intVal;
448 } else if (intVal >= -(2 << 14) && intVal < (2 << 14)) {
449 setOpcode(Constants.SIPUSH);
450 _arg = intVal;
451 } else {
452 _arg = getPool().findIntEntry(intVal, true);
453 setOpcode((_arg > 255 || wide) ? Constants.LDCW
454 : Constants.LDC);
455 }
456 } else if (value instanceof String) {
457 _arg = getPool().findStringEntry((String) value, true);
458 setOpcode((_arg > 255 || wide) ? Constants.LDCW : Constants.LDC);
459 } else
460 throw new IllegalArgumentException(String.valueOf(value));
461
462 if (len != getLength())
463 invalidateByteIndexes();
464 }
465
466 /***
467 * When adding class entries, make sure the bytecode spec supports them.
468 */
469 private void ensureBytecodeVersion() {
470 BCClass bc = getCode().getMethod().getDeclarer();
471 if (bc.getMajorVersion() < Constants.MAJOR_VERSION_JAVA5) {
472 bc.setMajorVersion(Constants.MAJOR_VERSION_JAVA5);
473 bc.setMinorVersion(Constants.MINOR_VERSION_JAVA5);
474 }
475 }
476 }