/* * 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.dx.cf.code; import com.android.dx.rop.cst.CstType; import com.android.dx.rop.type.StdTypeList; import com.android.dx.rop.type.TypeList; import com.android.dx.util.FixedSizeList; import com.android.dx.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); } } }