• 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.cf.code;
18 
19 import com.android.dex.util.ExceptionWithContext;
20 import com.android.dx.rop.code.RegisterSpec;
21 import com.android.dx.rop.type.Type;
22 import com.android.dx.rop.type.TypeBearer;
23 import com.android.dx.util.Hex;
24 import java.util.ArrayList;
25 
26 /**
27  * Representation of a set of local variable arrays, with Java semantics.
28  * This peculiar case is to support in-method subroutines, which can
29  * have different locals sets for each caller.
30  *
31  * <p><b>Note:</b> For the most part, the documentation for this class
32  * ignores the distinction between {@link com.android.dx.rop.type.Type} and {@link
33  * com.android.dx.rop.type.TypeBearer}.</p>
34  */
35 public class LocalsArraySet extends LocalsArray {
36 
37     /**
38      * The primary LocalsArray represents the locals as seen from
39      * the subroutine itself, which is the merged representation of all the
40      * individual locals states.
41      */
42     private final OneLocalsArray primary;
43 
44     /**
45      * Indexed by label of caller block: the locals specific to each caller's
46      * invocation of the subroutine.
47      */
48     private final ArrayList<LocalsArray> secondaries;
49 
50     /**
51      * Constructs an instance. The locals array initially consists of
52      * all-uninitialized values (represented as {@code null}s).
53      *
54      * @param maxLocals {@code >= 0;} the maximum number of locals this instance
55      * can refer to
56      */
LocalsArraySet(int maxLocals)57     public LocalsArraySet(int maxLocals) {
58         super(maxLocals != 0);
59         primary = new OneLocalsArray(maxLocals);
60         secondaries = new ArrayList();
61     }
62 
63     /**
64      * Constructs an instance with the specified primary and secondaries set.
65      *
66      * @param primary {@code non-null;} primary locals to use
67      * @param secondaries {@code non-null;} secondaries set, indexed by subroutine
68      * caller label.
69      */
LocalsArraySet(OneLocalsArray primary, ArrayList<LocalsArray> secondaries)70     public LocalsArraySet(OneLocalsArray primary,
71             ArrayList<LocalsArray> secondaries) {
72         super(primary.getMaxLocals() > 0);
73 
74         this.primary = primary;
75         this.secondaries = secondaries;
76     }
77 
78     /**
79      * Constructs an instance which is a copy of another.
80      *
81      * @param toCopy {@code non-null;} instance to copy.
82      */
LocalsArraySet(LocalsArraySet toCopy)83     private LocalsArraySet(LocalsArraySet toCopy) {
84         super(toCopy.getMaxLocals() > 0);
85 
86         primary = toCopy.primary.copy();
87         secondaries = new ArrayList(toCopy.secondaries.size());
88 
89         int sz = toCopy.secondaries.size();
90         for (int i = 0; i < sz; i++) {
91             LocalsArray la = toCopy.secondaries.get(i);
92 
93             if (la == null) {
94                 secondaries.add(null);
95             } else {
96                 secondaries.add(la.copy());
97             }
98         }
99     }
100 
101 
102     /** @inheritDoc */
103     @Override
setImmutable()104     public void setImmutable() {
105         primary.setImmutable();
106 
107         for (LocalsArray la : secondaries) {
108             if (la != null) {
109                 la.setImmutable();
110             }
111         }
112         super.setImmutable();
113     }
114 
115     /** @inheritDoc */
116     @Override
copy()117     public LocalsArray copy() {
118         return new LocalsArraySet(this);
119     }
120 
121     /** @inheritDoc */
122     @Override
annotate(ExceptionWithContext ex)123     public void annotate(ExceptionWithContext ex) {
124         ex.addContext("(locals array set; primary)");
125         primary.annotate(ex);
126 
127         int sz = secondaries.size();
128         for (int label = 0; label < sz; label++) {
129             LocalsArray la = secondaries.get(label);
130 
131             if (la != null) {
132                 ex.addContext("(locals array set: primary for caller "
133                         + Hex.u2(label) + ')');
134 
135                 la.getPrimary().annotate(ex);
136             }
137         }
138     }
139 
140     /** {@inheritDoc*/
toHuman()141     public String toHuman() {
142         StringBuilder sb = new StringBuilder();
143 
144         sb.append("(locals array set; primary)\n");
145 
146         sb.append(getPrimary().toHuman());
147         sb.append('\n');
148 
149         int sz = secondaries.size();
150         for (int label = 0; label < sz; label++) {
151             LocalsArray la = secondaries.get(label);
152 
153             if (la != null) {
154                 sb.append("(locals array set: primary for caller "
155                         + Hex.u2(label) + ")\n");
156 
157                 sb.append(la.getPrimary().toHuman());
158                 sb.append('\n');
159             }
160         }
161 
162         return sb.toString();
163     }
164 
165     /** @inheritDoc */
166     @Override
makeInitialized(Type type)167     public void makeInitialized(Type type) {
168         int len = primary.getMaxLocals();
169 
170         if (len == 0) {
171             // We have to check for this before checking for immutability.
172             return;
173         }
174 
175         throwIfImmutable();
176 
177         primary.makeInitialized(type);
178 
179         for (LocalsArray la : secondaries) {
180             if (la != null) {
181                 la.makeInitialized(type);
182             }
183         }
184     }
185 
186     /** @inheritDoc */
187     @Override
getMaxLocals()188     public int getMaxLocals() {
189         return primary.getMaxLocals();
190     }
191 
192     /** @inheritDoc */
193     @Override
set(int idx, TypeBearer type)194     public void set(int idx, TypeBearer type) {
195         throwIfImmutable();
196 
197         primary.set(idx, type);
198 
199         for (LocalsArray la : secondaries) {
200             if (la != null) {
201                 la.set(idx, type);
202             }
203         }
204     }
205 
206     /** @inheritDoc */
207     @Override
set(RegisterSpec spec)208     public void set(RegisterSpec spec) {
209         set(spec.getReg(), spec);
210     }
211 
212     /** @inheritDoc */
213     @Override
invalidate(int idx)214     public void invalidate(int idx) {
215         throwIfImmutable();
216 
217         primary.invalidate(idx);
218 
219         for (LocalsArray la : secondaries) {
220             if (la != null) {
221                 la.invalidate(idx);
222             }
223         }
224     }
225 
226     /** @inheritDoc */
227     @Override
getOrNull(int idx)228     public TypeBearer getOrNull(int idx) {
229         return primary.getOrNull(idx);
230     }
231 
232     /** @inheritDoc */
233     @Override
get(int idx)234     public TypeBearer get(int idx) {
235         return primary.get(idx);
236     }
237 
238     /** @inheritDoc */
239     @Override
getCategory1(int idx)240     public TypeBearer getCategory1(int idx) {
241         return primary.getCategory1(idx);
242     }
243 
244     /** @inheritDoc */
245     @Override
getCategory2(int idx)246     public TypeBearer getCategory2(int idx) {
247         return primary.getCategory2(idx);
248     }
249 
250     /**
251      * Merges this set with another {@code LocalsArraySet} instance.
252      *
253      * @param other {@code non-null;} to merge
254      * @return {@code non-null;} this instance if merge was a no-op, or
255      * new merged instance.
256      */
mergeWithSet(LocalsArraySet other)257     private LocalsArraySet mergeWithSet(LocalsArraySet other) {
258         OneLocalsArray newPrimary;
259         ArrayList<LocalsArray> newSecondaries;
260         boolean secondariesChanged = false;
261 
262         newPrimary = primary.merge(other.getPrimary());
263 
264         int sz1 = secondaries.size();
265         int sz2 = other.secondaries.size();
266         int sz = Math.max(sz1, sz2);
267         newSecondaries = new ArrayList(sz);
268 
269         for (int i = 0; i < sz; i++) {
270             LocalsArray la1 = (i < sz1 ? secondaries.get(i) : null);
271             LocalsArray la2 = (i < sz2 ? other.secondaries.get(i) : null);
272             LocalsArray resultla = null;
273 
274             if (la1 == la2) {
275                 resultla = la1;
276             } else if (la1 == null) {
277                 resultla = la2;
278             } else if (la2 == null) {
279                 resultla = la1;
280             } else {
281                 try {
282                     resultla = la1.merge(la2);
283                 } catch (SimException ex) {
284                     ex.addContext(
285                             "Merging locals set for caller block " + Hex.u2(i));
286                 }
287             }
288 
289             secondariesChanged = secondariesChanged || (la1 != resultla);
290 
291             newSecondaries.add(resultla);
292         }
293 
294         if ((primary == newPrimary) && ! secondariesChanged ) {
295             return this;
296         }
297 
298         return new LocalsArraySet(newPrimary, newSecondaries);
299     }
300 
301     /**
302      * Merges this set with a {@code OneLocalsArray} instance.
303      *
304      * @param other {@code non-null;} to merge
305      * @return {@code non-null;} this instance if merge was a no-op, or
306      * new merged instance.
307      */
mergeWithOne(OneLocalsArray other)308     private LocalsArraySet mergeWithOne(OneLocalsArray other) {
309         OneLocalsArray newPrimary;
310         ArrayList<LocalsArray> newSecondaries;
311         boolean secondariesChanged = false;
312 
313         newPrimary = primary.merge(other.getPrimary());
314         newSecondaries = new ArrayList(secondaries.size());
315 
316         int sz = secondaries.size();
317         for (int i = 0; i < sz; i++) {
318             LocalsArray la = secondaries.get(i);
319             LocalsArray resultla = null;
320 
321             if (la != null) {
322                 try {
323                     resultla = la.merge(other);
324                 } catch (SimException ex) {
325                     ex.addContext("Merging one locals against caller block "
326                                     + Hex.u2(i));
327                 }
328             }
329 
330             secondariesChanged = secondariesChanged || (la != resultla);
331 
332             newSecondaries.add(resultla);
333         }
334 
335         if ((primary == newPrimary) && ! secondariesChanged ) {
336             return this;
337         }
338 
339         return new LocalsArraySet(newPrimary, newSecondaries);
340     }
341 
342     /** @inheritDoc */
343     @Override
merge(LocalsArray other)344     public LocalsArraySet merge(LocalsArray other) {
345         LocalsArraySet result;
346 
347         try {
348             if (other instanceof LocalsArraySet) {
349                 result = mergeWithSet((LocalsArraySet) other);
350             } else {
351                 result = mergeWithOne((OneLocalsArray) other);
352             }
353         } catch (SimException ex) {
354             ex.addContext("underlay locals:");
355             annotate(ex);
356             ex.addContext("overlay locals:");
357             other.annotate(ex);
358             throw ex;
359         }
360 
361         result.setImmutable();
362         return result;
363     }
364 
365     /**
366      * Gets the {@code LocalsArray} instance for a specified subroutine
367      * caller label, or null if label has no locals associated with it.
368      *
369      * @param label {@code >= 0;} subroutine caller label
370      * @return {@code null-ok;} locals if available.
371      */
getSecondaryForLabel(int label)372     private LocalsArray getSecondaryForLabel(int label) {
373         if (label >= secondaries.size()) {
374             return null;
375         }
376 
377         return secondaries.get(label);
378     }
379 
380     /** {@inheritDoc} */
381     @Override
mergeWithSubroutineCaller(LocalsArray other, int predLabel)382     public LocalsArraySet mergeWithSubroutineCaller
383             (LocalsArray other, int predLabel) {
384 
385         LocalsArray mine = getSecondaryForLabel(predLabel);
386         LocalsArray newSecondary;
387         OneLocalsArray newPrimary;
388 
389         newPrimary = primary.merge(other.getPrimary());
390 
391         if (mine == other) {
392             newSecondary = mine;
393         } else if (mine == null) {
394             newSecondary = other;
395         } else {
396             newSecondary = mine.merge(other);
397         }
398 
399         if ((newSecondary == mine) && (newPrimary == primary)) {
400             return this;
401         } else {
402             /*
403              * We're going to re-build a primary as a merge of all the
404              * secondaries.
405              */
406             newPrimary = null;
407 
408             int szSecondaries = secondaries.size();
409             int sz = Math.max(predLabel + 1, szSecondaries);
410             ArrayList<LocalsArray> newSecondaries = new ArrayList(sz);
411             for (int i = 0; i < sz; i++) {
412                 LocalsArray la = null;
413 
414                 if (i == predLabel) {
415                     /*
416                      * This LocalsArray always replaces any existing one,
417                      * since this is the result of a refined iteration.
418                      */
419                     la = newSecondary;
420                 } else if (i < szSecondaries) {
421                     la = secondaries.get(i);
422                 }
423 
424                 if (la != null) {
425                     if (newPrimary == null) {
426                         newPrimary = la.getPrimary();
427                     } else {
428                         newPrimary = newPrimary.merge(la.getPrimary());
429                     }
430                 }
431 
432                 newSecondaries.add(la);
433             }
434 
435             LocalsArraySet result
436                     = new LocalsArraySet(newPrimary, newSecondaries);
437             result.setImmutable();
438             return result;
439         }
440     }
441 
442     /**
443      * Returns a LocalsArray instance representing the locals state that should
444      * be used when returning to a subroutine caller.
445      *
446      * @param subLabel {@code >= 0;} A calling label of a subroutine
447      * @return {@code null-ok;} an instance for this subroutine, or null if subroutine
448      * is not in this set.
449      */
subArrayForLabel(int subLabel)450     public LocalsArray subArrayForLabel(int subLabel) {
451         LocalsArray result = getSecondaryForLabel(subLabel);
452         return result;
453     }
454 
455     /**{@inheritDoc}*/
456     @Override
getPrimary()457     protected OneLocalsArray getPrimary() {
458         return primary;
459     }
460 }
461