001package serp.bytecode;
002
003import java.io.*;
004
005import serp.bytecode.visitor.*;
006
007/**
008 * The <code>wide</code> instruction, which is used to allow other
009 * instructions to index values beyond what they can normally index baed
010 * on the length of their arguments.
011 *
012 * @author Abe White
013 */
014public class WideInstruction extends LocalVariableInstruction {
015    private static final Class[][] _mappings = new Class[][] {
016        { byte.class, int.class },
017        { boolean.class, int.class },
018        { char.class, int.class },
019        { short.class, int.class },
020        { void.class, int.class },
021    };
022    private int _ins = Constants.NOP;
023    private int _inc = -1;
024
025    WideInstruction(Code owner) {
026        super(owner, Constants.WIDE);
027    }
028
029    int getLength() {
030        // opcode, ins, index
031        int length = super.getLength() + 1 + 2;
032
033        // increment
034        if (_ins == Constants.IINC)
035            length += 2;
036        return length;
037    }
038
039    public int getStackChange() {
040        switch (_ins) {
041        case Constants.ILOAD:
042        case Constants.FLOAD:
043        case Constants.ALOAD:
044            return 1;
045        case Constants.LLOAD:
046        case Constants.DLOAD:
047            return 2;
048        case Constants.ISTORE:
049        case Constants.FSTORE:
050        case Constants.ASTORE:
051            return -1;
052        case Constants.LSTORE:
053        case Constants.DSTORE:
054            return -2;
055        default:
056            return 0;
057        }
058    }
059
060    public int getLogicalStackChange() {
061        switch (_ins) {
062        case Constants.ILOAD:
063        case Constants.FLOAD:
064        case Constants.ALOAD:
065        case Constants.LLOAD:
066        case Constants.DLOAD:
067            return 1;
068        case Constants.ISTORE:
069        case Constants.FSTORE:
070        case Constants.ASTORE:
071        case Constants.LSTORE:
072        case Constants.DSTORE:
073            return -1;
074        default:
075            return 0;
076        }
077    }
078
079    public String getTypeName() {
080        switch (_ins) {
081        case Constants.ILOAD:
082        case Constants.ISTORE:
083            return int.class.getName();
084        case Constants.LLOAD:
085        case Constants.LSTORE:
086            return long.class.getName();
087        case Constants.FLOAD:
088        case Constants.FSTORE:
089            return float.class.getName();
090        case Constants.DLOAD:
091        case Constants.DSTORE:
092            return double.class.getName();
093        case Constants.ALOAD:
094        case Constants.ASTORE:
095            return Object.class.getName();
096        default:
097            return null;
098        }
099    }
100
101    public TypedInstruction setType(String type) {
102        type = mapType(type, _mappings, true);
103        switch (_ins) {
104        case Constants.ILOAD:
105        case Constants.LLOAD:
106        case Constants.FLOAD:
107        case Constants.DLOAD:
108        case Constants.ALOAD:
109            if (type == null)
110                throw new IllegalStateException();
111            switch (type.charAt(0)) {
112            case 'i':
113                return (TypedInstruction) setInstruction(Constants.ILOAD);
114            case 'l':
115                return (TypedInstruction) setInstruction(Constants.LLOAD);
116            case 'f':
117                return (TypedInstruction) setInstruction(Constants.FLOAD);
118            case 'd':
119                return (TypedInstruction) setInstruction(Constants.DLOAD);
120            default:
121                return (TypedInstruction) setInstruction(Constants.ALOAD);
122            }
123        case Constants.ISTORE:
124        case Constants.LSTORE:
125        case Constants.FSTORE:
126        case Constants.DSTORE:
127        case Constants.ASTORE:
128            if (type == null)
129                throw new IllegalStateException();
130            switch (type.charAt(0)) {
131            case 'i':
132                return (TypedInstruction) setInstruction(Constants.ISTORE);
133            case 'l':
134                return (TypedInstruction) setInstruction(Constants.LSTORE);
135            case 'f':
136                return (TypedInstruction) setInstruction(Constants.FSTORE);
137            case 'd':
138                return (TypedInstruction) setInstruction(Constants.DSTORE);
139            default:
140                return (TypedInstruction) setInstruction(Constants.ASTORE);
141            }
142        default:
143            if (type != null)
144                throw new IllegalStateException("Augmented instruction not " 
145                    + "typed");
146            return this;
147        }
148    }
149
150    /**
151     * Return the opcode of the instruction to modify; this will return one
152     * of the constants defined in {@link Constants}.
153     */
154    public int getInstruction() {
155        return _ins;
156    }
157
158    /**
159     * Set the type of instruction this wide instruction modifies.
160     */
161    public WideInstruction setInstruction(Instruction ins) {
162        if (ins == null)
163            return setInstruction(Constants.NOP);
164        setInstruction(ins.getOpcode());
165        if (_ins == Constants.IINC)
166            _inc = ((IIncInstruction) ins).getIncrement();
167        return this;
168    }
169
170    /**
171     * Set the type of instruction this wide instruction modifies.
172     */
173    public WideInstruction setInstruction(int opcode) {
174        int len = getLength();
175        _ins = opcode;
176        if (len != getLength())
177            invalidateByteIndexes();
178        return this;
179    }
180
181    /**
182     * Set the type of instruction this wide instruction modifies.
183     *
184     * @return this instruction, for method chaining
185     */
186    public WideInstruction iinc() {
187        return setInstruction(Constants.IINC);
188    }
189
190    /**
191     * Set the type of instruction this wide instruction modifies.
192     *
193     * @return this instruction, for method chaining
194     */
195    public WideInstruction ret() {
196        return setInstruction(Constants.RET);
197    }
198
199    /**
200     * Set the type of instruction this wide instruction modifies.
201     *
202     * @return this instruction, for method chaining
203     */
204    public WideInstruction iload() {
205        return setInstruction(Constants.ILOAD);
206    }
207
208    /**
209     * Set the type of instruction this wide instruction modifies.
210     *
211     * @return this instruction, for method chaining
212     */
213    public WideInstruction fload() {
214        return setInstruction(Constants.FLOAD);
215    }
216
217    /**
218     * Set the type of instruction this wide instruction modifies.
219     *
220     * @return this instruction, for method chaining
221     */
222    public WideInstruction aload() {
223        return setInstruction(Constants.ALOAD);
224    }
225
226    /**
227     * Set the type of instruction this wide instruction modifies.
228     *
229     * @return this instruction, for method chaining
230     */
231    public WideInstruction lload() {
232        return setInstruction(Constants.LLOAD);
233    }
234
235    /**
236     * Set the type of instruction this wide instruction modifies.
237     *
238     * @return this instruction, for method chaining
239     */
240    public WideInstruction dload() {
241        return setInstruction(Constants.DLOAD);
242    }
243
244    /**
245     * Set the type of instruction this wide instruction modifies.
246     *
247     * @return this instruction, for method chaining
248     */
249    public WideInstruction istore() {
250        return setInstruction(Constants.ISTORE);
251    }
252
253    /**
254     * Set the type of instruction this wide instruction modifies.
255     *
256     * @return this instruction, for method chaining
257     */
258    public WideInstruction fstore() {
259        return setInstruction(Constants.FSTORE);
260    }
261
262    /**
263     * Set the type of instruction this wide instruction modifies.
264     *
265     * @return this instruction, for method chaining
266     */
267    public WideInstruction astore() {
268        return setInstruction(Constants.ASTORE);
269    }
270
271    /**
272     * Set the type of instruction this wide instruction modifies.
273     *
274     * @return this instruction, for method chaining
275     */
276    public WideInstruction lstore() {
277        return setInstruction(Constants.LSTORE);
278    }
279
280    /**
281     * Set the type of instruction this wide instruction modifies.
282     *
283     * @return this instruction, for method chaining
284     */
285    public WideInstruction dstore() {
286        return setInstruction(Constants.DSTORE);
287    }
288
289    /**
290     * Return the increment for this instruction if it augments IINC, or -1
291     * if unset.
292     */
293    public int getIncrement() {
294        return _inc;
295    }
296
297    /**
298     * Set the increment on this instruction if it augments IINC.
299     *
300     * @return this Instruction, for method chaining
301     */
302    public WideInstruction setIncrement(int val) {
303        _inc = val;
304        return this;
305    }
306
307    /**
308     * WideInstructions are equal if the instruction they augment is the same
309     * or unset.
310     */
311    public boolean equalsInstruction(Instruction other) {
312        if (other == this)
313            return true;
314        if (!super.equalsInstruction(other))
315            return false;
316        if (!(other instanceof WideInstruction))
317            return false;
318
319        WideInstruction ins = (WideInstruction) other;
320        int code = getInstruction();
321        int otherCode = ins.getInstruction();
322        if (code != otherCode)
323            return false;
324        if (code == Constants.IINC) {
325            int inc = getIncrement();
326            int otherInc = ins.getIncrement();
327            return (inc == -1) || (otherInc == -1) || (inc == otherInc);
328        }
329        return true;
330    }
331
332    public void acceptVisit(BCVisitor visit) {
333        visit.enterWideInstruction(this);
334        visit.exitWideInstruction(this);
335    }
336
337    void read(Instruction orig) {
338        super.read(orig);
339        setInstruction(((WideInstruction) orig).getInstruction());
340    }
341
342    void read(DataInput in) throws IOException {
343        super.read(in);
344        _ins = in.readUnsignedByte();
345        setLocal(in.readUnsignedShort());
346        if (_ins == Constants.IINC)
347            _inc = in.readUnsignedShort();
348    }
349
350    void write(DataOutput out) throws IOException {
351        super.write(out);
352        out.writeByte(_ins);
353        out.writeShort(getLocal());
354        if (_ins == Constants.IINC)
355            out.writeShort(_inc);
356    }
357}