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        if (end != null && (end.prev instanceof Instruction)) {
115             return (Instruction) end.prev;
116        }
117        return getCode().getLastInstruction();
118    }
119
120    /**
121     * Get the number of bytes for which this local has a value in
122     * the code byte array.
123     */
124    public int getLength() {
125        if (_end != null)
126            return _end.getByteIndex() + _end.getLength() 
127                - _target.getByteIndex();
128        return _length;
129    }
130
131    /**
132     * Set the last {@link Instruction} for which this local is in scope.
133     * The instruction must already be a part of the method.
134     * WARNING: if this instruction is deleted, the results are undefined.
135     */
136    public void setEnd(Instruction end) {
137        if (end.getCode() != getCode())
138            throw new IllegalArgumentException("Instruction pointers and " 
139                + "targets must be part of the same code block.");
140        _end = end;
141        _length = -1;
142    }
143
144    /**
145     * Set the number of bytes for which this local has a value in
146     * the code byte array.
147     */
148    public void setLength(int length) {
149        if (length < 0)
150            throw new IllegalArgumentException(String.valueOf(length));
151        _length = length;
152        _end = null;
153    }
154
155    public void updateTargets() {
156        _target.updateTargets();
157        _end = getEnd();
158    }
159
160    public void replaceTarget(Instruction oldTarget, Instruction newTarget) {
161        _target.replaceTarget(oldTarget, newTarget);
162        if (getEnd() == oldTarget)
163            setEnd(newTarget);
164    }
165
166    /////////////////////////
167    // Name, Type operations
168    /////////////////////////
169
170    /**
171     * Return the {@link ConstantPool} index of the {@link UTF8Entry} that
172     * describes the name of this local. Defaults to 0.
173     */
174    public int getNameIndex() {
175        return _nameIndex;
176    }
177
178    /**
179     * Set the {@link ConstantPool} index of the {@link UTF8Entry} that
180     * describes the name of this local.
181     */
182    public void setNameIndex(int nameIndex) {
183        _nameIndex = nameIndex;
184    }
185
186    /**
187     * Return the name of this local, or null if unset.
188     */
189    public String getName() {
190        if (getNameIndex() == 0)
191            return null;
192        return ((UTF8Entry) getPool().getEntry(getNameIndex())).getValue();
193    }
194
195    /**
196     * Set the name of this inner local.
197     */
198    public void setName(String name) {
199        if (name == null)
200            setNameIndex(0);
201        else
202            setNameIndex(getPool().findUTF8Entry(name, true));
203    }
204
205    /**
206     * Return the {@link ConstantPool} index of the {@link UTF8Entry} that
207     * describes this local. Defaults to 0.
208     */
209    public int getTypeIndex() {
210        return _descriptorIndex;
211    }
212
213    /**
214     * Set the {@link ConstantPool} index of the {@link UTF8Entry} that
215     * describes this local.
216     */
217    public void setTypeIndex(int index) {
218        _descriptorIndex = index;
219    }
220
221    /**
222     * Return the full name of the local's type, or null if unset.
223     */
224    public String getTypeName() {
225        if (getTypeIndex() == 0)
226            return null;
227        UTF8Entry entry = (UTF8Entry) getPool().getEntry(getTypeIndex());
228        return getProject().getNameCache().getExternalForm(entry.getValue(), 
229            false);
230    }
231
232    /**
233     * Set the type of this local.
234     */
235    public void setType(String type) {
236        if (type == null)
237            setTypeIndex(0);
238        else {
239            type = getProject().getNameCache().getInternalForm(type, true);
240            setTypeIndex(getPool().findUTF8Entry(type, true));
241        }
242    }
243
244    ///////////////////////////
245    // BCEntity implementation
246    ///////////////////////////
247
248    public Project getProject() {
249        return _owner.getProject();
250    }
251
252    public ConstantPool getPool() {
253        return _owner.getPool();
254    }
255
256    public ClassLoader getClassLoader() {
257        return _owner.getClassLoader();
258    }
259
260    public boolean isValid() {
261        return _owner != null;
262    }
263
264    //////////////////
265    // I/O operations
266    //////////////////
267
268    void read(DataInput in) throws IOException {
269        setStartPc(in.readUnsignedShort());
270        setLength(in.readUnsignedShort());
271        setNameIndex(in.readUnsignedShort());
272        setTypeIndex(in.readUnsignedShort());
273        setLocal(in.readUnsignedShort());
274    }
275
276    void write(DataOutput out) throws IOException {
277        out.writeShort(getStartPc());
278        out.writeShort(getLength());
279        out.writeShort(getNameIndex());
280        out.writeShort(getTypeIndex());
281        out.writeShort(getLocal());
282    }
283
284    public Code getCode() {
285        return _owner.getCode();
286    }
287}