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 }