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