LocalVariableList.java revision 333201833d506a3accdeac6ceb7caba8d4b95797
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.CstString;
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 descriptorSize = descriptorList.size();
81        LocalVariableList result = new LocalVariableList(descriptorSize);
82
83        for (int i = 0; i < descriptorSize; i++) {
84            Item item = descriptorList.get(i);
85            Item signatureItem = signatureList.itemToLocal(item);
86            if (signatureItem != null) {
87                CstString signature = signatureItem.getSignature();
88                item = item.withSignature(signature);
89            }
90            result.set(i, item);
91        }
92
93        result.setImmutable();
94        return result;
95    }
96
97    /**
98     * Constructs an instance.
99     *
100     * @param count the number of elements to be in the list
101     */
102    public LocalVariableList(int count) {
103        super(count);
104    }
105
106    /**
107     * Gets the indicated item.
108     *
109     * @param n {@code >= 0;} which item
110     * @return {@code null-ok;} the indicated item
111     */
112    public Item get(int n) {
113        return (Item) get0(n);
114    }
115
116    /**
117     * Sets the item at the given index.
118     *
119     * @param n {@code >= 0, < size();} which element
120     * @param item {@code non-null;} the item
121     */
122    public void set(int n, Item item) {
123        if (item == null) {
124            throw new NullPointerException("item == null");
125        }
126
127        set0(n, item);
128    }
129
130    /**
131     * Sets the item at the given index.
132     *
133     * <p><b>Note:</b> At least one of {@code descriptor} or
134     * {@code signature} must be passed as non-null.</p>
135     *
136     * @param n {@code >= 0, < size();} which element
137     * @param startPc {@code >= 0;} the start pc of this variable's scope
138     * @param length {@code >= 0;} the length (in bytecodes) of this variable's
139     * scope
140     * @param name {@code non-null;} the variable's name
141     * @param descriptor {@code null-ok;} the variable's type descriptor
142     * @param signature {@code null-ok;} the variable's type signature
143     * @param index {@code >= 0;} the variable's local index
144     */
145    public void set(int n, int startPc, int length, CstString name,
146            CstString descriptor, CstString signature, int index) {
147        set0(n, new Item(startPc, length, name, descriptor, signature, index));
148    }
149
150    /**
151     * Gets the local variable information in this instance which matches
152     * the given {@link com.android.dx.cf.code.LocalVariableList.Item}
153     * in all respects but the type descriptor and signature, if any.
154     *
155     * @param item {@code non-null;} local variable information to match
156     * @return {@code null-ok;} the corresponding local variable information stored
157     * in this instance, or {@code null} if there is no matching
158     * information
159     */
160    public Item itemToLocal(Item item) {
161        int sz = size();
162
163        for (int i = 0; i < sz; i++) {
164            Item one = (Item) get0(i);
165
166            if ((one != null) && one.matchesAllButType(item)) {
167                return one;
168            }
169        }
170
171        return null;
172    }
173
174    /**
175     * Gets the local variable information associated with a given address
176     * and local index, if any. <b>Note:</b> In standard classfiles, a
177     * variable's start point is listed as the address of the instruction
178     * <i>just past</i> the one that sets the variable.
179     *
180     * @param pc {@code >= 0;} the address to look up
181     * @param index {@code >= 0;} the local variable index
182     * @return {@code null-ok;} the associated local variable information, or
183     * {@code null} if none is known
184     */
185    public Item pcAndIndexToLocal(int pc, int index) {
186        int sz = size();
187
188        for (int i = 0; i < sz; i++) {
189            Item one = (Item) get0(i);
190
191            if ((one != null) && one.matchesPcAndIndex(pc, index)) {
192                return one;
193            }
194        }
195
196        return null;
197    }
198
199    /**
200     * Item in a local variable table.
201     */
202    public static class Item {
203        /** {@code >= 0;} the start pc of this variable's scope */
204        private final int startPc;
205
206        /** {@code >= 0;} the length (in bytecodes) of this variable's scope */
207        private final int length;
208
209        /** {@code non-null;} the variable's name */
210        private final CstString name;
211
212        /** {@code null-ok;} the variable's type descriptor */
213        private final CstString descriptor;
214
215        /** {@code null-ok;} the variable's type signature */
216        private final CstString signature;
217
218        /** {@code >= 0;} the variable's local index */
219        private final int index;
220
221        /**
222         * Constructs an instance.
223         *
224         * <p><b>Note:</b> At least one of {@code descriptor} or
225         * {@code signature} must be passed as non-null.</p>
226         *
227         * @param startPc {@code >= 0;} the start pc of this variable's scope
228         * @param length {@code >= 0;} the length (in bytecodes) of this variable's
229         * scope
230         * @param name {@code non-null;} the variable's name
231         * @param descriptor {@code null-ok;} the variable's type descriptor
232         * @param signature {@code null-ok;} the variable's type signature
233         * @param index {@code >= 0;} the variable's local index
234         */
235        public Item(int startPc, int length, CstString name,
236                CstString descriptor, CstString signature, int index) {
237            if (startPc < 0) {
238                throw new IllegalArgumentException("startPc < 0");
239            }
240
241            if (length < 0) {
242                throw new IllegalArgumentException("length < 0");
243            }
244
245            if (name == null) {
246                throw new NullPointerException("name == null");
247            }
248
249            if ((descriptor == null) && (signature == null)) {
250                throw new NullPointerException(
251                        "(descriptor == null) && (signature == null)");
252            }
253
254            if (index < 0) {
255                throw new IllegalArgumentException("index < 0");
256            }
257
258            this.startPc = startPc;
259            this.length = length;
260            this.name = name;
261            this.descriptor = descriptor;
262            this.signature = signature;
263            this.index = index;
264        }
265
266        /**
267         * Gets the start pc of this variable's scope.
268         *
269         * @return {@code >= 0;} the start pc of this variable's scope
270         */
271        public int getStartPc() {
272            return startPc;
273        }
274
275        /**
276         * Gets the length (in bytecodes) of this variable's scope.
277         *
278         * @return {@code >= 0;} the length (in bytecodes) of this variable's scope
279         */
280        public int getLength() {
281            return length;
282        }
283
284        /**
285         * Gets the variable's type descriptor.
286         *
287         * @return {@code null-ok;} the variable's type descriptor
288         */
289        public CstString getDescriptor() {
290            return descriptor;
291        }
292
293        /**
294         * Gets the variable's LocalItem, a (name, signature) tuple
295         *
296         * @return {@code null-ok;} the variable's type descriptor
297         */
298        public LocalItem getLocalItem() {
299            return LocalItem.make(name, signature);
300        }
301
302        /**
303         * Gets the variable's type signature. Private because if you need this,
304         * you want getLocalItem() instead.
305         *
306         * @return {@code null-ok;} the variable's type signature
307         */
308        private CstString getSignature() {
309            return signature;
310        }
311
312        /**
313         * Gets the variable's local index.
314         *
315         * @return {@code >= 0;} the variable's local index
316         */
317        public int getIndex() {
318            return index;
319        }
320
321        /**
322         * Gets the variable's type descriptor. This is a convenient shorthand
323         * for {@code Type.intern(getDescriptor().getString())}.
324         *
325         * @return {@code non-null;} the variable's type
326         */
327        public Type getType() {
328            return Type.intern(descriptor.getString());
329        }
330
331        /**
332         * Constructs and returns an instance which is identical to this
333         * one, except that the signature is changed to the given value.
334         *
335         * @param newSignature {@code non-null;} the new signature
336         * @return {@code non-null;} an appropriately-constructed instance
337         */
338        public Item withSignature(CstString newSignature) {
339            return new Item(startPc, length, name, descriptor, newSignature,
340                    index);
341        }
342
343        /**
344         * Gets whether this instance matches (describes) the given
345         * address and index.
346         *
347         * @param pc {@code >= 0;} the address in question
348         * @param index {@code >= 0;} the local variable index in question
349         * @return {@code true} iff this instance matches {@code pc}
350         * and {@code index}
351         */
352        public boolean matchesPcAndIndex(int pc, int index) {
353            return (index == this.index) &&
354                (pc >= startPc) &&
355                (pc < (startPc + length));
356        }
357
358        /**
359         * Gets whether this instance matches (describes) the given
360         * other instance exactly in all fields except type descriptor and
361         * type signature.
362         *
363         * @param other {@code non-null;} the instance to compare to
364         * @return {@code true} iff this instance matches
365         */
366        public boolean matchesAllButType(Item other) {
367            return (startPc == other.startPc)
368                && (length == other.length)
369                && (index == other.index)
370                && name.equals(other.name);
371        }
372    }
373}
374