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.code;
18
19import com.android.dexgen.rop.type.Type;
20import com.android.dexgen.rop.type.TypeList;
21import com.android.dexgen.util.FixedSizeList;
22
23/**
24 * List of {@link RegisterSpec} instances.
25 */
26public final class RegisterSpecList
27        extends FixedSizeList implements TypeList {
28    /** {@code non-null;} no-element instance */
29    public static final RegisterSpecList EMPTY = new RegisterSpecList(0);
30
31    /**
32     * Makes a single-element instance.
33     *
34     * @param spec {@code non-null;} the element
35     * @return {@code non-null;} an appropriately-constructed instance
36     */
37    public static RegisterSpecList make(RegisterSpec spec) {
38        RegisterSpecList result = new RegisterSpecList(1);
39        result.set(0, spec);
40        return result;
41    }
42
43    /**
44     * Makes a two-element instance.
45     *
46     * @param spec0 {@code non-null;} the first element
47     * @param spec1 {@code non-null;} the second element
48     * @return {@code non-null;} an appropriately-constructed instance
49     */
50    public static RegisterSpecList make(RegisterSpec spec0,
51                                        RegisterSpec spec1) {
52        RegisterSpecList result = new RegisterSpecList(2);
53        result.set(0, spec0);
54        result.set(1, spec1);
55        return result;
56    }
57
58    /**
59     * Makes a three-element instance.
60     *
61     * @param spec0 {@code non-null;} the first element
62     * @param spec1 {@code non-null;} the second element
63     * @param spec2 {@code non-null;} the third element
64     * @return {@code non-null;} an appropriately-constructed instance
65     */
66    public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1,
67                                        RegisterSpec spec2) {
68        RegisterSpecList result = new RegisterSpecList(3);
69        result.set(0, spec0);
70        result.set(1, spec1);
71        result.set(2, spec2);
72        return result;
73    }
74
75    /**
76     * Makes a four-element instance.
77     *
78     * @param spec0 {@code non-null;} the first element
79     * @param spec1 {@code non-null;} the second element
80     * @param spec2 {@code non-null;} the third element
81     * @param spec3 {@code non-null;} the fourth element
82     * @return {@code non-null;} an appropriately-constructed instance
83     */
84    public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1,
85                                        RegisterSpec spec2,
86                                        RegisterSpec spec3) {
87        RegisterSpecList result = new RegisterSpecList(4);
88        result.set(0, spec0);
89        result.set(1, spec1);
90        result.set(2, spec2);
91        result.set(3, spec3);
92        return result;
93    }
94
95    /**
96     * Constructs an instance. All indices initially contain {@code null}.
97     *
98     * @param size the size of the list
99     */
100    public RegisterSpecList(int size) {
101        super(size);
102    }
103
104    /** {@inheritDoc} */
105    public Type getType(int n) {
106        return get(n).getType().getType();
107    }
108
109    /** {@inheritDoc} */
110    public int getWordCount() {
111        int sz = size();
112        int result = 0;
113
114        for (int i = 0; i < sz; i++) {
115            result += getType(i).getCategory();
116        }
117
118        return result;
119    }
120
121    /** {@inheritDoc} */
122    public TypeList withAddedType(Type type) {
123        throw new UnsupportedOperationException("unsupported");
124    }
125
126    /**
127     * Gets the indicated element. It is an error to call this with the
128     * index for an element which was never set; if you do that, this
129     * will throw {@code NullPointerException}.
130     *
131     * @param n {@code >= 0, < size();} which element
132     * @return {@code non-null;} the indicated element
133     */
134    public RegisterSpec get(int n) {
135        return (RegisterSpec) get0(n);
136    }
137
138    /**
139     * Returns a RegisterSpec in this list that uses the specified register,
140     * or null if there is none in this list.
141     * @param reg Register to find
142     * @return RegisterSpec that uses argument or null.
143     */
144    public RegisterSpec specForRegister(int reg) {
145        int sz = size();
146        for (int i = 0; i < sz; i++) {
147            RegisterSpec rs;
148
149            rs = get(i);
150
151            if (rs.getReg() == reg) {
152                return rs;
153            }
154        }
155
156        return null;
157    }
158
159    /**
160     * Returns the index of a RegisterSpec in this list that uses the specified
161     * register, or -1 if none in this list uses the register.
162     * @param reg Register to find
163     * @return index of RegisterSpec or -1
164     */
165    public int indexOfRegister(int reg) {
166        int sz = size();
167        for (int i = 0; i < sz; i++) {
168            RegisterSpec rs;
169
170            rs = get(i);
171
172            if (rs.getReg() == reg) {
173                return i;
174            }
175        }
176
177        return -1;
178    }
179
180    /**
181     * Sets the element at the given index.
182     *
183     * @param n {@code >= 0, < size();} which element
184     * @param spec {@code non-null;} the value to store
185     */
186    public void set(int n, RegisterSpec spec) {
187        set0(n, spec);
188    }
189
190    /**
191     * Gets the minimum required register count implied by this
192     * instance. This is equal to the highest register number referred
193     * to plus the widest width (largest category) of the type used in
194     * that register.
195     *
196     * @return {@code >= 0;} the required registers size
197     */
198    public int getRegistersSize() {
199        int sz = size();
200        int result = 0;
201
202        for (int i = 0; i < sz; i++) {
203            RegisterSpec spec = (RegisterSpec) get0(i);
204            if (spec != null) {
205                int min = spec.getNextReg();
206                if (min > result) {
207                    result = min;
208                }
209            }
210        }
211
212        return result;
213    }
214
215    /**
216     * Returns a new instance, which is the same as this instance,
217     * except that it has an additional element prepended to the original.
218     * Mutability of the result is inherited from the original.
219     *
220     * @param spec {@code non-null;} the new first spec (to prepend)
221     * @return {@code non-null;} an appropriately-constructed instance
222     */
223    public RegisterSpecList withFirst(RegisterSpec spec) {
224        int sz = size();
225        RegisterSpecList result = new RegisterSpecList(sz + 1);
226
227        for (int i = 0; i < sz; i++) {
228            result.set0(i + 1, get0(i));
229        }
230
231        result.set0(0, spec);
232        if (isImmutable()) {
233            result.setImmutable();
234        }
235
236        return result;
237    }
238
239    /**
240     * Returns a new instance, which is the same as this instance,
241     * except that its first element is removed. Mutability of the
242     * result is inherited from the original.
243     *
244     * @return {@code non-null;} an appropriately-constructed instance
245     */
246    public RegisterSpecList withoutFirst() {
247        int newSize = size() - 1;
248
249        if (newSize == 0) {
250            return EMPTY;
251        }
252
253        RegisterSpecList result = new RegisterSpecList(newSize);
254
255        for (int i = 0; i < newSize; i++) {
256            result.set0(i, get0(i + 1));
257        }
258
259        if (isImmutable()) {
260            result.setImmutable();
261        }
262
263        return result;
264    }
265
266    /**
267     * Returns a new instance, which is the same as this instance,
268     * except that its last element is removed. Mutability of the
269     * result is inherited from the original.
270     *
271     * @return {@code non-null;} an appropriately-constructed instance
272     */
273    public RegisterSpecList withoutLast() {
274        int newSize = size() - 1;
275
276        if (newSize == 0) {
277            return EMPTY;
278        }
279
280        RegisterSpecList result = new RegisterSpecList(newSize);
281
282        for (int i = 0; i < newSize; i++) {
283            result.set0(i, get0(i));
284        }
285
286        if (isImmutable()) {
287            result.setImmutable();
288        }
289
290        return result;
291    }
292
293    /**
294     * Returns an instance that is identical to this one, except that
295     * all register numbers are offset by the given amount. Mutability
296     * of the result is inherited from the original.
297     *
298     * @param delta the amount to offset the register numbers by
299     * @return {@code non-null;} an appropriately-constructed instance
300     */
301    public RegisterSpecList withOffset(int delta) {
302        int sz = size();
303
304        if (sz == 0) {
305            // Don't bother making a new zero-element instance.
306            return this;
307        }
308
309        RegisterSpecList result = new RegisterSpecList(sz);
310
311        for (int i = 0; i < sz; i++) {
312            RegisterSpec one = (RegisterSpec) get0(i);
313            if (one != null) {
314                result.set0(i, one.withOffset(delta));
315            }
316        }
317
318        if (isImmutable()) {
319            result.setImmutable();
320        }
321
322        return result;
323    }
324
325    /**
326     * Returns an instance that is identical to this one, except that
327     * all register numbers are renumbered sequentially from the given
328     * base, with the first number duplicated if indicated.
329     *
330     * @param base the base register number
331     * @param duplicateFirst whether to duplicate the first number
332     * @return {@code non-null;} an appropriately-constructed instance
333     */
334    public RegisterSpecList withSequentialRegisters(int base,
335                                                    boolean duplicateFirst) {
336        int sz = size();
337
338        if (sz == 0) {
339            // Don't bother making a new zero-element instance.
340            return this;
341        }
342
343        RegisterSpecList result = new RegisterSpecList(sz);
344
345        for (int i = 0; i < sz; i++) {
346            RegisterSpec one = (RegisterSpec) get0(i);
347            result.set0(i, one.withReg(base));
348            if (duplicateFirst) {
349                duplicateFirst = false;
350            } else {
351                base += one.getCategory();
352            }
353        }
354
355        if (isImmutable()) {
356            result.setImmutable();
357        }
358
359        return result;
360    }
361
362}
363