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.dx.rop.cst.CstUtf8;
     20 import com.android.dx.rop.type.Type;
     21 import com.android.dx.rop.code.LocalItem;
     22 import com.android.dx.util.FixedSizeList;
     23 
     24 /**
     25  * List of "local variable" entries, which are the contents of
     26  * {@code LocalVariableTable} and {@code LocalVariableTypeTable}
     27  * attributes, as well as combinations of the two.
     28  */
     29 public final class LocalVariableList extends FixedSizeList {
     30     /** {@code non-null;} zero-size instance */
     31     public static final LocalVariableList EMPTY = new LocalVariableList(0);
     32 
     33     /**
     34      * Returns an instance which is the concatenation of the two given
     35      * instances. The result is immutable.
     36      *
     37      * @param list1 {@code non-null;} first instance
     38      * @param list2 {@code non-null;} second instance
     39      * @return {@code non-null;} combined instance
     40      */
     41     public static LocalVariableList concat(LocalVariableList list1,
     42                                            LocalVariableList list2) {
     43         if (list1 == EMPTY) {
     44             // easy case
     45             return list2;
     46         }
     47 
     48         int sz1 = list1.size();
     49         int sz2 = list2.size();
     50         LocalVariableList result = new LocalVariableList(sz1 + sz2);
     51 
     52         for (int i = 0; i < sz1; i++) {
     53             result.set(i, list1.get(i));
     54         }
     55 
     56         for (int i = 0; i < sz2; i++) {
     57             result.set(sz1 + i, list2.get(i));
     58         }
     59 
     60         result.setImmutable();
     61         return result;
     62     }
     63 
     64     /**
     65      * Returns an instance which is the result of merging the two
     66      * given instances, where one instance should have only type
     67      * descriptors and the other only type signatures. The merged
     68      * result is identical to the one with descriptors, except that
     69      * any element whose {name, index, start, length} matches an
     70      * element in the signature list gets augmented with the
     71      * corresponding signature. The result is immutable.
     72      *
     73      * @param descriptorList {@code non-null;} list with descriptors
     74      * @param signatureList {@code non-null;} list with signatures
     75      * @return {@code non-null;} the merged result
     76      */
     77     public static LocalVariableList mergeDescriptorsAndSignatures(
     78             LocalVariableList descriptorList,
     79             LocalVariableList signatureList) {
     80         int descriptorSize = descriptorList.size();
     81         LocalVariableList result = new LocalVariableList(descriptorSize);
     82 
     83         for (int i = 0; i < descriptorSize; i++) {
     84             Item item = descriptorList.get(i);
     85             Item signatureItem = signatureList.itemToLocal(item);
     86             if (signatureItem != null) {
     87                 CstUtf8 signature = signatureItem.getSignature();
     88                 item = item.withSignature(signature);
     89             }
     90             result.set(i, item);
     91         }
     92 
     93         result.setImmutable();
     94         return result;
     95     }
     96 
     97     /**
     98      * Constructs an instance.
     99      *
    100      * @param count the number of elements to be in the list
    101      */
    102     public LocalVariableList(int count) {
    103         super(count);
    104     }
    105 
    106     /**
    107      * Gets the indicated item.
    108      *
    109      * @param n {@code >= 0;} which item
    110      * @return {@code null-ok;} the indicated item
    111      */
    112     public Item get(int n) {
    113         return (Item) get0(n);
    114     }
    115 
    116     /**
    117      * Sets the item at the given index.
    118      *
    119      * @param n {@code >= 0, < size();} which element
    120      * @param item {@code non-null;} the item
    121      */
    122     public void set(int n, Item item) {
    123         if (item == null) {
    124             throw new NullPointerException("item == null");
    125         }
    126 
    127         set0(n, item);
    128     }
    129 
    130     /**
    131      * Sets the item at the given index.
    132      *
    133      * <p><b>Note:</b> At least one of {@code descriptor} or
    134      * {@code signature} must be passed as non-null.</p>
    135      *
    136      * @param n {@code >= 0, < size();} which element
    137      * @param startPc {@code >= 0;} the start pc of this variable's scope
    138      * @param length {@code >= 0;} the length (in bytecodes) of this variable's
    139      * scope
    140      * @param name {@code non-null;} the variable's name
    141      * @param descriptor {@code null-ok;} the variable's type descriptor
    142      * @param signature {@code null-ok;} the variable's type signature
    143      * @param index {@code >= 0;} the variable's local index
    144      */
    145     public void set(int n, int startPc, int length, CstUtf8 name,
    146             CstUtf8 descriptor, CstUtf8 signature, int index) {
    147         set0(n, new Item(startPc, length, name, descriptor, signature, index));
    148     }
    149 
    150     /**
    151      * Gets the local variable information in this instance which matches
    152      * the given {@link com.android.dx.cf.code.LocalVariableList.Item}
    153      * in all respects but the type descriptor and signature, if any.
    154      *
    155      * @param item {@code non-null;} local variable information to match
    156      * @return {@code null-ok;} the corresponding local variable information stored
    157      * in this instance, or {@code null} if there is no matching
    158      * information
    159      */
    160     public Item itemToLocal(Item item) {
    161         int sz = size();
    162 
    163         for (int i = 0; i < sz; i++) {
    164             Item one = (Item) get0(i);
    165 
    166             if ((one != null) && one.matchesAllButType(item)) {
    167                 return one;
    168             }
    169         }
    170 
    171         return null;
    172     }
    173 
    174     /**
    175      * Gets the local variable information associated with a given address
    176      * and local index, if any. <b>Note:</b> In standard classfiles, a
    177      * variable's start point is listed as the address of the instruction
    178      * <i>just past</i> the one that sets the variable.
    179      *
    180      * @param pc {@code >= 0;} the address to look up
    181      * @param index {@code >= 0;} the local variable index
    182      * @return {@code null-ok;} the associated local variable information, or
    183      * {@code null} if none is known
    184      */
    185     public Item pcAndIndexToLocal(int pc, int index) {
    186         int sz = size();
    187 
    188         for (int i = 0; i < sz; i++) {
    189             Item one = (Item) get0(i);
    190 
    191             if ((one != null) && one.matchesPcAndIndex(pc, index)) {
    192                 return one;
    193             }
    194         }
    195 
    196         return null;
    197     }
    198 
    199     /**
    200      * Item in a local variable table.
    201      */
    202     public static class Item {
    203         /** {@code >= 0;} the start pc of this variable's scope */
    204         private final int startPc;
    205 
    206         /** {@code >= 0;} the length (in bytecodes) of this variable's scope */
    207         private final int length;
    208 
    209         /** {@code non-null;} the variable's name */
    210         private final CstUtf8 name;
    211 
    212         /** {@code null-ok;} the variable's type descriptor */
    213         private final CstUtf8 descriptor;
    214 
    215         /** {@code null-ok;} the variable's type signature */
    216         private final CstUtf8 signature;
    217 
    218         /** {@code >= 0;} the variable's local index */
    219         private final int index;
    220 
    221         /**
    222          * Constructs an instance.
    223          *
    224          * <p><b>Note:</b> At least one of {@code descriptor} or
    225          * {@code signature} must be passed as non-null.</p>
    226          *
    227          * @param startPc {@code >= 0;} the start pc of this variable's scope
    228          * @param length {@code >= 0;} the length (in bytecodes) of this variable's
    229          * scope
    230          * @param name {@code non-null;} the variable's name
    231          * @param descriptor {@code null-ok;} the variable's type descriptor
    232          * @param signature {@code null-ok;} the variable's type signature
    233          * @param index {@code >= 0;} the variable's local index
    234          */
    235         public Item(int startPc, int length, CstUtf8 name,
    236                 CstUtf8 descriptor, CstUtf8 signature, int index) {
    237             if (startPc < 0) {
    238                 throw new IllegalArgumentException("startPc < 0");
    239             }
    240 
    241             if (length < 0) {
    242                 throw new IllegalArgumentException("length < 0");
    243             }
    244 
    245             if (name == null) {
    246                 throw new NullPointerException("name == null");
    247             }
    248 
    249             if ((descriptor == null) && (signature == null)) {
    250                 throw new NullPointerException(
    251                         "(descriptor == null) && (signature == null)");
    252             }
    253 
    254             if (index < 0) {
    255                 throw new IllegalArgumentException("index < 0");
    256             }
    257 
    258             this.startPc = startPc;
    259             this.length = length;
    260             this.name = name;
    261             this.descriptor = descriptor;
    262             this.signature = signature;
    263             this.index = index;
    264         }
    265 
    266         /**
    267          * Gets the start pc of this variable's scope.
    268          *
    269          * @return {@code >= 0;} the start pc of this variable's scope
    270          */
    271         public int getStartPc() {
    272             return startPc;
    273         }
    274 
    275         /**
    276          * Gets the length (in bytecodes) of this variable's scope.
    277          *
    278          * @return {@code >= 0;} the length (in bytecodes) of this variable's scope
    279          */
    280         public int getLength() {
    281             return length;
    282         }
    283 
    284         /**
    285          * Gets the variable's type descriptor.
    286          *
    287          * @return {@code null-ok;} the variable's type descriptor
    288          */
    289         public CstUtf8 getDescriptor() {
    290             return descriptor;
    291         }
    292 
    293         /**
    294          * Gets the variable's LocalItem, a (name, signature) tuple
    295          *
    296          * @return {@code null-ok;} the variable's type descriptor
    297          */
    298         public LocalItem getLocalItem() {
    299             return LocalItem.make(name, signature);
    300         }
    301 
    302         /**
    303          * Gets the variable's type signature. Private because if you need this,
    304          * you want getLocalItem() instead.
    305          *
    306          * @return {@code null-ok;} the variable's type signature
    307          */
    308         private CstUtf8 getSignature() {
    309             return signature;
    310         }
    311 
    312         /**
    313          * Gets the variable's local index.
    314          *
    315          * @return {@code >= 0;} the variable's local index
    316          */
    317         public int getIndex() {
    318             return index;
    319         }
    320 
    321         /**
    322          * Gets the variable's type descriptor. This is a convenient shorthand
    323          * for {@code Type.intern(getDescriptor().getString())}.
    324          *
    325          * @return {@code non-null;} the variable's type
    326          */
    327         public Type getType() {
    328             return Type.intern(descriptor.getString());
    329         }
    330 
    331         /**
    332          * Constructs and returns an instance which is identical to this
    333          * one, except that the signature is changed to the given value.
    334          *
    335          * @param newSignature {@code non-null;} the new signature
    336          * @return {@code non-null;} an appropriately-constructed instance
    337          */
    338         public Item withSignature(CstUtf8 newSignature) {
    339             return new Item(startPc, length, name, descriptor, newSignature,
    340                     index);
    341         }
    342 
    343         /**
    344          * Gets whether this instance matches (describes) the given
    345          * address and index.
    346          *
    347          * @param pc {@code >= 0;} the address in question
    348          * @param index {@code >= 0;} the local variable index in question
    349          * @return {@code true} iff this instance matches {@code pc}
    350          * and {@code index}
    351          */
    352         public boolean matchesPcAndIndex(int pc, int index) {
    353             return (index == this.index) &&
    354                 (pc >= startPc) &&
    355                 (pc < (startPc + length));
    356         }
    357 
    358         /**
    359          * Gets whether this instance matches (describes) the given
    360          * other instance exactly in all fields except type descriptor and
    361          * type signature.
    362          *
    363          * @param other {@code non-null;} the instance to compare to
    364          * @return {@code true} iff this instance matches
    365          */
    366         public boolean matchesAllButType(Item other) {
    367             return (startPc == other.startPc)
    368                 && (length == other.length)
    369                 && (index == other.index)
    370                 && name.equals(other.name);
    371         }
    372     }
    373 }
    374