1579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson/*
2579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Copyright (C) 2008 The Android Open Source Project
3579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *
4579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
5579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * you may not use this file except in compliance with the License.
6579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * You may obtain a copy of the License at
7579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *
8579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
9579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *
10579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Unless required by applicable law or agreed to in writing, software
11579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
12579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * See the License for the specific language governing permissions and
14579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * limitations under the License.
15579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */
16579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
17579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonpackage com.android.dx.dex.code;
18579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
19579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.rop.code.BasicBlock;
20579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.rop.code.BasicBlockList;
21579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.rop.code.RopMethod;
22579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.rop.cst.CstType;
23579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.rop.type.Type;
24579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.rop.type.TypeList;
25579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.util.IntList;
26579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
27579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.ArrayList;
28579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.HashSet;
29579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
30579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson/**
31579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Constructor of {@link CatchTable} instances from {@link RopMethod}
32579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * and associated data.
33579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */
34579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonpublic final class StdCatchBuilder implements CatchBuilder {
35579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** the maximum range of a single catch handler, in code units */
36579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static final int MAX_CATCH_RANGE = 65535;
37579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
38579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** {@code non-null;} method to build the list for */
39579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private final RopMethod method;
40579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
41579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** {@code non-null;} block output order */
42579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private final int[] order;
43579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
44579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** {@code non-null;} address objects for each block */
45579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private final BlockAddresses addresses;
46579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
47579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
48579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Constructs an instance. It merely holds onto its parameters for
49579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * a subsequent call to {@link #build}.
50579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
51579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param method {@code non-null;} method to build the list for
52579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param order {@code non-null;} block output order
53579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param addresses {@code non-null;} address objects for each block
54579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
55579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public StdCatchBuilder(RopMethod method, int[] order,
56579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            BlockAddresses addresses) {
57579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (method == null) {
58579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new NullPointerException("method == null");
59579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
60579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
61579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (order == null) {
62579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new NullPointerException("order == null");
63579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
64579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
65579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (addresses == null) {
66579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new NullPointerException("addresses == null");
67579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
68579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
69579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        this.method = method;
70579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        this.order = order;
71579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        this.addresses = addresses;
72579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
73579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
74579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** {@inheritDoc} */
75579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public CatchTable build() {
76579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return build(method, order, addresses);
77579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
78579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
79579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** {@inheritDoc} */
80579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public boolean hasAnyCatches() {
81579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        BasicBlockList blocks = method.getBlocks();
82579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int size = blocks.size();
83579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
84579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (int i = 0; i < size; i++) {
85579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            BasicBlock block = blocks.get(i);
86579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            TypeList catches = block.getLastInsn().getCatches();
87579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (catches.size() != 0) {
88579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                return true;
89579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
90579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
91579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
92579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return false;
93579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
94579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
95579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** {@inheritDoc} */
96579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public HashSet<Type> getCatchTypes() {
97579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        HashSet<Type> result = new HashSet<Type>(20);
98579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        BasicBlockList blocks = method.getBlocks();
99579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int size = blocks.size();
100579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
101579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (int i = 0; i < size; i++) {
102579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            BasicBlock block = blocks.get(i);
103579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            TypeList catches = block.getLastInsn().getCatches();
104579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            int catchSize = catches.size();
105579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
106579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            for (int j = 0; j < catchSize; j++) {
107579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                result.add(catches.getType(j));
108579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
109579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
110579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
111579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return result;
112579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
113579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
114579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
115579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Builds and returns the catch table for a given method.
116579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
117579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param method {@code non-null;} method to build the list for
118579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param order {@code non-null;} block output order
119579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param addresses {@code non-null;} address objects for each block
120579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @return {@code non-null;} the constructed table
121579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
122579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public static CatchTable build(RopMethod method, int[] order,
123579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            BlockAddresses addresses) {
124579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int len = order.length;
125579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        BasicBlockList blocks = method.getBlocks();
126579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        ArrayList<CatchTable.Entry> resultList =
127579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            new ArrayList<CatchTable.Entry>(len);
128579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        CatchHandlerList currentHandlers = CatchHandlerList.EMPTY;
129579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        BasicBlock currentStartBlock = null;
130579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        BasicBlock currentEndBlock = null;
131579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
132579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (int i = 0; i < len; i++) {
133579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            BasicBlock block = blocks.labelToBlock(order[i]);
134579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
135579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (!block.canThrow()) {
136579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                /*
137579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                 * There is no need to concern ourselves with the
138579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                 * placement of blocks that can't throw with respect
139579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                 * to the blocks that *can* throw.
140579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                 */
141579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                continue;
142579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
143579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
144579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            CatchHandlerList handlers = handlersFor(block, addresses);
145579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
146579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (currentHandlers.size() == 0) {
147579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                // This is the start of a new catch range.
148579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                currentStartBlock = block;
149579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                currentEndBlock = block;
150579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                currentHandlers = handlers;
151579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                continue;
152579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
153579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
154579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (currentHandlers.equals(handlers)
155579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    && rangeIsValid(currentStartBlock, block, addresses)) {
156579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                /*
157579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                 * The block we are looking at now has the same handlers
158579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                 * as the block that started the currently open catch
159579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                 * range, and adding it to the currently open range won't
160579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                 * cause it to be too long.
161579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                 */
162579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                currentEndBlock = block;
163579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                continue;
164579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
165579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
166579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            /*
167579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * The block we are looking at now has incompatible handlers,
168579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * so we need to finish off the last entry and start a new
169579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * one. Note: We only emit an entry if it has associated handlers.
170579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             */
171579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (currentHandlers.size() != 0) {
172579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                CatchTable.Entry entry =
173579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    makeEntry(currentStartBlock, currentEndBlock,
174579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                            currentHandlers, addresses);
175579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                resultList.add(entry);
176579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
177579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
178579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            currentStartBlock = block;
179579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            currentEndBlock = block;
180579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            currentHandlers = handlers;
181579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
182579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
183579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (currentHandlers.size() != 0) {
184579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Emit an entry for the range that was left hanging.
185579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            CatchTable.Entry entry =
186579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                makeEntry(currentStartBlock, currentEndBlock,
187579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                        currentHandlers, addresses);
188579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            resultList.add(entry);
189579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
190579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
191579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        // Construct the final result.
192579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
193579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int resultSz = resultList.size();
194579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
195579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (resultSz == 0) {
196579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            return CatchTable.EMPTY;
197579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
198579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
199579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        CatchTable result = new CatchTable(resultSz);
200579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
201579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (int i = 0; i < resultSz; i++) {
202579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            result.set(i, resultList.get(i));
203579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
204579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
205579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        result.setImmutable();
206579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return result;
207579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
208579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
209579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
210579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Makes the {@link CatchHandlerList} for the given basic block.
211579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
212579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param block {@code non-null;} block to get entries for
213579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param addresses {@code non-null;} address objects for each block
214579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @return {@code non-null;} array of entries
215579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
216579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static CatchHandlerList handlersFor(BasicBlock block,
217579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            BlockAddresses addresses) {
218579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        IntList successors = block.getSuccessors();
219579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int succSize = successors.size();
220579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int primary = block.getPrimarySuccessor();
221579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        TypeList catches = block.getLastInsn().getCatches();
222579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int catchSize = catches.size();
223579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
224579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (catchSize == 0) {
225579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            return CatchHandlerList.EMPTY;
226579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
227579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
228579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (((primary == -1) && (succSize != catchSize))
229579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                || ((primary != -1) &&
230579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                        ((succSize != (catchSize + 1))
231579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                                || (primary != successors.get(catchSize))))) {
232579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            /*
233579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * Blocks that throw are supposed to list their primary
234579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * successor -- if any -- last in the successors list, but
235579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             * that constraint appears to be violated here.
236579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson             */
237579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new RuntimeException(
238579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    "shouldn't happen: weird successors list");
239579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
240579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
241579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        /*
242579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson         * Reduce the effective catchSize if we spot a catch-all that
243579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson         * isn't at the end.
244579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson         */
245579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (int i = 0; i < catchSize; i++) {
246579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            Type type = catches.getType(i);
247579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (type.equals(Type.OBJECT)) {
248579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                catchSize = i + 1;
249579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                break;
250579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
251579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
252579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
253579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        CatchHandlerList result = new CatchHandlerList(catchSize);
254579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
255579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (int i = 0; i < catchSize; i++) {
256579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            CstType oneType = new CstType(catches.getType(i));
257579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            CodeAddress oneHandler = addresses.getStart(successors.get(i));
258579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            result.set(i, oneType, oneHandler.getAddress());
259579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
260579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
261579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        result.setImmutable();
262579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return result;
263579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
264579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
265579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
266579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Makes a {@link CatchTable#Entry} for the given block range and
267579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * handlers.
268579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
269579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param start {@code non-null;} the start block for the range (inclusive)
270579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param end {@code non-null;} the start block for the range (also inclusive)
271579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param handlers {@code non-null;} the handlers for the range
272579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param addresses {@code non-null;} address objects for each block
273579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
274579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static CatchTable.Entry makeEntry(BasicBlock start,
275579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            BasicBlock end, CatchHandlerList handlers,
276579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            BlockAddresses addresses) {
277579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        /*
278579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson         * We start at the *last* instruction of the start block, since
279579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson         * that's the instruction that can throw...
280579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson         */
281579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        CodeAddress startAddress = addresses.getLast(start);
282579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
283579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        // ...And we end *after* the last instruction of the end block.
284579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        CodeAddress endAddress = addresses.getEnd(end);
285579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
286579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return new CatchTable.Entry(startAddress.getAddress(),
287579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                endAddress.getAddress(), handlers);
288579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
289579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
290579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
291579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Gets whether the address range for the given two blocks is valid
292579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * for a catch handler. This is true as long as the covered range is
293579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * under 65536 code units.
294579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
295579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param start {@code non-null;} the start block for the range (inclusive)
296579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param end {@code non-null;} the start block for the range (also inclusive)
297579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param addresses {@code non-null;} address objects for each block
298579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @return {@code true} if the range is valid as a catch range
299579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
300579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static boolean rangeIsValid(BasicBlock start, BasicBlock end,
301579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            BlockAddresses addresses) {
302579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (start == null) {
303579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new NullPointerException("start == null");
304579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
305579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
306579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (end == null) {
307579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new NullPointerException("end == null");
308579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
309579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
310579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        // See above about selection of instructions.
311579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int startAddress = addresses.getLast(start).getAddress();
312579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int endAddress = addresses.getEnd(end).getAddress();
313579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
314579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return (endAddress - startAddress) <= MAX_CATCH_RANGE;
315579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
316579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson}
317