001    package serp.bytecode;
002    
003    import java.util.*;
004    
005    import serp.bytecode.visitor.*;
006    import serp.util.*;
007    
008    /**
009     * A conversion opcode such as <code>i2l, f2i</code>, etc.
010     * Changing the types of the instruction will automatically
011     * update the underlying opcode. Converting from one type to the same
012     * type will result in a <code>nop</code>.
013     *
014     * @author Abe White
015     */
016    public class ConvertInstruction extends TypedInstruction {
017        private static final Class[][] _mappings = new Class[][] {
018            { boolean.class, int.class },
019            { void.class, int.class },
020            { Object.class, int.class },
021        };
022        private static final Class[][] _fromMappings = new Class[][] {
023            { boolean.class, int.class },
024            { void.class, int.class },
025            { Object.class, int.class },
026            { byte.class, int.class },
027            { char.class, int.class },
028            { short.class, int.class },
029        };
030        String _toType = null;
031        String _fromType = null;
032    
033        ConvertInstruction(Code owner) {
034            super(owner);
035        }
036    
037        ConvertInstruction(Code owner, int opcode) {
038            super(owner, opcode);
039        }
040    
041        public int getLogicalStackChange() {
042            return 0;
043        }
044    
045        public int getStackChange() {
046            switch (getOpcode()) {
047            case Constants.I2L:
048            case Constants.I2D:
049            case Constants.F2L:
050            case Constants.F2D:
051                return 1;
052            case Constants.L2I:
053            case Constants.L2F:
054            case Constants.D2I:
055            case Constants.D2F:
056                return -1;
057            default:
058                return 0;
059            }
060        }
061    
062        public String getTypeName() {
063            switch (getOpcode()) {
064            case Constants.L2I:
065            case Constants.F2I:
066            case Constants.D2I:
067                return int.class.getName();
068            case Constants.I2L:
069            case Constants.F2L:
070            case Constants.D2L:
071                return long.class.getName();
072            case Constants.I2F:
073            case Constants.L2F:
074            case Constants.D2F:
075                return float.class.getName();
076            case Constants.I2D:
077            case Constants.L2D:
078            case Constants.F2D:
079                return double.class.getName();
080            case Constants.I2B:
081                return byte.class.getName();
082            case Constants.I2C:
083                return char.class.getName();
084            case Constants.I2S:
085                return short.class.getName();
086            default:
087                return _toType;
088            }
089        }
090    
091        public TypedInstruction setType(String type) {
092            String toType = mapType(type, _mappings, true);
093            String fromType = getFromTypeName();
094    
095            // if no valid opcode, remember current types in case they reset one
096            // to create a valid opcode
097            if (toType == null || fromType == null || toType.equals(fromType)) {
098                _toType = toType;
099                _fromType = fromType;
100                return (TypedInstruction) setOpcode(Constants.NOP);
101            }
102    
103            // ok, valid conversion possible, forget saved types
104            _toType = null;
105            _fromType = null;
106    
107            char to = toType.charAt(0);
108            char from = fromType.charAt(0);
109            switch (to) {
110            case 'i':
111                switch (from) {
112                case 'l':
113                    return (TypedInstruction) setOpcode(Constants.L2I);
114                case 'f':
115                    return (TypedInstruction) setOpcode(Constants.F2I);
116                case 'd':
117                    return (TypedInstruction) setOpcode(Constants.D2I);
118                }
119            case 'l':
120                switch (from) {
121                case 'i':
122                    return (TypedInstruction) setOpcode(Constants.I2L);
123                case 'f':
124                    return (TypedInstruction) setOpcode(Constants.F2L);
125                case 'd':
126                    return (TypedInstruction) setOpcode(Constants.D2L);
127                }
128            case 'f':
129                switch (from) {
130                case 'i':
131                    return (TypedInstruction) setOpcode(Constants.I2F);
132                case 'l':
133                    return (TypedInstruction) setOpcode(Constants.L2F);
134                case 'd':
135                    return (TypedInstruction) setOpcode(Constants.D2F);
136                }
137            case 'd':
138                switch (from) {
139                case 'i':
140                    return (TypedInstruction) setOpcode(Constants.I2D);
141                case 'l':
142                    return (TypedInstruction) setOpcode(Constants.L2D);
143                case 'f':
144                    return (TypedInstruction) setOpcode(Constants.F2D);
145                }
146            case 'b':
147                if (from == 'i')
148                    return (TypedInstruction) setOpcode(Constants.I2B);
149            case 'C':
150                if (from == 'i')
151                    return (TypedInstruction) setOpcode(Constants.I2C);
152            case 'S':
153                if (from == 'i')
154                    return (TypedInstruction) setOpcode(Constants.I2S);
155            default:
156                throw new IllegalStateException();
157            }
158        }
159    
160        /**
161         * Return the name of the type being converted from.
162         * If neither type has been set, this method will return null.
163         */
164        public String getFromTypeName() {
165            switch (getOpcode()) {
166            case Constants.I2L:
167            case Constants.I2F:
168            case Constants.I2D:
169            case Constants.I2B:
170            case Constants.I2S:
171            case Constants.I2C:
172                return int.class.getName();
173            case Constants.L2I:
174            case Constants.L2F:
175            case Constants.L2D:
176                return long.class.getName();
177            case Constants.F2I:
178            case Constants.F2L:
179            case Constants.F2D:
180                return float.class.getName();
181            case Constants.D2I:
182            case Constants.D2L:
183            case Constants.D2F:
184                return double.class.getName();
185            default:
186                return _fromType;
187            }
188        }
189    
190        /**
191         * Return the {@link Class} of the type being converted from.
192         * If neither type has been set, this method will return null.
193         */
194        public Class getFromType() {
195            String type = getFromTypeName();
196            if (type == null)
197                return null;
198            return Strings.toClass(type, getClassLoader());
199        }
200    
201        /**
202         * Return the bytecode of the type being converted from.
203         * If neither type has been set, this method will return null.
204         */
205        public BCClass getFromTypeBC() {
206            String type = getFromTypeName();
207            if (type == null)
208                return null;
209            return getProject().loadClass(type, getClassLoader());
210        }
211    
212        /**
213         * Set the type being converted from. Types that have no direct
214         * support will be converted accordingly.
215         *
216         * @return this instruction, for method chaining
217         */
218        public ConvertInstruction setFromType(String type) {
219            String fromType = mapType(type, _fromMappings, true);
220            String toType = getTypeName();
221    
222            // if no valid opcode, remember current types in case they reset one
223            // to create a valid opcode
224            if ((toType == null) || (fromType == null) || toType.equals(fromType)) {
225                _toType = toType;
226                _fromType = fromType;
227                return (ConvertInstruction) setOpcode(Constants.NOP);
228            }
229    
230            // ok, valid conversion possible, forget saved types
231            _toType = null;
232            _fromType = null;
233    
234            char to = toType.charAt(0);
235            char from = fromType.charAt(0);
236            switch (from) {
237            case 'i':
238                switch (to) {
239                case 'l':
240                    return (ConvertInstruction) setOpcode(Constants.I2L);
241                case 'f':
242                    return (ConvertInstruction) setOpcode(Constants.I2F);
243                case 'd':
244                    return (ConvertInstruction) setOpcode(Constants.I2D);
245                case 'b':
246                    return (ConvertInstruction) setOpcode(Constants.I2B);
247                case 'c':
248                    return (ConvertInstruction) setOpcode(Constants.I2C);
249                case 's':
250                    return (ConvertInstruction) setOpcode(Constants.I2S);
251                }
252            case 'l':
253                switch (to) {
254                case 'i':
255                    return (ConvertInstruction) setOpcode(Constants.L2I);
256                case 'f':
257                    return (ConvertInstruction) setOpcode(Constants.L2F);
258                case 'd':
259                    return (ConvertInstruction) setOpcode(Constants.L2D);
260                }
261            case 'f':
262                switch (to) {
263                case 'i':
264                    return (ConvertInstruction) setOpcode(Constants.F2I);
265                case 'l':
266                    return (ConvertInstruction) setOpcode(Constants.F2L);
267                case 'd':
268                    return (ConvertInstruction) setOpcode(Constants.F2D);
269                }
270            case 'd':
271                switch (to) {
272                case 'i':
273                    return (ConvertInstruction) setOpcode(Constants.D2I);
274                case 'l':
275                    return (ConvertInstruction) setOpcode(Constants.D2L);
276                case 'f':
277                    return (ConvertInstruction) setOpcode(Constants.D2F);
278                }
279            default:
280                throw new IllegalStateException();
281            }
282        }
283    
284        /**
285         * Set the type being converted from. Types that have no direct
286         * support will be converted accordingly.
287         *
288         * @return this instruction, for method chaining
289         */
290        public ConvertInstruction setFromType(Class type) {
291            if (type == null)
292                return setFromType((String) null);
293            return setFromType(type.getName());
294        }
295    
296        /**
297         * Set the type being converted from. Types that have no direct
298         * support will be converted accordingly.
299         *
300         * @return this instruction, for method chaining
301         */
302        public ConvertInstruction setFromType(BCClass type) {
303            if (type == null)
304                return setFromType((String) null);
305            return setFromType(type.getName());
306        }
307    
308        /**
309         * ConvertInstructions are equal if the types they convert between are
310         * either equal or unset.
311         */
312        public boolean equalsInstruction(Instruction other) {
313            if (other == this)
314                return true;
315            if (!(other instanceof ConvertInstruction))
316                return false;
317    
318            ConvertInstruction ins = (ConvertInstruction) other;
319            if (getOpcode() != Constants.NOP && getOpcode() == ins.getOpcode())
320                return true;
321    
322            String type = getTypeName();
323            String otherType = ins.getTypeName();
324            if (!(type == null || otherType == null || type.equals(otherType)))
325                return false;
326    
327            type = getFromTypeName();
328            otherType = ins.getFromTypeName();
329            return type == null || otherType == null || type.equals(otherType);
330        }
331    
332        public void acceptVisit(BCVisitor visit) {
333            visit.enterConvertInstruction(this);
334            visit.exitConvertInstruction(this);
335        }
336    
337        void read(Instruction orig) {
338            super.read(orig);
339            ConvertInstruction ins = (ConvertInstruction) orig;
340            _toType = ins._toType;
341            _fromType = ins._fromType;
342        }
343    }