001    package serp.bytecode;
002    
003    import java.io.*;
004    
005    import serp.bytecode.visitor.*;
006    
007    /**
008     * Loads a value from the locals table to the stack.
009     *
010     * @author Abe White
011     */
012    public class LoadInstruction extends LocalVariableInstruction {
013        private static final Class[][] _mappings = new Class[][] {
014            { byte.class, int.class },
015            { boolean.class, int.class },
016            { char.class, int.class },
017            { short.class, int.class },
018            { void.class, int.class },
019        };
020        String _type = null;
021    
022        LoadInstruction(Code owner) {
023            super(owner);
024        }
025    
026        LoadInstruction(Code owner, int opcode) {
027            super(owner, opcode);
028        }
029    
030        int getLength() {
031            switch (getOpcode()) {
032            case Constants.ILOAD:
033            case Constants.LLOAD:
034            case Constants.FLOAD:
035            case Constants.DLOAD:
036            case Constants.ALOAD:
037                return super.getLength() + 1;
038            default:
039                return super.getLength();
040            }
041        }
042    
043        public int getStackChange() {
044            switch (getOpcode()) {
045            case Constants.LLOAD:
046            case Constants.LLOAD0:
047            case Constants.LLOAD1:
048            case Constants.LLOAD2:
049            case Constants.LLOAD3:
050            case Constants.DLOAD:
051            case Constants.DLOAD0:
052            case Constants.DLOAD1:
053            case Constants.DLOAD2:
054            case Constants.DLOAD3:
055                return 2;
056            case Constants.NOP:
057                return 0;
058            default:
059                return 1;
060            }
061        }
062    
063        public int getLogicalStackChange() {
064            switch (getOpcode()) {
065            case Constants.NOP:
066                return 0;
067            default:
068                return 1;
069            }
070        }
071    
072        public String getTypeName() {
073            switch (getOpcode()) {
074            case Constants.ILOAD:
075            case Constants.ILOAD0:
076            case Constants.ILOAD1:
077            case Constants.ILOAD2:
078            case Constants.ILOAD3:
079                return int.class.getName();
080            case Constants.LLOAD:
081            case Constants.LLOAD0:
082            case Constants.LLOAD1:
083            case Constants.LLOAD2:
084            case Constants.LLOAD3:
085                return long.class.getName();
086            case Constants.FLOAD:
087            case Constants.FLOAD0:
088            case Constants.FLOAD1:
089            case Constants.FLOAD2:
090            case Constants.FLOAD3:
091                return float.class.getName();
092            case Constants.DLOAD:
093            case Constants.DLOAD0:
094            case Constants.DLOAD1:
095            case Constants.DLOAD2:
096            case Constants.DLOAD3:
097                return double.class.getName();
098            case Constants.ALOAD:
099            case Constants.ALOAD0:
100            case Constants.ALOAD1:
101            case Constants.ALOAD2:
102            case Constants.ALOAD3:
103                return Object.class.getName();
104            default:
105                return _type;
106            }
107        }
108    
109        public TypedInstruction setType(String type) {
110            type = mapType(type, _mappings, true);
111            int local = getLocal();
112            int len = getLength();
113    
114            // if an invalid type or local, revert to nop
115            if (type == null || local < 0) {
116                _type = type;
117                setOpcode(Constants.NOP);
118            } else {
119                // valid opcode, unset saved type
120                _type = null;
121                switch (type.charAt(0)) {
122                case 'i':
123                    setOpcode((local > 3) ? Constants.ILOAD
124                        : (Constants.ILOAD0 + local));
125                    break;
126                case 'l':
127                    setOpcode((local > 3) ? Constants.LLOAD
128                        : (Constants.LLOAD0 + local));
129                    break;
130                case 'f':
131                    setOpcode((local > 3) ? Constants.FLOAD
132                        : (Constants.FLOAD0 + local));
133                    break;
134                case 'd':
135                    setOpcode((local > 3) ? Constants.DLOAD
136                        : (Constants.DLOAD0 + local));
137                    break;
138                default:
139                    setOpcode((local > 3) ? Constants.ALOAD
140                        : (Constants.ALOAD0 + local));
141                }
142            }
143            if (len != getLength())
144                invalidateByteIndexes();
145            return this;
146        }
147    
148        /**
149         * Equivalent to <code>setLocal (0).setType (Object.class)</code>; the
150         * <code>this</code> ptr is always passed in local variable 0.
151         *
152         * @return this instruction, for method chaining
153         */
154        public LoadInstruction setThis() {
155            return (LoadInstruction) setLocal(0).setType(Object.class);
156        }
157    
158        /**
159         * Equivalent to <code>getLocal () == 0 && getType () ==
160         * Object.class</code>; the <code>this</code> ptr
161         * is always passed in local variable 0.
162         */
163        public boolean isThis() {
164            return getLocal() == 0 && getType() == Object.class;
165        }
166    
167        /**
168         * LoadInstructions are equal if the type they reference the same
169         * type and locals index or if either is unset.
170         */
171        public boolean equalsInstruction(Instruction other) {
172            if (other == this)
173                return true;
174            if (!super.equalsInstruction(other))
175                return false;
176    
177            String type = getTypeName();
178            String otherType = ((LoadInstruction) other).getTypeName();
179            return type == null || otherType == null || type.equals(otherType);
180        }
181    
182        public void acceptVisit(BCVisitor visit) {
183            visit.enterLoadInstruction(this);
184            visit.exitLoadInstruction(this);
185        }
186    
187        void read(Instruction orig) {
188            super.read(orig);
189            LoadInstruction ins = (LoadInstruction) orig;
190            _type = ins._type;
191        }
192    
193        void read(DataInput in) throws IOException {
194            super.read(in);
195            switch (getOpcode()) {
196            case Constants.ILOAD:
197            case Constants.LLOAD:
198            case Constants.FLOAD:
199            case Constants.DLOAD:
200            case Constants.ALOAD:
201                setLocal(in.readUnsignedByte());
202                break;
203            }
204        }
205    
206        void write(DataOutput out) throws IOException {
207            super.write(out);
208            switch (getOpcode()) {
209            case Constants.ILOAD:
210            case Constants.LLOAD:
211            case Constants.FLOAD:
212            case Constants.DLOAD:
213            case Constants.ALOAD:
214                out.writeByte(getLocal());
215            }
216        }
217    
218        void calculateOpcode() {
219            // taken care of when setting type
220            setType(getTypeName());
221        }
222    
223        void calculateLocal() {
224            switch (getOpcode()) {
225            case Constants.ILOAD0:
226            case Constants.LLOAD0:
227            case Constants.FLOAD0:
228            case Constants.DLOAD0:
229            case Constants.ALOAD0:
230                setLocal(0);
231                break;
232            case Constants.ILOAD1:
233            case Constants.LLOAD1:
234            case Constants.FLOAD1:
235            case Constants.DLOAD1:
236            case Constants.ALOAD1:
237                setLocal(1);
238                break;
239            case Constants.ILOAD2:
240            case Constants.LLOAD2:
241            case Constants.FLOAD2:
242            case Constants.DLOAD2:
243            case Constants.ALOAD2:
244                setLocal(2);
245                break;
246            case Constants.ILOAD3:
247            case Constants.LLOAD3:
248            case Constants.FLOAD3:
249            case Constants.DLOAD3:
250            case Constants.ALOAD3:
251                setLocal(3);
252                break;
253            }
254        }
255    }