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