1/*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License.  Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16package javassist.bytecode;
17
18import java.io.DataInputStream;
19import java.io.IOException;
20import java.util.Map;
21
22/**
23 * <code>LocalVariableTable_attribute</code>.
24 */
25public class LocalVariableAttribute extends AttributeInfo {
26    /**
27     * The name of this attribute <code>"LocalVariableTable"</code>.
28     */
29    public static final String tag = "LocalVariableTable";
30
31    /**
32     * The name of the attribute <code>"LocalVariableTypeTable"</code>.
33     */
34    public static final String typeTag = "LocalVariableTypeTable";
35
36    /**
37     * Constructs an empty LocalVariableTable.
38     */
39    public LocalVariableAttribute(ConstPool cp) {
40        super(cp, tag, new byte[2]);
41        ByteArray.write16bit(0, info, 0);
42    }
43
44    /**
45     * Constructs an empty LocalVariableTable.
46     *
47     * @param name      the attribute name.
48     *                  <code>LocalVariableAttribute.tag</code> or
49     *                  <code>LocalVariableAttribute.typeTag</code>.
50     * @see #tag
51     * @see #typeTag
52     * @since 3.1
53     * @deprecated
54     */
55    public LocalVariableAttribute(ConstPool cp, String name) {
56        super(cp, name, new byte[2]);
57        ByteArray.write16bit(0, info, 0);
58    }
59
60    LocalVariableAttribute(ConstPool cp, int n, DataInputStream in)
61        throws IOException
62    {
63        super(cp, n, in);
64    }
65
66    LocalVariableAttribute(ConstPool cp, String name, byte[] i) {
67        super(cp, name, i);
68    }
69
70    /**
71     * Appends a new entry to <code>local_variable_table</code>.
72     *
73     * @param startPc           <code>start_pc</code>
74     * @param length            <code>length</code>
75     * @param nameIndex         <code>name_index</code>
76     * @param descriptorIndex   <code>descriptor_index</code>
77     * @param index             <code>index</code>
78     */
79    public void addEntry(int startPc, int length, int nameIndex,
80                         int descriptorIndex, int index) {
81        int size = info.length;
82        byte[] newInfo = new byte[size + 10];
83        ByteArray.write16bit(tableLength() + 1, newInfo, 0);
84        for (int i = 2; i < size; ++i)
85            newInfo[i] = info[i];
86
87        ByteArray.write16bit(startPc, newInfo, size);
88        ByteArray.write16bit(length, newInfo, size + 2);
89        ByteArray.write16bit(nameIndex, newInfo, size + 4);
90        ByteArray.write16bit(descriptorIndex, newInfo, size + 6);
91        ByteArray.write16bit(index, newInfo, size + 8);
92        info = newInfo;
93    }
94
95    void renameClass(String oldname, String newname) {
96        ConstPool cp = getConstPool();
97        int n = tableLength();
98        for (int i = 0; i < n; ++i) {
99            int pos = i * 10 + 2;
100            int index = ByteArray.readU16bit(info, pos + 6);
101            if (index != 0) {
102                String desc = cp.getUtf8Info(index);
103                desc = renameEntry(desc, oldname, newname);
104                ByteArray.write16bit(cp.addUtf8Info(desc), info, pos + 6);
105            }
106        }
107    }
108
109    String renameEntry(String desc, String oldname, String newname) {
110        return Descriptor.rename(desc, oldname, newname);
111    }
112
113    void renameClass(Map classnames) {
114        ConstPool cp = getConstPool();
115        int n = tableLength();
116        for (int i = 0; i < n; ++i) {
117            int pos = i * 10 + 2;
118            int index = ByteArray.readU16bit(info, pos + 6);
119            if (index != 0) {
120                String desc = cp.getUtf8Info(index);
121                desc = renameEntry(desc, classnames);
122                ByteArray.write16bit(cp.addUtf8Info(desc), info, pos + 6);
123            }
124        }
125    }
126
127    String renameEntry(String desc, Map classnames) {
128        return Descriptor.rename(desc, classnames);
129    }
130
131    /**
132     * For each <code>local_variable_table[i].index</code>,
133     * this method increases <code>index</code> by <code>delta</code>.
134     *
135     * @param lessThan      the index does not change if it
136     *                      is less than this value.
137     */
138    public void shiftIndex(int lessThan, int delta) {
139        int size = info.length;
140        for (int i = 2; i < size; i += 10){
141            int org = ByteArray.readU16bit(info, i + 8);
142            if (org >= lessThan)
143                ByteArray.write16bit(org + delta, info, i + 8);
144        }
145    }
146
147    /**
148     * Returns <code>local_variable_table_length</code>.
149     * This represents the number of entries in the table.
150     */
151    public int tableLength() {
152        return ByteArray.readU16bit(info, 0);
153    }
154
155    /**
156     * Returns <code>local_variable_table[i].start_pc</code>.
157     * This represents the index into the code array from which the local
158     * variable is effective.
159     *
160     * @param i         the i-th entry.
161     */
162    public int startPc(int i) {
163        return ByteArray.readU16bit(info, i * 10 + 2);
164    }
165
166    /**
167     * Returns <code>local_variable_table[i].length</code>.
168     * This represents the length of the code region in which the local
169     * variable is effective.
170     *
171     * @param i         the i-th entry.
172     */
173    public int codeLength(int i) {
174        return ByteArray.readU16bit(info, i * 10 + 4);
175    }
176
177    /**
178     * Adjusts start_pc and length if bytecode is inserted in a method body.
179     */
180    void shiftPc(int where, int gapLength, boolean exclusive) {
181        int n = tableLength();
182        for (int i = 0; i < n; ++i) {
183            int pos = i * 10 + 2;
184            int pc = ByteArray.readU16bit(info, pos);
185            int len = ByteArray.readU16bit(info, pos + 2);
186
187            /* if pc == 0, then the local variable is a method parameter.
188             */
189            if (pc > where || (exclusive && pc == where && pc != 0))
190                ByteArray.write16bit(pc + gapLength, info, pos);
191            else if (pc + len > where || (exclusive && pc + len == where))
192                ByteArray.write16bit(len + gapLength, info, pos + 2);
193        }
194    }
195
196    /**
197     * Returns the value of <code>local_variable_table[i].name_index</code>.
198     * This represents the name of the local variable.
199     *
200     * @param i         the i-th entry.
201     */
202    public int nameIndex(int i) {
203        return ByteArray.readU16bit(info, i * 10 + 6);
204    }
205
206    /**
207     * Returns the name of the local variable
208     * specified by <code>local_variable_table[i].name_index</code>.
209     *
210     * @param i         the i-th entry.
211     */
212    public String variableName(int i) {
213        return getConstPool().getUtf8Info(nameIndex(i));
214    }
215
216    /**
217     * Returns the value of
218     * <code>local_variable_table[i].descriptor_index</code>.
219     * This represents the type descriptor of the local variable.
220     * <p>
221     * If this attribute represents a LocalVariableTypeTable attribute,
222     * this method returns the value of
223     * <code>local_variable_type_table[i].signature_index</code>.
224     * It represents the type of the local variable.
225     *
226     * @param i         the i-th entry.
227     */
228    public int descriptorIndex(int i) {
229        return ByteArray.readU16bit(info, i * 10 + 8);
230    }
231
232    /**
233     * This method is equivalent to <code>descriptorIndex()</code>.
234     * If this attribute represents a LocalVariableTypeTable attribute,
235     * this method should be used instead of <code>descriptorIndex()</code>
236     * since the method name is more appropriate.
237     *
238     * @param i         the i-th entry.
239     * @see #descriptorIndex(int)
240     * @see SignatureAttribute#toFieldSignature(String)
241     */
242    public int signatureIndex(int i) {
243        return descriptorIndex(i);
244    }
245
246    /**
247     * Returns the type descriptor of the local variable
248     * specified by <code>local_variable_table[i].descriptor_index</code>.
249     * <p>
250     * If this attribute represents a LocalVariableTypeTable attribute,
251     * this method returns the type signature of the local variable
252     * specified by <code>local_variable_type_table[i].signature_index</code>.
253      *
254     * @param i         the i-th entry.
255     */
256    public String descriptor(int i) {
257        return getConstPool().getUtf8Info(descriptorIndex(i));
258    }
259
260    /**
261     * This method is equivalent to <code>descriptor()</code>.
262     * If this attribute represents a LocalVariableTypeTable attribute,
263     * this method should be used instead of <code>descriptor()</code>
264     * since the method name is more appropriate.
265     *
266     * <p>To parse the string, call <code>toFieldSignature(String)</code>
267     * in <code>SignatureAttribute</code>.
268     *
269     * @param i         the i-th entry.
270     * @see #descriptor(int)
271     * @see SignatureAttribute#toFieldSignature(String)
272     */
273    public String signature(int i) {
274        return descriptor(i);
275    }
276
277    /**
278     * Returns <code>local_variable_table[i].index</code>.
279     * This represents the index of the local variable.
280     *
281     * @param i         the i-th entry.
282     */
283    public int index(int i) {
284        return ByteArray.readU16bit(info, i * 10 + 10);
285    }
286
287    /**
288     * Makes a copy.
289     *
290     * @param newCp     the constant pool table used by the new copy.
291     * @param classnames        should be null.
292     */
293    public AttributeInfo copy(ConstPool newCp, Map classnames) {
294        byte[] src = get();
295        byte[] dest = new byte[src.length];
296        ConstPool cp = getConstPool();
297        LocalVariableAttribute attr = makeThisAttr(newCp, dest);
298        int n = ByteArray.readU16bit(src, 0);
299        ByteArray.write16bit(n, dest, 0);
300        int j = 2;
301        for (int i = 0; i < n; ++i) {
302            int start = ByteArray.readU16bit(src, j);
303            int len = ByteArray.readU16bit(src, j + 2);
304            int name = ByteArray.readU16bit(src, j + 4);
305            int type = ByteArray.readU16bit(src, j + 6);
306            int index = ByteArray.readU16bit(src, j + 8);
307
308            ByteArray.write16bit(start, dest, j);
309            ByteArray.write16bit(len, dest, j + 2);
310            if (name != 0)
311                name = cp.copy(name, newCp, null);
312
313            ByteArray.write16bit(name, dest, j + 4);
314
315            if (type != 0)  {
316                String sig = cp.getUtf8Info(type);
317                sig = Descriptor.rename(sig, classnames);
318                type = newCp.addUtf8Info(sig);
319            }
320
321            ByteArray.write16bit(type, dest, j + 6);
322            ByteArray.write16bit(index, dest, j + 8);
323            j += 10;
324        }
325
326        return attr;
327    }
328
329    // LocalVariableTypeAttribute overrides this method.
330    LocalVariableAttribute makeThisAttr(ConstPool cp, byte[] dest) {
331        return new LocalVariableAttribute(cp, tag, dest);
332    }
333}
334