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