001 package edu.rice.cs.cunit.classFile.attributes; 002 003 import edu.rice.cs.cunit.classFile.attributes.visitors.IAttributeVisitor; 004 import edu.rice.cs.cunit.classFile.code.instructions.LineNumberTable; 005 import edu.rice.cs.cunit.classFile.constantPool.AUTFPoolInfo; 006 import edu.rice.cs.cunit.classFile.constantPool.ConstantPool; 007 import edu.rice.cs.cunit.util.Types; 008 009 /** 010 * Represents the LineNumberTable attribute in a class file. 011 * 012 * @author Mathias Ricken 013 */ 014 public class LineNumberTableAttributeInfo extends AAttributeInfo { 015 /** 016 * Storage class for line number/program counter _pairs. 017 */ 018 public static class LineNumberRecord { 019 public int startPC; 020 public int lineNo; 021 022 public LineNumberRecord(int startPC, int lineNo) { 023 assert(startPC <= 0xffff); 024 assert(lineNo <= 0xffff); 025 this.startPC = startPC; 026 this.lineNo = lineNo; 027 } 028 } 029 030 /** 031 * Constructor. 032 * 033 * @param name attribute name 034 * @param data attribute data 035 * @param cp constant pool 036 * 037 * @throws ClassFormatError 038 */ 039 public LineNumberTableAttributeInfo(AUTFPoolInfo name, byte[] data, ConstantPool cp) throws ClassFormatError { 040 super(name, data, cp); 041 } 042 043 /** 044 * Return the number of line number records. 045 * 046 * @return number of line number records 047 * 048 * @throws ClassFormatError 049 */ 050 public int getLineNumberCount() { 051 int res = Types.ushortFromBytes(_data, 0); 052 assert(res <= 0xffff); 053 return res; 054 } 055 056 /** 057 * Return a copy of the line number records. 058 * 059 * @return list of line number records 060 * 061 * @throws ClassFormatError 062 */ 063 public LineNumberRecord[] getLineNumbers() throws ClassFormatError { 064 int count = getLineNumberCount(); 065 LineNumberRecord[] lnr = new LineNumberRecord[count]; 066 for(int i = 0; i < count; ++i) { 067 lnr[i] = new LineNumberRecord(Types.ushortFromBytes(_data, 2 + 4 * i), 068 Types.ushortFromBytes(_data, 4 + 4 * i)); 069 } 070 return lnr; 071 } 072 073 /** 074 * Set the line number records. 075 * 076 * @param lnr list of line number records 077 */ 078 public void setLineNumbers(LineNumberRecord[] lnr) { 079 byte[] newData = new byte[2 + 4 * lnr.length]; 080 Types.bytesFromShort((short)lnr.length, newData, 0); 081 for(int i = 0; i < lnr.length; ++i) { 082 Types.bytesFromShort((short)(0xffff & lnr[i].startPC), newData, 2 + 4 * i); 083 Types.bytesFromShort((short)(0xffff & lnr[i].lineNo), newData, 4 + 4 * i); 084 } 085 setData(newData); 086 } 087 088 /** 089 * Execute a visitor on this attribute. 090 * 091 * @param visitor visitor 092 * @param param visitor-specific parameter 093 * 094 * @return visitor-specific return value 095 */ 096 public <R, D> R execute(IAttributeVisitor<R, D> visitor, D param) { 097 return visitor.lineNumberTableCase(this, param); 098 } 099 100 /** 101 * Return a human-readable version of this attribute. 102 * 103 * @return string 104 */ 105 public String toString() { 106 StringBuilder x = new StringBuilder(); 107 x.append(_name + " <" + getLineNumberCount() + " line numbers { "); 108 boolean first = true; 109 for(LineNumberRecord lnr : getLineNumbers()) { 110 if (first) { 111 first = false; 112 } 113 else { 114 x.append(", "); 115 } 116 x.append("(PC=" + lnr.startPC + " @ " + lnr.lineNo + ")"); 117 } 118 x.append(" } >"); 119 return x.toString(); 120 } 121 122 /** 123 * Adjust program counter values contained in this attribute, starting at startPC, by adding deltaPC to them. 124 * 125 * @param startPC program counter to start at 126 * @param deltaPC change in program counter values 127 */ 128 public void adjustPC(int startPC, int deltaPC) { 129 LineNumberRecord[] lnrs = getLineNumbers(); 130 for(LineNumberRecord l : lnrs) { 131 if (l.startPC >= startPC) { 132 // Debug.out.println("Adjusted LineNumberTableAttribute.lnrs[].startPC: "+l.startPC+" --> "+(l.startPC+deltaPC)); 133 l.startPC += deltaPC; 134 } 135 } 136 setLineNumbers(lnrs); 137 } 138 139 /** 140 * Translate the program counter values contained in this attribute from an old line number table to a new one. 141 * 142 * @param index critical point (insertion or deletion point) 143 * @param deltaIndex delta value to add to all old line numbers greater than the critical point 144 * @param oldLnt old line number table 145 * @param newLnt new line number table 146 */ 147 public void translatePC(int index, int deltaIndex, LineNumberTable oldLnt, LineNumberTable newLnt) { 148 LineNumberRecord[] lnrs = getLineNumbers(); 149 for(LineNumberRecord l : lnrs) { 150 int oldLineNo = oldLnt.getLineNumber(l.startPC); 151 oldLineNo += (oldLineNo > index) ? deltaIndex : 0; 152 l.startPC = (short)newLnt.getPC(oldLineNo); 153 } 154 setLineNumbers(lnrs); 155 } 156 157 /** 158 * Creates and returns a copy of this object. 159 */ 160 public Object clone() throws CloneNotSupportedException { 161 return super.clone(); 162 } 163 164 /** 165 * Returns the name of the attribute as it appears in the class file. 166 * 167 * @return name of the attribute. 168 */ 169 public static String getAttributeName() { 170 return "LineNumberTable"; 171 } 172 }