RegisterSpecSet.java revision 2ad60cfc28e14ee8f0bb038720836a4696c478ad
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.dx.rop.code;
18
19import com.android.dx.util.MutabilityControl;
20import com.android.dx.rop.cst.CstUtf8;
21
22/**
23 * Set of {@link RegisterSpec} instances, where a given register number
24 * may appear only once in the set.
25 */
26public final class RegisterSpecSet
27        extends MutabilityControl {
28    /** non-null; no-element instance */
29    public static final RegisterSpecSet EMPTY = new RegisterSpecSet(0);
30
31    /**
32     * non-null; array of register specs, where each element is
33     * <code>null</code> or is an instance whose <code>reg</code>
34     * matches the array index
35     */
36    private final RegisterSpec[] specs;
37
38    /** &gt;= -1; size of the set or <code>-1</code> if not yet calculated */
39    private int size;
40
41    /**
42     * Constructs an instance. The instance is initially empty.
43     *
44     * @param maxSize &gt;= 0; the maximum register number (exclusive) that
45     * may be represented in this instance
46     */
47    public RegisterSpecSet(int maxSize) {
48        super(maxSize != 0);
49
50        this.specs = new RegisterSpec[maxSize];
51        this.size = 0;
52    }
53
54    /** {@inheritDoc} */
55    @Override
56    public boolean equals(Object other) {
57        if (!(other instanceof RegisterSpecSet)) {
58            return false;
59        }
60
61        RegisterSpecSet otherSet = (RegisterSpecSet) other;
62        RegisterSpec[] otherSpecs = otherSet.specs;
63        int len = specs.length;
64
65        if ((len != otherSpecs.length) || (size() != otherSet.size())) {
66            return false;
67        }
68
69        for (int i = 0; i < len; i++) {
70            RegisterSpec s1 = specs[i];
71            RegisterSpec s2 = otherSpecs[i];
72
73            if (s1 == s2) {
74                continue;
75            }
76
77            if ((s1 == null) || !s1.equals(s2)) {
78                return false;
79            }
80        }
81
82        return true;
83    }
84
85    /** {@inheritDoc} */
86    @Override
87    public int hashCode() {
88        int len = specs.length;
89        int hash = 0;
90
91        for (int i = 0; i < len; i++) {
92            RegisterSpec spec = specs[i];
93            int oneHash = (spec == null) ? 0 : spec.hashCode();
94            hash = (hash * 31) + oneHash;
95        }
96
97        return hash;
98    }
99
100    /** {@inheritDoc} */
101    @Override
102    public String toString() {
103        int len = specs.length;
104        StringBuffer sb = new StringBuffer(len * 25);
105
106        sb.append('{');
107
108        boolean any = false;
109        for (int i = 0; i < len; i++) {
110            RegisterSpec spec = specs[i];
111            if (spec != null) {
112                if (any) {
113                    sb.append(", ");
114                } else {
115                    any = true;
116                }
117                sb.append(spec);
118            }
119        }
120
121        sb.append('}');
122        return sb.toString();
123    }
124
125    /**
126     * Gets the maximum number of registers that may be in this instance, which
127     * is also the maximum-plus-one of register numbers that may be
128     * represented.
129     *
130     * @return &gt;= 0; the maximum size
131     */
132    public int getMaxSize() {
133        return specs.length;
134    }
135
136    /**
137     * Gets the current size of this instance.
138     *
139     * @return &gt;= 0; the size
140     */
141    public int size() {
142        int result = size;
143
144        if (result < 0) {
145            int len = specs.length;
146
147            result = 0;
148            for (int i = 0; i < len; i++) {
149                if (specs[i] != null) {
150                    result++;
151                }
152            }
153
154            size = result;
155        }
156
157        return result;
158    }
159
160    /**
161     * Gets the element with the given register number, if any.
162     *
163     * @param reg &gt;= 0; the desired register number
164     * @return null-ok; the element with the given register number or
165     * <code>null</code> if there is none
166     */
167    public RegisterSpec get(int reg) {
168        try {
169            return specs[reg];
170        } catch (ArrayIndexOutOfBoundsException ex) {
171            // Translate the exception.
172            throw new IllegalArgumentException("bogus reg");
173        }
174    }
175
176    /**
177     * Gets the element with the same register number as the given
178     * spec, if any. This is just a convenient shorthand for
179     * <code>get(spec.getReg())</code>.
180     *
181     * @param spec non-null; spec with the desired register number
182     * @return null-ok; the element with the matching register number or
183     * <code>null</code> if there is none
184     */
185    public RegisterSpec get(RegisterSpec spec) {
186        return get(spec.getReg());
187    }
188
189    /**
190     * Returns the spec in this set that's currently associated with a given
191     * name, or null if there is none.
192     *
193     * @param local non-null; local item to search for
194     * @return null-ok; first register found with name.
195     */
196    public RegisterSpec localItemToSpec(LocalItem local) {
197        for (int reg = 0; reg < specs.length; reg++) {
198            if (specs[reg] != null && local.equals(specs[reg].getLocalItem())) {
199                return specs[reg];
200            }
201        }
202
203        return null;
204    }
205
206    /**
207     * Removes a spec from the set. Only the register number
208     * of the parameter is significant.
209     *
210     * @param toRemove non-null; register to remove.
211     */
212    public void remove(RegisterSpec toRemove) {
213        try {
214            specs[toRemove.getReg()] = null;
215            size = -1;
216        } catch (ArrayIndexOutOfBoundsException ex) {
217            // Translate the exception.
218            throw new IllegalArgumentException("bogus reg");
219        }
220    }
221
222    /**
223     * Puts the given spec into the set. If there is already an element in
224     * the set with the same register number, it is replaced. Additionally,
225     * if the previous element is for a category-2 register, then that
226     * previous element is nullified. Finally, if the given spec is for
227     * a category-2 register, then the immediately subsequent element
228     * is nullified.
229     *
230     * @param spec non-null; the register spec to put in the instance
231     */
232    public void put(RegisterSpec spec) {
233        throwIfImmutable();
234
235        if (spec == null) {
236            throw new NullPointerException("spec == null");
237        }
238
239        size = -1;
240
241        try {
242            int reg = spec.getReg();
243            specs[reg] = spec;
244
245            if (reg > 0) {
246                int prevReg = reg - 1;
247                RegisterSpec prevSpec = specs[prevReg];
248                if ((prevSpec != null) && (prevSpec.getCategory() == 2)) {
249                    specs[prevReg] = null;
250                }
251            }
252
253            if (spec.getCategory() == 2) {
254                specs[reg + 1] = null;
255            }
256        } catch (ArrayIndexOutOfBoundsException ex) {
257            // Translate the exception.
258            throw new IllegalArgumentException("spec.getReg() out of range");
259        }
260    }
261
262    /**
263     * Intersects this instance with the given one, modifying this
264     * instance. The intersection consists of the pairwise
265     * {@link RegisterSpec#intersect} of corresponding elements from
266     * this instance and the given one where both are non-null.
267     *
268     * @param other non-null; set to intersect with
269     * @param localPrimary whether local variables are primary to
270     * the intersection; if <code>true</code>, then the only non-null
271     * result elements occur when registers being intersected have
272     * equal names (or both have <code>null</code> names)
273     */
274    public void intersect(RegisterSpecSet other, boolean localPrimary) {
275        throwIfImmutable();
276
277        RegisterSpec[] otherSpecs = other.specs;
278        int thisLen = specs.length;
279        int len = Math.min(thisLen, otherSpecs.length);
280
281        size = -1;
282
283        for (int i = 0; i < len; i++) {
284            RegisterSpec spec = specs[i];
285
286            if (spec == null) {
287                continue;
288            }
289
290            RegisterSpec intersection =
291                spec.intersect(otherSpecs[i], localPrimary);
292            if (intersection != spec) {
293                specs[i] = intersection;
294            }
295        }
296
297        for (int i = len; i < thisLen; i++) {
298            specs[i] = null;
299        }
300    }
301
302    /**
303     * Returns an instance that is identical to this one, except that
304     * all register numbers are offset by the given amount. Mutability
305     * of the result is inherited from the original.
306     *
307     * @param delta the amount to offset the register numbers by
308     * @return non-null; an appropriately-constructed instance
309     */
310    public RegisterSpecSet withOffset(int delta) {
311        int len = specs.length;
312        RegisterSpecSet result = new RegisterSpecSet(len + delta);
313
314        for (int i = 0; i < len; i++) {
315            RegisterSpec spec = specs[i];
316            if (spec != null) {
317                result.put(spec.withOffset(delta));
318            }
319        }
320
321        result.size = size;
322
323        if (isImmutable()) {
324            result.setImmutable();
325        }
326
327        return result;
328    }
329
330    /**
331     * Makes and return a mutable copy of this instance.
332     *
333     * @return non-null; the mutable copy
334     */
335    public RegisterSpecSet mutableCopy() {
336        int len = specs.length;
337        RegisterSpecSet copy = new RegisterSpecSet(len);
338
339        for (int i = 0; i < len; i++) {
340            RegisterSpec spec = specs[i];
341            if (spec != null) {
342                copy.put(spec);
343            }
344        }
345
346        copy.size = size;
347
348        return copy;
349    }
350}
351