LocalVariableList.java revision 99409883d9c4c0ffb49b070ce307bb33a9dfe9f1
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.cf.code;
18
19import com.android.dx.rop.cst.CstUtf8;
20import com.android.dx.rop.type.Type;
21import com.android.dx.rop.code.LocalItem;
22import com.android.dx.util.FixedSizeList;
23
24/**
25 * List of "local variable" entries, which are the contents of
26 * {@code LocalVariableTable} and {@code LocalVariableTypeTable}
27 * attributes, as well as combinations of the two.
28 */
29public final class LocalVariableList extends FixedSizeList {
30    /** {@code non-null;} zero-size instance */
31    public static final LocalVariableList EMPTY = new LocalVariableList(0);
32
33    /**
34     * Returns an instance which is the concatenation of the two given
35     * instances. The result is immutable.
36     *
37     * @param list1 {@code non-null;} first instance
38     * @param list2 {@code non-null;} second instance
39     * @return {@code non-null;} combined instance
40     */
41    public static LocalVariableList concat(LocalVariableList list1,
42                                           LocalVariableList list2) {
43        if (list1 == EMPTY) {
44            // easy case
45            return list2;
46        }
47
48        int sz1 = list1.size();
49        int sz2 = list2.size();
50        LocalVariableList result = new LocalVariableList(sz1 + sz2);
51
52        for (int i = 0; i < sz1; i++) {
53            result.set(i, list1.get(i));
54        }
55
56        for (int i = 0; i < sz2; i++) {
57            result.set(sz1 + i, list2.get(i));
58        }
59
60        result.setImmutable();
61        return result;
62    }
63
64    /**
65     * Returns an instance which is the result of merging the two
66     * given instances, where one instance should have only type
67     * descriptors and the other only type signatures. The merged
68     * result is identical to the one with descriptors, except that
69     * any element whose {name, index, start, length} matches an
70     * element in the signature list gets augmented with the
71     * corresponding signature. The result is immutable.
72     *
73     * @param descriptorList {@code non-null;} list with descriptors
74     * @param signatureList {@code non-null;} list with signatures
75     * @return {@code non-null;} the merged result
76     */
77    public static LocalVariableList mergeDescriptorsAndSignatures(
78            LocalVariableList descriptorList,
79            LocalVariableList signatureList) {
80        int signatureSize = signatureList.size();
81        int descriptorSize = descriptorList.size();
82        LocalVariableList result = new LocalVariableList(descriptorSize);
83
84        for (int i = 0; i < descriptorSize; i++) {
85            Item item = descriptorList.get(i);
86            Item signatureItem = signatureList.itemToLocal(item);
87            if (signatureItem != null) {
88                CstUtf8 signature = signatureItem.getSignature();
89                item = item.withSignature(signature);
90            }
91            result.set(i, item);
92        }
93
94        result.setImmutable();
95        return result;
96    }
97
98    /**
99     * Constructs an instance.
100     *
101     * @param count the number of elements to be in the list
102     */
103    public LocalVariableList(int count) {
104        super(count);
105    }
106
107    /**
108     * Gets the indicated item.
109     *
110     * @param n {@code >= 0;} which item
111     * @return {@code null-ok;} the indicated item
112     */
113    public Item get(int n) {
114        return (Item) get0(n);
115    }
116
117    /**
118     * Sets the item at the given index.
119     *
120     * @param n {@code >= 0, < size();} which element
121     * @param item {@code non-null;} the item
122     */
123    public void set(int n, Item item) {
124        if (item == null) {
125            throw new NullPointerException("item == null");
126        }
127
128        set0(n, item);
129    }
130
131    /**
132     * Sets the item at the given index.
133     *
134     * <p><b>Note:</b> At least one of {@code descriptor} or
135     * {@code signature} must be passed as non-null.</p>
136     *
137     * @param n {@code >= 0, < size();} which element
138     * @param startPc {@code >= 0;} the start pc of this variable's scope
139     * @param length {@code >= 0;} the length (in bytecodes) of this variable's
140     * scope
141     * @param name {@code non-null;} the variable's name
142     * @param descriptor {@code null-ok;} the variable's type descriptor
143     * @param signature {@code null-ok;} the variable's type signature
144     * @param index {@code >= 0;} the variable's local index
145     */
146    public void set(int n, int startPc, int length, CstUtf8 name,
147            CstUtf8 descriptor, CstUtf8 signature, int index) {
148        set0(n, new Item(startPc, length, name, descriptor, signature, index));
149    }
150
151    /**
152     * Gets the local variable information in this instance which matches
153     * the given {@link com.android.dx.cf.code.LocalVariableList.Item}
154     * in all respects but the type descriptor and signature, if any.
155     *
156     * @param item {@code non-null;} local variable information to match
157     * @return {@code null-ok;} the corresponding local variable information stored
158     * in this instance, or {@code null} if there is no matching
159     * information
160     */
161    public Item itemToLocal(Item item) {
162        int sz = size();
163
164        for (int i = 0; i < sz; i++) {
165            Item one = (Item) get0(i);
166
167            if ((one != null) && one.matchesAllButType(item)) {
168                return one;
169            }
170        }
171
172        return null;
173    }
174
175    /**
176     * Gets the local variable information associated with a given address
177     * and local index, if any. <b>Note:</b> In standard classfiles, a
178     * variable's start point is listed as the address of the instruction
179     * <i>just past</i> the one that sets the variable.
180     *
181     * @param pc {@code >= 0;} the address to look up
182     * @param index {@code >= 0;} the local variable index
183     * @return {@code null-ok;} the associated local variable information, or
184     * {@code null} if none is known
185     */
186    public Item pcAndIndexToLocal(int pc, int index) {
187        int sz = size();
188
189        for (int i = 0; i < sz; i++) {
190            Item one = (Item) get0(i);
191
192            if ((one != null) && one.matchesPcAndIndex(pc, index)) {
193                return one;
194            }
195        }
196
197        return null;
198    }
199
200    /**
201     * Item in a local variable table.
202     */
203    public static class Item {
204        /** {@code >= 0;} the start pc of this variable's scope */
205        private final int startPc;
206
207        /** {@code >= 0;} the length (in bytecodes) of this variable's scope */
208        private final int length;
209
210        /** {@code non-null;} the variable's name */
211        private final CstUtf8 name;
212
213        /** {@code null-ok;} the variable's type descriptor */
214        private final CstUtf8 descriptor;
215
216        /** {@code null-ok;} the variable's type signature */
217        private final CstUtf8 signature;
218
219        /** {@code >= 0;} the variable's local index */
220        private final int index;
221
222        /**
223         * Constructs an instance.
224         *
225         * <p><b>Note:</b> At least one of {@code descriptor} or
226         * {@code signature} must be passed as non-null.</p>
227         *
228         * @param startPc {@code >= 0;} the start pc of this variable's scope
229         * @param length {@code >= 0;} the length (in bytecodes) of this variable's
230         * scope
231         * @param name {@code non-null;} the variable's name
232         * @param descriptor {@code null-ok;} the variable's type descriptor
233         * @param signature {@code null-ok;} the variable's type signature
234         * @param index {@code >= 0;} the variable's local index
235         */
236        public Item(int startPc, int length, CstUtf8 name,
237                CstUtf8 descriptor, CstUtf8 signature, int index) {
238            if (startPc < 0) {
239                throw new IllegalArgumentException("startPc < 0");
240            }
241
242            if (length < 0) {
243                throw new IllegalArgumentException("length < 0");
244            }
245
246            if (name == null) {
247                throw new NullPointerException("name == null");
248            }
249
250            if ((descriptor == null) && (signature == null)) {
251                throw new NullPointerException(
252                        "(descriptor == null) && (signature == null)");
253            }
254
255            if (index < 0) {
256                throw new IllegalArgumentException("index < 0");
257            }
258
259            this.startPc = startPc;
260            this.length = length;
261            this.name = name;
262            this.descriptor = descriptor;
263            this.signature = signature;
264            this.index = index;
265        }
266
267        /**
268         * Gets the start pc of this variable's scope.
269         *
270         * @return {@code >= 0;} the start pc of this variable's scope
271         */
272        public int getStartPc() {
273            return startPc;
274        }
275
276        /**
277         * Gets the length (in bytecodes) of this variable's scope.
278         *
279         * @return {@code >= 0;} the length (in bytecodes) of this variable's scope
280         */
281        public int getLength() {
282            return length;
283        }
284
285        /**
286         * Gets the variable's type descriptor.
287         *
288         * @return {@code null-ok;} the variable's type descriptor
289         */
290        public CstUtf8 getDescriptor() {
291            return descriptor;
292        }
293
294        /**
295         * Gets the variable's LocalItem, a (name, signature) tuple
296         *
297         * @return {@code null-ok;} the variable's type descriptor
298         */
299        public LocalItem getLocalItem() {
300            return LocalItem.make(name, signature);
301        }
302
303        /**
304         * Gets the variable's type signature. Private because if you need this,
305         * you want getLocalItem() instead.
306         *
307         * @return {@code null-ok;} the variable's type signature
308         */
309        private CstUtf8 getSignature() {
310            return signature;
311        }
312
313        /**
314         * Gets the variable's local index.
315         *
316         * @return {@code >= 0;} the variable's local index
317         */
318        public int getIndex() {
319            return index;
320        }
321
322        /**
323         * Gets the variable's type descriptor. This is a convenient shorthand
324         * for {@code Type.intern(getDescriptor().getString())}.
325         *
326         * @return {@code non-null;} the variable's type
327         */
328        public Type getType() {
329            return Type.intern(descriptor.getString());
330        }
331
332        /**
333         * Constructs and returns an instance which is identical to this
334         * one, except that the signature is changed to the given value.
335         *
336         * @param newSignature {@code non-null;} the new signature
337         * @return {@code non-null;} an appropriately-constructed instance
338         */
339        public Item withSignature(CstUtf8 newSignature) {
340            return new Item(startPc, length, name, descriptor, newSignature,
341                    index);
342        }
343
344        /**
345         * Gets whether this instance matches (describes) the given
346         * address and index.
347         *
348         * @param pc {@code >= 0;} the address in question
349         * @param index {@code >= 0;} the local variable index in question
350         * @return {@code true} iff this instance matches {@code pc}
351         * and {@code index}
352         */
353        public boolean matchesPcAndIndex(int pc, int index) {
354            return (index == this.index) &&
355                (pc >= startPc) &&
356                (pc < (startPc + length));
357        }
358
359        /**
360         * Gets whether this instance matches (describes) the given
361         * other instance exactly in all fields except type descriptor and
362         * type signature.
363         *
364         * @param other {@code non-null;} the instance to compare to
365         * @return {@code true} iff this instance matches
366         */
367        public boolean matchesAllButType(Item other) {
368            return (startPc == other.startPc)
369                && (length == other.length)
370                && (index == other.index)
371                && name.equals(other.name);
372        }
373    }
374}
375