Home | History | Annotate | Download | only in dexlib
      1 /*
      2  * [The "BSD licence"]
      3  * Copyright (c) 2010 Ben Gruver (JesusFreke)
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 package org.jf.dexlib;
     30 
     31 import org.jf.dexlib.Util.AnnotatedOutput;
     32 import org.jf.dexlib.Util.Input;
     33 import org.jf.dexlib.Util.ReadOnlyArrayList;
     34 
     35 import java.util.List;
     36 
     37 public class TypeListItem extends Item<TypeListItem> {
     38     private int hashCode = 0;
     39 
     40     private TypeIdItem[] typeList;
     41 
     42     /**
     43      * Creates a new uninitialized <code>TypeListItem</code>
     44      * @param dexFile The <code>DexFile</code> that this item belongs to
     45      */
     46     protected TypeListItem(DexFile dexFile) {
     47         super(dexFile);
     48     }
     49 
     50     /**
     51      * Creates a new <code>TypeListItem</code> for the given string
     52      * @param dexFile The <code>DexFile</code> that this item belongs to
     53      * @param typeList A list of the types that this <code>TypeListItem</code> represents
     54      */
     55     private TypeListItem(DexFile dexFile, TypeIdItem[] typeList) {
     56         super(dexFile);
     57 
     58         this.typeList = typeList;
     59     }
     60 
     61     /**
     62      * Returns a <code>TypeListItem</code> for the given values, and that has been interned into
     63      * the given <code>DexFile</code>
     64      * @param dexFile The <code>DexFile</code> that this item belongs to
     65      * @param typeList A list of the types that this <code>TypeListItem</code> represents
     66      * @return a <code>TypeListItem</code> for the given values, and that has been interned into
     67      * the given <code>DexFile</code>
     68      */
     69     public static TypeListItem internTypeListItem(DexFile dexFile, List<TypeIdItem> typeList) {
     70         TypeIdItem[] typeArray = new TypeIdItem[typeList.size()];
     71         typeList.toArray(typeArray);
     72         TypeListItem typeListItem = new TypeListItem(dexFile, typeArray);
     73         return dexFile.TypeListsSection.intern(typeListItem);
     74     }
     75 
     76     /**
     77      * Looks up the <code>TypeListItem</code> from the given <code>DexFile</code> for the given
     78      * list of types
     79      * @param dexFile the <code>Dexfile</code> to find the type in
     80      * @param typeList A list of the types that the <code>TypeListItem</code> represents
     81      * @return a <code>TypeListItem</code> from the given <code>DexFile</code> for the given
     82      * list of types, or null if it doesn't exist
     83      */
     84     public static TypeListItem lookupTypeListItem(DexFile dexFile, List<TypeIdItem> typeList) {
     85         TypeIdItem[] typeArray = new TypeIdItem[typeList.size()];
     86         typeList.toArray(typeArray);
     87         TypeListItem typeListItem = new TypeListItem(dexFile, typeArray);
     88         return dexFile.TypeListsSection.getInternedItem(typeListItem);
     89     }
     90 
     91     /** {@inheritDoc} */
     92     protected void readItem(Input in, ReadContext readContext) {
     93         int size = in.readInt();
     94         typeList = new TypeIdItem[size];
     95         for (int i=0; i<size; i++) {
     96             int typeIndex = in.readShort();
     97             typeList[i] = dexFile.TypeIdsSection.getItemByIndex(typeIndex);
     98         }
     99     }
    100 
    101     /** {@inheritDoc} */
    102     protected int placeItem(int offset) {
    103         return offset + 4 + typeList.length * 2;
    104     }
    105 
    106     /** {@inheritDoc} */
    107     protected void writeItem(AnnotatedOutput out) {
    108         //yes, the code to write the item is duplicated. This eliminates the need to iterate over the list twice
    109 
    110         if (out.annotates()) {
    111             out.annotate(4, "size: 0x" + Integer.toHexString(typeList.length) + " (" + typeList.length +")");
    112 
    113             for (TypeIdItem typeIdItem: typeList) {
    114                 out.annotate(2, "type_id_item: " + typeIdItem.getTypeDescriptor());
    115             }
    116         }
    117         out.writeInt(typeList.length);
    118         for (TypeIdItem typeIdItem: typeList) {
    119             int typeIndex = typeIdItem.getIndex();
    120             if (typeIndex > 0xffff) {
    121                 throw new RuntimeException(String.format("Error writing type_list entry. The type index of " +
    122                     "type %s is too large", typeIdItem.getTypeDescriptor()));
    123             }
    124             out.writeShort(typeIndex);
    125         }
    126     }
    127 
    128     /** {@inheritDoc} */
    129     public ItemType getItemType() {
    130         return ItemType.TYPE_TYPE_LIST;
    131     }
    132 
    133     /** {@inheritDoc} */
    134     public String getConciseIdentity() {
    135         return "type_list: " + getTypeListString("");
    136     }
    137 
    138     /** {@inheritDoc} */
    139     public int compareTo(TypeListItem o) {
    140         if (o == null) {
    141             return 1;
    142         }
    143 
    144         int thisSize = typeList.length;
    145         int otherSize = o.typeList.length;
    146         int size = Math.min(thisSize, otherSize);
    147 
    148         for (int i = 0; i < size; i++) {
    149             int result = typeList[i].compareTo(o.typeList[i]);
    150             if (result != 0) {
    151                 return result;
    152             }
    153         }
    154 
    155         if (thisSize < otherSize) {
    156             return -1;
    157         } else if (thisSize > otherSize) {
    158             return 1;
    159         } else {
    160             return 0;
    161         }
    162     }
    163 
    164     /**
    165      * @return the number of registers required for this <code>TypeListItem</code>
    166      */
    167     public int getRegisterCount() {
    168         int wordCount = 0;
    169         for (TypeIdItem typeIdItem: typeList) {
    170             wordCount += typeIdItem.getRegisterCount();
    171         }
    172         return wordCount;
    173     }
    174 
    175     /**
    176      * @return a string consisting of the type descriptors in this <code>TypeListItem</code>
    177      * that are separated by the given separator
    178      * @param separator the separator between each type
    179      */
    180     public String getTypeListString(String separator) {
    181         int size = 0;
    182         for (TypeIdItem typeIdItem: typeList) {
    183             size += typeIdItem.getTypeDescriptor().length();
    184             size += separator.length();
    185         }
    186 
    187         StringBuilder sb = new StringBuilder(size);
    188         for (TypeIdItem typeIdItem: typeList) {
    189             sb.append(typeIdItem.getTypeDescriptor());
    190             sb.append(separator);
    191         }
    192         if (typeList.length > 0) {
    193             sb.delete(sb.length() - separator.length(), sb.length());
    194         }
    195         return sb.toString();
    196     }
    197 
    198     /**
    199      * @return a string consisting of the shorty form of the type descriptors in this
    200      * <code>TypeListItem</code> that are directly concatenated together
    201      */
    202     public String getShortyString() {
    203         StringBuilder sb = new StringBuilder();
    204         for (TypeIdItem typeIdItem: typeList) {
    205             sb.append(typeIdItem.toShorty());
    206         }
    207         return sb.toString();
    208     }
    209 
    210     /**
    211      * @param index the index of the <code>TypeIdItem</code> to get
    212      * @return the <code>TypeIdItem</code> at the given index
    213      */
    214     public TypeIdItem getTypeIdItem(int index) {
    215         return typeList[index];
    216     }
    217 
    218     /**
    219      * @return the number of types in this <code>TypeListItem</code>
    220      */
    221     public int getTypeCount() {
    222         return typeList.length;
    223     }
    224 
    225     /**
    226      * @return an array of the <code>TypeIdItems</code> in this <code>TypeListItem</code>
    227      */
    228     public List<TypeIdItem> getTypes() {
    229         return new ReadOnlyArrayList<TypeIdItem>(typeList);
    230     }
    231 
    232     /**
    233      * Helper method to allow easier "inline" retrieval of of the list of TypeIdItems
    234      * @param typeListItem the typeListItem to return the types of (can be null)
    235      * @return an array of the <code>TypeIdItems</code> in the specified <code>TypeListItem</code>, or null if the
    236      * TypeListItem is null
    237      */
    238     public static List<TypeIdItem> getTypes(TypeListItem typeListItem) {
    239         return typeListItem==null?null:typeListItem.getTypes();
    240     }
    241 
    242     /**
    243      * calculate and cache the hashcode
    244      */
    245     private void calcHashCode() {
    246         int hashCode = 1;
    247 
    248         for (TypeIdItem typeIdItem: typeList) {
    249             hashCode = 31 * hashCode + typeIdItem.hashCode();
    250         }
    251         this.hashCode = hashCode;
    252     }
    253 
    254     @Override
    255     public int hashCode() {
    256         //there's a small possibility that the actual hash code will be 0. If so, we'll
    257         //just end up recalculating it each time
    258         if (hashCode == 0)
    259             calcHashCode();
    260         return hashCode;
    261     }
    262 
    263     @Override
    264     public boolean equals(Object o) {
    265         if (this==o) {
    266             return true;
    267         }
    268         if (o==null || !this.getClass().equals(o.getClass())) {
    269             return false;
    270         }
    271 
    272         //This assumes that the referenced items have been interned in both objects.
    273         //This is a valid assumption because all outside code must use the static
    274         //"getInterned..." style methods to make new items, and any item created
    275         //internally is guaranteed to be interned
    276         TypeListItem other = (TypeListItem)o;
    277         if (typeList.length != other.typeList.length) {
    278             return false;
    279         }
    280 
    281         for (int i=0; i<typeList.length; i++) {
    282             if (typeList[i] != other.typeList[i]) {
    283                 return false;
    284             }
    285         }
    286         return true;
    287     }
    288 }
    289