FileDocCategorySizeDatePackage
StdCatchBuilder.javaAPI DocAndroid 1.5 API10967Wed May 06 22:41:02 BST 2009com.android.dx.dex.code

StdCatchBuilder

public final class StdCatchBuilder extends Object implements CatchBuilder
Constructor of {@link CatchTable} instances from {@link RopMethod} and associated data.

Fields Summary
private static final int
MAX_CATCH_RANGE
the maximum range of a single catch handler, in code units
private final com.android.dx.rop.code.RopMethod
method
non-null; method to build the list for
private final int[]
order
non-null; block output order
private final BlockAddresses
addresses
non-null; address objects for each block
Constructors Summary
public StdCatchBuilder(com.android.dx.rop.code.RopMethod method, int[] order, BlockAddresses addresses)
Constructs an instance. It merely holds onto its parameters for a subsequent call to {@link #build}.

param
method non-null; method to build the list for
param
order non-null; block output order
param
addresses non-null; address objects for each block

    
                                                 
        
              
        if (method == null) {
            throw new NullPointerException("method == null");
        }

        if (order == null) {
            throw new NullPointerException("order == null");
        }

        if (addresses == null) {
            throw new NullPointerException("addresses == null");
        }

        this.method = method;
        this.order = order;
        this.addresses = addresses;
    
Methods Summary
public CatchTablebuild()
{@inheritDoc}

        return build(method, order, addresses);
    
public static CatchTablebuild(com.android.dx.rop.code.RopMethod method, int[] order, BlockAddresses addresses)
Builds and returns the catch table for a given method.

param
method non-null; method to build the list for
param
order non-null; block output order
param
addresses non-null; address objects for each block
return
non-null; the constructed table

        int len = order.length;
        BasicBlockList blocks = method.getBlocks();
        ArrayList<CatchTable.Entry> resultList =
            new ArrayList<CatchTable.Entry>(len);
        CatchHandlerList currentHandlers = CatchHandlerList.EMPTY;
        BasicBlock currentStartBlock = null;
        BasicBlock currentEndBlock = null;
        
        for (int i = 0; i < len; i++) {
            BasicBlock block = blocks.labelToBlock(order[i]);

            if (!block.canThrow()) {
                /*
                 * There is no need to concern ourselves with the
                 * placement of blocks that can't throw with respect
                 * to the blocks that *can* throw.
                 */
                continue;
            }

            CatchHandlerList handlers = handlersFor(block, addresses);

            if (currentHandlers.size() == 0) {
                // This is the start of a new catch range.
                currentStartBlock = block;
                currentEndBlock = block;
                currentHandlers = handlers;
                continue;
            }

            if (currentHandlers.equals(handlers)
                    && rangeIsValid(currentStartBlock, block, addresses)) {
                /*
                 * The block we are looking at now has the same handlers
                 * as the block that started the currently open catch
                 * range, and adding it to the currently open range won't
                 * cause it to be too long.
                 */
                currentEndBlock = block;
                continue;
            }

            /*
             * The block we are looking at now has incompatible handlers,
             * so we need to finish off the last entry and start a new
             * one. Note: We only emit an entry if it has associated handlers.
             */
            if (currentHandlers.size() != 0) {
                CatchTable.Entry entry =
                    makeEntry(currentStartBlock, currentEndBlock,
                            currentHandlers, addresses);
                resultList.add(entry);
            }

            currentStartBlock = block;
            currentEndBlock = block;
            currentHandlers = handlers;
        }

        if (currentHandlers.size() != 0) {
            // Emit an entry for the range that was left hanging.
            CatchTable.Entry entry =
                makeEntry(currentStartBlock, currentEndBlock,
                        currentHandlers, addresses);
            resultList.add(entry);
        }
        
        // Construct the final result.

        int resultSz = resultList.size();
        
        if (resultSz == 0) {
            return CatchTable.EMPTY;
        }

        CatchTable result = new CatchTable(resultSz);

        for (int i = 0; i < resultSz; i++) {
            result.set(i, resultList.get(i));
        }

        result.setImmutable();
        return result;
    
public java.util.HashSetgetCatchTypes()
{@inheritDoc}

        HashSet<Type> result = new HashSet<Type>(20);
        BasicBlockList blocks = method.getBlocks();
        int size = blocks.size();
        
        for (int i = 0; i < size; i++) {
            BasicBlock block = blocks.get(i);
            TypeList catches = block.getLastInsn().getCatches();
            int catchSize = catches.size();

            for (int j = 0; j < catchSize; j++) {
                result.add(catches.getType(j));
            }
        }

        return result;
    
private static CatchHandlerListhandlersFor(com.android.dx.rop.code.BasicBlock block, BlockAddresses addresses)
Makes the {@link CatchHandlerList} for the given basic block.

param
block non-null; block to get entries for
param
addresses non-null; address objects for each block
return
non-null; array of entries

        IntList successors = block.getSuccessors();
        int succSize = successors.size();
        int primary = block.getPrimarySuccessor();
        TypeList catches = block.getLastInsn().getCatches();
        int catchSize = catches.size();

        if (catchSize == 0) {
            return CatchHandlerList.EMPTY;
        }

        if (((primary == -1) && (succSize != catchSize))
                || ((primary != -1) &&
                        ((succSize != (catchSize + 1))
                                || (primary != successors.get(catchSize))))) {
            /*
             * Blocks that throw are supposed to list their primary
             * successor -- if any -- last in the successors list, but
             * that constraint appears to be violated here.
             */
            throw new RuntimeException(
                    "shouldn't happen: weird successors list");
        }

        /*
         * Reduce the effective catchSize if we spot a catch-all that
         * isn't at the end.
         */
        for (int i = 0; i < catchSize; i++) {
            Type type = catches.getType(i);
            if (type.equals(Type.OBJECT)) {
                catchSize = i + 1;
                break;
            }
        }
        
        CatchHandlerList result = new CatchHandlerList(catchSize);

        for (int i = 0; i < catchSize; i++) {
            CstType oneType = new CstType(catches.getType(i));
            CodeAddress oneHandler = addresses.getStart(successors.get(i));
            result.set(i, oneType, oneHandler.getAddress());
        }

        result.setImmutable();
        return result;
    
public booleanhasAnyCatches()
{@inheritDoc}

        HashSet<Type> result = new HashSet<Type>(20);
        BasicBlockList blocks = method.getBlocks();
        int size = blocks.size();
        
        for (int i = 0; i < size; i++) {
            BasicBlock block = blocks.get(i);
            TypeList catches = block.getLastInsn().getCatches();
            if (catches.size() != 0) {
                return true;
            }
        }

        return false;
    
private static CatchTable.EntrymakeEntry(com.android.dx.rop.code.BasicBlock start, com.android.dx.rop.code.BasicBlock end, CatchHandlerList handlers, BlockAddresses addresses)
Makes a {@link CatchTable#Entry} for the given block range and handlers.

param
start non-null; the start block for the range (inclusive)
param
end non-null; the start block for the range (also inclusive)
param
handlers non-null; the handlers for the range
param
addresses non-null; address objects for each block

        /*
         * We start at the *last* instruction of the start block, since
         * that's the instruction that can throw...
         */
        CodeAddress startAddress = addresses.getLast(start);

        // ...And we end *after* the last instruction of the end block.
        CodeAddress endAddress = addresses.getEnd(end);

        return new CatchTable.Entry(startAddress.getAddress(),
                endAddress.getAddress(), handlers);
    
private static booleanrangeIsValid(com.android.dx.rop.code.BasicBlock start, com.android.dx.rop.code.BasicBlock end, BlockAddresses addresses)
Gets whether the address range for the given two blocks is valid for a catch handler. This is true as long as the covered range is under 65536 code units.

param
start non-null; the start block for the range (inclusive)
param
end non-null; the start block for the range (also inclusive)
param
addresses non-null; address objects for each block
return
true if the range is valid as a catch range

        if (start == null) {
            throw new NullPointerException("start == null");
        }

        if (end == null) {
            throw new NullPointerException("end == null");
        }
        
        // See above about selection of instructions.
        int startAddress = addresses.getLast(start).getAddress();
        int endAddress = addresses.getEnd(end).getAddress();

        return (endAddress - startAddress) <= MAX_CATCH_RANGE;