001package serp.bytecode;
002
003import java.io.*;
004
005import serp.bytecode.visitor.*;
006
007/**
008 * Loads a value from the locals table to the stack.
009 *
010 * @author Abe White
011 */
012public 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}