001package serp.bytecode;
002
003import serp.bytecode.visitor.*;
004
005/**
006 * An instruction that has an argument of an index into the
007 * local variable table of the current frame. This includes most of the
008 * <code>load</code> and <code>store</code> instructions.
009 *
010 * <p>The local variable table size is fixed by the <code>maxLocals</code>
011 * property of the code block. Long and double types take up 2 local variable
012 * indexes.</p>
013 *
014 * <p>Parameter values to methods are loaded into the local variable table
015 * prior to the execution of the first instruction. The 0 index of the
016 * table is set to the instance of the class the method is being invoked on.</p>
017 *
018 * @author Abe White
019 */
020public abstract class LocalVariableInstruction extends TypedInstruction {
021    private int _index = -1;
022
023    LocalVariableInstruction(Code owner) {
024        super(owner);
025    }
026
027    LocalVariableInstruction(Code owner, int opcode) {
028        super(owner, opcode);
029        calculateLocal();
030    }
031
032    public String getTypeName() {
033        return null;
034    }
035
036    public TypedInstruction setType(String type) {
037        throw new UnsupportedOperationException();
038    }
039
040    /**
041     * Return the index of the local variable that this instruction operates on.
042     */
043    public int getLocal() {
044        return _index;
045    }
046
047    /**
048     * Set the index of the local variable that this instruction operates on.
049     *
050     * @return this instruction, for method chaining
051     */
052    public LocalVariableInstruction setLocal(int index) {
053        _index = index;
054        calculateOpcode();
055        return this;
056    }
057
058    /**
059     * Return the parameter that this instruction operates on, or -1 if none.
060     */
061    public int getParam() {
062        return getCode().getParamsIndex(getLocal());
063    }
064
065    /**
066     * Set the method parameter that this instruction operates on. This
067     * will set both the local index and the type of the instruction based
068     * on the current method parameters.
069     */
070    public LocalVariableInstruction setParam(int param) {
071        int local = getCode().getLocalsIndex(param);
072        if (local != -1) {
073            BCMethod method = getCode().getMethod();
074            setType(method.getParamNames()[param]);
075        }
076        return setLocal(local);
077    }
078
079    /**
080     * Return the local variable object this instruction
081     * operates on, or null if none.
082     *
083     * @see LocalVariableTable#getLocalVariable(int)
084     */
085    public LocalVariable getLocalVariable() {
086        LocalVariableTable table = getCode().getLocalVariableTable(false);
087        if (table == null)
088            return null;
089        return table.getLocalVariable(getLocal());
090    }
091
092    /**
093     * Set the local variable object this instruction
094     * operates on. This method will set both the type and local index
095     * of this instruction from the given local variable.
096     *
097     * @return this instruction, for method chaining
098     */
099    public LocalVariableInstruction setLocalVariable(LocalVariable local) {
100        if (local == null)
101            return setLocal(-1);
102        String type = local.getTypeName();
103        if (type != null)
104            setType(type);
105        return setLocal(local.getLocal());
106    }
107
108    /**
109     * Two local variable instructions are equal if the local index they
110     * reference is equal or if either index is 0/unset.
111     */
112    public boolean equalsInstruction(Instruction other) {
113        if (this == other)
114            return true;
115        if (!getClass().equals(other.getClass()))
116            return false;
117
118        LocalVariableInstruction ins = (LocalVariableInstruction) other;
119        int index = getLocal();
120        int insIndex = ins.getLocal();
121        return index == -1 || insIndex == -1 || index == insIndex;
122    }
123
124    void read(Instruction orig) {
125        super.read(orig);
126        setLocal(((LocalVariableInstruction) orig).getLocal());
127    }
128
129    /**
130     * Subclasses with variable opcodes can use this method to be
131     * notified that information possibly affecting the opcode has been changed.
132     */
133    void calculateOpcode() {
134    }
135
136    /**
137     * Subclasses can use this method to calculate
138     * the locals index based on their opcode.
139     */
140    void calculateLocal() {
141    }
142}