Home | History | Annotate | Download | only in code
      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      */
     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      */
     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      */
     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
    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
    117     public LocalsArray copy() {
    118         return new LocalsArraySet(this);
    119     }
    120 
    121     /** @inheritDoc */
    122     @Override
    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*/
    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
    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
    188     public int getMaxLocals() {
    189         return primary.getMaxLocals();
    190     }
    191 
    192     /** @inheritDoc */
    193     @Override
    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
    208     public void set(RegisterSpec spec) {
    209         set(spec.getReg(), spec);
    210     }
    211 
    212     /** @inheritDoc */
    213     @Override
    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
    228     public TypeBearer getOrNull(int idx) {
    229         return primary.getOrNull(idx);
    230     }
    231 
    232     /** @inheritDoc */
    233     @Override
    234     public TypeBearer get(int idx) {
    235         return primary.get(idx);
    236     }
    237 
    238     /** @inheritDoc */
    239     @Override
    240     public TypeBearer getCategory1(int idx) {
    241         return primary.getCategory1(idx);
    242     }
    243 
    244     /** @inheritDoc */
    245     @Override
    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      */
    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      */
    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
    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      */
    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
    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      */
    450     public LocalsArray subArrayForLabel(int subLabel) {
    451         LocalsArray result = getSecondaryForLabel(subLabel);
    452         return result;
    453     }
    454 
    455     /**{@inheritDoc}*/
    456     @Override
    457     protected OneLocalsArray getPrimary() {
    458         return primary;
    459     }
    460 }
    461