blob: d2a485733a7b45ff5bf817475f4c293b81992942 [file] [log] [blame]
/*
* 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);
}
}
}