/* * [The "BSD licence"] * Copyright (c) 2010 Ben Gruver (JesusFreke) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jf.dexlib; import org.jf.dexlib.Util.AnnotatedOutput; import org.jf.dexlib.Util.Input; import org.jf.dexlib.Util.ReadOnlyArrayList; import java.util.List; public class TypeListItem extends Item<TypeListItem> { private int hashCode = 0; private TypeIdItem[] typeList; /** * Creates a new uninitialized <code>TypeListItem</code> * @param dexFile The <code>DexFile</code> that this item belongs to */ protected TypeListItem(DexFile dexFile) { super(dexFile); } /** * Creates a new <code>TypeListItem</code> for the given string * @param dexFile The <code>DexFile</code> that this item belongs to * @param typeList A list of the types that this <code>TypeListItem</code> represents */ private TypeListItem(DexFile dexFile, TypeIdItem[] typeList) { super(dexFile); this.typeList = typeList; } /** * Returns a <code>TypeListItem</code> for the given values, and that has been interned into * the given <code>DexFile</code> * @param dexFile The <code>DexFile</code> that this item belongs to * @param typeList A list of the types that this <code>TypeListItem</code> represents * @return a <code>TypeListItem</code> for the given values, and that has been interned into * the given <code>DexFile</code> */ public static TypeListItem internTypeListItem(DexFile dexFile, List<TypeIdItem> typeList) { TypeIdItem[] typeArray = new TypeIdItem[typeList.size()]; typeList.toArray(typeArray); TypeListItem typeListItem = new TypeListItem(dexFile, typeArray); return dexFile.TypeListsSection.intern(typeListItem); } /** * Looks up the <code>TypeListItem</code> from the given <code>DexFile</code> for the given * list of types * @param dexFile the <code>Dexfile</code> to find the type in * @param typeList A list of the types that the <code>TypeListItem</code> represents * @return a <code>TypeListItem</code> from the given <code>DexFile</code> for the given * list of types, or null if it doesn't exist */ public static TypeListItem lookupTypeListItem(DexFile dexFile, List<TypeIdItem> typeList) { TypeIdItem[] typeArray = new TypeIdItem[typeList.size()]; typeList.toArray(typeArray); TypeListItem typeListItem = new TypeListItem(dexFile, typeArray); return dexFile.TypeListsSection.getInternedItem(typeListItem); } /** {@inheritDoc} */ protected void readItem(Input in, ReadContext readContext) { int size = in.readInt(); typeList = new TypeIdItem[size]; for (int i=0; i<size; i++) { int typeIndex = in.readShort(); typeList[i] = dexFile.TypeIdsSection.getItemByIndex(typeIndex); } } /** {@inheritDoc} */ protected int placeItem(int offset) { return offset + 4 + typeList.length * 2; } /** {@inheritDoc} */ protected void writeItem(AnnotatedOutput out) { //yes, the code to write the item is duplicated. This eliminates the need to iterate over the list twice if (out.annotates()) { out.annotate(4, "size: 0x" + Integer.toHexString(typeList.length) + " (" + typeList.length +")"); for (TypeIdItem typeIdItem: typeList) { out.annotate(2, "type_id_item: " + typeIdItem.getTypeDescriptor()); } } out.writeInt(typeList.length); for (TypeIdItem typeIdItem: typeList) { out.writeShort(typeIdItem.getIndex()); } } /** {@inheritDoc} */ public ItemType getItemType() { return ItemType.TYPE_TYPE_LIST; } /** {@inheritDoc} */ public String getConciseIdentity() { return "type_list: " + getTypeListString(""); } /** {@inheritDoc} */ public int compareTo(TypeListItem o) { if (o == null) { return 1; } int thisSize = typeList.length; int otherSize = o.typeList.length; int size = Math.min(thisSize, otherSize); for (int i = 0; i < size; i++) { int result = typeList[i].compareTo(o.typeList[i]); if (result != 0) { return result; } } if (thisSize < otherSize) { return -1; } else if (thisSize > otherSize) { return 1; } else { return 0; } } /** * @return the number of registers required for this <code>TypeListItem</code> */ public int getRegisterCount() { int wordCount = 0; for (TypeIdItem typeIdItem: typeList) { wordCount += typeIdItem.getRegisterCount(); } return wordCount; } /** * @return a string consisting of the type descriptors in this <code>TypeListItem</code> * that are separated by the given separator * @param separator the separator between each type */ public String getTypeListString(String separator) { int size = 0; for (TypeIdItem typeIdItem: typeList) { size += typeIdItem.getTypeDescriptor().length(); size += separator.length(); } StringBuilder sb = new StringBuilder(size); for (TypeIdItem typeIdItem: typeList) { sb.append(typeIdItem.getTypeDescriptor()); sb.append(separator); } if (typeList.length > 0) { sb.delete(sb.length() - separator.length(), sb.length()); } return sb.toString(); } /** * @return a string consisting of the shorty form of the type descriptors in this * <code>TypeListItem</code> that are directly concatenated together */ public String getShortyString() { StringBuilder sb = new StringBuilder(); for (TypeIdItem typeIdItem: typeList) { sb.append(typeIdItem.toShorty()); } return sb.toString(); } /** * @param index the index of the <code>TypeIdItem</code> to get * @return the <code>TypeIdItem</code> at the given index */ public TypeIdItem getTypeIdItem(int index) { return typeList[index]; } /** * @return the number of types in this <code>TypeListItem</code> */ public int getTypeCount() { return typeList.length; } /** * @return an array of the <code>TypeIdItems</code> in this <code>TypeListItem</code> */ public List<TypeIdItem> getTypes() { return new ReadOnlyArrayList<TypeIdItem>(typeList); } /** * Helper method to allow easier "inline" retrieval of of the list of TypeIdItems * @param typeListItem the typeListItem to return the types of (can be null) * @return an array of the <code>TypeIdItems</code> in the specified <code>TypeListItem</code>, or null if the * TypeListItem is null */ public static List<TypeIdItem> getTypes(TypeListItem typeListItem) { return typeListItem==null?null:typeListItem.getTypes(); } /** * calculate and cache the hashcode */ private void calcHashCode() { int hashCode = 1; for (TypeIdItem typeIdItem: typeList) { hashCode = 31 * hashCode + typeIdItem.hashCode(); } this.hashCode = hashCode; } @Override public int hashCode() { //there's a small possibility that the actual hash code will be 0. If so, we'll //just end up recalculating it each time if (hashCode == 0) calcHashCode(); return hashCode; } @Override public boolean equals(Object o) { if (this==o) { return true; } if (o==null || !this.getClass().equals(o.getClass())) { return false; } //This assumes that the referenced items have been interned in both objects. //This is a valid assumption because all outside code must use the static //"getInterned..." style methods to make new items, and any item created //internally is guaranteed to be interned TypeListItem other = (TypeListItem)o; if (typeList.length != other.typeList.length) { return false; } for (int i=0; i<typeList.length; i++) { if (typeList[i] != other.typeList[i]) { return false; } } return true; } }