Home | History | Annotate | Download | only in rop
      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      */
     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