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