001package serp.bytecode;
002
003import java.io.*;
004
005import serp.bytecode.lowlevel.*;
006import serp.bytecode.visitor.*;
007import 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 */
018public 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        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        } else if (value instanceof Float) {
416            float floatVal = ((Float) value).floatValue();
417            if ((floatVal == 0) || (floatVal == 1) || (floatVal == 2))
418                setOpcode(Constants.FCONST0 + (int) floatVal);
419            else {
420                _arg = getPool().findFloatEntry((float) floatVal, true);
421                setOpcode((_arg > 255 || wide) ? Constants.LDCW 
422                    : Constants.LDC);
423            }
424        } else if (value instanceof Long) {
425            long longVal = ((Long) value).longValue();
426            if (longVal == 0 || longVal == 1)
427                setOpcode(Constants.LCONST0 + (int) longVal);
428            else {
429                _arg = getPool().findLongEntry(longVal, true);
430                setOpcode(Constants.LDC2W);
431            }
432        } else if (value instanceof Double) {
433            double doubleVal = ((Double) value).doubleValue();
434            if (doubleVal == 0 || doubleVal == 1)
435                setOpcode(Constants.DCONST0 + (int) doubleVal);
436            else {
437                _arg = getPool().findDoubleEntry(doubleVal, true);
438                setOpcode(Constants.LDC2W);
439            }
440        } else if (value instanceof Integer) {
441            int intVal = ((Integer) value).intValue();
442            if (intVal >= -1 && intVal <= 5)
443                setOpcode(Constants.ICONST0 + intVal);
444            else if ((intVal >= -(2 << 6)) && (intVal < (2 << 6))) {
445                setOpcode(Constants.BIPUSH);
446                _arg = intVal;
447            } else if (intVal >= -(2 << 14) && intVal < (2 << 14)) {
448                setOpcode(Constants.SIPUSH);
449                _arg = intVal;
450            } else {
451                _arg = getPool().findIntEntry(intVal, true);
452                setOpcode((_arg > 255 || wide) ? Constants.LDCW 
453                    : Constants.LDC);
454            }
455        } else if (value instanceof String) {
456            _arg = getPool().findStringEntry((String) value, true);
457            setOpcode((_arg > 255 || wide) ? Constants.LDCW : Constants.LDC);
458        } else 
459            throw new IllegalArgumentException(String.valueOf(value));
460
461        if (len != getLength())
462            invalidateByteIndexes();
463    }
464}