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