001 package serp.bytecode;
002
003 import java.io.*;
004
005 import serp.bytecode.lowlevel.*;
006 import serp.bytecode.visitor.*;
007 import serp.util.*;
008
009 /**
010 * An instruction that that loads a constant onto the stack.
011 * The opcode represented by this instruction may change depending on the
012 * type and value of the constant set. For example, if the constant value
013 * is initially set to 5, the opcode will be <code>iconst5</code>; if later
014 * incremented to 6, the opcode will be changed to <code>bipush(6)</code>.
015 *
016 * @author Abe White
017 */
018 public class ConstantInstruction extends TypedInstruction {
019 private int _arg = -1;
020
021 ConstantInstruction(Code owner) {
022 super(owner);
023 }
024
025 ConstantInstruction(Code owner, int opcode) {
026 super(owner, opcode);
027 }
028
029 int getLength() {
030 switch (getOpcode()) {
031 case Constants.BIPUSH:
032 case Constants.LDC:
033 return super.getLength() + 1;
034 case Constants.SIPUSH:
035 case Constants.LDCW:
036 case Constants.LDC2W:
037 return super.getLength() + 2;
038 default:
039 return super.getLength();
040 }
041 }
042
043 public int getStackChange() {
044 String type = getTypeName();
045 if (double.class.getName().equals(type)
046 || long.class.getName().equals(type))
047 return 2;
048 return 1;
049 }
050
051 public int getLogicalStackChange() {
052 return 1;
053 }
054
055 public String getTypeName() {
056 int opcode = getOpcode();
057 switch (opcode) {
058 case Constants.NOP:
059 return null;
060 case Constants.ACONSTNULL:
061 return Object.class.getName();
062 case Constants.ICONSTM1:
063 case Constants.ICONST0:
064 case Constants.ICONST1:
065 case Constants.ICONST2:
066 case Constants.ICONST3:
067 case Constants.ICONST4:
068 case Constants.ICONST5:
069 case Constants.BIPUSH:
070 case Constants.SIPUSH:
071 return int.class.getName();
072 case Constants.LCONST0:
073 case Constants.LCONST1:
074 return long.class.getName();
075 case Constants.FCONST0:
076 case Constants.FCONST1:
077 case Constants.FCONST2:
078 return float.class.getName();
079 case Constants.DCONST0:
080 case Constants.DCONST1:
081 return double.class.getName();
082 }
083
084 Entry entry = getPool().getEntry(_arg);
085 switch (entry.getType()) {
086 case Entry.UTF8:
087 case Entry.STRING:
088 return String.class.getName();
089 case Entry.INT:
090 return int.class.getName();
091 case Entry.FLOAT:
092 return float.class.getName();
093 case Entry.LONG:
094 return long.class.getName();
095 case Entry.DOUBLE:
096 return double.class.getName();
097 case Entry.CLASS:
098 return Class.class.getName();
099 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 if (value instanceof Boolean)
159 value = Numbers.valueOf((((Boolean) value).booleanValue()) ? 1 : 0);
160 else if (value instanceof Character)
161 value = Numbers.valueOf((int) ((Character) value).charValue());
162 else if (value instanceof Byte)
163 value = Numbers.valueOf(((Byte) value).intValue());
164 else if (value instanceof Short)
165 value = Numbers.valueOf(((Short) value).intValue());
166 else if ((value != null) && !(value instanceof Number)
167 && !(value instanceof String) && !(value instanceof Class)
168 && !(value instanceof BCClass))
169 throw new IllegalArgumentException("value = " + value);
170
171 calculateOpcode(value, false);
172 return this;
173 }
174
175 /**
176 * Return the string value of this constant, or null if not set.
177 */
178 public String getStringValue() {
179 return (String) getValue();
180 }
181
182 /**
183 * Return the int value of this constant, or 0 if not set.
184 */
185 public int getIntValue() {
186 Object value = getValue();
187 return (value == null) ? 0 : ((Number) value).intValue();
188 }
189
190 /**
191 * Return the long value of this constant, or 0 if not set.
192 */
193 public long getLongValue() {
194 Object value = getValue();
195 return (value == null) ? 0L : ((Number) value).longValue();
196 }
197
198 /**
199 * Return the float value of this constant, or 0 if not set.
200 */
201 public float getFloatValue() {
202 Object value = getValue();
203 return (value == null) ? 0F : ((Number) value).floatValue();
204 }
205
206 /**
207 * Return the double value of this constant, or 0 if not set.
208 */
209 public double getDoubleValue() {
210 Object value = getValue();
211 return (value == null) ? 0D : ((Number) value).doubleValue();
212 }
213
214 /**
215 * Return the class value of this constant, or null if not set.
216 */
217 public String getClassNameValue() {
218 return (String) getValue();
219 }
220
221 /**
222 * Set this constant to null.
223 *
224 * @return this instruction, for method chaining
225 */
226 public ConstantInstruction setNull() {
227 calculateOpcode(null, false);
228 return this;
229 }
230
231 /**
232 * Set the value of this constant.
233 *
234 * @return this instruction, for method chaining
235 */
236 public ConstantInstruction setValue(String value) {
237 calculateOpcode(value, false);
238 return this;
239 }
240
241 /**
242 * Set the value of this constant.
243 *
244 * @return this instruction, for method chaining
245 */
246 public ConstantInstruction setValue(Class value) {
247 calculateOpcode(value, 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(BCClass value) {
257 calculateOpcode(value, false);
258 return this;
259 }
260
261 /**
262 * Set the value of this constant.
263 *
264 * @return this instruction, for method chaining
265 */
266 public ConstantInstruction setValue(int value) {
267 calculateOpcode(Numbers.valueOf(value), false);
268 return this;
269 }
270
271 /**
272 * Set the value of this constant.
273 *
274 * @return this instruction, for method chaining
275 */
276 public ConstantInstruction setValue(long value) {
277 calculateOpcode(Numbers.valueOf(value), false);
278 return this;
279 }
280
281 /**
282 * Set the value of this constant.
283 *
284 * @return this instruction, for method chaining
285 */
286 public ConstantInstruction setValue(float value) {
287 calculateOpcode(new Float(value), false);
288 return this;
289 }
290
291 /**
292 * Set the value of this constant.
293 *
294 * @return this instruction, for method chaining
295 */
296 public ConstantInstruction setValue(double value) {
297 calculateOpcode(new Double(value), false);
298 return this;
299 }
300
301 /**
302 * Set the value of this constant; note that this type is converted to int.
303 *
304 * @return this instruction, for method chaining
305 */
306 public ConstantInstruction setValue(boolean value) {
307 return setValue((value) ? 1 : 0);
308 }
309
310 /**
311 * Set the value of this constant; note that this type is converted to int.
312 *
313 * @return this instruction, for method chaining
314 */
315 public ConstantInstruction setValue(short value) {
316 return setValue((int) value);
317 }
318
319 /**
320 * Set the value of this constant; note that this type is converted to int.
321 *
322 * @return this instruction, for method chaining
323 */
324 public ConstantInstruction setValue(char value) {
325 return setValue((int) value);
326 }
327
328 /**
329 * ConstantInstructions are equal if the const they reference is the same,
330 * or if the const of either is unset.
331 */
332 public boolean equalsInstruction(Instruction other) {
333 if (this == other)
334 return true;
335 if (!(other instanceof ConstantInstruction))
336 return false;
337
338 Object value = getValue();
339 Object otherValue = ((ConstantInstruction) other).getValue();
340 return (value == null) || (otherValue == null)
341 || value.equals(otherValue);
342 }
343
344 public void acceptVisit(BCVisitor visit) {
345 visit.enterConstantInstruction(this);
346 visit.exitConstantInstruction(this);
347 }
348
349 void read(Instruction orig) {
350 super.read(orig);
351 ConstantInstruction ci = (ConstantInstruction) orig;
352 calculateOpcode(ci.getValue(), ci.getOpcode() == Constants.LDCW);
353 }
354
355 void read(DataInput in) throws IOException {
356 super.read(in);
357 switch (getOpcode()) {
358 case Constants.BIPUSH:
359 case Constants.LDC:
360 _arg = in.readUnsignedByte();
361 break;
362 case Constants.SIPUSH:
363 case Constants.LDCW:
364 case Constants.LDC2W:
365 _arg = in.readUnsignedShort();
366 }
367 }
368
369 void write(DataOutput out) throws IOException {
370 super.write(out);
371 switch (getOpcode()) {
372 case Constants.BIPUSH:
373 case Constants.LDC:
374 out.writeByte(_arg);
375 break;
376 case Constants.SIPUSH:
377 case Constants.LDCW:
378 case Constants.LDC2W:
379 out.writeShort(_arg);
380 break;
381 }
382 }
383
384 private void calculateOpcode(Object value, boolean wide) {
385 int len = getLength();
386 _arg = -1;
387 if (value == null)
388 setOpcode(Constants.ACONSTNULL);
389 else if (value instanceof Float) {
390 float floatVal = ((Float) value).floatValue();
391 if ((floatVal == 0) || (floatVal == 1) || (floatVal == 2))
392 setOpcode(Constants.FCONST0 + (int) floatVal);
393 else {
394 _arg = getPool().findFloatEntry((float) floatVal, true);
395 setOpcode((_arg > 255 || wide) ? Constants.LDCW
396 : Constants.LDC);
397 }
398 } else if (value instanceof Long) {
399 long longVal = ((Long) value).longValue();
400 if (longVal == 0 || longVal == 1)
401 setOpcode(Constants.LCONST0 + (int) longVal);
402 else {
403 _arg = getPool().findLongEntry(longVal, true);
404 setOpcode(Constants.LDC2W);
405 }
406 } else if (value instanceof Double) {
407 double doubleVal = ((Double) value).doubleValue();
408 if (doubleVal == 0 || doubleVal == 1)
409 setOpcode(Constants.DCONST0 + (int) doubleVal);
410 else {
411 _arg = getPool().findDoubleEntry(doubleVal, true);
412 setOpcode(Constants.LDC2W);
413 }
414 } else if (value instanceof Integer) {
415 int intVal = ((Integer) value).intValue();
416 if (intVal >= -1 && intVal <= 5)
417 setOpcode(Constants.ICONST0 + intVal);
418 else if ((intVal >= -(2 << 6)) && (intVal < (2 << 6))) {
419 setOpcode(Constants.BIPUSH);
420 _arg = intVal;
421 } else if (intVal >= -(2 << 14) && intVal < (2 << 14)) {
422 setOpcode(Constants.SIPUSH);
423 _arg = intVal;
424 } else {
425 _arg = getPool().findIntEntry(intVal, true);
426 setOpcode((_arg > 255 || wide) ? Constants.LDCW
427 : Constants.LDC);
428 }
429 } else if (value instanceof String) {
430 _arg = getPool().findStringEntry((String) value, true);
431 setOpcode((_arg > 255 || wide) ? Constants.LDCW : Constants.LDC);
432 } else if (value instanceof Class) {
433 String name = getProject().getNameCache().getInternalForm(((Class)
434 value).getName(), false);
435 _arg = getPool().findClassEntry(name, true);
436 setOpcode(Constants.LDCW);
437 } else if (value instanceof BCClass) {
438 BCClass bc = (BCClass) value;
439 ClassEntry entry = (ClassEntry)bc.getPool().getEntry(bc.getIndex());
440 if (bc.getPool() == getPool())
441 _arg = getPool().indexOf(entry);
442 else
443 _arg = getPool().findClassEntry((String) entry.getConstant(),
444 true);
445 setOpcode(Constants.LDCW);
446 } else
447 throw new IllegalArgumentException(String.valueOf(value));
448
449 if (len != getLength())
450 invalidateByteIndexes();
451 }
452 }