001    package serp.bytecode;
002    
003    import java.io.*;
004    import java.util.*;
005    
006    import serp.bytecode.visitor.*;
007    
008    /**
009     * Code blocks compiled from source have local tables mapping
010     * locals used in opcodes to their names and descriptions.
011     *
012     * @author Abe White
013     */
014    public abstract class LocalTable extends Attribute implements InstructionPtr {
015        private List _locals = new ArrayList();
016    
017        LocalTable(int nameIndex, Attributes owner) {
018            super(nameIndex, owner);
019        }
020    
021        /**
022         * Return all the locals of this method.
023         */
024        public Local[] getLocals() {
025            return (Local[]) _locals.toArray(newLocalArray(_locals.size()));
026        }
027    
028        /**
029         * Return the local with the given locals index, or null if none.
030         */
031        public Local getLocal(int local) {
032            for (int i = 0; i < _locals.size(); i++)
033                if (((Local) _locals.get(i)).getLocal() == local)
034                    return (Local) _locals.get(i);
035            return null;
036        }
037    
038        /**
039         * Return the local with the given name, or null if none. If multiple
040         * locals have the given name, which is returned is undefined.
041         */
042        public Local getLocal(String name) {
043            String loc;
044            for (int i = 0; i < _locals.size(); i++) {
045                loc = ((Local) _locals.get(i)).getName();
046                if ((loc == null && name == null) 
047                    || (loc != null && loc.equals(name)))
048                    return (Local) _locals.get(i);
049            }
050            return null;
051        }
052    
053        /**
054         * Return all locals with the given name, or empty array if none.
055         */
056        public Local[] getLocals(String name) {
057            List matches = new LinkedList();
058            String loc;
059            for (int i = 0; i < _locals.size(); i++) {
060                loc = ((Local) _locals.get(i)).getName();
061                if ((loc == null && name == null) 
062                    || (loc != null && loc.equals(name)))
063                    matches.add(_locals.get(i));
064            }
065            return (Local[]) matches.toArray(newLocalArray(matches.size()));
066        }
067    
068        /**
069         * Set the locals of this table. This method is useful when
070         * importing locals from another method.
071         */
072        public void setLocals(Local[] locals) {
073            clear();
074            if (locals != null)
075                for (int i = 0; i < locals.length; i++)
076                    addLocal(locals[i]);
077        }
078    
079        /**
080         * Import a local from another method/class. Note that
081         * the program counter and length from the given local is copied
082         * directly, and thus will be incorrect unless this method is the same
083         * as the one the local is copied from, or the pc and length are reset.
084         */
085        public Local addLocal(Local local) {
086            Local newLocal = addLocal(local.getName(), local.getTypeName());
087            newLocal.setStartPc(local.getStartPc());
088            newLocal.setLength(local.getLength());
089            return newLocal;
090        }
091    
092        /**
093         * Add a local to this table.
094         */
095        public Local addLocal() {
096            Local local = newLocal();
097            _locals.add(local);
098            return local;
099        }
100    
101        /**
102         * Create a new element of this table.
103         */
104        protected abstract Local newLocal();
105    
106        /**
107         * Create a new array.
108         */
109        protected abstract Local[] newLocalArray(int size);
110    
111        /**
112         * Add a local to this table.
113         */
114        public Local addLocal(String name, String type) {
115            Local local = addLocal();
116            local.setName(name);
117            local.setType(type);
118            return local;
119        }
120    
121        /**
122         * Clear all locals from this table.
123         */
124        public void clear() {
125            for (int i = 0; i < _locals.size(); i++)
126                ((Local) _locals.get(i)).invalidate();
127            _locals.clear();
128        }
129    
130        /**
131         * Removes the local with the given locals index from the table.
132         *
133         * @return true if a local was removed, false otherwise
134         */
135        public boolean removeLocal(int local) {
136            return removeLocal(getLocal(local));
137        }
138    
139        /**
140         * Removes the local with the given name from this method.
141         *
142         * @return true if a local was removed, false otherwise
143         */
144        public boolean removeLocal(String name) {
145            return removeLocal(getLocal(name));
146        }
147    
148        /**
149         * Removes a local from this method. After this method, the local
150         * will be invalid, and the result of any operations on it is undefined.
151         *
152         * @return true if a local was removed, false otherwise
153         */
154        public boolean removeLocal(Local local) {
155            if (local == null || !_locals.remove(local))
156                return false;
157            local.invalidate();
158            return true;
159        }
160    
161        public void updateTargets() {
162            for (int i = 0; i < _locals.size(); i++)
163                ((Local) _locals.get(i)).updateTargets();
164        }
165    
166        public void replaceTarget(Instruction oldTarget, Instruction newTarget) {
167            for (int i = 0; i < _locals.size(); i++)
168                ((Local) _locals.get(i)).replaceTarget(oldTarget, newTarget);
169        }
170    
171        public Code getCode() {
172            return (Code) getOwner();
173        }
174    
175        int getLength() {
176            return 2 + (10 * _locals.size());
177        }
178    
179        void read(Attribute other) {
180            setLocals(((LocalTable) other).getLocals());
181        }
182    
183        void read(DataInput in, int length) throws IOException {
184            clear();
185            int numLocals = in.readUnsignedShort();
186            Local Local;
187            for (int i = 0; i < numLocals; i++) {
188                Local = addLocal();
189                Local.read(in);
190            }
191        }
192    
193        void write(DataOutput out, int length) throws IOException {
194            out.writeShort(_locals.size());
195            for (int i = 0; i < _locals.size(); i++)
196                ((Local) _locals.get(i)).write(out);
197        }
198    }