001package serp.bytecode;
002
003import serp.bytecode.lowlevel.*;
004import serp.bytecode.visitor.*;
005
006/**
007 * Represents an instruction that manipulates the stack of the current
008 * frame. Using the {@link #setType} methods is a hint about the type being
009 * manipulated that might cause this instruction to use the wide version
010 * of the opcode it represents (if manipulating a long or double). This
011 * saves the developer from having to decide at compile time whether to
012 * use <code>pop</code> or <code>pop2</code>, etc.
013 *
014 * @author Abe White
015 */
016public class StackInstruction extends TypedInstruction {
017    StackInstruction(Code owner, int opcode) {
018        super(owner, opcode);
019    }
020
021    public int getStackChange() {
022        switch (getOpcode()) {
023        case Constants.POP:
024            return -1;
025        case Constants.POP2:
026            return -2;
027        case Constants.DUP:
028        case Constants.DUPX1:
029        case Constants.DUPX2:
030            return 1;
031        case Constants.DUP2:
032        case Constants.DUP2X1:
033        case Constants.DUP2X2:
034            return 2;
035        default:
036            return 0;
037        }
038    }
039
040    /**
041     * This method will always return null; use {@link #isWide} to determine
042     * if this is pop2, dup2, etc.
043     */
044    public String getTypeName() {
045        return null;
046    }
047
048    public TypedInstruction setType(String type) {
049        type = getProject().getNameCache().getExternalForm(type, false);
050        return setWide(long.class.getName().equals(type) 
051            || double.class.getName().equals(type));
052    }
053
054    /**
055     * Return whether to use the wide form of the current opcode for
056     * operations on longs or doubles.
057     */
058    public boolean isWide() {
059        switch (getOpcode()) {
060        case Constants.POP2:
061        case Constants.DUP2:
062        case Constants.DUP2X1:
063        case Constants.DUP2X2:
064            return true;
065        default:
066            return false;
067        }
068    }
069
070    /**
071     * Set whether to use the wide form of the current opcode for operations
072     * on longs or doubles.
073     *
074     * @return this instruction, for method chaining
075     */
076    public StackInstruction setWide(boolean wide) {
077        switch (getOpcode()) {
078        case Constants.POP:
079            if (wide)
080                setOpcode(Constants.POP2);
081            break;
082        case Constants.POP2:
083            if (!wide)
084                setOpcode(Constants.POP);
085            break;
086        case Constants.DUP:
087            if (wide)
088                setOpcode(Constants.DUP2);
089            break;
090        case Constants.DUP2:
091            if (!wide)
092                setOpcode(Constants.DUP);
093            break;
094        case Constants.DUPX1:
095            if (wide)
096                setOpcode(Constants.DUP2X1);
097            break;
098        case Constants.DUP2X1:
099            if (!wide)
100                setOpcode(Constants.DUPX1);
101            break;
102        case Constants.DUPX2:
103            if (wide)
104                setOpcode(Constants.DUP2X2);
105            break;
106        case Constants.DUP2X2:
107            if (!wide)
108                setOpcode(Constants.DUPX2);
109            break;
110        }
111        return this;
112    }
113
114    public void acceptVisit(BCVisitor visit) {
115        visit.enterStackInstruction(this);
116        visit.exitStackInstruction(this);
117    }
118}