View Javadoc

1   package serp.bytecode;
2   
3   import java.io.*;
4   import java.util.*;
5   
6   import serp.bytecode.visitor.*;
7   
8   /***
9    * Code blocks compiled from source have line number tables mapping
10   * opcodes to source lines. This table automatically maintains line
11   * numbers in ascending order by their start program counter position
12   * at all times.
13   *
14   * @author Abe White
15   */
16  public class LineNumberTable extends Attribute implements InstructionPtr {
17      private List _lineNumbers = new ArrayList();
18  
19      LineNumberTable(int nameIndex, Attributes owner) {
20          super(nameIndex, owner);
21      }
22  
23      /***
24       * Return the line numbers held in this table.
25       */
26      public LineNumber[] getLineNumbers() {
27          Collections.sort(_lineNumbers);
28          return (LineNumber[]) _lineNumbers.toArray
29              (new LineNumber[_lineNumbers.size()]);
30      }
31  
32      /***
33       * Return the line number for the given program counter, or null if none.
34       */
35      public LineNumber getLineNumber(int pc) {
36          for (int i = _lineNumbers.size() - 1; i >= 0; i--)
37              if (((LineNumber) _lineNumbers.get(i))._target.getByteIndex() <= pc)
38                  return (LineNumber) _lineNumbers.get(i);
39          return null;
40      }
41  
42      /***
43       * Return the line number for the given instruction, or null if none.
44       */
45      public LineNumber getLineNumber(Instruction ins) {
46          if (ins == null)
47              return null;
48          return getLineNumber(ins.getByteIndex());
49      }
50  
51      /***
52       * Set the line numbers for the table. This method is useful when
53       * importing line numbers from another method.
54       */
55      public void setLineNumbers(LineNumber[] lines) {
56          clear();
57          if (lines != null)
58              for (int i = 0; i < lines.length; i++)
59                  addLineNumber(lines[i]);
60      }
61  
62      /***
63       * Import a line number from another method.
64       *
65       * @return the newly added line number
66       */
67      public LineNumber addLineNumber(LineNumber ln) {
68          LineNumber line = addLineNumber();
69          line.setStartPc(ln.getStartPc());
70          line.setLine(ln.getLine());
71          return line;
72      }
73  
74      /***
75       * Add a new line number to this table.
76       */
77      public LineNumber addLineNumber() {
78          LineNumber ln = new LineNumber(this);
79          _lineNumbers.add(ln);
80          return ln;
81      }
82  
83      /***
84       * Add a new line number to this table.
85       */
86      public LineNumber addLineNumber(int startPc, int line) {
87          LineNumber ln = addLineNumber();
88          ln.setStartPc(startPc);
89          ln.setLine(line);
90          return ln;
91      }
92  
93      /***
94       * Add a new line number to this table.
95       */
96      public LineNumber addLineNumber(Instruction start, int line) {
97          LineNumber ln = addLineNumber();
98          ln.setStart(start);
99          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 }