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.dexgen.rop;
18
19import com.android.dexgen.rop.cst.CstType;
20import com.android.dexgen.rop.type.StdTypeList;
21import com.android.dexgen.rop.type.TypeList;
22import com.android.dexgen.util.FixedSizeList;
23import 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 */
29public 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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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         */
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         */
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         */
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         */
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         */
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         */
313        public boolean covers(int pc) {
314            return (pc >= startPc) && (pc < endPc);
315        }
316    }
317}
318