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