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            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    }