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