001package serp.bytecode;
002
003import java.io.*;
004import java.util.*;
005
006import 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 */
014public 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}