View Javadoc

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         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 }