001package serp.bytecode;
002
003import java.io.*;
004import java.util.*;
005
006import serp.bytecode.visitor.*;
007
008/**
009 * Code blocks compiled from source have line number tables mapping
010 * opcodes to source lines. This table automatically maintains line
011 * numbers in ascending order by their start program counter position
012 * at all times.
013 *
014 * @author Abe White
015 */
016public class LineNumberTable extends Attribute implements InstructionPtr {
017    private List _lineNumbers = new ArrayList();
018
019    LineNumberTable(int nameIndex, Attributes owner) {
020        super(nameIndex, owner);
021    }
022
023    /**
024     * Return the line numbers held in this table.
025     */
026    public LineNumber[] getLineNumbers() {
027        Collections.sort(_lineNumbers);
028        return (LineNumber[]) _lineNumbers.toArray
029            (new LineNumber[_lineNumbers.size()]);
030    }
031
032    /**
033     * Return the line number for the given program counter, or null if none.
034     */
035    public LineNumber getLineNumber(int pc) {
036        for (int i = _lineNumbers.size() - 1; i >= 0; i--)
037            if (((LineNumber) _lineNumbers.get(i))._target.getByteIndex() <= pc)
038                return (LineNumber) _lineNumbers.get(i);
039        return null;
040    }
041
042    /**
043     * Return the line number for the given instruction, or null if none.
044     */
045    public LineNumber getLineNumber(Instruction ins) {
046        if (ins == null)
047            return null;
048        return getLineNumber(ins.getByteIndex());
049    }
050
051    /**
052     * Set the line numbers for the table. This method is useful when
053     * importing line numbers from another method.
054     */
055    public void setLineNumbers(LineNumber[] lines) {
056        clear();
057        if (lines != null)
058            for (int i = 0; i < lines.length; i++)
059                addLineNumber(lines[i]);
060    }
061
062    /**
063     * Import a line number from another method.
064     *
065     * @return the newly added line number
066     */
067    public LineNumber addLineNumber(LineNumber ln) {
068        LineNumber line = addLineNumber();
069        line.setStartPc(ln.getStartPc());
070        line.setLine(ln.getLine());
071        return line;
072    }
073
074    /**
075     * Add a new line number to this table.
076     */
077    public LineNumber addLineNumber() {
078        LineNumber ln = new LineNumber(this);
079        _lineNumbers.add(ln);
080        return ln;
081    }
082
083    /**
084     * Add a new line number to this table.
085     */
086    public LineNumber addLineNumber(int startPc, int line) {
087        LineNumber ln = addLineNumber();
088        ln.setStartPc(startPc);
089        ln.setLine(line);
090        return ln;
091    }
092
093    /**
094     * Add a new line number to this table.
095     */
096    public LineNumber addLineNumber(Instruction start, int line) {
097        LineNumber ln = addLineNumber();
098        ln.setStart(start);
099        ln.setLine(line);
100        return ln;
101    }
102
103    /**
104     * Clear the line numbers.
105     */
106    public void clear() {
107        for (int i = 0; i < _lineNumbers.size(); i++)
108            ((LineNumber) _lineNumbers.get(i)).invalidate();
109        _lineNumbers.clear();
110    }
111
112    /**
113     * Remove the given line.
114     *
115     * @return true if the line was removed, false otherwise
116     */
117    public boolean removeLineNumber(LineNumber ln) {
118        if (ln == null || !_lineNumbers.remove(ln))
119            return false;
120        ln.invalidate();
121        return true;
122    }
123
124    /**
125     * Remove the line number for the given program counter.
126     *
127     * @return true if the line was removed, false otherwise
128     */
129    public boolean removeLineNumber(int pc) {
130        return removeLineNumber(getLineNumber(pc));
131    }
132
133    /**
134     * Remove the line number for the given instruction.
135     *
136     * @return true if the line was removed, false otherwise
137     */
138    public boolean removeLineNumber(Instruction ins) {
139        return removeLineNumber(getLineNumber(ins));
140    }
141
142    public void updateTargets() {
143        for (int i = 0; i < _lineNumbers.size(); i++)
144            ((LineNumber) _lineNumbers.get(i)).updateTargets();
145    }
146
147    public void replaceTarget(Instruction oldTarget, Instruction newTarget) {
148        for (int i = 0; i < _lineNumbers.size(); i++)
149            ((LineNumber) _lineNumbers.get(i)).replaceTarget(oldTarget,
150                newTarget);
151    }
152
153    public void acceptVisit(BCVisitor visit) {
154        visit.enterLineNumberTable(this);
155        LineNumber[] lines = getLineNumbers();
156        for (int i = 0; i < lines.length; i++)
157            lines[i].acceptVisit(visit);
158        visit.exitLineNumberTable(this);
159    }
160
161    int getLength() {
162        return 2 + (4 * _lineNumbers.size());
163    }
164
165    void read(Attribute other) {
166        setLineNumbers(((LineNumberTable) other).getLineNumbers());
167    }
168
169    void read(DataInput in, int length) throws IOException {
170        clear();
171        int numLines = in.readUnsignedShort();
172        LineNumber lineNumber;
173        for (int i = 0; i < numLines; i++) {
174            lineNumber = addLineNumber();
175            lineNumber.read(in);
176        }
177    }
178
179    void write(DataOutput out, int length) throws IOException {
180        LineNumber[] lines = getLineNumbers();
181        out.writeShort(lines.length);
182        for (int i = 0; i < lines.length; i++)
183            lines[i].write(out);
184    }
185
186    public Code getCode() {
187        return (Code) getOwner();
188    }
189}