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    }