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