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.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      */
     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         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      */
    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