1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dexgen.rop;
18
19import com.android.dexgen.util.FixedSizeList;
20
21/**
22 * List of "line number" entries, which are the contents of
23 * {@code LineNumberTable} attributes.
24 */
25public final class LineNumberList extends FixedSizeList {
26    /** {@code non-null;} zero-size instance */
27    public static final LineNumberList EMPTY = new LineNumberList(0);
28
29    /**
30     * Returns an instance which is the concatenation of the two given
31     * instances.
32     *
33     * @param list1 {@code non-null;} first instance
34     * @param list2 {@code non-null;} second instance
35     * @return {@code non-null;} combined instance
36     */
37    public static LineNumberList concat(LineNumberList list1,
38                                        LineNumberList list2) {
39        if (list1 == EMPTY) {
40            // easy case
41            return list2;
42        }
43
44        int sz1 = list1.size();
45        int sz2 = list2.size();
46        LineNumberList result = new LineNumberList(sz1 + sz2);
47
48        for (int i = 0; i < sz1; i++) {
49            result.set(i, list1.get(i));
50        }
51
52        for (int i = 0; i < sz2; i++) {
53            result.set(sz1 + i, list2.get(i));
54        }
55
56        return result;
57    }
58
59    /**
60     * Constructs an instance.
61     *
62     * @param count the number of elements to be in the list
63     */
64    public LineNumberList(int count) {
65        super(count);
66    }
67
68    /**
69     * Gets the indicated item.
70     *
71     * @param n {@code >= 0;} which item
72     * @return {@code null-ok;} the indicated item
73     */
74    public Item get(int n) {
75        return (Item) get0(n);
76    }
77
78    /**
79     * Sets the item at the given index.
80     *
81     * @param n {@code >= 0, < size();} which element
82     * @param item {@code non-null;} the item
83     */
84    public void set(int n, Item item) {
85        if (item == null) {
86            throw new NullPointerException("item == null");
87        }
88
89        set0(n, item);
90    }
91
92    /**
93     * Sets the item at the given index.
94     *
95     * @param n {@code >= 0, < size();} which element
96     * @param startPc {@code >= 0;} start pc of this item
97     * @param lineNumber {@code >= 0;} corresponding line number
98     */
99    public void set(int n, int startPc, int lineNumber) {
100        set0(n, new Item(startPc, lineNumber));
101    }
102
103    /**
104     * Gets the line number associated with the given address.
105     *
106     * @param pc {@code >= 0;} the address to look up
107     * @return {@code >= -1;} the associated line number, or {@code -1} if
108     * none is known
109     */
110    public int pcToLine(int pc) {
111        /*
112         * Line number entries don't have to appear in any particular
113         * order, so we have to do a linear search. TODO: If
114         * this turns out to be a bottleneck, consider sorting the
115         * list prior to use.
116         */
117        int sz = size();
118        int bestPc = -1;
119        int bestLine = -1;
120
121        for (int i = 0; i < sz; i++) {
122            Item one = get(i);
123            int onePc = one.getStartPc();
124            if ((onePc <= pc) && (onePc > bestPc)) {
125                bestPc = onePc;
126                bestLine = one.getLineNumber();
127                if (bestPc == pc) {
128                    // We can't do better than this
129                    break;
130                }
131            }
132        }
133
134        return bestLine;
135    }
136
137    /**
138     * Item in a line number table.
139     */
140    public static class Item {
141        /** {@code >= 0;} start pc of this item */
142        private final int startPc;
143
144        /** {@code >= 0;} corresponding line number */
145        private final int lineNumber;
146
147        /**
148         * Constructs an instance.
149         *
150         * @param startPc {@code >= 0;} start pc of this item
151         * @param lineNumber {@code >= 0;} corresponding line number
152         */
153        public Item(int startPc, int lineNumber) {
154            if (startPc < 0) {
155                throw new IllegalArgumentException("startPc < 0");
156            }
157
158            if (lineNumber < 0) {
159                throw new IllegalArgumentException("lineNumber < 0");
160            }
161
162            this.startPc = startPc;
163            this.lineNumber = lineNumber;
164        }
165
166        /**
167         * Gets the start pc of this item.
168         *
169         * @return the start pc
170         */
171        public int getStartPc() {
172            return startPc;
173        }
174
175        /**
176         * Gets the line number of this item.
177         *
178         * @return the line number
179         */
180        public int getLineNumber() {
181            return lineNumber;
182        }
183    }
184}
185