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.dexgen.rop;
18 
19 import com.android.dexgen.rop.cst.CstType;
20 import com.android.dexgen.rop.type.StdTypeList;
21 import com.android.dexgen.rop.type.TypeList;
22 import com.android.dexgen.util.FixedSizeList;
23 import com.android.dexgen.util.IntList;
24 
25 /**
26  * List of catch entries, that is, the elements of an "exception table,"
27  * which is part of a standard {@code Code} attribute.
28  */
29 public final class ByteCatchList extends FixedSizeList {
30     /** {@code non-null;} convenient zero-entry instance */
31     public static final ByteCatchList EMPTY = new ByteCatchList(0);
32 
33     /**
34      * Constructs an instance.
35      *
36      * @param count the number of elements to be in the table
37      */
ByteCatchList(int count)38     public ByteCatchList(int count) {
39         super(count);
40     }
41 
42     /**
43      * Gets the total length of this structure in bytes, when included in
44      * a {@code Code} attribute. The returned value includes the
45      * two bytes for {@code exception_table_length}.
46      *
47      * @return {@code >= 2;} the total length, in bytes
48      */
byteLength()49     public int byteLength() {
50         return 2 + size() * 8;
51     }
52 
53     /**
54      * Gets the indicated item.
55      *
56      * @param n {@code >= 0;} which item
57      * @return {@code null-ok;} the indicated item
58      */
get(int n)59     public Item get(int n) {
60         return (Item) get0(n);
61     }
62 
63     /**
64      * Sets the item at the given index.
65      *
66      * @param n {@code >= 0, < size();} which entry to set
67      * @param item {@code non-null;} the item
68      */
set(int n, Item item)69     public void set(int n, Item item) {
70         if (item == null) {
71             throw new NullPointerException("item == null");
72         }
73 
74         set0(n, item);
75     }
76 
77     /**
78      * Sets the item at the given index.
79      *
80      * @param n {@code >= 0, < size();} which entry to set
81      * @param startPc {@code >= 0;} the start pc (inclusive) of the handler's range
82      * @param endPc {@code >= startPc;} the end pc (exclusive) of the
83      * handler's range
84      * @param handlerPc {@code >= 0;} the pc of the exception handler
85      * @param exceptionClass {@code null-ok;} the exception class or
86      * {@code null} to catch all exceptions with this handler
87      */
set(int n, int startPc, int endPc, int handlerPc, CstType exceptionClass)88     public void set(int n, int startPc, int endPc, int handlerPc,
89             CstType exceptionClass) {
90         set0(n, new Item(startPc, endPc, handlerPc, exceptionClass));
91     }
92 
93     /**
94      * Gets the list of items active at the given address. The result is
95      * automatically made immutable.
96      *
97      * @param pc which address
98      * @return {@code non-null;} list of exception handlers active at
99      * {@code pc}
100      */
listFor(int pc)101     public ByteCatchList listFor(int pc) {
102         int sz = size();
103         Item[] resultArr = new Item[sz];
104         int resultSz = 0;
105 
106         for (int i = 0; i < sz; i++) {
107             Item one = get(i);
108             if (one.covers(pc) && typeNotFound(one, resultArr, resultSz)) {
109                 resultArr[resultSz] = one;
110                 resultSz++;
111             }
112         }
113 
114         if (resultSz == 0) {
115             return EMPTY;
116         }
117 
118         ByteCatchList result = new ByteCatchList(resultSz);
119         for (int i = 0; i < resultSz; i++) {
120             result.set(i, resultArr[i]);
121         }
122 
123         result.setImmutable();
124         return result;
125     }
126 
127     /**
128      * Helper method for {@link #listFor}, which tells whether a match
129      * is <i>not</i> found for the exception type of the given item in
130      * the given array. A match is considered to be either an exact type
131      * match or the class {@code Object} which represents a catch-all.
132      *
133      * @param item {@code non-null;} item with the exception type to look for
134      * @param arr {@code non-null;} array to search in
135      * @param count {@code non-null;} maximum number of elements in the array to check
136      * @return {@code true} iff the exception type is <i>not</i> found
137      */
typeNotFound(Item item, Item[] arr, int count)138     private static boolean typeNotFound(Item item, Item[] arr, int count) {
139         CstType type = item.getExceptionClass();
140 
141         for (int i = 0; i < count; i++) {
142             CstType one = arr[i].getExceptionClass();
143             if ((one == type) || (one == CstType.OBJECT)) {
144                 return false;
145             }
146         }
147 
148         return true;
149     }
150 
151     /**
152      * Returns a target list corresponding to this instance. The result
153      * is a list of all the exception handler addresses, with the given
154      * {@code noException} address appended if appropriate. The
155      * result is automatically made immutable.
156      *
157      * @param noException {@code >= -1;} the no-exception address to append, or
158      * {@code -1} not to append anything
159      * @return {@code non-null;} list of exception targets, with
160      * {@code noException} appended if necessary
161      */
toTargetList(int noException)162     public IntList toTargetList(int noException) {
163         if (noException < -1) {
164             throw new IllegalArgumentException("noException < -1");
165         }
166 
167         boolean hasDefault = (noException >= 0);
168         int sz = size();
169 
170         if (sz == 0) {
171             if (hasDefault) {
172                 /*
173                  * The list is empty, but there is a no-exception
174                  * address; so, the result is just that address.
175                  */
176                 return IntList.makeImmutable(noException);
177             }
178             /*
179              * The list is empty and there isn't even a no-exception
180              * address.
181              */
182             return IntList.EMPTY;
183         }
184 
185         IntList result = new IntList(sz + (hasDefault ? 1 : 0));
186 
187         for (int i = 0; i < sz; i++) {
188             result.add(get(i).getHandlerPc());
189         }
190 
191         if (hasDefault) {
192             result.add(noException);
193         }
194 
195         result.setImmutable();
196         return result;
197     }
198 
199     /**
200      * Returns a rop-style catches list equivalent to this one.
201      *
202      * @return {@code non-null;} the converted instance
203      */
toRopCatchList()204     public TypeList toRopCatchList() {
205         int sz = size();
206         if (sz == 0) {
207             return StdTypeList.EMPTY;
208         }
209 
210         StdTypeList result = new StdTypeList(sz);
211 
212         for (int i = 0; i < sz; i++) {
213             result.set(i, get(i).getExceptionClass().getClassType());
214         }
215 
216         result.setImmutable();
217         return result;
218     }
219 
220     /**
221      * Item in an exception handler list.
222      */
223     public static class Item {
224         /** {@code >= 0;} the start pc (inclusive) of the handler's range */
225         private final int startPc;
226 
227         /** {@code >= startPc;} the end pc (exclusive) of the handler's range */
228         private final int endPc;
229 
230         /** {@code >= 0;} the pc of the exception handler */
231         private final int handlerPc;
232 
233         /** {@code null-ok;} the exception class or {@code null} to catch all
234          * exceptions with this handler */
235         private final CstType exceptionClass;
236 
237         /**
238          * Constructs an instance.
239          *
240          * @param startPc {@code >= 0;} the start pc (inclusive) of the
241          * handler's range
242          * @param endPc {@code >= startPc;} the end pc (exclusive) of the
243          * handler's range
244          * @param handlerPc {@code >= 0;} the pc of the exception handler
245          * @param exceptionClass {@code null-ok;} the exception class or
246          * {@code null} to catch all exceptions with this handler
247          */
Item(int startPc, int endPc, int handlerPc, CstType exceptionClass)248         public Item(int startPc, int endPc, int handlerPc,
249                 CstType exceptionClass) {
250             if (startPc < 0) {
251                 throw new IllegalArgumentException("startPc < 0");
252             }
253 
254             if (endPc < startPc) {
255                 throw new IllegalArgumentException("endPc < startPc");
256             }
257 
258             if (handlerPc < 0) {
259                 throw new IllegalArgumentException("handlerPc < 0");
260             }
261 
262             this.startPc = startPc;
263             this.endPc = endPc;
264             this.handlerPc = handlerPc;
265             this.exceptionClass = exceptionClass;
266         }
267 
268         /**
269          * Gets the start pc (inclusive) of the handler's range.
270          *
271          * @return {@code >= 0;} the start pc (inclusive) of the handler's range.
272          */
getStartPc()273         public int getStartPc() {
274             return startPc;
275         }
276 
277         /**
278          * Gets the end pc (exclusive) of the handler's range.
279          *
280          * @return {@code >= startPc;} the end pc (exclusive) of the
281          * handler's range.
282          */
getEndPc()283         public int getEndPc() {
284             return endPc;
285         }
286 
287         /**
288          * Gets the pc of the exception handler.
289          *
290          * @return {@code >= 0;} the pc of the exception handler
291          */
getHandlerPc()292         public int getHandlerPc() {
293             return handlerPc;
294         }
295 
296         /**
297          * Gets the class of exception handled.
298          *
299          * @return {@code non-null;} the exception class; {@link CstType#OBJECT}
300          * if this entry handles all possible exceptions
301          */
getExceptionClass()302         public CstType getExceptionClass() {
303             return (exceptionClass != null) ?
304                 exceptionClass : CstType.OBJECT;
305         }
306 
307         /**
308          * Returns whether the given address is in the range of this item.
309          *
310          * @param pc the address
311          * @return {@code true} iff this item covers {@code pc}
312          */
covers(int pc)313         public boolean covers(int pc) {
314             return (pc >= startPc) && (pc < endPc);
315         }
316     }
317 }
318