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    }