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