001package serp.bytecode;
002
003import 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 */
011public 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}