001    package serp.bytecode;
002    
003    import java.io.*;
004    import java.util.*;
005    
006    import serp.bytecode.lowlevel.*;
007    import serp.bytecode.visitor.*;
008    import serp.util.*;
009    
010    /**
011     * A local variable or local variable type.
012     *
013     * @author Abe White
014     * @author Sakir Murat Cengiz
015     */
016    public 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    }