001package serp.bytecode;
002
003import java.io.*;
004import java.util.*;
005
006import serp.bytecode.lowlevel.*;
007import serp.bytecode.visitor.*;
008import serp.util.*;
009
010/**
011 * A local variable or local variable type.
012 *
013 * @author Abe White
014 * @author Sakir Murat Cengiz
015 */
016public abstract class Local implements BCEntity, InstructionPtr {
017    private LocalTable _owner = null;
018    private InstructionPtrStrategy _target = new InstructionPtrStrategy(this);
019    private Instruction _end = null;
020    private int _length = 0;
021    private int _nameIndex = 0;
022    private int _descriptorIndex = 0;
023    private int _index = 0;
024
025    Local(LocalTable owner) {
026        _owner = owner;
027    }
028
029    /**
030     * The owning table.
031     */
032    public LocalTable getTable() {
033        return _owner;
034    }
035
036    void invalidate() {
037        _owner = null;
038    }
039
040    //////////////////////////
041    // Local index operations
042    //////////////////////////
043
044    /**
045     * Get the local variable index of the current frame for this local.
046     */
047    public int getLocal() {
048        return _index;
049    }
050
051    /**
052     * Set the local variable index of the current frame for this local.
053     */
054    public void setLocal(int index) {
055        _index = index;
056    }
057
058    /**
059     * Return the parameter that this local corresponds to, or -1 if none.
060     */
061    public int getParam() {
062        return getCode().getParamsIndex(getLocal());
063    }
064
065    /**
066     * Set the method parameter that this local corresponds to.
067     */
068    public void setParam(int param) {
069        setLocal(_owner.getCode().getLocalsIndex(param));
070    }
071
072    ////////////////////////////
073    // Start, Length operations
074    ////////////////////////////
075
076    /**
077     * Return the index into the code byte array at which this local starts.
078     */
079    public int getStartPc() {
080        return _target.getByteIndex();
081    }
082
083    /**
084     * Return the instruction marking the beginning of this local.
085     */
086    public Instruction getStart() {
087        return _target.getTargetInstruction();
088    }
089
090    /**
091     * Set the index into the code byte array at which this local starts.
092     */
093    public void setStartPc(int startPc) {
094        _target.setByteIndex(startPc);
095    }
096
097    /**
098     * Set the {@link Instruction} marking the beginning this local.
099     * The instruction must already be a part of the method.
100     * WARNING: if this instruction is deleted, the results are undefined.
101     */
102    public void setStart(Instruction instruction) {
103        _target.setTargetInstruction(instruction);
104    }
105
106    /**
107     * The last {@link Instruction} for which this local is in scope.
108     */
109    public Instruction getEnd() {
110        if (_end != null)
111            return _end;
112        int idx = _target.getByteIndex() + _length;
113        Instruction end = getCode().getInstruction(idx);
114        return (end != null) ? (Instruction) end.prev 
115            : getCode().getLastInstruction();
116    }
117
118    /**
119     * Get the number of bytes for which this local has a value in
120     * the code byte array.
121     */
122    public int getLength() {
123        if (_end != null)
124            return _end.getByteIndex() + _end.getLength() 
125                - _target.getByteIndex();
126        return _length;
127    }
128
129    /**
130     * Set the last {@link Instruction} for which this local is in scope.
131     * The instruction must already be a part of the method.
132     * WARNING: if this instruction is deleted, the results are undefined.
133     */
134    public void setEnd(Instruction end) {
135        if (end.getCode() != getCode())
136            throw new IllegalArgumentException("Instruction pointers and " 
137                + "targets must be part of the same code block.");
138        _end = end;
139        _length = -1;
140    }
141
142    /**
143     * Set the number of bytes for which this local has a value in
144     * the code byte array.
145     */
146    public void setLength(int length) {
147        if (length < 0)
148            throw new IllegalArgumentException(String.valueOf(length));
149        _length = length;
150        _end = null;
151    }
152
153    public void updateTargets() {
154        _target.updateTargets();
155        _end = getEnd();
156    }
157
158    public void replaceTarget(Instruction oldTarget, Instruction newTarget) {
159        _target.replaceTarget(oldTarget, newTarget);
160        if (getEnd() == oldTarget)
161            setEnd(newTarget);
162    }
163
164    /////////////////////////
165    // Name, Type operations
166    /////////////////////////
167
168    /**
169     * Return the {@link ConstantPool} index of the {@link UTF8Entry} that
170     * describes the name of this local. Defaults to 0.
171     */
172    public int getNameIndex() {
173        return _nameIndex;
174    }
175
176    /**
177     * Set the {@link ConstantPool} index of the {@link UTF8Entry} that
178     * describes the name of this local.
179     */
180    public void setNameIndex(int nameIndex) {
181        _nameIndex = nameIndex;
182    }
183
184    /**
185     * Return the name of this local, or null if unset.
186     */
187    public String getName() {
188        if (getNameIndex() == 0)
189            return null;
190        return ((UTF8Entry) getPool().getEntry(getNameIndex())).getValue();
191    }
192
193    /**
194     * Set the name of this inner local.
195     */
196    public void setName(String name) {
197        if (name == null)
198            setNameIndex(0);
199        else
200            setNameIndex(getPool().findUTF8Entry(name, true));
201    }
202
203    /**
204     * Return the {@link ConstantPool} index of the {@link UTF8Entry} that
205     * describes this local. Defaults to 0.
206     */
207    public int getTypeIndex() {
208        return _descriptorIndex;
209    }
210
211    /**
212     * Set the {@link ConstantPool} index of the {@link UTF8Entry} that
213     * describes this local.
214     */
215    public void setTypeIndex(int index) {
216        _descriptorIndex = index;
217    }
218
219    /**
220     * Return the full name of the local's type, or null if unset.
221     */
222    public String getTypeName() {
223        if (getTypeIndex() == 0)
224            return null;
225        UTF8Entry entry = (UTF8Entry) getPool().getEntry(getTypeIndex());
226        return getProject().getNameCache().getExternalForm(entry.getValue(), 
227            false);
228    }
229
230    /**
231     * Set the type of this local.
232     */
233    public void setType(String type) {
234        if (type == null)
235            setTypeIndex(0);
236        else {
237            type = getProject().getNameCache().getInternalForm(type, true);
238            setTypeIndex(getPool().findUTF8Entry(type, true));
239        }
240    }
241
242    ///////////////////////////
243    // BCEntity implementation
244    ///////////////////////////
245
246    public Project getProject() {
247        return _owner.getProject();
248    }
249
250    public ConstantPool getPool() {
251        return _owner.getPool();
252    }
253
254    public ClassLoader getClassLoader() {
255        return _owner.getClassLoader();
256    }
257
258    public boolean isValid() {
259        return _owner != null;
260    }
261
262    //////////////////
263    // I/O operations
264    //////////////////
265
266    void read(DataInput in) throws IOException {
267        setStartPc(in.readUnsignedShort());
268        setLength(in.readUnsignedShort());
269        setNameIndex(in.readUnsignedShort());
270        setTypeIndex(in.readUnsignedShort());
271        setLocal(in.readUnsignedShort());
272    }
273
274    void write(DataOutput out) throws IOException {
275        out.writeShort(getStartPc());
276        out.writeShort(getLength());
277        out.writeShort(getNameIndex());
278        out.writeShort(getTypeIndex());
279        out.writeShort(getLocal());
280    }
281
282    public Code getCode() {
283        return _owner.getCode();
284    }
285}