001    package serp.bytecode;
002    
003    import serp.bytecode.visitor.*;
004    
005    /**
006     * An instruction comparing two stack values. Examples include
007     * <code>lcmp, fcmpl</code>, etc.
008     *
009     * @author Abe White
010     */
011    public class CmpInstruction extends TypedInstruction {
012        private static Class[][] _mappings = new Class[][] {
013            { int.class, long.class },
014            { byte.class, long.class },
015            { char.class, long.class },
016            { short.class, long.class },
017            { boolean.class, long.class },
018            { void.class, long.class },
019            { Object.class, long.class },
020        };
021    
022        CmpInstruction(Code owner) {
023            super(owner);
024        }
025    
026        CmpInstruction(Code owner, int opcode) {
027            super(owner, opcode);
028        }
029    
030        public int getLogicalStackChange() {
031            switch (getOpcode()) {
032            case Constants.NOP:
033                return 0;
034            default:
035                return -1;
036            }
037        }
038    
039        public int getStackChange() {
040            switch (getOpcode()) {
041            case Constants.LCMP:
042            case Constants.DCMPL:
043            case Constants.DCMPG:
044                return -3;
045            case Constants.NOP:
046                return 0;
047            default:
048                return -1;
049            }
050        }
051    
052        public String getTypeName() {
053            switch (getOpcode()) {
054            case Constants.LCMP:
055                return long.class.getName();
056            case Constants.FCMPL:
057            case Constants.FCMPG:
058                return float.class.getName();
059            case Constants.DCMPL:
060            case Constants.DCMPG:
061                return double.class.getName();
062            default:
063                return null;
064            }
065        }
066    
067        public TypedInstruction setType(String type) {
068            type = mapType(type, _mappings, true);
069            if (type == null)
070                return (TypedInstruction) setOpcode(Constants.NOP);
071    
072            int opcode = getOpcode();
073            switch (type.charAt(0)) {
074            case 'l':
075                return (TypedInstruction) setOpcode(Constants.LCMP);
076            case 'f':
077                if ((opcode == Constants.FCMPL) || (opcode == Constants.DCMPL))
078                    return (TypedInstruction) setOpcode(Constants.FCMPL);
079                return (TypedInstruction) setOpcode(Constants.FCMPG);
080            case 'd':
081                if ((opcode == Constants.FCMPL) || (opcode == Constants.DCMPL))
082                    return (TypedInstruction) setOpcode(Constants.DCMPL);
083                return (TypedInstruction) setOpcode(Constants.DCMPG);
084            default:
085                throw new IllegalStateException();
086            }
087        }
088    
089        /**
090         * Return the number that will be placed on the stack if this instruction
091         * is of type float or double and one of the operands is NaN. For
092         * FCMPG or DCMPG, this value will be 1; for FCMPL or DCMPL this value
093         * will be -1. For LCMP or if the type is unset, this value will be 0.
094         */
095        public int getNaNValue() {
096            switch (getOpcode()) {
097            case Constants.FCMPL:
098            case Constants.DCMPL:
099                return -1;
100            case Constants.FCMPG:
101            case Constants.DCMPG:
102                return 1;
103            default:
104                return 0;
105            }
106        }
107    
108        /**
109         * Set the number that will be placed on the stack if this instruction
110         * is of type float or double and one of the operands is NaN. For
111         * FCMPG or DCMPG, this value should be 1; for FCMPL or DCMPL this value
112         * should be -1. For LCMP, this value should be 0.
113         *
114         * @return this instruction, for method chaining
115         */
116        public CmpInstruction setNaNValue(int nan) {
117            switch (getOpcode()) {
118            case Constants.FCMPL:
119            case Constants.FCMPG:
120                if (nan == 1)
121                    setOpcode(Constants.FCMPG);
122                else if (nan == -1)
123                    setOpcode(Constants.FCMPL);
124                else
125                    throw new IllegalArgumentException("Invalid nan for type");
126            case Constants.DCMPL:
127            case Constants.DCMPG:
128                if (nan == 1)
129                    setOpcode(Constants.DCMPG);
130                else if (nan == -1)
131                    setOpcode(Constants.DCMPL);
132                else
133                    throw new IllegalArgumentException("Invalid nan for type");
134            default:
135                if (nan != 0)
136                    throw new IllegalArgumentException("Invalid nan for type");
137            }
138            return this;
139        }
140    
141        public void acceptVisit(BCVisitor visit) {
142            visit.enterCmpInstruction(this);
143            visit.exitCmpInstruction(this);
144        }
145    }