001package serp.bytecode;
002
003import java.util.*;
004
005import serp.bytecode.visitor.*;
006
007/**
008 * One of the math operations defined in the {@link Constants} interface.
009 * Changing the type or operation of the instruction will automatically
010 * update the underlying opcode.
011 *
012 * @author Abe White
013 */
014public class MathInstruction extends TypedInstruction {
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        { Object.class, int.class },
022    };
023    private int _op = -1;
024    private String _type = null;
025
026    MathInstruction(Code owner) {
027        super(owner);
028    }
029
030    MathInstruction(Code owner, int opcode) {
031        super(owner, opcode);
032        _op = getOperation();
033    }
034
035    public int getStackChange() {
036        int op = getOperation();
037        if (op == Constants.MATH_NEG || getOpcode() == Constants.NOP)
038            return 0;
039
040        String type = getTypeName();
041        if (long.class.getName().equals(type) 
042            || double.class.getName().equals(type)) {
043            switch (getOpcode()) {
044            case (Constants.LSHL):
045            case (Constants.LSHR):
046            case (Constants.LUSHR):
047                return -1;
048            default:
049                return -2;
050            }
051        }
052        return -1;
053    }
054
055    public int getLogicalStackChange() {
056        int op = getOperation();
057        if (op == Constants.MATH_NEG || getOpcode() == Constants.NOP)
058            return 0;
059        return -1;
060    }
061
062    public String getTypeName() {
063        switch (getOpcode()) {
064        case Constants.IADD:
065        case Constants.ISUB:
066        case Constants.IMUL:
067        case Constants.IDIV:
068        case Constants.IREM:
069        case Constants.INEG:
070        case Constants.ISHL:
071        case Constants.ISHR:
072        case Constants.IUSHR:
073        case Constants.IAND:
074        case Constants.IOR:
075        case Constants.IXOR:
076            return int.class.getName();
077        case Constants.LADD:
078        case Constants.LSUB:
079        case Constants.LMUL:
080        case Constants.LDIV:
081        case Constants.LREM:
082        case Constants.LNEG:
083        case Constants.LSHL:
084        case Constants.LSHR:
085        case Constants.LUSHR:
086        case Constants.LAND:
087        case Constants.LOR:
088        case Constants.LXOR:
089            return long.class.getName();
090        case Constants.FADD:
091        case Constants.FSUB:
092        case Constants.FMUL:
093        case Constants.FDIV:
094        case Constants.FREM:
095        case Constants.FNEG:
096            return float.class.getName();
097        case Constants.DADD:
098        case Constants.DSUB:
099        case Constants.DMUL:
100        case Constants.DDIV:
101        case Constants.DREM:
102        case Constants.DNEG:
103            return double.class.getName();
104        default:
105            return _type;
106        }
107    }
108
109    public TypedInstruction setType(String type) {
110        type = mapType(type, _mappings, true);
111
112        // if an invalid type or op, revert to nop
113        if (type == null || _op < 0) {
114            _type = type;
115            return (TypedInstruction) setOpcode(Constants.NOP);
116        }
117
118        // valid opcode, unset saved type
119        _type = null;
120        switch (type.charAt(0)) {
121        case 'i':
122            return (TypedInstruction) setOpcode(_op);
123        case 'l':
124            return (TypedInstruction) setOpcode(_op + 1);
125        case 'f':
126            return (TypedInstruction) setOpcode(_op + 2);
127        case 'd':
128            return (TypedInstruction) setOpcode(_op + 3);
129        default:
130            throw new IllegalStateException();
131        }
132    }
133
134    /**
135     * Set the math operation to be performed. This should be one of the
136     * math constant defined in {@link Constants}.
137     *
138     * @return this instruction, for method chaining
139     */
140    public MathInstruction setOperation(int operation) {
141        _op = operation;
142
143        // this calculates the opcode
144        setType(getTypeName());
145        return this;
146    }
147
148    /**
149     * Return the operation for this math instruction; will be one of the
150     * math constant defined in {@link Constants}, or -1 if unset.
151     */
152    public int getOperation() {
153        switch (getOpcode()) {
154        case Constants.IADD:
155        case Constants.LADD:
156        case Constants.FADD:
157        case Constants.DADD:
158            return Constants.MATH_ADD;
159        case Constants.ISUB:
160        case Constants.LSUB:
161        case Constants.FSUB:
162        case Constants.DSUB:
163            return Constants.MATH_SUB;
164        case Constants.IMUL:
165        case Constants.LMUL:
166        case Constants.FMUL:
167        case Constants.DMUL:
168            return Constants.MATH_MUL;
169        case Constants.IDIV:
170        case Constants.LDIV:
171        case Constants.FDIV:
172        case Constants.DDIV:
173            return Constants.MATH_DIV;
174        case Constants.IREM:
175        case Constants.LREM:
176        case Constants.FREM:
177        case Constants.DREM:
178            return Constants.MATH_REM;
179        case Constants.INEG:
180        case Constants.LNEG:
181        case Constants.FNEG:
182        case Constants.DNEG:
183            return Constants.MATH_NEG;
184        case Constants.ISHL:
185        case Constants.LSHL:
186            return Constants.MATH_SHL;
187        case Constants.ISHR:
188        case Constants.LSHR:
189            return Constants.MATH_SHR;
190        case Constants.IUSHR:
191        case Constants.LUSHR:
192            return Constants.MATH_USHR;
193        case Constants.IAND:
194        case Constants.LAND:
195            return Constants.MATH_AND;
196        case Constants.IOR:
197        case Constants.LOR:
198            return Constants.MATH_OR;
199        case Constants.IXOR:
200        case Constants.LXOR:
201            return Constants.MATH_XOR;
202        default:
203            return _op;
204        }
205    }
206
207    /**
208     * MathInstructions are equal if they have the same operation and type,
209     * or the operation and type of either is unset.
210     */
211    public boolean equalsInstruction(Instruction other) {
212        if (this == other)
213            return true;
214        if (!(other instanceof MathInstruction))
215            return false;
216
217        MathInstruction ins = (MathInstruction) other;
218        int op = getOperation();
219        int otherOp = ins.getOperation();
220        boolean opEq = op == -1 || otherOp == -1 || op == otherOp;
221
222        String type = getTypeName();
223        String otherType = ins.getTypeName();
224        boolean typeEq = type == null || otherType == null 
225            || type.equals(otherType);
226        return opEq && typeEq;
227    }
228
229    public void acceptVisit(BCVisitor visit) {
230        visit.enterMathInstruction(this);
231        visit.exitMathInstruction(this);
232    }
233
234    void read(Instruction orig) {
235        super.read(orig);
236        MathInstruction ins = (MathInstruction) orig;
237        _type = ins._type;
238        _op = ins._op;
239    }
240}