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 
17 package com.android.dx.cf.code;
18 
19 import com.android.dx.rop.code.LocalItem;
20 import com.android.dx.rop.cst.CstString;
21 import com.android.dx.rop.type.Type;
22 import 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  */
29 public 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      */
concat(LocalVariableList list1, LocalVariableList list2)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      */
mergeDescriptorsAndSignatures( LocalVariableList descriptorList, LocalVariableList signatureList)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      */
LocalVariableList(int count)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      */
get(int n)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      */
set(int n, Item item)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      */
set(int n, int startPc, int length, CstString name, CstString descriptor, CstString signature, int index)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      */
itemToLocal(Item item)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      */
pcAndIndexToLocal(int pc, int index)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          */
Item(int startPc, int length, CstString name, CstString descriptor, CstString signature, int index)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          */
getStartPc()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          */
getLength()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          */
getDescriptor()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          */
getLocalItem()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          */
getSignature()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          */
getIndex()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          */
getType()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          */
withSignature(CstString newSignature)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          */
matchesPcAndIndex(int pc, int index)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          */
matchesAllButType(Item other)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