| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.dexgen.rop; |
| |
| import com.android.dexgen.rop.cst.CstType; |
| import com.android.dexgen.rop.type.StdTypeList; |
| import com.android.dexgen.rop.type.TypeList; |
| import com.android.dexgen.util.FixedSizeList; |
| import com.android.dexgen.util.IntList; |
| |
| /** |
| * List of catch entries, that is, the elements of an "exception table," |
| * which is part of a standard {@code Code} attribute. |
| */ |
| public final class ByteCatchList extends FixedSizeList { |
| /** {@code non-null;} convenient zero-entry instance */ |
| public static final ByteCatchList EMPTY = new ByteCatchList(0); |
| |
| /** |
| * Constructs an instance. |
| * |
| * @param count the number of elements to be in the table |
| */ |
| public ByteCatchList(int count) { |
| super(count); |
| } |
| |
| /** |
| * Gets the total length of this structure in bytes, when included in |
| * a {@code Code} attribute. The returned value includes the |
| * two bytes for {@code exception_table_length}. |
| * |
| * @return {@code >= 2;} the total length, in bytes |
| */ |
| public int byteLength() { |
| return 2 + size() * 8; |
| } |
| |
| /** |
| * Gets the indicated item. |
| * |
| * @param n {@code >= 0;} which item |
| * @return {@code null-ok;} the indicated item |
| */ |
| public Item get(int n) { |
| return (Item) get0(n); |
| } |
| |
| /** |
| * Sets the item at the given index. |
| * |
| * @param n {@code >= 0, < size();} which entry to set |
| * @param item {@code non-null;} the item |
| */ |
| public void set(int n, Item item) { |
| if (item == null) { |
| throw new NullPointerException("item == null"); |
| } |
| |
| set0(n, item); |
| } |
| |
| /** |
| * Sets the item at the given index. |
| * |
| * @param n {@code >= 0, < size();} which entry to set |
| * @param startPc {@code >= 0;} the start pc (inclusive) of the handler's range |
| * @param endPc {@code >= startPc;} the end pc (exclusive) of the |
| * handler's range |
| * @param handlerPc {@code >= 0;} the pc of the exception handler |
| * @param exceptionClass {@code null-ok;} the exception class or |
| * {@code null} to catch all exceptions with this handler |
| */ |
| public void set(int n, int startPc, int endPc, int handlerPc, |
| CstType exceptionClass) { |
| set0(n, new Item(startPc, endPc, handlerPc, exceptionClass)); |
| } |
| |
| /** |
| * Gets the list of items active at the given address. The result is |
| * automatically made immutable. |
| * |
| * @param pc which address |
| * @return {@code non-null;} list of exception handlers active at |
| * {@code pc} |
| */ |
| public ByteCatchList listFor(int pc) { |
| int sz = size(); |
| Item[] resultArr = new Item[sz]; |
| int resultSz = 0; |
| |
| for (int i = 0; i < sz; i++) { |
| Item one = get(i); |
| if (one.covers(pc) && typeNotFound(one, resultArr, resultSz)) { |
| resultArr[resultSz] = one; |
| resultSz++; |
| } |
| } |
| |
| if (resultSz == 0) { |
| return EMPTY; |
| } |
| |
| ByteCatchList result = new ByteCatchList(resultSz); |
| for (int i = 0; i < resultSz; i++) { |
| result.set(i, resultArr[i]); |
| } |
| |
| result.setImmutable(); |
| return result; |
| } |
| |
| /** |
| * Helper method for {@link #listFor}, which tells whether a match |
| * is <i>not</i> found for the exception type of the given item in |
| * the given array. A match is considered to be either an exact type |
| * match or the class {@code Object} which represents a catch-all. |
| * |
| * @param item {@code non-null;} item with the exception type to look for |
| * @param arr {@code non-null;} array to search in |
| * @param count {@code non-null;} maximum number of elements in the array to check |
| * @return {@code true} iff the exception type is <i>not</i> found |
| */ |
| private static boolean typeNotFound(Item item, Item[] arr, int count) { |
| CstType type = item.getExceptionClass(); |
| |
| for (int i = 0; i < count; i++) { |
| CstType one = arr[i].getExceptionClass(); |
| if ((one == type) || (one == CstType.OBJECT)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Returns a target list corresponding to this instance. The result |
| * is a list of all the exception handler addresses, with the given |
| * {@code noException} address appended if appropriate. The |
| * result is automatically made immutable. |
| * |
| * @param noException {@code >= -1;} the no-exception address to append, or |
| * {@code -1} not to append anything |
| * @return {@code non-null;} list of exception targets, with |
| * {@code noException} appended if necessary |
| */ |
| public IntList toTargetList(int noException) { |
| if (noException < -1) { |
| throw new IllegalArgumentException("noException < -1"); |
| } |
| |
| boolean hasDefault = (noException >= 0); |
| int sz = size(); |
| |
| if (sz == 0) { |
| if (hasDefault) { |
| /* |
| * The list is empty, but there is a no-exception |
| * address; so, the result is just that address. |
| */ |
| return IntList.makeImmutable(noException); |
| } |
| /* |
| * The list is empty and there isn't even a no-exception |
| * address. |
| */ |
| return IntList.EMPTY; |
| } |
| |
| IntList result = new IntList(sz + (hasDefault ? 1 : 0)); |
| |
| for (int i = 0; i < sz; i++) { |
| result.add(get(i).getHandlerPc()); |
| } |
| |
| if (hasDefault) { |
| result.add(noException); |
| } |
| |
| result.setImmutable(); |
| return result; |
| } |
| |
| /** |
| * Returns a rop-style catches list equivalent to this one. |
| * |
| * @return {@code non-null;} the converted instance |
| */ |
| public TypeList toRopCatchList() { |
| int sz = size(); |
| if (sz == 0) { |
| return StdTypeList.EMPTY; |
| } |
| |
| StdTypeList result = new StdTypeList(sz); |
| |
| for (int i = 0; i < sz; i++) { |
| result.set(i, get(i).getExceptionClass().getClassType()); |
| } |
| |
| result.setImmutable(); |
| return result; |
| } |
| |
| /** |
| * Item in an exception handler list. |
| */ |
| public static class Item { |
| /** {@code >= 0;} the start pc (inclusive) of the handler's range */ |
| private final int startPc; |
| |
| /** {@code >= startPc;} the end pc (exclusive) of the handler's range */ |
| private final int endPc; |
| |
| /** {@code >= 0;} the pc of the exception handler */ |
| private final int handlerPc; |
| |
| /** {@code null-ok;} the exception class or {@code null} to catch all |
| * exceptions with this handler */ |
| private final CstType exceptionClass; |
| |
| /** |
| * Constructs an instance. |
| * |
| * @param startPc {@code >= 0;} the start pc (inclusive) of the |
| * handler's range |
| * @param endPc {@code >= startPc;} the end pc (exclusive) of the |
| * handler's range |
| * @param handlerPc {@code >= 0;} the pc of the exception handler |
| * @param exceptionClass {@code null-ok;} the exception class or |
| * {@code null} to catch all exceptions with this handler |
| */ |
| public Item(int startPc, int endPc, int handlerPc, |
| CstType exceptionClass) { |
| if (startPc < 0) { |
| throw new IllegalArgumentException("startPc < 0"); |
| } |
| |
| if (endPc < startPc) { |
| throw new IllegalArgumentException("endPc < startPc"); |
| } |
| |
| if (handlerPc < 0) { |
| throw new IllegalArgumentException("handlerPc < 0"); |
| } |
| |
| this.startPc = startPc; |
| this.endPc = endPc; |
| this.handlerPc = handlerPc; |
| this.exceptionClass = exceptionClass; |
| } |
| |
| /** |
| * Gets the start pc (inclusive) of the handler's range. |
| * |
| * @return {@code >= 0;} the start pc (inclusive) of the handler's range. |
| */ |
| public int getStartPc() { |
| return startPc; |
| } |
| |
| /** |
| * Gets the end pc (exclusive) of the handler's range. |
| * |
| * @return {@code >= startPc;} the end pc (exclusive) of the |
| * handler's range. |
| */ |
| public int getEndPc() { |
| return endPc; |
| } |
| |
| /** |
| * Gets the pc of the exception handler. |
| * |
| * @return {@code >= 0;} the pc of the exception handler |
| */ |
| public int getHandlerPc() { |
| return handlerPc; |
| } |
| |
| /** |
| * Gets the class of exception handled. |
| * |
| * @return {@code non-null;} the exception class; {@link CstType#OBJECT} |
| * if this entry handles all possible exceptions |
| */ |
| public CstType getExceptionClass() { |
| return (exceptionClass != null) ? |
| exceptionClass : CstType.OBJECT; |
| } |
| |
| /** |
| * Returns whether the given address is in the range of this item. |
| * |
| * @param pc the address |
| * @return {@code true} iff this item covers {@code pc} |
| */ |
| public boolean covers(int pc) { |
| return (pc >= startPc) && (pc < endPc); |
| } |
| } |
| } |