001    package serp.bytecode;
002    
003    import java.io.*;
004    
005    import 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     */
014    public 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    }