001package serp.bytecode;
002
003import java.io.*;
004
005import serp.bytecode.visitor.*;
006
007/**
008 * An instruction to store a value from a local variable onto the stack.
009 *
010 * @author Abe White
011 */
012public class StoreInstruction 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    StoreInstruction(Code owner) {
023        super(owner);
024    }
025
026    StoreInstruction(Code owner, int opcode) {
027        super(owner, opcode);
028    }
029
030    int getLength() {
031        switch (getOpcode()) {
032        case Constants.ISTORE:
033        case Constants.LSTORE:
034        case Constants.FSTORE:
035        case Constants.DSTORE:
036        case Constants.ASTORE:
037            return super.getLength() + 1;
038        default:
039            return super.getLength();
040        }
041    }
042
043    public int getLogicalStackChange() {
044        switch (getOpcode()) {
045        case Constants.NOP:
046            return 0;
047        default:
048            return -1;
049        }
050    }
051
052    public int getStackChange() {
053        switch (getOpcode()) {
054        case Constants.LSTORE:
055        case Constants.LSTORE0:
056        case Constants.LSTORE1:
057        case Constants.LSTORE2:
058        case Constants.LSTORE3:
059        case Constants.DSTORE:
060        case Constants.DSTORE0:
061        case Constants.DSTORE1:
062        case Constants.DSTORE2:
063        case Constants.DSTORE3:
064            return -2;
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.ISTORE:
075        case Constants.ISTORE0:
076        case Constants.ISTORE1:
077        case Constants.ISTORE2:
078        case Constants.ISTORE3:
079            return int.class.getName();
080        case Constants.LSTORE:
081        case Constants.LSTORE0:
082        case Constants.LSTORE1:
083        case Constants.LSTORE2:
084        case Constants.LSTORE3:
085            return long.class.getName();
086        case Constants.FSTORE:
087        case Constants.FSTORE0:
088        case Constants.FSTORE1:
089        case Constants.FSTORE2:
090        case Constants.FSTORE3:
091            return float.class.getName();
092        case Constants.DSTORE:
093        case Constants.DSTORE0:
094        case Constants.DSTORE1:
095        case Constants.DSTORE2:
096        case Constants.DSTORE3:
097            return double.class.getName();
098        case Constants.ASTORE:
099        case Constants.ASTORE0:
100        case Constants.ASTORE1:
101        case Constants.ASTORE2:
102        case Constants.ASTORE3:
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.ISTORE
124                    : (Constants.ISTORE0 + local));
125                break;
126            case 'l':
127                setOpcode((local > 3) ? Constants.LSTORE
128                    : (Constants.LSTORE0 + local));
129                break;
130            case 'f':
131                setOpcode((local > 3) ? Constants.FSTORE
132                    : (Constants.FSTORE0 + local));
133                break;
134            case 'd':
135                setOpcode((local > 3) ? Constants.DSTORE
136                    : (Constants.DSTORE0 + local));
137                break;
138            default:
139                setOpcode((local > 3) ? Constants.ASTORE
140                    : (Constants.ASTORE0 + local));
141                break;
142            }
143        }
144        if (len != getLength())
145            invalidateByteIndexes();
146        return this;
147    }
148
149    /**
150     * StoreInstructions are equal if the type they reference the same
151     * type and locals index or if either is unset.
152     */
153    public boolean equalsInstruction(Instruction other) {
154        if (other == this)
155            return true;
156        if (!super.equalsInstruction(other))
157            return false;
158
159        String type = getTypeName();
160        String otherType = ((StoreInstruction) other).getTypeName();
161        return type == null || otherType == null || type.equals(otherType);
162    }
163
164    public void acceptVisit(BCVisitor visit) {
165        visit.enterStoreInstruction(this);
166        visit.exitStoreInstruction(this);
167    }
168
169    void read(Instruction orig) {
170        super.read(orig);
171        StoreInstruction ins = (StoreInstruction) orig;
172        _type = ins._type;
173    }
174
175    void read(DataInput in) throws IOException {
176        super.read(in);
177        switch (getOpcode()) {
178        case Constants.ISTORE:
179        case Constants.LSTORE:
180        case Constants.FSTORE:
181        case Constants.DSTORE:
182        case Constants.ASTORE:
183            setLocal(in.readUnsignedByte());
184            break;
185        }
186    }
187
188    void write(DataOutput out) throws IOException {
189        super.write(out);
190        switch (getOpcode()) {
191        case Constants.ISTORE:
192        case Constants.LSTORE:
193        case Constants.FSTORE:
194        case Constants.DSTORE:
195        case Constants.ASTORE:
196            out.writeByte(getLocal());
197        }
198    }
199
200    void calculateOpcode() {
201        // taken care of when setting type
202        setType(getTypeName());
203    }
204
205    void calculateLocal() {
206        switch (getOpcode()) {
207        case Constants.ISTORE0:
208        case Constants.LSTORE0:
209        case Constants.FSTORE0:
210        case Constants.DSTORE0:
211        case Constants.ASTORE0:
212            setLocal(0);
213            break;
214        case Constants.ISTORE1:
215        case Constants.LSTORE1:
216        case Constants.FSTORE1:
217        case Constants.DSTORE1:
218        case Constants.ASTORE1:
219            setLocal(1);
220            break;
221        case Constants.ISTORE2:
222        case Constants.LSTORE2:
223        case Constants.FSTORE2:
224        case Constants.DSTORE2:
225        case Constants.ASTORE2:
226            setLocal(2);
227            break;
228        case Constants.ISTORE3:
229        case Constants.LSTORE3:
230        case Constants.FSTORE3:
231        case Constants.DSTORE3:
232        case Constants.ASTORE3:
233            setLocal(3);
234            break;
235        }
236    }
237}