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 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     */
016    public 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    }