• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.dx.rop.code;
18 
19 import 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  */
25 public 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      */
RegisterSpecSet(int maxSize)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
equals(Object other)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
hashCode()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
toString()101     public String toString() {
102         int len = specs.length;
103         StringBuilder sb = new StringBuilder(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      */
getMaxSize()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      */
size()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      */
get(int reg)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      */
get(RegisterSpec spec)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      */
findMatchingLocal(RegisterSpec spec)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      */
localItemToSpec(LocalItem local)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      */
remove(RegisterSpec toRemove)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      */
put(RegisterSpec spec)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      */
putAll(RegisterSpecSet set)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      */
intersect(RegisterSpecSet other, boolean localPrimary)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      */
withOffset(int delta)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      */
mutableCopy()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