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